4

I have an application that needs to log two different types of messages: application log messages and audit messages. Application log messages match the standard lo4j Logger perfectly, but there are several required parameters for the audit log.

I think I need to wrap log4j to add the additional required parameters to the debug(), info(), etc methods, but I hate the idea of wrapping log4j. Should I:

  1. Wrap log4j completely and provide my own Logger class that calls a log4j logger behind the scenese?
  2. Extend the log4j Logger class and add "audit log" methods with my required parameters?
  3. Do something even more elegant so I'm not wrapping a logging library...
palacsint
  • 28,416
  • 10
  • 82
  • 109
Brian
  • 2,375
  • 4
  • 24
  • 35

4 Answers4

4

The "message" of the error, warn, etc. methods of a log4j Logger is an arbitrary object; it doesn't need to be a string. You can create your own "message" class to contain the different parameters. Loggers can append the data differently by using a custom Layout class for the appender of the audit logger.

Nathan Ryan
  • 12,893
  • 4
  • 26
  • 37
  • That is a great idea, but I don't see a way to enforce that the developers provide the required parameters for the audit log entries. – Brian May 17 '11 at 12:01
  • No, there's no (easy) way to enforce the nature of object of the log message, other than failing at runtime (which is probably not what you want). But much of logging is about best practice anyway, so I don't see much difference here with respect to logging in the general sense. – Nathan Ryan May 17 '11 at 12:24
  • Yes, if only our logging was only a "best practice" and not required in certain circumstances by our customer's requirements. I think I'm stuck wrapping log4j to add the required parameters. – Brian May 17 '11 at 14:25
  • Too bad, but it still shouldn't be too difficult. Still use the custom `Layout` as recommended above. Create an extension of `Logger` with the custom logging methods, keeping the message and the additional arguments wrapped as a separate object. The implementation of the custom logging methods can simply call the superclass versions of the standard method (`error`, etc.) *Your* versions of the standard methods can be marked with an annotation, like `Deprecated`, that will produce a warning (or even an error) at compile time. This should force the use of the custom logging methods. – Nathan Ryan May 17 '11 at 14:42
4

I think you can use log4j for your application logs creating logs like:

private final static Logger log = new Logger(MyClass.class);

And for your audit log create a specific category:

private final static Logger log = new Logger("AuditTrail");

Having a different appender you can share accross multiple classes will allow you to configure the output destination and the format as ever you want in the config file.

Hope this helps.

Alberto Zaccagni
  • 30,779
  • 11
  • 72
  • 106
Philippe
  • 446
  • 2
  • 12
1

This will work: So far this is the best solution I've got...

(credit to hellojava in LOG4J: Modify logged message using custom appender)

Create a custom Log4j Layout such as this one:

import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Level;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;

public class AuditLayout extends PatternLayout {
    // Audit Summary Map:
    // <Level, Counter>

    private static Map<Level, Integer> auditMap = new HashMap<Level, Integer>();

    public static Map<Level, Integer> getAuditMap() {
        return auditMap;
    }

    public static void setAuditMap(Map<Level, Integer> auditMap) {
        AuditLayout.auditMap = auditMap;
    }

    @Override
    public String format(LoggingEvent event) {

        if (event.getMessage() != null && event.getMessage() instanceof String) {
            // Check the message level and update the audit object accordingly:
            if (!auditMap.containsKey(event.getLevel())) {
                auditMap.put(event.getLevel(), 1); 
            } else {
                int i = auditMap.get(event.getLevel());    
                i++;
                auditMap.put(event.getLevel(), i); 
            }
        }
        return super.format(event);
    }
}

This is a very simple Layout which checks the log level and holds a summary in a Map; i.e. DEBUG > 2, INFO > 10 etc.

Set the new Layout in your log4j.properties file (use one of your appenders):

# CONSOLE:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=DEBUG
# BEFORE::: log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# AFTER:::
log4j.appender.CONSOLE.layout=com.ca.utils.AuditLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{MM/dd/yyyy HH:mm:ss} %-5p %c line: %L - %m%n

Now use your auditMap in the Layout from your Main class, based on the Logger object:

private static AuditLayout auditLayout = null;
...
if (log.getRootLogger().getAppender("CONSOLE").getLayout() instanceof AuditLayout)
    auditLayout = (AuditLayout) log.getRootLogger().getAppender("CONSOLE").getLayout();
...
System.out.println("auditLayout: " + auditLayout.getAuditMap().toString());

I hope this helps!

Community
  • 1
  • 1
Naor Bar
  • 1,991
  • 20
  • 17
0

May be you can read the api NDC/MDC of log4j. Hope that can help you.

lykm
  • 15
  • 1
  • 8