Yes, logback does this well in fact.

I can show you and example of a logback encoder that encodes to a JSON
format. You can extend the concept to encode XML just the same. Possibly
you could use very similar code to mine and abstract your message
formatting into a builder pattern.

For my money though, I'd stick with JSON. I'd also steer you toward
Logstash and Kibana as a killer combo for dealing with logs--both of these
tools are free and open source.

I attached some (incomplete WRT to dependencies) code that should get the
main point across.

Good Luck.


Troy
/* Copyright (C) 2013 Partnet, Inc.  Confidential and Proprietary. */
package com.foo;


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

import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;

/**
 * @author thart
 *
 */
public class GsonLoggingEventBuilder
{

  @SerializedName("message")
  private String message;

  @SerializedName("source_host")
  private String sourceHost;

  @SerializedName("fields")
  private Map<String, Object> fields = new HashMap<String, Object>();

  public void setMessage(String message)
  {
    this.message = message;
  }

  public void setSourceHost(String sourceHost)
  {
    this.sourceHost = sourceHost;
  }

  /**
   * @param fieldName
   * @param fieldValue
   */
  public void addField(String fieldName, Object fieldValue)
  {
    if (fieldName == null) {
      throw new IllegalArgumentException("fieldName is null");
    }
    fields.put(fieldName, fieldValue);
  }

  public String toJson()
  {
    return new Gson().toJson(this);
    //return new GsonBuilder().setPrettyPrinting().create().toJson(this);
  }
}
package com.foo;
// just showing logback imports for brevity
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.encoder.EncoderBase;\

public class JsonEncoder
  extends EncoderBase<ILoggingEvent>
{
  /**
   * The charset to use when converting a String into bytes.
   * <p/>
   * By default this property has the value
   * <code>null</null> which corresponds to
   * the system's default charset.
   */
  private Charset charset;

  private boolean immediateFlush = true;


  /**
   * <p>Sets the immediateFlush option. The default value for immediateFlush is
   * 'true'. If set to true, the doEncode() method will immediately flush the
   * underlying OutputStream. Although immediate flushing is safer, it also
   * significantly degrades logging throughput.</p>
   */
  public void setImmediateFlush(boolean immediateFlush)
  {
    this.immediateFlush = immediateFlush;
  }

  public boolean isImmediateFlush()
  {
    return immediateFlush;
  }

  public Charset getCharset()
  {
    return charset;
  }

  /**
   * <p>Set the charset to use when converting the string returned by the layout
   * into bytes.</p>
   *
   * <p>By default this property has the value <code>null</null> which
   * corresponds to the system's default charset.</p>
   *
   * @param charset
   */
  public void setCharset(Charset charset)
  {
    this.charset = charset;
  }

  @Override
  public void close()
    throws IOException
  {
    outputStream.write(convertToBytes(CoreConstants.LINE_SEPARATOR, getCharset()));
  }

  @Override
  public void doEncode(ILoggingEvent event)
    throws IOException
  {
    GsonLoggingEventBuilder builder = new GsonLoggingEventBuilder();
    builder.setMessage(event.getFormattedMessage());

    builder.addField("level", event.getLevel().toString());
    builder.addField("logger", event.getLoggerName());
    builder.addField("thread", event.getThreadName());
    builder.addField("context.name", event.getLoggerContextVO().getName());

    addTimestamp(builder, event);
    addMDC(builder, event);
    addContext(builder, event);
    addThrowableInformation(builder, event);

    Charset charset = getCharset();
    outputStream.write(convertToBytes(builder.toJson(), charset));
    outputStream.write(convertToBytes(CoreConstants.LINE_SEPARATOR, charset));

    if (immediateFlush) {
      outputStream.flush();
    }
  }

  private void addTimestamp(JsonLoggingEventBuilder builder, ILoggingEvent event)
  {
    builder.addField("timestamp", formatTimestamp(event));
  }

  private void addThrowableInformation(JsonLoggingEventBuilder builder, ILoggingEvent event)
  {
    final IThrowableProxy tp = event.getThrowableProxy();
    if (tp != null) {
      builder.addField("stacktrace", ThrowableProxyUtil.asString(tp));
      //...what else should be included?
    }
  }

  private void addMDC(JsonLoggingEventBuilder builder, ILoggingEvent event)
  {
    Map<String, String> mdc = event.getMDCPropertyMap();
    if (!mdc.isEmpty()) {
      builder.addField("mdc", mdc);
    }
  }

  private void addContext(JsonLoggingEventBuilder builder, ILoggingEvent event)
  {
    Context ctx = getContext();
    if (ctx != null) {
      builder.addField("context", ctx.getCopyOfPropertyMap());
    }
  }
}
_______________________________________________
Logback-user mailing list
[email protected]
http://mailman.qos.ch/mailman/listinfo/logback-user

Reply via email to