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

Commit b363f60

Browse files
porglezompktoso
authored andcommitted
Make the @Traced macro take a TracedOperationName
**Motivation:** We want to allow richer customization of the operation name in the `@Traced` macro, in particular the difference between the different ways you can view a function name. **Modifications:** - Add a TracedOperationName type that represents the kinds of operation names we want to support. - Change the `@Traced` macro interface and expansion to use the new operation name type. **Result:** Now you can write `@Traced(.baseName)`, `@Traced(.fullName)` as well as `@Traced("custom name here")`, giving more flexibility between types of operation names.
1 parent 148582e commit b363f60

File tree

5 files changed

+184
-9
lines changed

5 files changed

+184
-9
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# ``TracingMacros/TracedOperationName``
2+
3+
### Examples
4+
5+
The default behavior is to use the base name of the function, but you can
6+
explicitly specify this as well. This creates a span named `"preheatOven"`:
7+
```swift
8+
@Traced(.baseName)
9+
func preheatOven(temperature: Int)
10+
```
11+
12+
You can request the full name of the function as the span name, this
13+
creates a span named `"preheatOven(temperature:)"`:
14+
```swift
15+
@Traced(.fullName)
16+
func preheatOven(temperature: Int)
17+
```
18+
19+
And it is also initializable with a string literal for fully custom names,
20+
this creates a span explicitly named `"preheat oven"`:
21+
```swift
22+
@Traced("preheat oven")
23+
func preheatOven(temperature: Int)
24+
```
25+
And if you need to load an existing string value as a name, you can use
26+
`.string(someString)` to adapt it.
27+
28+
29+
## Topics
30+
31+
### Create Operation Names
32+
- ``baseName``
33+
- ``fullName``
34+
- ``string(_:)``
35+
- ``init(stringLiteral:)``
36+
37+
### Convert an Operation Name to a String
38+
- ``operationName(baseName:fullName:)``

Sources/TracingMacros/Docs.docc/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ to a function and get started.
1515

1616
### Tracing functions
1717
- ``Traced(_:context:ofKind:span:)``
18+
- ``TracedOperationName``

Sources/TracingMacros/TracedMacro.swift

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,63 @@
1414
@_exported import ServiceContextModule
1515
import Tracing
1616

