From 00b0be4d8920e480667232e03d04c07dceccb9ee Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 26 Sep 2023 14:46:27 +0200 Subject: [PATCH] Remove generic argument from Histogram In #92, we added a Histogram that is generic over the observed type. This means that each Histogram was also carrying a generic argument. To work around this we created typealiases for `DurationHistogram` and `ValueHistogram`. The motivation behind this was to preserve as much detailed information as possible. Duration can be more precise than Double. However in the grand scheme of things we now believe that this is overkill and a simpler to use Histogram type is more valuable. Thus this patch removes the generic argument from Histogram. --- Sources/Prometheus/Docs.docc/swift-metrics.md | 44 ++-- Sources/Prometheus/Histogram.swift | 94 +++----- .../PrometheusCollectorRegistry.swift | 200 ++++-------------- .../Prometheus/PrometheusMetricsFactory.swift | 76 +++---- Tests/PrometheusTests/HistogramTests.swift | 39 ++-- .../PrometheusCollectorRegistryTests.swift | 62 +----- .../PrometheusMetricsFactoryTests.swift | 4 +- 7 files changed, 126 insertions(+), 393 deletions(-) diff --git a/Sources/Prometheus/Docs.docc/swift-metrics.md b/Sources/Prometheus/Docs.docc/swift-metrics.md index b9c9d5d..79089bb 100644 --- a/Sources/Prometheus/Docs.docc/swift-metrics.md +++ b/Sources/Prometheus/Docs.docc/swift-metrics.md @@ -111,28 +111,19 @@ generated in a third party library. #### Default buckets -Swift Metric ``Timer``s are backed by a Prometheus ``DurationHistogram`` and Swift Metric -``Recorder``s that aggregate are backed by a Prometheus ``ValueHistogram``. As a user, you can +Swift Metric ``Timer``s are backed by a Prometheus ``Histogram`` and Swift Metric +``Recorder``s that aggregate are also backed by a Prometheus ``Histogram``. As a user, you can specify which buckets shall be used within the backing ``Histogram``s. ```swift var factory = PrometheusMetricsFactory() -factory.defaultDurationHistogramBuckets = [ - .milliseconds(5), - .milliseconds(10), - .milliseconds(25), - .milliseconds(50), - .milliseconds(100), +factory.defaultTimerHistogramBuckets = [ + 0.005, 0.01, 0.025, 0.05, 0.1 ] -factory.defaultValueHistogramBuckets = [ - 5, - 10, - 25, - 50, - 100, - 250, +factory.defaultRecorderHistogramBuckets = [ + 5, 10, 25, 50, 100, 250, ] MetricSystem.bootstrap(factory) @@ -148,28 +139,19 @@ You can also specify the buckets by metric name: ```swift var factory = PrometheusMetricsFactory() -factory.defaultDurationHistogramBuckets = [ - .milliseconds(5), - .milliseconds(10), - .milliseconds(25), - .milliseconds(50), - .milliseconds(100), +factory.defaultTimerHistogramBuckets = [ + 0.005, 0.01, 0.025, 0.05, 0.1 ] -factory.durationHistogramBuckets["long"] = [ - .seconds(5), - .seconds(10), - .seconds(25), - .seconds(50), - .seconds(100), +factory.timerHistogramBuckets["long"] = [ + 5, 10, 25, 50, 100 ] ``` Now a `Timer` with the label "long" will use the buckets -`[.seconds(5), .seconds(10), .seconds(25), .seconds(50), .seconds(100),]`, whereas any other -`Timer` will use the default buckets -`[.milliseconds(5), .milliseconds(10), .milliseconds(25), .milliseconds(50), .milliseconds(100),]`. +`[5 sec, 10 sec, 25 sec, 50 sec, 100 sec]`, whereas any other +`Timer` will use the default buckets `[5 ms, 10ms, 25ms, 50ms, 100ms]`. -The same functionality is also available for ``ValueHistogram`` and aggregating `Recorder`s. +The same functionality is also available for ``Histogram`` that back aggregating `Recorder`s. [Swift Metrics]: https://github.com/apple/swift-metrics diff --git a/Sources/Prometheus/Histogram.swift b/Sources/Prometheus/Histogram.swift index dabe2a0..99e3a0d 100644 --- a/Sources/Prometheus/Histogram.swift +++ b/Sources/Prometheus/Histogram.swift @@ -14,40 +14,28 @@ import CoreMetrics -/// A type that can be used in a ``Histogram`` to create bucket boundaries -public protocol Bucketable: AdditiveArithmetic, Comparable, Sendable { - /// A string representation that is used in the Prometheus export - var bucketRepresentation: String { get } -} - -/// A Histogram to record timings -public typealias DurationHistogram = Histogram -/// A Histogram to record floating point values -public typealias ValueHistogram = Histogram - -/// A generic Histogram implementation -public final class Histogram: Sendable { +/// A Histogram implementation, that is backed by buckets in Double +public final class Histogram: Sendable { let name: String let labels: [(String, String)] - @usableFromInline struct State: Sendable { - @usableFromInline var buckets: [(Value, Int)] - @usableFromInline var sum: Value - @usableFromInline var count: Int + var buckets: [(Double, Int)] + var sum: Double + var count: Int @inlinable - init(buckets: [Value]) { + init(buckets: [Double]) { self.sum = .zero self.count = 0 self.buckets = buckets.map { ($0, 0) } } } - @usableFromInline let box: NIOLockedValueBox + let box: NIOLockedValueBox let prerenderedLabels: [UInt8]? - init(name: String, labels: [(String, String)], buckets: [Value]) { + init(name: String, labels: [(String, String)], buckets: [Double]) { self.name = name self.labels = labels @@ -56,7 +44,7 @@ public final class Histogram: Sendable { self.box = .init(.init(buckets: buckets)) } - public func record(_ value: Value) { + public func observe(_ value: Double) { self.box.withLockedValue { state in for i in state.buckets.startIndex..= value { @@ -67,20 +55,29 @@ public final class Histogram: Sendable { state.count += 1 } } + + public func observe(_ value: Duration) { + let value = Double(value.components.seconds) + Double(value.components.attoseconds) / 1e18 + self.observe(value) + } } extension Histogram: _SwiftMetricsSendableProtocol {} -extension Histogram: CoreMetrics.TimerHandler where Value == Duration { +extension Histogram: CoreMetrics.TimerHandler { public func recordNanoseconds(_ duration: Int64) { let value = Duration.nanoseconds(duration) - self.record(value) + self.observe(value) } } -extension Histogram: CoreMetrics.RecorderHandler where Value == Double { +extension Histogram: CoreMetrics.RecorderHandler { + public func record(_ value: Double) { + self.observe(value) + } + public func record(_ value: Int64) { - self.record(Double(value)) + self.observe(Double(value)) } } @@ -96,7 +93,7 @@ extension Histogram: PrometheusMetric { buffer.append(UInt8(ascii: #","#)) } buffer.append(contentsOf: #"le=""#.utf8) - buffer.append(contentsOf: "\(bucket.0.bucketRepresentation)".utf8) + buffer.append(contentsOf: "\(bucket.0)".utf8) buffer.append(UInt8(ascii: #"""#)) buffer.append(contentsOf: #"} "#.utf8) buffer.append(contentsOf: "\(bucket.1)".utf8) @@ -124,7 +121,7 @@ extension Histogram: PrometheusMetric { } else { buffer.append(UInt8(ascii: " ")) } - buffer.append(contentsOf: "\(state.sum.bucketRepresentation)".utf8) + buffer.append(contentsOf: "\(state.sum)".utf8) buffer.append(contentsOf: #"\#n"#.utf8) // count @@ -141,46 +138,3 @@ extension Histogram: PrometheusMetric { buffer.append(contentsOf: #"\#n"#.utf8) } } - -extension Duration: Bucketable { - public var bucketRepresentation: String { - let attos = String(unsafeUninitializedCapacity: 18) { buffer in - var num = self.components.attoseconds - - var positions = 17 - var length: Int? - while positions >= 0 { - defer { - positions -= 1 - num = num / 10 - } - let remainder = num % 10 - - if length != nil { - buffer[positions] = UInt8(ascii: "0") + UInt8(remainder) - } else { - if remainder == 0 { - continue - } - - length = positions + 1 - buffer[positions] = UInt8(ascii: "0") + UInt8(remainder) - } - } - - if length == nil { - buffer[0] = UInt8(ascii: "0") - length = 1 - } - - return length! - } - return "\(self.components.seconds).\(attos)" - } -} - -extension Double: Bucketable { - public var bucketRepresentation: String { - self.description - } -} diff --git a/Sources/Prometheus/PrometheusCollectorRegistry.swift b/Sources/Prometheus/PrometheusCollectorRegistry.swift index b12c451..ff08a29 100644 --- a/Sources/Prometheus/PrometheusCollectorRegistry.swift +++ b/Sources/Prometheus/PrometheusCollectorRegistry.swift @@ -52,10 +52,8 @@ public final class PrometheusCollectorRegistry: Sendable { case counterWithLabels([String], [LabelsKey: Counter]) case gauge(Gauge) case gaugeWithLabels([String], [LabelsKey: Gauge]) - case durationHistogram(DurationHistogram) - case durationHistogramWithLabels([String], [LabelsKey: DurationHistogram], [Duration]) - case valueHistogram(ValueHistogram) - case valueHistogramWithLabels([String], [LabelsKey: ValueHistogram], [Double]) + case histogram(Histogram) + case histogramWithLabels([String], [LabelsKey: Histogram], [Double]) } private let box = NIOLockedValueBox([String: Metric]()) @@ -225,140 +223,48 @@ public final class PrometheusCollectorRegistry: Sendable { } } - /// Creates a new ``DurationHistogram`` collector or returns the already existing one with the same name. + /// Creates a new ``Histogram`` collector or returns the already existing one with the same name. /// /// When the ``PrometheusCollectorRegistry/emit(into:)`` is called, metrics from the - /// created ``DurationHistogram`` will be part of the export. + /// created ``Histogram`` will be part of the export. /// - /// - Parameter name: A name to identify ``DurationHistogram``'s value. - /// - Parameter buckets: Define the buckets that shall be used within the ``DurationHistogram`` - /// - Returns: A ``DurationHistogram`` that is registered with this ``PrometheusCollectorRegistry`` - public func makeDurationHistogram(name: String, buckets: [Duration]) -> DurationHistogram { - self.box.withLockedValue { store -> DurationHistogram in + /// - Parameter name: A name to identify ``Histogram``'s value. + /// - Parameter buckets: Define the buckets that shall be used within the ``Histogram`` + /// - Returns: A ``Histogram`` that is registered with this ``PrometheusCollectorRegistry`` + public func makeHistogram(name: String, buckets: [Double]) -> Histogram { + self.box.withLockedValue { store -> Histogram in if let value = store[name] { - guard case .durationHistogram(let histogram) = value else { - fatalError(""" - Could not make DurationHistogram with name: \(name), since another - metric type already exists for the same name. - """ - ) - } - - return histogram - } else { - let gauge = DurationHistogram(name: name, labels: [], buckets: buckets) - store[name] = .durationHistogram(gauge) - return gauge - } - } - } - - /// Creates a new ``DurationHistogram`` collector or returns the already existing one with the same name. - /// - /// When the ``PrometheusCollectorRegistry/emit(into:)`` is called, metrics from the - /// created ``DurationHistogram`` will be part of the export. - /// - /// - Parameter name: A name to identify ``DurationHistogram``'s value. - /// - Parameter labels: Labels are sets of key-value pairs that allow us to characterize and organize - /// what’s actually being measured in a Prometheus metric. - /// - Parameter buckets: Define the buckets that shall be used within the ``DurationHistogram`` - /// - Returns: A ``DurationHistogram`` that is registered with this ``PrometheusCollectorRegistry`` - public func makeDurationHistogram(name: String, labels: [(String, String)], buckets: [Duration]) -> DurationHistogram { - guard !labels.isEmpty else { - return self.makeDurationHistogram(name: name, buckets: buckets) - } - - return self.box.withLockedValue { store -> DurationHistogram in - if let value = store[name] { - guard case .durationHistogramWithLabels(let labelNames, var dimensionLookup, let storedBuckets) = value else { - fatalError(""" - Could not make DurationHistogram with name: \(name) and labels: \(labels), since another - metric type already exists for the same name. - """ - ) - } - - let key = LabelsKey(labels) - if let histogram = dimensionLookup[key] { - return histogram - } - - // check if all labels match the already existing ones. - if labelNames != labels.allLabelNames { - fatalError(""" - Could not make DurationHistogram with name: \(name) and labels: \(labels), since the - label names don't match the label names of previously registered Gauges with - the same name. - """ - ) - } - if storedBuckets != buckets { - fatalError(""" - Could not make DurationHistogram with name: \(name) and labels: \(labels), since the - buckets don't match the buckets of previously registered TimeHistograms with - the same name. - """ - ) - } - - precondition(storedBuckets == buckets) - - let histogram = DurationHistogram(name: name, labels: labels, buckets: storedBuckets) - dimensionLookup[key] = histogram - store[name] = .durationHistogramWithLabels(labelNames, dimensionLookup, storedBuckets) - return histogram - } else { - let labelNames = labels.allLabelNames - let histogram = DurationHistogram(name: name, labels: labels, buckets: buckets) - - store[name] = .durationHistogramWithLabels(labelNames, [LabelsKey(labels): histogram], buckets) - return histogram - } - } - } - - /// Creates a new ``ValueHistogram`` collector or returns the already existing one with the same name. - /// - /// When the ``PrometheusCollectorRegistry/emit(into:)`` is called, metrics from the - /// created ``ValueHistogram`` will be part of the export. - /// - /// - Parameter name: A name to identify ``ValueHistogram``'s value. - /// - Parameter buckets: Define the buckets that shall be used within the ``ValueHistogram`` - /// - Returns: A ``ValueHistogram`` that is registered with this ``PrometheusCollectorRegistry`` - public func makeValueHistogram(name: String, buckets: [Double]) -> ValueHistogram { - self.box.withLockedValue { store -> ValueHistogram in - if let value = store[name] { - guard case .valueHistogram(let histogram) = value else { + guard case .histogram(let histogram) = value else { fatalError() } return histogram } else { - let gauge = ValueHistogram(name: name, labels: [], buckets: buckets) - store[name] = .valueHistogram(gauge) + let gauge = Histogram(name: name, labels: [], buckets: buckets) + store[name] = .histogram(gauge) return gauge } } } - /// Creates a new ``ValueHistogram`` collector or returns the already existing one with the same name. + /// Creates a new ``Histogram`` collector or returns the already existing one with the same name. /// /// When the ``PrometheusCollectorRegistry/emit(into:)`` is called, metrics from the - /// created ``ValueHistogram`` will be part of the export. + /// created ``Histogram`` will be part of the export. /// - /// - Parameter name: A name to identify ``ValueHistogram``'s value. + /// - Parameter name: A name to identify ``Histogram``'s value. /// - Parameter labels: Labels are sets of key-value pairs that allow us to characterize and organize /// what’s actually being measured in a Prometheus metric. - /// - Parameter buckets: Define the buckets that shall be used within the ``ValueHistogram`` - /// - Returns: A ``ValueHistogram`` that is registered with this ``PrometheusCollectorRegistry`` - public func makeValueHistogram(name: String, labels: [(String, String)], buckets: [Double]) -> ValueHistogram { + /// - Parameter buckets: Define the buckets that shall be used within the ``Histogram`` + /// - Returns: A ``Histogram`` that is registered with this ``PrometheusCollectorRegistry`` + public func makeHistogram(name: String, labels: [(String, String)], buckets: [Double]) -> Histogram { guard !labels.isEmpty else { - return self.makeValueHistogram(name: name, buckets: buckets) + return self.makeHistogram(name: name, buckets: buckets) } - return self.box.withLockedValue { store -> ValueHistogram in + return self.box.withLockedValue { store -> Histogram in if let value = store[name] { - guard case .valueHistogramWithLabels(let labelNames, var dimensionLookup, let storedBuckets) = value else { + guard case .histogramWithLabels(let labelNames, var dimensionLookup, let storedBuckets) = value else { fatalError() } @@ -371,15 +277,15 @@ public final class PrometheusCollectorRegistry: Sendable { precondition(labelNames == labels.allLabelNames) precondition(storedBuckets == buckets) - let histogram = ValueHistogram(name: name, labels: labels, buckets: storedBuckets) + let histogram = Histogram(name: name, labels: labels, buckets: storedBuckets) dimensionLookup[key] = histogram - store[name] = .valueHistogramWithLabels(labelNames, dimensionLookup, storedBuckets) + store[name] = .histogramWithLabels(labelNames, dimensionLookup, storedBuckets) return histogram } else { let labelNames = labels.allLabelNames - let histogram = ValueHistogram(name: name, labels: labels, buckets: buckets) + let histogram = Histogram(name: name, labels: labels, buckets: buckets) - store[name] = .valueHistogramWithLabels(labelNames, [LabelsKey(labels): histogram], buckets) + store[name] = .histogramWithLabels(labelNames, [LabelsKey(labels): histogram], buckets) return histogram } } @@ -431,44 +337,22 @@ public final class PrometheusCollectorRegistry: Sendable { } } - /// Unregisters a ``DurationHistogram`` from the ``PrometheusCollectorRegistry``. This means that this ``DurationHistogram`` - /// will not be included in future ``emit(into:)`` calls. - /// - /// - Note: If the provided ``DurationHistogram`` is unknown to the registry this function call will be ignored - /// - Parameter histogram: The ``DurationHistogram`` that shall be removed from the registry - public func unregisterTimeHistogram(_ histogram: DurationHistogram) { - self.box.withLockedValue { store in - switch store[histogram.name] { - case .durationHistogram(let storedHistogram): - guard storedHistogram === histogram else { return } - store.removeValue(forKey: histogram.name) - case .durationHistogramWithLabels(let labelNames, var dimensions, let buckets): - let dimensionsKey = LabelsKey(histogram.labels) - guard dimensions[dimensionsKey] === histogram else { return } - dimensions.removeValue(forKey: dimensionsKey) - store[histogram.name] = .durationHistogramWithLabels(labelNames, dimensions, buckets) - default: - return - } - } - } - - /// Unregisters a ``ValueHistogram`` from the ``PrometheusCollectorRegistry``. This means that this ``ValueHistogram`` + /// Unregisters a ``Histogram`` from the ``PrometheusCollectorRegistry``. This means that this ``ValueHistogram`` /// will not be included in future ``emit(into:)`` calls. /// /// - Note: If the provided ``ValueHistogram`` is unknown to the registry this function call will be ignored /// - Parameter histogram: The ``ValueHistogram`` that shall be removed from the registry - public func unregisterValueHistogram(_ histogram: ValueHistogram) { + public func unregisterHistogram(_ histogram: Histogram) { self.box.withLockedValue { store in switch store[histogram.name] { - case .valueHistogram(let storedHistogram): + case .histogram(let storedHistogram): guard storedHistogram === histogram else { return } store.removeValue(forKey: histogram.name) - case .valueHistogramWithLabels(let labelNames, var dimensions, let buckets): + case .histogramWithLabels(let labelNames, var dimensions, let buckets): let dimensionsKey = LabelsKey(histogram.labels) guard dimensions[dimensionsKey] === histogram else { return } dimensions.removeValue(forKey: dimensionsKey) - store[histogram.name] = .valueHistogramWithLabels(labelNames, dimensions, buckets) + store[histogram.name] = .histogramWithLabels(labelNames, dimensions, buckets) default: return } @@ -485,38 +369,28 @@ public final class PrometheusCollectorRegistry: Sendable { case .counter(let counter): buffer.addTypeLine(label: label, type: "counter") counter.emit(into: &buffer) - + case .counterWithLabels(_, let counters): buffer.addTypeLine(label: label, type: "counter") for counter in counters.values { counter.emit(into: &buffer) } - + case .gauge(let gauge): buffer.addTypeLine(label: label, type: "gauge") gauge.emit(into: &buffer) - + case .gaugeWithLabels(_, let gauges): buffer.addTypeLine(label: label, type: "gauge") for gauge in gauges.values { gauge.emit(into: &buffer) } - - case .durationHistogram(let histogram): + + case .histogram(let histogram): buffer.addTypeLine(label: label, type: "histogram") histogram.emit(into: &buffer) - - case .durationHistogramWithLabels(_, let histograms, _): - buffer.addTypeLine(label: label, type: "histogram") - for histogram in histograms.values { - histogram.emit(into: &buffer) - } - - case .valueHistogram(let histogram): - buffer.addTypeLine(label: label, type: "histogram") - histogram.emit(into: &buffer) - - case .valueHistogramWithLabels(_, let histograms, _): + + case .histogramWithLabels(_, let histograms, _): buffer.addTypeLine(label: label, type: "histogram") for histogram in histograms.values { histogram.emit(into: &buffer) diff --git a/Sources/Prometheus/PrometheusMetricsFactory.swift b/Sources/Prometheus/PrometheusMetricsFactory.swift index a0593e7..98a9cb7 100644 --- a/Sources/Prometheus/PrometheusMetricsFactory.swift +++ b/Sources/Prometheus/PrometheusMetricsFactory.swift @@ -14,7 +14,7 @@ import CoreMetrics -/// A wrapper around ``PrometheusCollectorRegistry`` to implement the `swift-metrics` `MetricsFactory` protocol +/// A wrapper around ``PrometheusCollectorRegistry`` to implement the swift-metrics `MetricsFactory` protocol public struct PrometheusMetricsFactory: Sendable { private static let _defaultRegistry = PrometheusCollectorRegistry() @@ -27,21 +27,23 @@ public struct PrometheusMetricsFactory: Sendable { /// The underlying ``PrometheusCollectorRegistry`` that is used to generate the swift-metrics handlers public var registry: PrometheusCollectorRegistry - /// The default histogram buckets for a ``TimeHistogram``. If there is no explicit overwrite - /// via ``durationHistogramBuckets``, the buckets provided here will be used for any new - /// Swift Metrics `Timer` type. - public var defaultDurationHistogramBuckets: [Duration] + /// The default histogram buckets, to back a swift-metrics `Timer`. + /// + /// If there is no explicit overwrite via ``PrometheusMetricsFactory/timerHistogramBuckets``, + /// the buckets provided here will be used for any new swift-metrics `Timer`. + public var defaultTimerHistogramBuckets: [Double] - /// The histogram buckets for a ``TimeHistogram`` per Timer label - public var durationHistogramBuckets: [String: [Duration]] + /// The buckets for a ``Histogram`` per `Timer` name to back a swift-metrics `Timer` + public var timerHistogramBuckets: [String: [Double]] - /// The default histogram buckets for a ``ValueHistogram``. If there is no explicit overwrite - /// via ``valueHistogramBuckets``, the buckets provided here will be used for any new - /// Swift Metrics `Summary` type. - public var defaultValueHistogramBuckets: [Double] + /// The default histogram buckets, to back a swift-metrics `Recorder`, that aggregates. + /// + /// If there is no explicit overwrite via ``PrometheusMetricsFactory/recorderHistogramBuckets``, + /// the buckets provided here will be used for any new swift-metrics `Recorder`, that aggregates. + public var defaultRecorderHistogramBuckets: [Double] - /// The histogram buckets for a ``ValueHistogram`` per label - public var valueHistogramBuckets: [String: [Double]] + /// The buckets for a ``Histogram`` per `Recorder` name to back a swift-metrics `Recorder`, that aggregates. + public var recorderHistogramBuckets: [String: [Double]] /// A closure to modify the name and labels used in the Swift Metrics API. This allows users /// to overwrite the Metric names in third party packages. @@ -50,34 +52,14 @@ public struct PrometheusMetricsFactory: Sendable { public init(registry: PrometheusCollectorRegistry = Self.defaultRegistry) { self.registry = registry - self.durationHistogramBuckets = [:] - self.defaultDurationHistogramBuckets = [ - .milliseconds(5), - .milliseconds(10), - .milliseconds(25), - .milliseconds(50), - .milliseconds(100), - .milliseconds(250), - .milliseconds(500), - .seconds(1), - .milliseconds(2500), - .seconds(5), - .seconds(10), + self.timerHistogramBuckets = [:] + self.defaultTimerHistogramBuckets = [ + 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, ] - self.valueHistogramBuckets = [:] - self.defaultValueHistogramBuckets = [ - 5, - 10, - 25, - 50, - 100, - 250, - 500, - 1000, - 2500, - 5000, - 10000, + self.recorderHistogramBuckets = [:] + self.defaultRecorderHistogramBuckets = [ + 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, ] self.nameAndLabelSanitizer = { ($0, $1) } @@ -98,8 +80,8 @@ extension PrometheusMetricsFactory: CoreMetrics.MetricsFactory { public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> CoreMetrics.RecorderHandler { let (label, dimensions) = self.nameAndLabelSanitizer(label, dimensions) if aggregate { - let buckets = self.valueHistogramBuckets[label] ?? self.defaultValueHistogramBuckets - return self.registry.makeValueHistogram(name: label, labels: dimensions, buckets: buckets) + let buckets = self.recorderHistogramBuckets[label] ?? self.defaultRecorderHistogramBuckets + return self.registry.makeHistogram(name: label, labels: dimensions, buckets: buckets) } else { return self.registry.makeGauge(name: label, labels: dimensions) } @@ -111,8 +93,8 @@ extension PrometheusMetricsFactory: CoreMetrics.MetricsFactory { public func makeTimer(label: String, dimensions: [(String, String)]) -> CoreMetrics.TimerHandler { let (label, dimensions) = self.nameAndLabelSanitizer(label, dimensions) - let buckets = self.durationHistogramBuckets[label] ?? self.defaultDurationHistogramBuckets - return self.registry.makeDurationHistogram(name: label, labels: dimensions, buckets: buckets) + let buckets = self.timerHistogramBuckets[label] ?? self.defaultTimerHistogramBuckets + return self.registry.makeHistogram(name: label, labels: dimensions, buckets: buckets) } public func destroyCounter(_ handler: CoreMetrics.CounterHandler) { @@ -133,8 +115,8 @@ extension PrometheusMetricsFactory: CoreMetrics.MetricsFactory { switch handler { case let gauge as Gauge: self.registry.unregisterGauge(gauge) - case let histogram as Histogram: - self.registry.unregisterValueHistogram(histogram) + case let histogram as Histogram: + self.registry.unregisterHistogram(histogram) default: break } @@ -148,9 +130,9 @@ extension PrometheusMetricsFactory: CoreMetrics.MetricsFactory { } public func destroyTimer(_ handler: CoreMetrics.TimerHandler) { - guard let histogram = handler as? Histogram else { + guard let histogram = handler as? Histogram else { return } - self.registry.unregisterTimeHistogram(histogram) + self.registry.unregisterHistogram(histogram) } } diff --git a/Tests/PrometheusTests/HistogramTests.swift b/Tests/PrometheusTests/HistogramTests.swift index 9928189..ee748e0 100644 --- a/Tests/PrometheusTests/HistogramTests.swift +++ b/Tests/PrometheusTests/HistogramTests.swift @@ -18,11 +18,8 @@ import Prometheus final class HistogramTests: XCTestCase { func testHistogramWithoutDimensions() { let client = PrometheusCollectorRegistry() - let histogram = client.makeDurationHistogram(name: "foo", labels: [], buckets: [ - .milliseconds(100), - .milliseconds(250), - .milliseconds(500), - .seconds(1), + let histogram = client.makeHistogram(name: "foo", labels: [], buckets: [ + 0.1, 0.25, 0.5, 1, ]) var buffer = [UInt8]() @@ -92,9 +89,9 @@ final class HistogramTests: XCTestCase { """ ) - // Record 80ms + // Record 90ms buffer.removeAll(keepingCapacity: true) - histogram.recordNanoseconds(80_000_000) // 80ms + histogram.recordNanoseconds(90_000_000) // 90ms client.emit(into: &buffer) XCTAssertEqual(String(decoding: buffer, as: Unicode.UTF8.self), """ # TYPE foo histogram @@ -103,7 +100,7 @@ final class HistogramTests: XCTestCase { foo_bucket{le="0.5"} 2 foo_bucket{le="1.0"} 3 foo_bucket{le="+Inf"} 4 - foo_sum 2.28 + foo_sum 2.29 foo_count 4 """ @@ -112,11 +109,8 @@ final class HistogramTests: XCTestCase { func testHistogramWithOneDimension() { let client = PrometheusCollectorRegistry() - let histogram = client.makeDurationHistogram(name: "foo", labels: [("bar", "baz")], buckets: [ - .milliseconds(100), - .milliseconds(250), - .milliseconds(500), - .seconds(1), + let histogram = client.makeHistogram(name: "foo", labels: [("bar", "baz")], buckets: [ + 0.1, 0.25, 0.5, 1, ]) var buffer = [UInt8]() @@ -185,9 +179,9 @@ final class HistogramTests: XCTestCase { """ ) - // Record 80ms + // Record 90ms buffer.removeAll(keepingCapacity: true) - histogram.recordNanoseconds(80_000_000) // 80ms + histogram.recordNanoseconds(90_000_000) // 90ms client.emit(into: &buffer) XCTAssertEqual(String(decoding: buffer, as: Unicode.UTF8.self), """ # TYPE foo histogram @@ -196,7 +190,7 @@ final class HistogramTests: XCTestCase { foo_bucket{bar="baz",le="0.5"} 2 foo_bucket{bar="baz",le="1.0"} 3 foo_bucket{bar="baz",le="+Inf"} 4 - foo_sum{bar="baz"} 2.28 + foo_sum{bar="baz"} 2.29 foo_count{bar="baz"} 4 """ @@ -205,11 +199,8 @@ final class HistogramTests: XCTestCase { func testHistogramWithTwoDimension() { let client = PrometheusCollectorRegistry() - let histogram = client.makeDurationHistogram(name: "foo", labels: [("bar", "baz"), ("abc", "xyz")], buckets: [ - .milliseconds(100), - .milliseconds(250), - .milliseconds(500), - .seconds(1), + let histogram = client.makeHistogram(name: "foo", labels: [("bar", "baz"), ("abc", "xyz")], buckets: [ + 0.1, 0.25, 0.5, 1, ]) var buffer = [UInt8]() @@ -278,9 +269,9 @@ final class HistogramTests: XCTestCase { """ ) - // Record 80ms + // Record 90ms buffer.removeAll(keepingCapacity: true) - histogram.recordNanoseconds(80_000_000) // 80ms + histogram.recordNanoseconds(90_000_000) // 90ms client.emit(into: &buffer) XCTAssertEqual(String(decoding: buffer, as: Unicode.UTF8.self), """ # TYPE foo histogram @@ -289,7 +280,7 @@ final class HistogramTests: XCTestCase { foo_bucket{bar="baz",abc="xyz",le="0.5"} 2 foo_bucket{bar="baz",abc="xyz",le="1.0"} 3 foo_bucket{bar="baz",abc="xyz",le="+Inf"} 4 - foo_sum{bar="baz",abc="xyz"} 2.28 + foo_sum{bar="baz",abc="xyz"} 2.29 foo_count{bar="baz",abc="xyz"} 4 """ diff --git a/Tests/PrometheusTests/PrometheusCollectorRegistryTests.swift b/Tests/PrometheusTests/PrometheusCollectorRegistryTests.swift index 6766a5e..78d8e73 100644 --- a/Tests/PrometheusTests/PrometheusCollectorRegistryTests.swift +++ b/Tests/PrometheusTests/PrometheusCollectorRegistryTests.swift @@ -113,59 +113,10 @@ final class PrometheusCollectorRegistryTests: XCTestCase { } - func testAskingForTheSameTimeHistogramReturnsTheSameTimeHistogram() { + func testAskingForTheSameHistogramReturnsTheSameTimeHistogram() { let client = PrometheusCollectorRegistry() - let histogram1 = client.makeDurationHistogram(name: "foo", buckets: [.seconds(1), .seconds(2), .seconds(3)]) - let histogram2 = client.makeDurationHistogram(name: "foo", buckets: [.seconds(1), .seconds(2), .seconds(3)]) - - XCTAssert(histogram1 === histogram2) - histogram1.record(.milliseconds(2500)) - histogram2.record(.milliseconds(1500)) - - var buffer = [UInt8]() - client.emit(into: &buffer) - XCTAssertEqual(String(decoding: buffer, as: Unicode.UTF8.self), """ - # TYPE foo histogram - foo_bucket{le="1.0"} 0 - foo_bucket{le="2.0"} 1 - foo_bucket{le="3.0"} 2 - foo_bucket{le="+Inf"} 2 - foo_sum 4.0 - foo_count 2 - - """ - ) - } - - func testAskingForTheSameTimeHistogramWithLabelsReturnsTheSameTimeHistogram() { - let client = PrometheusCollectorRegistry() - let histogram1 = client.makeDurationHistogram(name: "foo", labels: [("bar", "baz")], buckets: [.seconds(1), .seconds(2), .seconds(3)]) - let histogram2 = client.makeDurationHistogram(name: "foo", labels: [("bar", "baz")], buckets: [.seconds(1), .seconds(2), .seconds(3)]) - - XCTAssert(histogram1 === histogram2) - histogram1.record(.milliseconds(2500)) - histogram2.record(.milliseconds(1500)) - - var buffer = [UInt8]() - client.emit(into: &buffer) - XCTAssertEqual(String(decoding: buffer, as: Unicode.UTF8.self), """ - # TYPE foo histogram - foo_bucket{bar="baz",le="1.0"} 0 - foo_bucket{bar="baz",le="2.0"} 1 - foo_bucket{bar="baz",le="3.0"} 2 - foo_bucket{bar="baz",le="+Inf"} 2 - foo_sum{bar="baz"} 4.0 - foo_count{bar="baz"} 2 - - """ - ) - } - - - func testAskingForTheSameValueHistogramReturnsTheSameTimeHistogram() { - let client = PrometheusCollectorRegistry() - let histogram1 = client.makeValueHistogram(name: "foo", buckets: [1, 2, 3]) - let histogram2 = client.makeValueHistogram(name: "foo", buckets: [1, 2, 3]) + let histogram1 = client.makeHistogram(name: "foo", buckets: [1, 2, 3]) + let histogram2 = client.makeHistogram(name: "foo", buckets: [1, 2, 3]) XCTAssert(histogram1 === histogram2) histogram1.record(2.5) @@ -186,10 +137,10 @@ final class PrometheusCollectorRegistryTests: XCTestCase { ) } - func testAskingForTheSameValueHistogramWithLabelsReturnsTheSameTimeHistogram() { + func testAskingForTheSameHistogramWithLabelsReturnsTheSameTimeHistogram() { let client = PrometheusCollectorRegistry() - let histogram1 = client.makeValueHistogram(name: "foo", labels: [("bar", "baz")], buckets: [1, 2, 3]) - let histogram2 = client.makeValueHistogram(name: "foo", labels: [("bar", "baz")], buckets: [1, 2, 3]) + let histogram1 = client.makeHistogram(name: "foo", labels: [("bar", "baz")], buckets: [1, 2, 3]) + let histogram2 = client.makeHistogram(name: "foo", labels: [("bar", "baz")], buckets: [1, 2, 3]) XCTAssert(histogram1 === histogram2) histogram1.record(2.5) @@ -209,5 +160,4 @@ final class PrometheusCollectorRegistryTests: XCTestCase { """ ) } - } diff --git a/Tests/PrometheusTests/PrometheusMetricsFactoryTests.swift b/Tests/PrometheusTests/PrometheusMetricsFactoryTests.swift index c1592e2..aac6273 100644 --- a/Tests/PrometheusTests/PrometheusMetricsFactoryTests.swift +++ b/Tests/PrometheusTests/PrometheusMetricsFactoryTests.swift @@ -21,7 +21,7 @@ final class PrometheusMetricsFactoryTests: XCTestCase { let factory = PrometheusMetricsFactory(registry: registry) let timer = factory.makeTimer(label: "foo", dimensions: [("bar", "baz")]) - XCTAssertNotNil(timer as? Histogram) + XCTAssertNotNil(timer as? Histogram) } func testMakeRecorders() { @@ -32,7 +32,7 @@ final class PrometheusMetricsFactoryTests: XCTestCase { XCTAssertNotNil(maybeGauge as? Gauge) let maybeRecorder = factory.makeRecorder(label: "bar", dimensions: [], aggregate: true) - XCTAssertNotNil(maybeRecorder as? Histogram) + XCTAssertNotNil(maybeRecorder as? Histogram) } func testMakeCounters() {