java logging writing multiple times for each thread in polling system - java

threading polling system in java. and i have included logging for each thread separately. but the instance of fileappender not getting closed with thread. so in the next poll when thread comes there are two instance of fileappender so it is logging two times and again in next poll its logging three time and so on...
here is my code
public void run()
{
String parent = "Autobrs", name = "Pool";
name = name + poolId;
String loggerName = parent + "." + name;
Logger log = Logger.getLogger(loggerName);
DailyRollingFileAppender fileApp = new DailyRollingFileAppender();
fileApp.setName("Autobrs." + loggerName + "_FileAppender");
fileApp.setFile("c:\\java\\"+ name+".log");
fileApp.setLayout(new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} (%0F:%L) %3x - %m%n"));
fileApp.setThreshold(Level.toLevel("debug"));
fileApp.setAppend(true);
fileApp.activateOptions();
log.addAppender(fileApp);
}
and i don't know how to handle this.
pls help..

Since FileAppender inherits close() from Writeappender as stated here, use
fileApp.close()

String parent = "Autobrs", name = "Pool";
name = name + poolId;
String loggerName = parent + "." + name;
Logger log = Logger.getLogger(loggerName);
You might have memory leak here. Logger.getLogger() will keep a reference to all loggers it created.
Best practice would be to have one file appender configured by a main thread and each run() can log the poolId (or whatever) in its message.
Have multiple threads writing to a common logger & appender.

Related

logback: add a new appender programmatically and GLOBALLY as in logback.xml

As other posts show (e.g. this here) it's possible to add an appender via code instead of defining it in logback.xml
Problem: the program, as usual, uses a lot different Logger instances in several java classes. All of them instanciated like:
static final Logger logger = (Logger) LoggerFactory.getLogger(SomeClassName.class);
If I now add a new appender canonically (is it canonically?) like....
Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
LoggerContext lc = rootLogger.getLoggerContext();
PatternLayoutEncoder ple = new PatternLayoutEncoder();
ple.setPattern("%date{ISO8601} %level ${PID} %-32c{32} [%12t]: %message%n");
ple.setContext(lc);
ple.start();
SizeAndTimeBasedRollingPolicy<ILoggingEvent> rp = new SizeAndTimeBasedRollingPolicy<>();
rp.setContext(lc);
rp.setMaxHistory(25);
rp.setMaxFileSize(FileSize.valueOf("200KB"));
rp.setTotalSizeCap(FileSize.valueOf("1GB"));
rp.setFileNamePattern(
"logs/archived/" + Zeb2JiraRESTLoader.class.getSimpleName() + ".%d{yyyy-MM-dd}.%i.log.zip");
RollingFileAppender<ILoggingEvent> rfa = new RollingFileAppender<>();
rfa.setFile("logs/" + Zeb2JiraRESTLoader.class.getSimpleName() + ".log");
rfa.setName("FILE_APPENDER");
rfa.setContext(lc);
rfa.setEncoder(ple);
rfa.setRollingPolicy(rp);
rp.setParent(rfa);
rp.start();
rfa.start();
rootLogger.addAppender(rfa);
rootLogger.setLevel(Level.INFO);
rootLogger.setAdditive(true);
rootLogger.iteratorForAppenders().forEachRemaining(System.out::println);
// leads to console output:
// ch.qos.logback.core.ConsoleAppender[STDOUT] <-- defined in logback.xml
// ch.qos.logback.core.rolling.RollingFileAppender[FILE_APPENDER]
// logger is the logger of this class
logger.iteratorForAppenders().forEachRemaining(System.out::println);
// leads to NO console output at all. List is empty
...the unwanted behaviour is:
All Logger instances log into the Appender[STDOUT] defined in the logback.xml file
Only logs from the very class, where the new Appender has been added (see code above), log into the newly defined appender.
Wanted behaviour: All Logger instances shall log into both Appenders, the first one defined in the logback.xml file and the second one, defined by the java code above.

log4j2 with pax-logging: can't use values from StructuredDataMessage

