OpenTelemetry Ingestion Gateway for AppDynamics: Collector-Exporter
Overview
OpenTelemetry (OTel) is a collection of tools, APIs, and SDKs used to instrument, generate, collect, and export telemetry data (metrics, logs, and traces). This helps you analyze software performance and behavior. OpenTelemetry Ingestion Gateway for AppDynamics (OTIG) is an ingestion and translation mechanism based on the OpenTelemetry Collector. By utilizing the Collector processing engine and the custom AppDynamics Exporter, OTIG translates native OpenTelemetry protocols and semantics into AppDynamics Controller-specific data models. This approach ensures that OTIG:
-
presents OpenTelemetry data, such as traces and metrics, within the Controller UI.
-
maintains a consistent user experience with existing AppDynamics concepts, including Business Transactions (BTs) and Tiers.
Prerequisites
Ensure that your environment meets the following requirements before deploying the OTIG Collector-Exporter:
-
Controller Compatibility: AppDynamics On-Premises or SaaS Controller from version 26.4.0.
Note: 25.10.0 is the minimum version requirement. However, Node-level visibility and Metrics Browser are supported from 26.4.0. -
Deployment Constraint: The component is supported in AppDynamics On-Premises Virtual Appliance (VA) and cSaaS.
-
Supported Operating Systems: Amazon Linux, Red Hat Enterprise Linux (RHEL), and Debian-based distributions.
-
Supported Architectures: amd64 (x86_64) and arm64 (ARM64/aarch64).
-
Recommended Resource Limits: The following table provides the resource limits:
Profile Traces/sec Span/sec Metrics Datapoints/min Instances in Use OTel YAML Configuration Resources Demo 100 2000 15000 30 CODEprocessors: tail_sampling: decision_wait: 30sCODEresources: limits: cpu: 500m memory: 1Gi requests: cpu: 250m memory: 512MiSmall 500 15000 15000 30 CODEprocessors: tail_sampling: decision_wait: 30sCODEresources: limits: cpu: '1' memory: 6Gi requests: cpu: 512m memory: 2GiMedium 3000 60000 125000 250 CODEprocessors: tail_sampling: decision_wait: 30s num_traces: 120000CODEresources: limits: cpu: '5' memory: 28Gi requests: cpu: 2500m memory: 10GiLarge 10000 200000 500000 1000 CODEprocessors: tail_sampling: decision_wait: 30s num_traces: 500000 expected_new_traces_per_sec: 10000 exporters: appd: spanbuffersize: 200000 agentspanbuffersize: 25000 metricbuffersize: 500 maxagents: 2500CODEresources: limits: cpu: '12' memory: 75Gi requests: cpu: '3' memory: 30Gi
-
- Kubernetes Environment: Cluster version 1.20+ with kubectl and docker installed.
AppDynamics Collector-Exporter
-
Log in to the Downloads Portal.
-
Select OpenTelemetry Ingestion Gateway for AppDynamics from the Type list.
-
Download the appropriate package for your environment. You can view the following files after extracting the package:
-
VERSION: Current build version information. README.md: Installation instructions.otel-collector-k8s.yaml: Kubernetes manifests for deployment.otel-collector-image: Compressed Docker image archive.LICENSE: Pre-release license agreement.
-
-
Follow the installation and set-up instructions provided in the README.md file to complete the setup.
Application Name in the OTel Collector
The AppDynamics OpenTelemetry Collector determines the Application Name from resource attributes. The collector checks resource attributes in a specific order and uses the first available attribute it finds. The following are the detection priority:
appd.app.name(Highest priority)service.namespacedeployment.environment.name"unknown"(Default fallback)
Example 1: Set Default for Missing service.namespace
You must use this configuration to set a default application name only when service.namespace is missing, without overriding existing values.
processors:
resource/set_default_app_name:
attributes:
- key: service.namespace
action: insert
value: "default-application"
service:
pipelines:
traces:
processors: [resource/set_default_app_name, tail_sampling]
metrics:
processors: [resource/set_default_app_name]
-
Resources with
`service.namespace`→ Keep original value -
Resources without
`service.namespace`→ Set to `"default-application"
Example 2: Custom Application Name
The following example demonstrates how to use conditional logic to set application names based on resource attributes. This illustrates one approach to dynamic application name assignment. You can customize these conditions to match your specific deployment patterns, service naming conventions, or organizational requirements using the OpenTelemetry Transformation Language (OTTL).
trace_statements:
- conditions:
# Payment services
- IsMatch(resource.attributes["service.name"], ".*payment.*")
statements:
- set(resource.attributes["appd.app.name"], "payment-services")
metric_statements:
- conditions:
- IsMatch(resource.attributes["service.name"], ".*payment.*")
statements:
- set(resource.attributes["appd.app.name"], "payment-services")
service:
pipelines:
traces:
processors: [transform/set_custom_app_name, tail_sampling]
metrics:
processors: [transform/set_custom_app_name]
Collector Mapping Logic and Reference
You can configure the collector by using the following yaml file:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
tail_sampling:
decision_wait: 30s
num_traces: 500000
policies:
[
{
name: everything,
type: always_sample
}
]
transform/btnaming:
trace_statements:
- conditions:
- span.attributes["http.route"] != nil and IsRootSpan()
statements:
- set(span.attributes[""], span.attributes["http.route"])
- conditions:
- span.name != "" and IsRootSpan() and span.attributes["appd.bt.name"] == nil
statements:
- set(span.attributes[""], span.name)
# Example of setting a bt-wide error bit based on any span in the trace
transform/bt_error:
trace_statements:
- conditions:
- span.attributes["http.status_code"] >= 400
statements:
- set(span.attributes["appd.bt.error"], true)
filter/allow_redis:
metrics:
metric:
- not IsMatch(metric.name, "redis.*")
- Len(metric.data_points) != 1
transform/redis:
metric_statements:
- conditions:
- IsMatch(metric.name, "redis.*")
statements:
- replace_pattern(metric.name, "\\.", "|")
- set(metric.name, Concat(["Custom Metrics|", metric.name], ""))
exporters:
appd:
urlbase: "https://<your-controller-url>"
account: "e2e-customer"
accesskey: "fa1ddb6b-****-44c9-9649-************"
# NOTE: Uncomment the below property if your controller is of version 26.1 or below
# agentproducttype: "open-telemetry"
# tls:
# insecure: false
# ca_file: /etc/otel-certs/ca.crt
# snapshot limits
snapshots:
periodminutes: 1
errors: 2
slows: 5
randoms: 20
service:
telemetry:
logs:
level: "info"
pipelines:
traces:
receivers: [otlp]
processors: [transform/btnaming, transform/bt_error, tail_sampling]
exporters: [appd]
metrics:
receivers: [otlp]
processors: [filter/allow_redis, transform/redis]
exporters: [appd]
-
This YAML is just for an example and must not be used as it is.
-
For the metric, Len(metric.data_points) != 1, the custom AppDynamics collector processes only those metrics where the number of data points (
Len(data_points)) equals to exactly one. Metrics that contain multiple data points (due to multiple label or attribute combinations) are dropped by the collector. This behavior is a known limitation, particularly relevant when ingesting Redis metrics. Many Redis instrumentation libraries emit multi-dimensional metric series, for example,redis.commands{command="get", status="success"}, which include multiple label combinations.To comply with this limitation, metrics must encode dimension values directly into the metric name rather than as separate attributes. For instance, instead of emitting
redis.commands{command="get", status="success"}, the metric should be namedredis.commands.get.success. This approach guarantees exactly one data point per metric, ensuring that the collector to process it correctly.Additionally, the collector currently supports only the following metric types: Counter, UpDownCounter, and Observable Gauge (single-observation). Metric types such as Histograms and ExponentialHistograms are not processed and will be dropped.
The following are the example configurations based on the sample YAML:
Example1: Business Transaction Naming
transform/btnaming processor ensures that traces are grouped into meaningful Business Transactions in the UI.
- Logic: If an
http.routeattribute exists (example:/api/checkout), it is used as the BT name. If not, the span name is used as a fallback. -
Attribute:
http.route
Example 2: Error Reporting
The transform/bt_error processor identifies failed transactions.
- Logic: Any span with an HTTP status code of 400 or higher is marked as an error.
- Attribute:
appd.bt.error
Configure Certificate (optional)
To enable secure HTTPS connections using a custom CA certificate, modify otel-collector-k8s.yaml before deployment.
-
Create a ConfigMap with your CA certificate.
kubectl create configmap otel-ca-cert --from-file=ca.crt=</path/to/your/ca.crt> -n appdynamicsNote: Replace the /path/to/your/ca.crt with your system path where the certificate is available. -
Enable TLS in the Exporter Configuration. Uncomment the following tls block in the
otel-collector-k8s.yamlCODEexporters: appd: urlbase: "https://<your-controller-url>" account: "e2e-customer" accesskey: "fa1ddb6b-****-44c9-9649-************" # NOTE: Uncomment the below property if your controller is of version 26.1 or below # agentproducttype: "open-telemetry" tls: insecure: false ca_file: /etc/otel-certs/ca.crt # snapshot limits snapshots: periodminutes: 1 errors: 2 slows: 5 randoms: 20 -
Mount the Certificate in the Deployment. Uncomment the following
volumeMountsandvolumesblocks in theotel-collector-k8s.yamlCODEvolumeMounts: - name: otel-ca-cert mountPath: /etc/otel-certs readOnly: trueCODEvolumes: - name: otel-ca-cert configMap: name: otel-ca-cert items: - key: ca.crt path: ca.crt
Deploy OTel Collector
To deploy OTel Collector:
-
Create a namespace appdynamics in the cluster. If you require to use a custom namespace, you can ignore this step and ensure to use the same namespace during installation. Here, we have used namespace as
kubectl create namespace appdynamicsappdynamics. -
Load the Docker Image:
- Load image to local docker:
docker load -i otel-collector-image.tar - Tag image to private registry:
docker tag appdynamics/otel-collector:<version> <your-registry>/appdynamics/otel-collector:<version> -
Login to your private registry docker.
- Push the image to private registry:
docker push <your-registry>/appdynamics/otel-collector:<version>.
- Load image to local docker:
-
Configure and apply Manifests by updating the Exporters section in the
otel-collector-k8s.yamlwith your Controller URL, Account Name, and Access Key. -
Deploy the collector.
kubectl apply -f otel-collector-k8s.yaml -
Validate the installation.
kubectl get pods -n appdynamicskubectl logs -n appdynamics -l app=otel-collector -f
Node-Level Visibility
Node-level visibility enables you to move beyond simple tier-level monitoring. By registering OpenTelemetry-instrumented services as individual nodes within your tiers, you can have a granular control over performance tracking. This enables you to isolate issues to specific service instances, monitor individual node health, and maintain a clear hierarchy of your infrastructure, which is essential for complex, distributed environments.
Node Naming Strategies
-
Default Mode (Recommended): Uses a single aggregated node (
appd.node.name: "default") per tier. Ideal for Kubernetes and auto-scaling environments. - Per-Instance Mode: Each service instance appears as a separate node. Ideal for VM deployments or troubleshooting specific instances.
How Nodes Are Created
The collector automatically creates nodes based on resource attributes:
- Node Name:
appd.node.name - Tier Name:
appd.tier.name(Fallback:service.name) - Application Name:
appd.app.name(Fallback:service.namespace)
# Node Mode: By default all instances share one node ("default") per tier.
# A transformation like below could be added to enable multiple nodes per tier (one per host).
# Then add transform/nodename to the traces and metrics pipelines below.
# This can result in very high number of nodes, beyond 1000 nodes, additional tuning would be needed
processors:
transform/nodename:
trace_statements:
- context: resource
statements:
- set(attributes["appd.node.name"], attributes["host.name"])
metric_statements:
- context: resource
statements:
- set(attributes["appd.node.name"], attributes["host.name"])
service:
telemetry:
logs:
level: "info"
pipelines:
traces:
receivers: [ otlp ]
processors: [ transform/nodename, transform/btnaming, transform/bt_error, tail_sampling ]
exporters: [ appd ]
metrics:
receivers: [ otlp ]
processors: [ transform/nodename, filter/allow_redis, transform/redis ]
exporters: [ appd ]
Metrics Browser
Metrics Browser enables you to analyze custom OpenTelemetry metrics along your standard AppDynamics performance data. This integration allows you to leverage the complete AppDynamics Metrics Browser to create complex visualizations, set thresholds, and perform statistical analysis on OTel-generated telemetry. Whether you are monitoring custom application metrics, container utilization, or infrastructure health, you can seamlessly integrate these data points into your existing AppDynamics dashboards.
You can view metrics by using:
-
Metrics Browser - Enables you to explore the entire metric tree, view statistical summaries, and see raw time-series data for debugging. To view metrics using the Metrics Browser:
-
Select your application
-
Click Metric Browser from the navigation menu.
-
Expand: Application Infrastructure Performance > [Your Tier Name] > Custom Metrics.
-
Select any metric to view its time-series graph and statistical summary (Observed, Min, Max, Sum, Count).
-
- Dashboards - Enables you to create visual widgets to monitor specific metrics over time. To view metrics using the Dashboards:
-
In the Controller GUI, navigate to Tiers & Nodes and create a new dashboard or select an existing one from the My Dashboards tab.
-
Click Add Widget and select a chart type (Example: Time Series Graph).
-
In the Add Series dialog, select Custom (use any metrics) from the list.
-
Click Select a Metric and navigate through the hierarchy.
-
Click Save to visualize the OTel data on your dashboard.
-
Visualize OpenTelemetry Apps
Data ingested through the Collector-Exporter is associated to standard AppDynamics components for a consistent monitoring experience.
Business Transactions (BTs)
OTel traces are automatically converted into BTs.
- Naming: Use the attribute in your collector config to ensure BTs are named logically (Example: /api/checkout).
- Scorecards: View health, response times, and error rates for OTel-based transactions.
Snapshots
When a transaction is slow or results in an error, AppDynamics generates a Snapshot. Drill down into specific span attributes and metadata for troubleshooting.
Third-Party Metrics
Metrics from services like Redis can be organized using prefixes (Example: Custom Metrics|Redis|...)
Uninstallation
To remove the OTel Collector and all associated resources from your cluster:
kubectl delete -f otel-collector-k8s.yaml