Skip to content

Commit

Permalink
Add support for Event#getsequencenumber (logback 13) (#843)
Browse files Browse the repository at this point in the history
* Adapt SequenceJsonProvider to get the sequence number from the Event itself when logback >= 1.3
* Determine Logback version by introspecting a “logback-core” class (one of classic/access may not be on the classpath at runtime)
  • Loading branch information
brenuart authored Aug 23, 2022
1 parent 23e2b7b commit 6e4c98e
Show file tree
Hide file tree
Showing 12 changed files with 375 additions and 24 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1960,12 +1960,17 @@ The provider name is the xml element name to use when configuring.
<tr>
<td valign="top"><tt>sequence</tt></td>
<td>
<p>
Outputs an incrementing sequence number for every log event.
Useful for tracking pottential message loss during transport (eg. UDP).
<p>Event sequence number.
</p>
<p>With Logback 1.3 the sequence number is obtained from the event itself as long as the LoggerContext is configured with a `SequenceNumberGenerator` (which is not by default).
If no SequenceNumberGenerator is configured, the provider emits a warning and reverts to a locally generated incrementing number starting at 1.
</p>
<p>With Logback versions prior to 1.3 the sequence number is generated locally by the provider itself.
</p>
<ul>
<li><tt>fieldName</tt> - Output field name (<tt>sequence</tt>)</li></ul>
<li><tt>fieldName</tt> - Output field name (<tt>sequence</tt>)</li>
<li><tt>sequenceProvider</tt> - Alternate strategy to obtain the sequence number associated with the supplied event. Must implement `Function<ILoggingEvent, Long>` or `Function<IAccessEvent, Long>` depending on the type of event to process.
</ul>
</td>
</tr>
<tr>
Expand All @@ -1981,7 +1986,7 @@ The provider name is the xml element name to use when configuring.
<td><p>Event timestamp.</p>
<ul>
<li><tt>fieldName</tt> - Output field name (<tt>@timestamp</tt>)</li>
<li><tt>pattern</tt> - Output format (<tt>[ISO_OFFSET_DATE_TIME]</tt>) See <a href="#customizing-timestamp">above</a> for possible values.</li>
<li><tt>pattern</tt> - Output format (<tt>[ISO_OFFSET_DATE_TIME]</tt>) See <a href="#customizing-timestamp">Customizing Timestamp</a> for possible values.</li>
<li><tt>timeZone</tt> - Timezone (system timezone)</li>
</ul>
</td>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.logstash.logback.composite;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;

import net.logstash.logback.util.LogbackUtils;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import ch.qos.logback.core.spi.SequenceNumberGenerator;
import com.fasterxml.jackson.core.JsonGenerator;

