Skip to content

Commit 688e1fd

Browse files
MrLotU0xTimavolokhov
authored
Release 1.0.0 (#69)
* Sanitize Dimensions (#68) * Add failing test for sanitising dimension * Fix test * Add new DimensionsSanitizer * Remove labels parametrisation (#63) * Stop leaking 'le', 'quantile' in Summary/Histogram labels, remove labels parametrisation * chore: Remove base labels protocol and add ExpressibleByArrayLiteral * chore: Cleanup documentation. Remove deprecations Co-authored-by: Jari (LotU) <[email protected]> * Add async/await APIs (#67) * Add async/await APIs * Revert unrelated change * Add #if swift for 5.2 * Fix Swift version number check * Add task API Co-authored-by: Tim Condon <[email protected]> Co-authored-by: Anton <[email protected]>
1 parent 36740d1 commit 688e1fd

21 files changed

+437
-584
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
build/
66
.swiftpm/
77
.idea
8+
.vscode/

Diff for: Package.swift

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ let package = Package(
77
products: [
88
.library(
99
name: "SwiftPrometheus",
10-
targets: ["Prometheus"]),
11-
.executable(
12-
name: "PrometheusExample",
13-
targets: ["PrometheusExample"]),
10+
targets: ["Prometheus"])
1411
],
1512
dependencies: [
1613
.package(url: "https://github.com/apple/swift-metrics.git", from: "2.2.0"),

Diff for: README.md

+8-18
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,18 @@ summary.observe(4.7) // Observe the given value
102102
```
103103

104104
## Labels
105-
All metric types support adding labels, allowing for grouping of related metrics.
105+
All metric types support adding labels, allowing for grouping of related metrics. Labels are passed when recording values to your metric as an instance of `DimensionLabels`, or as an array of `(String, String)`.
106106

107107
Example with a counter:
108108

109109
```swift
110-
struct RouteLabels: MetricLabels {
111-
var route: String = "*"
112-
}
113-
114-
let counter = myProm.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter", withLabelType: RouteLabels.self)
110+
let counter = myProm.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter")
115111

116-
let counter = prom.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter", withLabelType: RouteLabels.self)
112+
let counter = prom.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter")
117113

118-
counter.inc(12, .init(route: "/"))
114+
counter.inc(12, .init([("route", "/users")]))
115+
// OR
116+
counter.inc(12, [("route", "/users")])
119117
```
120118

121119
# Exporting
@@ -125,16 +123,8 @@ Prometheus itself is designed to "pull" metrics from a destination. Following th
125123
By default, this should be accessible on your main serving port, at the `/metrics` endpoint. An example in [Vapor](https://vapor.codes) 4 syntax looks like:
126124

127125
```swift
128-
app.get("metrics") { req -> EventLoopFuture<String> in
129-
let promise = req.eventLoop.makePromise(of: String.self)
130-
DispatchQueue.global().async {
131-
do {
132-
try MetricsSystem.prometheus().collect(into: promise)
133-
} catch {
134-
promise.fail(error)
135-
}
136-
}
137-
return promise.futureResult
126+
app.get("metrics") { req async throws -> String in
127+
return try await MetricsSystem.prometheus().collect()
138128
}
139129
```
140130

Diff for: Sources/Prometheus/MetricTypes/Counter.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import NIOConcurrencyHelpers
33
/// Prometheus Counter metric
44
///
55
/// See: https://prometheus.io/docs/concepts/metric_types/#counter
6-
public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, PrometheusHandled {
6+
public class PromCounter<NumType: Numeric>: PromMetric, PrometheusHandled {
77
/// Prometheus instance that created this Counter
88
internal weak var prometheus: PrometheusClient?
99

@@ -22,7 +22,7 @@ public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, Pr
2222
private let initialValue: NumType
2323

2424
/// Storage of values that have labels attached
25-
internal var metrics: [Labels: NumType] = [:]
25+
internal var metrics: [DimensionLabels: NumType] = [:]
2626

2727
/// Lock used for thread safety
2828
internal let lock: Lock
@@ -75,7 +75,7 @@ public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, Pr
7575
/// - labels: Labels to attach to the value
7676
///
7777
@discardableResult
78-
public func inc(_ amount: NumType = 1, _ labels: Labels? = nil) -> NumType {
78+
public func inc(_ amount: NumType = 1, _ labels: DimensionLabels? = nil) -> NumType {
7979
return self.lock.withLock {
8080
if let labels = labels {
8181
var val = self.metrics[labels] ?? self.initialValue
@@ -95,7 +95,7 @@ public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, Pr
9595
/// - labels: Labels to get the value for
9696
///
9797
/// - Returns: The value of the Counter attached to the provided labels
98-
public func get(_ labels: Labels? = nil) -> NumType {
98+
public func get(_ labels: DimensionLabels? = nil) -> NumType {
9999
return self.lock.withLock {
100100
if let labels = labels {
101101
return self.metrics[labels] ?? initialValue

Diff for: Sources/Prometheus/MetricTypes/Gauge.swift

+11-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import NIOConcurrencyHelpers
55
/// Prometheus Gauge metric
66
///
77
/// See https://prometheus.io/docs/concepts/metric_types/#gauge
8-
public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: PromMetric, PrometheusHandled {
8+
public class PromGauge<NumType: DoubleRepresentable>: PromMetric, PrometheusHandled {
99
/// Prometheus instance that created this Gauge
1010
internal weak var prometheus: PrometheusClient?
1111

@@ -24,7 +24,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
2424
private let initialValue: NumType
2525

2626
/// Storage of values that have labels attached
27-
private var metrics: [Labels: NumType] = [:]
27+
private var metrics: [DimensionLabels: NumType] = [:]
2828

2929
/// Lock used for thread safety
3030
private let lock: Lock
@@ -78,7 +78,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
7878
///
7979
/// - Returns: The value of the Gauge attached to the provided labels
8080
@discardableResult
81-
public func setToCurrentTime(_ labels: Labels? = nil) -> NumType {
81+
public func setToCurrentTime(_ labels: DimensionLabels? = nil) -> NumType {
8282
return self.set(.init(Date().timeIntervalSince1970), labels)
8383
}
8484

@@ -94,7 +94,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
9494
///
9595
/// - Returns: The same type of function passed in for `body`, but wrapped to track progress.
9696
@inlinable
97-
public func trackInProgress<T>(_ labels: Labels? = nil, _ body: @escaping () throws -> T) -> (() throws -> T) {
97+
public func trackInProgress<T>(_ labels: DimensionLabels? = nil, _ body: @escaping () throws -> T) -> (() throws -> T) {
9898
return {
9999
self.inc()
100100
defer {
@@ -109,7 +109,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
109109
/// - labels: Labels to attach to the resulting value.
110110
/// - body: Closure to run & record execution time of.
111111
@inlinable
112-
public func time<T>(_ labels: Labels? = nil, _ body: @escaping () throws -> T) rethrows -> T {
112+
public func time<T>(_ labels: DimensionLabels? = nil, _ body: @escaping () throws -> T) rethrows -> T {
113113
let start = DispatchTime.now().uptimeNanoseconds
114114
defer {
115115
let delta = Double(DispatchTime.now().uptimeNanoseconds - start)
@@ -127,7 +127,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
127127
///
128128
/// - Returns: The value of the Gauge attached to the provided labels
129129
@discardableResult
130-
public func set(_ amount: NumType, _ labels: Labels? = nil) -> NumType {
130+
public func set(_ amount: NumType, _ labels: DimensionLabels? = nil) -> NumType {
131131
return self.lock.withLock {
132132
if let labels = labels {
133133
self.metrics[labels] = amount
@@ -147,7 +147,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
147147
///
148148
/// - Returns: The value of the Gauge attached to the provided labels
149149
@discardableResult
150-
public func inc(_ amount: NumType, _ labels: Labels? = nil) -> NumType {
150+
public func inc(_ amount: NumType, _ labels: DimensionLabels? = nil) -> NumType {
151151
return self.lock.withLock {
152152
if let labels = labels {
153153
var val = self.metrics[labels] ?? self.initialValue
@@ -168,7 +168,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
168168
///
169169
/// - Returns: The value of the Gauge attached to the provided labels
170170
@discardableResult
171-
public func inc(_ labels: Labels? = nil) -> NumType {
171+
public func inc(_ labels: DimensionLabels? = nil) -> NumType {
172172
return self.inc(1, labels)
173173
}
174174

@@ -180,7 +180,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
180180
///
181181
/// - Returns: The value of the Gauge attached to the provided labels
182182
@discardableResult
183-
public func dec(_ amount: NumType, _ labels: Labels? = nil) -> NumType {
183+
public func dec(_ amount: NumType, _ labels: DimensionLabels? = nil) -> NumType {
184184
return self.lock.withLock {
185185
if let labels = labels {
186186
var val = self.metrics[labels] ?? self.initialValue
@@ -201,7 +201,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
201201
///
202202
/// - Returns: The value of the Gauge attached to the provided labels
203203
@discardableResult
204-
public func dec(_ labels: Labels? = nil) -> NumType {
204+
public func dec(_ labels: DimensionLabels? = nil) -> NumType {
205205
return self.dec(1, labels)
206206
}
207207

@@ -211,7 +211,7 @@ public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: Prom
211211
/// - labels: Labels to get the value for
212212
///
213213
/// - Returns: The value of the Gauge attached to the provided labels
214-
public func get(_ labels: Labels? = nil) -> NumType {
214+
public func get(_ labels: DimensionLabels? = nil) -> NumType {
215215
return self.lock.withLock {
216216
if let labels = labels {
217217
return self.metrics[labels] ?? initialValue

Diff for: Sources/Prometheus/MetricTypes/Histogram.swift

+18-40
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,10 @@ public struct Buckets: ExpressibleByArrayLiteral {
6363
}
6464
}
6565

66-
/// Label type Histograms can use
67-
public protocol HistogramLabels: MetricLabels {
68-
/// Bucket
69-
var le: String { get set }
70-
}
71-
72-
extension HistogramLabels {
73-
/// Creates empty HistogramLabels
74-
init() {
75-
self.init()
76-
self.le = ""
77-
}
78-
}
79-
8066
/// Prometheus Histogram metric
8167
///
8268
/// See https://prometheus.io/docs/concepts/metric_types/#Histogram
83-
public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels>: PromMetric, PrometheusHandled {
69+
public class PromHistogram<NumType: DoubleRepresentable>: PromMetric, PrometheusHandled {
8470
/// Prometheus instance that created this Histogram
8571
internal weak var prometheus: PrometheusClient?
8672

@@ -93,19 +79,16 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
9379
public let _type: PromMetricType = .histogram
9480

9581
/// Bucketed values for this Histogram
96-
private var buckets: [PromCounter<NumType, EmptyLabels>] = []
82+
private var buckets: [PromCounter<NumType>] = []
9783

9884
/// Buckets used by this Histogram
9985
internal let upperBounds: [Double]
10086

101-
/// Labels for this Histogram
102-
internal let labels: Labels
103-
10487
/// Sub Histograms for this Histogram
105-
fileprivate var subHistograms: [Labels: PromHistogram<NumType, Labels>] = [:]
88+
fileprivate var subHistograms: [DimensionLabels: PromHistogram<NumType>] = [:]
10689

10790
/// Total value of the Histogram
108-
private let sum: PromCounter<NumType, EmptyLabels>
91+
private let sum: PromCounter<NumType>
10992

11093
/// Lock used for thread safety
11194
private let lock: Lock
@@ -115,19 +98,16 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
11598
/// - Parameters:
11699
/// - name: Name of the Histogram
117100
/// - help: Help text of the Histogram
118-
/// - labels: Labels for the Histogram
119101
/// - buckets: Buckets to use for the Histogram
120102
/// - p: Prometheus instance creating this Histogram
121-
internal init(_ name: String, _ help: String? = nil, _ labels: Labels = Labels(), _ buckets: Buckets = .defaultBuckets, _ p: PrometheusClient) {
103+
internal init(_ name: String, _ help: String? = nil, _ buckets: Buckets = .defaultBuckets, _ p: PrometheusClient) {
122104
self.name = name
123105
self.help = help
124106

125107
self.prometheus = p
126108

127109
self.sum = .init("\(self.name)_sum", nil, 0, p)
128110

129-
self.labels = labels
130-
131111
self.upperBounds = buckets.buckets
132112

133113
self.lock = Lock()
@@ -142,8 +122,8 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
142122
/// - Returns:
143123
/// Newline separated Prometheus formatted metric string
144124
public func collect() -> String {
145-
let (buckets, subHistograms, labels) = self.lock.withLock {
146-
(self.buckets, self.subHistograms, self.labels)
125+
let (buckets, subHistograms) = self.lock.withLock {
126+
(self.buckets, self.subHistograms)
147127
}
148128

149129
var output = [String]()
@@ -157,13 +137,13 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
157137
collectBuckets(buckets: buckets,
158138
upperBounds: self.upperBounds,
159139
name: self.name,
160-
labels: labels,
140+
labels: nil,
161141
sum: self.sum.get(),
162142
into: &output)
163143

164144
subHistograms.forEach { subHistogram in
165145
let (subHistogramBuckets, subHistogramLabels) = self.lock.withLock {
166-
(subHistogram.value.buckets, subHistogram.value.labels)
146+
(subHistogram.value.buckets, subHistogram.key)
167147
}
168148
collectBuckets(buckets: subHistogramBuckets,
169149
upperBounds: subHistogram.value.upperBounds,
@@ -175,22 +155,20 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
175155
return output.joined(separator: "\n")
176156
}
177157

178-
private func collectBuckets(buckets: [PromCounter<NumType, EmptyLabels>],
158+
private func collectBuckets(buckets: [PromCounter<NumType>],
179159
upperBounds: [Double],
180160
name: String,
181-
labels: Labels,
161+
labels: DimensionLabels?,
182162
sum: NumType,
183163
into output: inout [String]) {
184-
var labels = labels
185164
var acc: NumType = 0
186165
for (i, bound) in upperBounds.enumerated() {
187166
acc += buckets[i].get()
188-
labels.le = bound.description
189-
let labelsString = encodeLabels(labels)
167+
let labelsString = encodeLabels(EncodableHistogramLabels(labels: labels, le: bound.description))
190168
output.append("\(name)_bucket\(labelsString) \(acc)")
191169
}
192170

193-
let labelsString = encodeLabels(labels, ["le"])
171+
let labelsString = encodeLabels(EncodableHistogramLabels(labels: labels))
194172
output.append("\(name)_count\(labelsString) \(acc)")
195173

196174
output.append("\(name)_sum\(labelsString) \(sum)")
@@ -201,8 +179,8 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
201179
/// - Parameters:
202180
/// - value: Value to observe
203181
/// - labels: Labels to attach to the observed value
204-
public func observe(_ value: NumType, _ labels: Labels? = nil) {
205-
if let labels = labels, type(of: labels) != type(of: EmptyHistogramLabels()) {
182+
public func observe(_ value: NumType, _ labels: DimensionLabels? = nil) {
183+
if let labels = labels {
206184
self.getOrCreateHistogram(with: labels)
207185
.observe(value)
208186
}
@@ -222,7 +200,7 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
222200
/// - labels: Labels to attach to the resulting value.
223201
/// - body: Closure to run & record.
224202
@inlinable
225-
public func time<T>(_ labels: Labels? = nil, _ body: @escaping () throws -> T) rethrows -> T {
203+
public func time<T>(_ labels: DimensionLabels? = nil, _ body: @escaping () throws -> T) rethrows -> T {
226204
let start = DispatchTime.now().uptimeNanoseconds
227205
defer {
228206
let delta = Double(DispatchTime.now().uptimeNanoseconds - start)
@@ -232,7 +210,7 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
232210
}
233211

234212
/// Helper for histograms & labels
235-
fileprivate func getOrCreateHistogram(with labels: Labels) -> PromHistogram<NumType, Labels> {
213+
fileprivate func getOrCreateHistogram(with labels: DimensionLabels) -> PromHistogram<NumType> {
236214
let subHistograms = lock.withLock { self.subHistograms }
237215
if let histogram = subHistograms[labels] {
238216
precondition(histogram.name == self.name,
@@ -262,7 +240,7 @@ public class PromHistogram<NumType: DoubleRepresentable, Labels: HistogramLabels
262240
return histogram
263241
}
264242
guard let prometheus = prometheus else { fatalError("Lingering Histogram") }
265-
let newHistogram = PromHistogram(self.name, self.help, labels, Buckets(self.upperBounds), prometheus)
243+
let newHistogram = PromHistogram(self.name, self.help, Buckets(self.upperBounds), prometheus)
266244
self.subHistograms[labels] = newHistogram
267245
return newHistogram
268246
}

Diff for: Sources/Prometheus/MetricTypes/PromMetric.swift

-22
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,3 @@ internal protocol PrometheusHandled {
5050
/// Prometheus client handling this metric
5151
var prometheus: PrometheusClient? { get }
5252
}
53-
54-
/// Base MetricLabels protocol
55-
///
56-
/// MetricLabels are used to enrich & specify metrics.
57-
///
58-
/// struct Labels: MetricLabels {
59-
/// let status: String = "unknown"
60-
/// }
61-
/// let counter = myProm.createCounter(...)
62-
/// counter.inc(12, labels: Labels(status: "failure")
63-
/// counter.inc(1, labels: Labels(status: "success")
64-
/// Will result in the following Prometheus output:
65-
///
66-
/// # TYPE my_counter counter
67-
/// my_counter 0
68-
/// my_counter{status="unknown"} 0
69-
/// my_counter{status="failure"} 12
70-
/// my_counter{status="success"} 1
71-
public protocol MetricLabels: Encodable, Hashable {
72-
/// Create empty labels
73-
init()
74-
}

0 commit comments

Comments
 (0)