From e49c5223c32159da93477b8984934579649d9772 Mon Sep 17 00:00:00 2001 From: Ignacio Tischelman Date: Fri, 26 Jul 2024 18:15:31 -0300 Subject: [PATCH 1/5] WIP --- .../xcschemes/EmbraceIO-Package.xcscheme | 24 +++++++++ Package.swift | 16 +++++- .../LogType/LogType+Declarations.swift | 2 - .../LogType/LogType.swift | 6 +-- .../SpanType/SpanType+Declarations.swift | 20 ------- .../SpanType/SpanType.swift | 6 +-- .../Network/URLSessionTaskHandler.swift | 52 +++++++++--------- .../LowMemoryWarningCaptureService.swift | 9 +++- .../LowPowerModeCaptureService.swift | 9 +++- .../Capture/UX/Tap/TapCaptureService.swift | 16 ++---- .../View/ViewCaptureService+didAppear.swift | 10 ++-- .../WebView/WebViewCaptureService.swift | 13 +++-- .../Logs/EmbraceLogAttributesBuilder.swift | 31 ++++------- .../Exporter/StorageEmbraceLogExporter.swift | 3 +- .../Validators/LengthOfNameValidator.swift | 3 +- .../Payload/Builders/LogPayloadBuilder.swift | 3 +- .../Payload/Spans/SpanPayload.swift | 3 +- .../Public/Events/Breadcrumb.swift | 13 ++--- .../PushNotificationEvent.swift | 41 ++++++-------- .../Session/SessionSpanUtils.swift | 53 +++++++------------ Sources/EmbraceOTelInternal/EmbraceOTel.swift | 13 +++-- .../EmbraceSemantics/Span/Span+Embrace.swift | 15 +++--- .../Span/SpanAttributeKey.swift | 15 ------ .../Span/SpanData+Embrace.swift | 5 +- .../SpanBuilder/SpanBuilder+Embrace.swift | 11 ++-- .../Logs/LogSemantics+Crash.swift | 11 ++++ .../EmbraceSemantics/Logs/LogSemantics.swift | 18 +++++++ .../Span/SpanSemantics+LowPower.swift | 18 +++++++ .../Span/SpanSemantics+NetworkRequest.swift | 24 +++++++++ .../Span/SpanSemantics+Session.swift | 24 +++++++++ .../Span/SpanSemantics+View.swift | 17 ++++++ .../EmbraceSemantics/Span/SpanSemantics.swift | 14 +++++ .../SpanEventSemantics+Breadcrumb.swift | 16 ++++++ .../SpanEventSemantics+LowMemory.swift | 14 +++++ .../SpanEventSemantics+PushNotification.swift | 25 +++++++++ .../SpanEvent/SpanEventSemantics+Tap.swift | 17 ++++++ .../SpanEventSemantics+WebView.swift | 17 ++++++ .../SpanEvent/SpanEventSemantics.swift | 7 +++ .../Records/EmbraceStorage+Span.swift | 1 + 39 files changed, 413 insertions(+), 202 deletions(-) delete mode 100644 Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanAttributeKey.swift create mode 100644 Sources/EmbraceSemantics/Logs/LogSemantics+Crash.swift create mode 100644 Sources/EmbraceSemantics/Logs/LogSemantics.swift create mode 100644 Sources/EmbraceSemantics/Span/SpanSemantics+LowPower.swift create mode 100644 Sources/EmbraceSemantics/Span/SpanSemantics+NetworkRequest.swift create mode 100644 Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift create mode 100644 Sources/EmbraceSemantics/Span/SpanSemantics+View.swift create mode 100644 Sources/EmbraceSemantics/Span/SpanSemantics.swift create mode 100644 Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Breadcrumb.swift create mode 100644 Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+LowMemory.swift create mode 100644 Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+PushNotification.swift create mode 100644 Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Tap.swift create mode 100644 Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+WebView.swift create mode 100644 Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics.swift diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme index cb935d3c..a864ed4d 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme @@ -412,6 +412,20 @@ ReferencedContainer = "container:"> + + + + + + + + Self { - guard attributes[Keys.type] == nil else { + guard attributes[LogSemantics.keyEmbraceType] == nil else { return self } - attributes[Keys.type] = logType.rawValue + attributes[LogSemantics.keyEmbraceType] = logType.rawValue return self } @@ -87,7 +76,7 @@ class EmbraceLogAttributesBuilder { } if let value = record.stringValue { - let key = String(format: Keys.propertiesPrefix, record.key) + let key = String(format: LogSemantics.keyPropertiesPrefix, record.key) attributes[key] = value } } @@ -100,7 +89,7 @@ class EmbraceLogAttributesBuilder { guard let state = currentSession?.state else { return self } - attributes[Keys.state] = state + attributes[LogSemantics.keyState] = state return self } @@ -109,7 +98,7 @@ class EmbraceLogAttributesBuilder { guard let sessionId = currentSession?.id else { return self } - attributes[Keys.sessionId] = sessionId.toString + attributes[LogSemantics.keySessionId] = sessionId.toString return self } @@ -119,9 +108,9 @@ class EmbraceLogAttributesBuilder { return self } - attributes[Keys.crashId] = crashReport.id.withoutHyphen - attributes[Keys.crashProvider] = crashReport.provider - attributes[Keys.crashPayload] = crashReport.payload + attributes[LogSemantics.Crash.keyId] = crashReport.id.withoutHyphen + attributes[LogSemantics.Crash.keyProvider] = crashReport.provider + attributes[LogSemantics.Crash.keyPayload] = crashReport.payload return self } diff --git a/Sources/EmbraceCore/Internal/Logs/Exporter/StorageEmbraceLogExporter.swift b/Sources/EmbraceCore/Internal/Logs/Exporter/StorageEmbraceLogExporter.swift index 9a8831ce..97c7329b 100644 --- a/Sources/EmbraceCore/Internal/Logs/Exporter/StorageEmbraceLogExporter.swift +++ b/Sources/EmbraceCore/Internal/Logs/Exporter/StorageEmbraceLogExporter.swift @@ -6,6 +6,7 @@ import Foundation import EmbraceCommonInternal import EmbraceOTelInternal import EmbraceStorageInternal +import EmbraceSemantics class StorageEmbraceLogExporter: EmbraceLogRecordExporter { @ThreadSafe @@ -32,7 +33,7 @@ class StorageEmbraceLogExporter: EmbraceLogRecordExporter { for var log in logRecords where validation.execute(log: &log) { // do not export crash logs - guard log.attributes["emb.type"] != .string(LogType.crash.rawValue) else { + guard log.attributes[LogSemantics.keyEmbraceType] != .string(LogType.crash.rawValue) else { continue } diff --git a/Sources/EmbraceCore/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidator.swift b/Sources/EmbraceCore/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidator.swift index 5c5b1acd..fbcb8799 100644 --- a/Sources/EmbraceCore/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidator.swift +++ b/Sources/EmbraceCore/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidator.swift @@ -3,6 +3,7 @@ // import EmbraceOTelInternal +import EmbraceSemantics /// Validates the length of ``SpanData.name``. /// This compares the length of the String in characters, not bytes. @@ -22,6 +23,6 @@ class LengthOfNameValidator: SpanDataValidator { } private func shouldValidate(data: SpanData) -> Bool { - return data.embType != .networkHTTP + return data.embType != .networkRequest } } diff --git a/Sources/EmbraceCore/Payload/Builders/LogPayloadBuilder.swift b/Sources/EmbraceCore/Payload/Builders/LogPayloadBuilder.swift index 984ba3d4..b3243524 100644 --- a/Sources/EmbraceCore/Payload/Builders/LogPayloadBuilder.swift +++ b/Sources/EmbraceCore/Payload/Builders/LogPayloadBuilder.swift @@ -5,6 +5,7 @@ import Foundation import EmbraceStorageInternal import EmbraceCommonInternal +import EmbraceSemantics struct LogPayloadBuilder { static func build(log: LogRecord) -> LogPayload { @@ -12,7 +13,7 @@ struct LogPayloadBuilder { Attribute(key: entry.key, value: entry.value.description) } - finalAttributes.append(.init(key: "log.record.uid", value: log.identifier.toString)) + finalAttributes.append(.init(key: LogSemantics.keyId, value: log.identifier.toString)) return .init(timeUnixNano: String(Int(log.timestamp.nanosecondsSince1970)), severityNumber: log.severity.number, diff --git a/Sources/EmbraceCore/Payload/Spans/SpanPayload.swift b/Sources/EmbraceCore/Payload/Spans/SpanPayload.swift index 876577ac..388164bf 100644 --- a/Sources/EmbraceCore/Payload/Spans/SpanPayload.swift +++ b/Sources/EmbraceCore/Payload/Spans/SpanPayload.swift @@ -4,6 +4,7 @@ import Foundation import EmbraceOTelInternal +import EmbraceSemantics struct SpanPayload: Encodable { let traceId: String @@ -50,7 +51,7 @@ struct SpanPayload: Encodable { var attributeArray = PayloadUtils.convertSpanAttributes(span.attributes) if failed { - attributeArray.append(Attribute(key: "emb.error_code", value: "failure")) + attributeArray.append(Attribute(key: SpanSemantics.keyErrorCode, value: "failure")) } self.attributes = attributeArray } diff --git a/Sources/EmbraceCore/Public/Events/Breadcrumb.swift b/Sources/EmbraceCore/Public/Events/Breadcrumb.swift index 5ecdeaa0..71e461af 100644 --- a/Sources/EmbraceCore/Public/Events/Breadcrumb.swift +++ b/Sources/EmbraceCore/Public/Events/Breadcrumb.swift @@ -5,6 +5,7 @@ import Foundation import EmbraceOTelInternal import EmbraceCommonInternal +import EmbraceSemantics /// Class used to represent a Breadcrum as a SpanEvent. /// Usage example: @@ -15,22 +16,16 @@ public class Breadcrumb: NSObject, SpanEvent { public let timestamp: Date public private(set) var attributes: [String: AttributeValue] - private enum Constants { - static let name = "emb-breadcrumb" - static let messageKey = "message" - static let typeKey = "emb.type" - } - init( message: String, timestamp: Date = Date(), attributes: [String: AttributeValue] ) { - self.name = Constants.name + self.name = SpanEventSemantics.Bradcrumb.name self.timestamp = timestamp self.attributes = attributes - self.attributes[Constants.messageKey] = .string(message) - self.attributes[Constants.typeKey] = .string(LogType.breadcrumb.rawValue) + self.attributes[SpanEventSemantics.Bradcrumb.keyMessage] = .string(message) + self.attributes[SpanEventSemantics.keyEmbraceType] = .string(SpanType.breadcrumb.rawValue) } } diff --git a/Sources/EmbraceCore/Public/PushNotifications/PushNotificationEvent.swift b/Sources/EmbraceCore/Public/PushNotifications/PushNotificationEvent.swift index d9fedf31..a4930104 100644 --- a/Sources/EmbraceCore/Public/PushNotifications/PushNotificationEvent.swift +++ b/Sources/EmbraceCore/Public/PushNotifications/PushNotificationEvent.swift @@ -2,9 +2,11 @@ // Copyright © 2024 Embrace Mobile, Inc. All rights reserved. // -import EmbraceOTelInternal import Foundation import UserNotifications +import EmbraceOTelInternal +import EmbraceCommonInternal +import EmbraceSemantics /// Class used to represent a Push Notification as a SpanEvent. /// Usage example: @@ -44,11 +46,11 @@ public class PushNotificationEvent: NSObject, SpanEvent { ) throws { // find aps key - guard let apsDict = userInfo[Constants.rootKey] as? [AnyHashable: Any] else { + guard let apsDict = userInfo[Constants.apsRootKey] as? [AnyHashable: Any] else { throw PushNotificationError.invalidPayload("Couldn't find aps object!") } - self.name = Constants.eventName + self.name = SpanEventSemantics.PushNotification.name self.timestamp = timestamp let dict = Self.parse(apsDict: apsDict, captureData: captureData) @@ -60,11 +62,11 @@ public class PushNotificationEvent: NSObject, SpanEvent { var dict: [String: AttributeValue] = [:] // set types - dict["emb.type"] = .string(Constants.eventType) - dict[Constants.keyType] = + dict[SpanEventSemantics.keyEmbraceType] = .string(SpanType.pushNotification.rawValue) + dict[SpanEventSemantics.PushNotification.keyType] = isSilent(userInfo: apsDict) ? - .string(Constants.silentType) : - .string(Constants.notificationType) + .string(SpanEventSemantics.PushNotification.silentType) : + .string(SpanEventSemantics.PushNotification.notificationType) // capture data if enabled if captureData { @@ -87,23 +89,23 @@ public class PushNotificationEvent: NSObject, SpanEvent { let badge = apsDict[Constants.apsBadge] as? Int if let title = title { - dict[Constants.keyTitle] = .string(title) + dict[SpanEventSemantics.PushNotification.keyTitle] = .string(title) } if let subtitle = subtitle { - dict[Constants.keySubtitle] = .string(subtitle) + dict[SpanEventSemantics.PushNotification.keySubtitle] = .string(subtitle) } if let body = body { - dict[Constants.keyBody] = .string(body) + dict[SpanEventSemantics.PushNotification.keyBody] = .string(body) } if let category = category { - dict[Constants.keyCategory] = .string(category) + dict[SpanEventSemantics.PushNotification.keyCategory] = .string(category) } if let badge = badge { - dict[Constants.keyBadge] = .int(badge) + dict[SpanEventSemantics.PushNotification.keyBadge] = .int(badge) } } @@ -120,13 +122,7 @@ public class PushNotificationEvent: NSObject, SpanEvent { } struct Constants { - static let rootKey = "aps" - static let eventName = "emb-push-notification" - static let eventType = "sys.push_notification" - - static let notificationType = "notif" - static let silentType = "silent" - + static let apsRootKey = "aps" static let apsAlert = "alert" static let apsTitle = "title" static let apsTitleLocalized = "title-loc-key" @@ -137,13 +133,6 @@ public class PushNotificationEvent: NSObject, SpanEvent { static let apsCategory = "category" static let apsBadge = "badge" static let apsContentAvailable = "content-available" - - static let keyType = "notification.type" - static let keyTitle = "notification.title" - static let keySubtitle = "notification.subtitle" - static let keyBody = "notification.body" - static let keyCategory = "notification.category" - static let keyBadge = "notification.badge" } } diff --git a/Sources/EmbraceCore/Session/SessionSpanUtils.swift b/Sources/EmbraceCore/Session/SessionSpanUtils.swift index a94b826e..b7078d97 100644 --- a/Sources/EmbraceCore/Session/SessionSpanUtils.swift +++ b/Sources/EmbraceCore/Session/SessionSpanUtils.swift @@ -6,47 +6,34 @@ import Foundation import EmbraceCommonInternal import EmbraceStorageInternal import EmbraceOTelInternal +import EmbraceSemantics import OpenTelemetryApi struct SessionSpanUtils { - static let spanName = "emb-session" - static let spanType = "ux.session" - - enum AttributeKey: String { - case type = "emb.type" - case id = "emb.session_id" - case state = "emb.state" - case coldStart = "emb.cold_start" - case terminated = "emb.terminated" - case cleanExit = "emb.clean_exit" - case sessionNumber = "emb.session_number" - case heartbeat = "emb.heartbeat_time_unix_nano" - case crashId = "emb.crash_id" - } static func span(id: SessionIdentifier, startTime: Date, state: SessionState, coldStart: Bool) -> Span { - EmbraceOTel().buildSpan(name: spanName, type: .session) + EmbraceOTel().buildSpan(name: SpanSemantics.Session.name, type: .session) .setStartTime(time: startTime) - .setAttribute(key: AttributeKey.id.rawValue, value: id.toString) - .setAttribute(key: AttributeKey.state.rawValue, value: state.rawValue) - .setAttribute(key: AttributeKey.coldStart.rawValue, value: coldStart) + .setAttribute(key: SpanSemantics.Session.keyId, value: id.toString) + .setAttribute(key: SpanSemantics.Session.keyState, value: state.rawValue) + .setAttribute(key: SpanSemantics.Session.keyColdStart, value: coldStart) .startSpan() } static func setState(span: Span?, state: SessionState) { - span?.setAttribute(key: AttributeKey.state.rawValue, value: state.rawValue) + span?.setAttribute(key: SpanSemantics.Session.keyState, value: state.rawValue) } static func setHeartbeat(span: Span?, heartbeat: Date) { - span?.setAttribute(key: AttributeKey.heartbeat.rawValue, value: heartbeat.nanosecondsSince1970Truncated) + span?.setAttribute(key: SpanSemantics.Session.keyHeartbeat, value: heartbeat.nanosecondsSince1970Truncated) } static func setTerminated(span: Span?, terminated: Bool) { - span?.setAttribute(key: AttributeKey.terminated.rawValue, value: terminated) + span?.setAttribute(key: SpanSemantics.Session.keyTerminated, value: terminated) } static func setCleanExit(span: Span?, cleanExit: Bool) { - span?.setAttribute(key: AttributeKey.cleanExit.rawValue, value: cleanExit) + span?.setAttribute(key: SpanSemantics.Session.keyCleanExit, value: cleanExit) } static func payload( @@ -63,7 +50,7 @@ fileprivate extension SpanPayload { self.traceId = session.traceId self.spanId = session.spanId self.parentSpanId = nil - self.name = SessionSpanUtils.spanName + self.name = SpanSemantics.Session.name self.status = Status.ok.name self.startTime = session.startTime.nanosecondsSince1970Truncated self.endTime = session.endTime?.nanosecondsSince1970Truncated ?? @@ -71,42 +58,42 @@ fileprivate extension SpanPayload { var attributeArray: [Attribute] = [ Attribute( - key: SessionSpanUtils.AttributeKey.type.rawValue, - value: SessionSpanUtils.spanType + key: SpanSemantics.keyEmbraceType, + value: SpanType.session.rawValue ), Attribute( - key: SessionSpanUtils.AttributeKey.id.rawValue, + key: SpanSemantics.Session.keyId, value: session.id.toString ), Attribute( - key: SessionSpanUtils.AttributeKey.state.rawValue, + key: SpanSemantics.Session.keyState, value: session.state ), Attribute( - key: SessionSpanUtils.AttributeKey.coldStart.rawValue, + key: SpanSemantics.Session.keyColdStart, value: String(session.coldStart) ), Attribute( - key: SessionSpanUtils.AttributeKey.terminated.rawValue, + key: SpanSemantics.Session.keyTerminated, value: String(session.appTerminated) ), Attribute( - key: SessionSpanUtils.AttributeKey.cleanExit.rawValue, + key: SpanSemantics.Session.keyCleanExit, value: String(session.cleanExit) ), Attribute( - key: SessionSpanUtils.AttributeKey.heartbeat.rawValue, + key: SpanSemantics.Session.keyHeartbeat, value: String(session.lastHeartbeatTime.nanosecondsSince1970Truncated) ), Attribute( - key: SessionSpanUtils.AttributeKey.sessionNumber.rawValue, + key: SpanSemantics.Session.keySessionNumber, value: String(sessionNumber) ) ] if let crashId = session.crashReportId { attributeArray.append(Attribute( - key: SessionSpanUtils.AttributeKey.crashId.rawValue, + key: SpanSemantics.Session.keyCrashId, value: crashId )) } diff --git a/Sources/EmbraceOTelInternal/EmbraceOTel.swift b/Sources/EmbraceOTelInternal/EmbraceOTel.swift index b4e0af5a..dd75b792 100644 --- a/Sources/EmbraceOTelInternal/EmbraceOTel.swift +++ b/Sources/EmbraceOTelInternal/EmbraceOTel.swift @@ -1,9 +1,15 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + import Foundation import EmbraceCommonInternal +import EmbraceSemantics import OpenTelemetryApi import OpenTelemetrySdk -@objc public final class EmbraceOTel: NSObject { +@objc(EMBEmbraceOTel) +public final class EmbraceOTel: NSObject { let instrumentationName = "EmbraceOpenTelemetry" let instrumentationVersion = "semver:\(EmbraceMeta.sdkVersion)" @@ -69,8 +75,9 @@ import OpenTelemetrySdk let builder = tracer.spanBuilder(spanName: name) .setAttribute( - key: SpanAttributeKey.type, - value: type.rawValue ) + key: SpanSemantics.keyEmbraceType, + value: type.rawValue + ) for (key, value) in attributes { builder.setAttribute(key: key, value: value) diff --git a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/Span+Embrace.swift b/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/Span+Embrace.swift index 86f190f7..ae5953f1 100644 --- a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/Span+Embrace.swift +++ b/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/Span+Embrace.swift @@ -3,17 +3,14 @@ // import Foundation +import EmbraceSemantics import OpenTelemetryApi extension Span { - func setAttribute(key: SpanAttributeKey, value: String) { - setAttribute(key: key.rawValue, value: value) - } - /// Mark this Span as important so the backend will create aggregate metrics for it, and the UI will prioritize this span public func markAsKeySpan() { - setAttribute(key: SpanAttributeKey.isKey, value: "true") + setAttribute(key: SpanSemantics.keyIsKeySpan, value: "true") } /// Mark this Span as private. This is used for observability of the SDK itself @@ -21,7 +18,7 @@ extension Span { /// - Note: This method should not be called by application developers, it is only public /// to allow access to multiple targets within the Embrace SDK. public func markAsPrivate() { - setAttribute(key: SpanAttributeKey.isPrivate, value: "true") + setAttribute(key: SpanSemantics.keyIsPrivateSpan, value: "true") } public func end(errorCode: SpanErrorCode? = nil, time: Date = Date()) { @@ -33,15 +30,15 @@ extension Span { // get attributes from error if let error = error as? NSError { - setAttribute(key: SpanErrorAttributeKey.message.rawValue, value: error.localizedDescription) - setAttribute(key: SpanErrorAttributeKey.code.rawValue, value: error.code) + setAttribute(key: SpanSemantics.keyNSErrorMessage, value: error.localizedDescription) + setAttribute(key: SpanSemantics.keyNSErrorCode, value: error.code) errorCode = errorCode ?? .failure } // set error code if let errorCode = errorCode { - setAttribute(key: SpanAttributeKey.errorCode, value: errorCode.rawValue) + setAttribute(key: SpanSemantics.keyErrorCode, value: errorCode.rawValue) status = .error(description: errorCode.rawValue) } else { // no error or error code means the span ended successfully diff --git a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanAttributeKey.swift b/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanAttributeKey.swift deleted file mode 100644 index b59710c8..00000000 --- a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanAttributeKey.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. -// - -enum SpanAttributeKey: String { - case type = "emb.type" - case isKey = "emb.key" - case errorCode = "emb.error_code" - case isPrivate = "emb.private" -} - -enum SpanErrorAttributeKey: String { - case message = "error.message" - case code = "error.code" -} diff --git a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanData+Embrace.swift b/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanData+Embrace.swift index 8f19f60f..13ddd16f 100644 --- a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanData+Embrace.swift +++ b/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/Span/SpanData+Embrace.swift @@ -4,11 +4,12 @@ import Foundation import EmbraceCommonInternal +import EmbraceSemantics import OpenTelemetrySdk extension SpanData { public var embType: SpanType { - if let raw = attributes[SpanAttributeKey.type.rawValue] { + if let raw = attributes[SpanSemantics.keyEmbraceType] { switch raw { case let .string(val): return SpanType(rawValue: val) ?? .performance @@ -20,7 +21,7 @@ extension SpanData { } var errorCode: SpanErrorCode? { - guard let value = attributes[SpanAttributeKey.errorCode.rawValue] else { + guard let value = attributes[SpanSemantics.keyErrorCode] else { return nil } return SpanErrorCode(rawValue: value.description) diff --git a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/SpanBuilder/SpanBuilder+Embrace.swift b/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/SpanBuilder/SpanBuilder+Embrace.swift index 30423140..42f1c33b 100644 --- a/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/SpanBuilder/SpanBuilder+Embrace.swift +++ b/Sources/EmbraceOTelInternal/Trace/EmbraceSemantics/SpanBuilder/SpanBuilder+Embrace.swift @@ -3,24 +3,25 @@ // import EmbraceCommonInternal +import EmbraceSemantics import OpenTelemetryApi extension SpanBuilder { - @discardableResult func setAttribute(key: SpanAttributeKey, value: String) -> Self { - setAttribute(key: key.rawValue, value: value) + @discardableResult func setAttribute(key: String, value: String) -> Self { + setAttribute(key: key, value: value) return self } @discardableResult public func markAsPrivate() -> Self { - setAttribute(key: .isPrivate, value: "true") + setAttribute(key: SpanSemantics.keyIsPrivateSpan, value: "true") } @discardableResult public func markAsKeySpan() -> Self { - setAttribute(key: .isKey, value: "true") + setAttribute(key: SpanSemantics.keyIsKeySpan, value: "true") } @discardableResult public func error(errorCode: SpanErrorCode) -> Self { - setAttribute(key: SpanAttributeKey.errorCode, value: errorCode.rawValue) + setAttribute(key: SpanSemantics.keyErrorCode, value: errorCode.rawValue) } } diff --git a/Sources/EmbraceSemantics/Logs/LogSemantics+Crash.swift b/Sources/EmbraceSemantics/Logs/LogSemantics+Crash.swift new file mode 100644 index 00000000..40bd4c60 --- /dev/null +++ b/Sources/EmbraceSemantics/Logs/LogSemantics+Crash.swift @@ -0,0 +1,11 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +public extension LogSemantics { + struct Crash { + public static let keyId = "log.record.uid" + public static let keyProvider = "emb.provider" + public static let keyPayload = "emb.payload" + } +} diff --git a/Sources/EmbraceSemantics/Logs/LogSemantics.swift b/Sources/EmbraceSemantics/Logs/LogSemantics.swift new file mode 100644 index 00000000..2e5c83df --- /dev/null +++ b/Sources/EmbraceSemantics/Logs/LogSemantics.swift @@ -0,0 +1,18 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension LogType { + static let crash = LogType(system: "ios.crash") +} + +public struct LogSemantics { + public static let keyEmbraceType = "emb.type" + public static let keyId = "log.record.uid" + public static let keyState = "emb.state" + public static let keySessionId = "emb.session_id" + public static let keyStackTrace = "emb.stacktrace.ios" + public static let keyPropertiesPrefix = "emb.properties.%@" +} diff --git a/Sources/EmbraceSemantics/Span/SpanSemantics+LowPower.swift b/Sources/EmbraceSemantics/Span/SpanSemantics+LowPower.swift new file mode 100644 index 00000000..8b98bdcf --- /dev/null +++ b/Sources/EmbraceSemantics/Span/SpanSemantics+LowPower.swift @@ -0,0 +1,18 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let lowPower = SpanType(system: "low_power") +} + +public extension SpanSemantics { + struct LowPower { + public static let name = "emb-device-low-power" + public static let keyStartReason = "start_reason" + public static let systemQuery = "system_query" + public static let systemNotification = "system_notification" + } +} diff --git a/Sources/EmbraceSemantics/Span/SpanSemantics+NetworkRequest.swift b/Sources/EmbraceSemantics/Span/SpanSemantics+NetworkRequest.swift new file mode 100644 index 00000000..b671ab11 --- /dev/null +++ b/Sources/EmbraceSemantics/Span/SpanSemantics+NetworkRequest.swift @@ -0,0 +1,24 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let networkRequest = SpanType(performance: "network_request") +} + +public extension SpanSemantics { + struct NetworkRequest { + public static let keyUrl = "url.full" + public static let keyMethod = "http.request.method" + public static let keyBodySize = "http.request.body.size" + public static let keyTracingHeader = "emb.w3c_traceparent" + public static let keyStatusCode = "http.response.status_code" + public static let keyResponseSize = "http.response.body.size" + public static let keyErrorType = "error.type" + public static let keyErrorCode = "error.code" + public static let keyErrorMessage = "error.message" + public static let traceparentHeader = "traceparent" + } +} diff --git a/Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift b/Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift new file mode 100644 index 00000000..aaf0afc6 --- /dev/null +++ b/Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift @@ -0,0 +1,24 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let session = SpanType(ux: "session") +} + +public extension SpanSemantics { + struct Session { + public static let name = "emb-session" + public static let keyType = "emb.type" + public static let keyId = "emb.session_id" + public static let keyState = "emb.state" + public static let keyColdStart = "emb.cold_start" + public static let keyTerminated = "emb.terminated" + public static let keyCleanExit = "emb.clean_exit" + public static let keySessionNumber = "emb.session_number" + public static let keyHeartbeat = "emb.heartbeat_time_unix_nano" + public static let keyCrashId = "emb.crash_id" + } +} diff --git a/Sources/EmbraceSemantics/Span/SpanSemantics+View.swift b/Sources/EmbraceSemantics/Span/SpanSemantics+View.swift new file mode 100644 index 00000000..fa0556c4 --- /dev/null +++ b/Sources/EmbraceSemantics/Span/SpanSemantics+View.swift @@ -0,0 +1,17 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let view = SpanType(ux: "view") +} + +public extension SpanSemantics { + struct View { + public static let name = "emb-screen-view" + public static let keyViewTitle = "view.title" + public static let keyViewName = "view.name" + } +} diff --git a/Sources/EmbraceSemantics/Span/SpanSemantics.swift b/Sources/EmbraceSemantics/Span/SpanSemantics.swift new file mode 100644 index 00000000..7984cc6d --- /dev/null +++ b/Sources/EmbraceSemantics/Span/SpanSemantics.swift @@ -0,0 +1,14 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +public struct SpanSemantics { + public static let keyEmbraceType = "emb.type" + + public static let keyIsKeySpan = "emb.key" + public static let keyIsPrivateSpan = "emb.private" + public static let keyErrorCode = "emb.error_code" + + public static let keyNSErrorMessage = "error.message" + public static let keyNSErrorCode = "error.code" +} diff --git a/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Breadcrumb.swift b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Breadcrumb.swift new file mode 100644 index 00000000..ae92adb3 --- /dev/null +++ b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Breadcrumb.swift @@ -0,0 +1,16 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let breadcrumb = SpanType(system: "breadcrumb") +} + +public extension SpanEventSemantics { + struct Bradcrumb { + public static let name = "emb-breadcrumb" + public static let keyMessage = "message" + } +} diff --git a/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+LowMemory.swift b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+LowMemory.swift new file mode 100644 index 00000000..f9f18a0f --- /dev/null +++ b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+LowMemory.swift @@ -0,0 +1,14 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// +import EmbraceCommonInternal + +public extension SpanType { + static let lowMemory = SpanType(system: "low_memory") +} + +public extension SpanEventSemantics { + struct LowMemory { + public static let name = "emb-device-low-memory" + } +} diff --git a/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+PushNotification.swift b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+PushNotification.swift new file mode 100644 index 00000000..419f8d06 --- /dev/null +++ b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+PushNotification.swift @@ -0,0 +1,25 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let pushNotification = SpanType(system: "push_notification") +} + +public extension SpanEventSemantics { + struct PushNotification { + public static let name = "emb-push-notification" + + public static let keyType = "notification.type" + public static let keyTitle = "notification.title" + public static let keySubtitle = "notification.subtitle" + public static let keyBody = "notification.body" + public static let keyCategory = "notification.category" + public static let keyBadge = "notification.badge" + + public static let notificationType = "notif" + public static let silentType = "silent" + } +} diff --git a/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Tap.swift b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Tap.swift new file mode 100644 index 00000000..41bb703a --- /dev/null +++ b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+Tap.swift @@ -0,0 +1,17 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let tap = SpanType(ux: "tap") +} + +public extension SpanEventSemantics { + struct Tap { + public static let name = "emb-ui-tap" + public static let keyViewName = "view.name" + public static let keyCoordinates = "tap.coords" + } +} diff --git a/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+WebView.swift b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+WebView.swift new file mode 100644 index 00000000..7a2d32de --- /dev/null +++ b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics+WebView.swift @@ -0,0 +1,17 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +import EmbraceCommonInternal + +public extension SpanType { + static let webView = SpanType(ux: "webview") +} + +public extension SpanEventSemantics { + struct WebView { + public static let name = "emb-web-view" + public static let keyUrl = "webview.url" + public static let keyErrorCode = "webview.error_code" + } +} diff --git a/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics.swift b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics.swift new file mode 100644 index 00000000..ce461403 --- /dev/null +++ b/Sources/EmbraceSemantics/SpanEvent/SpanEventSemantics.swift @@ -0,0 +1,7 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +public struct SpanEventSemantics { + public static let keyEmbraceType = "emb.type" +} diff --git a/Sources/EmbraceStorageInternal/Records/EmbraceStorage+Span.swift b/Sources/EmbraceStorageInternal/Records/EmbraceStorage+Span.swift index 5d64a0c3..0e5a2e43 100644 --- a/Sources/EmbraceStorageInternal/Records/EmbraceStorage+Span.swift +++ b/Sources/EmbraceStorageInternal/Records/EmbraceStorage+Span.swift @@ -4,6 +4,7 @@ import Foundation import EmbraceCommonInternal +import EmbraceSemantics import GRDB extension EmbraceStorage { From 1c55c46d4e63c0364b30b580adfc4361e7c020c6 Mon Sep 17 00:00:00 2001 From: Ignacio Tischelman Date: Mon, 29 Jul 2024 12:38:22 -0300 Subject: [PATCH 2/5] Updating tests --- .../Span/SpanSemantics+Session.swift | 1 - .../LogType/LogTypeDeclarationTests.swift | 9 --- .../DefaultURLSessionTaskHandlerTests.swift | 12 ++-- .../Capture/UX/TapCaptureServiceTests.swift | 20 +++--- .../LengthOfNameValidatorTests.swift | 4 +- .../Payload/SpansPayloadBuilderTests.swift | 22 +++---- .../Public/BreadcrumbTests.swift | 2 +- .../Public/PushNotificationEventTests.swift | 59 +++++++++--------- .../Session/SessionSpanUtilsTests.swift | 61 ++++++------------- .../Processor/SingleSpanProcessorTests.swift | 2 +- .../SpanRecord/EmbraceStorage+SpanTests.swift | 4 +- 11 files changed, 83 insertions(+), 113 deletions(-) diff --git a/Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift b/Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift index aaf0afc6..dcc248ee 100644 --- a/Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift +++ b/Sources/EmbraceSemantics/Span/SpanSemantics+Session.swift @@ -11,7 +11,6 @@ public extension SpanType { public extension SpanSemantics { struct Session { public static let name = "emb-session" - public static let keyType = "emb.type" public static let keyId = "emb.session_id" public static let keyState = "emb.state" public static let keyColdStart = "emb.cold_start" diff --git a/Tests/EmbraceCommonInternalTests/LogType/LogTypeDeclarationTests.swift b/Tests/EmbraceCommonInternalTests/LogType/LogTypeDeclarationTests.swift index d5b15a1e..297b885e 100644 --- a/Tests/EmbraceCommonInternalTests/LogType/LogTypeDeclarationTests.swift +++ b/Tests/EmbraceCommonInternalTests/LogType/LogTypeDeclarationTests.swift @@ -26,13 +26,4 @@ class LogTypeDeclarationTests: XCTestCase { func test_logTypeDefault_isPrimarySystemAndSecondaryLog() { XCTAssertEqual(LogType.default, .init(primary: .system, secondary: "log")) } - - // MARK: - Breadcrumb - func test_logTypeBreadcrumb_isSysBreadcrumbString() { - XCTAssertEqual(LogType.breadcrumb.rawValue, "sys.breadcrumb") - } - - func test_logTypeBreadcrumb_isPrimarySystemAndSecondaryBreadcrumb() { - XCTAssertEqual(LogType.breadcrumb, .init(primary: .system, secondary: "breadcrumb")) - } } diff --git a/Tests/EmbraceCoreTests/Capture/Network/DefaultURLSessionTaskHandlerTests.swift b/Tests/EmbraceCoreTests/Capture/Network/DefaultURLSessionTaskHandlerTests.swift index 7c3dc179..8301603f 100644 --- a/Tests/EmbraceCoreTests/Capture/Network/DefaultURLSessionTaskHandlerTests.swift +++ b/Tests/EmbraceCoreTests/Capture/Network/DefaultURLSessionTaskHandlerTests.swift @@ -270,7 +270,7 @@ private extension DefaultURLSessionTaskHandlerTests { func thenHTTPNetworkSpanShouldBeCreated() { do { let span = try XCTUnwrap(otel.spanProcessor.startedSpans.first) - XCTAssertEqual(span.embType, .networkHTTP) + XCTAssertEqual(span.embType, .networkRequest) } catch let exception { XCTFail("Couldn't get span: \(exception.localizedDescription)") } @@ -414,7 +414,7 @@ private extension DefaultURLSessionTaskHandlerTests { do { let span = try XCTUnwrap(otel.spanProcessor.endedSpans.first) - let tracingHeader = span.attributes[DefaultURLSessionTaskHandler.SpanAttribute.tracingHeader]!.description + let tracingHeader = span.attributes["emb.w3c_traceparent"]!.description validateTracingHeaderForSpan(tracingHeader: tracingHeader, span: span) } catch let exception { @@ -425,7 +425,7 @@ private extension DefaultURLSessionTaskHandlerTests { func thenSpanShouldNotHaveTheTracingHeaderAttribute() { do { let span = try XCTUnwrap(otel.spanProcessor.endedSpans.first) - XCTAssertNil(span.attributes[DefaultURLSessionTaskHandler.SpanAttribute.tracingHeader]) + XCTAssertNil(span.attributes["emb.w3c_traceparent"]) } catch let exception { XCTFail("Couldn't get span: \(exception.localizedDescription)") @@ -436,7 +436,7 @@ private extension DefaultURLSessionTaskHandlerTests { do { let span = try XCTUnwrap(otel.spanProcessor.endedSpans.first) - XCTAssertEqual(span.attributes[DefaultURLSessionTaskHandler.SpanAttribute.url], .string(path)) + XCTAssertEqual(span.attributes["url.full"], .string(path)) } catch let exception { XCTFail("Couldn't get span: \(exception.localizedDescription)") } @@ -446,7 +446,7 @@ private extension DefaultURLSessionTaskHandlerTests { do { let span = try XCTUnwrap(otel.spanProcessor.endedSpans.first) - XCTAssertEqual(span.attributes[DefaultURLSessionTaskHandler.SpanAttribute.method], .string(method)) + XCTAssertEqual(span.attributes["http.request.method"], .string(method)) } catch let exception { XCTFail("Couldn't get span: \(exception.localizedDescription)") } @@ -456,7 +456,7 @@ private extension DefaultURLSessionTaskHandlerTests { do { let span = try XCTUnwrap(otel.spanProcessor.endedSpans.first) - XCTAssertEqual(span.attributes[DefaultURLSessionTaskHandler.SpanAttribute.bodySize], .int(bodySize)) + XCTAssertEqual(span.attributes["http.request.body.size"], .int(bodySize)) } catch let exception { XCTFail("Couldn't get span: \(exception.localizedDescription)") } diff --git a/Tests/EmbraceCoreTests/Capture/UX/TapCaptureServiceTests.swift b/Tests/EmbraceCoreTests/Capture/UX/TapCaptureServiceTests.swift index d493f00d..4a5e130f 100644 --- a/Tests/EmbraceCoreTests/Capture/UX/TapCaptureServiceTests.swift +++ b/Tests/EmbraceCoreTests/Capture/UX/TapCaptureServiceTests.swift @@ -35,12 +35,12 @@ final class TapCaptureServiceTests: XCTestCase { // then the tap is captured XCTAssertEqual(otel.events.count, 1) - XCTAssertEqual(otel.events[0].name, TapCaptureService.Constants.eventName) + XCTAssertEqual(otel.events[0].name, "emb-ui-tap") } func test_tap_notStarted() throws { // given an installed but not started tap capture service - let service = TapCaptureService() + _ = TapCaptureService() // when a tap is done let event = MockTapEvent(mockedTouches: [.init()]) @@ -112,7 +112,7 @@ final class TapCaptureServiceTests: XCTestCase { // then the tap is not captured without coordinates XCTAssertEqual(otel.events.count, 1) - XCTAssertNil(otel.events[0].attributes[TapCaptureService.Constants.tapCoordinates]) + XCTAssertNil(otel.events[0].attributes["tap.coords"]) } func test_delegate() throws { @@ -140,8 +140,8 @@ final class TapCaptureServiceTests: XCTestCase { // then the taps are captured correctly XCTAssertEqual(otel.events.count, 2) - XCTAssertNil(otel.events[0].attributes[TapCaptureService.Constants.tapCoordinates]) - XCTAssertNotNil(otel.events[1].attributes[TapCaptureService.Constants.tapCoordinates]) + XCTAssertNil(otel.events[0].attributes["tap.coords"]) + XCTAssertNotNil(otel.events[1].attributes["tap.coords"]) } func assertViewName(view: UIView, viewName: String) throws { @@ -158,8 +158,8 @@ final class TapCaptureServiceTests: XCTestCase { XCTAssertEqual(otel.events.count, 1) let event = try XCTUnwrap(otel.events.first) - XCTAssertEqual(event.attributes[TapCaptureService.Constants.viewName], .string(viewName)) - XCTAssertNotNil(event.attributes[TapCaptureService.Constants.tapCoordinates]) + XCTAssertEqual(event.attributes["view.name"], .string(viewName)) + XCTAssertNotNil(event.attributes["tap.coords"]) } func assertNoCoordinate(viewName: String) throws { @@ -178,9 +178,9 @@ final class TapCaptureServiceTests: XCTestCase { XCTAssertEqual(otel.events.count, 1) let event = try XCTUnwrap(otel.events.first) - XCTAssertEqual(event.attributes[TapCaptureService.Constants.viewName], .string(viewName)) - XCTAssertEqual(event.attributes["emb.type"], .string(TapCaptureService.Constants.eventType)) - XCTAssertNil(event.attributes[TapCaptureService.Constants.tapCoordinates]) + XCTAssertEqual(event.attributes["view.name"], .string(viewName)) + XCTAssertEqual(event.attributes["emb.type"], .string("ux.tap")) + XCTAssertNil(event.attributes["tap.coords"]) } } diff --git a/Tests/EmbraceCoreTests/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidatorTests.swift b/Tests/EmbraceCoreTests/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidatorTests.swift index 7dc2f6c5..853a7430 100644 --- a/Tests/EmbraceCoreTests/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidatorTests.swift +++ b/Tests/EmbraceCoreTests/Internal/SpanStorageExporter/Validation/Validators/LengthOfNameValidatorTests.swift @@ -18,7 +18,7 @@ final class LengthOfNameValidatorTests: XCTestCase { name: name, kind: .internal, startTime: Date(), - attributes: [SpanAttributeKey.type.rawValue: .string(type.rawValue)], + attributes: ["emb.type": .string(type.rawValue)], endTime: Date() ) } @@ -59,7 +59,7 @@ final class LengthOfNameValidatorTests: XCTestCase { func testOnNetworkSpan_validate_shouldNotTryToValidateLongNames() throws { let longName = "GET https://this-is-a-really-long-url.com/with/some/long/path?and=with&some=parameters&in=url" - var span = spanData(named: longName, type: .networkHTTP) + var span = spanData(named: longName, type: .networkRequest) let validator = LengthOfNameValidator() diff --git a/Tests/EmbraceCoreTests/Payload/SpansPayloadBuilderTests.swift b/Tests/EmbraceCoreTests/Payload/SpansPayloadBuilderTests.swift index 16f7d30c..dcd22035 100644 --- a/Tests/EmbraceCoreTests/Payload/SpansPayloadBuilderTests.swift +++ b/Tests/EmbraceCoreTests/Payload/SpansPayloadBuilderTests.swift @@ -99,7 +99,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then a session span is created // and its end time is valid XCTAssertEqual(closed.count, 1) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) + XCTAssertEqual(closed[0].name, "emb-session") XCTAssertEqual(closed[0].endTime, record.lastHeartbeatTime.nanosecondsSince1970Truncated) } @@ -119,7 +119,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then session span has an end time XCTAssertEqual(closed.count, 1) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) + XCTAssertEqual(closed[0].name, "emb-session") XCTAssertNotNil(closed[0].endTime) XCTAssertEqual(closed[0].endTime, sessionRecord.endTime!.nanosecondsSince1970Truncated) } @@ -137,7 +137,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 2) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(closed[1], payload) XCTAssertEqual(open.count, 0) } @@ -155,7 +155,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 1) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(open.count, 1) XCTAssertEqual(open[0], payload) } @@ -173,7 +173,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 2) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(closed[1], payload) XCTAssertEqual(open.count, 0) } @@ -191,7 +191,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 1) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(open.count, 1) XCTAssertEqual(open[0], payload) } @@ -208,7 +208,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 1) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(open.count, 0) } @@ -228,7 +228,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 2) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(closed[1], payload) XCTAssertEqual(open.count, 0) @@ -253,7 +253,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 2) - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(closed[1], payload) XCTAssertEqual(open.count, 0) @@ -276,7 +276,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then the spans are retrieved correctly XCTAssertEqual(closed.count, 1001) // 1000 spans + session span - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(open.count, 0) } @@ -303,7 +303,7 @@ final class SpansPayloadBuilderTests: XCTestCase { // then only the correct session span is included XCTAssertEqual(closed.count, 1) // 1000 spans + session span - XCTAssertEqual(closed[0].name, SessionSpanUtils.spanName) // session span always first + XCTAssertEqual(closed[0].name, "emb-session") // session span always first XCTAssertEqual(open.count, 0) } } diff --git a/Tests/EmbraceCoreTests/Public/BreadcrumbTests.swift b/Tests/EmbraceCoreTests/Public/BreadcrumbTests.swift index f797004a..9f81b451 100644 --- a/Tests/EmbraceCoreTests/Public/BreadcrumbTests.swift +++ b/Tests/EmbraceCoreTests/Public/BreadcrumbTests.swift @@ -18,6 +18,6 @@ class BreadcrumbTests: XCTestCase { func test_onInit_embTypeIsSysBreadcrumb() { let breadcrumb = Breadcrumb(message: "", attributes: .empty()) - XCTAssertEqual(breadcrumb.attributes["emb.type"], .string(LogType.breadcrumb.rawValue)) + XCTAssertEqual(breadcrumb.attributes["emb.type"], .string("sys.breadcrumb")) } } diff --git a/Tests/EmbraceCoreTests/Public/PushNotificationEventTests.swift b/Tests/EmbraceCoreTests/Public/PushNotificationEventTests.swift index 39f9d127..5440196a 100644 --- a/Tests/EmbraceCoreTests/Public/PushNotificationEventTests.swift +++ b/Tests/EmbraceCoreTests/Public/PushNotificationEventTests.swift @@ -5,12 +5,13 @@ @testable import EmbraceCore import XCTest import TestSupport +import EmbraceSemantics // swiftlint:disable force_cast force_try class PushNotificationEventTests: XCTestCase { let validPayload: [AnyHashable: Any] = [ - PushNotificationEvent.Constants.rootKey: [ + PushNotificationEvent.Constants.apsRootKey: [ PushNotificationEvent.Constants.apsAlert: [ PushNotificationEvent.Constants.apsTitle: "title", PushNotificationEvent.Constants.apsSubtitle: "subtitle", @@ -22,7 +23,7 @@ class PushNotificationEventTests: XCTestCase { ] let validLocalizedPayload: [AnyHashable: Any] = [ - PushNotificationEvent.Constants.rootKey: [ + PushNotificationEvent.Constants.apsRootKey: [ PushNotificationEvent.Constants.apsAlert: [ PushNotificationEvent.Constants.apsTitleLocalized: "title", PushNotificationEvent.Constants.apsSubtitleLocalized: "subtitle", @@ -34,7 +35,7 @@ class PushNotificationEventTests: XCTestCase { ] let validSilentPayload: [AnyHashable: Any] = [ - PushNotificationEvent.Constants.rootKey: [ + PushNotificationEvent.Constants.apsRootKey: [ PushNotificationEvent.Constants.apsContentAvailable: 1 ] ] @@ -59,11 +60,11 @@ class PushNotificationEventTests: XCTestCase { let event = try! PushNotificationEvent(userInfo: validPayload, captureData: false) // then the attributes are correct - XCTAssertEqual(event.name, PushNotificationEvent.Constants.eventName) - XCTAssertEqual(event.attributes["emb.type"], .string(PushNotificationEvent.Constants.eventType)) + XCTAssertEqual(event.name, "emb-push-notification") + XCTAssertEqual(event.attributes["emb.type"], .string("sys.push_notification")) XCTAssertEqual( - event.attributes[PushNotificationEvent.Constants.keyType], - .string(PushNotificationEvent.Constants.notificationType) + event.attributes["notification.type"], + .string("notif") ) XCTAssertEqual(event.attributes.count, 2) } @@ -73,11 +74,11 @@ class PushNotificationEventTests: XCTestCase { let event = try! PushNotificationEvent(userInfo: validSilentPayload, captureData: false) // then the attributes are correct - XCTAssertEqual(event.name, PushNotificationEvent.Constants.eventName) - XCTAssertEqual(event.attributes["emb.type"], .string(PushNotificationEvent.Constants.eventType)) + XCTAssertEqual(event.name, "emb-push-notification") + XCTAssertEqual(event.attributes["emb.type"], .string("sys.push_notification")) XCTAssertEqual( - event.attributes[PushNotificationEvent.Constants.keyType], - .string(PushNotificationEvent.Constants.silentType) + event.attributes["notification.type"], + .string("silent") ) XCTAssertEqual(event.attributes.count, 2) } @@ -87,17 +88,17 @@ class PushNotificationEventTests: XCTestCase { let event = try! PushNotificationEvent(userInfo: validPayload, captureData: true) // then the attributes are correct - XCTAssertEqual(event.name, PushNotificationEvent.Constants.eventName) - XCTAssertEqual(event.attributes["emb.type"], .string(PushNotificationEvent.Constants.eventType)) + XCTAssertEqual(event.name, "emb-push-notification") + XCTAssertEqual(event.attributes["emb.type"], .string("sys.push_notification")) XCTAssertEqual( - event.attributes[PushNotificationEvent.Constants.keyType], - .string(PushNotificationEvent.Constants.notificationType) + event.attributes["notification.type"], + .string("notif") ) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyTitle], .string("title")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keySubtitle], .string("subtitle")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyBody], .string("body")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyCategory], .string("category")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyBadge], .int(1)) + XCTAssertEqual(event.attributes["notification.title"], .string("title")) + XCTAssertEqual(event.attributes["notification.subtitle"], .string("subtitle")) + XCTAssertEqual(event.attributes["notification.body"], .string("body")) + XCTAssertEqual(event.attributes["notification.category"], .string("category")) + XCTAssertEqual(event.attributes["notification.badge"], .int(1)) } func test_validLocalizedPayload() throws { @@ -105,17 +106,17 @@ class PushNotificationEventTests: XCTestCase { let event = try! PushNotificationEvent(userInfo: validLocalizedPayload, captureData: true) // then the attributes are correct - XCTAssertEqual(event.name, PushNotificationEvent.Constants.eventName) - XCTAssertEqual(event.attributes["emb.type"], .string(PushNotificationEvent.Constants.eventType)) + XCTAssertEqual(event.name, "emb-push-notification") + XCTAssertEqual(event.attributes["emb.type"], .string("sys.push_notification")) XCTAssertEqual( - event.attributes[PushNotificationEvent.Constants.keyType], - .string(PushNotificationEvent.Constants.notificationType) + event.attributes["notification.type"], + .string("notif") ) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyTitle], .string("title")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keySubtitle], .string("subtitle")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyBody], .string("body")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyCategory], .string("category")) - XCTAssertEqual(event.attributes[PushNotificationEvent.Constants.keyBadge], .int(1)) + XCTAssertEqual(event.attributes["notification.title"], .string("title")) + XCTAssertEqual(event.attributes["notification.subtitle"], .string("subtitle")) + XCTAssertEqual(event.attributes["notification.body"], .string("body")) + XCTAssertEqual(event.attributes["notification.category"], .string("category")) + XCTAssertEqual(event.attributes["notification.badge"], .int(1)) } } diff --git a/Tests/EmbraceCoreTests/Session/SessionSpanUtilsTests.swift b/Tests/EmbraceCoreTests/Session/SessionSpanUtilsTests.swift index 025a5fbd..0e9f42e6 100644 --- a/Tests/EmbraceCoreTests/Session/SessionSpanUtilsTests.swift +++ b/Tests/EmbraceCoreTests/Session/SessionSpanUtilsTests.swift @@ -37,24 +37,12 @@ final class SessionSpanUtilsTests: XCTestCase { // then the span is correct let spanData = spanProcessor.startedSpans[0] - XCTAssertEqual(spanData.name, SessionSpanUtils.spanName) + XCTAssertEqual(spanData.name, "emb-session") XCTAssertEqual(spanData.startTime, TestConstants.date) - XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.type.rawValue], - .string(SessionSpanUtils.spanType) - ) - XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.id.rawValue], - .string(TestConstants.sessionId.toString) - ) - XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.state.rawValue], - .string(SessionState.foreground.rawValue) - ) - XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.coldStart.rawValue], - .bool(true) - ) + XCTAssertEqual(spanData.attributes["emb.type"], .string("ux.session")) + XCTAssertEqual(spanData.attributes["emb.session_id"], .string(TestConstants.sessionId.toString)) + XCTAssertEqual(spanData.attributes["emb.state"], .string(SessionState.foreground.rawValue)) + XCTAssertEqual(spanData.attributes["emb.cold_start"], .bool(true)) } func test_setState() throws { @@ -72,10 +60,7 @@ final class SessionSpanUtilsTests: XCTestCase { // then it is updated correctly let spanData = spanProcessor.endedSpans[0] - XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.state.rawValue], - .string(SessionState.background.rawValue) - ) + XCTAssertEqual(spanData.attributes["emb.state"], .string(SessionState.background.rawValue)) } func test_setHeartbeat() throws { @@ -95,7 +80,7 @@ final class SessionSpanUtilsTests: XCTestCase { // then it is updated correctly let spanData = spanProcessor.endedSpans[0] XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.heartbeat.rawValue], + spanData.attributes["emb.heartbeat_time_unix_nano"], .int(heartbeat.nanosecondsSince1970Truncated) ) } @@ -115,10 +100,7 @@ final class SessionSpanUtilsTests: XCTestCase { // then it is updated correctly let spanData = spanProcessor.endedSpans[0] - XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.terminated.rawValue], - .bool(true) - ) + XCTAssertEqual(spanData.attributes["emb.terminated"], .bool(true)) } func test_setCleanExit() throws { @@ -136,10 +118,7 @@ final class SessionSpanUtilsTests: XCTestCase { // then it is updated correctly let spanData = spanProcessor.endedSpans[0] - XCTAssertEqual( - spanData.attributes[SessionSpanUtils.AttributeKey.cleanExit.rawValue], - .bool(true) - ) + XCTAssertEqual(spanData.attributes["emb.clean_exit"], .bool(true)) } func test_payloadFromSesssion() throws { @@ -166,7 +145,7 @@ final class SessionSpanUtilsTests: XCTestCase { let payload = SessionSpanUtils.payload(from: session, sessionNumber: 100) // then the payload is correct - XCTAssertEqual(payload.name, SessionSpanUtils.spanName) + XCTAssertEqual(payload.name, "emb-session") XCTAssertEqual(payload.traceId, TestConstants.traceId) XCTAssertEqual(payload.spanId, TestConstants.spanId) XCTAssertNil(payload.parentSpanId) @@ -177,47 +156,47 @@ final class SessionSpanUtilsTests: XCTestCase { XCTAssertEqual(payload.links.count, 0) let typeAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.type.rawValue + $0.key == "emb.type" } - XCTAssertEqual(typeAttribute!.value, SessionSpanUtils.spanType) + XCTAssertEqual(typeAttribute!.value, "ux.session") let sessionAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.id.rawValue + $0.key == "emb.session_id" } XCTAssertEqual(sessionAttribute!.value, TestConstants.sessionId.toString) let stateAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.state.rawValue + $0.key == "emb.state" } XCTAssertEqual(stateAttribute!.value, SessionState.foreground.rawValue) let coldStartAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.coldStart.rawValue + $0.key == "emb.cold_start" } XCTAssertEqual(coldStartAttribute!.value, "true") let terminatedAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.terminated.rawValue + $0.key == "emb.terminated" } XCTAssertEqual(terminatedAttribute!.value, "true") let cleanExitAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.cleanExit.rawValue + $0.key == "emb.clean_exit" } XCTAssertEqual(cleanExitAttribute!.value, "false") let heartbeatAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.heartbeat.rawValue + $0.key == "emb.heartbeat_time_unix_nano" } XCTAssertEqual(heartbeatAttribute!.value, String(heartbeat.nanosecondsSince1970Truncated)) let sessionNumberAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.sessionNumber.rawValue + $0.key == "emb.session_number" } XCTAssertEqual(sessionNumberAttribute!.value, "100") let crashIdAttribute = payload.attributes.first { - $0.key == SessionSpanUtils.AttributeKey.crashId.rawValue + $0.key == "emb.crash_id" } XCTAssertEqual(crashIdAttribute!.value, "test") } diff --git a/Tests/EmbraceOTelInternalTests/Trace/Tracer/Span/Processor/SingleSpanProcessorTests.swift b/Tests/EmbraceOTelInternalTests/Trace/Tracer/Span/Processor/SingleSpanProcessorTests.swift index 921621d1..d2a6b7d5 100644 --- a/Tests/EmbraceOTelInternalTests/Trace/Tracer/Span/Processor/SingleSpanProcessorTests.swift +++ b/Tests/EmbraceOTelInternalTests/Trace/Tracer/Span/Processor/SingleSpanProcessorTests.swift @@ -123,7 +123,7 @@ final class SingleSpanProcessorTests: XCTestCase { let span = createSpanData(processor: processor) - span.setAttribute(key: SpanAttributeKey.errorCode, value: SpanErrorCode.unknown.rawValue) + span.setAttribute(key: "emb.error_code", value: SpanErrorCode.unknown.rawValue) let endTime = Date().addingTimeInterval(2) span.end(time: endTime) diff --git a/Tests/EmbraceStorageInternalTests/Records/SpanRecord/EmbraceStorage+SpanTests.swift b/Tests/EmbraceStorageInternalTests/Records/SpanRecord/EmbraceStorage+SpanTests.swift index 80f634ce..cb7e7510 100644 --- a/Tests/EmbraceStorageInternalTests/Records/SpanRecord/EmbraceStorage+SpanTests.swift +++ b/Tests/EmbraceStorageInternalTests/Records/SpanRecord/EmbraceStorage+SpanTests.swift @@ -56,7 +56,7 @@ final class EmbraceStorage_SpanTests: XCTestCase { func test_upsertSpan_limitIsUniqueToSpecificType() throws { storage.options.spanLimits[.performance] = 3 - storage.options.spanLimits[.networkHTTP] = 1 + storage.options.spanLimits[.networkRequest] = 1 // insert 3 .performance spans for i in 0..<3 { @@ -78,7 +78,7 @@ final class EmbraceStorage_SpanTests: XCTestCase { id: SpanId.random().hexString, name: "network \(i)", traceId: TraceId.random().hexString, - type: .networkHTTP, + type: .networkRequest, data: Data(), startTime: Date() ) From 241df31a4256ff4308303680048863d05181aed1 Mon Sep 17 00:00:00 2001 From: Ignacio Tischelman Date: Mon, 29 Jul 2024 12:52:44 -0300 Subject: [PATCH 3/5] Updating podspec --- .../xcschemes/EmbraceIO-Package.xcscheme | 14 ++++++++++++++ EmbraceIO.podspec | 8 ++++++++ Package.swift | 4 +++- Project.swift | 17 ++++++++++++++++- bin/build_xcframeworks.sh | 2 ++ bin/templates/EmbraceIO.podspec.tpl | 8 ++++++++ 6 files changed, 51 insertions(+), 2 deletions(-) diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme index a864ed4d..152af8f3 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/EmbraceIO-Package.xcscheme @@ -426,6 +426,20 @@ ReferencedContainer = "container:"> + + + + Date: Mon, 29 Jul 2024 12:59:18 -0300 Subject: [PATCH 4/5] Making session notifications public --- Sources/EmbraceCore/Session/SessionController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/EmbraceCore/Session/SessionController.swift b/Sources/EmbraceCore/Session/SessionController.swift index 3aa51be0..55c127d5 100644 --- a/Sources/EmbraceCore/Session/SessionController.swift +++ b/Sources/EmbraceCore/Session/SessionController.swift @@ -8,7 +8,7 @@ import EmbraceStorageInternal import EmbraceUploadInternal import EmbraceOTelInternal -extension Notification.Name { +public extension Notification.Name { static let embraceSessionDidStart = Notification.Name("embrace.session.did_start") static let embraceSessionWillEnd = Notification.Name("embrace.session.will_end") } From 8e8491f50f59c3751f106551c6601d1c5617191d Mon Sep 17 00:00:00 2001 From: Austin Emmons Date: Thu, 1 Aug 2024 17:48:47 -0400 Subject: [PATCH 5/5] Adds protocol `EmbraceType` that can be used whenever we want to use the `emb.type` attribute. Update SpanType, LogType to conform to new protocol Adds SpanEventType which also conforms to new protocol --- .../EmbraceType/EmbraceType.swift | 103 ++++++++++++++++++ .../LogType+Declarations.swift | 7 -- .../EmbraceType/LogType.swift | 14 +++ .../EmbraceType/SpanEventType.swift | 14 +++ .../SpanType+Declarations.swift | 9 -- .../EmbraceType/SpanType.swift | 15 +++ .../LogType/LogType.swift | 72 ------------ .../SpanType/SpanType.swift | 99 ----------------- 8 files changed, 146 insertions(+), 187 deletions(-) create mode 100644 Sources/EmbraceCommonInternal/EmbraceType/EmbraceType.swift rename Sources/EmbraceCommonInternal/{LogType => EmbraceType}/LogType+Declarations.swift (64%) create mode 100644 Sources/EmbraceCommonInternal/EmbraceType/LogType.swift create mode 100644 Sources/EmbraceCommonInternal/EmbraceType/SpanEventType.swift rename Sources/EmbraceCommonInternal/{SpanType => EmbraceType}/SpanType+Declarations.swift (72%) create mode 100644 Sources/EmbraceCommonInternal/EmbraceType/SpanType.swift delete mode 100644 Sources/EmbraceCommonInternal/LogType/LogType.swift delete mode 100644 Sources/EmbraceCommonInternal/SpanType/SpanType.swift diff --git a/Sources/EmbraceCommonInternal/EmbraceType/EmbraceType.swift b/Sources/EmbraceCommonInternal/EmbraceType/EmbraceType.swift new file mode 100644 index 00000000..b495e99d --- /dev/null +++ b/Sources/EmbraceCommonInternal/EmbraceType/EmbraceType.swift @@ -0,0 +1,103 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +/// An EmbraceType is an Embrace specific concept to allow for better categorization of Telemetry Primitives. +/// - This struct will be serialized into an `emb.type` attribute. +/// - This struct is encoded as a String with the format `.`. +/// - The primary category is required, but the secondary category is optional. +public protocol EmbraceType: Hashable, Codable, CustomStringConvertible, RawRepresentable where RawValue == String { + + var primary: PrimaryType { get } + + var secondary: String? { get } + + init(primary: PrimaryType, secondary: String?) +} + +/// Top level category for the EmbraceType +public enum PrimaryType: String { + /// Category for observing a logical operation + case performance = "perf" + + /// Category for observing the user's interaction or behavior + case ux = "ux" + + /// Category for observing the system's operation or status + case system = "sys" +} + +// MARK: - EmbraceType Extensions - + +extension EmbraceType { + public init(primary: PrimaryType, secondary: String? = nil) { + self.init(primary: primary, secondary: secondary) + } + + public init(performance secondary: String) { + self.init(primary: .performance, secondary: secondary) + } + + public init(ux secondary: String) { + self.init(primary: .ux, secondary: secondary) + } + + public init(system secondary: String) { + self.init(primary: .system, secondary: secondary) + } +} + +extension EmbraceType { + + public static var performance: Self { .init(primary: .performance, secondary: nil) } + + public static var ux: Self { .init(primary: .ux, secondary: nil) } + + public static var system: Self { .init(primary: .system, secondary: nil) } +} + +// MARK: RawRepresentable +extension EmbraceType { + public var rawValue: String { + [primary.rawValue, secondary] + .compactMap { $0 } + .joined(separator: ".") + } + + public init?(rawValue: String) { + let components = rawValue.components(separatedBy: ".") + guard let first = components.first, + let primary = PrimaryType(rawValue: first) else { + return nil + } + + let secondary = components.count > 1 ? components.dropFirst().joined(separator: ".") : nil + + self.init(primary: primary, secondary: secondary) + } +} + +// MARK: Codable +extension EmbraceType { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let rawValue = try container.decode(String.self) + + guard let embType = Self(rawValue: rawValue) else { + throw DecodingError.dataCorruptedError(in: container, + debugDescription: "Invalid EmbraceType: '\(rawValue.prefix(20))'") + } + + self = embType + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(rawValue) + } +} + +// MARK: CustomStringConvertible +extension EmbraceType { + public var description: String { rawValue } +} diff --git a/Sources/EmbraceCommonInternal/LogType/LogType+Declarations.swift b/Sources/EmbraceCommonInternal/EmbraceType/LogType+Declarations.swift similarity index 64% rename from Sources/EmbraceCommonInternal/LogType/LogType+Declarations.swift rename to Sources/EmbraceCommonInternal/EmbraceType/LogType+Declarations.swift index 08c1c3a1..7ae4b628 100644 --- a/Sources/EmbraceCommonInternal/LogType/LogType+Declarations.swift +++ b/Sources/EmbraceCommonInternal/EmbraceType/LogType+Declarations.swift @@ -2,13 +2,6 @@ // Copyright © 2024 Embrace Mobile, Inc. All rights reserved. // -import Foundation - -// MARK: - Primary categories -extension LogType { - public static let system = LogType(primary: .system) -} - // MARK: - System extension LogType { public static let `default` = LogType(system: "log") diff --git a/Sources/EmbraceCommonInternal/EmbraceType/LogType.swift b/Sources/EmbraceCommonInternal/EmbraceType/LogType.swift new file mode 100644 index 00000000..af602c96 --- /dev/null +++ b/Sources/EmbraceCommonInternal/EmbraceType/LogType.swift @@ -0,0 +1,14 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +/// The EmbraceType used in Log telemetry +public struct LogType: EmbraceType { + public let primary: PrimaryType + public let secondary: String? + + public init(primary: PrimaryType, secondary: String?) { + self.primary = primary + self.secondary = secondary + } +} diff --git a/Sources/EmbraceCommonInternal/EmbraceType/SpanEventType.swift b/Sources/EmbraceCommonInternal/EmbraceType/SpanEventType.swift new file mode 100644 index 00000000..b7b20bab --- /dev/null +++ b/Sources/EmbraceCommonInternal/EmbraceType/SpanEventType.swift @@ -0,0 +1,14 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +/// The EmbraceType used in Tracing telemetry, specifically on SpanEvent items. +public struct SpanEventType: EmbraceType { + public let primary: PrimaryType + public let secondary: String? + + public init(primary: PrimaryType, secondary: String?) { + self.primary = primary + self.secondary = secondary + } +} diff --git a/Sources/EmbraceCommonInternal/SpanType/SpanType+Declarations.swift b/Sources/EmbraceCommonInternal/EmbraceType/SpanType+Declarations.swift similarity index 72% rename from Sources/EmbraceCommonInternal/SpanType/SpanType+Declarations.swift rename to Sources/EmbraceCommonInternal/EmbraceType/SpanType+Declarations.swift index f2f6f549..5d67826d 100644 --- a/Sources/EmbraceCommonInternal/SpanType/SpanType+Declarations.swift +++ b/Sources/EmbraceCommonInternal/EmbraceType/SpanType+Declarations.swift @@ -2,15 +2,6 @@ // Copyright © 2024 Embrace Mobile, Inc. All rights reserved. // -import Foundation - -// MARK: - Primary categories -extension SpanType { - public static let performance = SpanType(primary: .performance) - public static let ux = SpanType(primary: .ux) - public static let system = SpanType(primary: .system) -} - // MARK: - Performance extension SpanType { diff --git a/Sources/EmbraceCommonInternal/EmbraceType/SpanType.swift b/Sources/EmbraceCommonInternal/EmbraceType/SpanType.swift new file mode 100644 index 00000000..a873d851 --- /dev/null +++ b/Sources/EmbraceCommonInternal/EmbraceType/SpanType.swift @@ -0,0 +1,15 @@ +// +// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. +// + +/// The EmbraceType used in Tracing telemetry +public struct SpanType: EmbraceType { + public let primary: PrimaryType + + public let secondary: String? + + public init(primary: PrimaryType, secondary: String? = nil) { + self.primary = primary + self.secondary = secondary + } +} diff --git a/Sources/EmbraceCommonInternal/LogType/LogType.swift b/Sources/EmbraceCommonInternal/LogType/LogType.swift deleted file mode 100644 index 7d87f4bf..00000000 --- a/Sources/EmbraceCommonInternal/LogType/LogType.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. -// - -import Foundation - -public struct LogType: Equatable { - let primary: Primary - let secondary: String? - - public init(primary: Primary, secondary: String? = nil) { - self.primary = primary - self.secondary = secondary - } - - public init(system secondary: String) { - self.primary = .system - self.secondary = secondary - } -} - -extension LogType: RawRepresentable { - public typealias RawValue = String - - public var rawValue: String { - [primary.rawValue, secondary] - .compactMap { $0 } - .joined(separator: ".") - } - - public init?(rawValue: String) { - let components = rawValue.components(separatedBy: ".") - guard let primary = Primary(rawValue: components.first ?? "") else { - return nil - } - self.primary = primary - if components.count > 1 { - self.secondary = components.dropFirst().joined(separator: ".") - } else { - self.secondary = nil - } - } -} - -extension LogType: Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(String.self) - - guard let logType = LogType(rawValue: rawValue) else { - throw DecodingError.dataCorruptedError(in: container, - debugDescription: "Invalid LogType: '\(rawValue.prefix(10))'") - } - - self = logType - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) - } -} - -extension LogType: CustomStringConvertible { - public var description: String { rawValue } -} - -public extension LogType { - enum Primary: String { - case system = "sys" - } -} diff --git a/Sources/EmbraceCommonInternal/SpanType/SpanType.swift b/Sources/EmbraceCommonInternal/SpanType/SpanType.swift deleted file mode 100644 index e23a4e6f..00000000 --- a/Sources/EmbraceCommonInternal/SpanType/SpanType.swift +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright © 2024 Embrace Mobile, Inc. All rights reserved. -// - -/// A SpanType is an Embrace specific concept to allow for better categorization of Spans. -/// - This struct will be serialized into the Span's `emb.type` attribute. -/// - This struct is encoded as a String with the format `.`. -/// - The primary category is required, but the secondary category is optional. -public struct SpanType: Equatable, Hashable { - let primary: Primary - - let secondary: String? - - public init(primary: Primary, secondary: String? = nil) { - self.primary = primary - self.secondary = secondary - } -} - -public extension SpanType { - - init(performance secondary: String) { - self.primary = .performance - self.secondary = secondary - } - - init(ux secondary: String) { - self.primary = .ux - self.secondary = secondary - } - - init(system secondary: String) { - self.primary = .system - self.secondary = secondary - } -} - -extension SpanType: RawRepresentable { - public typealias RawValue = String - - public var rawValue: String { - [primary.rawValue, secondary] - .compactMap { $0 } - .joined(separator: ".") - } - - public init?(rawValue: String) { - let components = rawValue.components(separatedBy: ".") - guard let first = components.first, - let primary = Primary(rawValue: first) else { - return nil - } - - self.primary = primary - if components.count > 1 { - self.secondary = components.dropFirst().joined(separator: ".") - } else { - self.secondary = nil - } - } -} - -extension SpanType: CustomStringConvertible { - public var description: String { rawValue } -} - -extension SpanType: Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let rawValue = try container.decode(String.self) - - guard let spanType = SpanType(rawValue: rawValue) else { - throw DecodingError.dataCorruptedError(in: container, - debugDescription: "Invalid SpanType: '\(rawValue.prefix(10))'") - } - - self = spanType - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) - } -} - -public extension SpanType { - - /// Top level category for the SpanType - enum Primary: String { - /// Category for observing a logical operation - case performance = "perf" - - /// Category for observing the user's interaction or behavior - case ux = "ux" - - /// Category for observing the system's operation or status - case system = "sys" - } -}