Java bindings for Chrome DevTools Protocol version 101, enabling browser automation and debugging capabilities through CDP integration
—
The logging domain provides browser-level logging functionality for capturing and managing browser console logs, network logs, and other browser-generated log messages separate from JavaScript console events.
Browser logging implementation that provides commands for enabling, clearing, and monitoring browser logs.
/**
* Browser logging functionality for CDP version 101
* Implements the Log interface for managing browser-generated log messages
*/
public class V101Log implements Log {
/**
* Creates a new log handler instance
*/
public V101Log();
/**
* Enable the log domain to start receiving log entries
* @return Command to enable browser logging
*/
public Command<Void> enable();
/**
* Clear all browser logs
* @return Command to clear the browser log buffer
*/
public Command<Void> clear();
/**
* Get an event stream for new log entries
* @return Event that fires when new log entries are added
*/
public Event<LogEntry> entryAdded();
}Usage Examples:
import org.openqa.selenium.devtools.v101.V101Log;
import org.openqa.selenium.devtools.idealized.log.model.LogEntry;
// Create log handler
V101Log log = new V101Log();
// Enable logging
devTools.send(log.enable());
// Listen for new log entries
devTools.addListener(log.entryAdded(), entry -> {
System.out.println("Browser Log [" + entry.getSource() + "]: " +
entry.getLogEntry().getMessage());
System.out.println("Level: " + entry.getLogEntry().getLevel());
System.out.println("Timestamp: " + new Date(entry.getLogEntry().getMillis()));
});
// Clear existing logs
devTools.send(log.clear());
// Navigate to generate logs
driver.get("https://example.com");
// Perform actions that generate browser logs
driver.executeScript("console.error('This will appear in browser logs');");
// Network requests will also generate logs
driver.findElement(By.tagName("body")).click();Represents a browser log entry with source information and standard Java logging details.
/**
* Represents a browser log entry with source and standard logging information
* Wraps the browser log data in familiar Java logging structures
*/
public class LogEntry {
/**
* Get the source of the log entry (console, network, security, etc.)
* @return String indicating the log source
*/
public String getSource();
/**
* Get the standard Java log entry with level, timestamp, and message
* @return java.util.logging.LogEntry with formatted log data
*/
public java.util.logging.LogEntry getLogEntry();
}Log Sources:
"console" - Browser console messages"network" - Network-related log messages"security" - Security-related warnings and errors"deprecation" - Deprecation warnings"worker" - Web worker log messages"violation" - Performance and security violations"intervention" - Browser interventions (blocking actions)"recommendation" - Browser recommendationsUsage Example:
devTools.addListener(log.entryAdded(), entry -> {
java.util.logging.LogEntry logEntry = entry.getLogEntry();
// Process logs based on source
switch (entry.getSource()) {
case "console":
handleConsoleLog(logEntry);
break;
case "network":
handleNetworkLog(logEntry);
break;
case "security":
handleSecurityLog(logEntry);
break;
case "deprecation":
handleDeprecationWarning(logEntry);
break;
default:
handleGenericLog(entry.getSource(), logEntry);
}
});
private void handleSecurityLog(java.util.logging.LogEntry logEntry) {
if (logEntry.getLevel().intValue() >= Level.SEVERE.intValue()) {
System.err.println("🔒 SECURITY ISSUE: " + logEntry.getMessage());
// Could trigger security test failure
}
}
private void handleNetworkLog(java.util.logging.LogEntry logEntry) {
if (logEntry.getMessage().contains("failed")) {
System.err.println("🌐 NETWORK ERROR: " + logEntry.getMessage());
}
}The log entries use standard Java logging levels and can be integrated with existing logging frameworks.
/**
* Standard Java logging levels used by browser log entries
*/
public enum Level {
FINEST, // Verbose browser logs
FINE, // Debug-level browser logs
INFO, // Informational browser logs
WARNING, // Browser warnings
SEVERE // Browser errors
}
/**
* Standard Java LogEntry with browser log data
*/
public class java.util.logging.LogEntry {
public Level getLevel();
public long getMillis();
public String getMessage();
public String getLoggerName();
public String getSourceClassName();
public String getSourceMethodName();
}Integration with Logging Frameworks:
import java.util.logging.Logger;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
// Create a custom handler for browser logs
Logger browserLogger = Logger.getLogger("browser-logs");
devTools.addListener(log.entryAdded(), entry -> {
java.util.logging.LogEntry browserLog = entry.getLogEntry();
// Create a LogRecord for standard Java logging
LogRecord record = new LogRecord(browserLog.getLevel(), browserLog.getMessage());
record.setMillis(browserLog.getMillis());
record.setLoggerName("browser." + entry.getSource());
record.setSourceClassName("Browser");
record.setSourceMethodName(entry.getSource());
// Publish to standard logging system
browserLogger.log(record);
});
// Or integrate with SLF4J
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger slf4jLogger = LoggerFactory.getLogger("browser-logs");
devTools.addListener(log.entryAdded(), entry -> {
java.util.logging.LogEntry browserLog = entry.getLogEntry();
String message = "[" + entry.getSource() + "] " + browserLog.getMessage();
switch (browserLog.getLevel().intValue()) {
case 1000: // SEVERE
slf4jLogger.error(message);
break;
case 900: // WARNING
slf4jLogger.warn(message);
break;
case 800: // INFO
slf4jLogger.info(message);
break;
default: // FINE, FINER, FINEST
slf4jLogger.debug(message);
}
});The underlying CDP Log domain types used by V101Log:
/**
* CDP Log.LogEntry event data
* Raw log entry data from the Chrome DevTools Protocol
*/
public class org.openqa.selenium.devtools.v101.log.model.LogEntry {
public String getSource();
public Level getLevel();
public String getText();
public Timestamp getTimestamp();
public Optional<String> getUrl();
public Optional<Integer> getLineNumber();
public Optional<StackTrace> getStackTrace();
public Optional<Integer> getNetworkRequestId();
public Optional<String> getWorkerId();
public Optional<List<String>> getArgs();
}
/**
* CDP log level enumeration
*/
public enum Level {
VERBOSE("verbose"),
INFO("info"),
WARNING("warning"),
ERROR("error");
public String toString();
}
/**
* CDP timestamp representation
*/
public class Timestamp {
public JsonElement toJson();
public String toString();
}The V101Log class internally uses these CDP Log domain commands:
// From org.openqa.selenium.devtools.v101.log.Log
public static Command<Void> enable();
public static Command<Void> disable();
public static Command<Void> clear();
public static Event<LogEntry> entryAdded();// Sophisticated log processing with filtering and categorization
public class BrowserLogAnalyzer {
private final Map<String, List<LogEntry>> logsBySource = new ConcurrentHashMap<>();
private final Map<Level, AtomicInteger> logCounts = new ConcurrentHashMap<>();
public void setupLogMonitoring(DevTools devTools, V101Log log) {
devTools.send(log.enable());
devTools.addListener(log.entryAdded(), this::processLogEntry);
}
private void processLogEntry(LogEntry entry) {
String source = entry.getSource();
java.util.logging.LogEntry logEntry = entry.getLogEntry();
Level level = logEntry.getLevel();
// Categorize by source
logsBySource.computeIfAbsent(source, k -> new ArrayList<>()).add(entry);
// Count by level
logCounts.computeIfAbsent(level, k -> new AtomicInteger(0)).incrementAndGet();
// Filter and process specific log types
if ("security".equals(source) && level.intValue() >= Level.WARNING.intValue()) {
handleSecurityIssue(entry);
}
if ("network".equals(source) && logEntry.getMessage().contains("blocked")) {
handleBlockedRequest(entry);
}
if ("deprecation".equals(source)) {
handleDeprecationWarning(entry);
}
}
public void printLogSummary() {
System.out.println("=== Browser Log Summary ===");
logCounts.forEach((level, count) -> {
System.out.println(level + ": " + count.get() + " entries");
});
System.out.println("\n=== Logs by Source ===");
logsBySource.forEach((source, logs) -> {
System.out.println(source + ": " + logs.size() + " entries");
});
}
private void handleSecurityIssue(LogEntry entry) {
System.err.println("🔒 SECURITY: " + entry.getLogEntry().getMessage());
// Could fail tests or trigger alerts
}
private void handleBlockedRequest(LogEntry entry) {
System.out.println("🚫 BLOCKED: " + entry.getLogEntry().getMessage());
// Could indicate CSP or CORS issues
}
private void handleDeprecationWarning(LogEntry entry) {
System.out.println("⚠️ DEPRECATED: " + entry.getLogEntry().getMessage());
// Could track deprecated API usage
}
}// Monitor browser performance through log analysis
public class PerformanceLogMonitor {
private final List<String> performanceIssues = new ArrayList<>();
public void setupPerformanceMonitoring(DevTools devTools, V101Log log) {
devTools.send(log.enable());
devTools.addListener(log.entryAdded(), entry -> {
String message = entry.getLogEntry().getMessage().toLowerCase();
// Detect performance-related log entries
if (message.contains("slow") ||
message.contains("timeout") ||
message.contains("performance") ||
message.contains("long task")) {
performanceIssues.add(entry.getLogEntry().getMessage());
System.out.println("⚡ PERFORMANCE ISSUE: " + entry.getLogEntry().getMessage());
}
// Detect memory-related issues
if (message.contains("memory") || message.contains("heap")) {
System.out.println("💾 MEMORY ISSUE: " + entry.getLogEntry().getMessage());
}
// Detect layout thrashing
if (message.contains("layout") || message.contains("reflow")) {
System.out.println("📐 LAYOUT ISSUE: " + entry.getLogEntry().getMessage());
}
});
}
public List<String> getPerformanceIssues() {
return new ArrayList<>(performanceIssues);
}
public void clearPerformanceIssues() {
performanceIssues.clear();
}
}// Persist logs for analysis and reporting
public class LogPersistence {
private final PrintWriter logWriter;
private final String testName;
public LogPersistence(String testName) throws IOException {
this.testName = testName;
this.logWriter = new PrintWriter(new FileWriter(testName + "-browser-logs.txt"));
}
public void setupLogPersistence(DevTools devTools, V101Log log) {
devTools.send(log.enable());
devTools.addListener(log.entryAdded(), entry -> {
java.util.logging.LogEntry logEntry = entry.getLogEntry();
// Write structured log data
logWriter.printf("[%s] %s %s: %s%n",
new Date(logEntry.getMillis()).toString(),
entry.getSource().toUpperCase(),
logEntry.getLevel(),
logEntry.getMessage()
);
logWriter.flush();
});
}
public void close() {
if (logWriter != null) {
logWriter.close();
}
}
// Generate summary report
public void generateReport() throws IOException {
try (PrintWriter reportWriter = new PrintWriter(new FileWriter(testName + "-log-summary.txt"))) {
reportWriter.println("Browser Log Summary for: " + testName);
reportWriter.println("Generated: " + new Date());
// Could include statistics, error counts, performance metrics
reportWriter.println("Log analysis would go here...");
}
}
}
// Usage in test
LogPersistence logPersistence = new LogPersistence("test-checkout-flow");
try {
logPersistence.setupLogPersistence(devTools, log);
// Run test...
} finally {
logPersistence.generateReport();
logPersistence.close();
}Install with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-devtools-v101