From e5f2d4cf77b8a4fa85bfdc2848dc0314c21c842d Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Mon, 30 Sep 2024 10:38:37 -0500 Subject: [PATCH 1/5] Add api usage snippets --- doc-snippets/api/build.gradle.kts | 12 +++ .../src/main/java/otel/AsyncCounterUsage.java | 50 ++++++++++ .../src/main/java/otel/AsyncGaugeUsage.java | 49 ++++++++++ .../java/otel/AsyncUpDownCounterUsage.java | 49 ++++++++++ .../src/main/java/otel/AttributesUsage.java | 79 ++++++++++++++++ .../api/src/main/java/otel/BaggageUsage.java | 74 +++++++++++++++ .../api/src/main/java/otel/ContextUsage.java | 91 +++++++++++++++++++ .../api/src/main/java/otel/CounterUsage.java | 42 +++++++++ .../main/java/otel/ExtractContextUsage.java | 82 +++++++++++++++++ .../api/src/main/java/otel/GaugeUsage.java | 43 +++++++++ .../java/otel/GlobalOpenTelemetryUsage.java | 16 ++++ .../src/main/java/otel/HistogramUsage.java | 43 +++++++++ .../main/java/otel/InjectContextUsage.java | 46 ++++++++++ .../src/main/java/otel/LogRecordUsage.java | 63 +++++++++++++ .../api/src/main/java/otel/NoopUsage.java | 73 +++++++++++++++ .../main/java/otel/OpenTelemetryUsage.java | 20 ++++ .../main/java/otel/ProvidersAndScopes.java | 51 +++++++++++ .../java/otel/SemanticAttributesUsage.java | 29 ++++++ .../main/java/otel/SpanAndContextUsage.java | 53 +++++++++++ .../api/src/main/java/otel/SpanUsage.java | 86 ++++++++++++++++++ .../main/java/otel/UpDownCounterUsage.java | 42 +++++++++ doc-snippets/api/src/main/java/otel/Util.java | 35 +++++++ settings.gradle.kts | 1 + 23 files changed, 1129 insertions(+) create mode 100644 doc-snippets/api/build.gradle.kts create mode 100644 doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/AttributesUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/BaggageUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/ContextUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/CounterUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/ExtractContextUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/GaugeUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/GlobalOpenTelemetryUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/HistogramUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/InjectContextUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/LogRecordUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/NoopUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/OpenTelemetryUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/ProvidersAndScopes.java create mode 100644 doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/SpanAndContextUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/SpanUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java create mode 100644 doc-snippets/api/src/main/java/otel/Util.java diff --git a/doc-snippets/api/build.gradle.kts b/doc-snippets/api/build.gradle.kts new file mode 100644 index 0000000000..65f15bf7c0 --- /dev/null +++ b/doc-snippets/api/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("java") +} + +val moduleName by extra { "io.opentelemetry.examples.docs.configuration" } + +dependencies { + implementation("io.opentelemetry:opentelemetry-api") + + implementation("io.opentelemetry.semconv:opentelemetry-semconv") + implementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating:1.26.0-alpha") +} diff --git a/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java b/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java new file mode 100644 index 0000000000..d59b8c898d --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java @@ -0,0 +1,50 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_SHAPE; +import static otel.Util.computeWidgetColor; +import static otel.Util.computeWidgetShape; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableLongCounter; +import java.util.concurrent.atomic.AtomicLong; + +public class AsyncCounterUsage { + // Pre-allocate attributes whenever possible + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void asyncCounterUsage(Meter meter) { + AtomicLong widgetCount = new AtomicLong(); + + // Construct an async counter to observe an existing counter in a callback + ObservableLongCounter asyncCounter = + meter + .counterBuilder("fully.qualified.counter") + .setDescription("A count of produced widgets") + .setUnit("{widget}") + // optionally change the type to double + // .ofDoubles() + // the callback is invoked a MetricReader reads metrics + .buildWithCallback( + observableMeasurement -> { + long currentWidgetCount = widgetCount.get(); + + // Record a measurement with no attributes. + // Attributes defaults to Attributes.empty(). + observableMeasurement.record(currentWidgetCount); + + // Record a measurement with attributes, using pre-allocated attributes whenever + // possible. + observableMeasurement.record(currentWidgetCount, WIDGET_RED_CIRCLE); + // Sometimes, attributes must be computed using application context. + observableMeasurement.record( + currentWidgetCount, + Attributes.of( + WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor())); + }); + + // Optionally close the counter to unregister the callback when required + asyncCounter.close(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java b/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java new file mode 100644 index 0000000000..8965d0ca3b --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java @@ -0,0 +1,49 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_SHAPE; +import static otel.Util.computeWidgetColor; +import static otel.Util.computeWidgetShape; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import java.util.concurrent.atomic.AtomicReference; + +public class AsyncGaugeUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void asyncGaugeUsage(Meter meter) { + AtomicReference processingLineTemp = new AtomicReference<>(273.0); + + // Construct an async counter to observe an existing counter in a callback + ObservableDoubleGauge asyncGauge = + meter + .gaugeBuilder("fully.qualified.gauge") + .setDescription("The current temperature of the widget processing line") + .setUnit("K") + // optionally change the type to long + // .ofLongs() + // the callback is invoked a MetricReader reads metrics + .buildWithCallback( + observableMeasurement -> { + double currentWidgetCount = processingLineTemp.get(); + + // Record a measurement with no attributes. + // Attributes defaults to Attributes.empty(). + observableMeasurement.record(currentWidgetCount); + + // Record a measurement with attributes, using pre-allocated attributes whenever + // possible. + observableMeasurement.record(currentWidgetCount, WIDGET_RED_CIRCLE); + // Sometimes, attributes must be computed using application context. + observableMeasurement.record( + currentWidgetCount, + Attributes.of( + WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor())); + }); + + // Optionally close the gauge to unregister the callback when required + asyncGauge.close(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java b/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java new file mode 100644 index 0000000000..3d2c8f247b --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java @@ -0,0 +1,49 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_SHAPE; +import static otel.Util.computeWidgetColor; +import static otel.Util.computeWidgetShape; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import java.util.concurrent.atomic.AtomicLong; + +public class AsyncUpDownCounterUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void asyncUpDownCounterUsage(Meter meter) { + AtomicLong queueLength = new AtomicLong(); + + // Construct an async updowncounter to observe an existing up down counter in a callback + ObservableLongUpDownCounter asyncUpDownCounter = + meter + .upDownCounterBuilder("fully.qualified.updowncounter") + .setDescription("Current length of widget processing queue") + .setUnit("{widget}") + // optionally change the type to double + // .ofDoubles() + // the callback is invoked a MetricReader reads metrics + .buildWithCallback( + observableMeasurement -> { + long currentWidgetCount = queueLength.get(); + + // Record a measurement with no attributes. + // Attributes defaults to Attributes.empty(). + observableMeasurement.record(currentWidgetCount); + + // Record a measurement with attributes, using pre-allocated attributes whenever + // possible. + observableMeasurement.record(currentWidgetCount, WIDGET_RED_CIRCLE); + // Sometimes, attributes must be computed using application context. + observableMeasurement.record( + currentWidgetCount, + Attributes.of( + WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor())); + }); + + // Optionally close the counter to unregister the callback when required + asyncUpDownCounter.close(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/AttributesUsage.java b/doc-snippets/api/src/main/java/otel/AttributesUsage.java new file mode 100644 index 0000000000..defec03c2b --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/AttributesUsage.java @@ -0,0 +1,79 @@ +package otel; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; +import java.util.Map; + +public class AttributesUsage { + // Establish static constant for attribute keys and reuse to avoid allocations + private static final AttributeKey SHOP_ID = AttributeKey.stringKey("com.acme.shop.id"); + private static final AttributeKey SHOP_NAME = + AttributeKey.stringKey("com.acme.shop.name"); + private static final AttributeKey CUSTOMER_ID = + AttributeKey.longKey("com.acme.customer.id"); + private static final AttributeKey CUSTOMER_NAME = + AttributeKey.stringKey("com.acme.customer.name"); + + public static void attributesUsage() { + // Use a varargs initializer and pre-allocated attribute keys. This is the most efficient way to + // create attributes. + Attributes attributes = + Attributes.of( + SHOP_ID, + "abc123", + SHOP_NAME, + "opentelemetry-demo", + CUSTOMER_ID, + 123L, + CUSTOMER_NAME, + "Jack"); + + // ...or use a builder. + attributes = + Attributes.builder() + .put(SHOP_ID, "abc123") + .put(SHOP_NAME, "opentelemetry-demo") + .put(CUSTOMER_ID, 123) + .put(CUSTOMER_NAME, "Jack") + // optionally initialize attribute keys on the fly + .put(AttributeKey.stringKey("com.acme.string-key"), "value") + .put(AttributeKey.booleanKey("com.acme.bool-key"), true) + .put(AttributeKey.longKey("com.acme.long-key"), 1L) + .put(AttributeKey.doubleKey("com.acme.double-key"), 1.1) + .put(AttributeKey.stringArrayKey("com.acme.string-array-key"), "value1", "value2") + .put(AttributeKey.booleanArrayKey("come.acme.bool-array-key"), true, false) + .put(AttributeKey.longArrayKey("come.acme.long-array-key"), 1L, 2L) + .put(AttributeKey.doubleArrayKey("come.acme.double-array-key"), 1.1, 2.2) + // optionally omit initializing AttributeKey + .put("com.acme.string-key", "value") + .put("com.acme.bool-key", true) + .put("come.acme.long-key", 1L) + .put("come.acme.double-key", 1.1) + .put("come.acme.string-array-key", "value1", "value2") + .put("come.acme.bool-array-key", true, false) + .put("come.acme.long-array-key", 1L, 2L) + .put("come.acme.double-array-key", 1.1, 2.2) + .build(); + + // Attributes has a variety of methods for manipulating and reading data. + // Read an attribute key: + String shopIdValue = attributes.get(SHOP_ID); + // Inspect size: + int size = attributes.size(); + boolean isEmpty = attributes.isEmpty(); + // Convert to a map representation: + Map, Object> map = attributes.asMap(); + // Iterate through entries, printing each to the template: (): \n + attributes.forEach( + (attributeKey, value) -> + System.out.printf( + "%s (%s): %s%n", attributeKey.getKey(), attributeKey.getType(), value)); + // Convert to a builder, remove the com.acme.customer.id and any entry whose key starts with + // com.acme.shop, and build a new instance: + AttributesBuilder builder = attributes.toBuilder(); + builder.remove(CUSTOMER_ID); + builder.removeIf(attributeKey -> attributeKey.getKey().startsWith("com.acme.shop")); + Attributes trimmedAttributes = builder.build(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/BaggageUsage.java b/doc-snippets/api/src/main/java/otel/BaggageUsage.java new file mode 100644 index 0000000000..9aa5271adf --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/BaggageUsage.java @@ -0,0 +1,74 @@ +package otel; + +import static io.opentelemetry.context.Context.current; + +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.baggage.BaggageEntry; +import io.opentelemetry.api.baggage.BaggageEntryMetadata; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.context.Scope; +import java.util.Map; +import java.util.stream.Collectors; + +public class BaggageUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void baggageUsage() { + // Access current baggage with Baggage.current() + // output => context baggage: {} + Baggage currentBaggage = Baggage.current(); + System.out.println("current baggage: " + asString(currentBaggage)); + // ...or from a Context + currentBaggage = Baggage.fromContext(current()); + + // Baggage has a variety of methods for manipulating and reading data. + // Convert to builder and add entries: + Baggage newBaggage = + Baggage.current().toBuilder() + .put("shopId", "abc123") + .put("shopName", "opentelemetry-demo", BaggageEntryMetadata.create("metadata")) + .build(); + // ...or start from empty + // newBaggage = Baggage.empty().toBuilder().put("shopId", "abc123").build(); + // output => new baggage: {shopId=abc123(), shopName=opentelemetry-demo(metadata)} + System.out.println("new baggage: " + asString(newBaggage)); + // Read an entry: + String shopIdValue = newBaggage.getEntryValue("shopId"); + // Inspect size: + int size = newBaggage.size(); + boolean isEmpty = newBaggage.isEmpty(); + // Convert to map representation: + Map map = newBaggage.asMap(); + // Iterate through entries: + newBaggage.forEach((s, baggageEntry) -> {}); + + // The current baggage still doesn't contain the new entries + // output => context baggage: {} + System.out.println("current baggage: " + asString(Baggage.current())); + + // Calling Baggage.makeCurrent() sets Baggage.current() to the baggage until the scope is + // closed, upon which Baggage.current() is restored to the state prior to when + // Baggage.makeCurrent() was called. + try (Scope scope = newBaggage.makeCurrent()) { + // The current baggage now contains the added value + // output => context baggage: {shopId=abc123(), shopName=opentelemetry-demo(metadata)} + System.out.println("current baggage: " + asString(Baggage.current())); + } + + // The current baggage no longer contains the new entries: + // output => context baggage: {} + System.out.println("current baggage: " + asString(Baggage.current())); + } + + private static String asString(Baggage baggage) { + return baggage.asMap().entrySet().stream() + .map( + entry -> + String.format( + "%s=%s(%s)", + entry.getKey(), + entry.getValue().getValue(), + entry.getValue().getMetadata().getValue())) + .collect(Collectors.joining(", ", "{", "}")); + } +} diff --git a/doc-snippets/api/src/main/java/otel/ContextUsage.java b/doc-snippets/api/src/main/java/otel/ContextUsage.java new file mode 100644 index 0000000000..524c697f67 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/ContextUsage.java @@ -0,0 +1,91 @@ +package otel; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.context.Scope; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class ContextUsage { + public static void contextUsage() throws Exception { + // Define an example context key + ContextKey exampleContextKey = ContextKey.named("example-context-key"); + + // Context doesn't contain the key until we add it + // Context.current() accesses the current context + // output => current context value: null + System.out.println("current context value: " + Context.current().get(exampleContextKey)); + + // Add entry to context + Context context = Context.current().with(exampleContextKey, "value"); + + // The local context var contains the added value + // output => context value: value + System.out.println("context value: " + context.get(exampleContextKey)); + // The current context still doesn't contain the value + // output => current context value: null + System.out.println("current context value: " + Context.current().get(exampleContextKey)); + + // Calling context.makeCurrent() sets Context.current() to the context until the scope is + // closed, upon which Context.current() is restored to the state prior to when + // context.makeCurrent() was called. The resulting Scope implements AutoCloseable and is + // normally used in a try-with-resources block. Failure to call Scope.close() is an error and + // may cause memory leaks or other issues. + try (Scope scope = context.makeCurrent()) { + // The current context now contains the added value + // output => context value: value + System.out.println("context value: " + context.get(exampleContextKey)); + } + + // The local context var still contains the added value + // output => context value: value + System.out.println("context value: " + context.get(exampleContextKey)); + // The current context no longer contains the value + // output => current context value: null + System.out.println("current context value: " + Context.current().get(exampleContextKey)); + + ExecutorService executorService = Executors.newSingleThreadExecutor(); + ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); + + // Context instances can be explicitly passed around application code, but its more convenient + // to use implicit context, calling Context.makeCurrent() and accessing via Context.current(). + // Context provides a number of utilities for implicit context propagation. These utilities wrap + // utility classes like Scheduler, and ExecutorService, ScheduledExecutorService, Runnable, + // Callable, Consumer, Supplier, Function, etc and modify their behavior to call + // Context.makeCurrent() before running. + context.wrap(ContextUsage::callable).call(); + context.wrap(ContextUsage::runnable).run(); + context.wrap(executorService).submit(ContextUsage::runnable); + context.wrap(scheduledExecutorService).schedule(ContextUsage::runnable, 1, TimeUnit.SECONDS); + context.wrapConsumer(ContextUsage::consumer).accept(new Object()); + context.wrapConsumer(ContextUsage::biConsumer).accept(new Object(), new Object()); + context.wrapFunction(ContextUsage::function).apply(new Object()); + context.wrapSupplier(ContextUsage::supplier).get(); + } + + /** Example {@link java.util.concurrent.Callable}. */ + private static Object callable() { + return new Object(); + } + + /** Example {@link Runnable}. */ + private static void runnable() {} + + /** Example {@link java.util.function.Consumer}. */ + private static void consumer(Object object) {} + + /** Example {@link java.util.function.BiConsumer}. */ + private static void biConsumer(Object object1, Object object2) {} + + /** Example {@link java.util.function.Function}. */ + private static Object function(Object object) { + return object; + } + + /** Example {@link java.util.function.Supplier}. */ + private static Object supplier() { + return new Object(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/CounterUsage.java b/doc-snippets/api/src/main/java/otel/CounterUsage.java new file mode 100644 index 0000000000..f0a4e7c6e4 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/CounterUsage.java @@ -0,0 +1,42 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_SHAPE; +import static otel.Util.computeWidgetColor; +import static otel.Util.computeWidgetShape; +import static otel.Util.customContext; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.Meter; + +public class CounterUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void counterUsage(Meter meter) { + // Construct a counter to record measurements that are always positive (monotonically + // increasing). + LongCounter counter = + meter + .counterBuilder("fully.qualified.counter") + .setDescription("A count of produced widgets") + .setUnit("{widget}") + // optionally change the type to double + // .ofDoubles() + .build(); + + // Record a measurement with no attributes or context. + // Attributes defaults to Attributes.empty(), context to Context.current(). + counter.add(1L); + + // Record a measurement with attributes, using pre-allocated attributes whenever possible. + counter.add(1L, WIDGET_RED_CIRCLE); + // Sometimes, attributes must be computed using application context. + counter.add( + 1L, Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor())); + + // Record a measurement with attributes, and context. + // Most users will opt to omit the context argument, preferring the default Context.current(). + counter.add(1L, WIDGET_RED_CIRCLE, customContext()); + } +} diff --git a/doc-snippets/api/src/main/java/otel/ExtractContextUsage.java b/doc-snippets/api/src/main/java/otel/ExtractContextUsage.java new file mode 100644 index 0000000000..9f09ce21a9 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/ExtractContextUsage.java @@ -0,0 +1,82 @@ +package otel; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.List; + +public class ExtractContextUsage { + private static final TextMapGetter TEXT_MAP_GETTER = new HttpRequestGetter(); + + public static void extractContextUsage() throws Exception { + // Create a ContextPropagators instance which propagates w3c trace context and w3c baggage + ContextPropagators propagators = + ContextPropagators.create( + TextMapPropagator.composite( + W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance())); + + // Create a server, which uses the propagators to extract context from requests + HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); + server.createContext("/path", new Handler(propagators)); + server.setExecutor(null); + server.start(); + } + + private static class Handler implements HttpHandler { + private final ContextPropagators contextPropagators; + + private Handler(ContextPropagators contextPropagators) { + this.contextPropagators = contextPropagators; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + // Extract the context from the request and make the context current + Context extractedContext = + contextPropagators + .getTextMapPropagator() + .extract(Context.current(), exchange, TEXT_MAP_GETTER); + try (Scope scope = extractedContext.makeCurrent()) { + // Do work with the extracted context + } finally { + String response = "success"; + exchange.sendResponseHeaders(200, response.length()); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes(StandardCharsets.UTF_8)); + os.close(); + } + } + } + + /** {@link TextMapSetter} with a {@link HttpExchange} carrier. */ + private static class HttpRequestGetter implements TextMapGetter { + @Override + public Iterable keys(HttpExchange carrier) { + return carrier.getRequestHeaders().keySet(); + } + + @Override + public String get(HttpExchange carrier, String key) { + if (carrier == null) { + return null; + } + List headers = carrier.getRequestHeaders().get(key); + if (headers == null || headers.isEmpty()) { + return null; + } + return headers.get(0); + } + } +} diff --git a/doc-snippets/api/src/main/java/otel/GaugeUsage.java b/doc-snippets/api/src/main/java/otel/GaugeUsage.java new file mode 100644 index 0000000000..4b7f3af75f --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/GaugeUsage.java @@ -0,0 +1,43 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_SHAPE; +import static otel.Util.computeWidgetColor; +import static otel.Util.computeWidgetShape; +import static otel.Util.customContext; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleGauge; +import io.opentelemetry.api.metrics.Meter; + +public class GaugeUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void gaugeUsage(Meter meter) { + // Construct a gauge to record measurements as they occur when cannot be spatially + // re-aggregated. + DoubleGauge gauge = + meter + .gaugeBuilder("fully.qualified.gauge") + .setDescription("The current temperature of the widget processing line") + .setUnit("K") + // optionally change the type to long + // .ofLongs() + .build(); + + // Record a measurement with no attributes or context. + // Attributes defaults to Attributes.empty(), context to Context.current(). + gauge.set(273.0); + + // Record a measurement with attributes, using pre-allocated attributes whenever possible. + gauge.set(273.0, WIDGET_RED_CIRCLE); + // Sometimes, attributes must be computed using application context. + gauge.set( + 273.0, + Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor())); + + // Record a measurement with attributes, and context. + // Most users will opt to omit the context argument, preferring the default Context.current(). + gauge.set(1L, WIDGET_RED_CIRCLE, customContext()); + } +} diff --git a/doc-snippets/api/src/main/java/otel/GlobalOpenTelemetryUsage.java b/doc-snippets/api/src/main/java/otel/GlobalOpenTelemetryUsage.java new file mode 100644 index 0000000000..b302a9bfe3 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/GlobalOpenTelemetryUsage.java @@ -0,0 +1,16 @@ +package otel; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; + +public class GlobalOpenTelemetryUsage { + + public static void openTelemetryUsage(OpenTelemetry openTelemetry) { + // Set the GlobalOpenTelemetry instance as early in the application lifecycle as possible + // Set must only be called once. Calling multiple times raises an exception. + GlobalOpenTelemetry.set(openTelemetry); + + // Get the GlobalOpenTelemetry instance. + openTelemetry = GlobalOpenTelemetry.get(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/HistogramUsage.java b/doc-snippets/api/src/main/java/otel/HistogramUsage.java new file mode 100644 index 0000000000..96c549bfc3 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/HistogramUsage.java @@ -0,0 +1,43 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_SHAPE; +import static otel.Util.computeWidgetColor; +import static otel.Util.computeWidgetShape; +import static otel.Util.customContext; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.Meter; + +public class HistogramUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void histogramUsage(Meter meter) { + // Construct a histogram to record measurements where the distribution is important. + DoubleHistogram histogram = + meter + .histogramBuilder("fully.qualified.histogram") + .setDescription("Length of time to process a widget") + .setUnit("s") + // optionally provide advice on useful default explicit bucket boundaries + // .setExplicitBucketBoundariesAdvice(Arrays.asList(1.0, 2.0, 3.0)) + // optionally change the type to long + // .ofLongs() + .build(); + + // Record a measurement with no attributes or context. + // Attributes defaults to Attributes.empty(), context to Context.current(). + histogram.record(1.1); + + // Record a measurement with attributes, using pre-allocated attributes whenever possible. + histogram.record(2.2, WIDGET_RED_CIRCLE); + // Sometimes, attributes must be computed using application context. + histogram.record( + 3.2, Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor())); + + // Record a measurement with attributes, and context. + // Most users will opt to omit the context argument, preferring the default Context.current(). + histogram.record(4.4, WIDGET_RED_CIRCLE, customContext()); + } +} diff --git a/doc-snippets/api/src/main/java/otel/InjectContextUsage.java b/doc-snippets/api/src/main/java/otel/InjectContextUsage.java new file mode 100644 index 0000000000..8fbad54719 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/InjectContextUsage.java @@ -0,0 +1,46 @@ +package otel; + +import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +public class InjectContextUsage { + private static final TextMapSetter TEXT_MAP_SETTER = new HttpRequestSetter(); + + public static void injectContextUsage() throws Exception { + // Create a ContextPropagators instance which propagates w3c trace context and w3c baggage + ContextPropagators propagators = + ContextPropagators.create( + TextMapPropagator.composite( + W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance())); + + // Create an HttpRequest builder + HttpClient httpClient = HttpClient.newBuilder().build(); + HttpRequest.Builder requestBuilder = + HttpRequest.newBuilder().uri(new URI("http://127.0.0.1:8080/resource")).GET(); + + // Given a ContextPropagators instance, inject the current context into the HTTP request carrier + propagators.getTextMapPropagator().inject(Context.current(), requestBuilder, TEXT_MAP_SETTER); + + // Send the request with the injected context + httpClient.send(requestBuilder.build(), HttpResponse.BodyHandlers.discarding()); + } + + /** {@link TextMapSetter} with a {@link HttpRequest.Builder} carrier. */ + private static class HttpRequestSetter implements TextMapSetter { + @Override + public void set(HttpRequest.Builder carrier, String key, String value) { + if (carrier == null) { + return; + } + carrier.setHeader(key, value); + } + } +} diff --git a/doc-snippets/api/src/main/java/otel/LogRecordUsage.java b/doc-snippets/api/src/main/java/otel/LogRecordUsage.java new file mode 100644 index 0000000000..68932cc34a --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/LogRecordUsage.java @@ -0,0 +1,63 @@ +package otel; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.Value; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class LogRecordUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void logRecordUsage(Logger logger) { + logger + .logRecordBuilder() + // Set body. Note, setBody(..) is called multiple times for demonstration purposes but only + // the last call is used. + // Set the body to a string, syntactic sugar for setBody(Value.of("log message")) + .setBody("log message") + // Optionally set the body to a Value to record arbitrarily complex structured data + .setBody(Value.of("log message")) + .setBody(Value.of(1L)) + .setBody(Value.of(1.1)) + .setBody(Value.of(true)) + .setBody(Value.of(new byte[] {'a', 'b', 'c'})) + .setBody(Value.of(Value.of("entry1"), Value.of("entry2"))) + .setBody( + Value.of( + Map.of( + "stringKey", + Value.of("entry1"), + "mapKey", + Value.of(Map.of("stringKey", Value.of("entry2")))))) + // Set severity + .setSeverity(Severity.DEBUG) + .setSeverityText("debug") + // Set timestamp + .setTimestamp(System.currentTimeMillis(), TimeUnit.MILLISECONDS) + // Optionally set the timestamp when the log was observed + .setObservedTimestamp(System.currentTimeMillis(), TimeUnit.MILLISECONDS) + // Set attributes + .setAttribute(AttributeKey.stringKey("com.acme.string-key"), "value") + .setAttribute(AttributeKey.booleanKey("com.acme.bool-key"), true) + .setAttribute(AttributeKey.longKey("com.acme.long-key"), 1L) + .setAttribute(AttributeKey.doubleKey("com.acme.double-key"), 1.1) + .setAttribute( + AttributeKey.stringArrayKey("com.acme.string-array-key"), + Arrays.asList("value1", "value2")) + .setAttribute( + AttributeKey.booleanArrayKey("come.acme.bool-array-key"), Arrays.asList(true, false)) + .setAttribute(AttributeKey.longArrayKey("come.acme.long-array-key"), Arrays.asList(1L, 2L)) + .setAttribute( + AttributeKey.doubleArrayKey("come.acme.double-array-key"), Arrays.asList(1.1, 2.2)) + .setAllAttributes(WIDGET_RED_CIRCLE) + // Optionally explicitly set the context used to correlate with spans. If omitted, + // Context.current() is used. + // .setContext(context) + // Emit the log record + .emit(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/NoopUsage.java b/doc-snippets/api/src/main/java/otel/NoopUsage.java new file mode 100644 index 0000000000..608ba5d52f --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/NoopUsage.java @@ -0,0 +1,73 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_RED_CIRCLE; +import static otel.Util.WIDGET_SHAPE; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.metrics.DoubleGauge; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; + +public class NoopUsage { + private static final String SCOPE_NAME = "fully.qualified.name"; + + public static void noopUsage() { + // Access the noop OpenTelemetry instance + OpenTelemetry noopOpenTelemetry = OpenTelemetry.noop(); + + // Noop tracing + Tracer noopTracer = OpenTelemetry.noop().getTracer(SCOPE_NAME); + noopTracer + .spanBuilder("span name") + .startSpan() + .setAttribute(WIDGET_SHAPE, "square") + .setStatus(StatusCode.OK) + .addEvent("event-name", Attributes.builder().put(WIDGET_COLOR, "red").build()) + .end(); + + // Noop metrics + Attributes attributes = WIDGET_RED_CIRCLE; + Meter noopMeter = OpenTelemetry.noop().getMeter(SCOPE_NAME); + DoubleHistogram histogram = noopMeter.histogramBuilder("fully.qualified.histogram").build(); + histogram.record(1.0, attributes); + // counter + LongCounter counter = noopMeter.counterBuilder("fully.qualified.counter").build(); + counter.add(1, attributes); + // async counter + noopMeter + .counterBuilder("fully.qualified.counter") + .buildWithCallback(observable -> observable.record(10, attributes)); + // updowncounter + LongUpDownCounter upDownCounter = + noopMeter.upDownCounterBuilder("fully.qualified.updowncounter").build(); + // async updowncounter + noopMeter + .upDownCounterBuilder("fully.qualified.updowncounter") + .buildWithCallback(observable -> observable.record(10, attributes)); + upDownCounter.add(-1, attributes); + // gauge + DoubleGauge gauge = noopMeter.gaugeBuilder("fully.qualified.gauge").build(); + gauge.set(1.1, attributes); + // async gauge + noopMeter + .gaugeBuilder("fully.qualified.gauge") + .buildWithCallback(observable -> observable.record(10, attributes)); + + // Noop logs + Logger noopLogger = OpenTelemetry.noop().getLogsBridge().get(SCOPE_NAME); + noopLogger + .logRecordBuilder() + .setBody("log message") + .setAttribute(WIDGET_SHAPE, "square") + .setSeverity(Severity.INFO) + .emit(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/OpenTelemetryUsage.java b/doc-snippets/api/src/main/java/otel/OpenTelemetryUsage.java new file mode 100644 index 0000000000..c82e5494c7 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/OpenTelemetryUsage.java @@ -0,0 +1,20 @@ +package otel; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.context.propagation.ContextPropagators; + +public class OpenTelemetryUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void openTelemetryUsage(OpenTelemetry openTelemetry) { + // Access TracerProvider, MeterProvider, LoggerProvider, ContextPropagators + TracerProvider tracerProvider = openTelemetry.getTracerProvider(); + MeterProvider meterProvider = openTelemetry.getMeterProvider(); + LoggerProvider loggerProvider = openTelemetry.getLogsBridge(); + ContextPropagators propagators = openTelemetry.getPropagators(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/ProvidersAndScopes.java b/doc-snippets/api/src/main/java/otel/ProvidersAndScopes.java new file mode 100644 index 0000000000..9d8d491b2a --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/ProvidersAndScopes.java @@ -0,0 +1,51 @@ +package otel; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.logs.Logger; +import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterProvider; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.TracerProvider; + +public class ProvidersAndScopes { + + private static final String SCOPE_NAME = "fully.qualified.name"; + private static final String SCOPE_VERSION = "1.0.0"; + private static final String SCOPE_SCHEMA_URL = "https://example"; + + public static void providersUsage(OpenTelemetry openTelemetry) { + // Access providers from an OpenTelemetry instance + TracerProvider tracerProvider = openTelemetry.getTracerProvider(); + MeterProvider meterProvider = openTelemetry.getMeterProvider(); + // NOTE: LoggerProvider is a special case and should only be used to bridge logs from other + // logging APIs / frameworks into OpenTelemetry. + LoggerProvider loggerProvider = openTelemetry.getLogsBridge(); + + // Access tracer, meter, logger from providers to record telemetry for a particular scope + Tracer tracer = + tracerProvider + .tracerBuilder(SCOPE_NAME) + .setInstrumentationVersion(SCOPE_VERSION) + .setSchemaUrl(SCOPE_SCHEMA_URL) + .build(); + Meter meter = + meterProvider + .meterBuilder(SCOPE_NAME) + .setInstrumentationVersion(SCOPE_VERSION) + .setSchemaUrl(SCOPE_SCHEMA_URL) + .build(); + Logger logger = + loggerProvider + .loggerBuilder(SCOPE_NAME) + .setInstrumentationVersion(SCOPE_VERSION) + .setSchemaUrl(SCOPE_SCHEMA_URL) + .build(); + + // ...optionally, shorthand versions are available if scope version and schemaUrl aren't + // available + tracer = tracerProvider.get(SCOPE_NAME); + meter = meterProvider.get(SCOPE_NAME); + logger = loggerProvider.get(SCOPE_NAME); + } +} diff --git a/doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java b/doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java new file mode 100644 index 0000000000..664f63420f --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java @@ -0,0 +1,29 @@ +package otel; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.semconv.HttpAttributes; +import io.opentelemetry.semconv.ServerAttributes; +import io.opentelemetry.semconv.incubating.HttpIncubatingAttributes; + +public class SemanticAttributesUsage { + public static void semanticAttributesUsage() { + // Semantic attributes are organized by top level domain and whether they are stable or + // incubating. + // For example: + // - stable attributes starting with http.* are in the HttpAttributes class. + // - stable attributes starting with server.* are in the ServerAttributes class. + // - incubating attributes starting with http.* are in the HttpIncubatingAttributes class. + // Attribute keys which define an enumeration of values are accessible in an inner + // {AttributeKey}Values class. + // For example, the enumeration of http.request.method values is available in the + // HttpAttributes.HttpRequestMethodValues class. + Attributes attributes = + Attributes.builder() + .put(HttpAttributes.HTTP_REQUEST_METHOD, HttpAttributes.HttpRequestMethodValues.GET) + .put(HttpAttributes.HTTP_ROUTE, "/users/:id") + .put(ServerAttributes.SERVER_ADDRESS, "example") + .put(ServerAttributes.SERVER_PORT, 8080L) + .put(HttpIncubatingAttributes.HTTP_RESPONSE_BODY_SIZE, 1024) + .build(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/SpanAndContextUsage.java b/doc-snippets/api/src/main/java/otel/SpanAndContextUsage.java new file mode 100644 index 0000000000..df418aa4be --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/SpanAndContextUsage.java @@ -0,0 +1,53 @@ +package otel; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; + +public class SpanAndContextUsage { + private final Tracer tracer; + + SpanAndContextUsage(Tracer tracer) { + this.tracer = tracer; + } + + public void nestedSpanUsage() { + // Start a span. Since we don't call makeCurrent(), we must explicitly call setParent on + // children. Wrap code in try / finally to ensure we end the span. + Span span = tracer.spanBuilder("span").startSpan(); + try { + // Start a child span, explicitly setting the parent. + Span childSpan = + tracer + .spanBuilder("span child") + // Explicitly set parent. + .setParent(span.storeInContext(Context.current())) + .startSpan(); + // Call makeCurrent(), adding childSpan to Context.current(). Spans created inside the scope + // will have their parent set to childSpan. + try (Scope childSpanScope = childSpan.makeCurrent()) { + // Call another method which creates a span. The span's parent will be childSpan since it is + // started in the childSpan scope. + doWork(); + } finally { + childSpan.end(); + } + } finally { + span.end(); + } + } + + private int doWork() { + Span doWorkSpan = tracer.spanBuilder("doWork").startSpan(); + try (Scope scope = doWorkSpan.makeCurrent()) { + int result = 0; + for (int i = 0; i < 10; i++) { + result += i; + } + return result; + } finally { + doWorkSpan.end(); + } + } +} diff --git a/doc-snippets/api/src/main/java/otel/SpanUsage.java b/doc-snippets/api/src/main/java/otel/SpanUsage.java new file mode 100644 index 0000000000..282ee95ab4 --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/SpanUsage.java @@ -0,0 +1,86 @@ +package otel; + +import static io.opentelemetry.context.Context.current; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import java.util.Arrays; + +public class SpanUsage { + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void spanUsage(Tracer tracer) { + // Get a span builder by providing the span name + Span span = + tracer + .spanBuilder("span name") + // Set span kind + .setSpanKind(SpanKind.INTERNAL) + // Set attributes + .setAttribute(AttributeKey.stringKey("com.acme.string-key"), "value") + .setAttribute(AttributeKey.booleanKey("com.acme.bool-key"), true) + .setAttribute(AttributeKey.longKey("com.acme.long-key"), 1L) + .setAttribute(AttributeKey.doubleKey("com.acme.double-key"), 1.1) + .setAttribute( + AttributeKey.stringArrayKey("com.acme.string-array-key"), + Arrays.asList("value1", "value2")) + .setAttribute( + AttributeKey.booleanArrayKey("come.acme.bool-array-key"), + Arrays.asList(true, false)) + .setAttribute( + AttributeKey.longArrayKey("come.acme.long-array-key"), Arrays.asList(1L, 2L)) + .setAttribute( + AttributeKey.doubleArrayKey("come.acme.double-array-key"), Arrays.asList(1.1, 2.2)) + // optionally omit initializing AttributeKey + .setAttribute("com.acme.string-key", "value") + .setAttribute("com.acme.bool-key", true) + .setAttribute("come.acme.long-key", 1L) + .setAttribute("come.acme.double-key", 1.1) + .setAllAttributes(WIDGET_RED_CIRCLE) + // Optionally explicitly set the parent span context. If omitted, the span's parent will + // be set using Context.current() + // .setParent(parentContext) + // Optionally add links + // .addLink(linkContext, linkAttributes) + // Start the span + .startSpan(); + + // Check if span is recording before computing additional data + if (span.isRecording()) { + // Update the span name with information not avilable when starting + span.updateName("new span name"); + + // Add additional attributes not available when starting + span.setAttribute("com.acme.string-key2", "value"); + + // Add additional span links not available when starting + span.addLink(exampleLinkContext()); + // optionally include attributes on the link + span.addLink(exampleLinkContext(), WIDGET_RED_CIRCLE); + + // Add span events + span.addEvent("my-event"); + // optionally include attributes on the event + span.addEvent("my-event", WIDGET_RED_CIRCLE); + + // Record exception, syntactic sugar for a span event with a specific shape + span.recordException(new RuntimeException("error")); + + // Set the span status + span.setStatus(StatusCode.OK, "status description"); + } + + // Finally, end the span + span.end(); + } + + /** Return a dummy link context. */ + private static SpanContext exampleLinkContext() { + return Span.fromContext(current()).getSpanContext(); + } +} diff --git a/doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java b/doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java new file mode 100644 index 0000000000..46e3d9a7fd --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java @@ -0,0 +1,42 @@ +package otel; + +import static otel.Util.WIDGET_COLOR; +import static otel.Util.WIDGET_SHAPE; +import static otel.Util.computeWidgetColor; +import static otel.Util.computeWidgetShape; +import static otel.Util.customContext; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.Meter; + +public class UpDownCounterUsage { + + private static final Attributes WIDGET_RED_CIRCLE = Util.WIDGET_RED_CIRCLE; + + public static void usage(Meter meter) { + // Construct an updowncounter to record measurements that go up and down. + LongUpDownCounter upDownCounter = + meter + .upDownCounterBuilder("fully.qualified.updowncounter") + .setDescription("Current length of widget processing queue") + .setUnit("{widget}") + // optionally change the type to double + // .ofDoubles() + .build(); + + // Record a measurement with no attributes or context. + // Attributes defaults to Attributes.empty(), context to Context.current(). + upDownCounter.add(1L); + + // Record a measurement with attributes, using pre-allocated attributes whenever possible. + upDownCounter.add(-1L, WIDGET_RED_CIRCLE); + // Sometimes, attributes must be computed using application context. + upDownCounter.add( + -1L, Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor())); + + // Record a measurement with attributes, and context. + // Most users will opt to omit the context argument, preferring the default Context.current(). + upDownCounter.add(1L, WIDGET_RED_CIRCLE, customContext()); + } +} diff --git a/doc-snippets/api/src/main/java/otel/Util.java b/doc-snippets/api/src/main/java/otel/Util.java new file mode 100644 index 0000000000..2b70e21adf --- /dev/null +++ b/doc-snippets/api/src/main/java/otel/Util.java @@ -0,0 +1,35 @@ +package otel; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import java.util.Random; + +class Util { + + private static final ContextKey SHAPE_CONTEXT_KEY = ContextKey.named("shape"); + private static final Random random = new Random(); + + static final AttributeKey WIDGET_SHAPE = AttributeKey.stringKey("com.acme.widget.shape"); + static final AttributeKey WIDGET_COLOR = AttributeKey.stringKey("com.acme.widget.color"); + + static final Attributes WIDGET_RED_SQUARE = + Attributes.of(WIDGET_SHAPE, "square", WIDGET_COLOR, "red"); + static final Attributes WIDGET_RED_CIRCLE = + Attributes.of(WIDGET_SHAPE, "circle", WIDGET_COLOR, "circle"); + + static String computeWidgetShape() { + return random.nextBoolean() ? "circle" : "square"; + } + + static String computeWidgetColor() { + return random.nextBoolean() ? "red" : "blue"; + } + + static Context customContext() { + return Context.current().with(SHAPE_CONTEXT_KEY, computeWidgetShape()); + } + + private Util() {} +} diff --git a/settings.gradle.kts b/settings.gradle.kts index ead4a2fff5..fa1274b7fb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -63,6 +63,7 @@ include( ":opentelemetry-examples-kotlin-extension", ":opentelemetry-examples-grpc", ":opentelemetry-examples-resource-detection-gcp", + ":doc-snippets:api", ":doc-snippets:configuration", ":doc-snippets:getting-started", ":doc-snippets:exporters", From 22cc954712bc864c7ae4edb4111dc487f153c841 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Tue, 1 Oct 2024 15:13:42 -0500 Subject: [PATCH 2/5] PR feedback --- doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java | 2 +- doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java | 2 +- .../api/src/main/java/otel/AsyncUpDownCounterUsage.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java b/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java index d59b8c898d..d88598d523 100644 --- a/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java +++ b/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java @@ -25,8 +25,8 @@ public static void asyncCounterUsage(Meter meter) { .setUnit("{widget}") // optionally change the type to double // .ofDoubles() - // the callback is invoked a MetricReader reads metrics .buildWithCallback( + // the callback is invoked a MetricReader reads metrics observableMeasurement -> { long currentWidgetCount = widgetCount.get(); diff --git a/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java b/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java index 8965d0ca3b..c9a5818349 100644 --- a/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java +++ b/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java @@ -24,8 +24,8 @@ public static void asyncGaugeUsage(Meter meter) { .setUnit("K") // optionally change the type to long // .ofLongs() - // the callback is invoked a MetricReader reads metrics .buildWithCallback( + // the callback is invoked a MetricReader reads metrics observableMeasurement -> { double currentWidgetCount = processingLineTemp.get(); diff --git a/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java b/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java index 3d2c8f247b..0a11faf208 100644 --- a/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java +++ b/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java @@ -24,8 +24,8 @@ public static void asyncUpDownCounterUsage(Meter meter) { .setUnit("{widget}") // optionally change the type to double // .ofDoubles() - // the callback is invoked a MetricReader reads metrics .buildWithCallback( + // the callback is invoked a MetricReader reads metrics observableMeasurement -> { long currentWidgetCount = queueLength.get(); From 88d98e6883f2d41529df9221c399b1b261d5c044 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Tue, 1 Oct 2024 16:53:42 -0500 Subject: [PATCH 3/5] PR feedback --- doc-snippets/api/src/main/java/otel/SpanUsage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc-snippets/api/src/main/java/otel/SpanUsage.java b/doc-snippets/api/src/main/java/otel/SpanUsage.java index 282ee95ab4..a251e8c41d 100644 --- a/doc-snippets/api/src/main/java/otel/SpanUsage.java +++ b/doc-snippets/api/src/main/java/otel/SpanUsage.java @@ -52,7 +52,7 @@ public static void spanUsage(Tracer tracer) { // Check if span is recording before computing additional data if (span.isRecording()) { - // Update the span name with information not avilable when starting + // Update the span name with information not available when starting span.updateName("new span name"); // Add additional attributes not available when starting From 6ec03dd862db97e12932d1a8bec278f041717534 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Wed, 2 Oct 2024 09:04:50 -0500 Subject: [PATCH 4/5] PR feedback --- doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java | 2 +- doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java | 2 +- .../api/src/main/java/otel/AsyncUpDownCounterUsage.java | 2 +- doc-snippets/api/src/main/java/otel/AttributesUsage.java | 4 ++-- doc-snippets/api/src/main/java/otel/BaggageUsage.java | 2 +- doc-snippets/api/src/main/java/otel/GaugeUsage.java | 2 +- doc-snippets/api/src/main/java/otel/HistogramUsage.java | 4 ++-- doc-snippets/api/src/main/java/otel/LogRecordUsage.java | 3 ++- .../api/src/main/java/otel/SemanticAttributesUsage.java | 2 +- doc-snippets/api/src/main/java/otel/SpanUsage.java | 7 ++++--- .../api/src/main/java/otel/UpDownCounterUsage.java | 2 +- 11 files changed, 17 insertions(+), 15 deletions(-) diff --git a/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java b/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java index d88598d523..a6123ea0e6 100644 --- a/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java +++ b/doc-snippets/api/src/main/java/otel/AsyncCounterUsage.java @@ -23,7 +23,7 @@ public static void asyncCounterUsage(Meter meter) { .counterBuilder("fully.qualified.counter") .setDescription("A count of produced widgets") .setUnit("{widget}") - // optionally change the type to double + // Uncomment to optionally change the type to double // .ofDoubles() .buildWithCallback( // the callback is invoked a MetricReader reads metrics diff --git a/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java b/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java index c9a5818349..4f8f071a1e 100644 --- a/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java +++ b/doc-snippets/api/src/main/java/otel/AsyncGaugeUsage.java @@ -22,7 +22,7 @@ public static void asyncGaugeUsage(Meter meter) { .gaugeBuilder("fully.qualified.gauge") .setDescription("The current temperature of the widget processing line") .setUnit("K") - // optionally change the type to long + // Uncomment to optionally change the type to long // .ofLongs() .buildWithCallback( // the callback is invoked a MetricReader reads metrics diff --git a/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java b/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java index 0a11faf208..f6b4a57b54 100644 --- a/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java +++ b/doc-snippets/api/src/main/java/otel/AsyncUpDownCounterUsage.java @@ -22,7 +22,7 @@ public static void asyncUpDownCounterUsage(Meter meter) { .upDownCounterBuilder("fully.qualified.updowncounter") .setDescription("Current length of widget processing queue") .setUnit("{widget}") - // optionally change the type to double + // Uncomment to optionally change the type to double // .ofDoubles() .buildWithCallback( // the callback is invoked a MetricReader reads metrics diff --git a/doc-snippets/api/src/main/java/otel/AttributesUsage.java b/doc-snippets/api/src/main/java/otel/AttributesUsage.java index defec03c2b..ba2beb7301 100644 --- a/doc-snippets/api/src/main/java/otel/AttributesUsage.java +++ b/doc-snippets/api/src/main/java/otel/AttributesUsage.java @@ -36,7 +36,7 @@ public static void attributesUsage() { .put(SHOP_NAME, "opentelemetry-demo") .put(CUSTOMER_ID, 123) .put(CUSTOMER_NAME, "Jack") - // optionally initialize attribute keys on the fly + // Optionally initialize attribute keys on the fly .put(AttributeKey.stringKey("com.acme.string-key"), "value") .put(AttributeKey.booleanKey("com.acme.bool-key"), true) .put(AttributeKey.longKey("com.acme.long-key"), 1L) @@ -45,7 +45,7 @@ public static void attributesUsage() { .put(AttributeKey.booleanArrayKey("come.acme.bool-array-key"), true, false) .put(AttributeKey.longArrayKey("come.acme.long-array-key"), 1L, 2L) .put(AttributeKey.doubleArrayKey("come.acme.double-array-key"), 1.1, 2.2) - // optionally omit initializing AttributeKey + // Optionally omit initializing AttributeKey .put("com.acme.string-key", "value") .put("com.acme.bool-key", true) .put("come.acme.long-key", 1L) diff --git a/doc-snippets/api/src/main/java/otel/BaggageUsage.java b/doc-snippets/api/src/main/java/otel/BaggageUsage.java index 9aa5271adf..bcd20406bb 100644 --- a/doc-snippets/api/src/main/java/otel/BaggageUsage.java +++ b/doc-snippets/api/src/main/java/otel/BaggageUsage.java @@ -28,7 +28,7 @@ public static void baggageUsage() { .put("shopId", "abc123") .put("shopName", "opentelemetry-demo", BaggageEntryMetadata.create("metadata")) .build(); - // ...or start from empty + // ...or uncomment to start from empty // newBaggage = Baggage.empty().toBuilder().put("shopId", "abc123").build(); // output => new baggage: {shopId=abc123(), shopName=opentelemetry-demo(metadata)} System.out.println("new baggage: " + asString(newBaggage)); diff --git a/doc-snippets/api/src/main/java/otel/GaugeUsage.java b/doc-snippets/api/src/main/java/otel/GaugeUsage.java index 4b7f3af75f..137209fbf5 100644 --- a/doc-snippets/api/src/main/java/otel/GaugeUsage.java +++ b/doc-snippets/api/src/main/java/otel/GaugeUsage.java @@ -21,7 +21,7 @@ public static void gaugeUsage(Meter meter) { .gaugeBuilder("fully.qualified.gauge") .setDescription("The current temperature of the widget processing line") .setUnit("K") - // optionally change the type to long + // Uncomment to optionally change the type to long // .ofLongs() .build(); diff --git a/doc-snippets/api/src/main/java/otel/HistogramUsage.java b/doc-snippets/api/src/main/java/otel/HistogramUsage.java index 96c549bfc3..aa4d68f87e 100644 --- a/doc-snippets/api/src/main/java/otel/HistogramUsage.java +++ b/doc-snippets/api/src/main/java/otel/HistogramUsage.java @@ -20,9 +20,9 @@ public static void histogramUsage(Meter meter) { .histogramBuilder("fully.qualified.histogram") .setDescription("Length of time to process a widget") .setUnit("s") - // optionally provide advice on useful default explicit bucket boundaries + // Uncomment to optionally provide advice on useful default explicit bucket boundaries // .setExplicitBucketBoundariesAdvice(Arrays.asList(1.0, 2.0, 3.0)) - // optionally change the type to long + // Uncomment to optionally change the type to long // .ofLongs() .build(); diff --git a/doc-snippets/api/src/main/java/otel/LogRecordUsage.java b/doc-snippets/api/src/main/java/otel/LogRecordUsage.java index 68932cc34a..50ecdafa75 100644 --- a/doc-snippets/api/src/main/java/otel/LogRecordUsage.java +++ b/doc-snippets/api/src/main/java/otel/LogRecordUsage.java @@ -54,7 +54,8 @@ public static void logRecordUsage(Logger logger) { .setAttribute( AttributeKey.doubleArrayKey("come.acme.double-array-key"), Arrays.asList(1.1, 2.2)) .setAllAttributes(WIDGET_RED_CIRCLE) - // Optionally explicitly set the context used to correlate with spans. If omitted, + // Uncomment to optionally explicitly set the context used to correlate with spans. If + // omitted, // Context.current() is used. // .setContext(context) // Emit the log record diff --git a/doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java b/doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java index 664f63420f..f68b5e6ff2 100644 --- a/doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java +++ b/doc-snippets/api/src/main/java/otel/SemanticAttributesUsage.java @@ -7,7 +7,7 @@ public class SemanticAttributesUsage { public static void semanticAttributesUsage() { - // Semantic attributes are organized by top level domain and whether they are stable or + // Semantic attributes are organized by top-level domain and whether they are stable or // incubating. // For example: // - stable attributes starting with http.* are in the HttpAttributes class. diff --git a/doc-snippets/api/src/main/java/otel/SpanUsage.java b/doc-snippets/api/src/main/java/otel/SpanUsage.java index a251e8c41d..0a6af29b9c 100644 --- a/doc-snippets/api/src/main/java/otel/SpanUsage.java +++ b/doc-snippets/api/src/main/java/otel/SpanUsage.java @@ -36,16 +36,17 @@ public static void spanUsage(Tracer tracer) { AttributeKey.longArrayKey("come.acme.long-array-key"), Arrays.asList(1L, 2L)) .setAttribute( AttributeKey.doubleArrayKey("come.acme.double-array-key"), Arrays.asList(1.1, 2.2)) - // optionally omit initializing AttributeKey + // Optionally omit initializing AttributeKey .setAttribute("com.acme.string-key", "value") .setAttribute("com.acme.bool-key", true) .setAttribute("come.acme.long-key", 1L) .setAttribute("come.acme.double-key", 1.1) .setAllAttributes(WIDGET_RED_CIRCLE) - // Optionally explicitly set the parent span context. If omitted, the span's parent will + // Uncomment to optionally explicitly set the parent span context. If omitted, the + // span's parent will // be set using Context.current() // .setParent(parentContext) - // Optionally add links + // Uncomment to optionally add links // .addLink(linkContext, linkAttributes) // Start the span .startSpan(); diff --git a/doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java b/doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java index 46e3d9a7fd..9675e05c9d 100644 --- a/doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java +++ b/doc-snippets/api/src/main/java/otel/UpDownCounterUsage.java @@ -21,7 +21,7 @@ public static void usage(Meter meter) { .upDownCounterBuilder("fully.qualified.updowncounter") .setDescription("Current length of widget processing queue") .setUnit("{widget}") - // optionally change the type to double + // Uncomment to optionally change the type to double // .ofDoubles() .build(); From 3cab9776b52bf8a169be724944be38ff4820e96b Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Wed, 2 Oct 2024 13:30:43 -0500 Subject: [PATCH 5/5] PR feedback --- doc-snippets/api/src/main/java/otel/LogRecordUsage.java | 3 +-- doc-snippets/api/src/main/java/otel/SpanUsage.java | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/doc-snippets/api/src/main/java/otel/LogRecordUsage.java b/doc-snippets/api/src/main/java/otel/LogRecordUsage.java index 50ecdafa75..90cbda5fd7 100644 --- a/doc-snippets/api/src/main/java/otel/LogRecordUsage.java +++ b/doc-snippets/api/src/main/java/otel/LogRecordUsage.java @@ -55,8 +55,7 @@ public static void logRecordUsage(Logger logger) { AttributeKey.doubleArrayKey("come.acme.double-array-key"), Arrays.asList(1.1, 2.2)) .setAllAttributes(WIDGET_RED_CIRCLE) // Uncomment to optionally explicitly set the context used to correlate with spans. If - // omitted, - // Context.current() is used. + // omitted, Context.current() is used. // .setContext(context) // Emit the log record .emit(); diff --git a/doc-snippets/api/src/main/java/otel/SpanUsage.java b/doc-snippets/api/src/main/java/otel/SpanUsage.java index 0a6af29b9c..00c691d421 100644 --- a/doc-snippets/api/src/main/java/otel/SpanUsage.java +++ b/doc-snippets/api/src/main/java/otel/SpanUsage.java @@ -43,10 +43,9 @@ public static void spanUsage(Tracer tracer) { .setAttribute("come.acme.double-key", 1.1) .setAllAttributes(WIDGET_RED_CIRCLE) // Uncomment to optionally explicitly set the parent span context. If omitted, the - // span's parent will - // be set using Context.current() + // span's parent will be set using Context.current(). // .setParent(parentContext) - // Uncomment to optionally add links + // Uncomment to optionally add links. // .addLink(linkContext, linkAttributes) // Start the span .startSpan();