Elastic Distributions of OpenTelemetry (EDOT) SDKs cover multiple languages:
This section provides guidance and examples for applications instrumentation in a Kubernetes environment for all supported languages.
In Kubernetes environments with the OpenTelemetry Operator, automatic (or zero-code) instrumentation simplifies the process by injecting and configuring instrumentation libraries into the targeted Pods.
On the other hand, manual instrumentation with OpenTelemetry allows you to customize trace spans, metrics, and logging directly in your application’s code. This approach provides more granular control over what and how data is captured.
The following table illustrates the different languages supported by OpenTelemetry (OTel) and the Elastic Stack, the type of SDK/API used for instrumentation (either zero-code or source code dependencies), and the corresponding deployment types (on-premises, ESS, or serverless) for each language.
Language | OTel SDK/API Type | Deployment Model Support |
---|---|---|
Java | EDOT Java - zero-code instrumentation | All deployment types |
Node.js | EDOT Node.js - zero-code instrumentation | All deployment types |
.NET | EDOT .NET - zero-code instrumentation | All deployment types |
PHP | EDOT PHP - source code dependencies | All deployment types |
Python | EDOT Python - zero-code instrumentation | All deployment types |
Swift | EDOT Swift - source code dependencies | ESS, on-premises |
Android | EDOT Android - source code dependencies | ESS, on-premises |
Javascript | Vanilla OTel RUM SDK/API - source code dependencies | ESS, on-premises |
Rust | Vanilla OTel Rust SDK/API - source code dependencies | All deployment types |
Ruby | Vanilla OTel Ruby SDK/API - source code dependencies | All deployment types |
Go | Vanilla OTel Go SDK/API - zero-code instrumentation | All deployment types |
C++ | Vanilla OTel C++ SDK/API - source code dependencies | All deployment types |
Before starting with application auto-instrumentation, ensure the following prerequisites are in place for proper setup:
kind: Instrumentation
object exists in the cluster.Zero-code instrumentation is handled by the operator through Instrumentation
objects, used to automatically inject the necessary SDKs and configuration into application workloads.
If you followed the getting started guide to install the operator, there should be an Instrumentation
object with name elastic-instrumentation
in namespace opentelemetry-operator-system
:
kubectl get instrumentation -A
NAMESPACE NAME AGE ENDPOINT SAMPLER SAMPLER ARG
opentelemetry-operator-system elastic-instrumentation 5d20h http://opentelemetry-kube-stack-daemon-collector.opentelemetry-operator-system.svc.cluster.local:4318 parentbased_traceidratio 1.0
The Instrumentation
object stores important parameters:
exporter:
endpoint: http://opentelemetry-kube-stack-daemon-collector.opentelemetry-operator-system.svc.cluster.local:4318
dotnet:
image: docker.elastic.co/observability/elastic-otel-dotnet:edge
java:
image: docker.elastic.co/observability/elastic-otel-javaagent:1.0.0
nodejs:
image: docker.elastic.co/observability/elastic-otel-node:0.4.1
python:
image: docker.elastic.co/observability/elastic-otel-python:0.4.1
To enable auto-instrumentation, add the corresponding language annotation to the Pods template (spec.template.metadata.annotations
) in your Deployment or relevant workload object (StatefulSet, Job, CronJob, etc.).
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
...
template:
metadata:
annotations:
instrumentation.opentelemetry.io/inject-<LANGUAGE>: "opentelemetry-operator-system/elastic-instrumentation"
...
spec:
containers:
- image: myapplication-image
name: app
...
where <LANGUAGE>
is one of: go
, java
, nodejs
, python
, dotnet
[!NOTE] Ensure you add the annotations at Pod level and not directly at the workload
spec
level (Deployment, Job, etc.). Ensure the annotation value points to an existingInstrumentation
object.
Alternatively, you can enable auto-instrumentation by adding the annotation at namespace level. This approach automatically applies instrumentation to all Pods within the specified namespace.
apiVersion: v1
kind: Namespace
metadata:
name: mynamespace
annotations:
instrumentation.opentelemetry.io/inject-<LANGUAGE>: "opentelemetry-operator-system/elastic-instrumentation"
After adding annotations to Pods or Namespaces, the applications must be restarted for the instrumentation injection to take effect:
kubectl rollout restart deployment/my-deployment
In case you have multiple Instrumentation objects with different settings or images, ensure you point your Pods to the desired Instrumentation
objects in the annotations.
The possible values for the annotation are detailed in the Operator documentation. For reference purposes, the values are:
"true"
: to inject Instrumentation instance with default
name from the current namespace."my-instrumentation"
: to inject Instrumentation instance with name "my-instrumentation"
in the current namespace."my-other-namespace/my-instrumentation"
: to inject Instrumentation instance with name "my-instrumentation"
from another namespace "my-other-namespace"
."false"
: do not inject.For details on instrumenting specific languages, refer to:
The following example creates a namespace with an annotation to instrument all Pods of the namespace with java
libraries.
kubectl create namespace java-apps
# Annotate app namespace
kubectl annotate namespace java-apps instrumentation.opentelemetry.io/inject-java="opentelemetry-operator-system/elastic-instrumentation"
# Run a java example application in the namespace
kubectl run otel-test -n java-apps --env OTEL_INSTRUMENTATION_METHODS_INCLUDE="test.Testing[methodB]" --image docker.elastic.co/demos/apm/k8s-webhook-test
After adding the annotation and restarting the Pods, run kubectl describe
on your application Pod to verify the SDK has been properly attached.
Ensure that the init container
, volume
, and environment variables
described in how auto-instrumentation works have been successfully injected into the Pod.
The OpenTelemetry Operator automates the process of instrumenting applications by injecting the necessary libraries and configuration into the application Pods. The process may vary slightly depending on the language, but it generally involves the following steps:
Creating a shared volume:
The operator declares an emptyDir
shared volume within the Pod, and mounts it the app container and a new init container. This volume serves as the medium for sharing the instrumentation library between the new init container and the application container.
Adding an init container:
The operator adds an init container into the Pod. This container is responsible for copying the OpenTelemetry instrumentation library to the shared volume.
Configuring the main container:
The operator injects environment variables into the main application container to configure OpenTelemetry settings (for example, OTEL_EXPORTER_OTLP_ENDPOINT
or OTEL_TRACES_SAMPLER
). Additionally, it links the instrumentation library to the application using mechanisms specific to the language runtime, such as:
javaagent
option using the JAVA_TOOL_OPTIONS environment variable.NODE_OPTIONS
environment variable.PYTHONPATH
environment variable to load the library sitecustomize module.You can apply OTEL specific configuration to your applications at two different levels:
Instrumentation
object level, for example configuring different settings per language.Use cases:
Consider also the creation of different Instrumentation
objects for different purposes, such as:
(TBD: add instructions and references about Instrumentation objects)
(TBD, in-progress)
The manual instrumentation… Configuration requirements (does every language has its own requirements)? Exporter destination? HTTP vs OTLP? does each EDOT SDK support different protocols?