Skip to content

Commit 43baf55

Browse files
authored
Merge pull request #4 from MrLotU/MetricsLib
Implement swift-metrics
2 parents 19756ae + bb6db84 commit 43baf55

File tree

107 files changed

+979
-23814
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+979
-23814
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
/.build
33
/Packages
44
/*.xcodeproj
5-
build/
5+
build/
6+
.swiftpm/

.travis.yml

Lines changed: 0 additions & 6 deletions
This file was deleted.

Package.resolved

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,22 @@ let package = Package(
99
name: "SwiftPrometheus",
1010
targets: ["Prometheus"]),
1111
],
12+
dependencies: [
13+
.package(url: "https://github.com/apple/swift-metrics.git", from: "1.0.0"),
14+
.package(url: "https://github.com/apple/swift-nio.git", Version("1.0.0") ..< Version("3.0.0")),
15+
],
1216
targets: [
1317
.target(
1418
name: "Prometheus",
15-
dependencies: []),
19+
dependencies: ["NIOConcurrencyHelpers"]),
20+
.target(
21+
name: "PrometheusMetrics",
22+
dependencies: ["Prometheus", "CoreMetrics"]),
1623
.target(
1724
name: "PrometheusExample",
18-
dependencies: ["Prometheus"]),
25+
dependencies: ["PrometheusMetrics", "Metrics"]),
1926
.testTarget(
2027
name: "SwiftPrometheusTests",
21-
dependencies: ["Prometheus"]),
28+
dependencies: ["Prometheus", "PrometheusMetrics"]),
2229
]
2330
)

README.md

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![Build Status](https://travis-ci.com/MrLotU/SwiftPrometheus.svg?branch=master)](https://travis-ci.com/MrLotU/SwiftPrometheus) [![Swift 5.0](https://img.shields.io/badge/swift-5.0-orange.svg?style=flat)](http://swift.org)
1+
[![CircleCI](https://circleci.com/gh/MrLotU/SwiftPrometheus.svg?style=svg)](https://circleci.com/gh/MrLotU/SwiftPrometheus)[![Swift 5.0](https://img.shields.io/badge/swift-5.0-orange.svg?style=flat)](http://swift.org)
22

33
# SwiftPrometheus, Prometheus client for Swift
44

@@ -8,14 +8,35 @@ A prometheus client for Swift supporting counters, gauges, histograms, summaries
88

99
For examples, see [main.swift](./Sources/PrometheusExample/main.swift)
1010

11+
First, we have to create an instance of our `PrometheusClient`:
12+
```swift
13+
import Prometheus
14+
let myProm = PrometheusClient()
15+
```
16+
17+
## Usage with Swift-Metrics
18+
_For more details about swift-metrics, check the GitHub repo [here](https://github.com/apple/swift-metrics)_
19+
20+
To use SwiftPrometheus with swift-metrics, all the setup required is this:
21+
```swift
22+
import PrometheusMetrics // Auto imports Prometheus too, but adds the swift-metrics compatibility
23+
let myProm = PrometheusClient()
24+
MetricsSystem.bootstrap(myProm)
25+
```
26+
27+
To use prometheus specific features in a later stage of your program, or to get your metrics out of the system, there is a convenience method added to `MetricsSystem`:
28+
```swift
29+
// This is the same instance was used in `.bootstrap()` earlier.
30+
let promInstance = try MetricsSystem.prometheus()
31+
```
32+
You can than use the same APIs that are layed out in the rest of this README
33+
1134
## Counter
1235

1336
Counters go up, and reset when the process restarts.
1437

1538
```swift
16-
let prom = PrometheusClient()
17-
18-
let counter = prom.createCounter(forType: Int.self, named: "my_counter")
39+
let counter = myProm.createCounter(forType: Int.self, named: "my_counter")
1940
counter.inc() // Increment by 1
2041
counter.inc(12) // Increment by given value
2142
```
@@ -25,9 +46,7 @@ counter.inc(12) // Increment by given value
2546
Gauges can go up and down
2647

2748
```swift
28-
let prom = PrometheusClient()
29-
30-
let gauge = prom.createGauge(forType: Int.self, named: "my_gauge")
49+
let gauge = myProm.createGauge(forType: Int.self, named: "my_gauge")
3150
gauge.inc() // Increment by 1
3251
gauge.dec(19) // Decrement by given value
3352
gauge.set(12) // Set to a given value
@@ -38,9 +57,7 @@ gauge.set(12) // Set to a given value
3857
Histograms track the size and number of events in buckets. This allows for aggregatable calculation of quantiles.
3958

4059
```swift
41-
let prom = PrometheusClient()
42-
43-
let histogram = prom.createHistogram(forType: Double.self, named: "my_histogram")
60+
let histogram = myProm.createHistogram(forType: Double.self, named: "my_histogram")
4461
histogram.observe(4.7) // Observe the given value
4562
```
4663

@@ -49,9 +66,7 @@ histogram.observe(4.7) // Observe the given value
4966
Summaries track the size and number of events
5067

5168
```swift
52-
let prom = PrometheusClient()
53-
54-
let summary = prom.createSummary(forType: Double.self, named: "my_summary")
69+
let summary = myProm.createSummary(forType: Double.self, named: "my_summary")
5570
summary.observe(4.7) // Observe the given value
5671
```
5772

@@ -72,7 +87,7 @@ struct MyInfoStruct: MetricLabels {
7287
}
7388
}
7489

75-
let prom = PrometheusClient()
90+
let info = myProm.createInfo(named: "my_info", helpText: "Just some info", labelType: MyInfoStruct.self)
7691

7792
let info = prom.createInfo(named: "my_info", helpText: "Just some info", labelType: MyInfoStruct.self)
7893

@@ -88,7 +103,7 @@ struct RouteLabels: MetricLabels {
88103
var route: String = "*"
89104
}
90105

91-
let prom = PrometheusClient()
106+
let counter = myProm.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter", withLabelType: RouteLabels.self)
92107

93108
let counter = prom.createCounter(forType: Int.self, named: "my_counter", helpText: "Just a counter", withLabelType: RouteLabels.self)
94109

@@ -101,12 +116,8 @@ To keep SwiftPrometheus as clean and lightweight as possible, there is no way of
101116

102117
This could look something like this:
103118
```swift
104-
router.get("/metrics") { request -> Future<String> in
105-
let promise = req.eventLoop.newPromise(String.self)
106-
prom.getMetrics {
107-
promise.succeed(result: $0)
108-
}
109-
return promise.futureResult
119+
router.get("/metrics") { request -> String in
120+
return myProm.collect()
110121
}
111122
```
112123
Here, I used [Vapor](https://github.com/vapor/vapor) syntax, but this will work with any web framework, since it's just returning a plain String.

Sources/Prometheus/MetricTypes/Counter.swift

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
1+
import NIOConcurrencyHelpers
2+
13
/// Prometheus Counter metric
24
///
3-
/// See https://prometheus.io/docs/concepts/metric_types/#counter
4-
public class Counter<NumType: Numeric, Labels: MetricLabels>: Metric, PrometheusHandled {
5+
/// See: https://prometheus.io/docs/concepts/metric_types/#counter
6+
public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, PrometheusHandled {
57
/// Prometheus instance that created this Counter
6-
internal let prometheus: PrometheusClient
8+
internal weak var prometheus: PrometheusClient?
79

810
/// Name of the Counter, required
911
public let name: String
1012
/// Help text of the Counter, optional
1113
public let help: String?
1214

1315
/// Type of the metric, used for formatting
14-
public let _type: MetricType = .counter
16+
public let _type: PromMetricType = .counter
1517

1618
/// Current value of the counter
1719
internal var value: NumType
1820

1921
/// Initial value of the counter
20-
private var initialValue: NumType
22+
private let initialValue: NumType
2123

2224
/// Storage of values that have labels attached
2325
internal var metrics: [Labels: NumType] = [:]
2426

27+
/// Lock used for thread safety
28+
internal let lock: Lock
29+
2530
/// Creates a new instance of a Counter
2631
///
2732
/// - Parameters:
@@ -35,14 +40,15 @@ public class Counter<NumType: Numeric, Labels: MetricLabels>: Metric, Prometheus
3540
self.initialValue = initialValue
3641
self.value = initialValue
3742
self.prometheus = p
43+
self.lock = Lock()
3844
}
3945

4046
/// Gets the metric string for this counter
4147
///
4248
/// - Returns:
4349
/// Newline seperated Prometheus formatted metric string
44-
public func getMetric(_ done: @escaping (String) -> Void) {
45-
prometheusQueue.async(flags: .barrier) {
50+
public func collect() -> String {
51+
return self.lock.withLock {
4652
var output = [String]()
4753

4854
if let help = self.help {
@@ -57,26 +63,28 @@ public class Counter<NumType: Numeric, Labels: MetricLabels>: Metric, Prometheus
5763
output.append("\(self.name)\(labelsString) \(value)")
5864
}
5965

60-
done(output.joined(separator: "\n"))
66+
return output.joined(separator: "\n")
6167
}
6268
}
6369

70+
6471
/// Increments the Counter
6572
///
6673
/// - Parameters:
6774
/// - amount: Amount to increment the counter with
6875
/// - labels: Labels to attach to the value
6976
///
70-
public func inc(_ amount: NumType = 1, _ labels: Labels? = nil, _ done: @escaping (NumType) -> Void = { _ in }) {
71-
prometheusQueue.async(flags: .barrier) {
77+
@discardableResult
78+
public func inc(_ amount: NumType = 1, _ labels: Labels? = nil) -> NumType {
79+
return self.lock.withLock {
7280
if let labels = labels {
7381
var val = self.metrics[labels] ?? self.initialValue
7482
val += amount
7583
self.metrics[labels] = val
76-
done(val)
84+
return val
7785
} else {
7886
self.value += amount
79-
done(self.value)
87+
return self.value
8088
}
8189
}
8290
}
@@ -88,10 +96,12 @@ public class Counter<NumType: Numeric, Labels: MetricLabels>: Metric, Prometheus
8896
///
8997
/// - Returns: The value of the Counter attached to the provided labels
9098
public func get(_ labels: Labels? = nil) -> NumType {
91-
if let labels = labels {
92-
return self.metrics[labels] ?? initialValue
93-
} else {
94-
return self.value
99+
return self.lock.withLock {
100+
if let labels = labels {
101+
return self.metrics[labels] ?? initialValue
102+
} else {
103+
return self.value
104+
}
95105
}
96106
}
97107
}

0 commit comments

Comments
 (0)