Send application telemetry
Deploy the CloudWatch agent where your application runs on Amazon EC2, Amazon ECS, Amazon EKS, an Azure VM, or Azure Kubernetes Service, point the application's OpenTelemetry exporter at it, and confirm data arrives in your space. If the application is not instrumented yet, add OpenTelemetry for Python, Node.js, Java, or .NET in the last step. The agent receives OTLP from your application and forwards metrics, traces, and logs to CloudWatch.
If you are monitoring an AI agent, see Send AI agent telemetry. If your application runs on AWS Lambda, see Send application telemetry from AWS Lambda; Lambda has no collector to deploy.
Steps 1 and 2 show one environment at a time: expand the section for where your application runs in each step. Sections are collapsed so you can see the whole page; open only yours.
If your application already uses OpenTelemetry, do Steps 1 to 3 and stop; Step 4 is only for applications that are not instrumented yet. If you already run your own OpenTelemetry collector or the ADOT SDK, you need only an endpoint: see "Choose an endpoint" in Send telemetry to CloudWatch Omni.
Note
Assisted setup. The Add source wizard in the Omni web UI (Settings › Ingestion) generates these commands for your selections. From your coding agent, the Omni skills in the Agent Toolkit for AWS do the same; see Use Omni skills. The rest of this page is the manual path.
Prerequisites
-
Transaction Search is enabled in the account and Region that receive traces. Traces sent before it is enabled are not searchable. See Transaction Search.
-
You can attach IAM policies to the role your compute uses: an instance role, a task role, the agent's service account role, or the federated role on Azure.
-
For Step 4, your application's language is one of Python, Node.js, Java, or .NET. Frameworks with zero-code instrumentation are listed in Supported environments, languages, and frameworks; others still work with the custom-instrumentation snippets.
Step 1: Deploy the CloudWatch agent (platform team or administrator)
Expand the environment your application runs on. Each section gives the install commands and the same resources as infrastructure as code, as tabs.
On Amazon EC2, you attach a managed policy to the instance role, install the agent with the OTLP preset (per instance, or fleet-wide with a State Manager association), then continue to Step 2.
Grant permissions
The instance role needs CloudWatchAgentServerPolicy, which covers metrics, logs, and traces, bound to the instance through an instance profile.
Install the agent and enable OTLP
For the full agent configuration reference, see Amazon CloudWatch agent in the Amazon CloudWatch User Guide.
On Amazon ECS (Fargate), you attach a managed policy to the task role, add the agent as a second container in the same task with the application depending on it, then continue to Step 2. Your application sends OTLP to localhost; the task shares one network namespace.
Grant permissions and egress
The task role needs CloudWatchAgentServerPolicy; the execution role needs AmazonECSTaskExecutionRolePolicy. The task needs egress to pull the agent image and reach the OTLP endpoints: a public IP in a public subnet, a NAT gateway, or VPC endpoints.
Important
The application container must depend on the agent container with condition START. The agent image has no health check, so condition HEALTHY hangs startup.
Add the sidecar to the task definition
For the agent configuration reference, see Amazon CloudWatch agent.
On Amazon EKS, you install the CloudWatch Observability add-on with OTLP enabled and give its agent credentials, then continue to Step 2. The add-on runs the agent as a DaemonSet and exposes a cloudwatch-agent Service in the amazon-cloudwatch namespace; your pods send OTLP to that Service over cluster DNS, not to localhost. The add-on is installed with the AWS CLI regardless of how you deploy your application, so there is one installation path here.
curl -fsSL https://raw.githubusercontent.com/aws/amazon-cloudwatch-agent/main/scripts/aws/setup.sh | \ CWAGENT_PLATFORM=aws_eks \ CWAGENT_K8S_CLUSTER_NAME=<my-cluster> \ CWAGENT_AWS_REGION=<region> \ CWAGENT_AWS_ENABLE_TRANSACTION_SEARCH=true \ sh
-
The script installs the EKS Pod Identity Agent, creates the agent role with a Pod Identity association, and installs the add-on with OTLP enabled. Pod Identity supplies the agent's credentials, so you do not annotate the service account for IAM roles for service accounts (IRSA).
Three settings must be right or telemetry never arrives, with no error in the agent log:
-
The OTLP receiver must be bound to
0.0.0.0, not the defaultlocalhost, or it refuses cross-pod traffic. If application OTLP is refused after the script runs, apply the override and restart the DaemonSet:
aws eks update-addon --cluster-name <my-cluster> --addon-name amazon-cloudwatch-observability \ --configuration-values '{"agent":{"config":{"opentelemetry":{"collect":{"otlp":{"grpc_endpoint":"0.0.0.0:4317","http_endpoint":"0.0.0.0:4318"}}}}}}' kubectl -n amazon-cloudwatch rollout restart daemonset/cloudwatch-agent
-
The agent must have credentials through Pod Identity (the script) or an IRSA-annotated service account. The add-on's
--service-account-role-arnflag does not annotate the service account; without the annotation the agent falls back to the node role and its exports are rejected. -
Application logs are in the cluster-scoped log group
/aws/cwagent/<my-cluster>/otlp, not in/aws/cwagent/otlp.
For a manual install with IRSA and the add-on configuration reference, see Install the CloudWatch Observability add-on in the Amazon CloudWatch User Guide.
On an Azure VM, you federate the VM's managed identity to an IAM role and install the CloudWatch agent on the VM, then continue to Step 2. The agent sends telemetry with temporary AWS credentials instead of a stored key; your application sends OTLP to localhost.
The complete procedure, including the OpenID Connect (OIDC) federation, the IAM role trust policy, and the onboarding script, is in Install the CloudWatch agent on Azure in the Amazon CloudWatch User Guide. Follow the VM section there, then return to Step 2.
On Azure Kubernetes Service, you federate workload identity for the cloudwatch-agent service account to an IAM role and install the CloudWatch Observability Helm chart, then continue to Step 2. Pods send OTLP to the cloudwatch-agent Service over cluster DNS.
The complete procedure, including the OIDC federation and the Helm values, is in Install the CloudWatch agent on Azure in the Amazon CloudWatch User Guide. Follow the AKS section there, then return to Step 2.
Note
Unlike the EKS add-on, the AKS Helm chart's OTLP receiver accepts cross-pod traffic by default; no 0.0.0.0 override is needed.
Step 2: Point your application at the agent (developer)
Set these environment variables on the application, never on the agent: one agent serves many services and already enriches telemetry with infrastructure attributes. The endpoint and the place the variables live depend on your environment, and the port and protocol depend on your language; expand the same section you chose in Step 1, then choose your language.
On Amazon EC2, set these in the application's service unit or start script, for example Environment= lines in a systemd unit:
On Amazon ECS, set these in the application container's environment block:
On Amazon EKS, set these in the container's env block in the pod spec:
On an Azure VM, set these in the application's service unit or start script:
On Azure Kubernetes Service, set these in the container's env block in the pod spec:
Step 3: Verify
Use the automated verification in the Omni web UI. In your space, open Settings › Ingestion and choose your source: the Add source flow's verification step confirms the agent is reporting and your application's telemetry is arriving, and points at the failing setting when it is not. Run it after every deployment change.
If verification fails, see the "Troubleshoot missing telemetry" section of this page.
The agent is reporting
Your application appears
If you run your own OpenTelemetry Collector instead of the CloudWatch agent, this is your check. If your application is not instrumented yet, do Step 4 first, then return to this check. Restart the application, then open your space. In Application map, the service appears under the service.name you set. In Trace Explorer, a trace for a recent request shows the entry span for your service with the resource attributes you set. Application logs are in /aws/cwagent/otlp on EC2, ECS, and Azure VMs, or /aws/cwagent/<cluster>/otlp on EKS and AKS. A custom metric is queryable with PromQL by its OpenTelemetry name, for example sum({__name__="orders.placed"}).
Step 4 (optional): Instrument your application with OpenTelemetry (developer)
Choose your language. The choice applies to every code sample below and is remembered on the other pages in this guide. Most HTTP frameworks, database clients, and messaging libraries are traced with no code changes; the table in each tab lists the frameworks with documented notes, and the OpenTelemetry registry lists the rest. For a framework not covered, the custom-instrumentation snippet still works.
Install the OpenTelemetry SDK
Add the CloudWatch plugin for OpenTelemetry with the SDK. The plugin generates the request, error, and duration span metrics that power the service views (including the Errors metric), and it meters every span before sampling, so those metrics reflect all requests rather than the sampled subset. It is available for Python, Node.js, Java, and .NET. The following table lists the package names and links. The plugin needs a metrics pipeline to emit into. If your application exports traces but no metrics, the plugin stays inert and Errors remains unpopulated. Configure a metrics exporter alongside your trace exporter.
If you instrument with the ADOT SDK instead and sample, your span metrics are computed from the sampled spans only.
| Language | Package | Documentation |
|---|---|---|
| Java | software.amazon.opentelemetry:cloudwatch-plugin-otel |
Java plugin README |
| Python | cloudwatch-plugin-otel |
Python plugin README |
| Node.js | @aws/cloudwatch-plugin-otel |
Node.js plugin README |
| .NET | AWS.OpenTelemetry.CloudWatchPluginOtel |
.NET plugin README |
Enable auto-instrumentation
Run the application under the auto-instrumentation agent for its language. Framework, HTTP, and database calls are traced with no code changes.
Add custom instrumentation
Set business attributes on the active span, mark your entry span as a server span so it counts in request metrics, and record a custom metric. These snippets assume the SDK is initialized by the step above.
Query custom metrics with PromQL by their OpenTelemetry name, for example sum({__name__="orders.placed"}). Restart the application and repeat Step 3.
Troubleshoot missing telemetry
| Symptom | Cause and fix |
|---|---|
| Logs and spans arrive, metrics do not | A metric attribute value is longer than 1024 characters; the CloudWatch OTLP metrics endpoint rejects the whole batch with HTTP 400. Trim the attribute. For Java, the usual cause is process.command_args on a long classpath: set OTEL_JAVA_DISABLED_RESOURCE_PROVIDERS=io.opentelemetry.instrumentation.resources.ProcessResourceProvider. |
| Nothing arrives; the SDK logs connection errors | Port and protocol do not match: 4317 is gRPC and 4318 is HTTP. Set OTEL_EXPORTER_OTLP_PROTOCOL to match the port. |
| Nothing arrives on ECS; the task hangs at startup | The application container depends on the agent with condition HEALTHY. Use START. |
Nothing arrives on ECS; the agent logs AccessDenied or times out |
The task role lacks CloudWatchAgentServerPolicy, or the task has no egress to the OTLP endpoints. Attach the policy; add a public IP, NAT gateway, or VPC endpoints. |
| Nothing arrives on EKS; no agent errors | The receiver is bound to localhost, or the agent service account has no credentials. Apply the 0.0.0.0 override; use Pod Identity or annotate the service account for IRSA, then restart the DaemonSet. |
| Nothing arrives from a new EC2 instance | The agent started before the instance profile reached the instance metadata service. Wait for the role in user data before fetch-config, or restart the agent. |
| Nothing arrives from an Azure VM or AKS | The federation is not complete: the IAM role trust policy does not match the identity, or the service account is not annotated. Recheck the federation steps in Install the CloudWatch agent on Azure. |
| The service appears under the agent's name | OTEL_SERVICE_NAME is set on the agent, not the application. Move it to the application. |
| A Python service shows one process span and no route spans | opentelemetry-instrument wrapped python instead of the server. Wrap uvicorn or gunicorn. |
| Traces exist but are not searchable | Transaction Search was enabled after the traces were sent. Enable it, then send new traces. |
| Only some requests appear in the trace list | Transaction Search indexes 1 percent of spans by default for the trace list; the spans themselves are all stored. Raise the indexing percentage in Transaction Search settings. |