Skip to content

Commit

Permalink
Use Date instead of nanoseconds
Browse files Browse the repository at this point in the history
Use time constructions based on Date instead of using nanoseconds, convert to nanoseconds only on exporting. It makes usage more natural from a user standpoint.
  • Loading branch information
Ignacio Bonafonte committed Jan 8, 2021
1 parent e9ac008 commit a01d278
Show file tree
Hide file tree
Showing 47 changed files with 354 additions and 383 deletions.
4 changes: 2 additions & 2 deletions Examples/Logging Tracer/LoggingSpan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class LoggingSpan: Span {
Logger.log("Span.End, Name: \(name)")
}

public func end(timestamp: UInt64) {
Logger.log("Span.End, Name: \(name), timestamp:\(timestamp)) }")
public func end(time: Date) {
Logger.log("Span.End, Name: \(name), time:\(time)) }")
}
}
2 changes: 1 addition & 1 deletion Examples/Logging Tracer/LoggingTracer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class LoggingTracer: Tracer {
return self
}

func setStartTimestamp(timestamp: UInt64) -> Self {
func setStartTime(time: Date) -> Self {
return self
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Exporters/DatadogExporter/Logs/LogEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ internal struct DDLog: Encodable {
TracingAttributes.spanID: "\(span.spanId.rawValue)"
]

self.date = Date(timeIntervalSince1970: TimeInterval.fromNanoseconds(event.epochNanos))
self.date = event.timestamp
self.status = Status(rawValue: event.attributes["status"]?.description ?? "info") ?? .info
self.message = attributes.removeValue(forKey: "message")?.description ?? "Span event"
self.serviceName = configuration.serviceName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ internal class FilesOrchestrator {
func getAllFiles(excludingFilesNamed excludedFileNames: Set<String> = []) -> [ReadableFile]? {
do {
return try directory.files()
.filter({ excludedFileNames.contains($0.name) == false })
.filter { excludedFileNames.contains($0.name) == false }
} catch {
print("🔥 Failed to obtain readable files: \(error)")
return nil
Expand Down Expand Up @@ -183,3 +183,24 @@ internal func fileCreationDateFrom(fileName: String) -> Date {
let millisecondsSinceReferenceDate = TimeInterval(UInt64(fileName) ?? 0) / 1_000
return Date(timeIntervalSinceReferenceDate: TimeInterval(millisecondsSinceReferenceDate))
}

private enum FixedWidthIntegerError<T: BinaryFloatingPoint>: Error {
case overflow(overflowingValue: T)
}

private extension FixedWidthInteger {
/* NOTE: RUMM-182
Self(:) is commonly used for conversion, however it fatalError() in case of conversion failure
Self(exactly:) does the exact same thing internally yet it returns nil instead of fatalError()
It is not trivial to guess if the conversion would fail or succeed, therefore we use Self(exactly:)
so that we don't need to guess in order to save the app from crashing

IMPORTANT: If you pass floatingPoint to Self(exactly:) without rounded(), it may return nil
*/
init<T: BinaryFloatingPoint>(withReportingOverflow floatingPoint: T) throws {
guard let converted = Self(exactly: floatingPoint.rounded()) else {
throw FixedWidthIntegerError<T>.overflow(overflowingValue: floatingPoint)
}
self = converted
}
}
4 changes: 2 additions & 2 deletions Sources/Exporters/DatadogExporter/Spans/SpanEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ internal struct DDSpan: Encodable {

self.serviceName = configuration.serviceName
self.resource = spanData.name
self.startTime = spanData.startEpochNanos
self.duration = spanData.endEpochNanos - spanData.startEpochNanos
self.startTime = spanData.startTime.timeIntervalSince1970.toNanoseconds
self.duration = spanData.endTime.timeIntervalSince(spanData.startTime).toNanoseconds

if spanData.status.isOk {
self.error = false
Expand Down
56 changes: 0 additions & 56 deletions Sources/Exporters/DatadogExporter/Utils/SwiftExtensions.swift

This file was deleted.

8 changes: 4 additions & 4 deletions Sources/Exporters/Jaeger/Adapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ final class Adapter {
let spanHex = span.spanId.hexString
let spanId = Int64(spanHex, radix: 16) ?? 0
let operationName = span.name
let startTime = Int64(span.startEpochNanos / 1000)
let duration = Int64((span.endEpochNanos - span.startEpochNanos) / 1000)
let startTime = Int64(span.startTime.timeIntervalSince1970.toMilliseconds)
let duration = Int64(span.endTime.timeIntervalSince(span.startTime).toMilliseconds)

var parentSpanId: Int64 = 0

Expand All @@ -76,7 +76,7 @@ final class Adapter {

tags.append(Tag(key: Adapter.keySpanKind, vType: .string, vStr: span.kind.rawValue.uppercased(), vDouble: nil, vBool: nil, vLong: nil, vBinary: nil))
tags.append(Tag(key: Adapter.keySpanStatusMessage, vType: .string, vStr: span.status.statusDescription ?? "", vDouble: nil, vBool: nil, vLong: nil, vBinary: nil))
tags.append(Tag(key: Adapter.keySpanStatusCode, vType: .long, vStr: nil, vDouble: nil, vBool: nil, vLong: Int64(span.status.statusCode.rawValue ?? 0), vBinary: nil))
tags.append(Tag(key: Adapter.keySpanStatusCode, vType: .long, vStr: nil, vDouble: nil, vBool: nil, vLong: Int64(span.status.statusCode.rawValue), vBinary: nil))

if span.status != .ok {
tags.append(Tag(key: keyError, vType: .bool, vStr: nil, vDouble: nil, vBool: true, vLong: nil, vBinary: nil))
Expand Down Expand Up @@ -142,7 +142,7 @@ final class Adapter {
}

static func toJaegerLog(event: SpanData.Event) -> Log {
let timestamp = Int64(event.epochNanos * 1000)
let timestamp = Int64(event.timestamp.timeIntervalSince1970.toMilliseconds)

var tags = TList<Tag>()
tags.append(Tag(key: Adapter.keyLogMessage, vType: .string, vStr: event.name, vDouble: nil, vBool: nil, vLong: nil, vBinary: nil))
Expand Down
24 changes: 12 additions & 12 deletions Sources/Exporters/OpenTelemetryProtocol/SpanAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,22 @@ struct SpanAdapter {
}
protoSpan.name = spanData.name
protoSpan.kind = toProtoSpanKind(kind: spanData.kind)
protoSpan.startTimeUnixNano = spanData.startEpochNanos
protoSpan.endTimeUnixNano = spanData.endEpochNanos
protoSpan.startTimeUnixNano = spanData.startTime.timeIntervalSince1970.toNanoseconds
protoSpan.endTimeUnixNano = spanData.endTime.timeIntervalSince1970.toNanoseconds
spanData.attributes.forEach {
protoSpan.attributes.append(CommonAdapter.toProtoAttribute(key: $0.key, attributeValue: $0.value))
}
protoSpan.droppedAttributesCount = UInt32(spanData.totalAttributeCount - spanData.attributes.count)
spanData.events.forEach {
protoSpan.events.append(toProtoSpanEvent(event: $0))
}
protoSpan.droppedEventsCount = UInt32(spanData.totalRecordedEvents - spanData.events.count )
protoSpan.droppedEventsCount = UInt32(spanData.totalRecordedEvents - spanData.events.count)

spanData.links.forEach {
protoSpan.links.append(toProtoSpanLink(link: $0))
}
protoSpan.droppedLinksCount = UInt32(spanData.totalRecordedLinks - spanData.links.count )
protoSpan.status = toStatusProto(status: spanData.status ?? .unset)
protoSpan.droppedLinksCount = UInt32(spanData.totalRecordedLinks - spanData.links.count)
protoSpan.status = toStatusProto(status: spanData.status)
return protoSpan
}

Expand All @@ -105,7 +105,7 @@ struct SpanAdapter {
static func toProtoSpanEvent(event: SpanData.Event) -> Opentelemetry_Proto_Trace_V1_Span.Event {
var protoEvent = Opentelemetry_Proto_Trace_V1_Span.Event()
protoEvent.name = event.name
protoEvent.timeUnixNano = event.epochNanos
protoEvent.timeUnixNano = event.timestamp.timeIntervalSince1970.toNanoseconds
event.attributes.forEach {
protoEvent.attributes.append(CommonAdapter.toProtoAttribute(key: $0.key, attributeValue: $0.value))
}
Expand All @@ -125,12 +125,12 @@ struct SpanAdapter {
static func toStatusProto(status: Status) -> Opentelemetry_Proto_Trace_V1_Status {
var statusProto = Opentelemetry_Proto_Trace_V1_Status()
switch status.statusCode {
case .ok:
statusProto.code = .ok
case .unset:
statusProto.code = .unset
case .error:
statusProto.code = .error
case .ok:
statusProto.code = .ok
case .unset:
statusProto.code = .unset
case .error:
statusProto.code = .error
}
if let desc = status.statusDescription {
statusProto.message = desc
Expand Down
27 changes: 13 additions & 14 deletions Sources/Exporters/Stdout/StdoutExporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@
//

import Foundation
import OpenTelemetrySdk
import OpenTelemetryApi
import OpenTelemetrySdk

public class StdoutExporter: SpanExporter {
let isDebug: Bool

public init(isDebug: Bool = false) {
self.isDebug = isDebug
}

public func export(spans: [SpanData]) -> SpanExporterResultCode {
let jsonEncoder = JSONEncoder()
for span in spans {
if (isDebug) {
if isDebug {
print("__________________")
print("Span \(span.name):")
print("TraceId: \(span.traceId.hexString)")
Expand All @@ -36,8 +36,8 @@ public class StdoutExporter: SpanExporter {
print("TraceFlags: \(span.traceFlags)")
print("TraceState: \(span.traceState)")
print("ParentSpanId: \(span.parentSpanId?.hexString ?? SpanId.invalid.hexString)")
print("Start: \(span.startEpochNanos)")
print("Duration: \(span.endEpochNanos - span.startEpochNanos) nanoseconds")
print("Start: \(span.startTime.timeIntervalSince1970.toNanoseconds)")
print("Duration: \(span.endTime.timeIntervalSince(span.startTime).toNanoseconds) nanoseconds")
print("Attributes: \(span.attributes)")
print("------------------\n")
} else {
Expand All @@ -53,27 +53,26 @@ public class StdoutExporter: SpanExporter {
}
return .success
}

public func flush() -> SpanExporterResultCode {
return .success
}

public func shutdown() {
}
public func shutdown() {}
}

fileprivate struct SpanExporterData: Encodable {
private struct SpanExporterData: Encodable {
private let span: String
private let traceId: String
private let spanId: String
private let spanKind: String
private let traceFlags: TraceFlags
private let traceState: TraceState
private let parentSpanId: String?
private let start: UInt64
private let duration: UInt64
private let start: Date
private let duration: TimeInterval
private let attributes: [String: AttributeValue]

init(span: SpanData) {
self.span = span.name
self.traceId = span.traceId.hexString
Expand All @@ -82,8 +81,8 @@ fileprivate struct SpanExporterData: Encodable {
self.traceFlags = span.traceFlags
self.traceState = span.traceState
self.parentSpanId = span.parentSpanId?.hexString ?? SpanId.invalid.hexString
self.start = span.startEpochNanos
self.duration = span.endEpochNanos - span.startEpochNanos
self.start = span.startTime
self.duration = span.endTime.timeIntervalSince(span.startTime)
self.attributes = span.attributes
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ struct ZipkinConversionExtension {
localEndpointCache[serviceName] = localEndpoint
}
}

if let serviceNamespace = attributeEnumerationState.serviceNamespace, !serviceNamespace.isEmpty {
attributeEnumerationState.tags["service.namespace"] = serviceNamespace
}

var remoteEndpoint: ZipkinEndpoint?
if (otelSpan.kind == .client || otelSpan.kind == .producer) && attributeEnumerationState.RemoteEndpointServiceName != nil {
if otelSpan.kind == .client || otelSpan.kind == .producer, attributeEnumerationState.RemoteEndpointServiceName != nil {
remoteEndpoint = remoteEndpointCache[attributeEnumerationState.RemoteEndpointServiceName!]
if remoteEndpoint == nil {
remoteEndpoint = ZipkinEndpoint(serviceName: attributeEnumerationState.RemoteEndpointServiceName!)
Expand All @@ -90,8 +90,8 @@ struct ZipkinConversionExtension {
id: ZipkinConversionExtension.EncodeSpanId(spanId: otelSpan.spanId),
kind: ZipkinConversionExtension.toSpanKind(otelSpan: otelSpan),
name: otelSpan.name,
timestamp: otelSpan.startEpochNanos / 1000,
duration: (otelSpan.endEpochNanos - otelSpan.startEpochNanos) / 1000,
timestamp: otelSpan.startTime.timeIntervalSince1970.toMicroseconds,
duration: otelSpan.endTime.timeIntervalSince(otelSpan.startTime).toMicroseconds,
localEndpoint: localEndpoint,
remoteEndpoint: remoteEndpoint,
annotations: annotations,
Expand Down Expand Up @@ -128,7 +128,7 @@ struct ZipkinConversionExtension {
}

private static func processEvents(event: SpanData.Event) -> ZipkinAnnotation {
return ZipkinAnnotation(timestamp: event.epochNanos / 1000, value: event.name)
return ZipkinAnnotation(timestamp: event.timestamp.timeIntervalSince1970.toMicroseconds, value: event.name)
}

private static func processAttributes(state: inout AttributeEnumerationState, key: String, value: AttributeValue) {
Expand Down
25 changes: 19 additions & 6 deletions Sources/OpenTelemetryApi/Internal/SwiftExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ import Foundation
public extension TimeInterval {
/// `TimeInterval` represented in milliseconds (capped to `UInt64.max`).
var toMilliseconds: UInt64 {
let miliseconds = self * 1_000
return UInt64(withReportingOverflow: miliseconds) ?? .max
let milliseconds = self * 1_000
return UInt64(withReportingOverflow: milliseconds) ?? .max
}

var toMicroseconds: UInt64 {
let microseconds = self * 1_000_000
return UInt64(withReportingOverflow: microseconds) ?? .max
}

/// `TimeInterval` represented in nanoseconds (capped to `UInt64.max`).
Expand All @@ -28,13 +33,21 @@ public extension TimeInterval {
return UInt64(withReportingOverflow: nanoseconds) ?? .max
}

static func fromNanoseconds(_ nanos: UInt64) -> TimeInterval {
return Double(nanos) / 1000000000
static func fromMilliseconds(_ millis: Int64) -> TimeInterval {
return Double(millis) / 1_000
}

static func fromMicroseconds(_ micros: Int64) -> TimeInterval {
return Double(micros) / 1_000_000
}

static func fromNanoseconds(_ nanos: Int64) -> TimeInterval {
return Double(nanos) / 1_000_000_000
}
}

fileprivate extension FixedWidthInteger {
init?<T: BinaryFloatingPoint>(withReportingOverflow floatingPoint: T) {
private extension FixedWidthInteger {
init?<T: BinaryFloatingPoint>(withReportingOverflow floatingPoint: T) {
guard let converted = Self(exactly: floatingPoint.rounded()) else {
return nil
}
Expand Down
9 changes: 8 additions & 1 deletion Sources/OpenTelemetryApi/Trace/DefaultSpan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import Foundation
/// implementation is available. All operations are no-op except context propagation.
/// Used also to stop tracing, see Tracer.withSpan()
public class DefaultSpan: Span {

public var name: String = ""

public var kind: SpanKind
Expand All @@ -28,6 +27,14 @@ public class DefaultSpan: Span {

public var scope: Scope?

public func end() {
scope?.close()
}

public func end(time: Date) {
end()
}

/// Returns a DefaultSpan with an invalid SpanContext.
public convenience init() {
self.init(context: SpanContext.invalid, kind: .client)
Expand Down
Loading

0 comments on commit a01d278

Please sign in to comment.