Skip to content

Commit f134485

Browse files
author
Ignacio Bonafonte
committed
With the current implementation of Async/Await and structured concurrency, our implementation used for automatic context handling is not working, and some changes are needed.
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. Apple might change their implementation and make them compatible with our previous code, then some changes might be needed.
1 parent 426742c commit f134485

File tree

4 files changed

+92
-19
lines changed

4 files changed

+92
-19
lines changed

Sources/OpenTelemetryApi/Context/ActivityContextManager.swift

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@ private let OS_ACTIVITY_CURRENT = unsafeBitCast(dlsym(UnsafeMutableRawPointer(bi
1717
class ActivityContextManager: ContextManager {
1818
static let instance = ActivityContextManager()
1919

20+
@available(macOS 12.0, iOS 15.0, tvOS 15.0, *)
21+
static let taskLocalContextManager = TaskLocalContextManager.instance
22+
2023
let rlock = NSRecursiveLock()
2124

2225
class ScopeElement {
2326
init(scope: os_activity_scope_state_s) {
2427
self.scope = scope
2528
}
2629

27-
deinit {
28-
}
30+
deinit {}
2931

3032
var scope: os_activity_scope_state_s
3133
}
@@ -34,7 +36,13 @@ class ActivityContextManager: ContextManager {
3436

3537
var contextMap = [os_activity_id_t: [String: AnyObject]]()
3638

37-
func getCurrentContextValue(forKey key: String) -> AnyObject? {
39+
func getCurrentContextValue(forKey key: OpenTelemetryContextKeys) -> AnyObject? {
40+
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, *) {
41+
// If running with task local, use first its stored value
42+
if let contextValue = ActivityContextManager.taskLocalContextManager.getCurrentContextValue(forKey: key) {
43+
return contextValue
44+
}
45+
}
3846
var parentIdent: os_activity_id_t = 0
3947
let activityIdent = os_activity_get_identifier(OS_ACTIVITY_CURRENT, &parentIdent)
4048
var contextValue: AnyObject?
@@ -43,22 +51,26 @@ class ActivityContextManager: ContextManager {
4351
rlock.unlock()
4452
return nil
4553
}
46-
contextValue = context[key]
54+
contextValue = context[key.rawValue]
4755
rlock.unlock()
4856
return contextValue
4957
}
5058

51-
func setCurrentContextValue(forKey key: String, value: AnyObject) {
59+
func setCurrentContextValue(forKey key: OpenTelemetryContextKeys, value: AnyObject) {
5260
var parentIdent: os_activity_id_t = 0
5361
var activityIdent = os_activity_get_identifier(OS_ACTIVITY_CURRENT, &parentIdent)
5462
rlock.lock()
55-
if contextMap[activityIdent] == nil || contextMap[activityIdent]?[key] != nil {
63+
if contextMap[activityIdent] == nil || contextMap[activityIdent]?[key.rawValue] != nil {
5664
var scope: os_activity_scope_state_s
5765
(activityIdent, scope) = createActivityContext()
5866
contextMap[activityIdent] = [String: AnyObject]()
5967
objectScope.setObject(ScopeElement(scope: scope), forKey: value)
6068
}
61-
contextMap[activityIdent]?[key] = value
69+
contextMap[activityIdent]?[key.rawValue] = value
70+
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, *) {
71+
// If running with task local, set the value after the activity, so activity is not empty
72+
ActivityContextManager.taskLocalContextManager.setCurrentContextValue(forKey: key, value: value)
73+
}
6274
rlock.unlock()
6375
}
6476

@@ -71,11 +83,18 @@ class ActivityContextManager: ContextManager {
7183
return (currentActivityId, activityState)
7284
}
7385

74-
func removeContextValue(forKey key: String, value: AnyObject) {
86+
func removeContextValue(forKey key: OpenTelemetryContextKeys, value: AnyObject) {
7587
if let scope = objectScope.object(forKey: value) {
7688
var scope = scope.scope
7789
os_activity_scope_leave(&scope)
7890
objectScope.removeObject(forKey: value)
7991
}
92+
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, *) {
93+
// If there is a parent activity, set its content as the task local
94+
ActivityContextManager.taskLocalContextManager.removeContextValue(forKey: key, value: value)
95+
if let currentContext = self.getCurrentContextValue(forKey: key) {
96+
ActivityContextManager.taskLocalContextManager.setCurrentContextValue(forKey: key, value: currentContext)
97+
}
98+
}
8099
}
81100
}

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

0 commit comments

Comments
 (0)