Skip to content

Commit 6912892

Browse files
author
Ignacio Bonafonte
committed
Merge branch 'main' into network-instrumentation
2 parents d829f17 + 62dfeac commit 6912892

File tree

8 files changed

+132
-72
lines changed

8 files changed

+132
-72
lines changed

Sources/OpenTelemetrySdk/Metrics/Configuration/MeterSharedState.swift

Lines changed: 0 additions & 33 deletions
This file was deleted.

Sources/OpenTelemetrySdk/Metrics/Export/MetricExporter.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,48 @@ public enum MetricExporterResultCode {
1919
case success
2020
case failureNotRetryable
2121
case failureRetryable
22+
23+
/// Merges the current result code with other result code
24+
/// - Parameter newResultCode: the result code to merge with
25+
mutating func mergeResultCode(newResultCode: MetricExporterResultCode) {
26+
// If both results are success then return success.
27+
if self == .success, newResultCode == .success {
28+
self = .success
29+
return
30+
} else if self == .failureRetryable || self == .success,
31+
newResultCode == .failureRetryable || newResultCode == .success
32+
{
33+
self = .failureRetryable
34+
}
35+
self = .failureNotRetryable
36+
}
2237
}
2338

2439
public protocol MetricExporter {
2540
func export(metrics: [Metric], shouldCancel: (() -> Bool)?) -> MetricExporterResultCode
2641
}
2742

43+
/// Implementation of the SpanExporter that simply forwards all received spans to a list of
44+
/// SpanExporter.
45+
/// Can be used to export to multiple backends using the same SpanProcessor} like a impleSampledSpansProcessor
46+
/// or a BatchSampledSpansProcessor.
47+
struct MultiMetricExporter: MetricExporter {
48+
49+
var metricExporters: [MetricExporter]
50+
51+
init(metricExporters: [MetricExporter]) {
52+
self.metricExporters = metricExporters
53+
}
54+
55+
func export(metrics: [Metric], shouldCancel: (() -> Bool)?) -> MetricExporterResultCode {
56+
var currentResultCode = MetricExporterResultCode.success
57+
metricExporters.forEach {
58+
currentResultCode.mergeResultCode(newResultCode: $0.export(metrics: metrics, shouldCancel: shouldCancel))
59+
}
60+
return currentResultCode
61+
}
62+
}
63+
2864
struct NoopMetricExporter: MetricExporter {
2965
func export(metrics: [Metric], shouldCancel: (() -> Bool)?) -> MetricExporterResultCode {
3066
return .success

Sources/OpenTelemetrySdk/Metrics/Export/MetricProcessorSdk.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,17 @@
1313
// limitations under the License.
1414
//
1515

16-
1716
import Foundation
1817

19-
public class MetricProcessorSdk : MetricProcessor {
20-
private let lock : Lock
21-
var metrics : [Metric]
22-
18+
public class MetricProcessorSdk: MetricProcessor {
19+
private let lock: Lock
20+
var metrics: [Metric]
21+
2322
public init() {
2423
metrics = [Metric]()
2524
lock = Lock()
2625
}
27-
26+
2827
/// Finish the current collection cycle and return the metrics it holds.
2928
/// This is called at the end of one collection cycle by the Controller.
3029
/// MetricProcessor can use this to clear its Metrics (in case of stateless).
@@ -36,7 +35,7 @@ public class MetricProcessorSdk : MetricProcessor {
3635
}
3736
return metrics
3837
}
39-
38+
4039
/// Process the metric. This method is called once every collection interval.
4140
/// - Parameters:
4241
/// - metric: the metric record.
@@ -47,8 +46,5 @@ public class MetricProcessorSdk : MetricProcessor {
4746
}
4847

4948
metrics.append(metric)
50-
5149
}
52-
53-
5450
}

Sources/OpenTelemetrySdk/Metrics/Export/PushMetricController.swift

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,31 @@
1515
import Foundation
1616

1717
class PushMetricController {
18-
public private(set) var pushInterval: TimeInterval
19-
var metricExporter: MetricExporter
20-
var metricProcessor: MetricProcessor
18+
var meterSharedState: MeterSharedState
2119
var meterProvider: MeterProviderSdk
2220

2321
let pushMetricQueue = DispatchQueue(label: "org.opentelemetry.PushMetricController.pushMetricQueue")
2422

25-
init(meterProvider: MeterProviderSdk, metricProcessor: MetricProcessor, metricExporter: MetricExporter, pushInterval: TimeInterval, shouldCancel: (() -> Bool)? = nil) {
23+
init(meterProvider: MeterProviderSdk, meterSharedState: MeterSharedState, shouldCancel: (() -> Bool)? = nil) {
2624
self.meterProvider = meterProvider
27-
self.metricProcessor = metricProcessor
28-
self.metricExporter = metricExporter
29-
self.pushInterval = pushInterval
30-
pushMetricQueue.asyncAfter(deadline: .now() + pushInterval) { [weak self] in
25+
self.meterSharedState = meterSharedState
26+
pushMetricQueue.asyncAfter(deadline: .now() + meterSharedState.metricPushInterval) { [weak self] in
3127
guard let self = self else {
3228
return
3329
}
3430
while !(shouldCancel?() ?? false) {
3531
autoreleasepool {
3632
let start = Date()
3733
let values = self.meterProvider.getMeters().values
38-
for index in values.indices {
39-
values[index].collect()
34+
values.forEach {
35+
$0.collect()
4036
}
4137

42-
let metricToExport = self.metricProcessor.finishCollectionCycle()
38+
let metricToExport = self.meterSharedState.metricProcessor.finishCollectionCycle()
4339

44-
_ = metricExporter.export(metrics: metricToExport, shouldCancel: shouldCancel)
40+
_ = meterSharedState.metricExporter.export(metrics: metricToExport, shouldCancel: shouldCancel)
4541
let timeInterval = Date().timeIntervalSince(start)
46-
let remainingWait = pushInterval - timeInterval
42+
let remainingWait = meterSharedState.metricPushInterval - timeInterval
4743
if remainingWait > 0 {
4844
usleep(UInt32(remainingWait * 1000000))
4945
}

Sources/OpenTelemetrySdk/Metrics/Configuration/MeterProviderSdk.swift renamed to Sources/OpenTelemetrySdk/Metrics/MeterProviderSdk.swift

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,35 @@ import OpenTelemetryApi
1818

1919
public class MeterProviderSdk: MeterProvider {
2020
private let lock = Lock()
21-
static public let defaultPushInterval: TimeInterval = 60
21+
public static let defaultPushInterval: TimeInterval = 60
2222

2323
var meterRegistry = [InstrumentationLibraryInfo: MeterSdk]()
2424

25-
var meterSharedState : MeterSharedState
25+
var meterSharedState: MeterSharedState
2626
var pushMetricController: PushMetricController!
2727
var defaultMeter: MeterSdk
2828

2929
public convenience init() {
3030
self.init(metricProcessor: NoopMetricProcessor(),
3131
metricExporter: NoopMetricExporter())
3232
}
33-
33+
3434
public init(metricProcessor: MetricProcessor,
3535
metricExporter: MetricExporter,
3636
metricPushInterval: TimeInterval = MeterProviderSdk.defaultPushInterval,
37-
resource: Resource = EnvVarResource.resource) {
38-
self.meterSharedState = MeterSharedState(metricProcessor:metricProcessor, metricPushInterval: metricPushInterval, resource: resource)
37+
resource: Resource = EnvVarResource.resource)
38+
{
39+
self.meterSharedState = MeterSharedState(metricProcessor: metricProcessor, metricPushInterval: metricPushInterval, metricExporter: metricExporter, resource: resource)
3940

4041
defaultMeter = MeterSdk(meterSharedState: self.meterSharedState, instrumentationLibraryInfo: InstrumentationLibraryInfo())
4142

4243
pushMetricController = PushMetricController(
4344
meterProvider: self,
44-
metricProcessor: metricProcessor,
45-
metricExporter: metricExporter,
46-
pushInterval: meterSharedState.metricPushInterval) {
47-
false
45+
meterSharedState: self.meterSharedState) {
46+
false
4847
}
4948
}
5049

51-
52-
5350
public func get(instrumentationName: String, instrumentationVersion: String? = nil) -> Meter {
5451
if instrumentationName.isEmpty {
5552
return defaultMeter
@@ -59,7 +56,7 @@ public class MeterProviderSdk: MeterProvider {
5956
defer {
6057
lock.unlock()
6158
}
62-
let instrumentationLibraryInfo = InstrumentationLibraryInfo(name: instrumentationName, version: instrumentationVersion)
59+
let instrumentationLibraryInfo = InstrumentationLibraryInfo(name: instrumentationName, version: instrumentationVersion)
6360
var meter: MeterSdk! = meterRegistry[instrumentationLibraryInfo]
6461
if meter == nil {
6562
meter = MeterSdk(meterSharedState: self.meterSharedState, instrumentationLibraryInfo: instrumentationLibraryInfo)
@@ -76,6 +73,30 @@ public class MeterProviderSdk: MeterProvider {
7673
return meterRegistry
7774
}
7875

76+
public func setMetricProcessor(_ metricProcessor: MetricProcessor) {
77+
pushMetricController.pushMetricQueue.sync {
78+
meterSharedState.metricProcessor = metricProcessor
79+
}
80+
}
81+
82+
public func addMetricExporter(_ metricExporter: MetricExporter) {
83+
pushMetricController.pushMetricQueue.sync {
84+
meterSharedState.addMetricExporter(metricExporter: metricExporter)
85+
}
86+
}
87+
88+
public func setMetricPushInterval(_ interval: TimeInterval) {
89+
pushMetricController.pushMetricQueue.sync {
90+
meterSharedState.metricPushInterval = interval
91+
}
92+
}
93+
94+
public func setResource(_ resource: Resource) {
95+
pushMetricController.pushMetricQueue.sync {
96+
meterSharedState.resource = resource
97+
}
98+
}
99+
79100
private static func createLibraryResourceLabels(name: String, version: String) -> [String: String] {
80101
var labels = ["name": name]
81102
if !version.isEmpty {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2020, OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
import Foundation
17+
18+
class MeterSharedState {
19+
/// Configures metric processor. (aka batcher).
20+
var metricProcessor: MetricProcessor
21+
/// Sets the push interval.
22+
var metricPushInterval: TimeInterval
23+
/// Sets the exporter
24+
var metricExporter: MetricExporter
25+
26+
var resource: Resource
27+
28+
init(metricProcessor: MetricProcessor, metricPushInterval: TimeInterval, metricExporter: MetricExporter, resource: Resource) {
29+
self.metricProcessor = metricProcessor
30+
self.metricPushInterval = metricPushInterval
31+
self.metricExporter = metricExporter
32+
self.resource = resource
33+
}
34+
35+
func addMetricExporter(metricExporter: MetricExporter) {
36+
if metricExporter is NoopMetricExporter {
37+
self.metricExporter = metricExporter
38+
} else if var multiMetricExporter = metricExporter as? MultiMetricExporter {
39+
multiMetricExporter.metricExporters.append(metricExporter)
40+
} else {
41+
self.metricExporter = MultiMetricExporter(metricExporters: [self.metricExporter, metricExporter])
42+
}
43+
}
44+
}

Sources/OpenTelemetrySdk/Trace/Export/SpanExporter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public enum SpanExporterResultCode {
4343

4444
/// Merges the current result code with other result code
4545
/// - Parameter newResultCode: the result code to merge with
46-
public mutating func mergeResultCode(newResultCode: SpanExporterResultCode) {
46+
mutating func mergeResultCode(newResultCode: SpanExporterResultCode) {
4747
// If both results are success then return success.
4848
if self == .success && newResultCode == .success {
4949
self = .success

Tests/OpenTelemetrySdkTests/Metrics/PushControllerTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ final class PushControllerTests: XCTestCase {
3838
// Setup 2 meters whose Collect will increment the collect count.
3939
var meter1CollectCount = 0
4040
var meter2CollectCount = 0
41-
let meterSharedState = MeterSharedState(metricProcessor: testProcessor, metricPushInterval: MeterProviderSdk.defaultPushInterval, resource: Resource())
41+
let meterSharedState = MeterSharedState(metricProcessor: testProcessor, metricPushInterval: controllerPushIntervalInSec, metricExporter: testExporter, resource: Resource())
4242

4343
let meterInstrumentationLibrary1 = InstrumentationLibraryInfo(name:"meter1")
4444

@@ -60,7 +60,7 @@ final class PushControllerTests: XCTestCase {
6060

6161
let pushInterval = controllerPushIntervalInSec
6262

63-
let controller = PushMetricController(meterProvider: meterProvider, metricProcessor: testProcessor, metricExporter: testExporter, pushInterval: pushInterval)
63+
let controller = PushMetricController(meterProvider: meterProvider, meterSharedState: meterSharedState)
6464

6565
// Validate that collect is called on Meter1, Meter2.
6666
validateMeterCollect(meterCollectCount: &meter1CollectCount, expectedMeterCollectCount: collectionCountExpectedMin, meterName: "meter1", timeout: maxWaitInSec)
@@ -70,7 +70,7 @@ final class PushControllerTests: XCTestCase {
7070
lock.withLockVoid {
7171
XCTAssertTrue(exportCalledCount >= collectionCountExpectedMin)
7272
}
73-
XCTAssertEqual(controller.pushInterval, pushInterval)
73+
XCTAssertEqual(controller.meterSharedState.metricPushInterval, pushInterval)
7474
}
7575

7676
func validateMeterCollect(meterCollectCount: inout Int, expectedMeterCollectCount: Int, meterName: String, timeout: TimeInterval) {

0 commit comments

Comments
 (0)