Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional API incubator docs #6356

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
45 changes: 41 additions & 4 deletions api/incubator/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
# ExtendedTracer
# API Incubator

Utility methods to make it easier to use the OpenTelemetry tracer.
Experimental APIs, including Event API, extended Log Bridge APIs, extended Metrics APIs, extended ContextPropagator APIs, and extended Trace APIs.

## Event API

Features:

* Event API for producing log records according to [Event Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/general/events/).

See [EventApiUsageTest](./src/test/java/io/opentelemetry/api/incubator/events/EventApiUsageTest.java).

## Extended Log Bridge API

Features:

* Set AnyValue log record body with arbitrarily complex data

See [ExtendedLogsBridgeApiUsageTest](./src/test/java/io/opentelemetry/api/incubator/logs/ExtendedLogsBridgeApiUsageTest.java).

## Extended Metrics APIs

## Usage Examples
Features:

* Synchronous gauge instrument
* Attributes advice

See [ExtendedMetricsApiUsageTest](./src/test/java/io/opentelemetry/api/incubator/metrics/ExtendedMetricsApiUsageTest.java).

## Extended ContextPropagator APIs

Features:

* Simplified injection / extraction of context

See [ExtendedContextPropagatorsUsageTest](./src/test/java/io/opentelemetry/api/incubator/propagation/ExtendedContextPropagatorsUsageTest.java).

## ExtendedTracer

Utility methods to make it easier to use the OpenTelemetry tracer.

Here are some examples how the utility methods can help reduce boilerplate code.

TODO: translate examples to test to ensure no java compilation issues.

### Tracing a function