17+
/// A span name for a traced operation, either derived from the function name or explicitly specified.
18+
///
19+
/// When using the ``Traced(_:context:ofKind:span:)`` macro, you can use this to customize the span name.
20+
public struct TracedOperationName: ExpressibleByStringLiteral {
21+
@usableFromInline
22+
let value: Name
23+
24+
@usableFromInline
25+
enum Name {
26+
case baseName
27+
case fullName
28+
case string(String)
29+
}
30+
31+
internal init(value: Name) {
32+
self.value = value
33+
}
34+
35+
/// Use a literal string as an operation name.
36+
public init(stringLiteral: String) {
37+
value = .string(stringLiteral)
38+
}
39+
40+
/// Use the base name of the attached function.
41+
///
42+
/// For `func preheatOven(temperature: Int)` this is `"preheatOven"`.
43+
public static let baseName = TracedOperationName(value: .baseName)
44+
45+
/// Use the full name of the attached function.
46+
///
47+
/// For `func preheatOven(temperature: Int)` this is `"preheatOven(temperature:)"`.
48+
/// This is provided by the `#function` macro.
49+
public static let fullName = TracedOperationName(value: .fullName)
50+
51+
/// Use an explicitly specified operation name.
52+
public static func string(_ text: String) -> Self {
53+
.init(value: .string(text))
54+
}
55+
56+
/// Helper logic to support the `Traced` macro turning this operation name into a string.
57+
/// Provided as an inference guide.
58+
///
59+
/// - Parameters:
60+
/// - baseName: The value to use for the ``baseName`` case. Must be
61+
/// specified explicitly because there's no equivalent of `#function`.
62+
/// - fullName: The value to use for the ``fullName`` case.
63+
@inlinable
64+
@_documentation(visibility: internal)
65+
public static func _getOperationName(_ name: Self, baseName: String, fullName: String = #function) -> String {
66+
switch name.value {
67+
case .baseName: baseName
68+
case .fullName: fullName
69+
case let .string(text): text
70+
}
71+
}
72+
}
73+
1774
#if compiler(>=6.0)
1875
/// Instrument a function to place the entire body inside a span.
1976
///
@@ -26,13 +83,13 @@ import Tracing
2683
/// back to the default.
2784
///
2885
/// - Parameters:
29-
/// - operationName: The name of the operation being traced. Defaults to the name of the function.
86+
/// - operationName: The name of the operation being traced.
3087
/// - context: The `ServiceContext` providing information on where to start the new ``Span``.
3188
/// - kind: The ``SpanKind`` of the new ``Span``.
3289
/// - spanName: The name of the span variable to introduce in the function. Pass `"_"` to omit it.
3390
@attached(body)
3491
public macro Traced(
35-
_ operationName: String? = nil,
92+
_ operationName: TracedOperationName = .baseName,
3693
context: ServiceContext? = nil,
3794
ofKind kind: SpanKind? = nil,
3895
span spanName: String = "span"

Sources/TracingMacrosImplementation/TracedMacro.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,20 @@ public struct TracedMacro: BodyMacro {
3131

3232
// Construct a withSpan call matching the invocation of the @Traced macro
3333
let (operationName, context, kind, spanName) = try extractArguments(from: node)
34+
let baseNameExpr = ExprSyntax(StringLiteralExprSyntax(content: function.name.text))
3435

3536
var withSpanCall = FunctionCallExprSyntax("withSpan()" as ExprSyntax)!
36-
withSpanCall.arguments.append(
37-
LabeledExprSyntax(
38-
expression: operationName ?? ExprSyntax(StringLiteralExprSyntax(content: function.name.text))
39-
)
40-
)
37+
let operationNameExpr: ExprSyntax
38+
if let operationName {
39+
if operationName.is(StringLiteralExprSyntax.self) {
40+
operationNameExpr = operationName
41+
} else {
42+
operationNameExpr = "TracedOperationName._getOperationName(\(operationName), baseName: \(baseNameExpr))"
43+
}
44+
} else {
45+
operationNameExpr = baseNameExpr
46+
}
47+
withSpanCall.arguments.append(LabeledExprSyntax(expression: operationNameExpr))
4148
func appendComma() {
4249
withSpanCall.arguments[withSpanCall.arguments.index(before: withSpanCall.arguments.endIndex)]
4350
.trailingComma = .commaToken()

Tests/TracingMacrosTests/TracedTests.swift

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,15 +236,49 @@ final class TracedMacroTests: XCTestCase {
236236
"""
237237
let globalName = "example"
238238
239-
@Traced(globalName)
239+
@Traced(.string(globalName))
240240
func example(param: Int) {
241241
span.attributes["param"] = param
242242
}
243243
""",
244244
expandedSource: """
245245
let globalName = "example"
246246
func example(param: Int) {
247-
withSpan(globalName) { span in
247+
withSpan(TracedOperationName._getOperationName(.string(globalName), baseName: "example")) { span in
248+
span.attributes["param"] = param
249+
}
250+
}
251+
""",
252+
macros: ["Traced": TracedMacro.self]
253+
)
254+
255+
assertMacroExpansion(
256+
"""
257+
@Traced(.baseName)
258+
func useBaseName(param: Int) {
259+
span.attributes["param"] = param
260+
}
261+
""",
262+
expandedSource: """
263+
func useBaseName(param: Int) {
264+
withSpan(TracedOperationName._getOperationName(.baseName, baseName: "useBaseName")) { span in
265+
span.attributes["param"] = param
266+
}
267+
}
268+
""",
269+
macros: ["Traced": TracedMacro.self]
270+
)
271+
272+
assertMacroExpansion(
273+
"""
274+
@Traced(.fullName)
275+
func useFullName(param: Int) {
276+
span.attributes["param"] = param
277+
}
278+
""",
279+
expandedSource: """
280+
func useFullName(param: Int) {
281+
withSpan(TracedOperationName._getOperationName(.fullName, baseName: "useFullName")) { span in
248282
span.attributes["param"] = param
249283
}
250284
}
@@ -460,9 +494,47 @@ func example(param: Int) {
460494
span.attributes["param"] = param
461495
}
462496

497+
let globalName = "example"
498+
499+
@Traced(.string(globalName))
500+
func withDynamicOperationName(param: Int) {
501+
span.attributes["param"] = param
502+
}
503+
504+
@Traced(.baseName)
505+
func useBaseName(param: Int) {
506+
span.attributes["param"] = param
507+
}
508+
509+
@Traced(.fullName)
510+
func useFullName(param: Int) {
511+
span.attributes["param"] = param
512+
}
513+
463514
@Traced("custom span name", context: .topLevel, ofKind: .client, span: "customSpan")
464515
func exampleWithParams(span: Int) {
465516
customSpan.attributes["span"] = span + 1
466517
}
467518

468519
#endif
520+
521+
extension TracedMacroTests {
522+
func test_operationNameBehavior() {
523+
XCTAssertEqual(
524+
TracedOperationName._getOperationName("example custom", baseName: "test_operationNameBehavior"),
525+
"example custom"
526+
)
527+
XCTAssertEqual(
528+
TracedOperationName._getOperationName(.string("example literal"), baseName: "test_operationNameBehavior"),
529+
"example literal"
530+
)
531+
XCTAssertEqual(
532+
TracedOperationName._getOperationName(.baseName, baseName: "test_operationNameBehavior"),
533+
"test_operationNameBehavior"
534+
)
535+
XCTAssertEqual(
536+
TracedOperationName._getOperationName(.fullName, baseName: "test_operationNameBehavior"),
537+
"test_operationNameBehavior()"
538+
)
539+
}
540+
}

0 commit comments

Comments
 (0)