Skip to content

Commit 275d379

Browse files
authored
Get summary up to spec (#26)
* Get summary up to spec * Update linuxmain
1 parent 48b1817 commit 275d379

File tree

5 files changed

+173
-90
lines changed

5 files changed

+173
-90
lines changed

Sources/Prometheus/MetricTypes/Summary.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import NIOConcurrencyHelpers
22
import enum CoreMetrics.TimeUnit
3+
import Dispatch
34

45
/// Label type Summaries can use
56
public protocol SummaryLabels: MetricLabels {
@@ -167,6 +168,21 @@ public class PromSummary<NumType: DoubleRepresentable, Labels: SummaryLabels>: P
167168
self.values.append(value)
168169
}
169170
}
171+
172+
/// Time the duration of a closure and observe the resulting time in seconds.
173+
///
174+
/// - parameters:
175+
/// - labels: Labels to attach to the resulting value.
176+
/// - body: Closure to run & record.
177+
@inlinable
178+
public func time<T>(_ labels: Labels? = nil, _ body: @escaping () throws -> T) rethrows -> T {
179+
let start = DispatchTime.now().uptimeNanoseconds
180+
defer {
181+
let delta = Double(DispatchTime.now().uptimeNanoseconds - start)
182+
self.observe(.init(delta / 1_000_000_000), labels)
183+
}
184+
return try body()
185+
}
170186
}
171187

172188
extension PrometheusClient {

Tests/SwiftPrometheusTests/PrometheusMetricsTests.swift

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -38,68 +38,6 @@ final class PrometheusMetricsTests: XCTestCase {
3838
""")
3939
}
4040

41-
func testSummary() {
42-
let summary = Timer(label: "my_summary")
43-
44-
summary.recordNanoseconds(1)
45-
summary.recordNanoseconds(2)
46-
summary.recordNanoseconds(4)
47-
summary.recordNanoseconds(10000)
48-
49-
let summaryTwo = Timer(label: "my_summary", dimensions: [("myValue", "labels")])
50-
summaryTwo.recordNanoseconds(123)
51-
52-
let promise = self.eventLoop.makePromise(of: String.self)
53-
prom.collect(promise.succeed)
54-
55-
XCTAssertEqual(try! promise.futureResult.wait(), """
56-
# TYPE my_summary summary
57-
my_summary{quantile="0.01"} 1.0
58-
my_summary{quantile="0.05"} 1.0
59-
my_summary{quantile="0.5"} 4.0
60-
my_summary{quantile="0.9"} 10000.0
61-
my_summary{quantile="0.95"} 10000.0
62-
my_summary{quantile="0.99"} 10000.0
63-
my_summary{quantile="0.999"} 10000.0
64-
my_summary_count 5
65-
my_summary_sum 10130.0
66-
my_summary{quantile="0.01", myValue="labels"} 123.0
67-
my_summary{quantile="0.05", myValue="labels"} 123.0
68-
my_summary{quantile="0.5", myValue="labels"} 123.0
69-
my_summary{quantile="0.9", myValue="labels"} 123.0
70-
my_summary{quantile="0.95", myValue="labels"} 123.0
71-
my_summary{quantile="0.99", myValue="labels"} 123.0
72-
my_summary{quantile="0.999", myValue="labels"} 123.0
73-
my_summary_count{myValue="labels"} 1
74-
my_summary_sum{myValue="labels"} 123.0
75-
""")
76-
}
77-
78-
func testSummaryWithPreferredDisplayUnit() {
79-
let summary = Timer(label: "my_summary", preferredDisplayUnit: .seconds)
80-
81-
summary.recordSeconds(1)
82-
summary.recordMilliseconds(2 * 1_000)
83-
summary.recordNanoseconds(4 * 1_000_000_000)
84-
summary.recordSeconds(10000)
85-
86-
let promise = self.eventLoop.makePromise(of: String.self)
87-
prom.collect(promise.succeed)
88-
89-
XCTAssertEqual(try! promise.futureResult.wait(), """
90-
# TYPE my_summary summary
91-
my_summary{quantile="0.01"} 1.0
92-
my_summary{quantile="0.05"} 1.0
93-
my_summary{quantile="0.5"} 3.0
94-
my_summary{quantile="0.9"} 10000.0
95-
my_summary{quantile="0.95"} 10000.0
96-
my_summary{quantile="0.99"} 10000.0
97-
my_summary{quantile="0.999"} 10000.0
98-
my_summary_count 4
99-
my_summary_sum 10007.0
100-
""")
101-
}
102-
10341
func testMetricDestroying() {
10442
let counter = Counter(label: "my_counter")
10543
counter.increment()
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import XCTest
2+
import NIO
3+
@testable import Prometheus
4+
@testable import CoreMetrics
5+
6+
final class SummaryTests: XCTestCase {
7+
struct BaseSummaryLabels: SummaryLabels {
8+
var quantile: String = ""
9+
let myValue: String
10+
11+
init() {
12+
self.myValue = "*"
13+
}
14+
15+
init(myValue: String) {
16+
self.myValue = myValue
17+
}
18+
}
19+
20+
var prom: PrometheusClient!
21+
var group: EventLoopGroup!
22+
var eventLoop: EventLoop {
23+
return group.next()
24+
}
25+
26+
override func setUp() {
27+
self.prom = PrometheusClient()
28+
self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
29+
MetricsSystem.bootstrapInternal(prom)
30+
}
31+
32+
override func tearDown() {
33+
self.prom = nil
34+
try! self.group.syncShutdownGracefully()
35+
}
36+
37+
func testSummary() {
38+
let summary = Timer(label: "my_summary")
39+
40+
summary.recordNanoseconds(1)
41+
summary.recordNanoseconds(2)
42+
summary.recordNanoseconds(4)
43+
summary.recordNanoseconds(10000)
44+
45+
let summaryTwo = Timer(label: "my_summary", dimensions: [("myValue", "labels")])
46+
summaryTwo.recordNanoseconds(123)
47+
48+
let promise = self.eventLoop.makePromise(of: String.self)
49+
prom.collect(promise.succeed)
50+
51+
XCTAssertEqual(try! promise.futureResult.wait(), """
52+
# TYPE my_summary summary
53+
my_summary{quantile="0.01"} 1.0
54+
my_summary{quantile="0.05"} 1.0
55+
my_summary{quantile="0.5"} 4.0
56+
my_summary{quantile="0.9"} 10000.0
57+
my_summary{quantile="0.95"} 10000.0
58+
my_summary{quantile="0.99"} 10000.0
59+
my_summary{quantile="0.999"} 10000.0
60+
my_summary_count 5
61+
my_summary_sum 10130.0
62+
my_summary{quantile="0.01", myValue="labels"} 123.0
63+
my_summary{quantile="0.05", myValue="labels"} 123.0
64+
my_summary{quantile="0.5", myValue="labels"} 123.0
65+
my_summary{quantile="0.9", myValue="labels"} 123.0
66+
my_summary{quantile="0.95", myValue="labels"} 123.0
67+
my_summary{quantile="0.99", myValue="labels"} 123.0
68+
my_summary{quantile="0.999", myValue="labels"} 123.0
69+
my_summary_count{myValue="labels"} 1
70+
my_summary_sum{myValue="labels"} 123.0
71+
""")
72+
}
73+
74+
func testSummaryWithPreferredDisplayUnit() {
75+
let summary = Timer(label: "my_summary", preferredDisplayUnit: .seconds)
76+
77+
summary.recordSeconds(1)
78+
summary.recordMilliseconds(2 * 1_000)
79+
summary.recordNanoseconds(4 * 1_000_000_000)
80+
summary.recordSeconds(10000)
81+
82+
let promise = self.eventLoop.makePromise(of: String.self)
83+
prom.collect(promise.succeed)
84+
85+
XCTAssertEqual(try! promise.futureResult.wait(), """
86+
# TYPE my_summary summary
87+
my_summary{quantile="0.01"} 1.0
88+
my_summary{quantile="0.05"} 1.0
89+
my_summary{quantile="0.5"} 3.0
90+
my_summary{quantile="0.9"} 10000.0
91+
my_summary{quantile="0.95"} 10000.0
92+
my_summary{quantile="0.99"} 10000.0
93+
my_summary{quantile="0.999"} 10000.0
94+
my_summary_count 4
95+
my_summary_sum 10007.0
96+
""")
97+
}
98+
99+
func testSummaryTime() {
100+
let summary = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", quantiles: [0.5, 0.9, 0.99], labels: BaseSummaryLabels.self)
101+
let delay = 0.05
102+
summary.time {
103+
Thread.sleep(forTimeInterval: delay)
104+
}
105+
// This setup checks `.startsWith` on a per-line basis
106+
// to prevent issues with subsecond differences per test run
107+
let lines = [
108+
"# HELP my_summary Summary for testing",
109+
"# TYPE my_summary summary",
110+
#"my_summary{quantile="0.5", myValue="*"} 0.05"#,
111+
#"my_summary{quantile="0.9", myValue="*"} 0.05"#,
112+
#"my_summary{quantile="0.99", myValue="*"} 0.05"#,
113+
#"my_summary_count{myValue="*"} 1.0"#,
114+
#"my_summary_sum{myValue="*"} 0.05"#
115+
]
116+
let sections = summary.collect().split(separator: "\n").map(String.init).enumerated().map { i, s in s.starts(with: lines[i]) }
117+
XCTAssert(sections.filter { !$0 }.isEmpty)
118+
}
119+
120+
func testSummaryStandalone() {
121+
let summary = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", quantiles: [0.5, 0.9, 0.99], labels: BaseSummaryLabels.self)
122+
let summaryTwo = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", quantiles: [0.5, 0.9, 0.99], labels: BaseSummaryLabels.self)
123+
124+
summary.observe(1)
125+
summary.observe(2)
126+
summary.observe(4)
127+
summaryTwo.observe(10000)
128+
129+
summary.observe(123, .init(myValue: "labels"))
130+
131+
XCTAssertEqual(summary.collect(), """
132+
# HELP my_summary Summary for testing
133+
# TYPE my_summary summary
134+
my_summary{quantile=\"0.5\", myValue=\"*\"} 4.0
135+
my_summary{quantile=\"0.9\", myValue=\"*\"} 10000.0
136+
my_summary{quantile=\"0.99\", myValue=\"*\"} 10000.0
137+
my_summary_count{myValue=\"*\"} 5.0
138+
my_summary_sum{myValue=\"*\"} 10130.0
139+
my_summary{quantile=\"0.5\", myValue=\"labels\"} 123.0
140+
my_summary{quantile=\"0.9\", myValue=\"labels\"} 123.0
141+
my_summary{quantile=\"0.99\", myValue=\"labels\"} 123.0
142+
my_summary_count{myValue=\"labels\"} 1.0
143+
my_summary_sum{myValue=\"labels\"} 123.0
144+
""")
145+
}
146+
}

Tests/SwiftPrometheusTests/SwiftPrometheusTests.swift

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,6 @@ final class SwiftPrometheusTests: XCTestCase {
1515
}
1616
}
1717

18-
struct BaseSummaryLabels: SummaryLabels {
19-
var quantile: String = ""
20-
let myValue: String
21-
22-
init() {
23-
self.myValue = "*"
24-
}
25-
26-
init(myValue: String) {
27-
self.myValue = myValue
28-
}
29-
}
30-
3118
var prom: PrometheusClient!
3219

3320
override func setUp() {
@@ -65,18 +52,4 @@ final class SwiftPrometheusTests: XCTestCase {
6552
XCTAssertEqual(metricsString, "# HELP my_counter Counter for testing\n# TYPE my_counter counter\nmy_counter 30\nmy_counter{myValue=\"labels\"} 30")
6653
}
6754
}
68-
69-
func testSummary() {
70-
let summary = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", quantiles: [0.5, 0.9, 0.99], labels: BaseSummaryLabels.self)
71-
let summaryTwo = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", quantiles: [0.5, 0.9, 0.99], labels: BaseSummaryLabels.self)
72-
73-
summary.observe(1)
74-
summary.observe(2)
75-
summary.observe(4)
76-
summaryTwo.observe(10000)
77-
78-
summary.observe(123, .init(myValue: "labels"))
79-
80-
XCTAssertEqual(summary.collect(), "# HELP my_summary Summary for testing\n# TYPE my_summary summary\nmy_summary{quantile=\"0.5\", myValue=\"*\"} 4.0\nmy_summary{quantile=\"0.9\", myValue=\"*\"} 10000.0\nmy_summary{quantile=\"0.99\", myValue=\"*\"} 10000.0\nmy_summary_count{myValue=\"*\"} 5.0\nmy_summary_sum{myValue=\"*\"} 10130.0\nmy_summary{quantile=\"0.5\", myValue=\"labels\"} 123.0\nmy_summary{quantile=\"0.9\", myValue=\"labels\"} 123.0\nmy_summary{quantile=\"0.99\", myValue=\"labels\"} 123.0\nmy_summary_count{myValue=\"labels\"} 1.0\nmy_summary_sum{myValue=\"labels\"} 123.0")
81-
}
8255
}

Tests/SwiftPrometheusTests/XCTestManifests.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,17 @@ extension PrometheusMetricsTests {
3333
("testCollectIntoBuffer", testCollectIntoBuffer),
3434
("testCounter", testCounter),
3535
("testMetricDestroying", testMetricDestroying),
36+
]
37+
}
38+
39+
extension SummaryTests {
40+
// DO NOT MODIFY: This is autogenerated, use:
41+
// `swift test --generate-linuxmain`
42+
// to regenerate.
43+
static let __allTests__SummaryTests = [
3644
("testSummary", testSummary),
45+
("testSummaryStandalone", testSummaryStandalone),
46+
("testSummaryTime", testSummaryTime),
3747
("testSummaryWithPreferredDisplayUnit", testSummaryWithPreferredDisplayUnit),
3848
]
3949
}
@@ -45,7 +55,6 @@ extension SwiftPrometheusTests {
4555
static let __allTests__SwiftPrometheusTests = [
4656
("testCounter", testCounter),
4757
("testMultipleCounter", testMultipleCounter),
48-
("testSummary", testSummary),
4958
]
5059
}
5160

@@ -54,6 +63,7 @@ public func __allTests() -> [XCTestCaseEntry] {
5463
testCase(GaugeTests.__allTests__GaugeTests),
5564
testCase(HistogramTests.__allTests__HistogramTests),
5665
testCase(PrometheusMetricsTests.__allTests__PrometheusMetricsTests),
66+
testCase(SummaryTests.__allTests__SummaryTests),
5767
testCase(SwiftPrometheusTests.__allTests__SwiftPrometheusTests),
5868
]
5969
}

0 commit comments

Comments
 (0)