All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.logstash.logback.encoder.CompositeJsonEncoder Maven / Gradle / Ivy

Go to download

Provides logback encoders, layouts, and appenders to log in JSON and other formats supported by Jackson

There is a newer version: 8.0
Show newest version
/*
 * Copyright 2013-2022 the original author or authors.
 *
 * 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.encoder;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Objects;

import net.logstash.logback.composite.AbstractCompositeJsonFormatter;
import net.logstash.logback.composite.JsonProviders;
import net.logstash.logback.decorate.JsonFactoryDecorator;
import net.logstash.logback.decorate.JsonGeneratorDecorator;
import net.logstash.logback.util.ReusableByteBuffer;
import net.logstash.logback.util.ThreadLocalReusableByteBuffer;

import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.encoder.EncoderBase;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.pattern.PatternLayoutBase;
import ch.qos.logback.core.spi.DeferredProcessingAware;

public abstract class CompositeJsonEncoder
        extends EncoderBase implements StreamingEncoder {
    private static final byte[] EMPTY_BYTES = new byte[0];

    /**
     * The minimum size of the byte buffer used when encoding events using {@link #encode(DeferredProcessingAware)}.
     *
     * 

The buffer automatically grows above the {@code #minBufferSize} when needed to * accommodate with larger events. However, only the first {@code minBufferSize} bytes * will be reused by subsequent invocations. It is therefore strongly advised to set * the minimum size at least equal to the average size of the encoded events to reduce * unnecessary memory allocations and reduce pressure on the garbage collector. */ private int minBufferSize = 1024; /** * Per-thread {@link ReusableByteBuffer} instance used when calling {@link #encode(DeferredProcessingAware)} */ private ThreadLocalReusableByteBuffer threadLocalBuffer; private Encoder prefix; private Encoder suffix; private final AbstractCompositeJsonFormatter formatter; private String lineSeparator = System.lineSeparator(); private byte[] lineSeparatorBytes; private Charset charset; public CompositeJsonEncoder() { super(); this.formatter = Objects.requireNonNull(createFormatter()); } protected abstract AbstractCompositeJsonFormatter createFormatter(); @Override public void encode(Event event, OutputStream outputStream) throws IOException { if (!isStarted()) { throw new IllegalStateException("Encoder is not started"); } encode(outputStream, event); } @Override public byte[] encode(Event event) { if (!isStarted()) { throw new IllegalStateException("Encoder is not started"); } ReusableByteBuffer buffer = threadLocalBuffer.acquire(); try { encode(buffer, event); return buffer.toByteArray(); } catch (IOException e) { addWarn("Error encountered while encoding log event. Event: " + event, e); return EMPTY_BYTES; } finally { threadLocalBuffer.release(); } } private void encode(OutputStream outputStream, Event event) throws IOException { encode(prefix, event, outputStream); formatter.writeEvent(event, outputStream); encode(suffix, event, outputStream); outputStream.write(lineSeparatorBytes); } private void encode(Encoder encoder, Event event, OutputStream outputStream) throws IOException { if (encoder != null) { byte[] data = encoder.encode(event); if (data != null) { outputStream.write(data); } } } @Override public void start() { if (isStarted()) { return; } super.start(); formatter.setContext(getContext()); formatter.start(); charset = Charset.forName(formatter.getEncoding()); lineSeparatorBytes = this.lineSeparator == null ? EMPTY_BYTES : this.lineSeparator.getBytes(charset); startWrapped(prefix); startWrapped(suffix); this.threadLocalBuffer = new ThreadLocalReusableByteBuffer(minBufferSize); } @SuppressWarnings({ "unchecked", "rawtypes" }) private void startWrapped(Encoder wrapped) { if (wrapped instanceof LayoutWrappingEncoder) { /* * Convenience hack to ensure the same charset is used in most cases. * * The charset for other encoders must be configured * on the wrapped encoder configuration. */ LayoutWrappingEncoder layoutWrappedEncoder = (LayoutWrappingEncoder) wrapped; layoutWrappedEncoder.setCharset(charset); if (layoutWrappedEncoder.getLayout() instanceof PatternLayoutBase) { /* * Don't ensure exception output (for ILoggingEvents) * or line separation (for IAccessEvents) */ PatternLayoutBase layout = (PatternLayoutBase) layoutWrappedEncoder.getLayout(); layout.setPostCompileProcessor(null); /* * The pattern will be re-parsed during start. * Needed so that the pattern is re-parsed without * the postCompileProcessor. */ layout.start(); } } if (wrapped != null && !wrapped.isStarted()) { wrapped.start(); } } @Override public void stop() { if (isStarted()) { super.stop(); formatter.stop(); stopWrapped(prefix); stopWrapped(suffix); threadLocalBuffer = null; } } private void stopWrapped(Encoder wrapped) { if (wrapped != null && wrapped.isStarted()) { wrapped.stop(); } } @Override public byte[] headerBytes() { return EMPTY_BYTES; } @Override public byte[] footerBytes() { return EMPTY_BYTES; } public JsonProviders getProviders() { return formatter.getProviders(); } public void setProviders(JsonProviders jsonProviders) { formatter.setProviders(jsonProviders); } public JsonFactoryDecorator getJsonFactoryDecorator() { return formatter.getJsonFactoryDecorator(); } public void setJsonFactoryDecorator(JsonFactoryDecorator jsonFactoryDecorator) { formatter.setJsonFactoryDecorator(jsonFactoryDecorator); } public JsonGeneratorDecorator getJsonGeneratorDecorator() { return formatter.getJsonGeneratorDecorator(); } public String getEncoding() { return formatter.getEncoding(); } /** * The character encoding to use (default = "{@code UTF-8}"). * Must an encoding supported by {@link com.fasterxml.jackson.core.JsonEncoding} * * @param encodingName encoding name */ public void setEncoding(String encodingName) { formatter.setEncoding(encodingName); } public void setFindAndRegisterJacksonModules(boolean findAndRegisterJacksonModules) { formatter.setFindAndRegisterJacksonModules(findAndRegisterJacksonModules); } public void setJsonGeneratorDecorator(JsonGeneratorDecorator jsonGeneratorDecorator) { formatter.setJsonGeneratorDecorator(jsonGeneratorDecorator); } public String getLineSeparator() { return lineSeparator; } /** * Sets which lineSeparator to use between events. *

* * The following values have special meaning: *

    *
  • {@code null} or empty string = no new line.
  • *
  • "{@code SYSTEM}" = operating system new line (default).
  • *
  • "{@code UNIX}" = unix line ending ({@code \n}).
  • *
  • "{@code WINDOWS}" = windows line ending {@code \r\n}).
  • *
*

* Any other value will be used as given as the lineSeparator. * * @param lineSeparator the line separator */ public void setLineSeparator(String lineSeparator) { this.lineSeparator = SeparatorParser.parseSeparator(lineSeparator); } public int getMinBufferSize() { return minBufferSize; } /** * The minimum size of the byte buffer used when encoding events. * *

The buffer automatically grows above the {@code #minBufferSize} when needed to * accommodate with larger events. However, only the first {@code minBufferSize} bytes * will be reused by subsequent invocations. It is therefore strongly advised to set * the minimum size at least equal to the average size of the encoded events to reduce * unnecessary memory allocations and reduce pressure on the garbage collector. * *

Note: changes to the buffer size will not be taken into account after the encoder * is started. * * @param minBufferSize minimum size of the byte buffer (in bytes) */ public void setMinBufferSize(int minBufferSize) { this.minBufferSize = minBufferSize; } protected AbstractCompositeJsonFormatter getFormatter() { return formatter; } public Encoder getPrefix() { return prefix; } public void setPrefix(Encoder prefix) { this.prefix = prefix; } public Encoder getSuffix() { return suffix; } public void setSuffix(Encoder suffix) { this.suffix = suffix; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy