Skip to content

Add resource_attributes support to opentelemetry section#2207

Open
movence wants to merge 2 commits into
mainfrom
feature/otel-resource-attributes
Open

Add resource_attributes support to opentelemetry section#2207
movence wants to merge 2 commits into
mainfrom
feature/otel-resource-attributes

Conversation

@movence

@movence movence commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Description

Adds support for setting additional resource attributes at the top level of the opentelemetry section. When a customer sets opentelemetry.resource_attributes, a resource processor is prepended to the front of each OTEL export pipeline (metrics, logs, traces) and upserts the configured key/value pairs onto every record.

Example config

{
  "opentelemetry": {
    "resource_attributes": {
      "team": "cloudwatch",
      "deployment.environment": "prod"
    },
    "collect": {
      "otlp": { "grpc_endpoint": "127.0.0.1:4317", "http_endpoint": "127.0.0.1:4318" }
    }
  }
}

Generated pipeline (metrics/logs/traces)

processors:
  - resource/opentelemetry        # <- new, first
  - resourcedetection/opentelemetry
  - transform/identity
  - ... (logs adds routing/cleanup)
  - batch/opentelemetry_*

Implementation

  • New JSON schema key opentelemetry.resource_attributes (string map, maxProperties: 30, per-value minLength: 1/maxLength: 1024), mirroring the existing metrics append_dimensions definition.
  • common.OtelResourceAttributesKey + a common.GetStringMap helper.
  • Reuses the existing contrib resourceprocessor and its WithAttributes option (emits upsert actions) — no new dependencies.
  • Prepends the processor in the three base pipeline translators, only when the key is set (existing configs are unchanged).

Design note (precedence)

The processor is placed first, i.e. before resourcedetection. Since resourcedetection runs with override: true, an agent-detected attribute (e.g. cloud.region, host.id) will win over a same-named customer value. If we want the customer value to always be authoritative for detected keys, the processor should instead run after resourcedetection (but still before the transforms). Raising this for a second opinion.

The field name is centralized in a single constant, so renaming (e.g. append_resource_attributes) is a one-line change if preferred.

Testing

  • Unit tests: GetStringMap; processor is Processors.Keys()[0] in all three base pipelines.
  • Schema validation samples: valid map passes; non-string value fails with 1 invalid_type error.
  • tocwconfig golden round-trip otlp_otel_resource_attributes_config (json input → yaml/toml goldens). TOML is byte-identical to the baseline (OTEL-only feature); YAML diff is exactly the new processor block + its prepend to the 3 pipelines.
  • go test + go vet clean across all touched packages.

Allow customers to set additional resource attributes at the top level of
the opentelemetry section. When set, a resource processor is prepended to
the front of the metrics, logs, and traces export pipelines and upserts the
configured key/value pairs onto every record.

- Add opentelemetry.resource_attributes to the JSON schema (string map).
- Add common.GetStringMap helper and the OtelResourceAttributesKey.
- Prepend resource/opentelemetry processor in the three base pipelines.
- Unit tests for the helper and processor ordering, schema validation
  samples, and a tocwconfig golden round-trip (json/yaml/toml).
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Binary Size Report

linux/amd64

Binary PR vs main (60b2321) vs v1.300070.0
amazon-cloudwatch-agent 233.3 MB ${\color{red}▲}$ +8.2 KB (+0.0%) ${\color{red}▲}$ +1.9 MB (+0.8%)
amazon-cloudwatch-agent-config-wizard 2.0 MB +0 B ${\color{red}▲}$ +86.0 KB (+4.5%)
config-downloader 2.0 MB +0 B ${\color{red}▲}$ +86.0 KB (+4.5%)
config-translator 2.0 MB +0 B ${\color{red}▲}$ +86.0 KB (+4.5%)
start-amazon-cloudwatch-agent 2.5 MB +0 B ${\color{red}▲}$ +81.9 KB (+3.4%)
workload-discovery 3.0 MB +0 B ${\color{red}▲}$ +86.0 KB (+2.9%)
Total 244.8 MB ${\color{red}▲}$ +8.2 KB (+0.0%) ${\color{red}▲}$ +2.4 MB (+1.0%)
linux/amd64 amazon-cloudwatch-agent (last 9 main commits + this PR)

 233 ┤                                        
     ┤            ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ███ ███ 
     ┤            ███ ███ ███ ███ ███ ███ ███ 
     ┤            ███ ███ ███ ███ ███ ███ ███ 
     ┤            ███ ███ ███ ███ ███ ███ ███ 
     ┤            ███ ███ ███ ███ ███ ███ ███ 
     ┤            ███ ███ ███ ███ ███ ███ ███ 
     ┤            ███ ███ ███ ███ ███ ███ ███ 
 231 ┤▄▄▄ ▄▄▄ ▄▄▄ ███ ███ ███ ███ ███ ███ ███ 
 MB  └────────────────────────────────────────
      42a07a3                             PR

