
Hi Maarten, I really like the idea. The existing socketAppender could be adapted to convert a logback LoggingEvent into log4j format before sending it off to chainsaw. A lot less code to maintain. Anyway, maybe we should aim for 1.2.x compatibility instead of 1.3? Slightly off topic but do you use Chainsaw regularly? How would you rate it on a scale of 1 to 10? Maarten Bosteels wrote:
I forgot to mention that the code currently depends on log4j-1.3 (which is an abandoned version) because otherwise chainsaw wouldn't show the location-info.
Maarten
On 9/26/07, Maarten Bosteels <maarten@apache.org> wrote:
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(); } } }
}
_______________________________________________ logback-dev mailing list logback-dev@qos.ch http://qos.ch/mailman/listinfo/logback-dev
-- Ceki Gülcü Logback: The reliable, generic, fast and flexible logging framework for Java. http://logback.qos.ch