collecting logs with log4j2 v2.9 - java

i have a question regarding log4j2 with version 2.9.
Basically I want to do the same as described here (log4j), only with 2.9:
Sample log4j v1.x
I need a logger that can be called in any method in the class. This is to collect all subsequent logs recursively from a certain starting point. The collection should be able to be read out in any form later on.
Logger logger = LogManager.getLogger();
public void meth1(){
StringWriter/ List/ String or whatever
logger.add(Collector);
logger.info("Start");
this.meth2();
this.meht3();
logger.info("Stop");
=> do something with the collected logs
}
public void meht2(){
logger.info("meth2: add Collection");
}
public void meth3(){
logger.info("meth3: add Collection");
}
public void meht4(){
logger.info("foo");
}
as soon as the end is set in a form, the following logs should be included in the collection:
Start
meth2: add Collection
meth3: add Collection
Stop
thanks for your help

After a while, I came up with the following solution which works for me using Log4J 2.8. I've added some comments to the code to explain the different steps necessary.
It's important that the logger is requested with the same name (line marked with /* 1 */ as for which the configuration is stored (line marked with /* 2 */. This implies, that the class name cannot be used to get the logger, or that MyClass.class.getName() should be used at /*2*/.
public class LoggingTest {
public static void main(String[] args) {
// define the logger, could also be static in class
final String loggerName = "myCollectingLogger";
Logger logger = LogManager.getLogger(loggerName); /* 1 */
// the log message collector
StringWriter writer = new StringWriter();
// start adapting the logger configuration
LoggerContext ctx = LoggerContext.getContext(false);
Configuration config = ctx.getConfiguration();
// create our appender
PatternLayout layout = PatternLayout.newBuilder().withPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n").build();
WriterAppender writerAppender = WriterAppender.newBuilder().setName("writerAppender").setTarget(writer).setLayout(layout).build();
// add the appender to a LoggerConfig
AppenderRef ref = AppenderRef.createAppenderRef("writerAppender", null, null);
AppenderRef[] refs = new AppenderRef[] { ref };
LoggerConfig loggerConfig = LoggerConfig.createLogger(false, Level.INFO, "example", null, refs, null, config, null);
loggerConfig.addAppender(writerAppender, null, null);
// enable the LoggerConfig in the LoggerContext
config.addLogger(loggerName, loggerConfig); /* 2 */
ctx.updateLoggers();
// use the logger:
logger.info("Start");
logger.warn("foo bar");
logger.error("relax, it's just a test");
logger.info("Stop");
System.out.println("--- the collected log messages: ---");
System.out.println(writer.toString());
}
}

thanks, I've adjusted it to my needs:
public static StringWriter createStringWriter(String classname){
StringWriter stringWriter = new StringWriter();
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
PatternLayout layout = PatternLayout.newBuilder()
.withPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} %level [%t] [%c] [%M] [%l] - %msg%n").build();
WriterAppender writerAppender = WriterAppender.newBuilder()
.setName(classname + "writeLogger")
.setTarget(stringWriter)
.setLayout(layout)
.build();
writerAppender.start();
config.addAppender(writerAppender);
LoggerConfig loggerConfig = config.getLoggerConfig(classname);
loggerConfig.addAppender(writerAppender, null, null);
ctx.updateLoggers();
return stringWriter;
}
public static void removeStringWriter(String classname){
final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
final Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(classname);
loggerConfig.removeAppender(classname + "writeLogger");
ctx.updateLoggers();
}

Related

Log4j2 fatjar programmatically config

