OpenTelemetry Java

repository·main·Indexed 25 days ago

https://github.com/open-telemetry/opentelemetry-java

The official implementation of the OpenTelemetry API and SDK for the Java ecosystem. It enables developers to instrument applications for traces, metrics, and logs. The project includes core API and SDK artifacts, OTLP and Prometheus exporters, Kotlin coroutine extensions, and shims for interoperability with OpenCensus and OpenTracing.

Tokens
18K
Snippets
39
Records
87
Agent score
78%

What's inside OpenTelemetry Java

  1. Overview of OpenTelemetry Java

    main

    OpenTelemetry Java provides two primary components for observability:

    1. OpenTelemetry API: Used for recording telemetry (traces, metrics, and logs) within your application code.
    2. OpenTelemetry SDK: Used for managing and processing the telemetry recorded by the API (e.g., configuring exporters, samplers, and processors).

    For detailed guides, instrumentation ecosystem information (including the OpenTelemetry Java agent), and end-to-end code examples, refer to the official opentelemetry.io Java Documentation.

  2. Use the OpenTracing Shim to bridge OpenTelemetry to OpenTracing

    main

    The OpenTracing shim acts as a bridge layer that allows you to use an OpenTelemetry Tracer as an implementation of an OpenTracing Tracer.

    Warning: OpenTracing is deprecated, and this shim is also deprecated. It is provided only for legacy compatibility. For new projects, you should migrate to the OpenTelemetry API directly.

  3. What is the OpenTelemetry OpenCensus Shim?

    main

    The OpenTelemetry OpenCensus Shim enables interoperability between OpenTelemetry and OpenCensus. It allows applications using OpenTelemetry to work alongside libraries instrumented with OpenCensus by:

    1. Traces: Exporting trace spans from both OpenTelemetry and OpenCensus while maintaining correct parent-child relationships.
    2. Metrics: Exporting OpenCensus metrics through any configured OpenTelemetry metric exporter.
  4. Use Impl (Implementation) packages for stable internal APIs

    main

    When code needs to be public to be shared across different OpenTelemetry modules but is not intended for end-user application developers, use the *.impl.* sub-package.

    Key characteristics of *.impl.* packages:

    • They provide full backwards-compatibility guarantees.
    • They are included in japicmp compatibility checks.
    • Public classes in these packages must include the following Javadoc disclaimer:
    /**
     * This class is not intended for use by application developers. Its API is stable and will not
     * be changed or removed in a backwards-incompatible manner.
     */
  5. Understand OpenTelemetry Java configuration interfaces

    main

    OpenTelemetry Java provides three primary ways to configure its components. All configuration interfaces are generally bound by the OpenTelemetry specification, though Java-specific extensions exist for ecosystem needs (e.g., otel.java.metrics.cardinality.limit).

    1. Programmatic configuration: The base layer where you invoke Java APIs (like builders) to construct components. This is the most flexible and is the preferred way to propose new configuration options.
    2. Env var / system property configuration: A flat configuration method using environment variables or system properties. These are interpreted as equivalent calls to the programmatic interface.
    3. Declarative configuration: A structured, YAML-based configuration. This is the priority interface for current and future enhancements and is designed to be more expressive than environment variables. It must be a strict superset of the environment variable/system property configuration.
  6. Use Internal packages for unstable internal code

    main

    Use *.internal.* packages for code that must be public for technical reasons (e.g., accessing code across packages within the same module) but should not be part of the public API.

    Key characteristics of *.internal.* packages:

    • They are excluded from semver guarantees and Javadoc.
    • They must not be used across module boundaries.
    • Public classes in these packages must carry one of two standard disclaimers:

    Standard internal disclaimer:

    /**
     * This class is internal and is hence not for public use. Its APIs are unstable and can change
     * at any time.
     */

    Incubating internal disclaimer:

    /**
     * This class is internal and experimental. Its APIs are unstable and can change at any time.
     * Its APIs (or a version of them) may be promoted to the public stable API in the future, but
     * no guarantees are made.
     */
  7. Handling immature or experimental signals

    main

    Experimental or immature signals (such as early-stage metrics or logs) are identified by an -alpha suffix in their version numbers (e.g., 1.0.0-alpha).

    Characteristics of immature signals:

    • Risk: Depending on -alpha API modules is at your own risk.
    • Transitivity: API modules for immature signals are not transitive dependencies of the main opentelemetry-api module.
    • Package Names: To facilitate easy migration to mature signals, immature APIs use the same Java package structures as the final mature versions (e.g., io.opentelemetry.api.metrics.*). This allows you to transition from alpha to stable usage without changing your import statements.
  8. How telemetry within SDK components works

    main

    SDK components (like SpanExporter or remote samplers) may need to emit their own telemetry. Because the SDK must be fully built before it can be used, and components are often initialized during the build process, these components must accept OpenTelemetry lazily.

    If you are developing a component that requires OpenTelemetry, you should implement a mechanism to set it after the SDK is built, or use lazy injection if your framework supports it.

    interface OpenTelemetryComponent {
      default void setOpenTelemetry(OpenTelemetry openTelemetry) {}
    }
    
    interface SpanExporter extends OpenTelemetryComponent {
    }
    
    public class BatchExporter implements SpanExporter {
      private volatile Tracer tracer;
    
      @Override
      public void setOpenTelemetry(OpenTelemetry openTelemetry) {
            tracer = openTelemetry.getTracerProvider().get("spanexporter");
      }
    
      @Override
      public void export() {
            Tracer tracer = this.tracer;
            if (tracer != null) {
              tracer.spanBuilder("export").startSpan();
            }
      }
    }
  9. Why build multiple SDK instances

    main

    While a global SDK instance is common, there are several reasons to build and manage specific instances of the SDK:

    • Dependency Injection: Integrates naturally with frameworks like Spring.
    • Multi-tenancy/Multi-concern: Allows having multiple instances in the same application, useful in scenarios with multiple classloaders or different observability requirements.
    • Lifecycle Management: Enables managing the SDK lifecycle (starting and shutting down) in sync with other application components, such as in serverless runtimes.