Before:
Expand Down Expand Up @@ -145,7 +182,7 @@ String transactionId = extendedTracer.spanBuilder("checkout_cart")
.startAndCall(() -> processCheckout(cartId));
```

## Exception handling
### Exception handling

`ExtendedTracer` re-throws exceptions without modification. This means you can
catch exceptions around `ExtendedTracer` calls and handle them as you would without `ExtendedTracer`.
Expand Down
5 changes: 4 additions & 1 deletion api/incubator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ dependencies {

annotationProcessor("com.google.auto.value:auto-value")

testImplementation("io.opentelemetry.semconv:opentelemetry-semconv:1.21.0-alpha")
testImplementation(project(":sdk:testing"))

testImplementation("io.opentelemetry.semconv:opentelemetry-semconv")

testImplementation("com.google.guava:guava")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.events;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;

import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.logs.AnyValue;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.logs.internal.AnyValueBody;
import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;

/** Demonstrating usage of Event API. */
class EventApiUsageTest {

@Test
void eventApiUsage() {
// Setup SdkEventLoggerProvider, which delegates to SdkLoggerProvider
InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create();
SdkLoggerProvider loggerProvider =
SdkLoggerProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// Simple processor w/ in-memory exporter used for demonstration purposes
.addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter))
.build();
EventLoggerProvider eventLoggerProvider = SdkEventLoggerProvider.create(loggerProvider);

// Get an EventLogger for a scope
EventLogger eventLogger = eventLoggerProvider.get("org.foo.my-scope");

// Emit an event
eventLogger
.builder("org.foo.my-event")
// Add fields to the payload. The API has helpers for adding field values which are
// primitives or arrays of primitives, but you can also add a field with type AnyValue,
// allowing for arbitrarily complex payloads.
.put("key1", "value1")
.put(
"key2",
AnyValue.of(
ImmutableMap.of(
"childKey1", AnyValue.of("value2"), "childKey2", AnyValue.of("value3"))))
// Optionally set other fields, including timestamp, severity, context, and attributes
// (attributes provide additional details about the event which are not part of the well
// defined payload)
.emit();

// Events manifest as log records with an event.name attribute, and with the payload fields in
// the AnyValue log record body
loggerProvider.forceFlush().join(10, TimeUnit.SECONDS);
assertThat(exporter.getFinishedLogRecordItems())
.satisfiesExactly(
logData -> {
assertThat(logData)
.hasAttributes(
Attributes.builder().put("event.name", "org.foo.my-event").build());
assertThat(((AnyValueBody) logData.getBody()).asAnyValue())
.isEqualTo(
AnyValue.of(
ImmutableMap.of(
"key1",
AnyValue.of("value1"),
"key2",
AnyValue.of(
ImmutableMap.of(
"childKey1",
AnyValue.of("value2"),
"childKey2",
AnyValue.of("value3"))))));
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.logs;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;

import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.logs.internal.AnyValueBody;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;

/** Demonstrating usage of extended Logs Bridge API. */
class ExtendedLogsBridgeApiUsageTest {

@Test
void extendedLogRecordBuilderUsage() {
// Setup SdkLoggerProvider
InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create();
SdkLoggerProvider loggerProvider =
SdkLoggerProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// Simple processor w/ in-memory exporter used for demonstration purposes
.addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter))
.build();

// Get a Logger for a scope
Logger logger = loggerProvider.get("org.foo.my-scope");

// Cast to ExtendedLogRecordBuilder, and emit a log
((ExtendedLogRecordBuilder) logger.logRecordBuilder())
// ...can set AnyValue log record body, allowing for arbitrarily complex data
.setBody(
AnyValue.of(
ImmutableMap.of(
"key1",
AnyValue.of("value1"),
"key2",
AnyValue.of(
ImmutableMap.of(
"childKey1",
AnyValue.of("value2"),
"childKey2",
AnyValue.of("value3"))))))
.emit();

// SDK can access AnyValue body by casting to AnyValueBody
loggerProvider.forceFlush().join(10, TimeUnit.SECONDS);
assertThat(exporter.getFinishedLogRecordItems())
.satisfiesExactly(
logData ->
assertThat(((AnyValueBody) logData.getBody()).asAnyValue())
.isEqualTo(
AnyValue.of(
ImmutableMap.of(
"key1",
AnyValue.of("value1"),
"key2",
AnyValue.of(
ImmutableMap.of(
"childKey1",
AnyValue.of("value2"),
"childKey2",
AnyValue.of("value3")))))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.metrics;

import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.sdk.metrics.InstrumentSelector;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.View;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import org.junit.jupiter.api.Test;

/** Demonstrating usage of extended Metrics API. */
class ExtendedMetricsApiUsageTest {

@Test
void synchronousGaugeUsage() {
// Setup SdkMeterProvider
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProvider meterProvider =
SdkMeterProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// In-memory reader used for demonstration purposes
.registerMetricReader(reader)
.build();

// Get a Meter for a scope
Meter meter = meterProvider.get("org.foo.my-scope");

// Cast GaugeBuilder to ExtendedDoubleGaugeBuilder
DoubleGauge gauge = ((ExtendedDoubleGaugeBuilder) meter.gaugeBuilder("my-gauge")).build();

// Call set synchronously to set the value
gauge.set(1.0, Attributes.builder().put("key", "value1").build());
gauge.set(2.0, Attributes.builder().put("key", "value2").build());

assertThat(reader.collectAllMetrics())
.satisfiesExactly(
metricData ->
assertThat(metricData)
.hasName("my-gauge")
.hasDoubleGaugeSatisfying(
gaugeAssert ->
gaugeAssert.hasPointsSatisfying(
point ->
point
.hasValue(1.0)
.hasAttributes(
Attributes.builder().put("key", "value1").build()),
point ->
point
.hasValue(2.0)
.hasAttributes(
Attributes.builder().put("key", "value2").build()))));
}

@Test
void attributesAdvice() {
// Setup SdkMeterProvider
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProvider meterProvider =
SdkMeterProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// In-memory reader used for demonstration purposes
.registerMetricReader(reader)
// Register a view which indicates that for counter1, attributes key1, key2 should be
// retained
.registerView(
InstrumentSelector.builder().setName("counter1").build(),
View.builder().setAttributeFilter(ImmutableSet.of("key1", "key2")).build())
.build();

// Get a Meter for a scope
Meter meter = meterProvider.get("org.foo.my-scope");

// To apply attribute advice, cast the instrument builder to appropriate
// Extended{Instrument}Builder, and call setAttributeAdvice
// Here we create counter1 and counter2, both configured to only retain attribute key1. counter1
// has a view configured which overrides this and retains key1, key2.
LongCounter counter1 =
((ExtendedLongCounterBuilder) meter.counterBuilder("counter1"))
.setAttributesAdvice(ImmutableList.of(AttributeKey.stringKey("key1")))
.build();
LongCounter counter2 =
((ExtendedLongCounterBuilder) meter.counterBuilder("counter2"))
.setAttributesAdvice(ImmutableList.of(AttributeKey.stringKey("key1")))
.build();

// Record data with attribute key1, key2
counter1.add(1, Attributes.builder().put("key1", "value1").put("key2", "value2").build());
counter2.add(1, Attributes.builder().put("key1", "value1").put("key2", "value2").build());

// Verify that counter1 has both key1, key2 since view overrides the attribute advice
// Verify that counter2 only has key1, since attribute advice causes key2 to be dropped by
// default
assertThat(reader.collectAllMetrics())
.satisfiesExactlyInAnyOrder(
metricData ->
assertThat(metricData)
.hasName("counter1")
.hasLongSumSatisfying(
sumAssert ->
sumAssert.hasPointsSatisfying(
point ->
point
.hasValue(1L)
.hasAttributes(
Attributes.builder()
.put("key1", "value1")
.put("key2", "value2")
.build()))),
metricData ->
assertThat(metricData)
.hasName("counter2")
.hasLongSumSatisfying(
sumAssert ->
sumAssert.hasPointsSatisfying(
point ->
point
.hasValue(1L)
.hasAttributes(
Attributes.builder().put("key1", "value1").build()))));
}
}
Loading
Loading