Log4j2 configuration for Selenium Framework

Log4j2 configuration for Selenium Framework 


What is log4j2

Log4j2 is the updated version of the popular and influential log4j library, used extensively throughout the Java ecosystem for so many years. Logging events is a critical aspect of software development. While there are lots of frameworks available in Java ecosystem, Log4J has been the most popular for decades, due to the flexibility and simplicity it provides.

Log4j 2 is a new and improved version of the classic Log4j framework.

In Log4J2, an appender is simply a destination for log events; it can be as simple as a console and can be complex like any RDBMS. Layouts determine how the logs will be presented and filters filter the data according to the various criterion.


Reasons to upgrade from Log4j 1.x to Log4j 2


Update: since August 2015, Log4j 1.x is officially End of Life and it is recommended to upgrade to Log4j 2. Update 2: Log4j 1.2 is broken in Java 9.

Community support: Log4j 1.x is not actively maintained, whereas Log4j 2 has an active community where questions are answered, features are added and bugs are fixed.
Async Loggers - performance similar to logging switched off
Custom log levels
Automatically reload its configuration upon modification without losing log events while reconfiguring.
Java 8-style lambda support for lazy logging
Log4j 2 is garbage-free (or at least low-garbage) since version 2.6
Filtering: filtering based on context data, markers, regular expressions, and other components in the Log event. Filters can be associated with Loggers. You can use a common Filter class in any of these circumstances.
Plugin Architecture - easy to extend by building custom components
Supported APIs: SLF4J, Commons Logging, Log4j-1.x and java.util.logging
Log4j 2 API separate from the Log4j 2 implementation. API supports more than just logging Strings: CharSequences, Objects and custom Messages. Messages allow support for interesting and complex constructs to be passed through the logging system and be efficiently manipulated. Users are free to create their own Message types and write custom Layouts, Filters and Lookups to manipulate them.
Concurrency improvements: log4j2 uses java.util.concurrent libraries to perform locking at the lowest level possible. Log4j-1.x has known deadlock issues.
Configuration via XML, JSON, YAML, properties configuration files or programmatically.


Be aware


log4j2.xml and log4j2.properties formats are different from the Log4j 1.2 configuration syntax
Log4j 2 is not fully compatible with Log4j 1.x: The Log4j 1.2 API is supported by the log4j-1.2-api adapter but customizations that rely on Log4j 1.2 internals may not work.
Java 6 required for version 2.0 to 2.3. Java 7 is required for Log4j 2.4 and later.

Tips when upgrading from log4j1 to log4j2


Common issues people are having when getting started with log4j2:

You need (at least) both log4j-api-2.6.2.jar and log4j-core-2.6.2.jar in your classpath
Log4j2 looks for a log4j2.xml config file, not a log4j.xml config file
Config file location: either put it in the classpath or specify its path with the log4j.configurationFile system property
To debug the configuration, use <Configuration status="trace"> in the beginning of your config file
I would recommend starting with one of the many sample configurations provided in the log4j2 manual, then add more bells and whistles bit by bit.
If your problem is not one of the above, please show your config and provide more detail on what problem you are experiencing. (Not sure what you expect from auto-configuration, this is a very basic function that logs ERROR events to the console if log4j2 cannot find a configuration file. This will rarely be sufficient.)


Basic Log4j2 Configuration

To start using log4j2 in your project, you simply need to add the log4j-core dependency. If you’re using Maven, you can add the following dependency to your pom.xml file:

1) log4j-core/2.19.0

2)log4j-api/2.19.0


<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->

<dependency>

    <groupId>org.apache.logging.log4j</groupId>

    <artifactId>log4j-core</artifactId>

    <version>2.19.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->

<dependency>

    <groupId>org.apache.logging.log4j</groupId>

    <artifactId>log4j-api</artifactId>

    <version>2.19.0</version>

</dependency>

Out of the box, log4j2 will automatically provide a simple configuration, if you don’t explicitly define one yourself. The default configuration logs to the console at a level of ERROR level or above.

https://logging.apache.org/log4j/2.x/manual/customloglevels.html 

Standard log levels built-in to Log4J
Standard Level                                              intLevel
OFF0
FATAL100
ERROR200
WARN300
INFO400
DEBUG500
TRACE600
ALLInteger.MAX_VALUE


To start logging messages using this basic configuration, all you need to do is obtain a Logger instance using the LogManager class:

private static Logger logger = LogManager.getLogger(MyService.class);
Then you can use the logger object with methods corresponding to the log level you want:

logger.error("This is an error message");


Customizing the Log4j2 Configuration

A custom log4j2 configuration can be created either programmatically or through a configuration file.

The library supports config files written in XML, JSON, YAML, as well as the .properties format. Here, we’re going to use XML to discuss all examples primarily.

First, you can override the default configuration by simply creating a log4j2.xml file on the classpath:

Log4j2 xml


log4j2.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- Extra logging related to initialization of Log4j. Set to debug or trace if log4j initialization is failing. -->
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level - %msg%n"/> </Console> <File name="File" fileName="c:\\temp\\log4.log" append="true"> <PatternLayout> <Pattern>%d{HH:mm:ss.SSS} [%t] %-5level - %msg%n</Pattern> </PatternLayout> </File> </Appenders> <Loggers> <Logger name="com.sdetadda" level="info" additivity="true"> <AppenderRef ref="Console"/> </Logger> <Root level="info"> <AppenderRef ref="File"/> </Root> </Loggers> </Configuration>

Let’s have a closer look at the tags used in this simple configuration:

Configuration: the root element of a log4j2 configuration file; the status attribute represents the level at which internal log4j events should be logged

Appenders: this element contains a list of appenders; in our example, an appender corresponding to the System console is defined

Loggers: this element contains a list of Logger instances. The Root element is a standard logger that outputs all messages

It’s important to understand that the Root logger is mandatory in every configuration. As discussed, if you don’t provide one, it will automatically be configured by default with a Console appender and the ERROR log level.

Additivity is set to true by default, that is children inherit the appenders of their ancestors by default.

log4j2.properties 

1) With console appender

# Set to debug or trace if log4j initialization is failing
status = warn
# Log files location
property.basePath = C:/temp/logs

# Name of the configuration
name = ConsoleLogDemo

# Console appender configuration
appender.console.type = Console
appender.console.name = consoleLogger
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Root logger level
rootLogger.level = debug

# Root logger referring to console appender
rootLogger.appenderRef.stdout.ref = consoleLogger

2) With file and console appenders 

name=PropertiesConfig
property.filename = logs
appenders = console, file

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n

appender.file.type = File
appender.file.name = LOGFILE
appender.file.fileName=${filename}/logs.log
appender.file.layout.type=PatternLayout
appender.file.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n

rootLogger.level = debug
rootLogger.appenderRefs = file
rootLogger.appenderRef.stdout.ref = LOGFILE


Why Use Log4j in Selenium?

Use Log4j logging framework in Selenium for the following reasons:

1) Log4j logging framework can help in debugging applications easily. With different log levels, it becomes easier to sort information based on categories.
2) The logging framework is open source and can help in logging in different log levels and suppress logs in different environments – which ultimately improves application performance.
3) The framework produces better output in general.
4) With three components and clarity in usage, it becomes easier to use and configure log4j in Selenium.
5) The setup is easy and free of cost, making it more accessible for faster debugging.
6) Log4j is a great logging framework to use in Selenium. Its robust design and clear components make it easier to track, monitor, and debug automation tests by keeping logs. 
7) Not only does debugging become easier, but Log4j also offers the ability to continue logging in different levels in both testing and production environments without creating any hassles.


TestLogger.java

package log4jdemo;

import java.time.Duration;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import io.github.bonigarcia.wdm.WebDriverManager;

public class TestLogger {

public static WebDriver driver;
public static Logger log;
public static void main(String[] args) {

log= LogManager.getLogger(TestLogger.class);

WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
driver.get("https://www.myntra.com/");
log.info("Logged into Myntra");
try {
boolean text= driver.findElement(By.xpath("//a[contains(text(),'Women')]")).isDisplayed();
}
catch(Exception e) {
System.out.print(e.getMessage());
log.error("Exception occured", new Exception("Element Not Found"));
}
finally {
driver.quit();
log.info("Quitting the driver");
}
}
}



-Raghavendra Mishra

Comments

Popular posts from this blog

Building Cucumber BDD Framework from Scratch using Selenium and TestNG