Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder
@Nullable private ExtendedAttributesMap extendedAttributes;

ExtendedSdkLogRecordBuilder(
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
super(loggerSharedState, instrumentationScopeInfo);
LoggerSharedState loggerSharedState,
InstrumentationScopeInfo instrumentationScopeInfo,
SdkLogger logger) {
super(loggerSharedState, instrumentationScopeInfo, logger);
}

@Override
Expand Down Expand Up @@ -128,30 +130,18 @@ public <T> ExtendedSdkLogRecordBuilder setAttribute(AttributeKey<T> key, @Nullab
}

@Override
public void emit() {
if (loggerSharedState.hasBeenShutdown()) {
return;
}
Context context = this.context == null ? Context.current() : this.context;
long observedTimestampEpochNanos =
this.observedTimestampEpochNanos == 0
? this.loggerSharedState.getClock().now()
: this.observedTimestampEpochNanos;
loggerSharedState
.getLogRecordProcessor()
.onEmit(
context,
ExtendedSdkReadWriteLogRecord.create(
loggerSharedState.getLogLimits(),
loggerSharedState.getResource(),
instrumentationScopeInfo,
eventName,
timestampEpochNanos,
observedTimestampEpochNanos,
Span.fromContext(context).getSpanContext(),
severity,
severityText,
body,
extendedAttributes));
protected ReadWriteLogRecord createLogRecord(Context context, long observedTimestampEpochNanos) {
return ExtendedSdkReadWriteLogRecord.create(
loggerSharedState.getLogLimits(),
loggerSharedState.getResource(),
instrumentationScopeInfo,
eventName,
timestampEpochNanos,
observedTimestampEpochNanos,
Span.fromContext(context).getSpanContext(),
severity,
severityText,
body,
extendedAttributes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ static SdkLogger createExtendedLogger(
}

static SdkLogRecordBuilder createExtendedLogRecordBuilder(
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
return new ExtendedSdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo);
LoggerSharedState loggerSharedState,
InstrumentationScopeInfo instrumentationScopeInfo,
SdkLogger logger) {
return new ExtendedSdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo, logger);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder {

protected final LoggerSharedState loggerSharedState;
protected final LogLimits logLimits;
protected final SdkLogger logger;

protected final InstrumentationScopeInfo instrumentationScopeInfo;
protected long timestampEpochNanos;
Expand All @@ -34,10 +35,13 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
@Nullable private AttributesMap attributes;

SdkLogRecordBuilder(
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
LoggerSharedState loggerSharedState,
InstrumentationScopeInfo instrumentationScopeInfo,
SdkLogger logger) {
this.loggerSharedState = loggerSharedState;
this.logLimits = loggerSharedState.getLogLimits();
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.logger = logger;
}

@Override
Expand Down Expand Up @@ -121,25 +125,30 @@ public void emit() {
return;
}
Context context = this.context == null ? Context.current() : this.context;
if (!logger.isEnabled(severity, context)) {
return;
}
long observedTimestampEpochNanos =
this.observedTimestampEpochNanos == 0
? this.loggerSharedState.getClock().now()
: this.observedTimestampEpochNanos;
loggerSharedState
.getLogRecordProcessor()
.onEmit(
context,
SdkReadWriteLogRecord.create(
loggerSharedState.getLogLimits(),
loggerSharedState.getResource(),
instrumentationScopeInfo,
timestampEpochNanos,
observedTimestampEpochNanos,
Span.fromContext(context).getSpanContext(),
severity,
severityText,
body,
attributes,
eventName));
.onEmit(context, createLogRecord(context, observedTimestampEpochNanos));
}

protected ReadWriteLogRecord createLogRecord(Context context, long observedTimestampEpochNanos) {
return SdkReadWriteLogRecord.create(
loggerSharedState.getLogLimits(),
loggerSharedState.getResource(),
instrumentationScopeInfo,
timestampEpochNanos,
observedTimestampEpochNanos,
Span.fromContext(context).getSpanContext(),
severity,
severityText,
body,
attributes,
eventName);
}
}
30 changes: 27 additions & 3 deletions sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerProvider;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
Expand All @@ -34,6 +36,8 @@ class SdkLogger implements Logger {
private final InstrumentationScopeInfo instrumentationScopeInfo;

protected volatile boolean loggerEnabled;
protected volatile int minimumSeverity;
protected volatile boolean traceBased;

SdkLogger(
LoggerSharedState loggerSharedState,
Expand All @@ -42,6 +46,8 @@ class SdkLogger implements Logger {
this.loggerSharedState = loggerSharedState;
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.loggerEnabled = loggerConfig.isEnabled();
this.minimumSeverity = loggerConfig.getMinimumSeverity();
this.traceBased = loggerConfig.isTraceBased();
}

static SdkLogger create(
Expand All @@ -58,8 +64,8 @@ public LogRecordBuilder logRecordBuilder() {
if (loggerEnabled) {
return INCUBATOR_AVAILABLE
? IncubatingUtil.createExtendedLogRecordBuilder(
loggerSharedState, instrumentationScopeInfo)
: new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo);
loggerSharedState, instrumentationScopeInfo, this)
: new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo, this);
}
return NOOP_LOGGER.logRecordBuilder();
}
Expand All @@ -71,10 +77,28 @@ InstrumentationScopeInfo getInstrumentationScopeInfo() {

// Visible for testing
public boolean isEnabled(Severity severity, Context context) {
return loggerEnabled;
if (!loggerEnabled) {
return false;
}

if (severity != Severity.UNDEFINED_SEVERITY_NUMBER
&& severity.getSeverityNumber() < minimumSeverity) {
return false;
}

if (traceBased) {
SpanContext spanContext = Span.fromContext(context).getSpanContext();
if (spanContext.isValid() && !spanContext.getTraceFlags().isSampled()) {
return false;
}
}

return true;
}

void updateLoggerConfig(LoggerConfig loggerConfig) {
loggerEnabled = loggerConfig.isEnabled();
minimumSeverity = loggerConfig.getMinimumSeverity();
traceBased = loggerConfig.isTraceBased();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
public abstract class LoggerConfig {

private static final LoggerConfig DEFAULT_CONFIG =
new AutoValue_LoggerConfig(/* enabled= */ true);
new AutoValue_LoggerConfig(
/* enabled= */ true, /* minimumSeverity= */ 0, /* traceBased= */ false);
private static final LoggerConfig DISABLED_CONFIG =
new AutoValue_LoggerConfig(/* enabled= */ false);
new AutoValue_LoggerConfig(
/* enabled= */ false, /* minimumSeverity= */ 0, /* traceBased= */ false);

/** Returns a disabled {@link LoggerConfig}. */
public static LoggerConfig disabled() {
Expand All @@ -44,6 +46,11 @@ public static LoggerConfig enabled() {
return DEFAULT_CONFIG;
}

/** Returns a new {@link Builder} for creating a {@link LoggerConfig}. */
public static Builder builder() {
return new Builder();
}

/**
* Returns the default {@link LoggerConfig}, which is used when no configurator is set or when the
* logger configurator returns {@code null} for a {@link InstrumentationScopeInfo}.
Expand All @@ -62,6 +69,86 @@ public static ScopeConfiguratorBuilder<LoggerConfig> configuratorBuilder() {

LoggerConfig() {}

/**
* Builder for {@link LoggerConfig}.
*
* <p>This class is internal and experimental. Its APIs are unstable and can change at any time.
* Its APIs (or a version of them) may be promoted to the public stable API in the future, but no
* guarantees are made.
*/
public static final class Builder {
private boolean enabled = true;
private int minimumSeverity = 0;
private boolean traceBased = false;

private Builder() {}

/**
* Sets whether the logger is enabled.
*
* @param enabled whether the logger is enabled
* @return this builder
*/
public Builder setEnabled(boolean enabled) {
this.enabled = enabled;
return this;
}

/**
* Sets the minimum severity level for log records to be processed.
*
* <p>Log records with a severity number less than this value will be dropped. Log records
* without a specified severity are not affected by this setting.
*
* @param minimumSeverity minimum severity level for log records to be processed
* @return this builder
*/
public Builder setMinimumSeverity(int minimumSeverity) {
this.minimumSeverity = minimumSeverity;
return this;
}

/**
* Sets whether to only process log records from traces when the trace is sampled.
*
* <p>When enabled, log records from unsampled traces will be dropped. Log records that are not
* associated with a trace context are unaffected.
*
* @param traceBased whether to only process log records from traces when the trace is sampled
* @return this builder
*/
public Builder setTraceBased(boolean traceBased) {
this.traceBased = traceBased;
return this;
}

/** Builds and returns a {@link LoggerConfig}. */
public LoggerConfig build() {
return new AutoValue_LoggerConfig(enabled, minimumSeverity, traceBased);
}
}

/** Returns {@code true} if this logger is enabled. Defaults to {@code true}. */
public abstract boolean isEnabled();

/**
* Returns the minimum severity level for log records to be processed.
*
* <p>Log records with a severity number less than this value will be dropped. Log records without
* a specified severity are not affected by this setting.
*
* <p>Defaults to {@code 0}.
*/
public abstract int getMinimumSeverity();

/**
* Returns {@code true} if this logger should only process log records from traces when the trace
* is sampled.
*
* <p>When enabled, log records from unsampled traces will be dropped. Log records that are not
* associated with a trace context are unaffected.
*
* <p>Defaults to {@code false}.
*/
public abstract boolean isTraceBased();
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import io.opentelemetry.api.common.AttributeKey;
Expand All @@ -24,6 +27,7 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
import io.opentelemetry.sdk.resources.Resource;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -57,7 +61,8 @@ void setup() {
when(loggerSharedState.getResource()).thenReturn(RESOURCE);
when(loggerSharedState.getClock()).thenReturn(clock);

builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO);
SdkLogger logger = new SdkLogger(loggerSharedState, SCOPE_INFO, LoggerConfig.enabled());
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO, logger);
}

@Test
Expand Down Expand Up @@ -121,6 +126,26 @@ void emit_NoFields() {
.hasSeverity(Severity.UNDEFINED_SEVERITY_NUMBER);
}

@Test
void emit_ChecksLoggerIsEnabled() {
SdkLogger logger = mock(SdkLogger.class);
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO, logger);

Severity severity = Severity.WARN;
Context context = Context.current();

// Configure mock to return false (disabled)
when(logger.isEnabled(severity, context)).thenReturn(false);

builder.setSeverity(severity).setContext(context).emit();

// Verify isEnabled was called with correct parameters
verify(logger).isEnabled(eq(severity), eq(context));

// Verify log was not processed (because isEnabled returned false)
assertThat(emittedLog.get()).isNull();
}

@Test
void testConvenienceAttributeMethods() {
builder
Expand Down
Loading
Loading