8
votes

I have a log4j appender defined like:

log4j.logger.com.example = DEBUG, filelog

log4j.appender.filelog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.filelog.File=c:/app.log
log4j.appender.filelog.layout=org.apache.log4j.PatternLayout
log4j.appender.filelog.layout.ConversionPattern=%d | %m%n
log4j.appender.filelog.DatePattern=.dd-MM-yyyy

In my class, I get the logger like:

Log logger = LogFactory.getLog(getClass());

This works properly. I want to have a logger that always logs certain messages (not errors, but things like how long transactions took). If I write these at DEBUG or INFO, I won't see them if the log level is changed. I think I can accomplish this using another appender that writes to the same file.

Is this possible to have two appenders write to the same file? How would I get the logger instance where I want to use the normal debug appender and the transactional appender in the same class? These messages won't all be in the same package, so I can't configure a certain package to always log. Will I have to have these appenders write to different files, or can I retrieve them both in the code and have something like:

Log alwaysLogger = LogFactory.getLog(ALWAYS);
alwaysLogger.debug("This message will always be written regardless of the level set on the filelog appender");

Update I could write to two different log files if necessary, but how would I get the logger instance in my class? I don't want to configure one package/class to always use one appender over the other as the classes will have to log information/error messages and the transactional "always" messages during a normal run. Is there a way to accomplish what I need even if it write to two different log files?

4

4 Answers

10
votes

I don't think log4j really supports two appenders writing to the same file because of synchronization issues. Your best bet would be, if possible, to make a switch to slf4j using Logback as your backend logging system since Logback is log4j's successor and also supports multiple appenders writing to one file. Check out FileAppender and it's prudent mode.

Update: from your description, it seems you should also check out markers. You only need one logger instance, and you can get more fine grained control using markers, because with them you basically say "this log statement has a special purpose, and has to be logged using an appropriate appender". You can also check log appender additivity, which is usually used when you use two or more appenders for one log statement.

2
votes

As far as I'm aware, the instantiation of loggers with a package & class identifier is just a convention. You are free to create multiple logger instances in any class and give them any identifier.

Log alwaysLogger = LogFactory.getLog("a.b.c.ALWAYS");
Log sometimesLogger = LogFactory.getLog("a.b.c.SOMETIMES");

As for writing to the same file, I have not tried it but it should be possible, at least from a single thread. You may need to write your own appender rather than relying on one of the default ones.

2
votes

I got this to work by using AsyncAppenders. I had my main appender which specified a file to log to, and had my other appenders refer to it.

e.g.

<appender name="top" class="org.apache.log4j.rolling.RollingFileAppender">
    <param name="file" value="myLog.log" />
</appender>

<appender name="other" class="org.apache.log4j.AsyncAppender">
    <appender-ref ref="top" />
</appender>

<logger name="com.example.MyCLass">
    <appender-ref ref="other" />
</logger>

You can add other filters, or whatever to the other appenders. As far as I can tell with my own app, the logs seem to roll, and the appender filters work as expected.

0
votes

Yes. You can "workaround" to have 2 appenders writing to the same file.

Checkout this code + example:

Any questions welcome.