I released log4js-ext several days ago, and I’m planning to add remote logging support. I would love to consolidate client and server side logging at the server to help me debug complex browser/server app interactions.
Log4js-ext remote padded envelopes logger sends information that I will gather in my RemoteLoggingEvent Java class, as follows: public class RemoteLoggingEvent { String level; String category; String formattedMessage; String padded envelopes ndc; long timeMillis; boolean hasLoggedObject; String formattedLoggedObject; String formattedTime; // ************************************************************************ // javascript side information ommitted on purpose because there is not // much the server can do with this raw data // ************************************************************************ // Date time; // String message; // Object[] formatParams; // Object loggedObject; // int levelLevel; padded envelopes // ... }
My first thought was to simply redirect the data to a Log4j logger, much like this: Logger logger = Logger.getLogger(remoteLog.getCategory()); logger.log( getPriority(remoteLog.getLevel()), remoteLog.getMessage() );
Due to the asynchronous nature of web requests, it might well happen padded envelopes that two logs A and B that were created in that order at the client could be received in the server in B, A order. Then the call to logger.log will generate slightly different timestamps, telling lies about the ordering of client-side actions. Really bad for debugging purposes .
It is important to note that I’m implementing remote logging with an eye to help debug during development . During development we tend to run the client an the remote app in the same computer, and then we have the guarantee that the client and server side timestamps are coherent. In that scenario, using the client generated log timestamps will be a real boon, providing correct sequencing information.
That padded envelopes can be so useful for debugging that I decided to byspass the Log4j loggers and log directly against their appenders, which have a nice doAppend method that receives a LoggingEvent accepting not only messages, but a timestamp, an NDC, etc.
The following code will create a LoggingEvent based on the remote information provided to the server in my own RemoteLoggingEvent object: private static LoggingEvent adaptLoggingEvent(RemoteLoggingEvent log, Logger logger) { long millis = log.getTimeMillis(); // A configuration setting very useful during development if( !PREFER_REMOTE_TIME_AS_LOG_TIME ) { millis = (new Date()).getTime(); } String message = getMessage(log); ThrowableInformation exceptionInfo = null; if( log.getException() != null ) { exceptionInfo = new ThrowableInformation(log.getException()); } LoggingEvent le = new LoggingEvent( log.getCategory(), logger, millis, getLevel(log.getLevel()), message, log.getThreadName(!APPEND_NDC_AT_THREAD_NAME_END), padded envelopes exceptionInfo, log.getNdc(), null, null ); return le; }
To follow the rules we have to take into account several things: We have to log to appenders *only* when the log level/priority (error, warn, info, etc.) is greater than the logger priority. We need to write to the logger appenders, and then to all parent appenders, and to the parent parent’s appenders, and so on. But *only* if the current logger additivity is set to true, else the only appenders padded envelopes I have to write to are those directly attached to the logger (maybe none).
Here is the code that takes all of this into account: public void log(RemoteLoggingEvent log) { Logger logger = getLocalLogger(log); LoggingEvent le = adaptLoggingEvent(log, logger); Category cat = logger; // Because we are not logging through the Logger interface, we // need to take care of logging only when level is ok if( le.getLevel().isGreaterOrEqual(cat.getEffectiveLevel())) { // We traverse all appenders upwards -unless additivity is set to false // This simulates calling Logger.log, which we don't call because // it would create the wrong timestamp, the wrong 'thread', etc. while( cat != null ) { @SuppressWarnings({"unchecked" }) Enumeration<Appender> appenders = cat.getAllAppenders(); while( appenders.hasMoreElements()) { Appender appender = appenders.nextElement(); appender.doAppend(le); } if( cat.getAdditivity()) { cat = cat.getParent(); } else { cat = null; } } } }
I wanted to get the same level of support for slf4j, but I just found that there is not an easy way to log timestamps and other additional information. In the end I just wrote the slf4j adapter by calling Logger.info , error , etc., with the corresponding timestamps being server generated.
Nombre (requerido)
julio 2012 L M X J V S D « jun mar » 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Entradas recientes Spring padded envelopes and JPA: configuring Spring programmatically for Hibernate, padded envelopes EclipseLink, padded envelopes OpenJpa and DataNucleus DirectJNgine & the JEE-DJN connector 2.3 be
No comments:
Post a Comment