diff --git a/api/src/main/java/io/opentelemetry/OpenTelemetry.java b/api/src/main/java/io/opentelemetry/OpenTelemetry.java
index e113a466548..ae7a9a3cc7d 100644
--- a/api/src/main/java/io/opentelemetry/OpenTelemetry.java
+++ b/api/src/main/java/io/opentelemetry/OpenTelemetry.java
@@ -23,6 +23,10 @@
import io.opentelemetry.correlationcontext.spi.CorrelationContextManagerFactory;
import io.opentelemetry.internal.Obfuscated;
import io.opentelemetry.internal.Utils;
+import io.opentelemetry.logs.DefaultLogSinkProvider;
+import io.opentelemetry.logs.LogSink;
+import io.opentelemetry.logs.spi.LogSinkProvider;
+import io.opentelemetry.logs.spi.LogSinkProviderFactory;
import io.opentelemetry.metrics.DefaultMeterProvider;
import io.opentelemetry.metrics.Meter;
import io.opentelemetry.metrics.MeterProvider;
@@ -55,6 +59,7 @@ public final class OpenTelemetry {
private final TracerProvider tracerProvider;
private final MeterProvider meterProvider;
private final CorrelationContextManager contextManager;
+ private final LogSinkProvider logSinkProvider;
private volatile ContextPropagators propagators =
DefaultContextPropagators.builder().addHttpTextFormat(new HttpTraceContext()).build();
@@ -144,6 +149,34 @@ public static Meter getMeter(String instrumentationName, String instrumentationV
return getMeterProvider().get(instrumentationName, instrumentationVersion);
}
+ /**
+ * Returns a singleton {@link LogSinkProvider}.
+ *
+ * @return registered LogSinkProvider or default via {@link DefaultLogSinkProvider#getInstance()}.
+ * @throws IllegalStateException if a specified MeterProvider (via system properties) could not be
+ * found.
+ * @since 0.1.0
+ */
+ private static LogSinkProvider getLogSinkProvider() {
+ return getInstance().logSinkProvider;
+ }
+
+ /**
+ * Gets or creates a named and versioned log sink instance.
+ *
+ *
This is a shortcut method for
+ * getLogSinkProvider().get(instrumentationName, instrumentationVersion).
+ *
+ * @param instrumentationName The name of the instrumentation library, not the name of the
+ * instrument*ed* library.
+ * @param instrumentationVersion The version of the instrumentation library.
+ * @return a tracer instance.
+ * @since 0.7.0
+ */
+ public static LogSink getLogSink(String instrumentationName, String instrumentationVersion) {
+ return getLogSinkProvider().get(instrumentationName, instrumentationVersion);
+ }
+
/**
* Returns a singleton {@link CorrelationContextManager}.
*
@@ -210,6 +243,13 @@ private OpenTelemetry() {
meterProviderFactory != null
? meterProviderFactory.create()
: DefaultMeterProvider.getInstance();
+
+ LogSinkProviderFactory logProviderFactory = loadSpi(LogSinkProviderFactory.class);
+ this.logSinkProvider =
+ logProviderFactory != null
+ ? logProviderFactory.create()
+ : DefaultLogSinkProvider.getInstance();
+
CorrelationContextManagerFactory contextManagerProvider =
loadSpi(CorrelationContextManagerFactory.class);
contextManager =
diff --git a/api/src/main/java/io/opentelemetry/common/AnyValue.java b/api/src/main/java/io/opentelemetry/common/AnyValue.java
new file mode 100644
index 00000000000..893fd4ec2ad
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/common/AnyValue.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.common;
+
+import com.google.auto.value.AutoValue;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * A class that represents all the possible values for a data body. An {@code AnyValue} can have 6
+ * types of values: {@code String}, {@code boolean}, {@code int}, {@code double}, {@code array}, or
+ * {@code kvlist}. represented through {@code AnyValue.Type}. A {@code array} or a {@code kvlist}
+ * can in turn hold other {@code AnyValue} instances, allowing for mapping to JSON-like structures.
+ *
+ * @since 0.7.0
+ */
+@Immutable
+public abstract class AnyValue {
+
+ /**
+ * An enum that represents all the possible value types for an {@code AnyValue}.
+ *
+ * @since 0.7.0
+ */
+ public enum Type {
+ STRING,
+ BOOL,
+ INT,
+ DOUBLE,
+ ARRAY,
+ KVLIST
+ }
+
+ /**
+ * Returns an {@code AnyValue} with a string value.
+ *
+ * @param stringValue The new value.
+ * @return an {@code AnyValue} with a string value.
+ * @since 0.7.0
+ */
+ public static AnyValue stringAnyValue(String stringValue) {
+ return AnyValueString.create(stringValue);
+ }
+
+ /**
+ * Returns the string value of this {@code AnyValue}. An UnsupportedOperationException will be
+ * thrown if getType() is not {@link AnyValue.Type#STRING}.
+ *
+ * @return the string value of this {@code AttributeValue}.
+ * @since 0.7.0
+ */
+ public String getStringValue() {
+ throw new UnsupportedOperationException(
+ String.format("This type can only return %s data", getType().name()));
+ }
+
+ /**
+ * Returns an {@code AnyValue} with an int value.
+ *
+ * @param intValue The new value.
+ * @return an {@code AnyValue} with a int value.
+ * @since 0.7.0
+ */
+ public static AnyValue intAnyValue(int intValue) {
+ return AnyValueInt.create(intValue);
+ }
+
+ public int getIntValue() {
+ throw new UnsupportedOperationException(
+ String.format("This type can only return %s data", getType().name()));
+ }
+
+ /**
+ * Returns an {@code AnyValue} with a bool value.
+ *
+ * @param boolValue The new value.
+ * @return an {@code AnyValue} with a bool value.
+ * @since 0.7.0
+ */
+ public static AnyValue boolAnyValue(boolean boolValue) {
+ return AnyValueBool.create(boolValue);
+ }
+
+ /**
+ * Returns the boolean value of this {@code AnyValue}. An UnsupportedOperationException will be
+ * thrown if getType() is not {@link AnyValue.Type#BOOL}.
+ *
+ * @return the boolean value of this {@code AttributeValue}.
+ * @since 0.7.0
+ */
+ public boolean getBoolValue() {
+ throw new UnsupportedOperationException(
+ String.format("This type can only return %s data", getType().name()));
+ }
+
+ /**
+ * Returns an {@code AnyValue} with a double value.
+ *
+ * @param doubleValue The new value.
+ * @return an {@code AnyValue} with a double value.
+ * @since 0.7.0
+ */
+ public static AnyValue doubleAnyValue(double doubleValue) {
+ return AnyValueDouble.create(doubleValue);
+ }
+
+ /**
+ * Returns the double value of this {@code AnyValue}. An UnsupportedOperationException will be
+ * thrown if getType() is not {@link AnyValue.Type#DOUBLE}.
+ *
+ * @return the double value of this {@code AttributeValue}.
+ * @since 0.7.0
+ */
+ public double getDoubleValue() {
+ throw new UnsupportedOperationException(
+ String.format("This type can only return %s data", getType().name()));
+ }
+
+ /**
+ * Returns an {@code AnyValue} with a array value.
+ *
+ * @param values The new value.
+ * @return an {@code AnyValue} with a array value.
+ * @since 0.7.0
+ */
+ public static AnyValue arrayAnyValue(List values) {
+ return AnyValueArray.create(values);
+ }
+
+ /**
+ * Returns the array value of this {@code AnyValue}. An UnsupportedOperationException will be
+ * thrown if getType() is not {@link AnyValue.Type#ARRAY}.
+ *
+ * @return the array value of this {@code AttributeValue}.
+ * @since 0.7.0
+ */
+ public List getArrayValue() {
+ throw new UnsupportedOperationException(
+ String.format("This type can only return %s data", getType().name()));
+ }
+
+ /**
+ * Returns an {@code AnyValue} with a kvlist value.
+ *
+ * @param values The new value.
+ * @return an {@code AnyValue} with a kvlist value.
+ * @since 0.7.0
+ */
+ public static AnyValue kvlistAnyValue(Map values) {
+ return AnyValueKvlist.create(values);
+ }
+
+ /**
+ * Returns the string value of this {@code AnyValue}. An UnsupportedOperationException will be
+ * thrown if getType() is not {@link AnyValue.Type#STRING}.
+ *
+ * @return the string value of this {@code AttributeValue}.
+ * @since 0.7.0
+ */
+ public Map getKvlistValue() {
+ throw new UnsupportedOperationException(
+ String.format("This type can only return %s data", getType().name()));
+ }
+
+ public abstract Type getType();
+
+ @Immutable
+ @AutoValue
+ abstract static class AnyValueString extends AnyValue {
+ AnyValueString() {}
+
+ static AnyValue create(String stringValue) {
+ return new AutoValue_AnyValue_AnyValueString(stringValue);
+ }
+
+ @Override
+ public final Type getType() {
+ return Type.STRING;
+ }
+
+ @Override
+ @Nullable
+ public abstract String getStringValue();
+ }
+
+ @Immutable
+ @AutoValue
+ abstract static class AnyValueInt extends AnyValue {
+ AnyValueInt() {}
+
+ static AnyValue create(int intValue) {
+ return new AutoValue_AnyValue_AnyValueInt(intValue);
+ }
+
+ @Override
+ public final Type getType() {
+ return Type.INT;
+ }
+
+ @Override
+ public abstract int getIntValue();
+ }
+
+ @Immutable
+ @AutoValue
+ abstract static class AnyValueBool extends AnyValue {
+ AnyValueBool() {}
+
+ static AnyValue create(boolean boolValue) {
+ return new AutoValue_AnyValue_AnyValueBool(boolValue);
+ }
+
+ @Override
+ public final Type getType() {
+ return Type.BOOL;
+ }
+
+ @Override
+ public abstract boolean getBoolValue();
+ }
+
+ @Immutable
+ @AutoValue
+ abstract static class AnyValueDouble extends AnyValue {
+ AnyValueDouble() {}
+
+ static AnyValue create(double doubleValue) {
+ return new AutoValue_AnyValue_AnyValueDouble(doubleValue);
+ }
+
+ @Override
+ public final Type getType() {
+ return Type.DOUBLE;
+ }
+
+ @Override
+ public abstract double getDoubleValue();
+ }
+
+ @Immutable
+ @AutoValue
+ abstract static class AnyValueArray extends AnyValue {
+ AnyValueArray() {}
+
+ static AnyValue create(List arrayValue) {
+ return new AutoValue_AnyValue_AnyValueArray(arrayValue);
+ }
+
+ @Override
+ public final Type getType() {
+ return Type.ARRAY;
+ }
+
+ @Override
+ public abstract List getArrayValue();
+ }
+
+ @Immutable
+ @AutoValue
+ abstract static class AnyValueKvlist extends AnyValue {
+ AnyValueKvlist() {}
+
+ static AnyValue create(Map kvlistValue) {
+ return new AutoValue_AnyValue_AnyValueKvlist(kvlistValue);
+ }
+
+ @Override
+ public final Type getType() {
+ return Type.KVLIST;
+ }
+
+ @Override
+ public abstract Map getKvlistValue();
+ }
+}
diff --git a/api/src/main/java/io/opentelemetry/logs/DefaultLogRecord.java b/api/src/main/java/io/opentelemetry/logs/DefaultLogRecord.java
new file mode 100644
index 00000000000..6b02f28462e
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/logs/DefaultLogRecord.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs;
+
+import io.opentelemetry.common.AnyValue;
+import io.opentelemetry.common.AttributeValue;
+import java.util.Map;
+
+public class DefaultLogRecord implements LogRecord {
+ private long timestamp;
+ private byte[] traceId;
+ private byte[] spanId;
+ private int flags;
+ private Severity severity;
+ private String severityText;
+ private String name;
+ private AnyValue body;
+ private Map attributes;
+
+ private DefaultLogRecord() {}
+
+ /**
+ * Clone method, used so that changes through the builder do not affect the record built.
+ *
+ * @param template LogRecord from which to copy
+ */
+ private DefaultLogRecord(DefaultLogRecord template) {
+ timestamp = template.timestamp;
+ traceId = template.traceId;
+ spanId = template.spanId;
+ flags = template.flags;
+ severity = template.severity;
+ severityText = template.severityText;
+ name = template.name;
+ body = template.body;
+ attributes = template.attributes;
+ }
+
+ @Override
+ public long getTimeUnixNano() {
+ return timestamp;
+ }
+
+ @Override
+ public byte[] getTraceId() {
+ return traceId;
+ }
+
+ @Override
+ public byte[] getSpanId() {
+ return spanId;
+ }
+
+ @Override
+ public int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public Severity getSeverity() {
+ return severity;
+ }
+
+ @Override
+ public String getSeverityText() {
+ return severityText;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public AnyValue getBody() {
+ return body;
+ }
+
+ @Override
+ public Map getAttributes() {
+ return attributes;
+ }
+
+ public static class Builder implements LogRecord.Builder {
+ DefaultLogRecord template = new DefaultLogRecord();
+
+ @Override
+ public LogRecord.Builder withUnixTimeNano(long timestamp) {
+ template.timestamp = timestamp;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withTraceId(byte[] traceId) {
+ template.traceId = traceId;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withSpanId(byte[] spanId) {
+ template.spanId = spanId;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withFlags(int flags) {
+ template.flags = flags;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withSeverity(Severity severity) {
+ template.severity = severity;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withSeverityText(String severityText) {
+ template.severityText = severityText;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withName(String name) {
+ template.name = name;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withBody(AnyValue body) {
+ template.body = body;
+ return this;
+ }
+
+ @Override
+ public LogRecord.Builder withAttributes(Map attributes) {
+ template.attributes = attributes;
+ return this;
+ }
+
+ @Override
+ public LogRecord build() {
+ return new DefaultLogRecord(template);
+ }
+ }
+}
diff --git a/api/src/main/java/io/opentelemetry/logs/DefaultLogSinkProvider.java b/api/src/main/java/io/opentelemetry/logs/DefaultLogSinkProvider.java
new file mode 100644
index 00000000000..e0371851b96
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/logs/DefaultLogSinkProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs;
+
+import io.opentelemetry.logs.spi.LogSinkProvider;
+import javax.annotation.Nullable;
+
+public class DefaultLogSinkProvider implements LogSinkProvider {
+
+ @Nullable private static DefaultLogSinkProvider instance;
+ @Nullable static LogSink sink;
+
+ @Override
+ public LogSink get(String instrumentationName) {
+ return getSink();
+ }
+
+ @Override
+ public LogSink get(String instrumentationName, String instrumentationVersion) {
+ return getSink();
+ }
+
+ private static LogSink getSink() {
+ if (sink == null) {
+ sink = new NoOpLogSink();
+ }
+ return sink;
+ }
+
+ /**
+ * Returns a shared no-op instance of {@link LogSinkProvider}.
+ *
+ * @return no-op instance
+ */
+ public static LogSinkProvider getInstance() {
+ if (instance == null) {
+ instance = new DefaultLogSinkProvider();
+ }
+ return instance;
+ }
+}
diff --git a/api/src/main/java/io/opentelemetry/logs/LogRecord.java b/api/src/main/java/io/opentelemetry/logs/LogRecord.java
new file mode 100644
index 00000000000..3d38d774201
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/logs/LogRecord.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs;
+
+import io.opentelemetry.common.AnyValue;
+import io.opentelemetry.common.AttributeValue;
+import java.util.Map;
+
+public interface LogRecord {
+ long getTimeUnixNano();
+
+ byte[] getTraceId();
+
+ byte[] getSpanId();
+
+ int getFlags();
+
+ Severity getSeverity();
+
+ String getSeverityText();
+
+ String getName();
+
+ AnyValue getBody();
+
+ Map getAttributes();
+
+ enum Severity {
+ UNDEFINED_SEVERITY_NUMBER(0),
+ TRACE(1),
+ TRACE2(2),
+ TRACE3(3),
+ TRACE4(4),
+ DEBUG(5),
+ DEBUG2(6),
+ DEBUG3(7),
+ DEBUG4(8),
+ INFO(9),
+ INFO2(10),
+ INFO3(11),
+ INFO4(12),
+ WARN(13),
+ WARN2(14),
+ WARN3(15),
+ WARN4(16),
+ ERROR(17),
+ ERROR2(18),
+ ERROR3(19),
+ ERROR4(20),
+ FATAL(21),
+ FATAL2(22),
+ FATAL3(23),
+ FATAL4(24),
+ ;
+
+ private final int severityNumber;
+
+ Severity(int severityNumber) {
+ this.severityNumber = severityNumber;
+ }
+
+ public int getSeverityNumber() {
+ return severityNumber;
+ }
+ }
+
+ interface Builder {
+ Builder withUnixTimeNano(long timestamp);
+
+ Builder withTraceId(byte[] traceId);
+
+ Builder withSpanId(byte[] spanId);
+
+ Builder withFlags(int flags);
+
+ Builder withSeverity(Severity severity);
+
+ Builder withSeverityText(String severityText);
+
+ Builder withName(String name);
+
+ Builder withBody(AnyValue body);
+
+ Builder withAttributes(Map attributes);
+
+ LogRecord build();
+ }
+}
diff --git a/api/src/main/java/io/opentelemetry/logs/LogSink.java b/api/src/main/java/io/opentelemetry/logs/LogSink.java
new file mode 100644
index 00000000000..6cd87fac5c4
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/logs/LogSink.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs;
+
+public interface LogSink {
+ /**
+ * Supply a {@link LogRecord} object for transport if a logging backend is supplied and
+ * configured.
+ *
+ * @param record record to transmit
+ * @since 0.7.0
+ */
+ void offer(LogRecord record);
+}
diff --git a/api/src/main/java/io/opentelemetry/logs/NoOpLogSink.java b/api/src/main/java/io/opentelemetry/logs/NoOpLogSink.java
new file mode 100644
index 00000000000..73321531ef5
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/logs/NoOpLogSink.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs;
+
+public class NoOpLogSink implements LogSink {
+ @Override
+ public void offer(LogRecord record) {}
+}
diff --git a/api/src/main/java/io/opentelemetry/logs/spi/LogSinkProvider.java b/api/src/main/java/io/opentelemetry/logs/spi/LogSinkProvider.java
new file mode 100644
index 00000000000..1f64f792381
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/logs/spi/LogSinkProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs.spi;
+
+import io.opentelemetry.logs.LogSink;
+
+public interface LogSinkProvider {
+
+ /**
+ * Gets or creates a named log sink instance.
+ *
+ * @param instrumentationName The name of the instrumentation library, not the name of the
+ * instrument*ed* library.
+ * @return a tracer instance.
+ * @since 0.7.0
+ */
+ LogSink get(String instrumentationName);
+
+ /**
+ * Gets or creates a named and versioned log sink instance.
+ *
+ * @param instrumentationName The name of the instrumentation library, not the name of the
+ * instrument*ed* library.
+ * @param instrumentationVersion The version of the instrumentation library.
+ * @return a log sink instance.
+ * @since 0.7.0
+ */
+ LogSink get(String instrumentationName, String instrumentationVersion);
+}
diff --git a/api/src/main/java/io/opentelemetry/logs/spi/LogSinkProviderFactory.java b/api/src/main/java/io/opentelemetry/logs/spi/LogSinkProviderFactory.java
new file mode 100644
index 00000000000..ab57affcaa7
--- /dev/null
+++ b/api/src/main/java/io/opentelemetry/logs/spi/LogSinkProviderFactory.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs.spi;
+
+/**
+ * LogSinkProviderFactory is a service provider for {@link LogSinkProvider}. Fully qualified class
+ * name of the implementation should be registered in {@code
+ * META-INF/services/io.opentelemetry.logs.spi.LogSinkProviderFactory}.
+ *
+ * A specific implementation can be selected by a system property {@code
+ * io.opentelemetry.logs.spi.LogSinkProviderFactory} with value of fully qualified class name.
+ *
+ * @see io.opentelemetry.OpenTelemetry
+ */
+public interface LogSinkProviderFactory {
+ /**
+ * Creates a new {@link LogSinkProvider} interface.
+ *
+ * @return a log sink provider instance
+ * @since 0.7.0
+ */
+ LogSinkProvider create();
+}
diff --git a/api/src/test/java/io/opentelemetry/common/AnyValueTest.java b/api/src/test/java/io/opentelemetry/common/AnyValueTest.java
new file mode 100644
index 00000000000..639073046ae
--- /dev/null
+++ b/api/src/test/java/io/opentelemetry/common/AnyValueTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class AnyValueTest {
+
+ @Test
+ public void stringAnyValue() {
+ String testString = "e176f922-cf82-4fe7-93d0-f68440a825cf";
+ AnyValue stringValue = AnyValue.stringAnyValue(testString);
+ assertThat(stringValue.getStringValue()).isEqualTo(testString);
+ assertThat(stringValue.getType()).isEqualTo(AnyValue.Type.STRING);
+ }
+
+ @Test
+ public void boolAnyValue() {
+ AnyValue boolValue = AnyValue.boolAnyValue(true);
+ assertThat(boolValue.getBoolValue()).isTrue();
+ assertThat(boolValue.getType()).isEqualTo(AnyValue.Type.BOOL);
+ }
+
+ @Test
+ public void intAnyValue() {
+ int testInt = 12345;
+ AnyValue intValue = AnyValue.intAnyValue(testInt);
+ assertThat(intValue.getIntValue()).isEqualTo(testInt);
+ assertThat(intValue.getType()).isEqualTo(AnyValue.Type.INT);
+ }
+
+ @Test
+ public void doubleAnyValue() {
+ double testDouble = 12345.0d;
+ AnyValue doubleValue = AnyValue.doubleAnyValue(testDouble);
+ assertThat(doubleValue.getDoubleValue()).isEqualTo(testDouble);
+ assertThat(doubleValue.getType()).isEqualTo(AnyValue.Type.DOUBLE);
+ }
+
+ @Test
+ public void arrayAnyValue() {
+ String testString = "b729c730-4378-455b-8387-0f47250c3549";
+ int testInt = 42;
+ List testList = new ArrayList<>();
+ testList.add(AnyValue.stringAnyValue(testString));
+ testList.add(AnyValue.intAnyValue(testInt));
+ AnyValue arrayValue = AnyValue.arrayAnyValue(testList);
+ assertThat(arrayValue.getArrayValue()).isEqualTo(testList);
+ assertThat(arrayValue.getType()).isEqualTo(AnyValue.Type.ARRAY);
+ }
+
+ @Test
+ public void kvlistAnyValue() {
+ String testKey = "b193ffe4-b657-4e8d-8110-b77839468389";
+ AnyValue testValue = AnyValue.intAnyValue(42);
+ Map testMap = new HashMap<>();
+ testMap.put(testKey, testValue);
+ AnyValue kvlistValue = AnyValue.kvlistAnyValue(testMap);
+ assertThat(kvlistValue.getKvlistValue()).isEqualTo(testMap);
+ assertThat(kvlistValue.getType()).isEqualTo(AnyValue.Type.KVLIST);
+ }
+}
diff --git a/api/src/test/java/io/opentelemetry/logs/DefaultLogRecordTest.java b/api/src/test/java/io/opentelemetry/logs/DefaultLogRecordTest.java
new file mode 100644
index 00000000000..1bec70d7337
--- /dev/null
+++ b/api/src/test/java/io/opentelemetry/logs/DefaultLogRecordTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opentelemetry.common.AnyValue;
+import io.opentelemetry.common.AttributeValue;
+import io.opentelemetry.logs.LogRecord.Severity;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class DefaultLogRecordTest {
+
+ @Test
+ public void testDefaultLogRecord() {
+ long testTimestamp = 123L;
+ byte[] testTraceId = {'a', 'b', 'c'};
+ byte[] testSpanId = {'z', 'y', 'x'};
+ int testFlags = 1;
+ Severity testSeverity = Severity.ERROR2;
+ String testSeverityText = "severityText";
+ String testName = "an event name";
+ AnyValue testBody = AnyValue.stringAnyValue("A test body");
+ Map testAttributes = new HashMap<>();
+ testAttributes.put("one", AttributeValue.stringAttributeValue("test1"));
+ testAttributes.put("two", AttributeValue.longAttributeValue(42L));
+
+ LogRecord.Builder builder =
+ new DefaultLogRecord.Builder()
+ .withUnixTimeNano(testTimestamp)
+ .withTraceId(testTraceId)
+ .withSpanId(testSpanId)
+ .withFlags(testFlags)
+ .withSeverity(testSeverity)
+ .withSeverityText(testSeverityText)
+ .withName(testName)
+ .withAttributes(testAttributes)
+ .withBody(testBody);
+ LogRecord record = builder.build();
+
+ assertThat(record.getTimeUnixNano()).isEqualTo(testTimestamp);
+ assertThat(record.getTraceId()).isEqualTo(testTraceId);
+ assertThat(record.getSpanId()).isEqualTo(testSpanId);
+ assertThat(record.getFlags()).isEqualTo(testFlags);
+ assertThat(record.getSeverity()).isEqualTo(testSeverity);
+ assertThat(record.getSeverityText()).isEqualTo(testSeverityText);
+ assertThat(record.getName()).isEqualTo(testName);
+ assertThat(record.getAttributes()).isEqualTo(testAttributes);
+ assertThat(record.getBody()).isEqualTo(testBody);
+
+ LogRecord secondRecord = builder.withUnixTimeNano(456L).build();
+ assertThat(secondRecord.getTimeUnixNano()).isEqualTo(456L);
+ assertThat(record.getTimeUnixNano()).isNotEqualTo(456L);
+ }
+}
diff --git a/api/src/test/java/io/opentelemetry/logs/LogSinkTest.java b/api/src/test/java/io/opentelemetry/logs/LogSinkTest.java
new file mode 100644
index 00000000000..c3a0407b984
--- /dev/null
+++ b/api/src/test/java/io/opentelemetry/logs/LogSinkTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020, OpenTelemetry 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 io.opentelemetry.logs;
+
+import io.opentelemetry.OpenTelemetry;
+import io.opentelemetry.common.AnyValue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class LogSinkTest {
+
+ @Test
+ public void testNoOpLogSink() {
+ LogSink sink = OpenTelemetry.getLogSink("tests", "0.1");
+ LogRecord record =
+ new DefaultLogRecord.Builder()
+ .withUnixTimeNano(System.nanoTime())
+ .withBody(AnyValue.stringAnyValue("test value"))
+ .build();
+ sink.offer(record);
+ }
+}