Skip to content

Commit 354fa7e

Browse files
committed
Merge branch 'master' into feature/floating-point-counter
2 parents 475dc0d + e85499c commit 354fa7e

File tree

7 files changed

+149
-106
lines changed

7 files changed

+149
-106
lines changed

Sources/Prometheus/MetricTypes/PromMetric.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public enum PromMetricType: String {
1313
}
1414

1515
public enum Prometheus {
16+
/// Default capacity of Summaries
17+
public static let defaultSummaryCapacity = 500
18+
1619
/// Default quantiles used by Summaries
1720
public static let defaultQuantiles = [0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999]
1821
}

Sources/Prometheus/MetricTypes/Summary.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import NIOConcurrencyHelpers
2+
import NIO
23
import struct CoreMetrics.TimeUnit
34
import Dispatch
45

@@ -43,8 +44,11 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
4344
private let count: PromCounter<NumType, EmptyLabels>
4445

4546
/// Values in this Summary
46-
private var values: [NumType] = []
47-
47+
private var values: CircularBuffer<NumType>
48+
49+
/// Number of values to keep for calculating quantiles
50+
internal let capacity: Int
51+
4852
/// Quantiles used by this Summary
4953
internal let quantiles: [Double]
5054

@@ -60,9 +64,10 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
6064
/// - name: Name of the Summary
6165
/// - help: Help text of the Summary
6266
/// - labels: Labels for the Summary
67+
/// - capacity: Number of values to keep for calculating quantiles
6368
/// - quantiles: Quantiles to use for the Summary
6469
/// - p: Prometheus instance creating this Summary
65-
internal init(_ name: String, _ help: String? = nil, _ labels: Labels = Labels(), _ quantiles: [Double] = Prometheus.defaultQuantiles, _ p: PrometheusClient) {
70+
internal init(_ name: String, _ help: String? = nil, _ labels: Labels = Labels(), _ capacity: Int = Prometheus.defaultSummaryCapacity, _ quantiles: [Double] = Prometheus.defaultQuantiles, _ p: PrometheusClient) {
6671
self.name = name
6772
self.help = help
6873

@@ -74,6 +79,10 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
7479

7580
self.count = .init("\(self.name)_count", nil, 0, p)
7681

82+
self.values = CircularBuffer(initialCapacity: capacity)
83+
84+
self.capacity = capacity
85+
7786
self.quantiles = quantiles
7887

7988
self.labels = labels
@@ -159,6 +168,9 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
159168
}
160169
self.count.inc(1)
161170
self.sum.inc(value)
171+
if self.values.count == self.capacity {
172+
_ = self.values.popFirst()
173+
}
162174
self.values.append(value)
163175
}
164176
}
@@ -190,7 +202,7 @@ extension PrometheusClient {
190202
if let summary = summaries.first {
191203
return summary
192204
} else {
193-
let newSummary = PromSummary<T, U>(summary.name, summary.help, labels, summary.quantiles, self)
205+
let newSummary = PromSummary<T, U>(summary.name, summary.help, labels, summary.capacity, summary.quantiles, self)
194206
summary.subSummaries.append(newSummary)
195207
return newSummary
196208
}

Sources/Prometheus/Prometheus.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ public class PrometheusClient {
240240
/// - type: The type the summary will observe
241241
/// - name: Name of the summary
242242
/// - helpText: Help text for the summary. Usually a short description
243+
/// - capacity: Number of observations to keep for calculating quantiles
243244
/// - quantiles: Quantiles to calculate
244245
/// - labels: Labels to give this summary. Can be left out to default to no labels
245246
///
@@ -248,15 +249,15 @@ public class PrometheusClient {
248249
forType type: T.Type,
249250
named name: String,
250251
helpText: String? = nil,
252+
capacity: Int = Prometheus.defaultSummaryCapacity,
251253
quantiles: [Double] = Prometheus.defaultQuantiles,
252254
labels: U.Type) -> PromSummary<T, U>
253255
{
254256
return self.lock.withLock {
255257
if let cachedSummary: PromSummary<T, U> = self._getMetricInstance(with: name, andType: .summary) {
256258
return cachedSummary
257259
}
258-
259-
let summary = PromSummary<T, U>(name, helpText, U(), quantiles, self)
260+
let summary = PromSummary<T, U>(name, helpText, U(), capacity, quantiles, self)
260261
let oldInstrument = self.metrics.updateValue(summary, forKey: name)
261262
precondition(oldInstrument == nil, "Label \(oldInstrument!.name) is already associated with a \(oldInstrument!._type).")
262263
return summary

Sources/Prometheus/PrometheusMetrics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ public struct DimensionLabels: MetricLabels {
385385
for index in 0..<lhs.dimensions.count {
386386
guard lhs.dimensions[index] == rhs.dimensions[index] else { return false }
387387
}
388-
return false
388+
return true
389389
}
390390
}
391391

Sources/PrometheusExample/main.swift

Lines changed: 99 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -6,111 +6,111 @@ let myProm = PrometheusClient()
66

77
MetricsSystem.bootstrap(PrometheusMetricsFactory(client: myProm))
88

9-
for _ in 0...Int.random(in: 10...100) {
10-
let c = Counter(label: "test")
11-
c.increment()
12-
}
9+
//for _ in 0...Int.random(in: 10...100) {
10+
// let c = Counter(label: "test")
11+
// c.increment()
12+
//}
1313

1414
for _ in 0...Int.random(in: 10...100) {
1515
let c = Counter(label: "test", dimensions: [("abc", "123")])
1616
c.increment()
1717
}
1818

19-
for _ in 0...Int.random(in: 100...500_000) {
20-
let r = Recorder(label: "recorder")
21-
r.record(Double.random(in: 0...20))
22-
}
23-
24-
for _ in 0...Int.random(in: 100...500_000) {
25-
let g = Gauge(label: "non_agg_recorder")
26-
g.record(Double.random(in: 0...20))
27-
}
28-
29-
for _ in 0...Int.random(in: 100...500_000) {
30-
let t = Timer(label: "timer")
31-
t.recordMicroseconds(Double.random(in: 20...150))
32-
}
33-
34-
for _ in 0...Int.random(in: 100...500_000) {
35-
let r = Recorder(label: "recorder", dimensions: [("abc", "123")])
36-
r.record(Double.random(in: 0...20))
37-
}
38-
39-
for _ in 0...Int.random(in: 100...500_000) {
40-
let g = Gauge(label: "non_agg_recorder", dimensions: [("abc", "123")])
41-
g.record(Double.random(in: 0...20))
42-
}
43-
44-
for _ in 0...Int.random(in: 100...500_000) {
45-
let t = Timer(label: "timer", dimensions: [("abc", "123")])
46-
t.recordMicroseconds(Double.random(in: 20...150))
47-
}
48-
49-
50-
struct MyCodable: MetricLabels {
51-
var thing: String = "*"
52-
}
53-
54-
let codable1 = MyCodable(thing: "Thing1")
55-
let codable2 = MyCodable(thing: "Thing2")
56-
57-
let counter = myProm.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter", initialValue: 12, withLabelType: MyCodable.self)
58-
59-
counter.inc(5)
60-
counter.inc(Int.random(in: 0...100), codable2)
61-
counter.inc(Int.random(in: 0...100), codable1)
62-
63-
let gauge = myProm.createGauge(forType: Int.self, named: "my_gauge", helpText: "Just a gauge", initialValue: 12, withLabelType: MyCodable.self)
64-
65-
gauge.inc(100)
66-
gauge.inc(Int.random(in: 0...100), codable2)
67-
gauge.inc(Int.random(in: 0...100), codable1)
68-
69-
struct HistogramThing: HistogramLabels {
70-
var le: String = ""
71-
let route: String
72-
73-
init() {
74-
self.route = "*"
75-
}
76-
77-
init(_ route: String) {
78-
self.route = route
79-
}
80-
}
81-
82-
let histogram = myProm.createHistogram(forType: Double.self, named: "my_histogram", helpText: "Just a histogram", labels: HistogramThing.self)
83-
84-
for _ in 0...Int.random(in: 10...50) {
85-
histogram.observe(Double.random(in: 0...1))
86-
}
87-
88-
for _ in 0...Int.random(in: 10...50) {
89-
histogram.observe(Double.random(in: 0...1), HistogramThing("/test"))
90-
}
91-
92-
struct SummaryThing: SummaryLabels {
93-
var quantile: String = ""
94-
let route: String
95-
96-
init() {
97-
self.route = "*"
98-
}
99-
100-
init(_ route: String) {
101-
self.route = route
102-
}
103-
}
104-
105-
let summary = myProm.createSummary(forType: Double.self, named: "my_summary", helpText: "Just a summary", labels: SummaryThing.self)
106-
107-
for _ in 0...Int.random(in: 100...1000) {
108-
summary.observe(Double.random(in: 0...10000))
109-
}
110-
111-
for _ in 0...Int.random(in: 100...1000) {
112-
summary.observe(Double.random(in: 0...10000), SummaryThing("/test"))
113-
}
19+
//for _ in 0...Int.random(in: 100...500_000) {
20+
// let r = Recorder(label: "recorder")
21+
// r.record(Double.random(in: 0...20))
22+
//}
23+
//
24+
//for _ in 0...Int.random(in: 100...500_000) {
25+
// let g = Gauge(label: "non_agg_recorder")
26+
// g.record(Double.random(in: 0...20))
27+
//}
28+
//
29+
//for _ in 0...Int.random(in: 100...500_000) {
30+
// let t = Timer(label: "timer")
31+
// t.recordMicroseconds(Double.random(in: 20...150))
32+
//}
33+
//
34+
//for _ in 0...Int.random(in: 100...500_000) {
35+
// let r = Recorder(label: "recorder", dimensions: [("abc", "123")])
36+
// r.record(Double.random(in: 0...20))
37+
//}
38+
//
39+
//for _ in 0...Int.random(in: 100...500_000) {
40+
// let g = Gauge(label: "non_agg_recorder", dimensions: [("abc", "123")])
41+
// g.record(Double.random(in: 0...20))
42+
//}
43+
//
44+
//for _ in 0...Int.random(in: 100...500_000) {
45+
// let t = Timer(label: "timer", dimensions: [("abc", "123")])
46+
// t.recordMicroseconds(Double.random(in: 20...150))
47+
//}
48+
49+
50+
//struct MyCodable: MetricLabels {
51+
// var thing: String = "*"
52+
//}
53+
//
54+
//let codable1 = MyCodable(thing: "Thing1")
55+
//let codable2 = MyCodable(thing: "Thing2")
56+
//
57+
//let counter = myProm.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter", initialValue: 12, withLabelType: MyCodable.self)
58+
//
59+
//counter.inc(5)
60+
//counter.inc(Int.random(in: 0...100), codable2)
61+
//counter.inc(Int.random(in: 0...100), codable1)
62+
//
63+
//let gauge = myProm.createGauge(forType: Int.self, named: "my_gauge", helpText: "Just a gauge", initialValue: 12, withLabelType: MyCodable.self)
64+
//
65+
//gauge.inc(100)
66+
//gauge.inc(Int.random(in: 0...100), codable2)
67+
//gauge.inc(Int.random(in: 0...100), codable1)
68+
//
69+
//struct HistogramThing: HistogramLabels {
70+
// var le: String = ""
71+
// let route: String
72+
//
73+
// init() {
74+
// self.route = "*"
75+
// }
76+
//
77+
// init(_ route: String) {
78+
// self.route = route
79+
// }
80+
//}
81+
//
82+
//let histogram = myProm.createHistogram(forType: Double.self, named: "my_histogram", helpText: "Just a histogram", labels: HistogramThing.self)
83+
//
84+
//for _ in 0...Int.random(in: 10...50) {
85+
// histogram.observe(Double.random(in: 0...1))
86+
//}
87+
//
88+
//for _ in 0...Int.random(in: 10...50) {
89+
// histogram.observe(Double.random(in: 0...1), HistogramThing("/test"))
90+
//}
91+
//
92+
//struct SummaryThing: SummaryLabels {
93+
// var quantile: String = ""
94+
// let route: String
95+
//
96+
// init() {
97+
// self.route = "*"
98+
// }
99+
//
100+
// init(_ route: String) {
101+
// self.route = route
102+
// }
103+
//}
104+
//
105+
//let summary = myProm.createSummary(forType: Double.self, named: "my_summary", helpText: "Just a summary", labels: SummaryThing.self)
106+
//
107+
//for _ in 0...Int.random(in: 100...1000) {
108+
// summary.observe(Double.random(in: 0...10000))
109+
//}
110+
//
111+
//for _ in 0...Int.random(in: 100...1000) {
112+
// summary.observe(Double.random(in: 0...10000), SummaryThing("/test"))
113+
//}
114114

115115
let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1)
116116
let prom = elg.next().makePromise(of: String.self)

Tests/SwiftPrometheusTests/PrometheusMetricsTests.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,14 @@ final class PrometheusMetricsTests: XCTestCase {
214214
let summary: PromSummary<Int64, DimensionSummaryLabels>? = prom.getMetricInstance(with: "duration_nanos", andType: .summary)
215215
XCTAssertNil(summary)
216216
}
217+
218+
func testDimensionLabelEquality() {
219+
let labelsA = DimensionLabels([("a", "a")])
220+
let labelsB = DimensionLabels([("b", "b")])
221+
let labelsATwo = DimensionLabels([("a", "a")])
222+
223+
XCTAssertEqual(labelsA, labelsATwo)
224+
XCTAssertNotEqual(labelsA, labelsB)
225+
XCTAssertNotEqual(labelsATwo, labelsB)
226+
}
217227
}

Tests/SwiftPrometheusTests/SummaryTests.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,21 @@ final class SummaryTests: XCTestCase {
144144
my_summary_sum{myValue=\"labels\"} 123.0
145145
""")
146146
}
147+
148+
func testStandaloneSummaryWithCustomCapacity() {
149+
let capacity = 10
150+
let summary = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", capacity: capacity, quantiles: [0.5, 0.99], labels: BaseSummaryLabels.self)
151+
152+
for i in 0 ..< capacity { summary.observe(Double(i * 1_000)) }
153+
for i in 0 ..< capacity { summary.observe(Double(i)) }
154+
155+
XCTAssertEqual(summary.collect(), """
156+
# HELP my_summary Summary for testing
157+
# TYPE my_summary summary
158+
my_summary{quantile="0.5", myValue="*"} 4.5
159+
my_summary{quantile="0.99", myValue="*"} 9.0
160+
my_summary_count{myValue="*"} 20.0
161+
my_summary_sum{myValue="*"} 45045.0
162+
""")
163+
}
147164
}

0 commit comments

Comments
 (0)