-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathCounter.swift
110 lines (95 loc) · 3.42 KB
/
Counter.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import NIOConcurrencyHelpers
/// Prometheus Counter metric
///
/// See: https://prometheus.io/docs/concepts/metric_types/#counter
public class PromCounter<NumType: Numeric>: PromMetric {
/// Name of the Counter, required
public let name: String
/// Help text of the Counter, optional
public let help: String?
/// Type of the metric, used for formatting
public let _type: PromMetricType = .counter
/// Current value of the counter
internal var value: NumType
/// Initial value of the counter
private let initialValue: NumType
/// Indicates wether or not metric has been used without labels
private var usedWithoutLabels: Bool
/// Storage of values that have labels attached
internal var metrics: [DimensionLabels: NumType] = [:]
/// Lock used for thread safety
internal let lock: Lock
/// Creates a new instance of a Counter
///
/// - Parameters:
/// - name: Name of the Counter
/// - help: Help text of the Counter
/// - initialValue: Initial value to set the counter to
/// - p: Prometheus instance that created this counter
internal init(_ name: String, _ help: String? = nil, _ initialValue: NumType = 0) {
self.name = name
self.help = help
self.initialValue = initialValue
self.value = initialValue
self.usedWithoutLabels = initialValue != 0
self.lock = Lock()
}
/// Gets the metric string for this counter
///
/// - Returns:
/// Newline separated Prometheus formatted metric string
public func collect() -> String {
let (value, metrics, usedWithoutLabels) = self.lock.withLock {
(self.value, self.metrics, self.usedWithoutLabels)
}
var output = [String]()
if let help = self.help {
output.append("# HELP \(self.name) \(help)")
}
output.append("# TYPE \(self.name) \(self._type)")
if usedWithoutLabels {
output.append("\(self.name) \(value)")
}
metrics.forEach { (labels, value) in
let labelsString = encodeLabels(labels)
output.append("\(self.name)\(labelsString) \(value)")
}
return output.joined(separator: "\n")
}
/// Increments the Counter
///
/// - Parameters:
/// - amount: Amount to increment the counter with
/// - labels: Labels to attach to the value
///
@discardableResult
public func inc(_ amount: NumType = 1, _ labels: DimensionLabels? = nil) -> NumType {
return self.lock.withLock {
if let labels = labels {
var val = self.metrics[labels] ?? self.initialValue
val += amount
self.metrics[labels] = val
return val
} else {
self.usedWithoutLabels = true
self.value += amount
return self.value
}
}
}
/// Gets the value of the Counter
///
/// - Parameters:
/// - labels: Labels to get the value for
///
/// - Returns: The value of the Counter attached to the provided labels
public func get(_ labels: DimensionLabels? = nil) -> NumType {
return self.lock.withLock {
if let labels = labels {
return self.metrics[labels] ?? initialValue
} else {
return self.value
}
}
}
}