Skip to content

feat(collector/receiver): support extra fields in structured JSON function logs#2251

Open
tomsobpl wants to merge 5 commits intoopen-telemetry:mainfrom
tomsobpl:feat/extra-fields-support-in-json-logging
Open

feat(collector/receiver): support extra fields in structured JSON function logs#2251
tomsobpl wants to merge 5 commits intoopen-telemetry:mainfrom
tomsobpl:feat/extra-fields-support-in-json-logging

Conversation

@tomsobpl
Copy link
Copy Markdown

Summary

When a Lambda function emits structured JSON logs (i.e., a log line with a message field), the telemetryapireceiver now extracts any additional fields from the JSON object and maps them to log record attributes.

Previously, only the message field was captured from structured JSON log records. Any extra fields present in the JSON object (e.g., userId, orderId, custom business data) were silently discarded.

{ "message": "order processed", "orderId": "123", "userId": "abc", "level": "info" }

Before:
→ Log record body: "order processed", no extra attributes.

After:
→ Log record body: "order processed", attributes: orderId = "123", userId = "abc".

Changes

  • receiver.go: After setting the log record body from the message field, iterate over all remaining keys in the parsed JSON record and add them as log record attributes. The well-known fields (level, message, requestId, timestamp) are skipped since they are already handled separately.
  • receiver_test.go: Extended test coverage for JSON logs containing extra fields, covering nested values, arrays, and numeric types.

Notes:

  • The attribute key names are taken as-is from the JSON log (no prefix added), keeping the mapping straightforward.
  • Values that cannot be converted via pcommon.Value.FromRaw emit a warning and are skipped rather than failing the entire record.

@tomsobpl tomsobpl requested a review from a team as a code owner April 14, 2026 06:24
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented Apr 14, 2026

CLA Signed
The committers listed above are authorized under a signed CLA.

@wpessers wpessers added bug Something isn't working go Pull requests that update Go code labels Apr 14, 2026
Comment on lines +500 to +503
if err := attr.FromRaw(value); err != nil {
r.logger.Warn(fmt.Sprintf("error creating attribute: %s", key), zap.Error(err))
continue
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If an error is returned out of the FromRaw call you log but shouldn't you also remove the attribute from the map?

default:
attr, _ := logRecord.Attributes().GetOrPutEmpty(key)
if err := attr.FromRaw(value); err != nil {
r.logger.Warn(fmt.Sprintf("error creating attribute: %s", key), zap.Error(err))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

fmt.Sprintf is okay but I'd prefer leveraging zap as much as possible here. Also not sure why we'd prefer logging this as a warning vs an error level but happy to hear your thoughts on that. I'd suggest something like:

Suggested change
r.logger.Warn(fmt.Sprintf("error creating attribute: %s", key), zap.Error(err))
r.logger.Warn("Failed to convert field to attribute", zap.String("key", key), zap.Error(err))

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I personally do not feel that after emiting an error level process should continue and that's why I chose warning here.

Probably it is more preferred to prioritize handling request correctly instead stopping it due to malformed log metadata. Warning developer that something was not correctly logged should be enough IMHO.

Anyway agree on the zap usage.


for key, value := range record {
switch key {
case "level", "message", "requestId", "timestamp":
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You really should include "type" here as well. Look at:

logRecord.Attributes().PutStr("type", el.Type)

Every log record gets an attribute with key "type" and a value like "function" or "platform.start", = the event type from the Telemetry API.

Now what if we have a log line originating from a user's lambda function code:

{
  "message": "buy order",
  "type": "checkout",
  "orderId": "123"
}

So now in your newly added code below (in the default branch) you will simply overwrite the type attribute. And its value will now be "checkout" instead of "function"...

Another, maybe better, option would be to prefix the additional fields that are in a user's logs so we can keep both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

That was an oversight, good catch.

In the first place I was thinking about adding prefixes to the fields but after some thought I abandoned this option. Developers should be familiar with limitations and reserved fields and should handle that themselves on business logic level.

But maybe it's worth to update documentation and point out all the reserved internal fields with info that they will be prioritized over users metadata. WDYT?

"requestId": "79b4f56e-95b1-4643-9700-2807f4e6",
"message": "Hello world, I am a function with extra data!",
"extraString": "stringValue",
"extraNumber": 2217,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This can never happen in production. a Record is always populated via json.Unmarshal into, which always decodes JSON numbers as float64. Consider using float64(2217) as input so the test mirrors reality.

See: https://pkg.go.dev/encoding/json#Unmarshal

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

Labels

bug Something isn't working go Pull requests that update Go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants