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

Commit addce91

Browse files
authored
Add OTel FaaS attributes (#28)
Closes #7
1 parent 8841fda commit addce91

File tree

2 files changed

+362
-0
lines changed

2 files changed

+362
-0
lines changed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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 FaaS spans.
19+
///
20+
/// OpenTelemetry Spec: [Semantic conventions for FaaS spans](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/faas.md#semantic-conventions-for-faas-spans)
21+
public var faaS: FaaSAttributes {
22+
get {
23+
.init(attributes: self)
24+
}
25+
set {
26+
self = newValue.attributes
27+
}
28+
}
29+
}
30+
31+
/// Semantic conventions for FaaS spans.
32+
///
33+
/// OpenTelemetry Spec: [Semantic conventions for FaaS spans](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/faas.md#semantic-conventions-for-faas-spans)
34+
@dynamicMemberLookup
35+
public struct FaaSAttributes: 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+
/// Type of the trigger which caused this function execution. E.g. ``FaaSAttributes/Trigger/dataSource``.
48+
///
49+
/// Clients invoking FaaS instances usually cannot set `faas.trigger`, since they would typically need to look in the payload
50+
/// to determine the event type. If clients set it, it should be the same as the trigger that corresponding incoming would have
51+
/// (i.e., this has nothing to do with the underlying transport used to make the API call to invoke the lambda, which is often HTTP).
52+
public var trigger: Key<Trigger> { "faas.trigger" }
53+
54+
/// The execution ID of the current function execution. E.g. "af9d5aa4-a685-4c5f-a22b-444f80b3cc28".
55+
public var executionID: Key<String> { "faas.execution" }
56+
57+
/// Whether the serverless function is executed for the first time (aka cold-start).
58+
public var isColdStart: Key<Bool> { "faas.coldstart" }
59+
60+
/// The name of the invoked function. E.g. "my-function".
61+
public var invokedFunction: Key<String> { "faas.invoked_name" }
62+
63+
/// The cloud provider of the invoked function. E.g. ``FaaSAttributes/Provider/alibabaCloud``.
64+
public var invokedProvider: Key<Provider> { "faas.invoked_provider" }
65+
66+
/// The cloud region of the invoked function. E.g. "eu-central-1".
67+
///
68+
/// For some cloud providers, like AWS or GCP, the region in which a function is hosted is essential to uniquely identify the function
69+
/// and also part of its endpoint. Since it's part of the endpoint being called, the region is always known to clients.
70+
/// In these cases, faas.invoked\_region MUST be set accordingly.
71+
///
72+
/// If the region is unknown to the client or not required for identifying the invoked function, setting faas.invoked\_region is optional.
73+
public var invokedRegion: Key<String> { "faas.invoked_region" }
74+
}
75+
76+
// MARK: - Document
77+
78+
/// Span attributes for functions triggered via ``FaaSAttributes/Trigger/dataSource``.
79+
///
80+
/// OpenTelemetry Spec: [Semantic conventions for FaaS Spans - Datasource](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/faas.md#datasource)
81+
public var document: DocumentAttributes {
82+
get {
83+
.init(attributes: self.attributes)
84+
}
85+
set {
86+
self.attributes = newValue.attributes
87+
}
88+
}
89+
90+
/// Span attributes for functions triggered via ``FaaSAttributes/Trigger/dataSource``.
91+
///
92+
/// OpenTelemetry Spec: [Semantic conventions for FaaS Spans - Datasource](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/faas.md#datasource)
93+
public struct DocumentAttributes: SpanAttributeNamespace {
94+
public var attributes: SpanAttributes
95+
96+
public init(attributes: SpanAttributes) {
97+
self.attributes = attributes
98+
}
99+
100+
public struct NestedSpanAttributes: NestedSpanAttributesProtocol {
101+
public init() {}
102+
103+
/// The name of the source on which the triggering operation was performed.
104+
/// For example, in Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the database name.
105+
public var collection: Key<String> { "faas.document.collection" }
106+
107+
/// Describes the type of the operation that was performed on the data.
108+
/// E.g. ``FaaSAttributes/DocumentAttributes/Operation/insert``.
109+
public var operation: Key<Operation> { "faas.document.operation" }
110+
111+
/// A string containing the time when the data was accessed
112+
/// in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format
113+
/// expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). E.g. "2020-01-23T13:47:06Z".
114+
public var time: Key<String> { "faas.document.time" }
115+
116+
/// The document name/table subjected to the operation.
117+
/// For example, in Cloud Storage or S3 is the name of the file, and in Cosmos DB the table name.
118+
public var name: Key<String> { "faas.document.name" }
119+
}
120+
121+
/// Well-known data operations,
122+
/// used for the ``FaaSAttributes/DocumentAttributes/NestedSpanAttributes/operation`` span attribute.
123+
public struct Operation: RawRepresentable, SpanAttributeConvertible {
124+
public let rawValue: String
125+
126+
public init(rawValue: String) {
127+
self.rawValue = rawValue
128+
}
129+
130+
/// When a new object is created.
131+
public static let insert = Operation(rawValue: "insert")
132+
133+
/// When an object is modified.
134+
public static let edit = Operation(rawValue: "edit")
135+
136+
/// When an object is deleted.
137+
public static let delete = Operation(rawValue: "delete")
138+
139+
public func toSpanAttribute() -> SpanAttribute {
140+
.string(self.rawValue)
141+
}
142+
}
143+
}
144+
145+
// MARK: - Timer
146+
147+
/// Span attributes for functions triggered via ``FaaSAttributes/Trigger/timer``.
148+
///
149+
/// OpenTelemetry Spec: [Semantic conventions for FaaS Spans - Timer](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/faas.md#timer)
150+
public var timer: TimerAttributes {
151+
get {
152+
.init(attributes: self.attributes)
153+
}
154+
set {
155+
self.attributes = newValue.attributes
156+
}
157+
}
158+
159+
/// Span attributes for functions triggered via ``FaaSAttributes/Trigger/timer``.
160+
///
161+
/// OpenTelemetry Spec: [Semantic conventions for FaaS Spans - Timer](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.11.0/specification/trace/semantic_conventions/faas.md#timer)
162+
public struct TimerAttributes: SpanAttributeNamespace {
163+
public var attributes: SpanAttributes
164+
165+
public init(attributes: SpanAttributes) {
166+
self.attributes = attributes
167+
}
168+
169+
public struct NestedSpanAttributes: NestedSpanAttributesProtocol {
170+
public init() {}
171+
172+
/// A string containing the function invocation time
173+
/// in the [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format
174+
/// expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). E.g. "2020-01-23T13:47:06Z".
175+
public var time: Key<String> { "faas.time" }
176+
177+
/// A string containing the schedule period as
178+
/// [Cron Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm).
179+
/// E.g. "0/5 \* \* \* ? \*".
180+
public var cron: Key<String> { "faas.cron" }
181+
}
182+
}
183+
}
184+
185+
extension FaaSAttributes {
186+
/// Triggers which caused a function execution, used for the ``FaaSAttributes/NestedSpanAttributes/trigger`` span attribute.
187+
public struct Trigger: RawRepresentable, SpanAttributeConvertible {
188+
public let rawValue: String
189+
190+
public init(rawValue: String) {
191+
self.rawValue = rawValue
192+
}
193+
194+
/// A response to some data source operation such as a database or filesystem read/write.
195+
///
196+
/// When using this data source make sure to also set the ``FaaSAttributes/document`` span attributes.
197+
public static let dataSource = Trigger(rawValue: "datasource")
198+
199+
/// To provide an answer to an inbound HTTP request.
200+
public static let http = Trigger(rawValue: "http")
201+
202+
/// A function is set to be executed when messages are sent to a messaging system.
203+
public static let pubSub = Trigger(rawValue: "pubsub")
204+
205+
/// A function is scheduled to be executed regularly.
206+
///
207+
/// When using this data source make sure to also set the ``FaaSAttributes/timer`` span attributes.
208+
public static let timer = Trigger(rawValue: "timer")
209+
210+
/// If none of the other well-known triggers apply.
211+
public static let other = Trigger(rawValue: "other")
212+
213+
public func toSpanAttribute() -> SpanAttribute {
214+
.string(self.rawValue)
215+
}
216+
}
217+
}
218+
219+
extension FaaSAttributes {
220+
/// Well-known cloud providers, used for the ``FaaSAttributes/NestedSpanAttributes/invokedProvider`` span attribute.
221+
public struct Provider: RawRepresentable, SpanAttributeConvertible {
222+
public let rawValue: String
223+
224+
public init(rawValue: String) {
225+
self.rawValue = rawValue
226+
}
227+
228+
/// Alibaba Cloud
229+
public static let alibabaCloud = Provider(rawValue: "alibaba_cloud")
230+
231+
/// Amazon Web Services
232+
public static let aws = Provider(rawValue: "aws")
233+
234+
/// Microsoft Azure
235+
public static let azure = Provider(rawValue: "azure")
236+
237+
/// Google Cloud Platform
238+
public static let gpc = Provider(rawValue: "gpc")
239+
240+
/// Tencent Cloud
241+
public static let tencentCloud = Provider(rawValue: "tencent_cloud")
242+
243+
public func toSpanAttribute() -> SpanAttribute {
244+
.string(self.rawValue)
245+
}
246+
}
247+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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+
import TracingOpenTelemetrySemanticConventions
17+
import XCTest
18+
19+
final class FaaSSemanticsTests: XCTestCase {
20+
private var attributes = SpanAttributes()
21+
22+
override func setUp() {
23+
self.attributes = [:]
24+
}
25+
26+
func test_FaaS() {
27+
self.attributes.faaS.executionID = "42"
28+
self.attributes.faaS.isColdStart = true
29+
self.attributes.faaS.invokedFunction = "my-function"
30+
self.attributes.faaS.invokedRegion = "eu-central-1"
31+
32+
XCTAssertSpanAttributesEqual(self.attributes, [
33+
"faas.execution": "42",
34+
"faas.coldstart": true,
35+
"faas.invoked_name": "my-function",
36+
"faas.invoked_region": "eu-central-1",
37+
])
38+
}
39+
40+
func test_FaaSTrigger() {
41+
self.attributes.faaS.trigger = .dataSource
42+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.trigger": "datasource"])
43+
44+
self.attributes.faaS.trigger = .http
45+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.trigger": "http"])
46+
47+
self.attributes.faaS.trigger = .pubSub
48+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.trigger": "pubsub"])
49+
50+
self.attributes.faaS.trigger = .timer
51+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.trigger": "timer"])
52+
53+
self.attributes.faaS.trigger = .other
54+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.trigger": "other"])
55+
56+
self.attributes.faaS.trigger = .init(rawValue: "future")
57+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.trigger": "future"])
58+
}
59+
60+
func test_FaaSInvokedProvider() {
61+
self.attributes.faaS.invokedProvider = .alibabaCloud
62+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.invoked_provider": "alibaba_cloud"])
63+
64+
self.attributes.faaS.invokedProvider = .aws
65+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.invoked_provider": "aws"])
66+
67+
self.attributes.faaS.invokedProvider = .azure
68+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.invoked_provider": "azure"])
69+
70+
self.attributes.faaS.invokedProvider = .gpc
71+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.invoked_provider": "gpc"])
72+
73+
self.attributes.faaS.invokedProvider = .tencentCloud
74+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.invoked_provider": "tencent_cloud"])
75+
76+
self.attributes.faaS.invokedProvider = .init(rawValue: "future")
77+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.invoked_provider": "future"])
78+
}
79+
80+
func test_FaaSDocument() {
81+
self.attributes.faaS.document.collection = "my-bucket"
82+
self.attributes.faaS.document.time = "2020-01-23T13:47:06Z"
83+
self.attributes.faaS.document.name = "42.txt"
84+
85+
XCTAssertSpanAttributesEqual(self.attributes, [
86+
"faas.document.collection": "my-bucket",
87+
"faas.document.time": "2020-01-23T13:47:06Z",
88+
"faas.document.name": "42.txt",
89+
])
90+
}
91+
92+
func test_FaaSDocumentOperation() {
93+
self.attributes.faaS.document.operation = .insert
94+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.document.operation": "insert"])
95+
96+
self.attributes.faaS.document.operation = .edit
97+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.document.operation": "edit"])
98+
99+
self.attributes.faaS.document.operation = .delete
100+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.document.operation": "delete"])
101+
102+
self.attributes.faaS.document.operation = .init(rawValue: "future")
103+
XCTAssertSpanAttributesEqual(self.attributes, ["faas.document.operation": "future"])
104+
}
105+
106+
func test_FaaSTimer() {
107+
self.attributes.faaS.timer.time = "2020-01-23T13:47:06Z"
108+
self.attributes.faaS.timer.cron = "0/5 * * * ? *"
109+
110+
XCTAssertSpanAttributesEqual(self.attributes, [
111+
"faas.time": "2020-01-23T13:47:06Z",
112+
"faas.cron": "0/5 * * * ? *",
113+
])
114+
}
115+
}

0 commit comments

Comments
 (0)