How can get correct logger line number in the following context

4.5k views Asked by At

I have spend some time to figure out a sustainable way to filter out ERROR logs from my application in real time from JBoss server. With Log4j I can easily use SocketAppender and I configured it the way it will notify ERROR logs in real time. Unfortunately I can't do this with JBoss AS 7. I can't find a SocketAppender kind of implementation on it other than a SMTPAppender. So I came up with a way to solve this. Please take a look what I have done so far.

I wrote a kind of wrapper for Logger.

A sample class of my application

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyClass {
 private Logger logger= LoggerFactory.getLogger(MyClass.class);

 public void updateData(String data){
   logger.info("updating data with {} ",data);
   try {
        // do somthing
   }catch (Exception e){
        //something went wrong
       logger.error("update fail ",e);// I want to get this message real time
    }
 }
}

Now I thought of a way to get ERROR log.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyLogger {
    private Logger logger = null;

    private MyLogger() {
    }

    public static MyLogger getLogger(Class clazz) {
        MyLogger logger = new MyLogger();
        logger.logger = LoggerFactory.getLogger(clazz);
        return logger;
    }

    public void info(String format, Object... arguments) {
        logger.info(format,arguments);
    }

    public void error(String format, Object... arguments) {
        // now i can send this message real time,
        // i am calling web service with error log by a new Thread 
        logger.error(format,arguments); // say line number 10
    }
}

MyLogger use as follows

public class MyClass {
    private static MyLogger logger= MyLogger.getLogger(MyClass.class);

    public void updateData(String data){
       logger.info("updating data with {} ",data);
       try {
            // do somthing
       }catch (Exception e){
            //something went wrong
           logger.error("update fail ",e); // say line number 14
       }
    }
}

At this point my requirement matched, But this create a new issue.

Following is the logger format configuration of JBoss

<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p (%F:%L) (%t) %s%E%n"/>

Now %F-File name will become the MyLogger class. and %L- Line number will become the line number of MyLogger which leads inaccurate logs .

part of the sample log

(MyLogger.java : 10) // but this should be (MyClass.java : 14)

Now I have change this pattern to

<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c:%L] (%t) %s%E%n"/>

This will almost work but %c-class name gives correct class as expected. But there is no way to get line number(correct line number).

After change logger paten what I get

(com.my.test.MyClass : 10) // but this should be (com.my.test.MyClass : 14)    

Here are my questions.

  1. Is there any log appender (SocketAppender or JMSAppender) work with JBoss AS7 ?(found none working solution with Jboss AS 7)
  2. Is there any better approach rather than this?
  3. Is there any way to get correct Line number using this approach?

I have already asked an question in JBossDeveloper. But still there is no comment at least.

https://developer.jboss.org/thread/249729

Any help highly appreciate.

1

There are 1 answers

1
Federico Sierra On BEST ANSWER

If you are using a version equal or higher to JBoss 7.2 (EAP 6.1), you can use the Log4j appenders as custom handlers (check this issue).

eg.

<custom-handler name="server" class="org.apache.log4j.net.SocketAppender" module="org.apache.log4j">
  <level name="INFO"/>
  <formatter>
    <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
  </formatter>
  <properties>
    <property name="RemoteHost" value="10.10.10.10"/>
    <property name="Port" value="1234"/>
    <property name="LocationInfo" value="true"/>
    <property name="ReconnectionDelay" value="10000"/>
  </properties>
</custom-handler>

In other case the only one way to make it work in JBoss AS 7 is to develop a custom java.util.logging.Handler for SocketAppender.

Take a look in Creating a custom logging handler in JBOSS as 7.1.0 Final and Custom log handlers on 7.0.1 to create your custom log handler and then delegate them to Log4jAppenderHandler.

eg (sample code):

import java.util.Collections;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.LogRecord;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.net.SocketAppender;
import org.apache.log4j.net.SyslogAppender;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;
import org.jboss.logmanager.ExtLogRecord;
import org.jboss.logmanager.log4j.handlers.Log4jAppenderHandler;


public class SocketHandler extends Handler {

    private final Log4jAppenderHandler log4j = new Log4jAppenderHandler(new SocketAppender());

    @Override
    public void publish(LogRecord record) {
        if (record == null) {
            return;
        }
        ExtLogRecord extRecord;
        if (record instanceof ExtLogRecord) {
            extRecord = (ExtLogRecord) record;
        } else {
            extRecord = ExtLogRecord.wrap(record);
        }
        if (isLoggable(record)) {
            LoggingEvent event = new LoggingEvent(extRecord.getLoggerClassName(),
                    Logger.getLogger(extRecord.getLoggerClassName()), extRecord.getMillis(),
                    Level.toLevel(extRecord.getLevel().toString()), extRecord.getFormattedMessage(),
                    extRecord.getThreadName(), extRecord.getThrown() == null ? null
                            : new ThrowableInformation(extRecord.getThrown()), extRecord.getNdc(),
                    new LocationInfo(new Throwable(), extRecord.getLoggerClassName()),
                    Collections.singletonMap("org.jboss.logmanager.record", extRecord));
            ((SyslogAppender) log4j.getAppender()).doAppend(event);
            flush();
        }
    }

    @Override
    public void flush() {
        log4j.flush();
    }

    @Override
    public void close() throws SecurityException {
        log4j.close();
    }

    @Override
    public void setFilter(Filter filter) {
        log4j.setFilter(filter);
    }

    @Override
    public void setFormatter(Formatter formatter) {
        log4j.setFormatter(formatter);
    }

    public void setThreshold(String threshold) {
        ((SocketAppender) log4j.getAppender()).setThreshold(Level.toLevel(threshold));
    }

    public void setRemoteHost(String remoteHost) {
        ((SocketAppender) log4j.getAppender()).setRemoteHost(remoteHost);
    }

    public void setPort(String port) {
        try {  
            ((SocketAppender) log4j.getAppender()).setPort(Integer.parseInt(port));  
       } catch (Exception e) {}  
    }

    public void setReconnectionDelay(String reconnectionDelay) {
        try {  
            ((SocketAppender) log4j.getAppender()).setReconnectionDelay(Integer.parseInt(reconnectionDelay));  
       } catch (Exception e) {}  
    }

    public void setLocationInfo(String locationInfo) {
        try {  
            ((SocketAppender) log4j.getAppender()).setLocationInfo(Boolean.parseBoolean(locationInfo));  
       } catch (Exception e) {}  
    }

}

I think the best approach is this, so you would not have the logging implementation in your application.

See also: Logging Configuration

I hope this help.