Skip to content
This repository was archived by the owner on Jul 11, 2025. It is now read-only.

Commit 8841fda

Browse files
authored
Add OTel messaging attributes (#26)
Closes undefined
1 parent 95b5d7b commit 8841fda

File tree

2 files changed

+466
-0
lines changed

2 files changed

+466
-0
lines changed
Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Distributed Tracing open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift Distributed Tracing project
6+
// authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Tracing
16+
17+
extension SpanAttributes {
18+
/// Semantic conventions for messaging systems.
19+
///
20+
/// OpenTelemetry Spec: [Messaging Systems](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md)
21+
public var messaging: MessagingAttributes {
22+
get {
23+
.init(attributes: self)
24+
}
25+
set {
26+
self = newValue.attributes
27+
}
28+
}
29+
}
30+
31+
/// Semantic conventions for messaging systems.
32+
///
33+
/// OpenTelemetry Spec: [Messaging Systems](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md)
34+
@dynamicMemberLookup
35+
public struct MessagingAttributes: SpanAttributeNamespace {
36+
public var attributes: SpanAttributes
37+
38+
public init(attributes: SpanAttributes) {
39+
self.attributes = attributes
40+
}
41+
42+
// MARK: - General
43+
44+
public struct NestedSpanAttributes: NestedSpanAttributesProtocol {
45+
public init() {}
46+
47+
/// A string identifying the messaging system. E.g. "kafka".
48+
public var system: Key<String> { "messaging.system" }
49+
50+
/// The message destination name. E.g. "MyTopic".
51+
///
52+
/// This might be equal to the span name but is required nevertheless.
53+
public var destination: Key<String> { "messaging.destination" }
54+
55+
/// The kind of message destination. E.g. ``MessagingAttributes/DestinationKind/topic``.
56+
public var destinationKind: Key<DestinationKind> { "messaging.destination_kind" }
57+
58+
/// Whether the message destination is temporary.
59+
public var isTemporaryDestination: Key<Bool> { "messaging.temp_destination" }
60+
61+
/// The name of the transport protocol. E.g. "AMQP".
62+
public var `protocol`: Key<String> { "messaging.protocol" }
63+
64+
/// The version of the transport protocol. E.g. "0.9.1".
65+
public var protocolVersion: Key<String> { "messaging.protocol_version" }
66+
67+
/// Connection string. E.g. "https://queue.amazonaws.com/80398EXAMPLE/MyQueue".
68+
public var url: Key<String> { "messaging.url" }
69+
70+
/// A value used by the messaging system as an identifier for the message, represented as a string. E.g. "452a7c7c7c7048c2f887f61572b18fc2".
71+
public var messageID: Key<String> { "messaging.message_id" }
72+
73+
/// The [conversation ID](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#conversations)
74+
/// identifying the conversation to which the message belongs, represented as a string. Sometimes called "Correlation ID".
75+
/// E.g. "MyConversationId".
76+
public var conversationID: Key<String> { "messaging.conversation_id" }
77+
78+
/// The (uncompressed) size of the message payload in bytes. E.g. 2738.
79+
///
80+
/// Also use this attribute if it is unknown whether the compressed or uncompressed payload size is reported.
81+
public var messagePayloadSizeInBytes: Key<Int> { "messaging.message_payload_size_bytes" }
82+
83+
/// The compressed size of the message payload in bytes. E.g. 2048.
84+
public var compressedMessagePayloadSizeInBytes: Key<Int> { "messaging.message_payload_compressed_size_bytes" }
85+
86+
/// The kind of message consumption as defined in ``MessagingAttributes/Operation``.
87+
/// E.g. ``MessagingAttributes/Operation/receive``.
88+
public var operation: Key<Operation> { "messaging.operation" }
89+
90+
/// The identifier for the consumer receiving a message.
91+
///
92+
/// ## Kafka
93+
/// Set it to ``MessagingAttributes/KafkaAttributes/NestedSpanAttributes/consumerGroup`` - ``MessagingAttributes/KafkaAttributes/NestedSpanAttributes/clientID``,
94+
/// if both are present, or only ``MessagingAttributes/KafkaAttributes/NestedSpanAttributes/consumerGroup``.
95+
///
96+
/// ## Brokers such as RabbitMQ and Artemis
97+
/// Set it to the `client_id` of the client consuming the message.
98+
public var consumerID: Key<String> { "messaging.consumer_id" }
99+
}
100+
101+
// MARK: - RabbitMQ
102+
103+
/// Semantic conventions for RabbitMQ spans.
104+
///
105+
/// [OpenTelemetry Spec: RabbitMQ](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#rabbitmq)
106+
public var rabbitMQ: RabbitMQAttributes {
107+
get {
108+
.init(attributes: self.attributes)
109+
}
110+
set {
111+
self.attributes = newValue.attributes
112+
}
113+
}
114+
115+
/// Semantic conventions for RabbitMQ spans.
116+
///
117+
/// [OpenTelemetry Spec: RabbitMQ](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#rabbitmq)
118+
public struct RabbitMQAttributes: SpanAttributeNamespace {
119+
public var attributes: SpanAttributes
120+
121+
public init(attributes: SpanAttributes) {
122+
self.attributes = attributes
123+
}
124+
125+
public struct NestedSpanAttributes: NestedSpanAttributesProtocol {
126+
public init() {}
127+
128+
/// RabbitMQ message routing key. E.g. "myKey"
129+
public var routingKey: Key<String> { "messaging.rabbitmq.routing_key" }
130+
}
131+
}
132+
133+
// MARK: - Kafka
134+
135+
/// Semantic conventions for Kafka spans.
136+
///
137+
/// [OpenTelemetry Spec: Kafka](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#apache-kafka)
138+
public var kafka: KafkaAttributes {
139+
get {
140+
.init(attributes: self.attributes)
141+
}
142+
set {
143+
self.attributes = newValue.attributes
144+
}
145+
}
146+
147+
/// Semantic conventions for Kafka spans.
148+
///
149+
/// [OpenTelemetry Spec: Kafka](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#apache-kafka)
150+
public struct KafkaAttributes: SpanAttributeNamespace {
151+
public var attributes: SpanAttributes
152+
153+
public init(attributes: SpanAttributes) {
154+
self.attributes = attributes
155+
}
156+
157+
public struct NestedSpanAttributes: NestedSpanAttributesProtocol {
158+
public init() {}
159+
160+
/// Message keys in Kafka are used for grouping alike messages to ensure they're processed on the same partition.
161+
/// They differ from ``MessagingAttributes/NestedSpanAttributes/messageID`` in that they're not unique.
162+
/// E.g. "myKey".
163+
///
164+
/// - Note: If the key is null, the attribute MUST NOT be set.
165+
public var messageKey: Key<String> { "messaging.kafka.message_key" }
166+
167+
/// Name of the Kafka Consumer Group that is handling the message.
168+
/// Only applies to consumers, not producers. E.g. "my-group".
169+
public var consumerGroup: Key<String> { "messaging.kafka.consumer_group" }
170+
171+
/// Client ID for the Consumer or Producer that is handling the message. E.g. "client-5".
172+
public var clientID: Key<String> { "messaging.kafka.client_id" }
173+
174+
/// Partition the message is sent to. E.g. 2.
175+
public var partition: Key<Int> { "messaging.kafka.partition" }
176+
177+
/// Whether the message is a tombstone. If not set, it is assumed to be false.
178+
public var isTombstone: Key<Bool> { "messaging.kafka.tombstone" }
179+
}
180+
}
181+
182+
/// Semantic conventions for RocketMQ spans.
183+
///
184+
/// [OpenTelemetry Spec: RocketMQ](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#apache-rocketmq)
185+
public var rocketMQ: RocketMQAttributes {
186+
get {
187+
.init(attributes: self.attributes)
188+
}
189+
set {
190+
self.attributes = newValue.attributes
191+
}
192+
}
193+
194+
/// Semantic conventions for RocketMQ spans.
195+
///
196+
/// [OpenTelemetry Spec: RocketMQ](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#apache-rocketmq)
197+
public struct RocketMQAttributes: SpanAttributeNamespace {
198+
public var attributes: SpanAttributes
199+
200+
public init(attributes: SpanAttributes) {
201+
self.attributes = attributes
202+
}
203+
204+
public struct NestedSpanAttributes: NestedSpanAttributesProtocol {
205+
public init() {}
206+
207+
/// Namespace of RocketMQ resources, resources in different namespaces are individual. E.g. "myNamespace".
208+
public var namespace: Key<String> { "messaging.rocketmq.namespace" }
209+
210+
/// Name of the RocketMQ producer/consumer group that is handling the message. E.g. "myConsumerGroup".
211+
///
212+
/// The client type is identified by the SpanKind.
213+
public var clientGroup: Key<String> { "messaging.rocketmq.client_group" }
214+
215+
/// The unique identifier for each client. E.g. "myhost@8742@s8083jm".
216+
public var clientID: Key<String> { "messaging.rocketmq.client_id" }
217+
218+
/// Type of message. E.g. ``MessagingAttributes/RocketMQAttributes/MessageType/normal``.
219+
public var messageType: Key<MessageType> { "messaging.rocketmq.message_type" }
220+
221+
/// The secondary classifier of message besides topic. E.g. "tagA".
222+
public var messageTag: Key<String> { "messaging.rocketmq.message_tag" }
223+
224+
/// Key(s) of message, another way to mark message besides message id. E.g. ["keyA", "keyB"].
225+
public var messageKeys: Key<[String]> { "messaging.rocketmq.message_keys" }
226+
227+
/// Model of message consumption. E.g. ``MessagingAttributes/RocketMQAttributes/ConsumptionModel/clustering``.
228+
///
229+
/// - Note: This only applies to consumer spans.
230+
public var consumptionModel: Key<ConsumptionModel> { "messaging.rocketmq.consumption_model" }
231+
}
232+
233+
/// Possible values for the ``MessagingAttributes/RocketMQAttributes/NestedSpanAttributes/messageType`` span attribute.
234+
public struct MessageType: RawRepresentable, SpanAttributeConvertible {
235+
public let rawValue: String
236+
237+
public init(rawValue: String) {
238+
self.rawValue = rawValue
239+
}
240+
241+
/// Normal message.
242+
public static let normal = MessageType(rawValue: "normal")
243+
244+
/// FIFO message.
245+
public static let fifo = MessageType(rawValue: "fifo")
246+
247+
/// Delay message.
248+
public static let delay = MessageType(rawValue: "delay")
249+
250+
/// Transaction message.
251+
public static let transaction = MessageType(rawValue: "transaction")
252+
253+
public func toSpanAttribute() -> SpanAttribute {
254+
.string(self.rawValue)
255+
}
256+
}
257+
258+
/// Possible values for the ``MessagingAttributes/RocketMQAttributes/NestedSpanAttributes/consumptionModel`` span attribute.
259+
public struct ConsumptionModel: RawRepresentable, SpanAttributeConvertible {
260+
public let rawValue: String
261+
262+
public init(rawValue: String) {
263+
self.rawValue = rawValue
264+
}
265+
266+
/// Clustering consumption model.
267+
public static let clustering = ConsumptionModel(rawValue: "clustering")
268+
269+
/// Broadcasting consumption model.
270+
public static let broadcasting = ConsumptionModel(rawValue: "broadcasting")
271+
272+
public func toSpanAttribute() -> SpanAttribute {
273+
.string(self.rawValue)
274+
}
275+
}
276+
}
277+
}
278+
279+
// MARK: - Destination Kind
280+
281+
extension MessagingAttributes {
282+
/// Possible values for the ``MessagingAttributes/NestedSpanAttributes/destinationKind`` span attribute.
283+
public struct DestinationKind: RawRepresentable, SpanAttributeConvertible {
284+
public let rawValue: String
285+
286+
public init(rawValue: String) {
287+
self.rawValue = rawValue
288+
}
289+
290+
/// A message sent to a queue.
291+
public static let queue = DestinationKind(rawValue: "queue")
292+
293+
/// A message sent to a topic.
294+
public static let topic = DestinationKind(rawValue: "topic")
295+
296+
public func toSpanAttribute() -> SpanAttribute {
297+
.string(self.rawValue)
298+
}
299+
}
300+
}
301+
302+
// MARK: - Operation
303+
304+
extension MessagingAttributes {
305+
/// Possible values for the ``MessagingAttributes/NestedSpanAttributes/operation`` span attribute.
306+
///
307+
/// - Note: A "send" operation is purposefully not defined,
308+
/// because in this case ``MessagingAttributes/NestedSpanAttributes/operation`` should not be set at all
309+
/// as it can be inferred from the span kind.
310+
///
311+
/// OpenTelemetry Spec: [Messaging Operation Names](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/messaging.md#operation-names)
312+
public struct Operation: RawRepresentable, SpanAttributeConvertible {
313+
public let rawValue: String
314+
315+
public init(rawValue: String) {
316+
self.rawValue = rawValue
317+
}
318+
319+
/// A message is received from a destination by a message consumer/server.
320+
public static let receive = Operation(rawValue: "receive")
321+
322+
/// A message that was previously received from a destination is processed by a message consumer/server.
323+
public static let process = Operation(rawValue: "process")
324+
325+
public func toSpanAttribute() -> SpanAttribute {
326+
.string(self.rawValue)
327+
}
328+
}
329+
}

0 commit comments

Comments
 (0)