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-1113 Pass logged error to RUM integration #423

Merged
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
14 changes: 7 additions & 7 deletions Sources/Datadog/Core/Utils/DDError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@
import Foundation

/// Common representation of Swift `Error` used by different features.
internal struct DDError {
let title: String
internal struct DDError: Equatable {
let type: String
let message: String
let details: String
let stack: String

init(error: Error) {
if isNSErrorOrItsSubclass(error) {
let nsError = error as NSError
self.title = "\(nsError.domain) - \(nsError.code)"
self.type = "\(nsError.domain) - \(nsError.code)"
if nsError.userInfo[NSLocalizedDescriptionKey] != nil {
self.message = nsError.localizedDescription
} else {
self.message = nsError.description
}
self.details = "\(nsError)"
self.stack = "\(nsError)"
} else {
let swiftError = error
self.title = "\(type(of: swiftError))"
self.type = "\(Swift.type(of: swiftError))"
self.message = "\(swiftError)"
self.details = "\(swiftError)"
self.stack = "\(swiftError)"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ internal struct LoggingForTracingAdapter {
loggingOutput.writeLogWith(
level: level,
message: message,
// TODO: RUMM-1112
error: nil,
date: date,
attributes: LogAttributes(
userAttributes: userAttributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ internal struct LoggingWithRUMErrorsIntegration {
private let rumErrorsIntegration = RUMErrorsIntegration()

func addError(for log: Log) {
rumErrorsIntegration.addError(with: log.message, stack: nil, source: .logger)
rumErrorsIntegration.addError(
with: log.error?.message ?? log.message,
type: log.error?.type,
stack: log.error?.stack,
source: .logger
)
}
}
4 changes: 2 additions & 2 deletions Sources/Datadog/FeaturesIntegration/RUMIntegrations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal struct RUMContextIntegration {
/// Creates RUM Errors with given message.
internal struct RUMErrorsIntegration {
/// Adds RUM Error with given message and stack to current RUM View.
func addError(with message: String, stack: String?, source: RUMInternalErrorSource, attributes: [AttributeKey: AttributeValue] = [:]) {
rumMonitor?.addError(message: message, stack: stack, source: source, attributes: attributes)
func addError(with message: String, type: String?, stack: String?, source: RUMInternalErrorSource, attributes: [AttributeKey: AttributeValue] = [:]) {
rumMonitor?.addError(message: message, type: type, stack: stack, source: source, attributes: attributes)
}
}
18 changes: 1 addition & 17 deletions Sources/Datadog/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ public enum LogLevel: Int, Codable {
case critical
}

internal enum ErrorAttributes {
static let kind = "error.kind"
static let message = "error.message"
static let stack = "error.stack"
}

/// Because `Logger` is a common name widely used across different projects, the `Datadog.Logger` may conflict when
/// using `Logger.builder`. In such case, following `DDLogger` typealias can be used to avoid compiler ambiguity.
///
Expand Down Expand Up @@ -226,17 +220,6 @@ public class Logger {
let date = dateProvider.currentDate()

var combinedUserAttributes = messageAttributes ?? [:]
if let someError = error {
let ddError = DDError(error: someError)
let errorAttributes = [
ErrorAttributes.kind: ddError.title,
ErrorAttributes.message: ddError.message,
ErrorAttributes.stack: ddError.details
]
combinedUserAttributes.merge(errorAttributes) { userAttribute, _ in
return userAttribute
}
}
combinedUserAttributes = queue.sync {
return self.loggerAttributes.merging(combinedUserAttributes) { _, userAttributeValue in
return userAttributeValue // use message attribute when the same key appears also in logger attributes
Expand All @@ -260,6 +243,7 @@ public class Logger {
logOutput.writeLogWith(
level: level,
message: message,
error: error.flatMap { DDError(error: $0) },
date: date,
attributes: LogAttributes(
userAttributes: combinedUserAttributes,
Expand Down
3 changes: 2 additions & 1 deletion Sources/Datadog/Logging/Log/LogBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ internal struct LogBuilder {
/// Adjusts log's time (device time) to server time.
let dateCorrector: DateCorrectorType?

func createLogWith(level: LogLevel, message: String, date: Date, attributes: LogAttributes, tags: Set<String>) -> Log {
func createLogWith(level: LogLevel, message: String, error: DDError?, date: Date, attributes: LogAttributes, tags: Set<String>) -> Log {
return Log(
date: dateCorrector?.currentCorrection.applying(to: date) ?? date,
status: logStatus(for: level),
message: message,
error: error,
serviceName: serviceName,
environment: environment,
loggerName: loggerName,
Expand Down
14 changes: 14 additions & 0 deletions Sources/Datadog/Logging/Log/LogEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal struct Log: Encodable {
let date: Date
let status: Status
let message: String
let error: DDError?
let serviceName: String
let environment: String
let loggerName: String
Expand Down Expand Up @@ -49,6 +50,12 @@ internal struct LogEncoder {
case serviceName = "service"
case tags = "ddtags"

// MARK: - Error

case errorKind = "error.kind"
case errorMessage = "error.message"
case errorStack = "error.stack"

// MARK: - Application info

case applicationVersion = "version"
Expand Down Expand Up @@ -98,6 +105,13 @@ internal struct LogEncoder {
try container.encode(log.message, forKey: .message)
try container.encode(log.serviceName, forKey: .serviceName)

// Encode log.error properties
if let someError = log.error {
try container.encode(someError.type, forKey: .errorKind)
try container.encode(someError.message, forKey: .errorMessage)
try container.encode(someError.stack, forKey: .errorStack)
}

// Encode logger info
try container.encode(log.loggerName, forKey: .loggerName)
try container.encode(log.loggerVersion, forKey: .loggerVersion)
Expand Down
4 changes: 2 additions & 2 deletions Sources/Datadog/Logging/LogOutputs/LogConsoleOutput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ internal struct LogConsoleOutput: LogOutput {
self.printingFunction = printingFunction
}

func writeLogWith(level: LogLevel, message: String, date: Date, attributes: LogAttributes, tags: Set<String>) {
let log = logBuilder.createLogWith(level: level, message: message, date: date, attributes: attributes, tags: tags)
func writeLogWith(level: LogLevel, message: String, error: DDError?, date: Date, attributes: LogAttributes, tags: Set<String>) {
let log = logBuilder.createLogWith(level: level, message: message, error: error, date: date, attributes: attributes, tags: tags)
printingFunction(formatter.format(log: log))
}
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/Datadog/Logging/LogOutputs/LogFileOutput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ internal struct LogFileOutput: LogOutput {
/// Integration with RUM Errors.
let rumErrorsIntegration: LoggingWithRUMErrorsIntegration?

func writeLogWith(level: LogLevel, message: String, date: Date, attributes: LogAttributes, tags: Set<String>) {
let log = logBuilder.createLogWith(level: level, message: message, date: date, attributes: attributes, tags: tags)
func writeLogWith(level: LogLevel, message: String, error: DDError?, date: Date, attributes: LogAttributes, tags: Set<String>) {
let log = logBuilder.createLogWith(level: level, message: message, error: error, date: date, attributes: attributes, tags: tags)
fileWriter.write(value: log)

if level.rawValue >= LogLevel.error.rawValue {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Datadog/Logging/LogOutputs/LogOutput.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ internal struct LogAttributes {

/// Type writting logs to some destination.
internal protocol LogOutput {
func writeLogWith(level: LogLevel, message: String, date: Date, attributes: LogAttributes, tags: Set<String>)
func writeLogWith(level: LogLevel, message: String, error: DDError?, date: Date, attributes: LogAttributes, tags: Set<String>)
}
10 changes: 5 additions & 5 deletions Sources/Datadog/Logging/LogOutputs/LogUtilityOutputs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Foundation

/// `LogOutput` which does nothing.
internal struct NoOpLogOutput: LogOutput {
func writeLogWith(level: LogLevel, message: String, date: Date, attributes: LogAttributes, tags: Set<String>) {}
func writeLogWith(level: LogLevel, message: String, error: DDError?, date: Date, attributes: LogAttributes, tags: Set<String>) {}
}

/// Combines one or more `LogOutputs` into one.
Expand All @@ -19,18 +19,18 @@ internal struct CombinedLogOutput: LogOutput {
self.combinedOutputs = outputs
}

func writeLogWith(level: LogLevel, message: String, date: Date, attributes: LogAttributes, tags: Set<String>) {
combinedOutputs.forEach { $0.writeLogWith(level: level, message: message, date: date, attributes: attributes, tags: tags) }
func writeLogWith(level: LogLevel, message: String, error: DDError?, date: Date, attributes: LogAttributes, tags: Set<String>) {
combinedOutputs.forEach { $0.writeLogWith(level: level, message: message, error: error, date: date, attributes: attributes, tags: tags) }
}
}

internal struct ConditionalLogOutput: LogOutput {
let conditionedOutput: LogOutput
let condition: (LogLevel) -> Bool

func writeLogWith(level: LogLevel, message: String, date: Date, attributes: LogAttributes, tags: Set<String>) {
func writeLogWith(level: LogLevel, message: String, error: DDError?, date: Date, attributes: LogAttributes, tags: Set<String>) {
if condition(level) {
conditionedOutput.writeLogWith(level: level, message: message, date: date, attributes: attributes, tags: tags)
conditionedOutput.writeLogWith(level: level, message: message, error: error, date: date, attributes: attributes, tags: tags)
}
}
}
4 changes: 2 additions & 2 deletions Sources/Datadog/OpenTracing/OTSpan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ public extension OTSpan {
) {
let dderror = DDError(error: error)
setError(
kind: dderror.title,
kind: dderror.type,
message: dderror.message,
stack: dderror.details,
stack: dderror.stack,
file: file,
line: line
)
Expand Down
22 changes: 16 additions & 6 deletions Sources/Datadog/RUM/RUMMonitor/RUMCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,17 @@ internal struct RUMAddCurrentViewErrorCommand: RUMCommand {

/// The error message.
let message: String
/// The origin of this error.
let source: RUMInternalErrorSource
/// Error type.
let type: String?
/// Error stacktrace.
let stack: String?
/// The origin of this error.
let source: RUMInternalErrorSource

init(
time: Date,
message: String,
type: String?,
stack: String?,
source: RUMInternalErrorSource,
attributes: [AttributeKey: AttributeValue]
Expand All @@ -79,6 +82,7 @@ internal struct RUMAddCurrentViewErrorCommand: RUMCommand {
self.source = source
self.attributes = attributes
self.message = message
self.type = type
self.stack = stack
}

Expand All @@ -93,8 +97,9 @@ internal struct RUMAddCurrentViewErrorCommand: RUMCommand {
self.attributes = attributes

let dderror = DDError(error: error)
self.message = dderror.title
self.stack = dderror.details
self.message = dderror.message
self.type = dderror.type
self.stack = dderror.stack
}
}

Expand Down Expand Up @@ -167,6 +172,8 @@ internal struct RUMStopResourceWithErrorCommand: RUMResourceCommand {

/// The error message.
let errorMessage: String
/// Error type.
let errorType: String?
/// The origin of the error (network, webview, ...)
let errorSource: RUMInternalErrorSource
/// Error stacktrace.
Expand All @@ -178,13 +185,15 @@ internal struct RUMStopResourceWithErrorCommand: RUMResourceCommand {
resourceKey: String,
time: Date,
message: String,
type: String?,
source: RUMInternalErrorSource,
httpStatusCode: Int?,
attributes: [AttributeKey: AttributeValue]
) {
self.resourceKey = resourceKey
self.time = time
self.errorMessage = message
self.errorType = type
self.errorSource = source
self.attributes = attributes
self.httpStatusCode = httpStatusCode
Expand All @@ -207,9 +216,10 @@ internal struct RUMStopResourceWithErrorCommand: RUMResourceCommand {
self.httpStatusCode = httpStatusCode

let dderror = DDError(error: error)
self.errorMessage = dderror.title
self.errorMessage = dderror.message
self.errorType = dderror.type
// The stack will give the networking error (`NSError`) description in most cases:
self.stack = dderror.details
self.stack = dderror.stack
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ internal class RUMResourceScope: RUMScope {
),
source: command.errorSource.toRUMDataFormat,
stack: command.stack,
type: command.errorMessage
type: command.errorType
),
service: nil,
session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user),
Expand Down
2 changes: 1 addition & 1 deletion Sources/Datadog/RUM/RUMMonitor/Scopes/RUMViewScope.swift
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ internal class RUMViewScope: RUMScope, RUMContextProvider {
resource: nil,
source: command.source.toRUMDataFormat,
stack: command.stack,
type: command.message
type: command.type
),
service: nil,
session: .init(hasReplay: nil, id: context.sessionID.toRUMDataFormat, type: .user),
Expand Down
5 changes: 4 additions & 1 deletion Sources/Datadog/RUMMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -307,11 +307,12 @@ public class RUMMonitor: DDRUMMonitor, RUMCommandSubscriber {
}
return nil
}()
addError(message: message, stack: stack, source: RUMInternalErrorSource(source), attributes: attributes)
addError(message: message, type: nil, stack: stack, source: RUMInternalErrorSource(source), attributes: attributes)
}

internal func addError(
message: String,
type: String?,
stack: String?,
source: RUMInternalErrorSource,
attributes: [AttributeKey: AttributeValue]
Expand All @@ -320,6 +321,7 @@ public class RUMMonitor: DDRUMMonitor, RUMCommandSubscriber {
command: RUMAddCurrentViewErrorCommand(
time: dateProvider.currentDate(),
message: message,
type: type,
stack: stack,
source: source,
attributes: attributes
Expand Down Expand Up @@ -491,6 +493,7 @@ public class RUMMonitor: DDRUMMonitor, RUMCommandSubscriber {
resourceKey: resourceKey,
time: dateProvider.currentDate(),
message: errorMessage,
type: nil,
source: .network,
httpStatusCode: (response as? HTTPURLResponse)?.statusCode,
attributes: attributes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class LoggingStorageBenchmarkTests: XCTestCase {
date: Date(),
status: .info,
message: "message \(Int.random(in: 0..<100))",
error: DDError(error: ErrorMock("description")),
serviceName: "service-name",
environment: "benchmarks",
loggerName: "logger-name",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ class RUMManualInstrumentationScenarioTests: IntegrationTests, RUMCommonAsserts
XCTAssertEqual(view1.resourceEvents[0].resource.type, .image)
XCTAssertGreaterThan(view1.resourceEvents[0].resource.duration, 100_000_000 - 1) // ~0.1s
XCTAssertLessThan(view1.resourceEvents[0].resource.duration, 100_000_000 * 3) // less than 0.3s
XCTAssertEqual(view1.errorEvents[0].error.message, "NSURLErrorDomain - -1011")
XCTAssertEqual(view1.errorEvents[0].error.type, "NSURLErrorDomain - -1011")
XCTAssertEqual(view1.errorEvents[0].error.message, "Bad response.")
XCTAssertEqual(
view1.errorEvents[0].error.stack,
#"Error Domain=NSURLErrorDomain Code=-1011 "Bad response." UserInfo={NSLocalizedDescription=Bad response.}"#
Expand Down
Loading