-
Notifications
You must be signed in to change notification settings - Fork 227
feat: auto-inject AWS account ID into telemetry from Lambda extension registration #2004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: auto-inject AWS account ID into telemetry from Lambda extension registration #2004
Conversation
… 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
|
|
||
| // Prepend resource/aws-account-id processor | ||
| processors = append([]interface{}{resourceProc}, processors...) | ||
| updates[fmt.Sprintf("%s::%s::%s::%s", serviceKey, pipelinesKey, telemetryType, processorsKey)] = processors |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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?
Summary
Automatically extract the AWS account ID from the Lambda Extensions API registration response and inject it as the
cloud.account.idattribute 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
AccountIdfield toRegisterResponseand request theaccountIdfeature viaLambda-Extension-Accept-Featureheadercloud.account.idattribute into all pipelinesBenefits