Skip to content

Commit 7395189

Browse files
author
Ignacio Bonafonte
authored
Merge pull request #230 from nachoBonafonte/TaskLocal-Context
Add support for automatic context handling when using Swift structured concurrency. Create a TaskLocal context manager that can handle async/await tasks and also use it from the ActivityContext manager, so we can mix both behaviours. Refactor a bit the classes and protocols for consistency.
2 parents c0761d2 + fe92414 commit 7395189

File tree

4 files changed

+101
-19
lines changed

4 files changed

+101
-19
lines changed

Sources/OpenTelemetryApi/Context/ActivityContextManager.swift

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ private let OS_ACTIVITY_CURRENT = unsafeBitCast(dlsym(UnsafeMutableRawPointer(bi
1616

1717
class ActivityContextManager: ContextManager {
1818
static let instance = ActivityContextManager()
19+
#if swift(>=5.5)
20+
@available(macOS 12.0, iOS 15.0, tvOS 15.0, *)
21+
static let taskLocalContextManager = TaskLocalContextManager.instance
22+
#endif
1923

2024
let rlock = NSRecursiveLock()
2125

@@ -24,8 +28,7 @@ class ActivityContextManager: ContextManager {
2428
self.scope = scope
2529
}
2630

27-
deinit {
28-
}
31+
deinit {}
2932

3033
var scope: os_activity_scope_state_s
3134
}
@@ -34,7 +37,15 @@ class ActivityContextManager: ContextManager {
3437

3538
var contextMap = [os_activity_id_t: [String: AnyObject]]()
3639

37-
func getCurrentContextValue(forKey key: String) -> AnyObject? {
40+
func getCurrentContextValue(forKey key: OpenTelemetryContextKeys) -> AnyObject? {
41+
#if swift(>=5.5)
42+
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, *) {
43+
// If running with task local, use first its stored value
44+
if let contextValue = ActivityContextManager.taskLocalContextManager.getCurrentContextValue(forKey: key) {
45+
return contextValue
46+
}
47+
}
48+
#endif
3849
var parentIdent: os_activity_id_t = 0
3950
let activityIdent = os_activity_get_identifier(OS_ACTIVITY_CURRENT, &parentIdent)
4051
var contextValue: AnyObject?
@@ -43,22 +54,28 @@ class ActivityContextManager: ContextManager {
4354
rlock.unlock()
4455
return nil
4556
}
46-
contextValue = context[key]
57+
contextValue = context[key.rawValue]
4758
rlock.unlock()
4859
return contextValue
4960
}
5061

51-
func setCurrentContextValue(forKey key: String, value: AnyObject) {
62+
func setCurrentContextValue(forKey key: OpenTelemetryContextKeys, value: AnyObject) {
5263
var parentIdent: os_activity_id_t = 0
5364
var activityIdent = os_activity_get_identifier(OS_ACTIVITY_CURRENT, &parentIdent)
5465
rlock.lock()
55-
if contextMap[activityIdent] == nil || contextMap[activityIdent]?[key] != nil {
66+
if contextMap[activityIdent] == nil || contextMap[activityIdent]?[key.rawValue] != nil {
5667
var scope: os_activity_scope_state_s
5768
(activityIdent, scope) = createActivityContext()
5869
contextMap[activityIdent] = [String: AnyObject]()
5970
objectScope.setObject(ScopeElement(scope: scope), forKey: value)
6071
}
61-
contextMap[activityIdent]?[key] = value
72+
contextMap[activityIdent]?[key.rawValue] = value
73+
#if swift(>=5.5)
74+
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, *) {
75+
// If running with task local, set the value after the activity, so activity is not empty
76+
ActivityContextManager.taskLocalContextManager.setCurrentContextValue(forKey: key, value: value)
77+
}
78+
#endif
6279
rlock.unlock()
6380
}
6481

@@ -71,11 +88,20 @@ class ActivityContextManager: ContextManager {
7188
return (currentActivityId, activityState)
7289
}
7390

74-
func removeContextValue(forKey key: String, value: AnyObject) {
91+
func removeContextValue(forKey key: OpenTelemetryContextKeys, value: AnyObject) {
7592
if let scope = objectScope.object(forKey: value) {
7693
var scope = scope.scope
7794
os_activity_scope_leave(&scope)
7895
objectScope.removeObject(forKey: value)
7996
}
97+
#if swift(>=5.5)
98+
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, *) {
99+
// If there is a parent activity, set its content as the task local
100+
ActivityContextManager.taskLocalContextManager.removeContextValue(forKey: key, value: value)
101+
if let currentContext = self.getCurrentContextValue(forKey: key) {
102+
ActivityContextManager.taskLocalContextManager.setCurrentContextValue(forKey: key, value: currentContext)
103+
}
104+
}
105+
#endif
80106
}
81107
}

Sources/OpenTelemetryApi/Context/ContextManager.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import Foundation
77

88
public protocol ContextManager: AnyObject {
9-
func getCurrentContextValue(forKey: String) -> AnyObject?
10-
func setCurrentContextValue(forKey: String, value: AnyObject)
11-
func removeContextValue(forKey: String, value: AnyObject)
9+
func getCurrentContextValue(forKey: OpenTelemetryContextKeys) -> AnyObject?
10+
func setCurrentContextValue(forKey: OpenTelemetryContextKeys, value: AnyObject)
11+
func removeContextValue(forKey: OpenTelemetryContextKeys, value: AnyObject)
1212
}

Sources/OpenTelemetryApi/Context/OpenTelemetryContextProvider.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,17 @@ import Foundation
77
import os.activity
88

99
/// Keys used by Opentelemetry to store values in the Context
10-
internal struct OpenTelemetryContextKeys {
11-
static let span = "opentelemetrycontext.span"
12-
static let baggage = "opentelemetrycontext.baggage"
10+
public enum OpenTelemetryContextKeys: String {
11+
case span
12+
case baggage
1313
}
1414

15-
1615
public struct OpenTelemetryContextProvider {
17-
1816
var contextManager: ContextManager
1917

20-
2118
/// Returns the Span from the current context
2219
public var activeSpan: Span? {
23-
return contextManager.getCurrentContextValue(forKey: OpenTelemetryContextKeys.span) as? Span
20+
return contextManager.getCurrentContextValue(forKey: .span) as? Span
2421
}
2522

2623
/// Returns the Baggage from the current context
@@ -30,7 +27,7 @@ public struct OpenTelemetryContextProvider {
3027

3128
/// Sets the span as the activeSpan for the current context
3229
/// - Parameter span: the Span to be set to the current context
33-
public func setActiveSpan(_ span: Span) {
30+
public func setActiveSpan(_ span: Span) {
3431
contextManager.setCurrentContextValue(forKey: OpenTelemetryContextKeys.span, value: span)
3532
}
3633

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import Foundation
7+
8+
#if swift(>=5.5)
9+
@available(macOS 12.0, iOS 15.0, tvOS 15.0, *)
10+
enum ContextManagement {
11+
@TaskLocal
12+
static var span: Span?
13+
public static func setSpan(wrappedSpan: TaskLocal<Span?>) {
14+
_span = wrappedSpan
15+
}
16+
17+
@TaskLocal
18+
static var baggage: Baggage?
19+
public static func setBaggage(wrappedBaggage: TaskLocal<Baggage?>) {
20+
_baggage = wrappedBaggage
21+
}
22+
}
23+
24+
@available(macOS 12.0, iOS 15.0, tvOS 15.0, *)
25+
class TaskLocalContextManager: ContextManager {
26+
static let instance = TaskLocalContextManager()
27+
28+
func getCurrentContextValue(forKey key: OpenTelemetryContextKeys) -> AnyObject? {
29+
switch key {
30+
case .span:
31+
return ContextManagement.span
32+
case .baggage:
33+
return ContextManagement.baggage
34+
}
35+
}
36+
37+
func setCurrentContextValue(forKey key: OpenTelemetryContextKeys, value: AnyObject) {
38+
switch key {
39+
case .span:
40+
if let span = value as? Span {
41+
ContextManagement.setSpan(wrappedSpan: TaskLocal(wrappedValue: span))
42+
}
43+
case .baggage:
44+
if let baggage = value as? Baggage {
45+
ContextManagement.setBaggage(wrappedBaggage: TaskLocal(wrappedValue: baggage))
46+
}
47+
}
48+
}
49+
50+
func removeContextValue(forKey key: OpenTelemetryContextKeys, value: AnyObject) {
51+
switch key {
52+
case .span:
53+
ContextManagement.setSpan(wrappedSpan: TaskLocal(wrappedValue: nil))
54+
case .baggage:
55+
ContextManagement.setBaggage(wrappedBaggage: TaskLocal(wrappedValue: nil))
56+
}
57+
}
58+
}
59+
#endif

0 commit comments

Comments
 (0)