Notable changes:

linux/arm64
Binary PR vs main (60b2321) vs v1.300070.0
amazon-cloudwatch-agent 212.2 MB ${\color{red}▲}$ +65.5 KB (+0.0%) ${\color{red}▲}$ +720.9 KB (+0.3%)
amazon-cloudwatch-agent-config-wizard 2.0 MB +0 B ${\color{green}▼}$ 22 B (-0.0%)
config-downloader 2.0 MB +0 B ${\color{green}▼}$ 22 B (-0.0%)
config-translator 2.0 MB +0 B ${\color{green}▼}$ 22 B (-0.0%)
start-amazon-cloudwatch-agent 2.4 MB +0 B ${\color{red}▲}$ +65.5 KB (+2.8%)
workload-discovery 2.9 MB +0 B ${\color{red}▲}$ +65.5 KB (+2.3%)
Total 223.5 MB ${\color{red}▲}$ +65.5 KB (+0.0%) ${\color{red}▲}$ +851.8 KB (+0.4%)
windows/amd64
Binary PR vs main (60b2321) vs v1.300070.0
amazon-cloudwatch-agent.exe 236.2 MB ${\color{red}▲}$ +18.4 KB (+0.0%) ${\color{red}▲}$ +2.0 MB (+0.9%)
amazon-cloudwatch-agent-config-wizard.exe 2.1 MB +0 B ${\color{red}▲}$ +130.6 KB (+6.5%)
config-downloader.exe 2.1 MB +0 B ${\color{red}▲}$ +130.0 KB (+6.5%)
config-translator.exe 2.1 MB +0 B ${\color{red}▲}$ +130.6 KB (+6.5%)
start-amazon-cloudwatch-agent.exe 2.3 MB +0 B ${\color{red}▲}$ +112.1 KB (+5.2%)
workload-discovery.exe 2.9 MB +0 B ${\color{red}▲}$ +91.6 KB (+3.3%)
Total 247.9 MB ${\color{red}▲}$ +18.4 KB (+0.0%) ${\color{red}▲}$ +2.6 MB (+1.1%)
Investigating size changes

Use go-size-analyzer to compare binaries:

GOEXPERIMENT=jsonv2 go install github.com/Zxilly/go-size-analyzer/cmd/gsa@latest
gsa diff --old <baseline-binary> --new <new-binary>

… order

Address review findings on the resource_attributes feature:

- Reject empty attribute keys and reserved log-routing keys
  (aws.log.group.name, aws.log.stream.name, aws.log.source) in the resource
  processor's static-attributes path, so customers cannot clobber
  agent-managed attributes. Key validation is done in the translator because
  the config schema is draft-04, under which gojsonschema does not enforce
  propertyNames.
- Emit attributes in sorted key order for deterministic generated config.
- Document in the schema that resource detection (override: true) runs after
  and takes precedence for auto-detected keys, so the behavior is explicit
  rather than silent.

Adds WithReservedKeys option, unit tests for empty/reserved/sorted paths, and
a dedicated resource_attributes_test.go for the pipeline helper.
@movence
movence marked this pull request as ready for review July 17, 2026 13:29
@movence
movence requested a review from a team as a code owner July 17, 2026 13:29
@movence movence added the ready for testing Indicates this PR is ready for integration tests to run label Jul 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we just add this to an existing one? We're starting to have to manage and regenerate too many sample configs.

attributes map[string]string
factory processor.Factory
attributes map[string]string
reservedKeys []string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should we just store this as a collection.Set[string]?

}
keys = append(keys, k)
}
sort.Strings(keys)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use errors.Join so all errors are shown at once instead of just the first?

Comment on lines +15 to +19
var reservedResourceAttributeKeys = []string{
"aws.log.group.name",
"aws.log.stream.name",
"aws.log.source",
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense in practice, but I think we'll likely want to rename all of our internally temporary resource attributes that we use and remove. That can be done as a follow up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for testing Indicates this PR is ready for integration tests to run

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants