Chrome DevTools Protocol version 115 bindings for Selenium Java WebDriver enabling programmatic browser debugging capabilities
—
Browser console log collection, filtering, and event stream management with level conversion and timestamp handling through the v115Log class.
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();
}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');");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 Level | Java Level | Description |
|---|---|---|
verbose | FINEST | Detailed debug information |
info | INFO | General information messages |
warning | WARNING | Warning conditions |
error | SEVERE | Error conditions |
| (default) | INFO | Fallback 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());
}
});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 lifecycleFilter 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));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());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());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");
});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();
}
}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
}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