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() {