diff --git a/README.md b/README.md index babd6126..b44494b2 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ The structure of the output, and the data it contains, is fully configurable. * [Customizing Stack Traces](#customizing-stack-traces) * [Prefix/Suffix/Separator](#prefixsuffixseparator) * [Composite Encoder/Layout](#composite-encoderlayout) + * [Providers common to LoggingEvents and AccessEvents](#providers-common-to-loggingevents-and-accessevents) * [Providers for LoggingEvents](#providers-for-loggingevents) * [Providers for AccessEvents](#providers-for-accessevents) * [Nested JSON Provider](#nested-json-provider) @@ -1871,164 +1872,186 @@ These encoders/layouts make use of an internal buffer to hold the JSON output du The size of this buffer is set to `1024` bytes by default. A different size can be configured by setting the `minBufferSize` property to the desired value. The buffer automatically grows above the `minBufferSize` when needed to accommodate with larger events. However, only the first `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. +### Providers common to LoggingEvents and AccessEvents -#### Providers for LoggingEvents - -The table below lists the available providers for LoggingEvents, and their configuration properties (defaults in parentheses). +The table below lists the providers available to both _LoggingEvents_ and _AccessEvents_. The provider name is the xml element name to use when configuring. - + - - - - - - + - - + - - + - - + - - - + - - + - - + + +
Provider Description/Properties
timestamp

Event timestamp

-
    -
  • fieldName - Output field name (@timestamp)
  • -
  • pattern - Output format ([ISO_OFFSET_DATE_TIME]) See above for possible values.
  • -
  • timeZone - Timezone (local timezone)
  • -
-
version

Logstash JSON format version

+
context

Outputs entries from logback's context.

    -
  • fieldName - Output field name (@version)
  • -
  • version - Output value (1)
  • -
  • writeAsInteger - Write the version as a integer value (false = write as a string value)
  • +
  • fieldName - Sub-object field name (no sub-object)
message

Formatted log event message

+
nestedField +

Nests a JSON object under the configured fieldName.

+

The nested object is populated by other providers added to this provider.

+

See Nested JSON provider.

    -
  • fieldName - Output field name (message)
  • -
  • messageSplitRegex - If null or empty, write the message text as is (the default behavior). - Otherwise, split the message text using the specified regex and write it as an array. - See the Customizing Message section for details.
  • +
  • fieldName - Output field name
  • +
  • providers - The providers that should populate the nested object.
rawMessage

Raw log event message, as opposed to formatted log where parameters are resolved

+
pattern +

Outputs fields from a configured JSON Object string, + while substituting patterns supported by logback's PatternLayout. +

+

+ See Pattern JSON Provider +

    -
  • fieldName - Output field name (raw_message)
  • +
  • pattern - JSON object string (no default)
  • +
  • omitEmptyFields - whether to omit fields with empty values (false)
loggerName

Name of the logger that logged the message

+
sequence +

+ Outputs an incrementing sequence number for every log event. + Useful for tracking pottential message loss during transport (eg. UDP). +

    -
  • fieldName - Output field name (logger_name)
  • -
  • shortenedLoggerNameLength - Length to which the name will be attempted to be abbreviated (no abbreviation)
  • -
+
  • fieldName - Output field name (sequence)
  • threadName

    Name of the thread from which the event was logged

    +

    Name of the thread from which the event was logged.

    • fieldName - Output field name (thread_name)
    logLevel

    Logger level text (INFO, WARN, etc)

    +
    timestamp

    Event timestamp.

      -
    • fieldName - Output field name (level)
    • +
    • fieldName - Output field name (@timestamp)
    • +
    • pattern - Output format ([ISO_OFFSET_DATE_TIME]) See above for possible values.
    • +
    • timeZone - Timezone (local timezone)
    logLevelValue

    Logger level numerical value

    +
    uuid + Outputs random UUID as field value. Handy when you want to provide unique identifier + for log lines. +
      -
    • fieldName - Output field name (level_value)
    • +
    • fieldName - Output field name (uuid)
    • +
    • strategy - UUID generation strategy (random). Supported options:
      • random - for Type 4 UUID
      • +
      • time - for Type 1 time based UUID
      • +
    • +
    • ethernet - Only for 'time' strategy. When defined - MAC address to use for location part of UUID. Set it to interface value to use real underlying network interface or to specific values like 00:C0:F0:3D:5B:7C
    +

    Note: The com.fasterxml.uuid:java-uuid-generator optional dependency must be added to applications that use the `uuid` provider.

    callerData

    Outputs data about from where the logger was called (class/method/file/line) +

    version

    Logstash JSON format version.

      -
    • fieldName - Sub-object field name (no sub-object)
    • -
    • classFieldName - Field name for class name (caller_class_name)
    • -
    • methodFieldName - Field name for method name (caller_method_name)
    • -
    • fileFieldName - Field name for file name (caller_file_name)
    • -
    • lineFieldName - Field name for line number (caller_line_number)
    • +
    • fieldName - Output field name (@version)
    • +
    • version - Output value (1)
    • +
    • writeAsInteger - Write the version as a integer value (false = write as a string value)
    + + +### Providers for LoggingEvents + +The [common providers mentioned above](#providers-common-to-loggingevents-and-accessevents), and the providers listed in the table below, are available for _LoggingEvents_. +The provider name is the xml element name to use when configuring. Each provider's configuration properties are shown, with default configuration values in parenthesis. + + + + + + + - - + - + - - + - - + - - + - - + - - + - - + @@ -2050,136 +2073,125 @@ The provider name is the xml element name to use when configuring. - - + - - + - - + - - + - - + - + - - + - - + + + + +
    ProviderDescription/Properties
    stackTrace

    Stacktrace of any throwable logged with the event. Stackframes are separated by newline chars.

    +
    arguments +

    Outputs fields from the event arguments array.

    +

    See Event-specific Custom Fields.

      -
    • fieldName - Output field name (stack_trace)
    • -
    • throwableConverter - The ThrowableHandlingConverter to use to format the stacktrace (stack_trace)
    • +
    • fieldName - Sub-object field name (no sub-object)
    • +
    • includeNonStructuredArguments - Include arguments that are not an instance + of StructuredArgument. + Object field name will be nonStructuredArgumentsFieldPrefix prepend to the argument index. + (default=false) +
    • +
    • nonStructuredArgumentsFieldPrefix - Object field name prefix (default=arg)
    rootStackTraceElement

    (Only if a throwable was logged) Outputs a JSON Object containing the class and method name from which the outer-most exception was thrown.

    +
    callerData

    Outputs data about from where the logger was called (class/method/file/line).

      -
    • fieldName - Output field name (root_stack_trace_element)
    • -
    • classFieldName - Field name containing the class name from which the outermost exception was thrown (class_name)
    • -
    • methodFieldName - Field name containing the method name from which the outermost exception was thrown (method_name)
    • +
    • fieldName - Sub-object field name (no sub-object)
    • +
    • classFieldName - Field name for class name (caller_class_name)
    • +
    • methodFieldName - Field name for method name (caller_method_name)
    • +
    • fileFieldName - Field name for file name (caller_file_name)
    • +
    • lineFieldName - Field name for line number (caller_line_number)
    stackHash

    (Only if a throwable was logged) Computes and outputs a hexadecimal hash of the throwable stack.

    -

    This helps identifying several occurrences of the same error (more info).

    +
    contextName

    Outputs the name of logback's context.

      -
    • fieldName - Output field name (stack_hash)
    • -
    • exclude - Regular expression pattern matching stack trace elements to exclude when computing the error hash
    • -
    • exclusions - Coma separated list of regular expression patterns matching stack trace elements to exclude when computing the error hash
    • +
    • fieldName - Output field name (context)
    throwableClassName

    (Only if a throwable was logged) Outputs a field that contains the class name of the thrown Throwable.

    +
    loggerName

    Name of the logger that logged the message.

      -
    • fieldName - Output field name (throwable_class)
    • -
    • useSimpleClassName - When true, the throwable's simple class name will be used. When false, the fully qualified class name will be used. (true)
    • +
    • fieldName - Output field name (logger_name)
    • +
    • shortenedLoggerNameLength - Length to which the name will be attempted to be abbreviated (no abbreviation)
    throwableRootCauseClassName

    (Only if a throwable was logged) Outputs a field that contains the class name of the root cause of the thrown Throwable.

    +
    logLevel

    Logger level text (INFO, WARN, etc).

      -
    • fieldName - Output field name (throwable_root_cause_class)
    • -
    • useSimpleClassName - When true, the throwable's simple class name will be used. When false, the fully qualified class name will be used. (true)
    • +
    • fieldName - Output field name (level)
    context

    Outputs entries from logback's context

    +
    logLevelValue

    Logger level numerical value.

      -
    • fieldName - Sub-object field name (no sub-object)
    • +
    • fieldName - Output field name (level_value)
    contextName

    Outputs the name of logback's context

    -
      -
    • fieldName - Output field name (context)
    • -
    +
    logstashMarkers

    Used to output Logstash Markers as specified in Event-specific Custom Fields.

    tags

    Outputs logback markers as a comma separated list

    +
    message

    Formatted log event message.

      -
    • fieldName - Output field name (tags)
    • +
    • fieldName - Output field name (message)
    • +
    • messageSplitRegex - If null or empty, write the message text as is (the default behavior). + Otherwise, split the message text using the specified regex and write it as an array. + See the Customizing Message section for details.
    logstashMarkers

    Used to output Logstash Markers as specified in Event-specific Custom Fields

    +
    rawMessage

    Raw log event message, as opposed to formatted log where parameters are resolved.

    +
      +
    • fieldName - Output field name (raw_message)
    • +
    nestedField -

    Nests a JSON object under the configured fieldName.

    -

    The nested object is populated by other providers added to this provider.

    -

    See Nested JSON provider

    +
    rootStackTraceElement

    (Only if a throwable was logged) Outputs a JSON Object containing the class and method name from which the outer-most exception was thrown.

      -
    • fieldName - Output field name
    • -
    • providers - The providers that should populate the nested object.
    • +
    • fieldName - Output field name (root_stack_trace_element)
    • +
    • classFieldName - Field name containing the class name from which the outermost exception was thrown (class_name)
    • +
    • methodFieldName - Field name containing the method name from which the outermost exception was thrown (method_name)
    pattern -

    Outputs fields from a configured JSON Object string, - while substituting patterns supported by logback's PatternLayout. -

    -

    - See Pattern JSON Provider -

    +
    stackHash

    (Only if a throwable was logged) Computes and outputs a hexadecimal hash of the throwable stack.

    +

    This helps identifying several occurrences of the same error (more info).

      -
    • pattern - JSON object string (no default)
    • -
    • omitEmptyFields - whether to omit fields with empty values (false)
    • +
    • fieldName - Output field name (stack_hash)
    • +
    • exclude - Regular expression pattern matching stack trace elements to exclude when computing the error hash
    • +
    • exclusions - Coma separated list of regular expression patterns matching stack trace elements to exclude when computing the error hash
    arguments -

    Outputs fields from the event arguments array. -

    -

    - See Event-specific Custom Fields -

    +
    stackTrace

    Stacktrace of any throwable logged with the event. Stackframes are separated by newline chars.

      -
    • fieldName - Sub-object field name (no sub-object)
    • -
    • includeNonStructuredArguments - Include arguments that are not an instance - of StructuredArgument. - (default=false) - Object field name will be nonStructuredArgumentsFieldPrefix prepend to the argument index
    • -
    • nonStructuredArgumentsFieldPrefix - Object field name prefix (default=arg)
    • +
    • fieldName - Output field name (stack_trace)
    • +
    • throwableConverter - The ThrowableHandlingConverter to use to format the stacktrace (stack_trace)
    uuid

    Outputs random UUID as field value. Handy when you want to provide unique identifier - for log lines -

    +
    tags

    Outputs logback markers as a comma separated list.

      -
    • fieldName - Output field name (uuid)
    • -
    • strategy - UUID generation strategy (random). Supported options:
      • random - for Type 4 UUID
      • -
      • time - for Type 1 time based UUID
      • -
    • -
    • ethernet - Only for 'time' strategy. When defined - MAC address to use for location part of UUID. Set it to interface value to use real underlying network interface or to specific values like 00:C0:F0:3D:5B:7C
    • +
    • fieldName - Output field name (tags)
    -

    Note: The com.fasterxml.uuid:java-uuid-generator optional dependency must be added to applications that use the `uuid` provider.

    sequence -

    - Outputs an incrementing sequence number for every log event. - Useful for tracking pottential message loss during transport (eg. UDP) -

    +
    throwableClassName

    (Only if a throwable was logged) Outputs a field that contains the class name of the thrown Throwable.

      -
    • fieldName - Output field name (sequence)
    +
  • fieldName - Output field name (throwable_class)
  • +
  • useSimpleClassName - When true, the throwable's simple class name will be used. When false, the fully qualified class name will be used. (true)
  • +
    throwableRootCauseClassName

    (Only if a throwable was logged) Outputs a field that contains the class name of the root cause of the thrown Throwable.

    +
      +
    • fieldName - Output field name (throwable_root_cause_class)
    • +
    • useSimpleClassName - When true, the throwable's simple class name will be used. When false, the fully qualified class name will be used. (true)
    • +
    +
    -#### Providers for AccessEvents +### Providers for AccessEvents -The table below lists the available providers for AccessEvents, and their configuration properties (defaults in parentheses). -The provider name is the xml element name to use when configuring. +The [common providers mentioned above](#providers-common-to-loggingevents-and-accessevents), and the providers listed in the table below, are available for _AccessEvents_. +The provider name is the xml element name to use when configuring. Each provider's configuration properties are shown, with default configuration values in parenthesis. - + - - + - - + - - - - - - - - - - - - - - - - - - - + - - + - - - - - - - - + diff --git a/src/main/java/net/logstash/logback/LogstashFormatter.java b/src/main/java/net/logstash/logback/LogstashFormatter.java index 4350ae83..49502ada 100644 --- a/src/main/java/net/logstash/logback/LogstashFormatter.java +++ b/src/main/java/net/logstash/logback/LogstashFormatter.java @@ -32,12 +32,12 @@ import net.logstash.logback.composite.loggingevent.LoggingEventCompositeJsonFormatter; import net.logstash.logback.composite.loggingevent.LoggingEventFormattedTimestampJsonProvider; import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders; +import net.logstash.logback.composite.loggingevent.LoggingEventThreadNameJsonProvider; import net.logstash.logback.composite.loggingevent.LogstashMarkersJsonProvider; import net.logstash.logback.composite.loggingevent.MdcJsonProvider; import net.logstash.logback.composite.loggingevent.MessageJsonProvider; import net.logstash.logback.composite.loggingevent.StackTraceJsonProvider; import net.logstash.logback.composite.loggingevent.TagsJsonProvider; -import net.logstash.logback.composite.loggingevent.ThreadNameJsonProvider; import net.logstash.logback.fieldnames.LogstashFieldNames; import ch.qos.logback.classic.pattern.ThrowableHandlingConverter; @@ -71,7 +71,7 @@ public class LogstashFormatter extends LoggingEventCompositeJsonFormatter { private final LogstashVersionJsonProvider versionProvider = new LogstashVersionJsonProvider<>(); private final MessageJsonProvider messageProvider = new MessageJsonProvider(); private final LoggerNameJsonProvider loggerNameProvider = new LoggerNameJsonProvider(); - private final ThreadNameJsonProvider threadNameProvider = new ThreadNameJsonProvider(); + private final LoggingEventThreadNameJsonProvider threadNameProvider = new LoggingEventThreadNameJsonProvider(); private final LogLevelJsonProvider logLevelProvider = new LogLevelJsonProvider(); private final LogLevelValueJsonProvider logLevelValueProvider = new LogLevelValueJsonProvider(); diff --git a/src/main/java/net/logstash/logback/composite/AbstractCompositeJsonFormatter.java b/src/main/java/net/logstash/logback/composite/AbstractCompositeJsonFormatter.java new file mode 100644 index 00000000..269cefa5 --- /dev/null +++ b/src/main/java/net/logstash/logback/composite/AbstractCompositeJsonFormatter.java @@ -0,0 +1,351 @@ +/* + * Copyright 2013-2021 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.composite; + +import java.io.Closeable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Objects; +import java.util.ServiceConfigurationError; + +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.util.ObjectPool; +import net.logstash.logback.util.ProxyOutputStream; + +import ch.qos.logback.access.spi.IAccessEvent; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.DeferredProcessingAware; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.util.CloseUtil; +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonFactory.Feature; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * Formats logstash Events as JSON using {@link JsonProvider}s. + * + *

    The {@link AbstractCompositeJsonFormatter} starts the JSON object ('{'), + * then delegates writing the contents of the object to the {@link JsonProvider}s, + * and then ends the JSON object ('}'). + * + *

    Jackson {@link JsonGenerator} are initially created with a "disconnected" output stream so they can be + * reused multiple times with different target output stream. They are kept in an internal pool whose + * size is technically unbounded. It will however never hold more entries than the number of concurrent + * threads accessing it. Entries are kept in the pool using soft references so they can be garbage + * collected by the JVM when running low in memory. + * + *

    {@link JsonGenerator} instances are *not* reused after they threw an exception. This is to prevent + * reusing an instance whose internal state may be unpredictable. + * + * @param type of event ({@link ILoggingEvent} or {@link IAccessEvent}). + * + * @author brenuart + */ +public abstract class AbstractCompositeJsonFormatter + extends ContextAwareBase implements LifeCycle { + + /** + * Used to create the necessary {@link JsonGenerator}s for generating JSON. + */ + private JsonFactory jsonFactory; + + /** + * Decorates the {@link #jsonFactory}. + * Allows customization of the {@link #jsonFactory}. + */ + private JsonFactoryDecorator jsonFactoryDecorator; + + /** + * Decorates the generators generated by the {@link #jsonFactory}. + * Allows customization of the generators. + */ + private JsonGeneratorDecorator jsonGeneratorDecorator; + + /** + * The providers that are used to populate the output JSON object. + */ + private JsonProviders jsonProviders = new JsonProviders<>(); + + private JsonEncoding encoding = JsonEncoding.UTF8; + + private boolean findAndRegisterJacksonModules = true; + + private volatile boolean started; + + private ObjectPool pool; + + + public AbstractCompositeJsonFormatter(ContextAware declaredOrigin) { + super(declaredOrigin); + } + + @Override + public void start() { + if (isStarted()) { + return; + } + if (jsonFactoryDecorator == null) { + jsonFactoryDecorator = new NullJsonFactoryDecorator(); + } + if (jsonGeneratorDecorator == null) { + jsonGeneratorDecorator = new NullJsonGeneratorDecorator(); + } + if (jsonProviders.getProviders().isEmpty()) { + addError("No providers configured"); + } + jsonFactory = createJsonFactory(); + jsonProviders.setContext(context); + jsonProviders.setJsonFactory(jsonFactory); + jsonProviders.start(); + + pool = new ObjectPool<>(this::createJsonFormatter); + started = true; + } + + @Override + public void stop() { + if (isStarted()) { + pool.clear(); + jsonProviders.stop(); + jsonFactory = null; + started = false; + } + } + + @Override + public boolean isStarted() { + return started; + } + + + /** + * Write an event in the given output stream. + * + * @param event the event to write + * @param outputStream the output stream to write the event into + * @throws IOException thrown upon failure to write the event + */ + public void writeEvent(Event event, OutputStream outputStream) throws IOException { + Objects.requireNonNull(outputStream); + if (!isStarted()) { + throw new IllegalStateException("Formatter is not started"); + } + + try (JsonFormatter formatter = this.pool.acquire()) { + formatter.writeEvent(outputStream, event); + } + } + + + /** + * Create a reusable {@link JsonFormatter} bound to a {@link DisconnectedOutputStream}. + * + * @return {@link JsonFormatter} writing JSON content in the output stream + */ + private JsonFormatter createJsonFormatter() { + try { + DisconnectedOutputStream outputStream = new DisconnectedOutputStream(); + JsonGenerator generator = createGenerator(outputStream); + return new JsonFormatter(outputStream, generator); + } catch (IOException e) { + throw new IllegalStateException("Unable to initialize Jackson JSON layer", e); + } + + } + + private class JsonFormatter implements ObjectPool.Lifecycle, Closeable { + private final JsonGenerator generator; + private final DisconnectedOutputStream stream; + private boolean recyclable = true; + + JsonFormatter(DisconnectedOutputStream outputStream, JsonGenerator generator) { + this.stream = Objects.requireNonNull(outputStream); + this.generator = Objects.requireNonNull(generator); + } + + public void writeEvent(OutputStream outputStream, Event event) throws IOException { + try { + this.stream.connect(outputStream); + writeEventToGenerator(generator, event); + + } catch (IOException | RuntimeException e) { + this.recyclable = false; + throw e; + + } finally { + this.stream.disconnect(); + } + } + + @Override + public boolean recycle() { + return this.recyclable; + } + + @Override + public void dispose() { + CloseUtil.closeQuietly(this.generator); + } + + @Override + public void close() throws IOException { + AbstractCompositeJsonFormatter.this.pool.release(this); + } + } + + private static class DisconnectedOutputStream extends ProxyOutputStream { + DisconnectedOutputStream() { + super(null); + } + + public void connect(OutputStream out) { + this.delegate = out; + } + + public void disconnect() { + this.delegate = null; + } + } + + private JsonFactory createJsonFactory() { + ObjectMapper objectMapper = new ObjectMapper() + /* + * Assume empty beans are ok. + */ + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + + if (findAndRegisterJacksonModules) { + try { + objectMapper.findAndRegisterModules(); + } catch (ServiceConfigurationError serviceConfigurationError) { + addError("Error occurred while dynamically loading jackson modules", serviceConfigurationError); + } + } + + return decorateFactory(objectMapper.getFactory()); + } + + private JsonFactory decorateFactory(JsonFactory factory) { + return this.jsonFactoryDecorator.decorate(factory) + /* + * Jackson buffer recycling works by maintaining a pool of buffers per thread. This + * feature works best when one JsonGenerator is created per thread, typically in J2EE + * environments. + * + * Each JsonFormatter uses its own instance of JsonGenerator and is reused multiple times + * possibly on different threads. The memory buffers allocated by the JsonGenerator do + * not belong to a particular thread - hence the recycling feature should be disabled. + */ + .disable(Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING); + } + + protected void writeEventToGenerator(JsonGenerator generator, Event event) throws IOException { + generator.writeStartObject(); + jsonProviders.writeTo(generator, event); + generator.writeEndObject(); + generator.flush(); + } + + protected void prepareForDeferredProcessing(Event event) { + event.prepareForDeferredProcessing(); + jsonProviders.prepareForDeferredProcessing(event); + } + + private JsonGenerator createGenerator(OutputStream outputStream) throws IOException { + return decorateGenerator(jsonFactory.createGenerator(outputStream, encoding)); + } + + private JsonGenerator decorateGenerator(JsonGenerator generator) { + return this.jsonGeneratorDecorator.decorate(generator) + /* + * When generators are flushed, don't flush the underlying outputStream. + * + * This allows some streaming optimizations when using an encoder. + * + * The encoder generally determines when the stream should be flushed + * by an 'immediateFlush' property. + * + * The 'immediateFlush' property of the encoder can be set to false + * when the appender performs the flushes at appropriate times + * (such as the end of a batch in the AbstractLogstashTcpSocketAppender). + */ + .disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM) + + /* + * Don't let the json generator close the underlying outputStream and let the + * encoder managed it. + */ + .disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + } + + public JsonFactory getJsonFactory() { + return jsonFactory; + } + + 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 JsonProviders getProviders() { + return jsonProviders; + } + + public String getEncoding() { + return encoding.getJavaName(); + } + + public void setEncoding(String encodingName) { + for (JsonEncoding encoding: JsonEncoding.values()) { + if (encoding.getJavaName().equalsIgnoreCase(encodingName) || encoding.name().equalsIgnoreCase(encodingName)) { + this.encoding = encoding; + return; + } + } + throw new IllegalArgumentException("Unknown encoding " + encodingName); + } + + public void setProviders(JsonProviders jsonProviders) { + this.jsonProviders = Objects.requireNonNull(jsonProviders); + } + + public boolean isFindAndRegisterJacksonModules() { + return findAndRegisterJacksonModules; + } + + public void setFindAndRegisterJacksonModules(boolean findAndRegisterJacksonModules) { + this.findAndRegisterJacksonModules = findAndRegisterJacksonModules; + } +} diff --git a/src/main/java/net/logstash/logback/composite/AbstractFormattedTimestampJsonProvider.java b/src/main/java/net/logstash/logback/composite/AbstractFormattedTimestampJsonProvider.java new file mode 100644 index 00000000..f6011769 --- /dev/null +++ b/src/main/java/net/logstash/logback/composite/AbstractFormattedTimestampJsonProvider.java @@ -0,0 +1,245 @@ +/* + * Copyright 2013-2021 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.composite; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.time.Instant; +import java.time.format.DateTimeFormatter; +import java.util.TimeZone; + +import net.logstash.logback.fieldnames.LogstashCommonFieldNames; +import net.logstash.logback.util.TimeZoneUtils; + +import ch.qos.logback.core.spi.DeferredProcessingAware; +import com.fasterxml.jackson.core.JsonGenerator; + +/** + * Writes the timestamp field as either: + *

    + */ +public abstract class AbstractFormattedTimestampJsonProvider extends AbstractFieldJsonProvider implements FieldNamesAware { + + public static final String FIELD_TIMESTAMP = "@timestamp"; + + /** + * Setting the {@link #pattern} as this value will make it so that the timestamp + * is written as a number value of the milliseconds since unix epoch. + */ + public static final String UNIX_TIMESTAMP_AS_NUMBER = "[UNIX_TIMESTAMP_AS_NUMBER]"; + + /** + * Setting the {@link #pattern} as this value will make it so that the timestamp + * is written as a string value representing the number of milliseconds since unix epoch + */ + public static final String UNIX_TIMESTAMP_AS_STRING = "[UNIX_TIMESTAMP_AS_STRING]"; + + /** + * The default {@link #pattern} value. + */ + private static final String DEFAULT_PATTERN = "[ISO_OFFSET_DATE_TIME]"; + + /** + * Keyword used by {@link #setTimeZone(String)} to denote the system default time zone. + */ + public static final String DEFAULT_TIMEZONE_KEYWORD = "[DEFAULT]"; + + /** + * The pattern in which to format the timestamp. + * + *

    Possible values:

    + * + *
      + *
    • {@value #UNIX_TIMESTAMP_AS_NUMBER} - timestamp written as a JSON number value of the milliseconds since unix epoch
    • + *
    • {@value #UNIX_TIMESTAMP_AS_STRING} - timestamp written as a JSON string value of the milliseconds since unix epoch
    • + *
    • [constant] - timestamp written using the {@link DateTimeFormatter} constant specified by constant (e.g. {@code [ISO_OFFSET_DATE_TIME]})
    • + *
    • any other value - timestamp written by a {@link DateTimeFormatter} created from the pattern string specified + *
    + */ + private String pattern = DEFAULT_PATTERN; + + /** + * The timezone for which to write the timestamp. + * Only applicable if the pattern is not {@value #UNIX_TIMESTAMP_AS_NUMBER} or {@value #UNIX_TIMESTAMP_AS_STRING} + */ + private TimeZone timeZone = TimeZone.getDefault(); + + /** + * Writes the timestamp to the JsonGenerator. + */ + private TimestampWriter timestampWriter; + + /** + * Writes the timestamp to the JsonGenerator + */ + private interface TimestampWriter { + void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException; + + String getTimestampAsString(long timestampInMillis); + } + + /** + * Writes the timestamp to the JsonGenerator as a string formatted by the pattern. + */ + private static class PatternTimestampWriter implements TimestampWriter { + + private final DateTimeFormatter formatter; + + PatternTimestampWriter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + + @Override + public void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException { + JsonWritingUtils.writeStringField(generator, fieldName, getTimestampAsString(timestampInMillis)); + } + + @Override + public String getTimestampAsString(long timestampInMillis) { + return formatter.format(Instant.ofEpochMilli(timestampInMillis)); + } + } + + /** + * Writes the timestamp to the JsonGenerator as a number of milliseconds since unix epoch. + */ + private static class NumberTimestampWriter implements TimestampWriter { + + @Override + public void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException { + JsonWritingUtils.writeNumberField(generator, fieldName, timestampInMillis); + } + + @Override + public String getTimestampAsString(long timestampInMillis) { + return Long.toString(timestampInMillis); + } + } + + /** + * Writes the timestamp to the JsonGenerator as a string representation of the of milliseconds since unix epoch. + */ + private static class StringTimestampWriter implements TimestampWriter { + + @Override + public void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException { + JsonWritingUtils.writeStringField(generator, fieldName, getTimestampAsString(timestampInMillis)); + } + + @Override + public String getTimestampAsString(long timestampInMillis) { + return Long.toString(timestampInMillis); + } + + } + + public AbstractFormattedTimestampJsonProvider() { + setFieldName(FIELD_TIMESTAMP); + updateTimestampWriter(); + } + + @Override + public void setFieldNames(FieldNames fieldNames) { + setFieldName(fieldNames.getTimestamp()); + } + + @Override + public void writeTo(JsonGenerator generator, Event event) throws IOException { + timestampWriter.writeTo(generator, getFieldName(), getTimestampAsMillis(event)); + } + + protected String getFormattedTimestamp(Event event) { + return timestampWriter.getTimestampAsString(getTimestampAsMillis(event)); + } + + protected abstract long getTimestampAsMillis(Event event); + + /** + * Updates the {@link #timestampWriter} value based on the current pattern and timeZone. + */ + private void updateTimestampWriter() { + if (UNIX_TIMESTAMP_AS_NUMBER.equals(pattern)) { + timestampWriter = new NumberTimestampWriter(); + } else if (UNIX_TIMESTAMP_AS_STRING.equals(pattern)) { + timestampWriter = new StringTimestampWriter(); + } else if (pattern.startsWith("[") && pattern.endsWith("]")) { + String constant = pattern.substring("[".length(), pattern.length() - "]".length()); + try { + Field field = DateTimeFormatter.class.getField(constant); + if (Modifier.isStatic(field.getModifiers()) + && Modifier.isFinal(field.getModifiers()) + && field.getType().equals(DateTimeFormatter.class)) { + try { + DateTimeFormatter formatter = (DateTimeFormatter) field.get(null); + timestampWriter = new PatternTimestampWriter(formatter.withZone(timeZone.toZoneId())); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException(String.format("Unable to get value of constant named %s in %s", constant, DateTimeFormatter.class), e); + } + } else { + throw new IllegalArgumentException(String.format("Field named %s in %s is not a constant %s", constant, DateTimeFormatter.class, DateTimeFormatter.class)); + } + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException(String.format("No constant named %s found in %s", constant, DateTimeFormatter.class), e); + } + } else { + timestampWriter = new PatternTimestampWriter(DateTimeFormatter.ofPattern(pattern).withZone(timeZone.toZoneId())); + } + } + + public String getPattern() { + return pattern; + } + public void setPattern(String pattern) { + this.pattern = pattern; + updateTimestampWriter(); + } + + /** + * Get the time zone used to write the timestamp. + * + * @return the time zone used to write the timestamp + */ + public String getTimeZone() { + return timeZone.getID(); + } + + /** + * Set the timezone for which to write the timestamp. + * Only applicable if the pattern is not {@value #UNIX_TIMESTAMP_AS_NUMBER} or {@value #UNIX_TIMESTAMP_AS_STRING}. + * + *

    The value of the {@code timeZone} can be any string accepted by java's {@link TimeZone#getTimeZone(String)} method. + * For example "America/Los_Angeles" or "GMT+10". + * + *

    Use a blank string, {@code null} or the value {@value #DEFAULT_TIMEZONE_KEYWORD} to use the default TimeZone of the system. + * + * @param timeZone the textual representation of the desired time zone + * @throws IllegalArgumentException if the input string is not a valid TimeZone representation + */ + public void setTimeZone(String timeZone) { + if (timeZone == null || timeZone.trim().isEmpty() || DEFAULT_TIMEZONE_KEYWORD.equalsIgnoreCase(timeZone)) { + this.timeZone = TimeZone.getDefault(); + } else { + this.timeZone = TimeZoneUtils.parseTimeZone(timeZone); + } + updateTimestampWriter(); + } +} diff --git a/src/main/java/net/logstash/logback/composite/AbstractThreadNameJsonProvider.java b/src/main/java/net/logstash/logback/composite/AbstractThreadNameJsonProvider.java new file mode 100644 index 00000000..01b29d33 --- /dev/null +++ b/src/main/java/net/logstash/logback/composite/AbstractThreadNameJsonProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013-2021 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.composite; + +import java.io.IOException; + +import net.logstash.logback.fieldnames.LogstashCommonFieldNames; + +import ch.qos.logback.core.spi.DeferredProcessingAware; +import com.fasterxml.jackson.core.JsonGenerator; + +public abstract class AbstractThreadNameJsonProvider extends AbstractFieldJsonProvider implements FieldNamesAware { + + public static final String FIELD_THREAD_NAME = "thread_name"; + + public AbstractThreadNameJsonProvider() { + setFieldName(FIELD_THREAD_NAME); + } + + @Override + public void writeTo(JsonGenerator generator, Event event) throws IOException { + JsonWritingUtils.writeStringField(generator, getFieldName(), getThreadName(event)); + } + + @Override + public void setFieldNames(LogstashCommonFieldNames fieldNames) { + setFieldName(fieldNames.getThread()); + } + + protected abstract String getThreadName(Event event); +} diff --git a/src/main/java/net/logstash/logback/composite/CompositeJsonFormatter.java b/src/main/java/net/logstash/logback/composite/CompositeJsonFormatter.java index 309d95c0..5a19f9b7 100644 --- a/src/main/java/net/logstash/logback/composite/CompositeJsonFormatter.java +++ b/src/main/java/net/logstash/logback/composite/CompositeJsonFormatter.java @@ -15,337 +15,17 @@ */ package net.logstash.logback.composite; -import java.io.Closeable; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Objects; -import java.util.ServiceConfigurationError; - -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.util.ObjectPool; -import net.logstash.logback.util.ProxyOutputStream; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.spi.ContextAware; -import ch.qos.logback.core.spi.ContextAwareBase; import ch.qos.logback.core.spi.DeferredProcessingAware; -import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.util.CloseUtil; -import com.fasterxml.jackson.core.JsonEncoding; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonFactory.Feature; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; /** - * Formats logstash Events as JSON using {@link JsonProvider}s. - * - *

    The {@link CompositeJsonFormatter} starts the JSON object ('{'), - * then delegates writing the contents of the object to the {@link JsonProvider}s, - * and then ends the JSON object ('}'). - * - *

    Jackson {@link JsonGenerator} are initially created with a "disconnected" output stream so they can be - * reused multiple times with different target output stream. They are kept in an internal pool whose - * size is technically unbounded. It will however never hold more entries than the number of concurrent - * threads accessing it. Entries are kept in the pool using soft references so they can be garbage - * collected by the JVM when running low in memory. - * - *

    {@link JsonGenerator} instances are *not* reused after they threw an exception. This is to prevent - * reusing an instance whose internal state may be unpredictable. - * - * @param type of event ({@link ILoggingEvent} or {@link IAccessEvent}). - * - * @author brenuart + * @deprecated use {@link AbstractCompositeJsonFormatter} instead. */ -public abstract class CompositeJsonFormatter - extends ContextAwareBase implements LifeCycle { - - /** - * Used to create the necessary {@link JsonGenerator}s for generating JSON. - */ - private JsonFactory jsonFactory; - - /** - * Decorates the {@link #jsonFactory}. - * Allows customization of the {@link #jsonFactory}. - */ - private JsonFactoryDecorator jsonFactoryDecorator; - - /** - * Decorates the generators generated by the {@link #jsonFactory}. - * Allows customization of the generators. - */ - private JsonGeneratorDecorator jsonGeneratorDecorator; +@Deprecated +public abstract class CompositeJsonFormatter extends AbstractCompositeJsonFormatter { - /** - * The providers that are used to populate the output JSON object. - */ - private JsonProviders jsonProviders = new JsonProviders<>(); - - private JsonEncoding encoding = JsonEncoding.UTF8; - - private boolean findAndRegisterJacksonModules = true; - - private volatile boolean started; - - private ObjectPool pool; - - public CompositeJsonFormatter(ContextAware declaredOrigin) { super(declaredOrigin); } - @Override - public void start() { - if (isStarted()) { - return; - } - if (jsonFactoryDecorator == null) { - jsonFactoryDecorator = new NullJsonFactoryDecorator(); - } - if (jsonGeneratorDecorator == null) { - jsonGeneratorDecorator = new NullJsonGeneratorDecorator(); - } - if (jsonProviders.getProviders().isEmpty()) { - addError("No providers configured"); - } - jsonFactory = createJsonFactory(); - jsonProviders.setContext(context); - jsonProviders.setJsonFactory(jsonFactory); - jsonProviders.start(); - - pool = new ObjectPool<>(this::createJsonFormatter); - started = true; - } - - @Override - public void stop() { - if (isStarted()) { - pool.clear(); - jsonProviders.stop(); - jsonFactory = null; - started = false; - } - } - - @Override - public boolean isStarted() { - return started; - } - - - /** - * Write an event in the given output stream. - * - * @param event the event to write - * @param outputStream the output stream to write the event into - * @throws IOException thrown upon failure to write the event - */ - public void writeEvent(Event event, OutputStream outputStream) throws IOException { - Objects.requireNonNull(outputStream); - if (!isStarted()) { - throw new IllegalStateException("Formatter is not started"); - } - - try (JsonFormatter formatter = this.pool.acquire()) { - formatter.writeEvent(outputStream, event); - } - } - - - /** - * Create a reusable {@link JsonFormatter} bound to a {@link DisconnectedOutputStream}. - * - * @return {@link JsonFormatter} writing JSON content in the output stream - */ - private JsonFormatter createJsonFormatter() { - try { - DisconnectedOutputStream outputStream = new DisconnectedOutputStream(); - JsonGenerator generator = createGenerator(outputStream); - return new JsonFormatter(outputStream, generator); - } catch (IOException e) { - throw new IllegalStateException("Unable to initialize Jackson JSON layer", e); - } - - } - - private class JsonFormatter implements ObjectPool.Lifecycle, Closeable { - private final JsonGenerator generator; - private final DisconnectedOutputStream stream; - private boolean recyclable = true; - - JsonFormatter(DisconnectedOutputStream outputStream, JsonGenerator generator) { - this.stream = Objects.requireNonNull(outputStream); - this.generator = Objects.requireNonNull(generator); - } - - public void writeEvent(OutputStream outputStream, Event event) throws IOException { - try { - this.stream.connect(outputStream); - writeEventToGenerator(generator, event); - - } catch (IOException | RuntimeException e) { - this.recyclable = false; - throw e; - - } finally { - this.stream.disconnect(); - } - } - - @Override - public boolean recycle() { - return this.recyclable; - } - - @Override - public void dispose() { - CloseUtil.closeQuietly(this.generator); - } - - @Override - public void close() throws IOException { - CompositeJsonFormatter.this.pool.release(this); - } - } - - private static class DisconnectedOutputStream extends ProxyOutputStream { - DisconnectedOutputStream() { - super(null); - } - - public void connect(OutputStream out) { - this.delegate = out; - } - - public void disconnect() { - this.delegate = null; - } - } - - private JsonFactory createJsonFactory() { - ObjectMapper objectMapper = new ObjectMapper() - /* - * Assume empty beans are ok. - */ - .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - - if (findAndRegisterJacksonModules) { - try { - objectMapper.findAndRegisterModules(); - } catch (ServiceConfigurationError serviceConfigurationError) { - addError("Error occurred while dynamically loading jackson modules", serviceConfigurationError); - } - } - - return decorateFactory(objectMapper.getFactory()); - } - - private JsonFactory decorateFactory(JsonFactory factory) { - return this.jsonFactoryDecorator.decorate(factory) - /* - * Jackson buffer recycling works by maintaining a pool of buffers per thread. This - * feature works best when one JsonGenerator is created per thread, typically in J2EE - * environments. - * - * Each JsonFormatter uses its own instance of JsonGenerator and is reused multiple times - * possibly on different threads. The memory buffers allocated by the JsonGenerator do - * not belong to a particular thread - hence the recycling feature should be disabled. - */ - .disable(Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING); - } - - protected void writeEventToGenerator(JsonGenerator generator, Event event) throws IOException { - generator.writeStartObject(); - jsonProviders.writeTo(generator, event); - generator.writeEndObject(); - generator.flush(); - } - - protected void prepareForDeferredProcessing(Event event) { - event.prepareForDeferredProcessing(); - jsonProviders.prepareForDeferredProcessing(event); - } - - private JsonGenerator createGenerator(OutputStream outputStream) throws IOException { - return decorateGenerator(jsonFactory.createGenerator(outputStream, encoding)); - } - - private JsonGenerator decorateGenerator(JsonGenerator generator) { - return this.jsonGeneratorDecorator.decorate(generator) - /* - * When generators are flushed, don't flush the underlying outputStream. - * - * This allows some streaming optimizations when using an encoder. - * - * The encoder generally determines when the stream should be flushed - * by an 'immediateFlush' property. - * - * The 'immediateFlush' property of the encoder can be set to false - * when the appender performs the flushes at appropriate times - * (such as the end of a batch in the AbstractLogstashTcpSocketAppender). - */ - .disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM) - - /* - * Don't let the json generator close the underlying outputStream and let the - * encoder managed it. - */ - .disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - } - - public JsonFactory getJsonFactory() { - return jsonFactory; - } - - 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 JsonProviders getProviders() { - return jsonProviders; - } - - public String getEncoding() { - return encoding.getJavaName(); - } - - public void setEncoding(String encodingName) { - for (JsonEncoding encoding: JsonEncoding.values()) { - if (encoding.getJavaName().equalsIgnoreCase(encodingName) || encoding.name().equalsIgnoreCase(encodingName)) { - this.encoding = encoding; - return; - } - } - throw new IllegalArgumentException("Unknown encoding " + encodingName); - } - - public void setProviders(JsonProviders jsonProviders) { - this.jsonProviders = Objects.requireNonNull(jsonProviders); - } - - public boolean isFindAndRegisterJacksonModules() { - return findAndRegisterJacksonModules; - } - - public void setFindAndRegisterJacksonModules(boolean findAndRegisterJacksonModules) { - this.findAndRegisterJacksonModules = findAndRegisterJacksonModules; - } } diff --git a/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java b/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java index 76757336..a830b542 100644 --- a/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java +++ b/src/main/java/net/logstash/logback/composite/FormattedTimestampJsonProvider.java @@ -15,232 +15,13 @@ */ package net.logstash.logback.composite; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.time.Instant; -import java.time.format.DateTimeFormatter; -import java.util.TimeZone; - import net.logstash.logback.fieldnames.LogstashCommonFieldNames; -import net.logstash.logback.util.TimeZoneUtils; import ch.qos.logback.core.spi.DeferredProcessingAware; -import com.fasterxml.jackson.core.JsonGenerator; /** - * Writes the timestamp field as either: - *

      - *
    • A string value formatted by a {@link DateTimeFormatter} pattern
    • - *
    • A string value representing the number of milliseconds since unix epoch (designated by specifying the pattern value as {@value #UNIX_TIMESTAMP_AS_STRING})
    • - *
    • A number value of the milliseconds since unix epoch (designated by specifying the pattern value as {@value #UNIX_TIMESTAMP_AS_NUMBER})
    • - *
    + * @deprecated use {@link AbstractFormattedTimestampJsonProvider} instead */ -public abstract class FormattedTimestampJsonProvider extends AbstractFieldJsonProvider implements FieldNamesAware { - - public static final String FIELD_TIMESTAMP = "@timestamp"; - - /** - * Setting the {@link #pattern} as this value will make it so that the timestamp - * is written as a number value of the milliseconds since unix epoch. - */ - public static final String UNIX_TIMESTAMP_AS_NUMBER = "[UNIX_TIMESTAMP_AS_NUMBER]"; - - /** - * Setting the {@link #pattern} as this value will make it so that the timestamp - * is written as a string value representing the number of milliseconds since unix epoch - */ - public static final String UNIX_TIMESTAMP_AS_STRING = "[UNIX_TIMESTAMP_AS_STRING]"; - - /** - * The default {@link #pattern} value. - */ - private static final String DEFAULT_PATTERN = "[ISO_OFFSET_DATE_TIME]"; - - /** - * Keyword used by {@link #setTimeZone(String)} to denote the system default time zone. - */ - public static final String DEFAULT_TIMEZONE_KEYWORD = "[DEFAULT]"; - - /** - * The pattern in which to format the timestamp. - * - *

    Possible values:

    - * - *
      - *
    • {@value #UNIX_TIMESTAMP_AS_NUMBER} - timestamp written as a JSON number value of the milliseconds since unix epoch
    • - *
    • {@value #UNIX_TIMESTAMP_AS_STRING} - timestamp written as a JSON string value of the milliseconds since unix epoch
    • - *
    • [constant] - timestamp written using the {@link DateTimeFormatter} constant specified by constant (e.g. {@code [ISO_OFFSET_DATE_TIME]})
    • - *
    • any other value - timestamp written by a {@link DateTimeFormatter} created from the pattern string specified - *
    - */ - private String pattern = DEFAULT_PATTERN; - - /** - * The timezone for which to write the timestamp. - * Only applicable if the pattern is not {@value #UNIX_TIMESTAMP_AS_NUMBER} or {@value #UNIX_TIMESTAMP_AS_STRING} - */ - private TimeZone timeZone = TimeZone.getDefault(); - - /** - * Writes the timestamp to the JsonGenerator. - */ - private TimestampWriter timestampWriter; - - /** - * Writes the timestamp to the JsonGenerator - */ - private interface TimestampWriter { - void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException; - - String getTimestampAsString(long timestampInMillis); - } - - /** - * Writes the timestamp to the JsonGenerator as a string formatted by the pattern. - */ - private static class PatternTimestampWriter implements TimestampWriter { - - private final DateTimeFormatter formatter; - - PatternTimestampWriter(DateTimeFormatter formatter) { - this.formatter = formatter; - } - - - @Override - public void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException { - JsonWritingUtils.writeStringField(generator, fieldName, getTimestampAsString(timestampInMillis)); - } - - @Override - public String getTimestampAsString(long timestampInMillis) { - return formatter.format(Instant.ofEpochMilli(timestampInMillis)); - } - } - - /** - * Writes the timestamp to the JsonGenerator as a number of milliseconds since unix epoch. - */ - private static class NumberTimestampWriter implements TimestampWriter { - - @Override - public void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException { - JsonWritingUtils.writeNumberField(generator, fieldName, timestampInMillis); - } - - @Override - public String getTimestampAsString(long timestampInMillis) { - return Long.toString(timestampInMillis); - } - } - - /** - * Writes the timestamp to the JsonGenerator as a string representation of the of milliseconds since unix epoch. - */ - private static class StringTimestampWriter implements TimestampWriter { - - @Override - public void writeTo(JsonGenerator generator, String fieldName, long timestampInMillis) throws IOException { - JsonWritingUtils.writeStringField(generator, fieldName, getTimestampAsString(timestampInMillis)); - } - - @Override - public String getTimestampAsString(long timestampInMillis) { - return Long.toString(timestampInMillis); - } - - } - - public FormattedTimestampJsonProvider() { - setFieldName(FIELD_TIMESTAMP); - updateTimestampWriter(); - } - - @Override - public void setFieldNames(FieldNames fieldNames) { - setFieldName(fieldNames.getTimestamp()); - } - - @Override - public void writeTo(JsonGenerator generator, Event event) throws IOException { - timestampWriter.writeTo(generator, getFieldName(), getTimestampAsMillis(event)); - } - - protected String getFormattedTimestamp(Event event) { - return timestampWriter.getTimestampAsString(getTimestampAsMillis(event)); - } - - protected abstract long getTimestampAsMillis(Event event); - - /** - * Updates the {@link #timestampWriter} value based on the current pattern and timeZone. - */ - private void updateTimestampWriter() { - if (UNIX_TIMESTAMP_AS_NUMBER.equals(pattern)) { - timestampWriter = new NumberTimestampWriter(); - } else if (UNIX_TIMESTAMP_AS_STRING.equals(pattern)) { - timestampWriter = new StringTimestampWriter(); - } else if (pattern.startsWith("[") && pattern.endsWith("]")) { - String constant = pattern.substring("[".length(), pattern.length() - "]".length()); - try { - Field field = DateTimeFormatter.class.getField(constant); - if (Modifier.isStatic(field.getModifiers()) - && Modifier.isFinal(field.getModifiers()) - && field.getType().equals(DateTimeFormatter.class)) { - try { - DateTimeFormatter formatter = (DateTimeFormatter) field.get(null); - timestampWriter = new PatternTimestampWriter(formatter.withZone(timeZone.toZoneId())); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException(String.format("Unable to get value of constant named %s in %s", constant, DateTimeFormatter.class), e); - } - } else { - throw new IllegalArgumentException(String.format("Field named %s in %s is not a constant %s", constant, DateTimeFormatter.class, DateTimeFormatter.class)); - } - } catch (NoSuchFieldException e) { - throw new IllegalArgumentException(String.format("No constant named %s found in %s", constant, DateTimeFormatter.class), e); - } - } else { - timestampWriter = new PatternTimestampWriter(DateTimeFormatter.ofPattern(pattern).withZone(timeZone.toZoneId())); - } - } +public abstract class FormattedTimestampJsonProvider extends AbstractFormattedTimestampJsonProvider { - public String getPattern() { - return pattern; - } - public void setPattern(String pattern) { - this.pattern = pattern; - updateTimestampWriter(); - } - - /** - * Get the time zone used to write the timestamp. - * - * @return the time zone used to write the timestamp - */ - public String getTimeZone() { - return timeZone.getID(); - } - - - /** - * Set the timezone for which to write the timestamp. - * Only applicable if the pattern is not {@value #UNIX_TIMESTAMP_AS_NUMBER} or {@value #UNIX_TIMESTAMP_AS_STRING}. - * - *

    The value of the {@code timeZone} can be any string accepted by java's {@link TimeZone#getTimeZone(String)} method. - * For example "America/Los_Angeles" or "GMT+10". - * - *

    Use a blank string, {@code null} or the value {@value #DEFAULT_TIMEZONE_KEYWORD} to use the default TimeZone of the system. - * - * @param timeZone the textual representation of the desired time zone - * @throws IllegalArgumentException if the input string is not a valid TimeZone representation - */ - public void setTimeZone(String timeZone) { - if (timeZone == null || timeZone.trim().isEmpty() || DEFAULT_TIMEZONE_KEYWORD.equalsIgnoreCase(timeZone)) { - this.timeZone = TimeZone.getDefault(); - } else { - this.timeZone = TimeZoneUtils.parseTimeZone(timeZone); - } - updateTimestampWriter(); - } } diff --git a/src/main/java/net/logstash/logback/composite/JsonProviders.java b/src/main/java/net/logstash/logback/composite/JsonProviders.java index f972a25d..0b8f50a8 100644 --- a/src/main/java/net/logstash/logback/composite/JsonProviders.java +++ b/src/main/java/net/logstash/logback/composite/JsonProviders.java @@ -41,7 +41,7 @@ */ public class JsonProviders implements JsonFactoryAware { - private final List> jsonProviders = new ArrayList>(); + private final List> jsonProviders = new ArrayList<>(); public void start() { for (JsonProvider jsonProvider : jsonProviders) { @@ -101,4 +101,16 @@ public List> getProviders() { public void addContext(ContextJsonProvider provider) { addProvider(provider); } + public void addGlobalCustomFields(GlobalCustomFieldsJsonProvider provider) { + addProvider(provider); + } + public void addSequence(SequenceJsonProvider provider) { + addProvider(provider); + } + public void addUuid(UuidJsonProvider provider) { + addProvider(provider); + } + public void addVersion(LogstashVersionJsonProvider provider) { + addProvider(provider); + } } diff --git a/src/main/java/net/logstash/logback/composite/SequenceJsonProvider.java b/src/main/java/net/logstash/logback/composite/SequenceJsonProvider.java new file mode 100644 index 00000000..1df80bbb --- /dev/null +++ b/src/main/java/net/logstash/logback/composite/SequenceJsonProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2021 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.composite; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; + +import ch.qos.logback.core.spi.DeferredProcessingAware; +import com.fasterxml.jackson.core.JsonGenerator; + +/** + * Outputs an incrementing sequence number. + * Useful for determining if log events get lost along the transport chain. + */ +public class SequenceJsonProvider extends AbstractFieldJsonProvider { + + public static final String FIELD_SEQUENCE = "sequence"; + + private final AtomicLong sequenceNumber = new AtomicLong(0L); + + public SequenceJsonProvider() { + setFieldName(FIELD_SEQUENCE); + } + + @Override + public void writeTo(JsonGenerator generator, Event iLoggingEvent) throws IOException { + JsonWritingUtils.writeNumberField(generator, getFieldName(), sequenceNumber.incrementAndGet()); + } + +} diff --git a/src/main/java/net/logstash/logback/composite/UuidJsonProvider.java b/src/main/java/net/logstash/logback/composite/UuidJsonProvider.java new file mode 100644 index 00000000..bcf4362c --- /dev/null +++ b/src/main/java/net/logstash/logback/composite/UuidJsonProvider.java @@ -0,0 +1,113 @@ +/* + * Copyright 2013-2021 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.composite; + +import java.io.IOException; + +import ch.qos.logback.core.spi.DeferredProcessingAware; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.uuid.EthernetAddress; +import com.fasterxml.uuid.Generators; +import com.fasterxml.uuid.NoArgGenerator; +import com.fasterxml.uuid.impl.TimeBasedGenerator; + +/** + * Outputs random UUID as field value. + * Handy when you want to provide unique identifier for log lines. + */ +public class UuidJsonProvider extends AbstractFieldJsonProvider { + + public static final String FIELD_UUID = "uuid"; + + /** + * Type 4 UUID. + */ + public static final String STRATEGY_RANDOM = "random"; + + /** + * Type 1 time based UUID. + * + * When the time strategy is used, then + * {@link #ethernet} can be set to either 'interface' (to automatically pick a MAC address from a network interface) + * or a MAC address string. + */ + public static final String STRATEGY_TIME = "time"; + + private NoArgGenerator uuids = Generators.randomBasedGenerator(); + + /** + * One of {@value #STRATEGY_RANDOM} or {@value #STRATEGY_TIME}. + */ + private String strategy = STRATEGY_RANDOM; + + /** + * For {@link UuidStrategy#time} strategy only, + * 'interface' or ethernet MAC address. + */ + private String ethernet; + + public UuidJsonProvider() { + setFieldName(FIELD_UUID); + } + + @Override + public void writeTo(JsonGenerator generator, Event event) throws IOException { + JsonWritingUtils.writeStringField(generator, getFieldName(), uuids.generate().toString()); + } + + public String getStrategy() { + return strategy; + } + + public void setStrategy(String strategy) { + this.strategy = strategy; + + uuids = newUuidStrategy(strategy, ethernet); + } + + public String getEthernet() { + return ethernet; + } + + public void setEthernet(String ethernet) { + this.ethernet = ethernet; + + uuids = newUuidStrategy(this.strategy, this.ethernet); + } + + private NoArgGenerator newUuidStrategy(String strategy, String ethernet) { + + if (STRATEGY_TIME.equalsIgnoreCase(strategy)) { + return newTimeBasedGenerator(ethernet); + } + if (STRATEGY_RANDOM.equalsIgnoreCase(strategy)) { + return Generators.randomBasedGenerator(); + } + throw new IllegalArgumentException("Unknown strategy: " + strategy); + } + + private TimeBasedGenerator newTimeBasedGenerator(String ethernet) { + if (ethernet == null) { + return Generators.timeBasedGenerator(); + } + + if ("interface".equalsIgnoreCase(ethernet)) { + return Generators.timeBasedGenerator(EthernetAddress.fromInterface()); + } + + return Generators.timeBasedGenerator(EthernetAddress.valueOf(ethernet)); + } +} diff --git a/src/main/java/net/logstash/logback/composite/accessevent/AccessEventCompositeJsonFormatter.java b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventCompositeJsonFormatter.java index d2eaffe0..81e33984 100644 --- a/src/main/java/net/logstash/logback/composite/accessevent/AccessEventCompositeJsonFormatter.java +++ b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventCompositeJsonFormatter.java @@ -15,7 +15,7 @@ */ package net.logstash.logback.composite.accessevent; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import ch.qos.logback.access.spi.IAccessEvent; @@ -23,9 +23,9 @@ import ch.qos.logback.core.spi.ContextAware; /** - * A {@link CompositeJsonFormatter} for {@link IAccessEvent}s. + * A {@link AbstractCompositeJsonFormatter} for {@link IAccessEvent}s. */ -public class AccessEventCompositeJsonFormatter extends CompositeJsonFormatter { +public class AccessEventCompositeJsonFormatter extends AbstractCompositeJsonFormatter { public AccessEventCompositeJsonFormatter(ContextAware declaredOrigin) { super(declaredOrigin); diff --git a/src/main/java/net/logstash/logback/composite/accessevent/AccessEventFormattedTimestampJsonProvider.java b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventFormattedTimestampJsonProvider.java index 139c934e..ed92368b 100644 --- a/src/main/java/net/logstash/logback/composite/accessevent/AccessEventFormattedTimestampJsonProvider.java +++ b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventFormattedTimestampJsonProvider.java @@ -15,12 +15,12 @@ */ package net.logstash.logback.composite.accessevent; -import net.logstash.logback.composite.FormattedTimestampJsonProvider; +import net.logstash.logback.composite.AbstractFormattedTimestampJsonProvider; import net.logstash.logback.fieldnames.LogstashAccessFieldNames; import ch.qos.logback.access.spi.IAccessEvent; -public class AccessEventFormattedTimestampJsonProvider extends FormattedTimestampJsonProvider { +public class AccessEventFormattedTimestampJsonProvider extends AbstractFormattedTimestampJsonProvider { @Override protected long getTimestampAsMillis(IAccessEvent event) { diff --git a/src/main/java/net/logstash/logback/composite/accessevent/AccessEventJsonProviders.java b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventJsonProviders.java index c78c9fcf..5aa31d81 100644 --- a/src/main/java/net/logstash/logback/composite/accessevent/AccessEventJsonProviders.java +++ b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventJsonProviders.java @@ -15,9 +15,7 @@ */ package net.logstash.logback.composite.accessevent; -import net.logstash.logback.composite.GlobalCustomFieldsJsonProvider; import net.logstash.logback.composite.JsonProviders; -import net.logstash.logback.composite.LogstashVersionJsonProvider; import ch.qos.logback.access.spi.IAccessEvent; @@ -39,12 +37,19 @@ public class AccessEventJsonProviders extends JsonProviders { public void addTimestamp(AccessEventFormattedTimestampJsonProvider provider) { addProvider(provider); } - public void addVersion(LogstashVersionJsonProvider provider) { + + /** + * @deprecated Use {@link #addMessage(AccessMessageJsonProvider)} instead. + * @param provider the provider to add + */ + @Deprecated + public void addAccessMessage(AccessMessageJsonProvider provider) { addProvider(provider); } - public void addAccessMessage(AccessMessageJsonProvider provider) { + public void addMessage(AccessMessageJsonProvider provider) { addProvider(provider); } + public void addMethod(MethodJsonProvider provider) { addProvider(provider); } @@ -84,8 +89,7 @@ public void addPattern(AccessEventPatternJsonProvider provider) { public void addNestedField(AccessEventNestedJsonProvider provider) { addProvider(provider); } - public void addGlobalCustomFields(GlobalCustomFieldsJsonProvider provider) { + public void addThreadName(AccessEventThreadNameJsonProvider provider) { addProvider(provider); } - } diff --git a/src/main/java/net/logstash/logback/composite/accessevent/AccessEventThreadNameJsonProvider.java b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventThreadNameJsonProvider.java new file mode 100644 index 00000000..8ea84f9f --- /dev/null +++ b/src/main/java/net/logstash/logback/composite/accessevent/AccessEventThreadNameJsonProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.composite.accessevent; + +import net.logstash.logback.composite.AbstractThreadNameJsonProvider; +import net.logstash.logback.composite.JsonProvider; + +import ch.qos.logback.access.spi.IAccessEvent; + +/** + * {@link JsonProvider} producing a single JSON field with the {@link IAccessEvent#getThreadName()}. + * + * @author brenuart + */ +public class AccessEventThreadNameJsonProvider extends AbstractThreadNameJsonProvider { + + @Override + protected String getThreadName(IAccessEvent event) { + return event.getThreadName(); + } +} diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventCompositeJsonFormatter.java b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventCompositeJsonFormatter.java index f94b7d63..5cb9842d 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventCompositeJsonFormatter.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventCompositeJsonFormatter.java @@ -15,7 +15,7 @@ */ package net.logstash.logback.composite.loggingevent; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -23,9 +23,9 @@ import ch.qos.logback.core.spi.ContextAware; /** - * A {@link CompositeJsonFormatter} for {@link ILoggingEvent}s. + * A {@link AbstractCompositeJsonFormatter} for {@link ILoggingEvent}s. */ -public class LoggingEventCompositeJsonFormatter extends CompositeJsonFormatter { +public class LoggingEventCompositeJsonFormatter extends AbstractCompositeJsonFormatter { public LoggingEventCompositeJsonFormatter(ContextAware declaredOrigin) { super(declaredOrigin); diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProvider.java b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProvider.java index d830eb4a..b3d19f10 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProvider.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProvider.java @@ -15,12 +15,12 @@ */ package net.logstash.logback.composite.loggingevent; -import net.logstash.logback.composite.FormattedTimestampJsonProvider; +import net.logstash.logback.composite.AbstractFormattedTimestampJsonProvider; import net.logstash.logback.fieldnames.LogstashFieldNames; import ch.qos.logback.classic.spi.ILoggingEvent; -public class LoggingEventFormattedTimestampJsonProvider extends FormattedTimestampJsonProvider { +public class LoggingEventFormattedTimestampJsonProvider extends AbstractFormattedTimestampJsonProvider { @Override protected long getTimestampAsMillis(ILoggingEvent event) { diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventJsonProviders.java b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventJsonProviders.java index b82c0140..041a4c54 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventJsonProviders.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventJsonProviders.java @@ -15,9 +15,7 @@ */ package net.logstash.logback.composite.loggingevent; -import net.logstash.logback.composite.GlobalCustomFieldsJsonProvider; import net.logstash.logback.composite.JsonProviders; -import net.logstash.logback.composite.LogstashVersionJsonProvider; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -39,9 +37,6 @@ public class LoggingEventJsonProviders extends JsonProviders { public void addTimestamp(LoggingEventFormattedTimestampJsonProvider provider) { addProvider(provider); } - public void addVersion(LogstashVersionJsonProvider provider) { - addProvider(provider); - } public void addMessage(MessageJsonProvider provider) { addProvider(provider); } @@ -51,7 +46,7 @@ public void addRawMessage(RawMessageJsonProvider provider) { public void addLoggerName(LoggerNameJsonProvider provider) { addProvider(provider); } - public void addThreadName(ThreadNameJsonProvider provider) { + public void addThreadName(LoggingEventThreadNameJsonProvider provider) { addProvider(provider); } public void addLogLevel(LogLevelJsonProvider provider) { @@ -78,9 +73,6 @@ public void addContextName(ContextNameJsonProvider provider) { public void addMdc(MdcJsonProvider provider) { addProvider(provider); } - public void addGlobalCustomFields(GlobalCustomFieldsJsonProvider provider) { - addProvider(provider); - } public void addTags(TagsJsonProvider provider) { addProvider(provider); } @@ -96,17 +88,10 @@ public void addArguments(ArgumentsJsonProvider provider) { public void addNestedField(LoggingEventNestedJsonProvider provider) { addProvider(provider); } - public void addUuid(UuidProvider provider) { - addProvider(provider); - } public void addThrowableClassName(ThrowableClassNameJsonProvider provider) { addProvider(provider); } public void addThrowableRootCauseClassName(ThrowableRootCauseClassNameJsonProvider provider) { addProvider(provider); } - public void addSequence(SequenceJsonProvider provider) { - addProvider(provider); - } - } diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventThreadNameJsonProvider.java b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventThreadNameJsonProvider.java new file mode 100644 index 00000000..0dae9ae4 --- /dev/null +++ b/src/main/java/net/logstash/logback/composite/loggingevent/LoggingEventThreadNameJsonProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2021 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.composite.loggingevent; + +import net.logstash.logback.composite.AbstractThreadNameJsonProvider; +import net.logstash.logback.composite.JsonProvider; + +import ch.qos.logback.classic.spi.ILoggingEvent; + +/** + * {@link JsonProvider} producing a single JSON field with the {@link ILoggingEvent#getThreadName()}. + * + * @author brenuart + */ +public class LoggingEventThreadNameJsonProvider extends AbstractThreadNameJsonProvider { + + @Override + protected String getThreadName(ILoggingEvent event) { + return event.getThreadName(); + } +} diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/SequenceJsonProvider.java b/src/main/java/net/logstash/logback/composite/loggingevent/SequenceJsonProvider.java index 3c3e1948..b42d5794 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/SequenceJsonProvider.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/SequenceJsonProvider.java @@ -15,32 +15,20 @@ */ package net.logstash.logback.composite.loggingevent; -import java.io.IOException; -import java.util.concurrent.atomic.AtomicLong; - -import net.logstash.logback.composite.AbstractFieldJsonProvider; -import net.logstash.logback.composite.JsonWritingUtils; - import ch.qos.logback.classic.spi.ILoggingEvent; -import com.fasterxml.jackson.core.JsonGenerator; /** * Outputs an incrementing sequence number. * Useful for determining if log events get lost along the transport chain. + * + * @deprecated use {@link net.logstash.logback.composite.SequenceJsonProvider} instead */ -public class SequenceJsonProvider extends AbstractFieldJsonProvider { - - public static final String FIELD_SEQUENCE = "sequence"; - - private final AtomicLong sequenceNumber = new AtomicLong(0L); - - public SequenceJsonProvider() { - setFieldName(FIELD_SEQUENCE); - } +@Deprecated +public class SequenceJsonProvider extends net.logstash.logback.composite.SequenceJsonProvider { @Override - public void writeTo(JsonGenerator generator, ILoggingEvent iLoggingEvent) throws IOException { - JsonWritingUtils.writeNumberField(generator, getFieldName(), sequenceNumber.incrementAndGet()); + public void start() { + addWarn(this.getClass().getName() + " is deprecated, use " + net.logstash.logback.composite.SequenceJsonProvider.class.getName() + " instead."); + super.start(); } - } diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/ThreadNameJsonProvider.java b/src/main/java/net/logstash/logback/composite/loggingevent/ThreadNameJsonProvider.java index a2e38e36..a7605c63 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/ThreadNameJsonProvider.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/ThreadNameJsonProvider.java @@ -15,32 +15,15 @@ */ package net.logstash.logback.composite.loggingevent; -import java.io.IOException; - -import net.logstash.logback.composite.AbstractFieldJsonProvider; -import net.logstash.logback.composite.FieldNamesAware; -import net.logstash.logback.composite.JsonWritingUtils; -import net.logstash.logback.fieldnames.LogstashFieldNames; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import com.fasterxml.jackson.core.JsonGenerator; - -public class ThreadNameJsonProvider extends AbstractFieldJsonProvider implements FieldNamesAware { - - public static final String FIELD_THREAD_NAME = "thread_name"; - - public ThreadNameJsonProvider() { - setFieldName(FIELD_THREAD_NAME); - } - - @Override - public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException { - JsonWritingUtils.writeStringField(generator, getFieldName(), event.getThreadName()); - } - +/** + * @deprecated Use {@link LoggingEventThreadNameJsonProvider} instead. + */ +@Deprecated +public class ThreadNameJsonProvider extends LoggingEventThreadNameJsonProvider { + @Override - public void setFieldNames(LogstashFieldNames fieldNames) { - setFieldName(fieldNames.getThread()); + public void start() { + addWarn(this.getClass().getName() + " is deprecated, use " + LoggingEventThreadNameJsonProvider.class.getName() + " instead."); + super.start(); } - } diff --git a/src/main/java/net/logstash/logback/composite/loggingevent/UuidProvider.java b/src/main/java/net/logstash/logback/composite/loggingevent/UuidProvider.java index d9f4d706..c3a0ca54 100644 --- a/src/main/java/net/logstash/logback/composite/loggingevent/UuidProvider.java +++ b/src/main/java/net/logstash/logback/composite/loggingevent/UuidProvider.java @@ -15,102 +15,22 @@ */ package net.logstash.logback.composite.loggingevent; -import java.io.IOException; - -import net.logstash.logback.composite.AbstractFieldJsonProvider; -import net.logstash.logback.composite.JsonWritingUtils; +import net.logstash.logback.composite.UuidJsonProvider; import ch.qos.logback.classic.spi.ILoggingEvent; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.uuid.EthernetAddress; -import com.fasterxml.uuid.Generators; -import com.fasterxml.uuid.NoArgGenerator; -import com.fasterxml.uuid.impl.TimeBasedGenerator; /** * Outputs random UUID as field value. * Handy when you want to provide unique identifier for log lines. + * + * @deprecated use {@link UuidJsonProvider} instead. */ -public class UuidProvider extends AbstractFieldJsonProvider { - - public static final String FIELD_UUID = "uuid"; - - /** - * Type 4 UUID. - */ - public static final String STRATEGY_RANDOM = "random"; - - /** - * Type 1 time based UUID. - * - * When the time strategy is used, then - * {@link #ethernet} can be set to either 'interface' (to automatically pick a MAC address from a network interface) - * or a MAC address string. - */ - public static final String STRATEGY_TIME = "time"; - - private NoArgGenerator uuids = Generators.randomBasedGenerator(); - - /** - * One of {@value #STRATEGY_RANDOM} or {@value #STRATEGY_TIME}. - */ - private String strategy = STRATEGY_RANDOM; - - /** - * For {@link UuidStrategy#time} strategy only, - * 'interface' or ethernet MAC address. - */ - private String ethernet; - - public UuidProvider() { - setFieldName(FIELD_UUID); - } - +@Deprecated +public class UuidProvider extends UuidJsonProvider { + @Override - public void writeTo(JsonGenerator generator, ILoggingEvent iLoggingEvent) throws IOException { - JsonWritingUtils.writeStringField(generator, getFieldName(), uuids.generate().toString()); - } - - public String getStrategy() { - return strategy; - } - - public void setStrategy(String strategy) { - this.strategy = strategy; - - uuids = newUuidStrategy(strategy, ethernet); - } - - public String getEthernet() { - return ethernet; - } - - public void setEthernet(String ethernet) { - this.ethernet = ethernet; - - uuids = newUuidStrategy(this.strategy, this.ethernet); - } - - private NoArgGenerator newUuidStrategy(String strategy, String ethernet) { - - if (STRATEGY_TIME.equalsIgnoreCase(strategy)) { - return newTimeBasedGenerator(ethernet); - } - if (STRATEGY_RANDOM.equalsIgnoreCase(strategy)) { - return Generators.randomBasedGenerator(); - } - throw new IllegalArgumentException("Unknown strategy: " + strategy); - } - - private TimeBasedGenerator newTimeBasedGenerator(String ethernet) { - if (ethernet == null) { - return Generators.timeBasedGenerator(); - } - - if ("interface".equalsIgnoreCase(ethernet)) { - return Generators.timeBasedGenerator(EthernetAddress.fromInterface()); - } - - return Generators.timeBasedGenerator(EthernetAddress.valueOf(ethernet)); + public void start() { + addWarn(this.getClass().getName() + " is deprecated, use " + UuidJsonProvider.class.getName() + " instead."); + super.start(); } } diff --git a/src/main/java/net/logstash/logback/decorate/JsonFactoryDecorator.java b/src/main/java/net/logstash/logback/decorate/JsonFactoryDecorator.java index ea7fbf09..e53b6fbd 100644 --- a/src/main/java/net/logstash/logback/decorate/JsonFactoryDecorator.java +++ b/src/main/java/net/logstash/logback/decorate/JsonFactoryDecorator.java @@ -15,14 +15,14 @@ */ package net.logstash.logback.decorate; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.MappingJsonFactory; /** * Decorates the {@link JsonFactory} used by a - * {@link CompositeJsonFormatter}. + * {@link AbstractCompositeJsonFormatter}. *

    * This allows you to customize the factory used by the formatters. *

    diff --git a/src/main/java/net/logstash/logback/encoder/AccessEventCompositeJsonEncoder.java b/src/main/java/net/logstash/logback/encoder/AccessEventCompositeJsonEncoder.java index f64f25fc..e02b8575 100644 --- a/src/main/java/net/logstash/logback/encoder/AccessEventCompositeJsonEncoder.java +++ b/src/main/java/net/logstash/logback/encoder/AccessEventCompositeJsonEncoder.java @@ -15,7 +15,7 @@ */ package net.logstash.logback.encoder; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.composite.accessevent.AccessEventCompositeJsonFormatter; import net.logstash.logback.composite.accessevent.AccessEventJsonProviders; @@ -27,7 +27,7 @@ public class AccessEventCompositeJsonEncoder extends CompositeJsonEncoder createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new AccessEventCompositeJsonFormatter(this); } diff --git a/src/main/java/net/logstash/logback/encoder/CompositeJsonEncoder.java b/src/main/java/net/logstash/logback/encoder/CompositeJsonEncoder.java index b7860743..420daca5 100644 --- a/src/main/java/net/logstash/logback/encoder/CompositeJsonEncoder.java +++ b/src/main/java/net/logstash/logback/encoder/CompositeJsonEncoder.java @@ -20,7 +20,7 @@ import java.nio.charset.Charset; import java.util.Objects; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.decorate.JsonFactoryDecorator; import net.logstash.logback.decorate.JsonGeneratorDecorator; @@ -56,7 +56,7 @@ public abstract class CompositeJsonEncoder prefix; private Encoder suffix; - private final CompositeJsonFormatter formatter; + private final AbstractCompositeJsonFormatter formatter; private String lineSeparator = System.lineSeparator(); @@ -69,7 +69,7 @@ public CompositeJsonEncoder() { this.formatter = Objects.requireNonNull(createFormatter()); } - protected abstract CompositeJsonFormatter createFormatter(); + protected abstract AbstractCompositeJsonFormatter createFormatter(); @Override public void encode(Event event, OutputStream outputStream) throws IOException { @@ -286,7 +286,7 @@ public void setMinBufferSize(int minBufferSize) { this.minBufferSize = minBufferSize; } - protected CompositeJsonFormatter getFormatter() { + protected AbstractCompositeJsonFormatter getFormatter() { return formatter; } diff --git a/src/main/java/net/logstash/logback/encoder/LoggingEventCompositeJsonEncoder.java b/src/main/java/net/logstash/logback/encoder/LoggingEventCompositeJsonEncoder.java index ebd934b7..363a9465 100644 --- a/src/main/java/net/logstash/logback/encoder/LoggingEventCompositeJsonEncoder.java +++ b/src/main/java/net/logstash/logback/encoder/LoggingEventCompositeJsonEncoder.java @@ -15,7 +15,7 @@ */ package net.logstash.logback.encoder; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.composite.loggingevent.LoggingEventCompositeJsonFormatter; import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders; @@ -26,7 +26,7 @@ public class LoggingEventCompositeJsonEncoder extends CompositeJsonEncoder { @Override - protected CompositeJsonFormatter createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new LoggingEventCompositeJsonFormatter(this); } diff --git a/src/main/java/net/logstash/logback/encoder/LogstashAccessEncoder.java b/src/main/java/net/logstash/logback/encoder/LogstashAccessEncoder.java index 4f97c5e0..2bd27698 100644 --- a/src/main/java/net/logstash/logback/encoder/LogstashAccessEncoder.java +++ b/src/main/java/net/logstash/logback/encoder/LogstashAccessEncoder.java @@ -16,7 +16,7 @@ package net.logstash.logback.encoder; import net.logstash.logback.LogstashAccessFormatter; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.composite.accessevent.HeaderFilter; import net.logstash.logback.composite.accessevent.IncludeExcludeHeaderFilter; @@ -28,7 +28,7 @@ public class LogstashAccessEncoder extends AccessEventCompositeJsonEncoder { @Override - protected CompositeJsonFormatter createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new LogstashAccessFormatter(this); } diff --git a/src/main/java/net/logstash/logback/encoder/LogstashEncoder.java b/src/main/java/net/logstash/logback/encoder/LogstashEncoder.java index 8779a061..e157c1c1 100644 --- a/src/main/java/net/logstash/logback/encoder/LogstashEncoder.java +++ b/src/main/java/net/logstash/logback/encoder/LogstashEncoder.java @@ -18,7 +18,7 @@ import java.util.List; import net.logstash.logback.LogstashFormatter; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.fieldnames.LogstashFieldNames; @@ -28,7 +28,7 @@ public class LogstashEncoder extends LoggingEventCompositeJsonEncoder { @Override - protected CompositeJsonFormatter createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new LogstashFormatter(this); } diff --git a/src/main/java/net/logstash/logback/fieldnames/LogstashCommonFieldNames.java b/src/main/java/net/logstash/logback/fieldnames/LogstashCommonFieldNames.java index 1fd127eb..1b685dd6 100644 --- a/src/main/java/net/logstash/logback/fieldnames/LogstashCommonFieldNames.java +++ b/src/main/java/net/logstash/logback/fieldnames/LogstashCommonFieldNames.java @@ -15,8 +15,10 @@ */ package net.logstash.logback.fieldnames; -import net.logstash.logback.composite.FormattedTimestampJsonProvider; +import net.logstash.logback.composite.AbstractFormattedTimestampJsonProvider; +import net.logstash.logback.composite.AbstractThreadNameJsonProvider; import net.logstash.logback.composite.LogstashVersionJsonProvider; +import net.logstash.logback.composite.UuidJsonProvider; import net.logstash.logback.composite.loggingevent.MessageJsonProvider; /** @@ -38,9 +40,12 @@ public abstract class LogstashCommonFieldNames { */ public static final String IGNORE_FIELD_INDICATOR = "[ignore]"; - private String timestamp = FormattedTimestampJsonProvider.FIELD_TIMESTAMP; + private String timestamp = AbstractFormattedTimestampJsonProvider.FIELD_TIMESTAMP; private String version = LogstashVersionJsonProvider.FIELD_VERSION; private String message = MessageJsonProvider.FIELD_MESSAGE; + private String thread = AbstractThreadNameJsonProvider.FIELD_THREAD_NAME; + private String uuid = UuidJsonProvider.FIELD_UUID; + private String context; public String getTimestamp() { @@ -85,4 +90,20 @@ public String getContext() { public void setContext(String context) { this.context = context; } + + public String getThread() { + return thread; + } + + public void setThread(String thread) { + this.thread = thread; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } } diff --git a/src/main/java/net/logstash/logback/fieldnames/LogstashFieldNames.java b/src/main/java/net/logstash/logback/fieldnames/LogstashFieldNames.java index cc9b2166..9dbca202 100644 --- a/src/main/java/net/logstash/logback/fieldnames/LogstashFieldNames.java +++ b/src/main/java/net/logstash/logback/fieldnames/LogstashFieldNames.java @@ -22,8 +22,6 @@ import net.logstash.logback.composite.loggingevent.RootStackTraceElementJsonProvider; import net.logstash.logback.composite.loggingevent.StackTraceJsonProvider; import net.logstash.logback.composite.loggingevent.TagsJsonProvider; -import net.logstash.logback.composite.loggingevent.ThreadNameJsonProvider; -import net.logstash.logback.composite.loggingevent.UuidProvider; /** * Names of standard fields that appear in the JSON output. @@ -31,7 +29,6 @@ public class LogstashFieldNames extends LogstashCommonFieldNames { private String logger = LoggerNameJsonProvider.FIELD_LOGGER_NAME; - private String thread = ThreadNameJsonProvider.FIELD_THREAD_NAME; private String level = LogLevelJsonProvider.FIELD_LEVEL; private String levelValue = LogLevelValueJsonProvider.FIELD_LEVEL_VALUE; private String caller; @@ -46,7 +43,6 @@ public class LogstashFieldNames extends LogstashCommonFieldNames { private String tags = TagsJsonProvider.FIELD_TAGS; private String mdc; private String arguments; - private String uuid = UuidProvider.FIELD_UUID; public String getLogger() { return logger; @@ -56,14 +52,6 @@ public void setLogger(String logger) { this.logger = logger; } - public String getThread() { - return thread; - } - - public void setThread(String thread) { - this.thread = thread; - } - public String getLevel() { return level; } @@ -177,14 +165,6 @@ public void setArguments(String arguments) { this.arguments = arguments; } - public String getUuid() { - return uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } - public String getRootStackTraceElement() { return rootStackTraceElement; } diff --git a/src/main/java/net/logstash/logback/layout/AccessEventCompositeJsonLayout.java b/src/main/java/net/logstash/logback/layout/AccessEventCompositeJsonLayout.java index 5fd76e7d..8b815a0a 100644 --- a/src/main/java/net/logstash/logback/layout/AccessEventCompositeJsonLayout.java +++ b/src/main/java/net/logstash/logback/layout/AccessEventCompositeJsonLayout.java @@ -15,7 +15,7 @@ */ package net.logstash.logback.layout; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.composite.accessevent.AccessEventCompositeJsonFormatter; import net.logstash.logback.composite.accessevent.AccessEventJsonProviders; @@ -27,7 +27,7 @@ public class AccessEventCompositeJsonLayout extends CompositeJsonLayout createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new AccessEventCompositeJsonFormatter(this); } diff --git a/src/main/java/net/logstash/logback/layout/CompositeJsonLayout.java b/src/main/java/net/logstash/logback/layout/CompositeJsonLayout.java index 59198339..a7ecacc8 100644 --- a/src/main/java/net/logstash/logback/layout/CompositeJsonLayout.java +++ b/src/main/java/net/logstash/logback/layout/CompositeJsonLayout.java @@ -21,7 +21,7 @@ import java.io.Writer; import java.util.Objects; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.decorate.JsonFactoryDecorator; import net.logstash.logback.decorate.JsonGeneratorDecorator; @@ -68,14 +68,14 @@ public abstract class CompositeJsonLayout */ private ReusableByteBufferPool bufferPool; - private final CompositeJsonFormatter formatter; + private final AbstractCompositeJsonFormatter formatter; public CompositeJsonLayout() { super(); this.formatter = Objects.requireNonNull(createFormatter()); } - protected abstract CompositeJsonFormatter createFormatter(); + protected abstract AbstractCompositeJsonFormatter createFormatter(); @Override public String doLayout(Event event) { @@ -215,7 +215,7 @@ public void setFindAndRegisterJacksonModules(boolean findAndRegisterJacksonModul formatter.setFindAndRegisterJacksonModules(findAndRegisterJacksonModules); } - protected CompositeJsonFormatter getFormatter() { + protected AbstractCompositeJsonFormatter getFormatter() { return formatter; } diff --git a/src/main/java/net/logstash/logback/layout/LoggingEventCompositeJsonLayout.java b/src/main/java/net/logstash/logback/layout/LoggingEventCompositeJsonLayout.java index df1dd93b..1f7bca68 100644 --- a/src/main/java/net/logstash/logback/layout/LoggingEventCompositeJsonLayout.java +++ b/src/main/java/net/logstash/logback/layout/LoggingEventCompositeJsonLayout.java @@ -15,7 +15,7 @@ */ package net.logstash.logback.layout; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProviders; import net.logstash.logback.composite.loggingevent.LoggingEventCompositeJsonFormatter; import net.logstash.logback.composite.loggingevent.LoggingEventJsonProviders; @@ -26,7 +26,7 @@ public class LoggingEventCompositeJsonLayout extends CompositeJsonLayout { @Override - protected CompositeJsonFormatter createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new LoggingEventCompositeJsonFormatter(this); } diff --git a/src/main/java/net/logstash/logback/layout/LogstashAccessLayout.java b/src/main/java/net/logstash/logback/layout/LogstashAccessLayout.java index 44b45b86..71aff37b 100644 --- a/src/main/java/net/logstash/logback/layout/LogstashAccessLayout.java +++ b/src/main/java/net/logstash/logback/layout/LogstashAccessLayout.java @@ -16,7 +16,7 @@ package net.logstash.logback.layout; import net.logstash.logback.LogstashAccessFormatter; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.composite.accessevent.HeaderFilter; import net.logstash.logback.composite.accessevent.IncludeExcludeHeaderFilter; @@ -28,7 +28,7 @@ public class LogstashAccessLayout extends AccessEventCompositeJsonLayout { @Override - protected CompositeJsonFormatter createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new LogstashAccessFormatter(this); } diff --git a/src/main/java/net/logstash/logback/layout/LogstashLayout.java b/src/main/java/net/logstash/logback/layout/LogstashLayout.java index 6e450cf1..97ea34ad 100644 --- a/src/main/java/net/logstash/logback/layout/LogstashLayout.java +++ b/src/main/java/net/logstash/logback/layout/LogstashLayout.java @@ -18,7 +18,7 @@ import java.util.List; import net.logstash.logback.LogstashFormatter; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.fieldnames.LogstashFieldNames; @@ -28,7 +28,7 @@ public class LogstashLayout extends LoggingEventCompositeJsonLayout { @Override - protected CompositeJsonFormatter createFormatter() { + protected AbstractCompositeJsonFormatter createFormatter() { return new LogstashFormatter(this); } diff --git a/src/test/java/net/logstash/logback/ConfigurationTest.java b/src/test/java/net/logstash/logback/ConfigurationTest.java index ee84e898..d8f7a942 100644 --- a/src/test/java/net/logstash/logback/ConfigurationTest.java +++ b/src/test/java/net/logstash/logback/ConfigurationTest.java @@ -29,6 +29,8 @@ import net.logstash.logback.composite.GlobalCustomFieldsJsonProvider; import net.logstash.logback.composite.JsonProvider; import net.logstash.logback.composite.LogstashVersionJsonProvider; +import net.logstash.logback.composite.SequenceJsonProvider; +import net.logstash.logback.composite.UuidJsonProvider; import net.logstash.logback.composite.loggingevent.ArgumentsJsonProvider; import net.logstash.logback.composite.loggingevent.CallerDataJsonProvider; import net.logstash.logback.composite.loggingevent.ContextNameJsonProvider; @@ -38,15 +40,13 @@ import net.logstash.logback.composite.loggingevent.LoggingEventFormattedTimestampJsonProvider; import net.logstash.logback.composite.loggingevent.LoggingEventNestedJsonProvider; import net.logstash.logback.composite.loggingevent.LoggingEventPatternJsonProvider; +import net.logstash.logback.composite.loggingevent.LoggingEventThreadNameJsonProvider; import net.logstash.logback.composite.loggingevent.LogstashMarkersJsonProvider; import net.logstash.logback.composite.loggingevent.MdcJsonProvider; import net.logstash.logback.composite.loggingevent.MessageJsonProvider; import net.logstash.logback.composite.loggingevent.RawMessageJsonProvider; -import net.logstash.logback.composite.loggingevent.SequenceJsonProvider; import net.logstash.logback.composite.loggingevent.StackTraceJsonProvider; import net.logstash.logback.composite.loggingevent.TagsJsonProvider; -import net.logstash.logback.composite.loggingevent.ThreadNameJsonProvider; -import net.logstash.logback.composite.loggingevent.UuidProvider; import net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder; import net.logstash.logback.marker.Markers; import net.logstash.logback.stacktrace.ShortenedThrowableConverter; @@ -129,7 +129,7 @@ private void verifyCommonProviders(List> providers) Assertions.assertNotNull(loggerNameJsonProvider); Assertions.assertEquals("logger_name", loggerNameJsonProvider.getFieldName()); - ThreadNameJsonProvider threadNameJsonProvider = getInstance(providers, ThreadNameJsonProvider.class); + LoggingEventThreadNameJsonProvider threadNameJsonProvider = getInstance(providers, LoggingEventThreadNameJsonProvider.class); Assertions.assertNotNull(threadNameJsonProvider); Assertions.assertEquals("thread_name", threadNameJsonProvider.getFieldName()); @@ -191,11 +191,11 @@ private void verifyCommonProviders(List> providers) ArgumentsJsonProvider argumentsJsonProvider = getInstance(providers, ArgumentsJsonProvider.class); Assertions.assertNotNull(argumentsJsonProvider); - UuidProvider uuidProvider = getInstance(nestedJsonProvider.getProviders().getProviders(), UuidProvider.class); + UuidJsonProvider uuidProvider = getInstance(nestedJsonProvider.getProviders().getProviders(), UuidJsonProvider.class); Assertions.assertNotNull(uuidProvider); Assertions.assertEquals("id", uuidProvider.getFieldName()); Assertions.assertEquals("00:C0:F0:3D:5B:7C", uuidProvider.getEthernet()); - Assertions.assertEquals(UuidProvider.STRATEGY_TIME, uuidProvider.getStrategy()); + Assertions.assertEquals(UuidJsonProvider.STRATEGY_TIME, uuidProvider.getStrategy()); SequenceJsonProvider sequenceJsonProvider = getInstance(providers, SequenceJsonProvider.class); Assertions.assertNotNull(sequenceJsonProvider); diff --git a/src/test/java/net/logstash/logback/composite/loggingevent/SequenceJsonProviderTest.java b/src/test/java/net/logstash/logback/composite/SequenceJsonProviderTest.java similarity index 92% rename from src/test/java/net/logstash/logback/composite/loggingevent/SequenceJsonProviderTest.java rename to src/test/java/net/logstash/logback/composite/SequenceJsonProviderTest.java index 5f640213..a6a5fc31 100644 --- a/src/test/java/net/logstash/logback/composite/loggingevent/SequenceJsonProviderTest.java +++ b/src/test/java/net/logstash/logback/composite/SequenceJsonProviderTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.logstash.logback.composite.loggingevent; +package net.logstash.logback.composite; import static org.mockito.Mockito.verify; @@ -29,7 +29,7 @@ @ExtendWith(MockitoExtension.class) public class SequenceJsonProviderTest { - private SequenceJsonProvider provider = new SequenceJsonProvider(); + private SequenceJsonProvider provider = new SequenceJsonProvider<>(); @Mock private JsonGenerator generator; diff --git a/src/test/java/net/logstash/logback/composite/loggingevent/UuidProviderTest.java b/src/test/java/net/logstash/logback/composite/UuidJsonProviderTest.java similarity index 84% rename from src/test/java/net/logstash/logback/composite/loggingevent/UuidProviderTest.java rename to src/test/java/net/logstash/logback/composite/UuidJsonProviderTest.java index 4b4c4202..7f31ac92 100644 --- a/src/test/java/net/logstash/logback/composite/loggingevent/UuidProviderTest.java +++ b/src/test/java/net/logstash/logback/composite/UuidJsonProviderTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.logstash.logback.composite.loggingevent; +package net.logstash.logback.composite; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.matches; @@ -29,10 +29,10 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class UuidProviderTest { +public class UuidJsonProviderTest { public static final String UUID = "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"; - private UuidProvider provider = new UuidProvider(); + private UuidJsonProvider provider = new UuidJsonProvider<>(); @Mock private JsonGenerator generator; @@ -44,7 +44,7 @@ public class UuidProviderTest { public void testDefaultName() throws IOException { provider.writeTo(generator, event); - verify(generator).writeStringField(eq(UuidProvider.FIELD_UUID), matches(UUID)); + verify(generator).writeStringField(eq(UuidJsonProvider.FIELD_UUID), matches(UUID)); } @Test @@ -58,7 +58,7 @@ public void testFieldName() throws IOException { @Test public void testStrategy() throws IOException { - provider.setStrategy(UuidProvider.STRATEGY_TIME); + provider.setStrategy(UuidJsonProvider.STRATEGY_TIME); provider.writeTo(generator, event); @@ -67,7 +67,7 @@ public void testStrategy() throws IOException { @Test public void testEthernet() throws IOException { - provider.setStrategy(UuidProvider.STRATEGY_TIME); + provider.setStrategy(UuidJsonProvider.STRATEGY_TIME); provider.setEthernet("00:C0:F0:3D:5B:7C"); provider.writeTo(generator, event); diff --git a/src/test/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProviderTest.java b/src/test/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProviderTest.java index 4e3c287c..8d887545 100644 --- a/src/test/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProviderTest.java +++ b/src/test/java/net/logstash/logback/composite/loggingevent/LoggingEventFormattedTimestampJsonProviderTest.java @@ -25,7 +25,7 @@ import java.time.format.DateTimeFormatter; import java.util.TimeZone; -import net.logstash.logback.composite.FormattedTimestampJsonProvider; +import net.logstash.logback.composite.AbstractFormattedTimestampJsonProvider; import ch.qos.logback.classic.spi.ILoggingEvent; import com.fasterxml.jackson.core.JsonGenerator; @@ -52,7 +52,7 @@ public void withDefaults() throws IOException { provider.writeTo(generator, event); String expectedValue = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(0)); - verify(generator).writeStringField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); + verify(generator).writeStringField(AbstractFormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); } @Test @@ -64,7 +64,7 @@ public void customTimeZone() throws IOException { provider.writeTo(generator, event); String expectedValue = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.of("UTC")).format(Instant.ofEpochMilli(0)); - verify(generator).writeStringField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); + verify(generator).writeStringField(AbstractFormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); } @Test @@ -76,7 +76,7 @@ public void constant() throws IOException { provider.writeTo(generator, event); String expectedValue = DateTimeFormatter.ISO_DATE_TIME.withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(0)); - verify(generator).writeStringField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); + verify(generator).writeStringField(AbstractFormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); } @Test @@ -97,28 +97,28 @@ public void customPattern() throws IOException { provider.writeTo(generator, event); String expectedValue = DateTimeFormatter.ofPattern(pattern).withZone(TimeZone.getDefault().toZoneId()).format(Instant.ofEpochMilli(0)); - verify(generator).writeStringField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); + verify(generator).writeStringField(AbstractFormattedTimestampJsonProvider.FIELD_TIMESTAMP, expectedValue); } @Test public void unixEpochAsNumber() throws IOException { LoggingEventFormattedTimestampJsonProvider provider = new LoggingEventFormattedTimestampJsonProvider(); - provider.setPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER); + provider.setPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER); when(event.getTimeStamp()).thenReturn(0L); provider.writeTo(generator, event); - verify(generator).writeNumberField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, 0L); + verify(generator).writeNumberField(AbstractFormattedTimestampJsonProvider.FIELD_TIMESTAMP, 0L); } @Test public void unixEpochAsString() throws IOException { LoggingEventFormattedTimestampJsonProvider provider = new LoggingEventFormattedTimestampJsonProvider(); - provider.setPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING); + provider.setPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING); when(event.getTimeStamp()).thenReturn(0L); provider.writeTo(generator, event); - verify(generator).writeStringField(FormattedTimestampJsonProvider.FIELD_TIMESTAMP, "0"); + verify(generator).writeStringField(AbstractFormattedTimestampJsonProvider.FIELD_TIMESTAMP, "0"); } } diff --git a/src/test/java/net/logstash/logback/composite/loggingevent/ThreadNameJsonProviderTest.java b/src/test/java/net/logstash/logback/composite/loggingevent/LoggingEventThreadNameJsonProviderTest.java similarity index 89% rename from src/test/java/net/logstash/logback/composite/loggingevent/ThreadNameJsonProviderTest.java rename to src/test/java/net/logstash/logback/composite/loggingevent/LoggingEventThreadNameJsonProviderTest.java index 4af8c8e2..c29a08f6 100644 --- a/src/test/java/net/logstash/logback/composite/loggingevent/ThreadNameJsonProviderTest.java +++ b/src/test/java/net/logstash/logback/composite/loggingevent/LoggingEventThreadNameJsonProviderTest.java @@ -30,9 +30,9 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -public class ThreadNameJsonProviderTest { +public class LoggingEventThreadNameJsonProviderTest { - private ThreadNameJsonProvider provider = new ThreadNameJsonProvider(); + private LoggingEventThreadNameJsonProvider provider = new LoggingEventThreadNameJsonProvider(); @Mock private JsonGenerator generator; @@ -47,7 +47,7 @@ public void testDefaultName() throws IOException { provider.writeTo(generator, event); - verify(generator).writeStringField(ThreadNameJsonProvider.FIELD_THREAD_NAME, "threadName"); + verify(generator).writeStringField(LoggingEventThreadNameJsonProvider.FIELD_THREAD_NAME, "threadName"); } @Test diff --git a/src/test/java/net/logstash/logback/encoder/CompositeJsonEncoderTest.java b/src/test/java/net/logstash/logback/encoder/CompositeJsonEncoderTest.java index a667ab97..3a166da9 100644 --- a/src/test/java/net/logstash/logback/encoder/CompositeJsonEncoderTest.java +++ b/src/test/java/net/logstash/logback/encoder/CompositeJsonEncoderTest.java @@ -32,7 +32,7 @@ import java.nio.charset.StandardCharsets; import net.logstash.logback.TestJsonProvider; -import net.logstash.logback.composite.CompositeJsonFormatter; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -56,7 +56,7 @@ public class CompositeJsonEncoderTest { private final TestCompositeJsonEncoder encoder = new TestCompositeJsonEncoder(); - private CompositeJsonFormatter formatter; + private AbstractCompositeJsonFormatter formatter; private LoggerContext context = new LoggerContext(); @@ -261,8 +261,8 @@ private static class TestCompositeJsonEncoder extends CompositeJsonEncoder createFormatter() { - CompositeJsonFormatter formatter = spy(new CompositeJsonFormatter(this) { + protected AbstractCompositeJsonFormatter createFormatter() { + AbstractCompositeJsonFormatter formatter = spy(new AbstractCompositeJsonFormatter(this) { @Override protected void writeEventToGenerator(JsonGenerator generator, ILoggingEvent event) throws IOException { if (exceptionToThrow != null) { diff --git a/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java b/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java index baafa15a..80621169 100644 --- a/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java +++ b/src/test/java/net/logstash/logback/encoder/LogstashAccessEncoderTest.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.TimeZone; -import net.logstash.logback.composite.FormattedTimestampJsonProvider; +import net.logstash.logback.composite.AbstractFormattedTimestampJsonProvider; import ch.qos.logback.access.spi.IAccessEvent; import ch.qos.logback.core.Context; @@ -135,7 +135,7 @@ public void unixTimestampAsNumber() throws Exception { IAccessEvent event = mockBasicILoggingEvent(); when(event.getTimeStamp()).thenReturn(timestamp); - encoder.setTimestampPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER); + encoder.setTimestampPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER); encoder.start(); byte[] encoded = encoder.encode(event); @@ -151,7 +151,7 @@ public void unixTimestampAsString() throws Exception { IAccessEvent event = mockBasicILoggingEvent(); when(event.getTimeStamp()).thenReturn(timestamp); - encoder.setTimestampPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING); + encoder.setTimestampPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING); encoder.start(); byte[] encoded = encoder.encode(event); diff --git a/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java b/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java index 44eb5d5f..6c1902f9 100644 --- a/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java +++ b/src/test/java/net/logstash/logback/encoder/LogstashEncoderTest.java @@ -38,7 +38,7 @@ import java.util.Map; import java.util.TimeZone; -import net.logstash.logback.composite.FormattedTimestampJsonProvider; +import net.logstash.logback.composite.AbstractFormattedTimestampJsonProvider; import net.logstash.logback.decorate.JsonFactoryDecorator; import net.logstash.logback.decorate.JsonGeneratorDecorator; import net.logstash.logback.fieldnames.LogstashCommonFieldNames; @@ -623,7 +623,7 @@ public void unixTimestampAsNumber() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); when(event.getTimeStamp()).thenReturn(timestamp); - encoder.setTimestampPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER); + encoder.setTimestampPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_NUMBER); encoder.start(); byte[] encoded = encoder.encode(event); @@ -639,7 +639,7 @@ public void unixTimestampAsString() throws Exception { ILoggingEvent event = mockBasicILoggingEvent(Level.ERROR); when(event.getTimeStamp()).thenReturn(timestamp); - encoder.setTimestampPattern(FormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING); + encoder.setTimestampPattern(AbstractFormattedTimestampJsonProvider.UNIX_TIMESTAMP_AS_STRING); encoder.start(); byte[] encoded = encoder.encode(event); diff --git a/src/test/java/net/logstash/logback/layout/CompositeJsonLayoutTest.java b/src/test/java/net/logstash/logback/layout/CompositeJsonLayoutTest.java index aa9fc793..27c771de 100644 --- a/src/test/java/net/logstash/logback/layout/CompositeJsonLayoutTest.java +++ b/src/test/java/net/logstash/logback/layout/CompositeJsonLayoutTest.java @@ -20,8 +20,8 @@ import java.io.IOException; +import net.logstash.logback.composite.AbstractCompositeJsonFormatter; import net.logstash.logback.composite.AbstractJsonProvider; -import net.logstash.logback.composite.CompositeJsonFormatter; import ch.qos.logback.core.Layout; import ch.qos.logback.core.LayoutBase; @@ -40,8 +40,8 @@ public class CompositeJsonLayoutTest { */ static class TesterCompositeJsonLayout extends CompositeJsonLayout { @Override - protected CompositeJsonFormatter createFormatter() { - CompositeJsonFormatter formatter = new CompositeJsonFormatter(this) { }; + protected AbstractCompositeJsonFormatter createFormatter() { + AbstractCompositeJsonFormatter formatter = new AbstractCompositeJsonFormatter(this) { }; formatter.getProviders().addProvider(new AbstractJsonProvider() { @Override public void writeTo(JsonGenerator generator, DeferredProcessingAware event) throws IOException {

    Provider Description/Properties
    timestamp

    Event timestamp

    +
    contentLength

    Content length.

      -
    • fieldName - Output field name (@timestamp)
    • -
    • pattern - Output format ([ISO_OFFSET_DATE_TIME]) See above for possible values.
    • -
    • timeZone - Timezone (local timezone)
    • +
    • fieldName - Output field name (content_length)
    version

    Logstash JSON format version

    +
    elapsedTime

    Elapsed time in milliseconds.

      -
    • fieldName - Output field name (@version)
    • -
    • version - Output value (1)
    • -
    • writeAsInteger - Write the version as a integer value (false = write as a string value)
    • +
    • fieldName - Output field name (elapsed_time)
    message

    Message in the form `${remoteHost} - ${remoteUser} [${timestamp}] "${requestUrl}" ${statusCode} ${contentLength}`

    +

    Message in the form `${remoteHost} - ${remoteUser} [${timestamp}] "${requestUrl}" ${statusCode} ${contentLength}`.

    • fieldName - Output field name (message)
    • +
    • pattern - Output format of the timestamp ([ISO_OFFSET_DATE_TIME]). See above for possible values.
    • +
    • timeZone - Timezone (local timezone)
    method

    HTTP method

    +

    HTTP method.

    • fieldName - Output field name (method)
    @@ -2187,39 +2199,15 @@ The provider name is the xml element name to use when configuring.
    protocol

    HTTP protocol

    +

    HTTP protocol.

    • fieldName - Output field name (protocol)
    statusCode

    HTTP status code

    -
      -
    • fieldName - Output field name (status_code)
    • -
    -
    requestedUrl

    Requested URL

    -
      -
    • fieldName - Output field name (requested_url)
    • -
    -
    requestedUri

    Requested URI

    -
      -
    • fieldName - Output field name (requested_uri)
    • -
    -
    remoteHost

    Remote Host

    +

    Remote Host.

    • fieldName - Output field name (remote_host)
    @@ -2227,31 +2215,31 @@ The provider name is the xml element name to use when configuring.
    remoteUser

    Remote User

    +

    Remote User.

    • fieldName - Output field name (remote_user)
    contentLength

    Content length

    +
    requestedUri

    Requested URI.

      -
    • fieldName - Output field name (content_length)
    • +
    • fieldName - Output field name (requested_uri)
    elapsedTime

    Elapsed time in milliseconds

    +
    requestedUrl

    Requested URL.

      -
    • fieldName - Output field name (elapsed_time)
    • +
    • fieldName - Output field name (requested_url)
    requestHeaders

    Include the request headers

    +

    Include the request headers.

    • fieldName - Output field name (no default, must be provided)
    • lowerCaseHeaderNames - Write header names in lower case (false)
    • @@ -2263,7 +2251,7 @@ The provider name is the xml element name to use when configuring.
    responseHeaders

    Include the response headers

    +

    Include the response headers.

    • fieldName - Output field name (no default, must be provided)
    • lowerCaseHeaderNames - Write header names in lower case (false)
    • @@ -2274,29 +2262,10 @@ The provider name is the xml element name to use when configuring.
    nestedField -

    Nests a JSON object under the configured fieldName.

    -

    The nested object is populated by other providers added to this provider.

    -

    See Nested JSON provider

    -
      -
    • fieldName - Output field name
    • -
    • providers - The providers that should populate the nested object.
    • -
    -
    pattern -

    Outputs fields from a configured JSON Object string, - while substituting patterns supported by logback access's PatternLayout. -

    -

    - See Pattern JSON Provider -

    +
    statusCode

    HTTP status code.

      -
    • pattern - JSON object string (no default)
    • -
    • omitEmptyFields - whether to omit fields with empty values (false)
    • +
    • fieldName - Output field name (status_code)