I'm trying to get log4j2 work within a fatjar.
When running in intellij every works fine, but when building my fatjar an running it like
java -jar dx.jar
only errors are logged to console, and nothing to the rollingfile. It seams ignoring my config.
Configuration:
String logFile = logPath + "dxpos.log";
String logFilePattern = logPath + "dxpos-%d{yyyy-MM-dd}.log";
LogManager.shutdown();
var builder = ConfigurationBuilderFactory.newConfigurationBuilder();
var layout = builder.newLayout("PatternLayout")
.addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss} %-5p [%t] %c: %m%n");
var console = builder.newAppender("console", "Console")
.addAttribute("target", "SYSTEM_OUT")
.addAttribute("immediateFlush", true)
.add(layout);
var triggeringPolicy = builder.newComponent("Policies")
.addComponent(builder.newComponent("TimeBasedTriggeringPolicy"))
.addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M"));
var rollingFile = builder.newAppender("rollingFile", "RollingFile")
.addAttribute("fileName", logFile)
.addAttribute("filePattern", logFilePattern)
.addAttribute("immediateFlush", true)
.addAttribute("append", true)
.addComponent(triggeringPolicy)
.add(layout);
var rootLogger = builder.newRootLogger(Level.INFO)
.add(builder.newAppenderRef("console"))
.add(builder.newAppenderRef("rollingFile"));
builder.add(console);
builder.add(rollingFile);
builder.add(rootLogger);
Configurator.initialize(builder.build());
After searching a file I a switch from programmatically config method to XML
To getting it to work you have to declare the logger
public static Logger logger = null;
Add some variables like that
MDC.put("logFile", logFile);
MDC.put("LogFilePattern", logFilePattern);
And create the logger
logger = LoggerFactory.getLogger(MainForm.class);

Initiate Log4j2 RollingFileAppender in Java

I'm using log4j2.
I want to create a RollingFileAppender which rotates the log file on a daily basis.
The name of the logfile is unknown until the application has started (the logfile name is assembled from the application config).
That is why I need to add a RollingFileAppender at runtime.
I have the following code:
public static final ConfigurationBuilder<BuiltConfiguration> BUILDER = ConfigurationBuilderFactory.newConfigurationBuilder
public void initFileLoggerWithFilePattern(final String pattern) {
final LoggerComponentBuilder logger = BUILDER.newLogger("FileLogger", Level.DEBUG);
final AppenderComponentBuilder appender = createFileAppenderWithFilePattern(pattern);
BUILDER.add(appender);
logger.add(BUILDER.newAppenderRef("RollingFileAppender"));
BUILDER.add(logger);
Configurator.initialize(BUILDER.build());
}
public AppenderComponentBuilder createFileAppenderWithFilePattern(final String pattern) {
final AppenderComponentBuilder acb = BUILDER.newAppender("RollingFileAppender", "RollingFile");
acb.addAttribute("fileName", pattern);
acb.addAttribute("filePattern", pattern);
acb.addComponent(createPatternLayout());
acb.addComponent(createTimeBasedTriggeringPolicy());
return acb;
}
public LayoutComponentBuilder createPatternLayout() {
final LayoutComponentBuilder lcb = BUILDER.newLayout("PatternLayout");
lcb.addAttribute("pattern", "%d{yyyy-MM-dd HH:mm:ss.SSS}{GMT}Z %m");
return lcb;
}
public ComponentBuilder createTimeBasedTriggeringPolicy() {
final ComponentBuilder policies = BUILDER.newComponent("Policies");
final ComponentBuilder policy = BUILDER.newComponent("TimeBasedTriggeringPolicy");
policies.addComponent(policy);
return policies;
}
The problem is that this code changes absolutely nothing. No appender as well as no Logger is being added to the configuration. The "FileLogger" that was created programmatically is not available.
I used this code to print the loggers and Appenders after executing the code above.
private void printLog4jConfig() {
final LoggerContext context = (LoggerContext) LogManager.getContext(false);
final Configuration config = context.getConfiguration();
// Print appenders
for(Appender app : config.getAppenders().values()) {
System.out.println(app.getName());
}
// Print Loggers and their Appenders
for(LoggerConfig lc : config.getLoggers().values()) {
System.out.println(lc);
for(Appender app : lc.getAppenders().values()) {
System.out.println(" " + app);
}
}
}
Output:
Appenders
-------------
STDOUT
Loggers
-------------
root
STDOUT
Console
STDOUT
My Question:
What is wrong with my code? Why is my Appender as well as my Logger not added? Respectively why is the configuration not being refreshed / updated ?
How can I add a RollingFileAppender as well as a logger to the log4j2 configuration during runtime?
I found the solution to my problem.
Instead using
Configurator.initialize(BUILDER.build())
I had to use
Configurator.reconfigure(BUILDER.build());
This reloaded the configuration and i was able to see and use my appender and logger.

How to get all the appender names in logback

How to get a list of all the appender names which have been set in a List in Java while using logback.
The following code will gather all appenders in the current LoggerContext:
private Map<String, Appender<ILoggingEvent>> getAppendersMap() {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Map<String, Appender<ILoggingEvent>> appendersMap = new HashMap<>();
for (Logger logger : loggerContext.getLoggerList()) {
Iterator<Appender<ILoggingEvent>> appenderIterator = logger.iteratorForAppenders();
while (appenderIterator.hasNext()) {
Appender<ILoggingEvent> appender = appenderIterator.next();
if (!appendersMap.containsKey(appender.getName())) {
appendersMap.put(appender.getName(), appender);
}
}
}
return appendersMap;
}
You can then list the names like so:
Map<String, Appender<ILoggingEvent>> appendersMap = getAppendersMap();
for (String key : appendersMap.keySet()) {
logger.info("appender name = {}", key);
}
Thanks #glytching.
I found a shorter answer:
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
for (ch.qos.logback.classic.Logger logger : context.getLoggerList()) {
for (Iterator<Appender<ILoggingEvent>> index = logger.iteratorForAppenders(); index.hasNext();) {
Appender<ILoggingEvent> appender = index.next();
}
}

Programmatically log into multiple files using log4j

I want to write logs in separate files Programmatically,
public class ProgLoggerMultipaleFiles {
HashMap<LogCategory, Logger> myLogHashMap = new HashMap<LogCategory, Logger>();
public ProgLoggerMultipaleFiles() {
JLogger jlog = new JLogger();
jlog.startFileLog("mylog");
jlog.startFileLog("HC");
jlog.startFileLog("MC");
jlog.startFileLog("DC");
myLogHashMap.put(LogCategory.mylog, Logger.getLogger("mylog"));
myLogHashMap.put(LogCategory.HC, Logger.getLogger("HC"));
myLogHashMap.put(LogCategory.MC, Logger.getLogger("MC"));
myLogHashMap.put(LogCategory.DC, Logger.getLogger("DC"));
String parameter = "Hello";
log(LogCategory.mylog,Priority.DEBUG,"This is debug : " + parameter);
log(LogCategory.mylog,Priority.INFO,"This is info : " + parameter);
log(LogCategory.mylog,Priority.WARN,"This is warn : " + parameter);
log(LogCategory.mylog,Priority.ERROR,"This is error : " + parameter);
log(LogCategory.mylog,Priority.FATAL,"This is fatal : " + parameter);
log(LogCategory.HC,Priority.FATAL,"HC");
log(LogCategory.MC,Priority.FATAL,"MC");
log(LogCategory.DC,Priority.FATAL,"DC");
}
public void log(LogCategory category,Priority priority,String msg){
myLogHashMap.get(category).log(priority, msg);
}
public enum LogCategory{
mylog,HC,MC,DC
}
public static void main(String[] args) {
ProgLoggerMultipaleFiles plog = new ProgLoggerMultipaleFiles();
}
}
And I initialize loggers and Appenders in this class,
public class JLogger {
public JLogger() {
startConsolLog();
}
public void startConsolLog(){
ConsoleAppender console = new ConsoleAppender(); //create appender
//configure the appender
String PATTERN = "%d [%p|%c|%C{1}] %m%n";
console.setLayout(new PatternLayout(PATTERN));
console.setThreshold(Level.FATAL);
console.activateOptions();
//add appender to any Logger (here is root)
Logger.getRootLogger().addAppender(console);
}
public void startFileLog(String fileName){
FileAppender fa = new FileAppender();
fa.setName(fileName);
fa.setFile(fileName+".log");
fa.setLayout(new PatternLayout("%d %-5p [%c{1}] %m%n"));
fa.setThreshold(Level.DEBUG);
fa.setAppend(true);
fa.activateOptions();
//add appender to any Logger (here is root)
Logger.getRootLogger().setAdditivity(false);
Logger.getRootLogger().addAppender(fa);
//repeat with all other desired appenders
}
}
When I run this code creates 4 diff files but all messages are logged into all files.
Thanks in advance.
Log4j Loggers work like a tree. When you get Loggers like LogManager.getLogger(MyClass.class) and MyClass is in org.my.company namespace, log4j will traverse the configuration and look for "org.my.company.MyClass" then "org.my.company" and so on until it finds one. If not it uses the rootlogger.
So you can "tap" that tree with appenders. For example you want all classes in "org.my.company.api" to log into a special file: Configure a logger named "org.my.company.api" , add that fileappender to it and get the Loggers with *.class.
In your case it's a bit different. You get Loggers with a specific name that is probably not in the namespace tree. So if there is no logger with that special name, the root logger is used. Thus all messages go everywhere.
So what you have to do is configure not only appenders, but also Loggers with those specific names - "mylog" for example and add the respective appender to only that logger.
Your tree:
root ← Appender Console, mylog, HC, MC, ...
but you need actually:
root ← Appender Console
|- mylog ← Appender for mylog.log
|- MC ← Appender for MC.log
|- ...
Try:
public void startFileLog(String fileName){
FileAppender fa = new FileAppender();
fa.setName(fileName);
fa.setFile(fileName+".log");
fa.setLayout(new PatternLayout("%d %-5p [%c{1}] %m%n"));
fa.setThreshold(Level.DEBUG);
fa.setAppend(true);
fa.activateOptions();
//add appender to any Logger (here is NOT root)
Logger.getLogger(fileName).setAdditivity(false); // messages will not go to root logger anymore!
Logger.getLogger(fileName).addAppender(fa);
//repeat with all other desired appenders
}

Configure log4j2 for use from applet

Where log4j2.xml should be placed for use in applet? Can it log both to Java Console and to files on user computer?
I placed in applet resources conf/log4j2.xml and read it from applet. Applet loads it incorrectly, so i fix the fields from the applet code:
public static Logger getLogger(Class className) {
//get logger configuration
LoggerContext loggerContext = Configurator.initialize("client", className.getClassLoader(), className.getClassLoader().getResource("conf/log4j2.xml").getFile());
Configuration configuration = loggerContext.getConfiguration();
//set root logger to desired level
LoggerConfig loggerConfig = configuration.getLoggerConfig("");
loggerConfig.setLevel(Level.INFO);
//obtain appender
Appender appender = obtainAppender(configuration);
//get logger for required class
org.apache.logging.log4j.core.Logger loggerForClass = loggerContext.getLogger(className.getName());
//associate logger for required class with just created appender
configuration.addLoggerAppender(loggerForClass, appender);
return loggerForClass;
}
private static Appender obtainAppender(Configuration configuration) {
//create appender
TriggeringPolicy[] triggeringPolicies = {OnStartupTriggeringPolicy.createPolicy(), TimeBasedTriggeringPolicy.createPolicy("5", "true"), SizeBasedTriggeringPolicy.createPolicy("5 MB")};
TriggeringPolicy triggeringPolicy = CompositeTriggeringPolicy.createPolicy(triggeringPolicies);
return RollingFileAppender.createAppender(CLIENT_LOG_PATH + FileUtils.FILE_SEPARATOR + "my_client.log",
CLIENT_LOG_PATH + FileUtils.FILE_SEPARATOR + "/$${date:yyyy-MM}/my_client-%d{MM-dd-yyyy-HH-mm}-%i.log",
"", APPLET_APPENDER,
"true", "true",
triggeringPolicy, null,
PatternLayout.createLayout("%d{dd/MM/yyyy HH:mm:ss} %-5p [%t] [%c{1}] %m%n", configuration, null, "UTF-8"),
null, "true", configuration);
}

Categories