Skip to content

Commit

Permalink
Add OTelLogHandler that sends OTelLogEntry to an OTelLogEntryProcessor
Browse files Browse the repository at this point in the history
  • Loading branch information
Joannis committed Mar 25, 2024
1 parent 34f000a commit 543a26f
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 0 deletions.
22 changes: 22 additions & 0 deletions Sources/OTel/Logging/OTLPLogEntryDataModel.swift
@@ -0,0 +1,22 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift OTel open source project
//
// Copyright (c) 2024 Moritz Lang and the Swift OTel project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Logging

@_spi(Logging)
public struct OTelLogEntry: Equatable, Sendable {
public let body: String
public let level: Logger.Level
public let metadata: Logger.Metadata?
public let timeNanosecondsSinceEpoch: UInt64
}
62 changes: 62 additions & 0 deletions Sources/OTel/Logging/OTelLogHandler.swift
@@ -0,0 +1,62 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift OTel open source project
//
// Copyright (c) 2024 Moritz Lang and the Swift OTel project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import AsyncAlgorithms
import Logging
import NIOConcurrencyHelpers
import ServiceLifecycle
import Logging
import Tracing

@_spi(Logging)
public struct OTelLogHandler: Sendable, LogHandler {
public var metadata: Logging.Logger.Metadata
public var logLevel: Logging.Logger.Level
private let processor: any OTelLogEntryProcessor

public init(
processor: any OTelLogEntryProcessor,
logLevel: Logger.Level,
metadata: Logger.Metadata = [:]
) {
self.processor = processor
self.logLevel = logLevel
self.metadata = metadata

Check warning on line 34 in Sources/OTel/Logging/OTelLogHandler.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/OTelLogHandler.swift#L31-L34

Added lines #L31 - L34 were not covered by tests
}

public subscript(metadataKey key: String) -> Logging.Logger.Metadata.Value? {
get { metadata[key] }
set { metadata[key] = newValue }

Check warning on line 39 in Sources/OTel/Logging/OTelLogHandler.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/OTelLogHandler.swift#L38-L39

Added lines #L38 - L39 were not covered by tests
}

public func log(
level: Logger.Level,
message: Logger.Message,
metadata: Logger.Metadata?,
source: String,
file: String,
function: String,
line: UInt
) {
let instant = DefaultTracerClock().now

Check warning on line 51 in Sources/OTel/Logging/OTelLogHandler.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/OTelLogHandler.swift#L50-L51

Added lines #L50 - L51 were not covered by tests

let message = OTelLogEntry(
body: message.description,
level: level,
metadata: metadata,
timeNanosecondsSinceEpoch: instant.nanosecondsSinceEpoch
)

Check warning on line 58 in Sources/OTel/Logging/OTelLogHandler.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/OTelLogHandler.swift#L53-L58

Added lines #L53 - L58 were not covered by tests

processor.onLog(message)

Check warning on line 60 in Sources/OTel/Logging/OTelLogHandler.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/OTelLogHandler.swift#L60

Added line #L60 was not covered by tests
}
}
31 changes: 31 additions & 0 deletions Sources/OTel/Logging/Processing/OTelLogEntryProcessor.swift
@@ -0,0 +1,31 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift OTel open source project
//
// Copyright (c) 2024 Moritz Lang and the Swift OTel project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import ServiceLifecycle
import ServiceContextModule

/// Log processors allow for processing logs throughout their lifetime via ``onStart(_:parentContext:)`` and ``onEnd(_:)`` calls.
/// Usually, log processors will forward logs to a configurable ``OTelLogEntryExporter``.
///
/// [OpenTelemetry specification: LogRecord processor](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/logs/sdk.md#logrecordprocessor)
///
/// ### Implementation Notes
///
/// On shutdown, processors forwarding logs to an ``OTelLogEntryExporter`` MUST shutdown that exporter.
@_spi(Logging)
public protocol OTelLogEntryProcessor: Service & Sendable {
func onLog(_ log: OTelLogEntry)

/// Force log processors that batch logs to flush immediately.
func forceFlush() async throws
}
40 changes: 40 additions & 0 deletions Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift
@@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift OTel open source project
//
// Copyright (c) 2024 Moritz Lang and the Swift OTel project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import ServiceContextModule

/// A log entry processor that ignores all operations, used when no logs should be processed.
@_spi(Logging)
public struct OTelNoOpLogEntryProcessor: OTelLogEntryProcessor, CustomStringConvertible {
public let description = "OTelNoOpLogEntryProcessor"

Check warning on line 19 in Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift#L19

Added line #L19 was not covered by tests

private let stream: AsyncStream<Void>
private let continuation: AsyncStream<Void>.Continuation

/// Initialize a no-op log entry processor.
public init() {
(stream, continuation) = AsyncStream.makeStream()

Check warning on line 26 in Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift#L25-L26

Added lines #L25 - L26 were not covered by tests
}

public func run() async {
for await _ in stream.cancelOnGracefulShutdown() {}

Check warning on line 30 in Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift#L29-L30

Added lines #L29 - L30 were not covered by tests
}

public func onLog(_ log: OTelLogEntry) {
// no-op

Check warning on line 34 in Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift#L33-L34

Added lines #L33 - L34 were not covered by tests
}

public func forceFlush() async throws {
// no-op

Check warning on line 38 in Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OTel/Logging/Processing/OTelNoOpLogEntryProcessor.swift#L37-L38

Added lines #L37 - L38 were not covered by tests
}
}

0 comments on commit 543a26f

Please sign in to comment.