/
ZipkinTraceExporter.swift
75 lines (61 loc) · 2.51 KB
/
ZipkinTraceExporter.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import Foundation
import OpenTelemetrySdk
public class ZipkinTraceExporter: SpanExporter {
public var options: ZipkinTraceExporterOptions
var localEndPoint: ZipkinEndpoint
public init(options: ZipkinTraceExporterOptions) {
self.options = options
localEndPoint = ZipkinTraceExporter.getLocalZipkinEndpoint(name: options.serviceName)
}
public func export(spans: [SpanData], explicitTimeout: TimeInterval? = nil) -> SpanExporterResultCode {
guard let url = URL(string: self.options.endpoint) else { return .failure }
var request = URLRequest(url: url)
request.timeoutInterval = min(explicitTimeout ?? TimeInterval.greatestFiniteMagnitude, options.timeoutSeconds)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
options.additionalHeaders.forEach {
request.addValue($0.value, forHTTPHeaderField: $0.key)
}
let spans = encodeSpans(spans: spans)
do {
request.httpBody = try JSONEncoder().encode(spans)
} catch {
return .failure
}
var status: SpanExporterResultCode = .failure
let sem = DispatchSemaphore(value: 0)
let task = URLSession.shared.dataTask(with: request) { _, _, error in
if error != nil {
status = .failure
} else {
status = .success
}
sem.signal()
}
task.resume()
sem.wait()
return status
}
public func flush(explicitTimeout: TimeInterval? = nil) -> SpanExporterResultCode {
return .success
}
public func shutdown(explicitTimeout: TimeInterval? = nil) {
}
func encodeSpans(spans: [SpanData]) -> [ZipkinSpan] {
return spans.map { ZipkinConversionExtension.toZipkinSpan(otelSpan: $0, defaultLocalEndpoint: localEndPoint) }
}
static func getLocalZipkinEndpoint(name: String? = nil) -> ZipkinEndpoint {
let hostname = name ?? ProcessInfo.processInfo.hostName
#if os(OSX)
let ipv4 = Host.current().addresses.filter{ NetworkUtils.isValidIpv4Address($0) }.sorted().first
let ipv6 = Host.current().addresses.filter { NetworkUtils.isValidIpv6Address($0) }.sorted().first
return ZipkinEndpoint(serviceName: hostname, ipv4: ipv4, ipv6: ipv6, port: nil)
#else
return ZipkinEndpoint(serviceName: hostname)
#endif
}
}