Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read log4j output to a web page?

I have a web page, used for admin purposes, which runs a task (image fetching from a remote site).
In order to be able to debug the task using the browser only, no ssh etc, I'd like to be able to read all log output from the executing thread and spit it out to the web page.
The task boils down to:

  1. Changing log level for current thread at the beginning of the call and restore when the call is done.
  2. Reading all log output by current thread and storing it in a string.

So in pseudocode my execute() method would look like this: (I'm using struts2)

public String execute() throws Exception {
  turnLoggingLevelToDebugOnlyForThisThread()
  ... do stuff...
  restoreLoggingLevelForThisThread()
  String logs = readAllLogsByThisThread();
}

Can this be done with log4j?

I'm using tomcat, struts2, log4j and slf4j.

EDIT 1: I should note that the motivation is to be able to see the existing logs on a web page without needing to add new log lines in code. Think of a nice web debug interface that lets you run your operation and the result spits out the logs of the operation.
EDIT 2: I should also note that I'm already using log4j (via slf4j) and a log4j.xml, so the solution I'm seeking needs to live aside the currently logging system, not ruin it.

like image 836
Ran Avatar asked Mar 31 '10 19:03

Ran


People also ask

How do I view log4j in web application?

If no location is defined Log4j will search for a file that starts with "log4j2" in the WEB-INF directory. If more than one file is found, and if a file that starts with "log4j2- name " is present, where name is the name of the web application, then it will be used. Otherwise the first file will be used.

How do you read log4j?

Log4j is an open source project based on the work of many authors. It allows the developer to control which log statements are output with arbitrary granularity. It is fully configurable at runtime using external configuration files.

What is log4j format message lookup?

Lookups provide a way to add values to the Log4j configuration at arbitrary places. They are a particular type of Plugin that implements the StrLookup interface. Information on how to use Lookups in configuration files can be found in the Property Substitution section of the Configuration page.


1 Answers

You can create a log4j appender to write to a StringWriter. Here is an example I did some time ago:

consoleWriter = new StringWriter();
WriterAppender appender = new WriterAppender(new PatternLayout("%d{ISO8601} %p - %m%n"),consoleWriter);
appender.setName("CONSOLE_APPENDER");
appender.setThreshold(org.apache.log4j.Level.ERROR);
Logger.getRootLogger().addAppender(appender);

It writes all ERROR logs to the consoleWriter, but you can set the scope or the level of the logs as you wish. The scope (logger name) should be a unique identifier of the thread. something like this:

Logger.getLogger("Thread-00001").addAppender(appender);

Your thread should write to this logger.

Logger.getLogger("Thread-00001").info("blah blah blah");

And when you want to finish logging the thread:

Logger.getLogger("Thread-00001").removeAppender("CONSOLE_APPENDER");

UPDATE: Here is a working example. Writes error logs to a file (set in log4j.xml) + writes all thread logs to a StringWriter, when enabled:

import java.io.StringWriter;
import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.apache.log4j.WriterAppender;
import org.apache.log4j.PatternLayout;

public class Log4jTest implements Runnable {

    public static final String CONSOLE_APPENDER = "CONSOLE_APPENDER";
    private static WriterAppender appender = null;
    private static int counter = 1;

    public static synchronized String getNextId() {
        return "Thread_00"+(counter++);
    }

    public void run() {
        String id="UNKNOWN";
        try {
            id = getNextId();
            Logger log = Logger.getLogger(id);
            log.addAppender(appender);
            log.setLevel(Level.DEBUG);
            log.info(id+" log message 1");
            log.removeAppender(CONSOLE_APPENDER);
            log.info(id+" log message 2");
            log.error(id+" log message 3");
        } catch (Exception e) {
            System.out.println("Error in "+id);
            e.printStackTrace();
        }
    }

    public static void main(String [] args) {
        try {
            StringWriter consoleWriter = new StringWriter();
            appender = new WriterAppender(new PatternLayout("%d{ISO8601} %p - %m%n"),consoleWriter);
            appender.setName(CONSOLE_APPENDER);
            appender.setThreshold(org.apache.log4j.Level.DEBUG);

            for (int i=0; i<5; i++) {
                Thread t = new Thread(new Log4jTest());
                t.start();
            }

            Thread.sleep(200);
            System.out.println(consoleWriter.getBuffer().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And here is my log4j.xml:

<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
  <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
    <param name="File" value="./Log4jTest.log" />
    <param name="MaxFileSize" value="1000KB" />
    <param name="MaxBackupIndex" value="5" />
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} %p - %m%n" />
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
      <param name="LevelMin" value="WARN" />
      <param name="LevelMax" value="FATAL" />
    </filter>
  </appender>
  <root>
    <level value="ERROR" />
    <appender-ref ref="FILE" />
  </root>
</log4j:configuration>
like image 176
Miklos Csuka Avatar answered Oct 20 '22 04:10

Miklos Csuka