Skip to content

Conversation

@RaphaelManke
Copy link

Summary

Automatically extract the AWS account ID from the Lambda Extensions API registration response and inject it as the cloud.account.id attribute into all telemetry (traces, logs, metrics) via a confmap converter.

This implementation uses the OpenTelemetry Collector's converter pattern to mutate the loaded configuration, ensuring the account ID is automatically available without requiring configuration or environment variables.

Changes

  • extensionapi/client.go: Added AccountId field to RegisterResponse and request the accountId feature via Lambda-Extension-Accept-Feature header
  • accountidprocessor/converter.go: New confmap converter that automatically injects a resource processor for cloud.account.id attribute into all pipelines
  • accountidprocessor/converter_test.go: Comprehensive tests for converter behavior (empty account ID, no pipelines, multiple pipelines, leading zeros)
  • extensionapi/client_test.go: Tests for JSON unmarshaling with leading zero preservation
  • lambdacomponents/default.go: Updated to accept accountID and return converter factories
  • collector.go: Updated to accept and register custom converters
  • manager.go: Updated to pass account ID through the initialization flow

Benefits

  • Account ID automatically available in all telemetry without configuration
  • No environment variables needed, uses AWS Lambda API response
  • Follows OpenTelemetry Collector patterns
  • Static injection at startup with no runtime overhead
  • Comprehensive test coverage for edge cases (leading zeros, multiple pipelines)

… registration

Automatically extract the AWS account ID from the Lambda Extensions API
registration response and inject it as the 'cloud.account.id' attribute
into all telemetry (traces, logs, metrics) via a confmap converter.

Changes:
- Add AccountId field to RegisterResponse struct
- Request Lambda-Extension-Accept-Feature header with 'accountId' value
- Create accountidprocessor converter to inject cloud.account.id attribute
- Update lambdacomponents.Components to accept accountID and return converters
- Update collector.NewCollector to accept and register custom converters
- Update manager to pass account ID through the initialization flow

Includes:
- Comprehensive tests for JSON unmarshaling with leading zero preservation
- Tests for converter behavior across different pipeline configurations
- Tests for account ID handling in the extension API client

Benefits:
- Account ID automatically available in all telemetry without configuration
- No environment variables needed, uses AWS Lambda API response
- Follows OpenTelemetry Collector converter pattern
- Static injection at startup, no runtime overhead
@RaphaelManke RaphaelManke requested a review from a team as a code owner October 24, 2025 12:24
@wpessers wpessers added the go Pull requests that update Go code label Oct 28, 2025

// Prepend resource/aws-account-id processor
processors = append([]interface{}{resourceProc}, processors...)
updates[fmt.Sprintf("%s::%s::%s::%s", serviceKey, pipelinesKey, telemetryType, processorsKey)] = processors
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm by no means a golang / otel collector expert. But I compared the way this update is being done to how the converter for the decouple processor works. It's idempotent in a way, because there it is checked whether the processor should be added instead of blindly adding it: https://github.com/open-telemetry/opentelemetry-lambda/blob/main/collector/internal/confmap/converter/decoupleafterbatchconverter/converter.go#L82-L86 However I'm guessing that is less relevant here as we do not expect users to define an accountIdProcessor themselves? And instead we assume that this processor will always be added once to each pipeline?

extensionNameHeader = "Lambda-Extension-Name"
extensionIdentiferHeader = "Lambda-Extension-Identifier"
extensionErrorType = "Lambda-Extension-Function-Error-Type"
extensionAcceptFeatureHeader = "Lambda-Extension-Accept-Feature"
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, but can you please format this

return &converter{accountID: accountID}
}

func (c converter) Convert(_ context.Context, conf *confmap.Conf) error {
Copy link
Member

@maxday maxday Nov 27, 2025

Choose a reason for hiding this comment

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

I think we can tweak this function a bit:

  • use `(c* converter) in the definition
  • simplify a bit the for loop
  • remove the usage of sprintf

This will give something like:

func (c *converter) Convert(_ context.Context, conf *confmap.Conf) error {
	if c.accountID == "" {
		return nil
	}

	svc, ok := conf.Get(serviceKey).(map[string]interface{})
	if !ok {
		return nil
	}

	pipes, ok := svc[pipelinesKey].(map[string]interface{})
	if !ok {
		return nil
	}

	for _, v := range pipes {
		p, ok := v.(map[string]interface{})
		if !ok {
			continue
		}

		raw, _ := p[processorsKey]
		procs, _ := raw.([]interface{})
		p[processorsKey] = append([]interface{}{resourceProc}, procs...)
	}

	resourceProcessor := map[string]interface{}{
		resourceProc: map[string]interface{}{
			"attributes": []map[string]interface{}{
				{
					"key":    accountIDAttrKey,
					"value":  c.accountID,
					"action": "insert",
				},
			},
		},
	}

	return conf.Merge(confmap.NewFromStringMap(map[string]interface{}{
		"processors": resourceProcessor,
	}))
}

WDYT?

pipelinesKey = "pipelines"
processorsKey = "processors"
resourceProc = "resource/aws-account-id"
accountIDAttrKey = "cloud.account.id"
Copy link
Member

Choose a reason for hiding this comment

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

do you know if those strings already exists in a semver convention naming package? I'm not sure for other keys, but a quick search for this one: https://github.com/open-telemetry/opentelemetry-go/blob/a0a0acdceb0608265890ab6d983e9d1f7b73e735/semconv/v1.4.0/resource.go#L25 shows that we are already exporting this one, let's reuse it?

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

Labels

go Pull requests that update Go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants