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 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 java.util.TimeZone; 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 { /** * 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; /** * Used to format timestamps. */ protected FastDateFormat isoDateTimeTimeZoneFormatWithMillis = createFastDateFormat(null); 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 a map as String fields to the generator if and only if the fieldName and values are not null. */ protected void writeMapStringFields(JsonGenerator generator, String fieldName, Map<String, String> map) throws IOException, JsonMappingException { if (shouldWriteField(fieldName) && map != null && !map.isEmpty()) { generator.writeObjectFieldStart(fieldName); for (Map.Entry<String, String> entry : map.entrySet()) { writeStringField(generator, entry.getKey(), entry.getValue()); } generator.writeEndObject(); } } /** * 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(LogstashCommonFieldNames.IGNORE_FIELD_INDICATOR); } private BufferRecycler getBufferRecycler() { SoftReference<BufferRecycler> bufferRecyclerReference = recycler.get(); BufferRecycler bufferRecycler = bufferRecyclerReference.get(); if (bufferRecycler == null) { recycler.remove(); return getBufferRecycler(); } return bufferRecycler; } private FastDateFormat createFastDateFormat(TimeZone timeZone) { return FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ", timeZone); } 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; } public String getTimeZone() { return this.isoDateTimeTimeZoneFormatWithMillis.getTimeZone().getID(); } public void setTimeZone(String timeZoneId) { this.isoDateTimeTimeZoneFormatWithMillis = createFastDateFormat(TimeZone.getTimeZone(timeZoneId)); } }