[logback-dev] Logback Chainsaw Bridge

Maarten Bosteels maarten at apache.org
Wed Sep 26 11:10:32 CEST 2007


Hello,

I've written a Logback Chainsaw Bridge: your programs can use logback
and you can receive the events in Chainsaw using the SImpleReceiver.

The idea is pretty simple:

* You specify a "ch.qos.logback.classic.net.SocketAppender" in logback.xml
* LogbackChainsawBridge reads incoming logback events from the wire
* LogbackChainsawBridge converts the events to Log4j events
* LogbackChainsawBridge sends the events  to chainsaw using a SocketAppender

It's certainly not a perfect solution, I hope there will be a cool
LogBackViewer someday that can compete with chainsaw. (maybe when I
have lots of time...)
Or does it exist already ?

In the meantime, some of you might find it interesting.
The following attributes are sent to chainsaw:
* ThreadName
* Logger
* Timestamp
* Message
* Level
* MDC
* CallerData (LocationInfo)
* Throwable

Feedback welcome.

Maarten


package ch.org.logback;

import java.net.ServerSocket;
import java.net.Socket;
import java.io.ObjectInputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.EOFException;
import java.util.Map;
import java.util.Hashtable;

/**
 * This program will listen on the specified port for Logback logging events
 * and forward them as Log4j events to another port (to be received by Chainsaw)
 */
public class LogbackChainsawBridge {

  static int port;

  static boolean includeCallerData = false;

  public static void main(String argv[]) throws Exception {
    if (argv.length == 0) {
      init("5555", "false");
    }
    if (argv.length == 1) {
      init(argv[0], "false");
    }
    if (argv.length == 2) {
      init(argv[0], argv[1]);
    }

    if (argv.length > 2) {
      usage("too many arguments");
    }
    runServer();
  }

  static void runServer() {
    org.apache.log4j.net.SocketAppender socketAppender
        = new org.apache.log4j.net.SocketAppender("localhost", 4445);
    try {
      info("Listening on port " + port);
      ServerSocket serverSocket = new ServerSocket(port);
      //noinspection InfiniteLoopStatement
      while (true) {
        info("Waiting to accept a new client.");
        Socket socket = serverSocket.accept();
        info("Connected to client at " + socket.getInetAddress());
        info("Starting new socket node.");
        new Thread(new SocketHandler(socket, socketAppender)).start();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  static void usage(String msg) {
    System.err.println(msg);
    System.err.println("Usage: java " +
MySimpleSocketServer.class.getName() + "[port] [includeCallerData]");
    System.err.println("  port : on which port the logback events are
coming on");
    System.err.println("  includeCallerData : true when you want to
include caller data");
    System.exit(1);
  }

  private static void info(String message) {
    System.out.println(message);
  }

  static void init(String portStr, String includeCallerDataStr) {
    try {
      port = Integer.parseInt(portStr);
    } catch (NumberFormatException e) {
      e.printStackTrace();
      usage("Could not interpret port number [" + portStr + "].");
    }
    includeCallerData = Boolean.parseBoolean(includeCallerDataStr);
  }

  private static org.apache.log4j.spi.ThrowableInformation
convertToLog4jhTrowableInformation (
      ch.qos.logback.classic.spi.ThrowableInformation throwableInformation) {

    if (throwableInformation == null ||
        throwableInformation.getThrowableStrRep() == null ||
        throwableInformation.getThrowableStrRep().length == 0) {
      return null;
    }
    return new org.apache.log4j.spi.ThrowableInformation(throwableInformation.getThrowableStrRep());
  }

  private static org.apache.log4j.spi.LocationInfo convertToLog4jLocationInfo (
      ch.qos.logback.classic.spi.CallerData[] callerData) {
    if (!includeCallerData || callerData == null || callerData.length == 0) {
      return org.apache.log4j.spi.LocationInfo.NA_LOCATION_INFO;
    }
    ch.qos.logback.classic.spi.CallerData data = callerData[0];
    return new org.apache.log4j.spi.LocationInfo(
        data.getFileName(),
        data.getClassName(),
        data.getMethodName(),
        String.valueOf(data.getLineNumber()));
  }

  private static org.apache.log4j.spi.LoggingEvent convertToLog4jEvent (
      ch.qos.logback.classic.spi.LoggingEvent event) {
    String fqnOfCategoryClass = "todo: fqn";
    long timestamp = event.getTimeStamp();
    org.apache.log4j.Level level = org.apache.log4j.Level.toLevel(
event.getLevel().toInt() );
    String message = event.getFormattedMessage();
    String threadName = event.getThreadName();
    String loggerName = event.getLoggerRemoteView().getName();
    org.apache.log4j.spi.ThrowableInformation throwableInformation
        = convertToLog4jhTrowableInformation(event.getThrowableInformation());
    String ndc = null; // not supported by SLF4J and LogBack ?


    org.apache.log4j.spi.LocationInfo locationInfo =
convertToLog4jLocationInfo(event.getCallerData());


    Map<String,String> mdc = event.getMDCPropertyMap();

    org.apache.log4j.spi.LoggingEvent log4jEvent = new
org.apache.log4j.spi.LoggingEvent();

    log4jEvent.setFQNOfLoggerClass(fqnOfCategoryClass);
    log4jEvent.setLoggerName(loggerName);
    log4jEvent.setTimeStamp(timestamp);
    log4jEvent.setLevel(level);
    log4jEvent.setMessage(message);
    log4jEvent.setThreadName(threadName);
    log4jEvent.setThrowableInformation(throwableInformation);
    if (mdc != null) {
      //noinspection unchecked
      log4jEvent.setProperties(new Hashtable(mdc));
    }
    log4jEvent.setLocationInformation(locationInfo);
    log4jEvent.setNDC(ndc);
    //log4jEvent.setSequenceNumber(4);
    log4jEvent.setRenderedMessage(message);
    return log4jEvent;
  }


  private static class SocketHandler implements Runnable {

    Socket socket;
    ObjectInputStream ois;
    org.apache.log4j.net.SocketAppender log4jAppender;

    public SocketHandler(Socket socket,
org.apache.log4j.net.SocketAppender log4jAppender) {
      this.socket = socket;
      this.log4jAppender = log4jAppender;
      try {
        ois = new ObjectInputStream(new BufferedInputStream(socket
            .getInputStream()));
      } catch (Exception e) {
        System.err.println("Could not open ObjectInputStream to " + socket);
        e.printStackTrace();
      }
    }


    public void run() {
      ch.qos.logback.classic.spi.LoggingEvent event;
      try {
        while (true) {
          // read an event from the wire
          try {
            event = (ch.qos.logback.classic.spi.LoggingEvent) ois.readObject();
          } catch (EOFException e) {
            info("client disconnected");
            break;
          } catch (IOException e) {
            e.printStackTrace();
            break;
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
            break;
          }

          if (event == null) {
            continue;
          }
          org.apache.log4j.spi.LoggingEvent log4jEvent =
convertToLog4jEvent(event);

          log4jAppender.append(log4jEvent);

        }
      } catch (Exception e) {
        System.err.println("Unexpected exception. Closing connection.");
        e.printStackTrace();
      }

      try {
        ois.close();
      } catch (Exception e) {
        System.err.println("Could not close connection.");
        e.printStackTrace();
      }
    }
  }

}



More information about the logback-dev mailing list