Java tutorial
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.opentable.logging; import java.io.IOException; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicLong; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonGenerator.Feature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.util.TokenBuffer; import org.slf4j.Marker; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.encoder.EncoderBase; /** * This class encodes log fields as a JSON object, and writes each as a separate line to the outputStream. * <p> * You can use this encoder by adding the following phrase to any appender: * <pre> * <encoder class="com.opentable.logging.JsonLogEncoder"> * </pre> */ public class JsonLogEncoder extends EncoderBase<ILoggingEvent> { private static final AtomicLong LOG_SEQUENCE_NUMBER = new AtomicLong(0); private final ObjectMapper mapper; private Class<?> customEventClass = HttpLogFields.class; public JsonLogEncoder() { // TODO: This sucks - - won't get the mapper customizations. Find a way to inject this. this.mapper = new ObjectMapper().disable(SerializationFeature.WRITE_NULL_MAP_VALUES) .setSerializationInclusion(Include.NON_NULL).configure(Feature.AUTO_CLOSE_TARGET, false); } public void setCustomEventClass(String customEventClass) throws ClassNotFoundException { this.customEventClass = Class.forName(customEventClass); } @Override public void doEncode(ILoggingEvent event) throws IOException { final ObjectNode logLine; if (customEventClass != null && customEventClass.isAssignableFrom(event.getClass())) { final TokenBuffer buf = new TokenBuffer(mapper, false); mapper.writerWithType(customEventClass).writeValue(buf, event); logLine = mapper.readTree(buf.asParser()); } else { logLine = mapper.valueToTree(new ApplicationLogEvent(event)); } final Marker marker = event.getMarker(); if (marker instanceof LogMetadata) { ObjectNode metadataNode = mapper.valueToTree(((LogMetadata) marker).getMetadata()); logLine.setAll(metadataNode); for (Object o : ((LogMetadata) marker).getInlines()) { metadataNode = mapper.valueToTree(o); logLine.setAll(metadataNode); } } for (Entry<String, String> e : event.getMDCPropertyMap().entrySet()) { if (!logLine.has(e.getKey())) { logLine.put(e.getKey(), e.getValue()); } } logLine.put("sequencenumber", LOG_SEQUENCE_NUMBER.incrementAndGet()); writeJsonNode(logLine); } protected void writeJsonNode(final ObjectNode logLine) throws IOException { synchronized (outputStream) { mapper.writeValue(outputStream, logLine); outputStream.write('\n'); } } @Override public void close() throws IOException { // Nothing to do here } }