I'm using pax-logging-api along with pax-logging-log4j2 for logging from my OSGi bundles. I would like to utilize Log4J2's StructuredDataMessage (using EventLogger) to write some messages to a database. However, I'm unable to read the values I put into the StructuredDataMessage from the appenders when using Pax Logging.
The following works in a non-OSGi project using the Log4J2 libraries directly:
log4j2.properties:
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %m%n
appender.event.type = Console
appender.event.name = event
appender.event.layout.type = PatternLayout
appender.event.layout.pattern = %marker ${sd:id} ${sd:testKey} %n %m%n
rootLogger.level = debug
rootLogger.appenderRef.console.ref = STDOUT
logger.event.name = EventLogger
logger.event.level = debug
logger.event.appenderRef.console.ref = event
logger.event.additivity = false
Test.java:
public class Test {
private static final Logger LOGGER = LogManager.getLogger(Test.class);
public static void main(String[] args) {
StructuredDataMessage msg = new StructuredDataMessage("1", "message", "event");
msg.put("testKey", "testValue");
LOGGER.info(msg);
EventLogger.logEvent(msg);
}
}
Output:
1 testValue event [1 testKey="testValue"] message
EVENT 1 testValue
event [1 testKey="testValue"] message
Note that the event appender properly dereferenced the sd keys from the StructuredDataMessage.
However, the following does not work in OSGi with pax-logging:
org.ops4j.pax.logging.cfg:
log4j2.appender.console.type = Console
log4j2.appender.console.name = STDOUT
log4j2.appender.console.layout.type = PatternLayout
log4j2.appender.console.layout.pattern = %m%n
log4j2.appender.event.type = Console
log4j2.appender.event.name = event
log4j2.appender.event.layout.type = PatternLayout
log4j2.appender.event.layout.pattern = %marker \$\\\{sd:id\} \$\\\{sd:testKey\} %n %m%n
log4j2.rootLogger.level = debug
log4j2.rootLogger.appenderRef.console.ref = STDOUT
log4j2.logger.event.name = EventLogger
log4j2.logger.event.level = debug
log4j2.logger.event.appenderRef.console.ref = event
log4j2.logger.event.additivity = false
Test.java:
public class Test implements BundleActivator {
private static final Logger LOGGER = LogManager.getLogger(Test.class);
#Override
public void start(BundleContext context) throws Exception {
StructuredDataMessage msg = new StructuredDataMessage("1", "message", "event");
msg.put("testKey", "testValue");
LOGGER.info(msg);
EventLogger.logEvent(msg, Level.INFO);
}
#Override
public void stop(BundleContext context) throws Exception {
}
}
Output:
event [1 testKey="testValue"] message
EVENT ${sd:id} ${sd:testKey}
event [1 testKey="testValue"] message
Is there a trick to getting this to work in pax-logging? I am able to access values from the MDC using \$\\\{ctx:key\} when applicable, so I'm assuming the syntax is similar. I've also tried using the lookups in patterns for RoutingAppender, FileAppender, etc. to no avail.
Thanks in advance!
Edit: I'm using the latest version of pax-logging-api and pax-logging-log4j2 (1.11.3)
OK, it's not yet a definitive answer - simply comment is too short to describe what happens.
The stack trace with your call is:
"pipe-restart 238#10666" prio=5 tid=0xc3 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at org.ops4j.pax.logging.log4j2.internal.PaxLoggerImpl.doLog0(PaxLoggerImpl.java:354)
at org.ops4j.pax.logging.log4j2.internal.PaxLoggerImpl.doLog(PaxLoggerImpl.java:337)
at org.ops4j.pax.logging.log4j2.internal.PaxLoggerImpl.inform(PaxLoggerImpl.java:233)
at org.ops4j.pax.logging.internal.TrackingLogger.inform(TrackingLogger.java:209)
at org.ops4j.pax.logging.log4jv2.Log4jv2Logger.logMessage(Log4jv2Logger.java:162)
at org.apache.logging.log4j.spi.AbstractLogger.log(AbstractLogger.java:2102)
at org.apache.logging.log4j.spi.AbstractLogger.tryLogMessage(AbstractLogger.java:2190)
at org.apache.logging.log4j.spi.AbstractLogger.logMessageTrackRecursion(AbstractLogger.java:2144)
at org.apache.logging.log4j.spi.AbstractLogger.logMessageSafely(AbstractLogger.java:2127)
at org.apache.logging.log4j.spi.AbstractLogger.logIfEnabled(AbstractLogger.java:1828)
at org.apache.logging.log4j.EventLogger.logEvent(EventLogger.java:56)
at grgr.test.ActivatorLogging.start(ActivatorLogging.java:39)
...
org.ops4j.pax.logging.log4jv2.Log4jv2Logger.logMessage() is bridge between logging facade and logging backend.
Remember - with pax-logging, you can, say, use Commons Logging facade with Log4J1 backend, or Log4j2 facade (and that's what you're doing) with e.g., Logback backend.
That's why org.ops4j.pax.logging.log4jv2.Log4jv2Logger.logMessage() does this:
} else if (level.intLevel() >= Level.INFO.intLevel()) {
m_delegate.inform(paxMarker, message.getFormattedMessage(), t, fqcn);
and your structured message is changed into String event [1 testKey="testValue"] message.
Only then configured appenders are called - and the appender with layout that extract structured data can't find it, because the structured message is already converted to plain String.
These 3 lines:
at org.ops4j.pax.logging.log4j2.internal.PaxLoggerImpl.inform(PaxLoggerImpl.java:233)
at org.ops4j.pax.logging.internal.TrackingLogger.inform(TrackingLogger.java:209)
at org.ops4j.pax.logging.log4jv2.Log4jv2Logger.logMessage(Log4jv2Logger.java:162)
do the crossing from pax-logging-api (facade) through TrackingLogger (bridge) to pax-logging-log4j2 (backend) losing structured information in between.
I've created https://ops4j1.jira.com/browse/PAXLOGGING-302 and hope to do something about it soon.
EDIT1
The key is that in org.apache.logging.log4j.core.lookup.StructuredDataLookup#lookup(), this condition is true:
if (event == null || !(event.getMessage() instanceof StructuredDataMessage)) {
return null;
}
EDIT2
I've just fixed https://ops4j1.jira.com/browse/PAXLOGGING-302 and this test proves it works:
Logger logger = LogManager.getLogger("my.logger");
logger.info(new StructuredDataMessage("1", "hello!", "typeX").with("key1", "sd1"));
logger.info(new StringMapMessage().with("key1", "map1"));
List<String> lines = readLines();
assertTrue(lines.contains("my.logger/org.ops4j.pax.logging.it.Log4J2MessagesIntegrationTest typeX/sd1 [INFO] typeX [1 key1=\"sd1\"] hello!"));
assertTrue(lines.contains("my.logger/org.ops4j.pax.logging.it.Log4J2MessagesIntegrationTest ${sd:type}/map1 [INFO] key1=\"map1\""));
the configuration is:
log4j2.appender.console.type = Console
log4j2.appender.console.name = console
log4j2.appender.console.layout.type = PatternLayout
log4j2.appender.console.layout.pattern = %c/%C ${sd:type}/${map:key1} [%p] %m%n
log4j2.rootLogger.level = info
log4j2.rootLogger.appenderRef.file.ref = console
(you have to use the escape sequences you've used, if configuring via etc/org.ops4j.pax.logging.cfg in Karaf).

log4j => add caller file and line number to log

I use following pattern [%file:%line] %msg%n to output file + number to my log.
I as well use a simple wrapper class, that I call L.java. Now it does not make sense to output [L.java:74] Message... to my log. Instead, I would like to output the calling file name and line number...
Is that somehow possible with log4j?
The PatternLayout are slighty different between log4j 1.x and 2.x, and I do not know which version are you using, but I think you can't achieve this by configuration in neither versions.
You can achieve that programmatically (but this is going to affect your performance), I think in your L.java method you will have to use a method like:
private Logger logger = getYourLoggerAsYouAreCurrentlyDoing();
public enum LogLevel { INFO,DEBUG, ERROR, ETC }
void log(String msg, LogLevel level) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
String callerClass = stackTraceElements[1].getClassName();
String callerLine = "" + stackTraceElements[1].getLineNumber();
String msg = callerClass + ":" + callerLine + "-" + msg;
switch(LogLevel) {
case INFO: logger.info(msg); break;
case DEBUG: logger.debug(msg); break;
//etc.
}
}
And in case another method with the Throwable argument to log stacktraces.

How to print customized prefix in log4j

By default, log4j will print the class name as the prefix of log. Now my case is that I have multiple instances of class A in one JVM, and there's one log in this class. I'd like the log as following to allow me know which instance is printing this line of log ? How can I achieve this ?
2014-09-09 13:07:08,512 INFO com.myexample.A(id1)
2014-09-09 13:07:08,514 INFO com.myexample.A(id2)
I think you can not do it by settings in log4j.properties file. See possible variants here.
But you can solve the problem like this:
class A {
Logger LOGGER;
A(String id) {
LOGGER = Logger.getLogger(getClass() + "(" + id + ")");
}
void myMethod() {
LOGGER.info("Hello!");
}
}
Easiest way is to add desired ID to your log message:
if (logger.isDebugEnabled()) {
int id = this.hashCode(); // Invoke ID getter method
logger.debug( String.format("[ %d ] My log message with ID", id) );
}

Remove timestamp from GWT logger

I'd like to remove the timestamp from GWT logging output on the console.
What's the simplest way to do this? Ideally, in the .gwt.xml configuration would be great.
Here is an example output currently with the timestamp:
Wed Mar 21 08:23:57 EDT 2012 Job
FINE: Job: 'updater': end
EDIT: I am only interested in the client side.
This logging capability is not really configurable. You need to write your own formatter:
call this at the beginning of onModuleLoad():
Handler[] handlers = Logger.getLogger("").getHandlers();
for(Handler h : handlers){
h.setFormatter(new MyCustomLogFormatter());
}
And here is an example of a formatter:
public class MyCustomLogFormatter extends TextLogFormatter{
private static DateTimeFormat timeFormat = DateTimeFormat.getFormat("HH:mm:ss.SSS");
public MyCustomLogFormatter() {
super(true);
}
#Override
public String format(LogRecord event) {
StringBuilder message = new StringBuilder();
message.append(getRecordInfo(event, " "));
message.append(event.getMessage());
message.append(getStackTraceAsString(event.getThrown(), "\n", "\t"));
return message.toString();
}
#Override
protected String getRecordInfo(LogRecord event, String newline) {
Date date = new Date(event.getMillis());
StringBuilder s = new StringBuilder();
s.append(timeFormat.format(date));
s.append(" GWT ");
s.append(event.getLevel().getName());
String loggerName = event.getLoggerName();
String[] split = loggerName.split("\\.");
s.append(" ");
s.append(split[split.length-1]);
s.append(newline);
s.append(": ");
return s.toString();
}
}
More: http://code.google.com/webtoolkit/doc/latest/DevGuideLogging.html
The accepted answer shows how to customize GWT log messages. The example is longer than necessary, though. In case anyone wants to just remove the timestamp (the original question), here is a shorter snippet:
Handler[] handlers = Logger.getLogger("").getHandlers();
for (Handler h : handlers) {
h.setFormatter(new TextLogFormatter(false) {
#Override
public String format(LogRecord event) {
return event.getLoggerName() + ": " +
event.getLevel().getName() + ": " +
event.getMessage();
}
});
}
Watch out for the "gotcha" of doing this in the constructor of a singleton dependency-injected class---you want to make sure GWT has had the chance to actually add handlers to the logger first.
It uses the same pattern config as log4j, see here: http://code.google.com/p/gwt-log/wiki/GettingStarted#Control_the_format_of_your_log_messages
In the gwt-log wiki it says:
Server side logging automatically detects Apache log4j, falling back
to JDK 1.4 logging
As there is a "FINE" log level in your post, it should be the second case. Either include Log4J in your classpath or check "logging.properties" from your current jdr/jre-conf directory.

Categories