/**
* Outputs an incrementing sequence number.
* Useful for determining if log events get lost along the transport chain.
*
* <p>With Logback 1.3 the sequence number is obtained from {@link ILoggingEvent#getSequenceNumber()} provided the
* LoggerContext is configured with a {@link SequenceNumberGenerator} (which is not by default).
* If no SequenceNumberGenerator is configured, the provider issues a warning and reverts to a locally generated
* incrementing number.
*
* <p>With Logback versions prior to 1.3 the sequence number is generated locally by the provider itself.
*
* <p>If needed, a different strategy can be used by setting a custom provider with {@link #setSequenceProvider(Function)}.
*/
public abstract class AbstractSequenceJsonProvider<Event extends DeferredProcessingAware> extends AbstractFieldJsonProvider<Event> {

/**
* Default JSON field name
*/
public static final String FIELD_SEQUENCE = "sequence";

/**
* The function used to get the sequence number associated with the supplied event.
* May be {@code null} until the provider is started.
* If none is explicitly assigned through {@link #setSequenceProvider(Function)} one is automatically deduced from the Logback version
* found on the classpath.
*/
private Function<Event, Long> sequenceProvider;


public AbstractSequenceJsonProvider() {
setFieldName(FIELD_SEQUENCE);
}


@Override
public void start() {
if (getContext() == null) {
throw new IllegalStateException("No context given to " + this.getClass().getName());
}
if (this.sequenceProvider == null) {
this.sequenceProvider = createSequenceProvider();
}

super.start();
}


@Override
public void writeTo(JsonGenerator generator, Event event) throws IOException {
if (!isStarted()) {
throw new IllegalStateException("Provider " + this.getClass().getName() + " is not started");
}
JsonWritingUtils.writeNumberField(generator, getFieldName(), sequenceProvider.apply(event));
}

/**
* Assign a custom sequence provider instead of relying on the default.
*
* @param sequenceProvider the sequence provider to use to retrieve the sequence number for the event
*/
public void setSequenceProvider(Function<Event, Long> sequenceProvider) {
this.sequenceProvider = Objects.requireNonNull(sequenceProvider);
}

/**
* Get the sequence provider used to get the sequence number associated with the supplied event.
*
* @return a sequence provider
*/
public Function<Event, Long> getSequenceProvider() {
return sequenceProvider;
}


/**
* Create a default sequence provider depending on the current Logback version.
*
* <p>With Logback 1.3 the sequence number is obtained for {@link ILoggingEvent#getSequenceNumber()} provided the
* LoggerContext is configured with a {@link SequenceNumberGenerator} (which is not by default).
* If no SequenceNumberGenerator is configured, the provider issues a warning and reverts to a locally generated
* incrementing number.
*
* <p>With Logback versions prior to 1.3 the sequence number is generated locally by the provider itself.
*
* @return a sequence provider
*/
protected Function<Event, Long> createSequenceProvider() {
if (LogbackUtils.isVersion13()) {
if (getContext() == null || getContext().getSequenceNumberGenerator() == null) {
this.addWarn("No <sequenceNumberGenerator> defined in Logback configuration - revert to using a local incrementing sequence number.");
}
else {
return createNativeSequenceNumberFieldAccessor();
}
}
return new Function<Event, Long>() {
private final AtomicLong sequence = new AtomicLong(0L);

@Override
public Long apply(Event t) {
return sequence.incrementAndGet();
}
};
}


/**
* Get a function used to access the sequenceNumber field of the supplied event.
*
* @return a function used to access the sequenceNumber field of the supplied event.
*/
protected abstract Function<Event, Long> createNativeSequenceNumberFieldAccessor();
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,6 @@ public void addContext(ContextJsonProvider<Event> provider) {
public void addGlobalCustomFields(GlobalCustomFieldsJsonProvider<Event> provider) {
addProvider(provider);
}
public void addSequence(SequenceJsonProvider<Event> provider) {
addProvider(provider);
}
public void addUuid(UuidJsonProvider<Event> provider) {
addProvider(provider);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
/**
* Outputs an incrementing sequence number.
* Useful for determining if log events get lost along the transport chain.
*
* @deprecated use {@link net.logstash.logback.composite.loggingevent.SequenceJsonProvider} or {@link net.logstash.logback.composite.accessevent.SequenceJsonProvider} instead.
*/
public class SequenceJsonProvider<Event extends DeferredProcessingAware> extends AbstractFieldJsonProvider<Event> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,7 @@ public void addNestedField(AccessEventNestedJsonProvider provider) {
public void addThreadName(AccessEventThreadNameJsonProvider provider) {
addProvider(provider);
}
public void addSequence(SequenceJsonProvider provider) {
addProvider(provider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.logstash.logback.composite.accessevent;

import java.util.function.Function;

import net.logstash.logback.composite.AbstractSequenceJsonProvider;

import ch.qos.logback.access.spi.IAccessEvent;


public class SequenceJsonProvider extends AbstractSequenceJsonProvider<IAccessEvent> {

@Override
protected Function<IAccessEvent, Long> createNativeSequenceNumberFieldAccessor() {
return IAccessEvent::getSequenceNumber;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,7 @@ public void addThrowableRootCauseClassName(ThrowableRootCauseClassNameJsonProvid
public void addThrowableRootCauseMessage(ThrowableRootCauseMessageJsonProvider provider) {
addProvider(provider);
}
public void addSequence(SequenceJsonProvider provider) {
addProvider(provider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,17 @@
*/
package net.logstash.logback.composite.loggingevent;

import java.util.function.Function;

import net.logstash.logback.composite.AbstractSequenceJsonProvider;

import ch.qos.logback.classic.spi.ILoggingEvent;

/**
* 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
*/
@Deprecated
public class SequenceJsonProvider extends net.logstash.logback.composite.SequenceJsonProvider<ILoggingEvent> {

public class SequenceJsonProvider extends AbstractSequenceJsonProvider<ILoggingEvent> {

@Override
public void start() {
addWarn(this.getClass().getName() + " is deprecated, use " + net.logstash.logback.composite.SequenceJsonProvider.class.getName() + " instead.");
super.start();
protected Function<ILoggingEvent, Long> createNativeSequenceNumberFieldAccessor() {
return ILoggingEvent::getSequenceNumber;
}
}
6 changes: 3 additions & 3 deletions src/main/java/net/logstash/logback/util/LogbackUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@
*/
package net.logstash.logback.util;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.LoggerContext;

public abstract class LogbackUtils {

private static final boolean IS_VERSION_13 = hasMethod(ILoggingEvent.class, "getInstant");
private static final boolean IS_VERSION_13 = hasMethod(LoggerContext.class, "getSequenceNumberGenerator");

private LogbackUtils() {
// utility class
}

private static boolean hasMethod(Class<?> clazz, String name, Class<?>... args) {
try {
return clazz.getMethod("getInstant", args) != null;
return clazz.getMethod(name, args) != null;
}
catch (NoSuchMethodException e) {
return false;
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/net/logstash/logback/ConfigurationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
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;
Expand All @@ -49,6 +48,7 @@
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.encoder.LoggingEventCompositeJsonEncoder;
Expand Down Expand Up @@ -208,7 +208,7 @@ private void verifyCommonProviders(List<JsonProvider<ILoggingEvent>> providers)
assertThat(uuidProvider.getEthernet()).isEqualTo("00:C0:F0:3D:5B:7C");
assertThat(uuidProvider.getStrategy()).isEqualTo(UuidJsonProvider.STRATEGY_TIME);

SequenceJsonProvider<ILoggingEvent> sequenceJsonProvider = getInstance(providers, SequenceJsonProvider.class);
SequenceJsonProvider sequenceJsonProvider = getInstance(providers, SequenceJsonProvider.class);
assertThat(sequenceJsonProvider).isNotNull();
assertThat(sequenceJsonProvider.getFieldName()).isEqualTo("sequenceNumberField");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
@Deprecated
public class SequenceJsonProviderTest {

private SequenceJsonProvider<ILoggingEvent> provider = new SequenceJsonProvider<>();
Expand Down
Loading

0 comments on commit 6e4c98e

Please sign in to comment.