CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-seleniumhq-selenium--selenium-devtools-v115

Chrome DevTools Protocol version 115 bindings for Selenium Java WebDriver enabling programmatic browser debugging capabilities

Pending
Overview
Eval results
Files

logging-operations.mddocs/

Browser Logging

Browser console log collection, filtering, and event stream management with level conversion and timestamp handling through the v115Log class.

Capabilities

Log Domain

Comprehensive browser console log management with event streaming and level conversion.

/**
 * Browser log domain for console log collection and management
 * Implements the idealized Log interface for version independence
 */
public class v115Log implements Log {
    /**
     * Enable log domain for console log collection
     * @return Command to enable log domain
     */
    public Command<Void> enable();
    
    /**
     * Clear all browser console logs
     * @return Command to clear browser logs
     */
    public Command<Void> clear();
    
    /**
     * Get log entry added event stream
     * @return Event stream for new log entries
     */
    public Event<LogEntry> entryAdded();
}

Log Collection

Enable browser log collection and receive log entries as they are created.

Usage Examples:

import org.openqa.selenium.devtools.v115.v115Log;
import org.openqa.selenium.devtools.idealized.log.model.LogEntry;
import java.util.logging.Level;

// Create log domain
v115Log log = new v115Log();

// Enable log collection
devTools.send(log.enable());

// Listen for log entries
devTools.addListener(log.entryAdded(), logEntry -> {
    System.out.println("Browser Log Entry:");
    System.out.println("  Kind: " + logEntry.getKind());
    
    org.openqa.selenium.logging.LogEntry entry = logEntry.getEntry();
    System.out.println("  Level: " + entry.getLevel());
    System.out.println("  Timestamp: " + new Date(entry.getTimestamp()));
    System.out.println("  Message: " + entry.getMessage());
    System.out.println();
});

// Navigate and generate logs
driver.get("https://example.com");

// Generate console logs from browser
driver.executeScript("console.log('Information message');");
driver.executeScript("console.warn('Warning message');");
driver.executeScript("console.error('Error message');");

// JavaScript error
driver.executeScript("throw new Error('Test error');");

Log Level Conversion

The v115Log class automatically converts CDP log levels to standard Java logging levels.

Supported Level Mappings:

/**
 * Convert CDP log level to Java logging level
 * @param level CDP LogEntry.Level enum
 * @return Java logging Level
 */
private Level fromCdpLevel(LogEntry.Level level);

Level Conversion Table:

CDP LevelJava LevelDescription
verboseFINESTDetailed debug information
infoINFOGeneral information messages
warningWARNINGWarning conditions
errorSEVEREError conditions
(default)INFOFallback for unknown levels

Usage Examples:

// The level conversion happens automatically in log event processing
devTools.addListener(log.entryAdded(), logEntry -> {
    org.openqa.selenium.logging.LogEntry entry = logEntry.getEntry();
    Level javaLevel = entry.getLevel();
    
    // Handle different log levels
    switch (javaLevel.getName()) {
        case "SEVERE" -> System.err.println("ERROR: " + entry.getMessage());
        case "WARNING" -> System.out.println("WARN: " + entry.getMessage());
        case "INFO" -> System.out.println("INFO: " + entry.getMessage());
        case "FINEST" -> System.out.println("DEBUG: " + entry.getMessage());
        default -> System.out.println("LOG: " + entry.getMessage());
    }
});

Log Management

Clear browser logs and manage log collection lifecycle.

Usage Examples:

// Clear existing logs before starting test
devTools.send(log.clear());

// Run test operations
driver.get("https://example.com");
performTestOperations();

// Clear logs after test
devTools.send(log.clear());

// Disable log collection when done
// Note: Log domain doesn't have explicit disable - use DevTools session lifecycle

Advanced Log Filtering

Filter and categorize log entries based on content and level.

Usage Examples:

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

// Set up log categorization
Map<String, AtomicInteger> logCounts = new ConcurrentHashMap<>();
List<String> errorMessages = Collections.synchronizedList(new ArrayList<>());
List<String> networkLogs = Collections.synchronizedList(new ArrayList<>());

devTools.addListener(log.entryAdded(), logEntry -> {
    org.openqa.selenium.logging.LogEntry entry = logEntry.getEntry();
    String message = entry.getMessage();
    Level level = entry.getLevel();
    
    // Count logs by level
    String levelName = level.getName();
    logCounts.computeIfAbsent(levelName, k -> new AtomicInteger(0)).incrementAndGet();
    
    // Collect error messages
    if (level == Level.SEVERE) {
        errorMessages.add(message);
    }
    
    // Filter network-related logs
    if (message.contains("Failed to load resource") || 
        message.contains("XMLHttpRequest") ||
        message.contains("fetch")) {
        networkLogs.add(message);
    }
    
    // Filter security warnings
    if (message.contains("Mixed Content") || 
        message.contains("Insecure") ||
        message.contains("HTTPS")) {
        System.out.println("SECURITY WARNING: " + message);
    }
});

// Generate logs and analyze
driver.get("https://example.com");
performComplexOperations();

// Print analysis
System.out.println("\nLog Analysis:");
logCounts.forEach((level, count) -> 
    System.out.println(level + ": " + count.get() + " entries"));

System.out.println("\nErrors found: " + errorMessages.size());
errorMessages.forEach(error -> System.out.println("  - " + error));

System.out.println("\nNetwork issues: " + networkLogs.size());
networkLogs.forEach(networkLog -> System.out.println("  - " + networkLog));

Log Pattern Matching

Identify specific log patterns for automated testing and monitoring.

Usage Examples:

import java.util.regex.Pattern;
import java.util.concurrent.CompletableFuture;

// Set up pattern-based log monitoring
Pattern errorPattern = Pattern.compile("Error:.*|Uncaught.*|TypeError:.*", Pattern.CASE_INSENSITIVE);
Pattern apiErrorPattern = Pattern.compile("HTTP.*[45]\\d\\d|API.*error|Request failed", Pattern.CASE_INSENSITIVE);
Pattern performancePattern = Pattern.compile("Performance.*|Slow.*|Timeout.*", Pattern.CASE_INSENSITIVE);

CompletableFuture<String> firstErrorFuture = new CompletableFuture<>();
List<String> apiErrors = Collections.synchronizedList(new ArrayList<>());
List<String> performanceIssues = Collections.synchronizedList(new ArrayList<>());

devTools.addListener(log.entryAdded(), logEntry -> {
    String message = logEntry.getEntry().getMessage();
    
    // Detect first JavaScript error
    if (errorPattern.matcher(message).find() && !firstErrorFuture.isDone()) {
        firstErrorFuture.complete(message);
    }
    
    // Collect API errors
    if (apiErrorPattern.matcher(message).find()) {
        apiErrors.add(message);
    }
    
    // Collect performance issues
    if (performancePattern.matcher(message).find()) {
        performanceIssues.add(message);
    }
});

// Run operations and wait for specific conditions
driver.get("https://app.example.com");

try {
    // Wait for first error (with timeout)
    String firstError = firstErrorFuture.get(10, TimeUnit.SECONDS);
    System.out.println("First error detected: " + firstError);
} catch (TimeoutException e) {
    System.out.println("No errors detected within timeout");
} catch (Exception e) {
    System.err.println("Error waiting for log: " + e.getMessage());
}

// Analyze collected issues
System.out.println("API errors detected: " + apiErrors.size());
System.out.println("Performance issues detected: " + performanceIssues.size());

Integration with WebDriver Logs

Combine DevTools logs with WebDriver's built-in logging capabilities.

Usage Examples:

import org.openqa.selenium.logging.LogType;
import org.openqa.selenium.logging.LoggingPreferences;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.logging.LogEntries;

// Set up WebDriver logging (complementary to DevTools logging)
ChromeOptions options = new ChromeOptions();
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.BROWSER, Level.ALL);
logPrefs.enable(LogType.PERFORMANCE, Level.INFO);
options.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);

ChromeDriver driver = new ChromeDriver(options);
DevTools devTools = driver.getDevTools();
devTools.createSession();

// Enable DevTools logging
v115Log devToolsLog = new v115Log();
devTools.send(devToolsLog.enable());

// Collect logs from both sources
List<String> devToolsLogs = Collections.synchronizedList(new ArrayList<>());
List<String> webDriverLogs = Collections.synchronizedList(new ArrayList<>());

// DevTools log collection
devTools.addListener(devToolsLog.entryAdded(), logEntry -> {
    devToolsLogs.add("DevTools: " + logEntry.getEntry().getMessage());
});

// Perform operations
driver.get("https://example.com");
performTestOperations();

// Collect WebDriver logs
LogEntries browserLogs = driver.manage().logs().get(LogType.BROWSER);
browserLogs.forEach(entry -> {
    webDriverLogs.add("WebDriver: " + entry.getMessage());
});

// Compare and analyze both log sources
System.out.println("DevTools logs: " + devToolsLogs.size());
System.out.println("WebDriver logs: " + webDriverLogs.size());

// Find overlapping entries
Set<String> uniqueMessages = new HashSet<>();
devToolsLogs.forEach(log -> uniqueMessages.add(log.substring(log.indexOf(":") + 2)));
webDriverLogs.forEach(log -> uniqueMessages.add(log.substring(log.indexOf(":") + 2)));

System.out.println("Unique log messages: " + uniqueMessages.size());

Timestamp Handling

CDP Timestamp Conversion

The v115Log class handles CDP timestamp conversion with error recovery.

/**
 * Convert CDP timestamp to milliseconds since epoch
 * @param timestamp CDP Timestamp object
 * @return Long timestamp in milliseconds, or current time if conversion fails
 */
private long fromCdpTimestamp(Timestamp timestamp);

Usage Examples:

// Timestamp conversion happens automatically, but you can access it
devTools.addListener(log.entryAdded(), logEntry -> {
    org.openqa.selenium.logging.LogEntry entry = logEntry.getEntry();
    long timestamp = entry.getTimestamp();
    
    // Convert to various time formats
    Instant instant = Instant.ofEpochMilli(timestamp);
    LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    
    System.out.println("Log timestamp: " + dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
    System.out.println("Relative time: " + Duration.between(Instant.now(), instant).toMillis() + "ms ago");
});

Error Handling

Malformed Timestamps

The system gracefully handles malformed CDP timestamps:

// Automatic fallback to current system time for invalid timestamps
private long fromCdpTimestamp(Timestamp timestamp) {
    try {
        return Long.parseLong(timestamp.toString());
    } catch (NumberFormatException e) {
        // Return current time as fallback
        return System.currentTimeMillis();
    }
}

Log Collection Failures

Handle cases where log collection fails or is interrupted:

try {
    devTools.send(log.enable());
    System.out.println("Log collection enabled");
} catch (Exception e) {
    System.err.println("Failed to enable log collection: " + e.getMessage());
    // Continue without DevTools logging, rely on WebDriver logs
}

// Safe log clearing
try {
    devTools.send(log.clear());
    System.out.println("Browser logs cleared");
} catch (Exception e) {
    System.err.println("Failed to clear logs: " + e.getMessage());
    // Not critical - continue operation
}

Memory Management

Prevent memory leaks when collecting large numbers of log entries:

// Use bounded collections for log storage
private final Queue<String> recentLogs = new ArrayDeque<String>() {
    @Override
    public boolean add(String item) {
        if (size() >= 1000) {  // Keep only last 1000 logs
            poll();
        }
        return super.add(item);
    }
};

devTools.addListener(log.entryAdded(), logEntry -> {
    String message = logEntry.getEntry().getMessage();
    
    // Store with size limit
    synchronized (recentLogs) {
        recentLogs.add(message);
    }
});

Install with Tessl CLI

npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-devtools-v115

docs

domain-management.md

index.md

javascript-integration.md

logging-operations.md

network-operations.md

runtime-events.md

target-management.md

tile.json