-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial: Telemetry/Tracing interface (#34)
- Loading branch information
1 parent
734c836
commit 3398413
Showing
10 changed files
with
298 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Decide package open source project | ||
// | ||
// Copyright (c) 2020-2023 Maxim Bazarov and the Decide package | ||
// open source project authors | ||
// Licensed under MIT | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
|
||
/// While enabling extreme modularity by decoupling components, | ||
/// we must not loose the context of execution while debugging | ||
/// or dealing with incidents. | ||
/// | ||
/// Decide guaranties that every state mutation whether it's structured | ||
/// or unstructured can be traced to the point of changes origin. | ||
/// | ||
/// `Context` represents this origin of the change by storing the information about | ||
/// the point of execution e.g. class or function | ||
/// as well as the location in the source code. | ||
/// | ||
public final class Context: Sendable { | ||
|
||
/// The file path where the execution happened. | ||
public let file: String | ||
|
||
/// The line number where the execution happened. | ||
public let line: Int | ||
|
||
public init(file: String = #fileID, line: Int = #line) { | ||
self.file = file | ||
self.line = line | ||
} | ||
} | ||
|
||
extension Context: CustomDebugStringConvertible { | ||
public var debugDescription: String { | ||
"\(file):\(line)" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Decide package open source project | ||
// | ||
// Copyright (c) 2020-2023 Maxim Bazarov and the Decide package | ||
// open source project authors | ||
// Licensed under MIT | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import OSLog | ||
|
||
final class UnstructuredMutation<V>: TelemetryEvent { | ||
let category: String = "Unstructured State Mutation" | ||
let name: String = "Property updated:" | ||
let logLevel: OSLogType = .debug | ||
let context: Decide.Context | ||
|
||
let keyPath: String | ||
let value: V | ||
|
||
init(context: Decide.Context, keyPath: String, value: V) { | ||
self.keyPath = keyPath | ||
self.context = context | ||
self.value = value | ||
} | ||
|
||
func message() -> String { | ||
"\(keyPath) -> \(String(reflecting: value))" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Decide package open source project | ||
// | ||
// Copyright (c) 2020-2023 Maxim Bazarov and the Decide package | ||
// open source project authors | ||
// Licensed under MIT | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import OSLog | ||
|
||
final class OSLogTelemetryObserver: TelemetryObserver { | ||
|
||
static let subsystem = "State and Side-effects (Decide)" | ||
|
||
static let unsafeTracingEnabled: Bool = { | ||
guard let config: String = ProcessInfo | ||
.processInfo | ||
.environment["DECIDE_UNSAFE_TRACING_ENABLED"] | ||
else { | ||
return false | ||
} | ||
|
||
if let intValue = Int(config) { | ||
return intValue == 1 ? true : false | ||
} | ||
|
||
if config.replacingOccurrences(of: " ", with: "").lowercased() == "true" { | ||
return true | ||
} | ||
|
||
if config.replacingOccurrences(of: " ", with: "").lowercased() == "yes" { | ||
return true | ||
} | ||
|
||
return false | ||
}() | ||
|
||
func eventDidOccur<E>(_ event: E) where E : TelemetryEvent { | ||
let logger = Logger(subsystem: Self.subsystem, category: event.category) | ||
if Self.unsafeTracingEnabled { | ||
unsafeTrace(event: event, logger: logger) | ||
} else { | ||
trace(event: event, logger: logger) | ||
} | ||
|
||
} | ||
|
||
func trace<E>(event: E, logger: Logger) where E : TelemetryEvent { | ||
switch event.logLevel { | ||
case .debug: | ||
logger.debug("\(event.name): \(event.message(), privacy: .sensitive)\n context: \(event.context.debugDescription)") | ||
case .info: | ||
logger.info("\(event.message(), privacy: .sensitive)") | ||
case .error: | ||
logger.error("\(event.message(), privacy: .sensitive)") | ||
case .fault: | ||
logger.fault("\(event.message(), privacy: .sensitive)") | ||
default: | ||
logger.log("\(event.message(), privacy: .sensitive)") | ||
} | ||
} | ||
|
||
func unsafeTrace<E>(event: E, logger: Logger) where E : TelemetryEvent { | ||
switch event.logLevel { | ||
case .debug: | ||
logger.debug("\(event.name): \(event.message(), privacy: .sensitive)\n context: \(event.context.debugDescription)") | ||
case .info: | ||
logger.info("\(event.message(), privacy: .public)") | ||
case .error: | ||
logger.error("\(event.message(), privacy: .public)") | ||
case .fault: | ||
logger.fault("\(event.message(), privacy: .public)") | ||
default: | ||
logger.log("\(event.message(), privacy: .public)") | ||
} | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the Decide package open source project | ||
// | ||
// Copyright (c) 2020-2023 Maxim Bazarov and the Decide package | ||
// open source project authors | ||
// Licensed under MIT | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
import OSLog | ||
|
||
final class Telemetry { | ||
|
||
/// Minimum log level to log. | ||
/// [Choose the Appropriate Log Level for Each Message](https://developer.apple.com/documentation/os/logging/generating_log_messages_from_your_code#3665947) | ||
var logLevel: OSLogType = .default | ||
|
||
let observer: TelemetryObserver | ||
|
||
init(observer: TelemetryObserver) { | ||
self.observer = observer | ||
} | ||
|
||
func log<E: TelemetryEvent>(event: E) { | ||
guard event.logLevel.rawValue >= self.logLevel.rawValue | ||
else { return } | ||
observer.eventDidOccur(event) | ||
} | ||
} | ||
|
||
final class DoNotObserve: TelemetryObserver { | ||
func eventDidOccur<E>(_ event: E) where E : TelemetryEvent {} | ||
} | ||
extension Telemetry { | ||
static let noTelemetry = Telemetry(observer: DoNotObserve()) | ||
} | ||
|
||
/// [Choose the Appropriate Log Level for Each Message](https://developer.apple.com/documentation/os/logging/generating_log_messages_from_your_code#3665947) | ||
protocol TelemetryEvent { | ||
var category: String { get } | ||
var name: String { get } | ||
var context: Context { get } | ||
var logLevel: OSLogType { get } | ||
|
||
func message() -> String | ||
} | ||
|
||
protocol TelemetryObserver { | ||
/// Called every time an event with debug level | ||
/// equal or greater than current occur. | ||
func eventDidOccur<E: TelemetryEvent>(_ event: E) | ||
} | ||
|
Oops, something went wrong.