Skip to content

Commit 48b1817

Browse files
authored
Get Gauge up to spec (#27)
* Get Gauge up to spec * Update wording remarks
1 parent 504476e commit 48b1817

File tree

7 files changed

+169
-55
lines changed

7 files changed

+169
-55
lines changed

Sources/Prometheus/MetricTypes/Gauge.swift

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import struct Foundation.Date
2+
import Dispatch
13
import NIOConcurrencyHelpers
24

35
/// Prometheus Gauge metric
46
///
57
/// See https://prometheus.io/docs/concepts/metric_types/#gauge
6-
public class PromGauge<NumType: Numeric, Labels: MetricLabels>: PromMetric, PrometheusHandled {
8+
public class PromGauge<NumType: DoubleRepresentable, Labels: MetricLabels>: PromMetric, PrometheusHandled {
79
/// Prometheus instance that created this Gauge
810
internal weak var prometheus: PrometheusClient?
911

@@ -68,12 +70,61 @@ public class PromGauge<NumType: Numeric, Labels: MetricLabels>: PromMetric, Prom
6870
}
6971
}
7072

73+
/// Sets the Gauge to the current unixtime in seconds
74+
///
75+
/// - Parameters:
76+
/// - labels: Labels to attach to the value
77+
///
78+
/// - Returns: The value of the Gauge attached to the provided labels
79+
@discardableResult
80+
public func setToCurrentTime(_ labels: Labels? = nil) -> NumType {
81+
return self.set(.init(Date().timeIntervalSince1970), labels)
82+
}
83+
84+
/// Tracks in progress blocks of code or functions.
85+
///
86+
/// func someFunc() -> String { return "ABC" }
87+
/// let newFunc = myGauge.trackInprogress(someFunc)
88+
/// newFunc() // returns "ABC" and increments & decrements Gauge
89+
///
90+
/// - Parameters:
91+
/// - labels: Labels to attach to the value
92+
/// - body: Function to wrap progress tracker around
93+
///
94+
/// - Returns: The same type of function passed in for `body`, but wrapped to track progress.
95+
@inlinable
96+
public func trackInProgress<T>(_ labels: Labels? = nil, _ body: @escaping () throws -> T) -> (() throws -> T) {
97+
return {
98+
self.inc()
99+
defer {
100+
self.dec()
101+
}
102+
return try body()
103+
}
104+
}
105+
/// Time the execution duration of a closure and observe the resulting time in seconds.
106+
///
107+
/// - parameters:
108+
/// - labels: Labels to attach to the resulting value.
109+
/// - body: Closure to run & record execution time of.
110+
@inlinable
111+
public func time<T>(_ labels: Labels? = nil, _ body: @escaping () throws -> T) rethrows -> T {
112+
let start = DispatchTime.now().uptimeNanoseconds
113+
defer {
114+
let delta = Double(DispatchTime.now().uptimeNanoseconds - start)
115+
self.set(.init(delta / 1_000_000_000), labels)
116+
}
117+
return try body()
118+
}
119+
120+
71121
/// Sets the Gauge
72122
///
73123
/// - Parameters:
74124
/// - amount: Amount to set the gauge to
75125
/// - labels: Labels to attach to the value
76126
///
127+
/// - Returns: The value of the Gauge attached to the provided labels
77128
@discardableResult
78129
public func set(_ amount: NumType, _ labels: Labels? = nil) -> NumType {
79130
return self.lock.withLock {
@@ -92,8 +143,8 @@ public class PromGauge<NumType: Numeric, Labels: MetricLabels>: PromMetric, Prom
92143
/// - Parameters:
93144
/// - amount: Amount to increment the Gauge with
94145
/// - labels: Labels to attach to the value
95-
/// - done: Completion handler
96146
///
147+
/// - Returns: The value of the Gauge attached to the provided labels
97148
@discardableResult
98149
public func inc(_ amount: NumType, _ labels: Labels? = nil) -> NumType {
99150
return self.lock.withLock {
@@ -113,8 +164,8 @@ public class PromGauge<NumType: Numeric, Labels: MetricLabels>: PromMetric, Prom
113164
///
114165
/// - Parameters:
115166
/// - labels: Labels to attach to the value
116-
/// - done: Completion handler
117167
///
168+
/// - Returns: The value of the Gauge attached to the provided labels
118169
@discardableResult
119170
public func inc(_ labels: Labels? = nil) -> NumType {
120171
return self.inc(1, labels)
@@ -125,8 +176,8 @@ public class PromGauge<NumType: Numeric, Labels: MetricLabels>: PromMetric, Prom
125176
/// - Parameters:
126177
/// - amount: Amount to decrement the Gauge with
127178
/// - labels: Labels to attach to the value
128-
/// - done: Completion handler
129179
///
180+
/// - Returns: The value of the Gauge attached to the provided labels
130181
@discardableResult
131182
public func dec(_ amount: NumType, _ labels: Labels? = nil) -> NumType {
132183
return self.lock.withLock {
@@ -147,6 +198,7 @@ public class PromGauge<NumType: Numeric, Labels: MetricLabels>: PromMetric, Prom
147198
/// - Parameters:
148199
/// - labels: Labels to attach to the value
149200
///
201+
/// - Returns: The value of the Gauge attached to the provided labels
150202
@discardableResult
151203
public func dec(_ labels: Labels? = nil) -> NumType {
152204
return self.dec(1, labels)
@@ -158,7 +210,6 @@ public class PromGauge<NumType: Numeric, Labels: MetricLabels>: PromMetric, Prom
158210
/// - labels: Labels to get the value for
159211
///
160212
/// - Returns: The value of the Gauge attached to the provided labels
161-
///
162213
public func get(_ labels: Labels? = nil) -> NumType {
163214
return self.lock.withLock {
164215
if let labels = labels {

Sources/Prometheus/Utils.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,19 @@ public protocol DoubleRepresentable: Numeric {
6161
var doubleValue: Double {get}
6262

6363
init(_ double: Double)
64+
65+
init(_ int: Int)
6466
}
6567

6668
/// Numbers that convert to other types
6769
public protocol ConvertibleNumberType: DoubleRepresentable {}
6870
public extension ConvertibleNumberType {
6971
/// Number as a Float
70-
var floatValue: Float {get {return Float(doubleValue)}}
72+
var floatValue: Float { return Float(doubleValue) }
7173
/// Number as an Int
72-
var intValue: Int {get {return lrint(doubleValue)}}
74+
var intValue: Int { return lrint(doubleValue) }
7375
/// Number as a CGFloat
74-
var CGFloatValue: CGFloat {get {return CGFloat(doubleValue)}}
76+
var CGFloatValue: CGFloat { return CGFloat(doubleValue) }
7577
}
7678

7779
/// Double Representable Conformance

Sources/PrometheusExample/main.swift

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ for _ in 0...Int.random(in: 100...1000) {
115115
let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1)
116116
let prom = elg.next().makePromise(of: String.self)
117117

118-
prom.futureResult.whenSuccess {
119-
print($0)
120-
}
121-
122118
try! MetricsSystem.prometheus().collect(prom.succeed)
119+
120+
print(try! prom.futureResult.wait())
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import XCTest
2+
import NIO
3+
@testable import Prometheus
4+
@testable import CoreMetrics
5+
6+
final class GaugeTests: XCTestCase {
7+
struct BaseLabels: MetricLabels {
8+
let myValue: String
9+
10+
init() {
11+
self.myValue = "*"
12+
}
13+
14+
init(myValue: String) {
15+
self.myValue = myValue
16+
}
17+
}
18+
19+
var prom: PrometheusClient!
20+
var group: EventLoopGroup!
21+
var eventLoop: EventLoop {
22+
return group.next()
23+
}
24+
25+
override func setUp() {
26+
self.prom = PrometheusClient()
27+
self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
28+
MetricsSystem.bootstrapInternal(prom)
29+
}
30+
31+
override func tearDown() {
32+
self.prom = nil
33+
try! self.group.syncShutdownGracefully()
34+
}
35+
36+
func testGaugeSwiftMetrics() {
37+
let gauge = Gauge(label: "my_gauge")
38+
39+
gauge.record(10)
40+
gauge.record(12)
41+
gauge.record(20)
42+
43+
let gaugeTwo = Gauge(label: "my_gauge", dimensions: [("myValue", "labels")])
44+
gaugeTwo.record(10)
45+
46+
let promise = self.eventLoop.makePromise(of: String.self)
47+
prom.collect(promise.succeed)
48+
49+
XCTAssertEqual(try! promise.futureResult.wait(), """
50+
# TYPE my_gauge gauge
51+
my_gauge 20.0
52+
my_gauge{myValue=\"labels\"} 10.0
53+
""")
54+
}
55+
56+
func testGaugeTime() {
57+
let gauge = prom.createGauge(forType: Double.self, named: "my_gauge")
58+
let delay = 0.05
59+
gauge.time {
60+
Thread.sleep(forTimeInterval: delay)
61+
}
62+
// Using starts(with:) here since the exact subseconds might differ per-test.
63+
XCTAssert(gauge.collect().starts(with: """
64+
# TYPE my_gauge gauge
65+
my_gauge 0.05
66+
"""))
67+
}
68+
69+
func testGaugeStandalone() {
70+
let gauge = prom.createGauge(forType: Int.self, named: "my_gauge", helpText: "Gauge for testing", initialValue: 10, withLabelType: BaseLabels.self)
71+
XCTAssertEqual(gauge.get(), 10)
72+
gauge.inc(10)
73+
XCTAssertEqual(gauge.get(), 20)
74+
gauge.dec(12)
75+
XCTAssertEqual(gauge.get(), 8)
76+
gauge.set(20)
77+
gauge.inc(10, BaseLabels(myValue: "labels"))
78+
XCTAssertEqual(gauge.get(), 20)
79+
XCTAssertEqual(gauge.get(BaseLabels(myValue: "labels")), 20)
80+
81+
let gaugeTwo = prom.createGauge(forType: Int.self, named: "my_gauge", helpText: "Gauge for testing", initialValue: 10, withLabelType: BaseLabels.self)
82+
XCTAssertEqual(gaugeTwo.get(), 20)
83+
gaugeTwo.inc()
84+
XCTAssertEqual(gauge.get(), 21)
85+
XCTAssertEqual(gaugeTwo.get(), 21)
86+
87+
XCTAssertEqual(gauge.collect(), """
88+
# HELP my_gauge Gauge for testing
89+
# TYPE my_gauge gauge
90+
my_gauge 21
91+
my_gauge{myValue="labels"} 20
92+
""")
93+
}
94+
}

Tests/SwiftPrometheusTests/PrometheusMetricsTests.swift

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

41-
func testGauge() {
42-
let gauge = Gauge(label: "my_gauge")
43-
44-
gauge.record(10)
45-
gauge.record(12)
46-
gauge.record(20)
47-
48-
let gaugeTwo = Gauge(label: "my_gauge", dimensions: [("myValue", "labels")])
49-
gaugeTwo.record(10)
50-
51-
let promise = self.eventLoop.makePromise(of: String.self)
52-
prom.collect(promise.succeed)
53-
54-
XCTAssertEqual(try! promise.futureResult.wait(), """
55-
# TYPE my_gauge gauge
56-
my_gauge 20.0
57-
my_gauge{myValue=\"labels\"} 10.0
58-
""")
59-
}
60-
6141
func testSummary() {
6242
let summary = Timer(label: "my_summary")
6343

Tests/SwiftPrometheusTests/SwiftPrometheusTests.swift

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -66,27 +66,6 @@ final class SwiftPrometheusTests: XCTestCase {
6666
}
6767
}
6868

69-
func testGauge() {
70-
let gauge = prom.createGauge(forType: Int.self, named: "my_gauge", helpText: "Gauge for testing", initialValue: 10, withLabelType: BaseLabels.self)
71-
XCTAssertEqual(gauge.get(), 10)
72-
gauge.inc(10)
73-
XCTAssertEqual(gauge.get(), 20)
74-
gauge.dec(12)
75-
XCTAssertEqual(gauge.get(), 8)
76-
gauge.set(20)
77-
gauge.inc(10, BaseLabels(myValue: "labels"))
78-
XCTAssertEqual(gauge.get(), 20)
79-
XCTAssertEqual(gauge.get(BaseLabels(myValue: "labels")), 20)
80-
81-
let gaugeTwo = prom.createGauge(forType: Int.self, named: "my_gauge", helpText: "Gauge for testing", initialValue: 10, withLabelType: BaseLabels.self)
82-
XCTAssertEqual(gaugeTwo.get(), 20)
83-
gaugeTwo.inc()
84-
XCTAssertEqual(gauge.get(), 21)
85-
XCTAssertEqual(gaugeTwo.get(), 21)
86-
87-
XCTAssertEqual(gauge.collect(), "# HELP my_gauge Gauge for testing\n# TYPE my_gauge gauge\nmy_gauge 21\nmy_gauge{myValue=\"labels\"} 20")
88-
}
89-
9069
func testSummary() {
9170
let summary = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", quantiles: [0.5, 0.9, 0.99], labels: BaseSummaryLabels.self)
9271
let summaryTwo = prom.createSummary(forType: Double.self, named: "my_summary", helpText: "Summary for testing", quantiles: [0.5, 0.9, 0.99], labels: BaseSummaryLabels.self)

Tests/SwiftPrometheusTests/XCTestManifests.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
#if !canImport(ObjectiveC)
22
import XCTest
33

4+
extension GaugeTests {
5+
// DO NOT MODIFY: This is autogenerated, use:
6+
// `swift test --generate-linuxmain`
7+
// to regenerate.
8+
static let __allTests__GaugeTests = [
9+
("testGaugeStandalone", testGaugeStandalone),
10+
("testGaugeSwiftMetrics", testGaugeSwiftMetrics),
11+
("testGaugeTime", testGaugeTime),
12+
]
13+
}
14+
415
extension HistogramTests {
516
// DO NOT MODIFY: This is autogenerated, use:
617
// `swift test --generate-linuxmain`
@@ -21,7 +32,6 @@ extension PrometheusMetricsTests {
2132
("testCollectAFewMetricsIntoString", testCollectAFewMetricsIntoString),
2233
("testCollectIntoBuffer", testCollectIntoBuffer),
2334
("testCounter", testCounter),
24-
("testGauge", testGauge),
2535
("testMetricDestroying", testMetricDestroying),
2636
("testSummary", testSummary),
2737
("testSummaryWithPreferredDisplayUnit", testSummaryWithPreferredDisplayUnit),
@@ -34,14 +44,14 @@ extension SwiftPrometheusTests {
3444
// to regenerate.
3545
static let __allTests__SwiftPrometheusTests = [
3646
("testCounter", testCounter),
37-
("testGauge", testGauge),
3847
("testMultipleCounter", testMultipleCounter),
3948
("testSummary", testSummary),
4049
]
4150
}
4251

4352
public func __allTests() -> [XCTestCaseEntry] {
4453
return [
54+
testCase(GaugeTests.__allTests__GaugeTests),
4555
testCase(HistogramTests.__allTests__HistogramTests),
4656
testCase(PrometheusMetricsTests.__allTests__PrometheusMetricsTests),
4757
testCase(SwiftPrometheusTests.__allTests__SwiftPrometheusTests),

0 commit comments

Comments
 (0)