net.logstash.logback.LogstashAbstractFormatter.java Source code

Java tutorial

Introduction

Here is the source code for net.logstash.logback.LogstashAbstractFormatter.java

Source

/**
 * 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 net.logstash.logback;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.ref.SoftReference;
import java.util.Map;

import net.logstash.logback.decorate.JsonFactoryDecorator;
import net.logstash.logback.decorate.JsonGeneratorDecorator;
import net.logstash.logback.decorate.NullJsonFactoryDecorator;
import net.logstash.logback.decorate.NullJsonGeneratorDecorator;
import net.logstash.logback.fieldnames.LogstashCommonFieldNames;

import org.apache.commons.lang.time.FastDateFormat;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import ch.qos.logback.core.spi.LifeCycle;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.io.SegmentedStringWriter;
import com.fasterxml.jackson.core.util.BufferRecycler;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MappingJsonFactory;

abstract class LogstashAbstractFormatter<EventType extends DeferredProcessingAware, FieldNamesType extends LogstashCommonFieldNames>
        implements LifeCycle {
    /**
     * Field name to use in logback configuration files
     * if you want the field to be ignored (not output).
     * 
     * Unfortunately, logback does not provide a way to set a
     * field value to null via xml config,
     * so we have to fall back to using this magic string.
     * 
     * Note that if you're programmatically configuring the field names,
     * then you can just set the field name to null in the
     * FieldNamesType.  
     */
    public static final String IGNORE_FIELD_INDICATOR = "[ignore]";

    protected static final FastDateFormat ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS = FastDateFormat
            .getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");

    /**
     * This <code>ThreadLocal</code> contains a {@link java.lang.ref.SoftReference} to a {@link BufferRecycler} used to provide a low-cost
     * buffer recycling between writer instances.
     */
    private final ThreadLocal<SoftReference<BufferRecycler>> recycler = new ThreadLocal<SoftReference<BufferRecycler>>() {
        protected SoftReference<BufferRecycler> initialValue() {
            final BufferRecycler bufferRecycler = new BufferRecycler();
            return new SoftReference<BufferRecycler>(bufferRecycler);
        }
    };

    /**
     * The jsonFactory provided by {@link #jsonFactoryProvider}.
     * This will only non-null when the formatter is started.
     */
    private MappingJsonFactory jsonFactory = (MappingJsonFactory) new MappingJsonFactory()
            .enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);

    /**
     * Decorates the {@link #jsonFactory}.
     * Allows customization of the {@link #jsonFactory}. 
     */
    private JsonFactoryDecorator jsonFactoryDecorator = new NullJsonFactoryDecorator();

    /**
     * Decorates the generators generated by the {@link #jsonFactory}. 
     * Allows customization of the generators. 
     */
    private JsonGeneratorDecorator jsonGeneratorDecorator = new NullJsonGeneratorDecorator();

    /**
     * The field names to use when writing the standard event fields
     */
    protected FieldNamesType fieldNames;

    private volatile boolean started;

    protected final ContextAware contextAware;

    protected LogstashAbstractFormatter(ContextAware contextAware, FieldNamesType fieldNames) {
        this.contextAware = contextAware;
        this.fieldNames = fieldNames;
    }

    @Override
    public void start() {
        jsonFactory = this.jsonFactoryDecorator.decorate(this.jsonFactory);
        started = true;
    }

    @Override
    public void stop() {
        started = false;
    }

    @Override
    public boolean isStarted() {
        return started;
    }

    public JsonFactory getJsonFactory() {
        return jsonFactory;
    }

    public byte[] writeValueAsBytes(EventType event, Context context) throws IOException {
        ByteArrayBuilder outputStream = new ByteArrayBuilder(getBufferRecycler());

        try {
            writeValueToOutputStream(event, context, outputStream);
            return outputStream.toByteArray();
        } finally {
            outputStream.release();
        }
    }

    public void writeValueToOutputStream(EventType event, Context context, OutputStream outputStream)
            throws IOException {
        JsonGenerator generator = createGenerator(outputStream);
        writeValueToGenerator(generator, event, context);
    }

    public String writeValueAsString(EventType event, Context context) throws IOException {
        SegmentedStringWriter writer = new SegmentedStringWriter(getBufferRecycler());

        JsonGenerator generator = createGenerator(writer);
        writeValueToGenerator(generator, event, context);
        return writer.getAndClear();
    }

    private JsonGenerator createGenerator(OutputStream outputStream) throws IOException {
        return this.jsonGeneratorDecorator.decorate(jsonFactory.createGenerator(outputStream));
    }

    private JsonGenerator createGenerator(Writer writer) throws IOException {
        return this.jsonGeneratorDecorator.decorate(jsonFactory.createGenerator(writer));
    }

    protected abstract void writeValueToGenerator(JsonGenerator generator, EventType event, Context context)
            throws IOException;

    protected void writeMapEntries(JsonGenerator generator, Map<?, ?> map)
            throws IOException, JsonMappingException {
        if (map != null) {
            for (Map.Entry<?, ?> entry : map.entrySet()) {
                generator.writeFieldName(entry.getKey().toString());
                generator.writeObject(entry.getValue());
            }
        }
    }

    /**
     * Writes the field to the generator if and only if the fieldName and fieldValue are not null.
     */
    protected void writeStringField(JsonGenerator generator, String fieldName, String fieldValue)
            throws IOException {
        if (shouldWriteField(fieldName) && shouldWriteField(fieldValue)) {
            generator.writeStringField(fieldName, fieldValue);
        }
    }

    /**
     * Writes the field to the generator if and only if the fieldName is not null.
     */
    protected void writeNumberField(JsonGenerator generator, String fieldName, int fieldValue) throws IOException {
        if (shouldWriteField(fieldName)) {
            generator.writeNumberField(fieldName, fieldValue);
        }
    }

    /**
     * Writes the field to the generator if and only if the fieldName is not null.
     */
    protected void writeNumberField(JsonGenerator generator, String fieldName, long fieldValue) throws IOException {
        if (shouldWriteField(fieldName)) {
            generator.writeNumberField(fieldName, fieldValue);
        }
    }

    private boolean shouldWriteField(String fieldName) {
        return fieldName != null && !fieldName.equals(IGNORE_FIELD_INDICATOR);
    }

    private BufferRecycler getBufferRecycler() {
        SoftReference<BufferRecycler> bufferRecyclerReference = recycler.get();
        BufferRecycler bufferRecycler = bufferRecyclerReference.get();
        if (bufferRecycler == null) {
            recycler.remove();
            return getBufferRecycler();
        }
        return bufferRecycler;
    }

    public FieldNamesType getFieldNames() {
        return fieldNames;
    }

    public void setFieldNames(FieldNamesType fieldNames) {
        this.fieldNames = fieldNames;
    }

    public JsonFactoryDecorator getJsonFactoryDecorator() {
        return jsonFactoryDecorator;
    }

    public void setJsonFactoryDecorator(JsonFactoryDecorator jsonFactoryDecorator) {
        this.jsonFactoryDecorator = jsonFactoryDecorator;
    }

    public JsonGeneratorDecorator getJsonGeneratorDecorator() {
        return jsonGeneratorDecorator;
    }

    public void setJsonGeneratorDecorator(JsonGeneratorDecorator jsonGeneratorDecorator) {
        this.jsonGeneratorDecorator = jsonGeneratorDecorator;
    }
}