Skip to content

feat: update to otel semconv 1.40.0#719

Open
epbensimpson wants to merge 1 commit intoFarfetch:masterfrom
epbensimpson:otel-conventions-1.4.0
Open

feat: update to otel semconv 1.40.0#719
epbensimpson wants to merge 1 commit intoFarfetch:masterfrom
epbensimpson:otel-conventions-1.4.0

Conversation

@epbensimpson
Copy link
Copy Markdown

@epbensimpson epbensimpson commented Apr 2, 2026

Description

Updates KafkaFlow.OpenTelemetry to the OTEL Semantic Conventions 1.40.0 (current version at time of writing).

https://github.com/open-telemetry/semantic-conventions/blob/v1.40.0/docs/messaging/kafka.md

This contains some breaking changes to attribute names due to the unstable nature of the spec.

Implements #470

Summary of changes

Removed

Attribute Reason
peer.service Contained the bootstrap server list, which does not represent the broker that handled a given message

Renamed

Old New Where
messaging.operation = "publish" messaging.operation.type = "send" + messaging.operation.name = "publish" Producer
messaging.source.name messaging.destination.name Consumer
messaging.kafka.consumer.group messaging.consumer.group.name Consumer
messaging.kafka.message.offset messaging.kafka.offset Both
messaging.kafka.source.partition messaging.destination.partition.id Consumer
messaging.kafka.destination.partition messaging.destination.partition.id Producer

Added

Attribute Where Notes
messaging.client.id Consumer Set to ConsumerContext.ConsumerName — closest available proxy for the Kafka client ID, which is not exposed by IConsumerContext
messaging.kafka.message.tombstone = true Both Only emitted when message value is null
messaging.message.body.size Consumer Only emitted when the message value is still byte[] (i.e. before deserialization middleware has run)
error.type Both Emitted in error handlers only; value is the exception's fully-qualified type name

Bug fix

messaging.kafka.message.key: the consumer previously always emitted this tag (empty string for null keys, with an unsafe cast to byte[]), and the producer emitted the raw
object. Both now share a FormatMessageKey helper that UTF-8 decodes byte[] keys, calls ToString() on other types, and omits the tag entirely when the key is null.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have added tests to cover my changes
  • I have made corresponding changes to the documentation

Disclaimer

By sending us your contributions, you are agreeing that your contribution is made subject to the terms of our Contributor Ownership Statement

use "process" for operation.type
@epbensimpson epbensimpson force-pushed the otel-conventions-1.4.0 branch from c44c60f to 16cbcde Compare April 6, 2026 23:10
activity.SetTag(ActivitySourceAccessor.AttributeMessagingKafkaMessageKey, messageKey);
activity.SetTag(ActivitySourceAccessor.AttributeMessagingKafkaMessageOffset, context.ConsumerContext.Offset);
activity.SetTag(AttributeMessagingKafkaSourcePartition, context.ConsumerContext.Partition);
activity.SetTag(AttributeKeys.OperationType, ProcessString);
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.

The messaging.operation.type tag name for consumer is being set with the value process instead of receive like you wrote in your PR description

Copy link
Copy Markdown
Author

@epbensimpson epbensimpson Apr 16, 2026

Choose a reason for hiding this comment

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

Ah I think I made that change after raising the PR, I spotted the list of well-known values and process seems like the more appropriate value here:

Value Description
process One or more messages are processed by a consumer.
receive One or more messages are requested by a consumer. This operation refers to pull-based scenarios, where consumers explicitly call methods of messaging SDKs to receive messages.

I'll update the PR description

internal static class AttributeKeys
{
public const string ClientId = "messaging.client.id";
public const string ConsumerGroupName = "messaging.consumer.group.name";
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.

I would put this consumer specific attribute in the consumer handler, just like the process attribute, or else move all consumer/producer attributes to this class. What's your opinion @brmagadutra @joelfoliveira ?

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 may be missing something but I'm fairly sure all of the attribute keys are defined here? Except for messaging.system which is still inline in SetGenericTags (and could probably be moved for consistency)

The only strings defined in the Producer/Consumer handlers are for tag values, not keys.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants