Let clock be overridden for log creators (#3823)

* Allow the clock to be overridden so that we aren't directly coupled to System.currentTimeMillis()

* move clock usage to dedicated test

* move clock foolery to its own dedicated test method

* address some code review comments and checkstyle violations

* cleanup: remove builder() from LogData interface and remove clock methods from LogBuilder interface.

* make inner impl static

* make method package private

* factor builder out to top-level class and remove creation work from constructor

* fix up tests for SdkLogEmitter and SdkLogBuilder

* backfill coverage for second shutdown

* remove mock
This commit is contained in:
jason plumb 2021-11-08 21:00:33 -08:00 committed by GitHub
parent 4b7e50928f
commit 7b86d53427
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 407 additions and 174 deletions

View File

@ -14,6 +14,7 @@ import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.logs.export.LogExporter;
import io.opentelemetry.sdk.resources.Resource;
@ -31,7 +32,7 @@ class OtlpJsonLoggingLogExporterTest {
Resource.create(Attributes.builder().put("key", "value").build());
private static final LogData LOG1 =
LogData.builder(RESOURCE, InstrumentationLibraryInfo.create("instrumentation", "1"))
LogDataBuilder.create(RESOURCE, InstrumentationLibraryInfo.create("instrumentation", "1"))
.setName("testLog1")
.setBody("body1")
.setFlags(0)
@ -44,7 +45,7 @@ class OtlpJsonLoggingLogExporterTest {
.build();
private static final LogData LOG2 =
LogData.builder(RESOURCE, InstrumentationLibraryInfo.create("instrumentation2", "2"))
LogDataBuilder.create(RESOURCE, InstrumentationLibraryInfo.create("instrumentation2", "2"))
.setName("testLog2")
.setBody("body2")
.setFlags(0)

View File

@ -17,6 +17,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.Body;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.time.LocalDateTime;
@ -50,7 +51,8 @@ class SystemOutLogExporterTest {
}
private static LogData sampleLog(long timestamp) {
return LogData.builder(Resource.empty(), InstrumentationLibraryInfo.create("logTest", "1.0"))
return LogDataBuilder.create(
Resource.empty(), InstrumentationLibraryInfo.create("logTest", "1.0"))
.setAttributes(Attributes.of(stringKey("cheese"), "cheddar", longKey("amount"), 1L))
.setBody(Body.stringBody("message"))
.setSeverity(Severity.ERROR3)

View File

@ -31,6 +31,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.Body;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.IdGenerator;
@ -318,7 +319,7 @@ class OtlpHttpLogExporterTest {
}
private static LogData generateFakeLog() {
return LogData.builder(
return LogDataBuilder.create(
Resource.getDefault(),
InstrumentationLibraryInfo.create("testLib", "1.0", "http://url"))
.setName("log-name")

View File

@ -24,7 +24,7 @@ import io.opentelemetry.proto.logs.v1.InstrumentationLibraryLogs;
import io.opentelemetry.proto.logs.v1.LogRecord;
import io.opentelemetry.proto.logs.v1.ResourceLogs;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.io.ByteArrayOutputStream;
@ -51,7 +51,7 @@ class LogsRequestMarshalerTest {
ResourceLogsMarshaler[] resourceLogsMarshalers =
ResourceLogsMarshaler.create(
Collections.singleton(
LogData.builder(
LogDataBuilder.create(
Resource.builder().put("one", 1).setSchemaUrl("http://url").build(),
InstrumentationLibraryInfo.create("testLib", "1.0", "http://url"))
.setName(NAME)
@ -84,7 +84,7 @@ class LogsRequestMarshalerTest {
parse(
LogRecord.getDefaultInstance(),
LogMarshaler.create(
LogData.builder(
LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setName(NAME)
@ -117,7 +117,7 @@ class LogsRequestMarshalerTest {
parse(
LogRecord.getDefaultInstance(),
LogMarshaler.create(
LogData.builder(
LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setBody(BODY)

View File

@ -33,6 +33,7 @@ import io.opentelemetry.proto.logs.v1.ResourceLogs;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.io.ByteArrayOutputStream;
@ -368,7 +369,7 @@ class OtlpGrpcLogsExporterTest {
}
private static LogData generateFakeLog() {
return LogData.builder(
return LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setEpoch(Instant.now())

View File

@ -23,6 +23,7 @@ import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse;
import io.opentelemetry.proto.collector.logs.v1.LogsServiceGrpc;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.nio.charset.StandardCharsets;
@ -39,7 +40,7 @@ class ExportTest {
private static final List<LogData> LOGS =
Collections.singletonList(
LogData.builder(
LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setEpoch(Instant.now())

View File

@ -23,6 +23,7 @@ import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse;
import io.opentelemetry.proto.collector.logs.v1.LogsServiceGrpc;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.nio.charset.StandardCharsets;
@ -39,7 +40,7 @@ class ExportTest {
private static final List<LogData> LOGS =
Collections.singletonList(
LogData.builder(
LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setEpoch(Instant.now())

View File

@ -23,6 +23,7 @@ import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse;
import io.opentelemetry.proto.collector.logs.v1.LogsServiceGrpc;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.nio.charset.StandardCharsets;
@ -39,7 +40,7 @@ class ExportTest {
private static final List<LogData> LOGS =
Collections.singletonList(
LogData.builder(
LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setEpoch(Instant.now())

View File

@ -20,6 +20,7 @@ import io.opentelemetry.exporter.otlp.internal.grpc.OkHttpGrpcExporterBuilder;
import io.opentelemetry.proto.collector.logs.v1.ExportLogsServiceResponse;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.net.InetAddress;
@ -40,7 +41,7 @@ class OkHttpOnlyExportTest {
private static final List<LogData> LOGS =
Collections.singletonList(
LogData.builder(
LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setEpoch(Instant.now())

View File

@ -53,6 +53,7 @@ import io.opentelemetry.proto.trace.v1.Span.Link;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.Body;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.logs.export.LogExporter;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
@ -375,7 +376,7 @@ abstract class OtlpExporterIntegrationTest {
private static void testLogExporter(LogExporter logExporter) {
LogData logData =
LogData.builder(
LogDataBuilder.create(
RESOURCE,
InstrumentationLibraryInfo.create(
OtlpExporterIntegrationTest.class.getName(), null))

View File

@ -5,6 +5,7 @@
package io.opentelemetry.sdk.logs;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.resources.Resource;
import java.util.List;
@ -18,11 +19,13 @@ final class LogEmitterSharedState {
private final Object lock = new Object();
private final Resource resource;
private final LogProcessor logProcessor;
private final Clock clock;
@Nullable private volatile CompletableResultCode shutdownResult = null;
LogEmitterSharedState(Resource resource, List<LogProcessor> logProcessors) {
LogEmitterSharedState(Resource resource, List<LogProcessor> logProcessors, Clock clock) {
this.resource = resource;
this.logProcessor = LogProcessor.composite(logProcessors);
this.clock = clock;
}
Resource getResource() {
@ -33,6 +36,10 @@ final class LogEmitterSharedState {
return logProcessor;
}
Clock getClock() {
return clock;
}
boolean hasBeenShutdown() {
return shutdownResult != null;
}

View File

@ -0,0 +1,99 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.logs;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.logs.data.Body;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
/** {@link SdkLogBuilder} is the SDK implementation of {@link LogBuilder}. */
final class SdkLogBuilder implements LogBuilder {
private final LogDataBuilder logDataBuilder;
private final LogEmitterSharedState logEmitterSharedState;
SdkLogBuilder(LogEmitterSharedState logEmitterSharedState, LogDataBuilder logDataBuilder) {
this.logEmitterSharedState = logEmitterSharedState;
this.logDataBuilder = logDataBuilder;
}
@Override
public LogBuilder setEpoch(long timestamp, TimeUnit unit) {
logDataBuilder.setEpoch(timestamp, unit);
return this;
}
@Override
public LogBuilder setEpoch(Instant instant) {
logDataBuilder.setEpoch(instant);
return this;
}
@Override
public LogBuilder setTraceId(String traceId) {
logDataBuilder.setTraceId(traceId);
return this;
}
@Override
public LogBuilder setSpanId(String spanId) {
logDataBuilder.setSpanId(spanId);
return this;
}
@Override
public LogBuilder setFlags(int flags) {
logDataBuilder.setFlags(flags);
return this;
}
@Override
public LogBuilder setSeverity(Severity severity) {
logDataBuilder.setSeverity(severity);
return this;
}
@Override
public LogBuilder setSeverityText(String severityText) {
logDataBuilder.setSeverityText(severityText);
return this;
}
@Override
public LogBuilder setName(String name) {
logDataBuilder.setName(name);
return this;
}
@Override
public LogBuilder setBody(Body body) {
logDataBuilder.setBody(body);
return this;
}
@Override
public LogBuilder setBody(String body) {
logDataBuilder.setBody(body);
return this;
}
@Override
public LogBuilder setAttributes(Attributes attributes) {
logDataBuilder.setAttributes(attributes);
return this;
}
@Override
public void emit() {
if (logEmitterSharedState.hasBeenShutdown()) {
return;
}
logEmitterSharedState.getLogProcessor().emit(logDataBuilder.build());
}
}

View File

@ -5,14 +5,8 @@
package io.opentelemetry.sdk.logs;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.Body;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
/** SDK implementation of {@link LogEmitter}. */
final class SdkLogEmitter implements LogEmitter {
@ -29,95 +23,16 @@ final class SdkLogEmitter implements LogEmitter {
@Override
public LogBuilder logBuilder() {
return new SdkLogBuilder();
LogDataBuilder logDataBuilder =
LogDataBuilder.create(
logEmitterSharedState.getResource(),
instrumentationLibraryInfo,
logEmitterSharedState.getClock());
return new SdkLogBuilder(logEmitterSharedState, logDataBuilder);
}
// VisibleForTesting
InstrumentationLibraryInfo getInstrumentationLibraryInfo() {
return instrumentationLibraryInfo;
}
private final class SdkLogBuilder implements LogBuilder {
private final LogDataBuilder logDataBuilder;
SdkLogBuilder() {
this.logDataBuilder =
LogData.builder(logEmitterSharedState.getResource(), instrumentationLibraryInfo);
}
@Override
public LogBuilder setEpoch(long timestamp, TimeUnit unit) {
logDataBuilder.setEpoch(timestamp, unit);
return this;
}
@Override
public LogBuilder setEpoch(Instant instant) {
logDataBuilder.setEpoch(instant);
return this;
}
@Override
public LogBuilder setTraceId(String traceId) {
logDataBuilder.setTraceId(traceId);
return this;
}
@Override
public LogBuilder setSpanId(String spanId) {
logDataBuilder.setSpanId(spanId);
return this;
}
@Override
public LogBuilder setFlags(int flags) {
logDataBuilder.setFlags(flags);
return this;
}
@Override
public LogBuilder setSeverity(Severity severity) {
logDataBuilder.setSeverity(severity);
return this;
}
@Override
public LogBuilder setSeverityText(String severityText) {
logDataBuilder.setSeverityText(severityText);
return this;
}
@Override
public LogBuilder setName(String name) {
logDataBuilder.setName(name);
return this;
}
@Override
public LogBuilder setBody(Body body) {
logDataBuilder.setBody(body);
return this;
}
@Override
public LogBuilder setBody(String body) {
logDataBuilder.setBody(body);
return this;
}
@Override
public LogBuilder setAttributes(Attributes attributes) {
logDataBuilder.setAttributes(attributes);
return this;
}
@Override
public void emit() {
if (logEmitterSharedState.hasBeenShutdown()) {
return;
}
logEmitterSharedState.getLogProcessor().emit(logDataBuilder.build());
}
}
}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.sdk.logs;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.internal.ComponentRegistry;
import io.opentelemetry.sdk.resources.Resource;
@ -32,8 +33,8 @@ public final class SdkLogEmitterProvider implements Closeable {
return new SdkLogEmitterProviderBuilder();
}
SdkLogEmitterProvider(Resource resource, List<LogProcessor> processors) {
this.sharedState = new LogEmitterSharedState(resource, processors);
SdkLogEmitterProvider(Resource resource, List<LogProcessor> processors, Clock clock) {
this.sharedState = new LogEmitterSharedState(resource, processors, clock);
this.logEmitterComponentRegistry =
new ComponentRegistry<>(
instrumentationLibraryInfo ->

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.logs;
import static java.util.Objects.requireNonNull;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.resources.Resource;
import java.util.ArrayList;
@ -17,6 +18,7 @@ public final class SdkLogEmitterProviderBuilder {
private final List<LogProcessor> logProcessors = new ArrayList<>();
private Resource resource = Resource.getDefault();
private Clock clock = Clock.getDefault();
SdkLogEmitterProviderBuilder() {}
@ -46,12 +48,28 @@ public final class SdkLogEmitterProviderBuilder {
return this;
}
/**
* Assign a {@link Clock}. The {@link Clock} may be used to determine "now" in the event that the
* epoch millis are not set directly.
*
* <p>The {@code clock} must be thread-safe and return immediately (no remote calls, as contention
* free as possible).
*
* @param clock The clock to use for all temporal needs.
* @return this
*/
public SdkLogEmitterProviderBuilder setClock(Clock clock) {
requireNonNull(clock, "clock");
this.clock = clock;
return this;
}
/**
* Create a {@link SdkLogEmitterProvider} instance.
*
* @return an instance configured with the provided options
*/
public SdkLogEmitterProvider build() {
return new SdkLogEmitterProvider(resource, logProcessors);
return new SdkLogEmitterProvider(resource, logProcessors, clock);
}
}

View File

@ -19,12 +19,6 @@ import javax.annotation.concurrent.Immutable;
@Immutable
public interface LogData {
/** Returns a new {@link LogDataBuilder}. */
static LogDataBuilder builder(
Resource resource, InstrumentationLibraryInfo instrumentationLibraryInfo) {
return new LogDataBuilder(resource, instrumentationLibraryInfo);
}
/** Returns the resource of this log. */
Resource getResource();

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.logs.data;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.resources.Resource;
import java.time.Instant;
@ -27,11 +28,26 @@ public final class LogDataBuilder {
@Nullable private String severityText;
@Nullable private String name;
private Body body = Body.stringBody("");
private final Clock clock;
private final AttributesBuilder attributeBuilder = Attributes.builder();
LogDataBuilder(Resource resource, InstrumentationLibraryInfo instrumentationLibraryInfo) {
private LogDataBuilder(
Resource resource, InstrumentationLibraryInfo instrumentationLibraryInfo, Clock clock) {
this.resource = resource;
this.instrumentationLibraryInfo = instrumentationLibraryInfo;
this.clock = clock;
}
/** Returns a new {@link LogDataBuilder} with the default clock. */
public static LogDataBuilder create(
Resource resource, InstrumentationLibraryInfo instrumentationLibraryInfo) {
return create(resource, instrumentationLibraryInfo, Clock.getDefault());
}
/** Returns a new {@link LogDataBuilder}. */
public static LogDataBuilder create(
Resource resource, InstrumentationLibraryInfo instrumentationLibraryInfo, Clock clock) {
return new LogDataBuilder(resource, instrumentationLibraryInfo, clock);
}
/** Set the epoch timestamp using the timestamp and unit. */
@ -102,7 +118,7 @@ public final class LogDataBuilder {
/** Build a {@link LogData} instance from the configured properties. */
public LogData build() {
if (epochNanos == 0) {
epochNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis());
epochNanos = clock.now();
}
return LogDataImpl.create(
resource,

View File

@ -0,0 +1,31 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.logs;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.opentelemetry.sdk.common.CompletableResultCode;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
class LogEmitterSharedStateTest {
@Test
void shutdown() {
LogProcessor logProcessor = mock(LogProcessor.class);
CompletableResultCode code = new CompletableResultCode();
when(logProcessor.shutdown()).thenReturn(code);
List<LogProcessor> processors = Collections.singletonList(logProcessor);
LogEmitterSharedState state = new LogEmitterSharedState(null, processors, null);
state.shutdown();
state.shutdown();
verify(logProcessor, times(1)).shutdown();
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.logs;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.logs.data.Body;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
class SdkLogBuilderTest {
@Test
void buildAndEmit() {
Instant now = Instant.now();
String name = "skippy";
String bodyStr = "body";
String spanId = "abc123";
String traceId = "99321";
int flags = 21;
String sevText = "sevText";
Severity severity = Severity.DEBUG3;
Attributes attrs = Attributes.empty();
AtomicReference<LogData> seenLog = new AtomicReference<>();
LogProcessor logProcessor = seenLog::set;
LogEmitterSharedState state = mock(LogEmitterSharedState.class);
LogDataBuilder delegate = mock(LogDataBuilder.class);
LogData logData = mock(LogData.class);
Body body = mock(Body.class);
when(state.getLogProcessor()).thenReturn(logProcessor);
when(delegate.build()).thenReturn(logData);
SdkLogBuilder builder = new SdkLogBuilder(state, delegate);
builder.setBody(body);
verify(delegate).setBody(body);
builder.setBody(bodyStr);
verify(delegate).setBody(bodyStr);
builder.setEpoch(123, TimeUnit.SECONDS);
verify(delegate).setEpoch(123, TimeUnit.SECONDS);
builder.setEpoch(now);
verify(delegate).setEpoch(now);
builder.setAttributes(attrs);
verify(delegate).setAttributes(attrs);
builder.setFlags(flags);
verify(delegate).setFlags(flags);
builder.setName(name);
verify(delegate).setName(name);
builder.setSeverity(severity);
verify(delegate).setSeverity(severity);
builder.setSeverityText(sevText);
verify(delegate).setSeverityText(sevText);
builder.setSpanId(spanId);
verify(delegate).setSpanId(spanId);
builder.setTraceId(traceId);
verify(delegate).setTraceId(traceId);
builder.emit();
assertThat(seenLog.get()).isSameAs(logData);
}
@Test
void emitAfterShutdown() {
LogEmitterSharedState state = mock(LogEmitterSharedState.class);
LogDataBuilder delegate = mock(LogDataBuilder.class);
when(state.hasBeenShutdown()).thenReturn(true);
SdkLogBuilder builder = new SdkLogBuilder(state, delegate);
builder.emit();
verify(state, never()).getLogProcessor();
verifyNoInteractions(delegate);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.logs;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.resources.Resource;
import java.util.LinkedList;
import java.util.List;
import org.junit.jupiter.api.Test;
class SdkLogEmitterProviderBuilderTest {
@Test
void canSetClock() {
Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(13L);
List<LogData> seenLogs = new LinkedList<>();
LogProcessor processor = seenLogs::add;
SdkLogEmitterProviderBuilder builder =
new SdkLogEmitterProviderBuilder()
.setResource(Resource.getDefault())
.addLogProcessor(processor)
.setClock(clock);
SdkLogEmitterProvider provider = builder.build();
provider.logEmitterBuilder("inst").build().logBuilder().emit();
assertThat(seenLogs.size()).isEqualTo(1);
assertThat(seenLogs.get(0).getEpochNanos()).isEqualTo(13L);
}
}

View File

@ -7,14 +7,20 @@ package io.opentelemetry.sdk.logs;
import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.resources.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -179,4 +185,18 @@ class SdkLogEmitterProviderTest {
sdkLogEmitterProvider.close();
verify(logProcessor).shutdown();
}
@Test
void canSetClock() {
long now = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis());
Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(now);
List<LogData> seenLogs = new LinkedList<>();
logProcessor = seenLogs::add;
sdkLogEmitterProvider =
SdkLogEmitterProvider.builder().setClock(clock).addLogProcessor(logProcessor).build();
sdkLogEmitterProvider.logEmitterBuilder(null).build().logBuilder().emit();
assertThat(seenLogs.size()).isEqualTo(1);
assertThat(seenLogs.get(0).getEpochNanos()).isEqualTo(now);
}
}

View File

@ -6,75 +6,36 @@
package io.opentelemetry.sdk.logs;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.Body;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.resources.Resource;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class SdkLogEmitterTest {
private static final String INSTRUMENTATION_LIBRARY_NAME = SdkLogEmitter.class.getName();
private static final String INSTRUMENTATION_LIBRARY_VERSION = "0.0.1";
private static final String SCHEMA_URL = "http://schemaurl";
@Mock private LogProcessor logProcessor;
private SdkLogEmitterProvider sdkLogEmitterProvider;
private LogEmitter sdkLogEmitter;
@BeforeEach
void setup() {
when(logProcessor.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
sdkLogEmitterProvider = SdkLogEmitterProvider.builder().addLogProcessor(logProcessor).build();
sdkLogEmitter =
sdkLogEmitterProvider
.logEmitterBuilder(INSTRUMENTATION_LIBRARY_NAME)
.setInstrumentationVersion(INSTRUMENTATION_LIBRARY_VERSION)
.setSchemaUrl(SCHEMA_URL)
.build();
}
@Test
void emit() {
long epochMillis = System.currentTimeMillis();
Body body = Body.stringBody("message");
sdkLogEmitter.logBuilder().setEpoch(epochMillis, TimeUnit.MILLISECONDS).setBody(body).emit();
void logBuilder() {
Instant now = Instant.now();
LogEmitterSharedState state = mock(LogEmitterSharedState.class);
InstrumentationLibraryInfo info = InstrumentationLibraryInfo.create("foo", "bar");
AtomicReference<LogData> seenLog = new AtomicReference<>();
LogProcessor logProcessor = seenLog::set;
ArgumentCaptor<LogData> captor = ArgumentCaptor.forClass(LogData.class);
verify(logProcessor).emit(captor.capture());
when(state.getResource()).thenReturn(Resource.getDefault());
when(state.getLogProcessor()).thenReturn(logProcessor);
LogData logData = captor.getValue();
assertThat(logData.getResource()).isEqualTo(Resource.getDefault());
assertThat(logData.getInstrumentationLibraryInfo())
.isEqualTo(
InstrumentationLibraryInfo.create(
INSTRUMENTATION_LIBRARY_NAME, INSTRUMENTATION_LIBRARY_VERSION, SCHEMA_URL));
assertThat(logData.getEpochNanos()).isEqualTo(TimeUnit.MILLISECONDS.toNanos(epochMillis));
assertThat(logData.getBody()).isEqualTo(body);
}
SdkLogEmitter emitter = new SdkLogEmitter(state, info);
LogBuilder logBuilder = emitter.logBuilder();
logBuilder.setEpoch(now);
logBuilder.setBody("foo");
@Test
void emit_AfterShutdown() {
sdkLogEmitterProvider.shutdown().join(10, TimeUnit.SECONDS);
sdkLogEmitter.logBuilder().setEpoch(Instant.now()).setBody("message").emit();
verify(logProcessor, never()).emit(any());
// Have to test through the builder
logBuilder.emit();
assertThat(seenLog.get().getBody().asString()).isEqualTo("foo");
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.logs.data;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.resources.Resource;
import org.junit.jupiter.api.Test;
class LogDataBuilderTest {
@Test
void canSetClock() {
Resource resource = Resource.getDefault();
InstrumentationLibraryInfo libraryInfo = InstrumentationLibraryInfo.empty();
Clock clock = mock(Clock.class);
when(clock.now()).thenReturn(12L);
LogDataBuilder builder = LogDataBuilder.create(resource, libraryInfo, clock);
LogData result = builder.build();
assertEquals(12L, result.getEpochNanos());
}
}

View File

@ -11,6 +11,7 @@ import io.opentelemetry.api.trace.TraceFlags;
import io.opentelemetry.api.trace.TraceId;
import io.opentelemetry.sdk.common.InstrumentationLibraryInfo;
import io.opentelemetry.sdk.logs.data.LogData;
import io.opentelemetry.sdk.logs.data.LogDataBuilder;
import io.opentelemetry.sdk.logs.data.Severity;
import io.opentelemetry.sdk.resources.Resource;
import java.util.concurrent.TimeUnit;
@ -18,7 +19,7 @@ import java.util.concurrent.TimeUnit;
public final class TestUtil {
public static LogData createLogData(Severity severity, String message) {
return LogData.builder(
return LogDataBuilder.create(
Resource.create(Attributes.builder().put("testKey", "testValue").build()),
InstrumentationLibraryInfo.create("instrumentation", "1"))
.setEpoch(System.currentTimeMillis(), TimeUnit.MILLISECONDS)

View File

@ -0,0 +1 @@
mock-maker-inline