Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUMM-2173 Add log level threshold option #867

Merged
merged 3 commits into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Changes

* [FEATURE] Allow filtering outgoing logs with a status threshold. See [#867][]

# 1.11.0-rc1 / 18-05-2022

### Changes
Expand Down
35 changes: 29 additions & 6 deletions Sources/Datadog/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ public class Logger {
internal var bundleWithTrace = true
internal var useFileOutput = true
internal var useConsoleLogFormat: ConsoleLogFormat?
internal var datadogReportingThreshold: LogLevel = .debug

/// Sets the service name that will appear in logs.
/// - Parameter serviceName: the service name (default value is set to application bundle identifier)
Expand Down Expand Up @@ -358,6 +359,14 @@ public class Logger {
return self
}

/// Set the minim log level reported to Datadog servers.
/// Any log with a level equal or above to the threshold will be sent
/// - Parameter datadogReportingThreshold: `LogLevel.debug` by default
public func set(datadogReportingThreshold: LogLevel) -> Builder {
self.datadogReportingThreshold = datadogReportingThreshold
return self
}

/// Format to use when printing logs to console if `printLogsToConsole(_:)` is enabled.
public enum ConsoleLogFormat {
/// Prints short representation of log.
Expand Down Expand Up @@ -437,9 +446,12 @@ public class Logger {
case (true, let format?):
let logOutput = CombinedLogOutput(
combine: [
LogFileOutput(
fileWriter: loggingFeature.storage.writer,
rumErrorsIntegration: LoggingWithRUMErrorsIntegration()
ConditionalLogOutput(
conditionedOutput: LogFileOutput(
fileWriter: loggingFeature.storage.writer,
rumErrorsIntegration: LoggingWithRUMErrorsIntegration()
),
condition: reportLogsAbove(threshold: datadogReportingThreshold)
),
LogConsoleOutput(
format: format,
Expand All @@ -449,9 +461,12 @@ public class Logger {
)
return (logBuilder, logOutput)
case (true, nil):
let logOutput = LogFileOutput(
fileWriter: loggingFeature.storage.writer,
rumErrorsIntegration: LoggingWithRUMErrorsIntegration()
let logOutput = ConditionalLogOutput(
conditionedOutput: LogFileOutput(
fileWriter: loggingFeature.storage.writer,
rumErrorsIntegration: LoggingWithRUMErrorsIntegration()
),
condition: reportLogsAbove(threshold: datadogReportingThreshold)
)
return (logBuilder, logOutput)
case (false, let format?):
Expand All @@ -470,3 +485,11 @@ public class Logger {
}
}
}

internal func reportLogsAbove(threshold: LogLevel?) -> (LogEvent) -> Bool {
return { log in
let logSeverity = LogLevel(from: log.status)?.rawValue ?? 0
let thresholdValue = threshold?.rawValue ?? .max
return logSeverity >= thresholdValue
}
}
2 changes: 1 addition & 1 deletion Sources/Datadog/Logging/Log/LogEventEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
/// All mutable properties are subject of sanitization.
public struct LogEvent: Encodable {
/// The Log event status definitions.
public enum Status: String, Encodable, CaseIterable {
public enum Status: String, Encodable, CaseIterable, Equatable {
case debug
case info
case notice
Expand Down
22 changes: 22 additions & 0 deletions Sources/DatadogObjc/Logger+objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ public enum DDSDKVerbosityLevel: Int {
case critical
}

@objc
public enum DDLogLevel: Int {
case debug
case info
case notice
case warn
case error
case critical
}

@objc
public class DDLogger: NSObject {
internal let sdkLogger: Logger
Expand Down Expand Up @@ -189,6 +199,18 @@ public class DDLoggerBuilder: NSObject {
_ = sdkBuilder.printLogsToConsole(enabled)
}

@objc
public func set(datadogReportingThreshold: DDLogLevel) {
switch datadogReportingThreshold {
case .debug: _ = sdkBuilder.set(datadogReportingThreshold: .debug)
case .info: _ = sdkBuilder.set(datadogReportingThreshold: .info)
case .notice: _ = sdkBuilder.set(datadogReportingThreshold: .notice)
case .warn: _ = sdkBuilder.set(datadogReportingThreshold: .warn)
case .error: _ = sdkBuilder.set(datadogReportingThreshold: .error)
case .critical: _ = sdkBuilder.set(datadogReportingThreshold: .critical)
}
}

@objc
public func build() -> DDLogger {
return DDLogger(sdkLogger: sdkBuilder.build())
Expand Down
36 changes: 26 additions & 10 deletions Tests/DatadogTests/Datadog/LoggerBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,13 @@ class LoggerBuilderTests: XCTestCase {

let feature = LoggingFeature.instance!
XCTAssertTrue(
logger.logOutput is LogFileOutput,
"When Logging feature is enabled the Logger should use `LogFileOutput`."
logger.logOutput is ConditionalLogOutput,
"When Logging feature is enabled the Logger should use `ConditionalLogOutput`."
)
let conditionedOutput = (logger.logOutput as! ConditionalLogOutput).conditionedOutput
XCTAssertTrue(
conditionedOutput is LogFileOutput,
"When Logging feature is enabled the Logger should use `ConditionalLogOutput` with a `LogFileOutput`."
)
let logBuilder = try XCTUnwrap(
logger.logBuilder,
Expand Down Expand Up @@ -105,8 +110,13 @@ class LoggerBuilderTests: XCTestCase {

let feature = LoggingFeature.instance!
XCTAssertTrue(
logger.logOutput is LogFileOutput,
"When Logging feature is enabled the Logger should use `LogFileOutput`."
logger.logOutput is ConditionalLogOutput,
"When Logging feature is enabled the Logger should use `ConditionalLogOutput`."
)
let conditionedOutput = (logger.logOutput as! ConditionalLogOutput).conditionedOutput
XCTAssertTrue(
conditionedOutput is LogFileOutput,
"When Logging feature is enabled the Logger should use `ConditionalLogOutput` with a `LogFileOutput`."
)
let logBuilder = try XCTUnwrap(
logger.logBuilder,
Expand All @@ -127,11 +137,13 @@ class LoggerBuilderTests: XCTestCase {

logger = Logger.builder.build()
XCTAssertNotNil(logger.logBuilder)
XCTAssertTrue(logger.logOutput is LogFileOutput)
XCTAssertTrue(logger.logOutput is ConditionalLogOutput)
XCTAssertTrue((logger.logOutput as! ConditionalLogOutput).conditionedOutput is LogFileOutput)

logger = Logger.builder.sendLogsToDatadog(true).build()
XCTAssertNotNil(logger.logBuilder)
XCTAssertTrue(logger.logOutput is LogFileOutput)
XCTAssertTrue(logger.logOutput is ConditionalLogOutput)
XCTAssertTrue((logger.logOutput as! ConditionalLogOutput).conditionedOutput is LogFileOutput)

logger = Logger.builder.sendLogsToDatadog(false).build()
XCTAssertNil(logger.logBuilder)
Expand All @@ -141,18 +153,21 @@ class LoggerBuilderTests: XCTestCase {
var combinedOutputs = try (logger.logOutput as? CombinedLogOutput).unwrapOrThrow().combinedOutputs
XCTAssertNotNil(logger.logBuilder)
XCTAssertEqual(combinedOutputs.count, 2)
XCTAssertTrue(combinedOutputs[0] is LogFileOutput)
XCTAssertTrue(combinedOutputs[0] is ConditionalLogOutput)
XCTAssertTrue((combinedOutputs[0] as! ConditionalLogOutput).conditionedOutput is LogFileOutput)
XCTAssertTrue(combinedOutputs[1] is LogConsoleOutput)

logger = Logger.builder.printLogsToConsole(false).build()
XCTAssertNotNil(logger.logBuilder)
XCTAssertTrue(logger.logOutput is LogFileOutput)
XCTAssertTrue(logger.logOutput is ConditionalLogOutput)
XCTAssertTrue((logger.logOutput as! ConditionalLogOutput).conditionedOutput is LogFileOutput)

logger = Logger.builder.sendLogsToDatadog(true).printLogsToConsole(true).build()
combinedOutputs = try (logger.logOutput as? CombinedLogOutput).unwrapOrThrow().combinedOutputs
XCTAssertNotNil(logger.logBuilder)
XCTAssertEqual(combinedOutputs.count, 2)
XCTAssertTrue(combinedOutputs[0] is LogFileOutput)
XCTAssertTrue(combinedOutputs[0] is ConditionalLogOutput)
XCTAssertTrue((combinedOutputs[0] as! ConditionalLogOutput).conditionedOutput is LogFileOutput)
XCTAssertTrue(combinedOutputs[1] is LogConsoleOutput)

logger = Logger.builder.sendLogsToDatadog(false).printLogsToConsole(true).build()
Expand All @@ -161,7 +176,8 @@ class LoggerBuilderTests: XCTestCase {

logger = Logger.builder.sendLogsToDatadog(true).printLogsToConsole(false).build()
XCTAssertNotNil(logger.logBuilder)
XCTAssertTrue(logger.logOutput is LogFileOutput)
XCTAssertTrue(logger.logOutput is ConditionalLogOutput)
XCTAssertTrue((logger.logOutput as! ConditionalLogOutput).conditionedOutput is LogFileOutput)

logger = Logger.builder.sendLogsToDatadog(false).printLogsToConsole(false).build()
XCTAssertNil(logger.logBuilder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,42 @@ class LogFileOutputTests: XCTestCase {
log2Matcher.assertStatus(equals: "warn")
log2Matcher.assertMessage(equals: "log message 2")
}

func testItFiltersUnwantedLevels() throws {
let discardedCombos: [LogLevel: [LogEvent.Status]] = [.critical: [.error, .warn, .notice, .info, .debug], .error: [.warn, .notice, .info, .debug], .warn: [.notice, .info, .debug], .notice: [.info, .debug], .info: [.debug]]
let fileCreationDateProvider = RelativeDateProvider(startingFrom: .mockDecember15th2019At10AMUTC())
let logFileName = fileNameFrom(fileCreationDate: .mockDecember15th2019At10AMUTC())

var failedCombos: [String] = []
for (thresholdLevel, logStatuses) in discardedCombos {
for (logStatus) in logStatuses {
let output = ConditionalLogOutput(
conditionedOutput: LogFileOutput(
fileWriter: FileWriter(
dataFormat: LoggingFeature.dataFormat,
orchestrator: FilesOrchestrator(
directory: temporaryDirectory,
performance: PerformancePreset.combining(
storagePerformance: .writeEachObjectToNewFileAndReadAllFiles,
uploadPerformance: .noOp
),
dateProvider: fileCreationDateProvider
)
),
rumErrorsIntegration: nil
),
condition: reportLogsAbove(threshold: thresholdLevel)
)

let log: LogEvent = .mockWith(status: logStatus, message: "Lorem ipsum dolor sit amet…")
output.write(log: log)

if temporaryDirectory.hasFile(named: logFileName) {
failedCombos.append("Did not expect to write log with status \(logStatus) (threshold was \(thresholdLevel))")
try temporaryDirectory.file(named: logFileName).delete()
}
}
}
XCTAssertEqual(failedCombos, [])
}
}
2 changes: 2 additions & 0 deletions docs/log_collection.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,15 @@ DDDatadog.verbosityLevel = DDSDKVerbosityLevelDebug;
let logger = Logger.builder
.sendNetworkInfo(true)
.printLogsToConsole(true, usingFormat: .shortWith(prefix: "[iOS App] "))
.set(datadogReportingThreshold: .info)
.build()
```
{{% /tab %}}
{{% tab "Objective-C" %}}
```objective-c
DDLoggerBuilder *builder = [DDLogger builder];
[builder sendNetworkInfo:YES];
[builder setWithDatadogReportingThreshold:.info];
[builder printLogsToConsole:YES];

DDLogger *logger = [builder build];
Expand Down