In any test automation framework, an effective logging strategy plays a crucial role in improving maintainability and debugging efficiency. Well-structured logs provide valuable insights into test execution, making it easier to analyze failures and understand test behavior.
Below are some key reasons why logging is essential:
- Facilitates efficient debugging and issue resolution.
- Enhances test reports by providing detailed step-by-step execution insights.
Do We Need a Third-Party Logging Library?
Java provides built-in logging utilities through the System.out
class, which allow logs
to be printed to the console or written to files. However, achieving advanced logging features such
as rolling file logs, HTML report integration, or structured logging requires extensive custom
implementation. To simplify this, third-party libraries like SLF4J, Log4J, or Logback
can be leveraged.
Among these, Log4J is widely adopted in test automation projects. This article focuses on configuring Log4J in a Cucumber-based test automation framework to implement an efficient logging strategy.
Aspect-Oriented Programming (AOP) and Logging
Logging is not a core business requirement but a cross-cutting concern. To maintain clean code architecture, it is essential to separate logging logic from business logic. This ensures better maintainability and modularity.
Implementing a Custom Log Appender
To seamlessly integrate logs into Cucumber reports, a custom Log4J appender can be created. Below is
an implementation of a CucumberLogAppender
that captures log messages and associates
them with the respective Cucumber scenario.
import com.hema.acs.api.bags.test_data_beans.ScenarioContext;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
@Plugin(
name = "CucumberLogAppender",
category = Core.CATEGORY_NAME,
elementType = Appender.ELEMENT_TYPE)
public class CucumberLogAppender extends AbstractAppender {
protected CucumberLogAppender(String name, Filter filter) {
super(name, filter, null, true, Property.EMPTY_ARRAY);
}
@PluginFactory
public static CucumberLogAppender createAppender(@PluginAttribute("name") String name, @PluginElement("Filter") Filter filter) {
return new CucumberLogAppender(name, filter);
}
@Override
public void append(LogEvent event) {
ScenarioContext.getInstance().getCurrentScenario().log(event.getMessage().getFormattedMessage());
}
}
Configuring Log4J2 for the Framework
The following Log4J2 JSON configuration demonstrates how to set up different logging
appenders, including console logging, rolling file logging, and our custom
CucumberLogAppender
:
{
"configuration": {
"status": "error",
"name": "JSONConfigDemo",
"packages": "com.hemasundar.test_automation.test_automation_api",
"ThresholdFilter": {
"level": "debug"
},
"CustomLevels": {
"CustomLevel": {
"name": "NAME_TEST",
"intLevel": "50"
}
},
"appenders": {
"Console": {
"name": "STDOUT",
"PatternLayout": {
"pattern": "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"
}
},
"CucumberLogAppender": {
"name": "STDOUT",
"PatternLayout": {
"pattern": "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"
}
},
"RollingFile": {
"name": "File",
"fileName": "target/surefire-reports/log.log",
"filePattern": "logs/backup-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz",
"PatternLayout": {
"pattern": "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"
},
"Policies": {
"SizeBasedTriggeringPolicy": {
"size": "100 MB"
}
},
"DefaultRolloverStrategy": {
"max": "10"
}
}
},
"loggers": {
"root": {
"level": "debug",
"AppenderRef": [
{
"ref": "STDOUT",
"level": "info"
},
{
"ref": "File",
"level": "info"
}
]
}
}
}
}
Utilizing Logging in Code
Once Log4J is configured, logging can be seamlessly integrated into the code. The following example demonstrates logging scenario details before execution.
import com.hema.acs.api.bags.api_requests.DedomenaApis;
import com.hema.acs.api.bags.test_data_beans.ScenarioContext;
import com.hema.acs.api.bags.utils.CustomLogging;
import io.cucumber.java.*;
import lombok.extern.log4j.Log4j2;
import java.util.ArrayList;
import java.util.List;
/**
* @author hemasundarpenugonda
*/
@Log4j2
public class ServiceHooks {
@Before
public void printScenarioDetails(Scenario scenario) {
log.info(scenario.getUri());
log.info(scenario.getName());
}
}
Conclusion
A well-structured logging strategy in a test automation framework enhances debugging capabilities and improves test reporting. By integrating Log4J with Cucumber, we can capture meaningful logs, ensure better traceability, and make troubleshooting more efficient. Implementing a custom log appender allows direct integration with Cucumber reports, making logs more relevant and useful for test analysis.
By following the principles of Aspect-Oriented Programming (AOP), we maintain a clean separation of concerns, ensuring that logging does not interfere with business logic. Implementing such strategies makes test automation frameworks more efficient, scalable, and maintainable.