Skip to content

Commit

Permalink
Merge pull request #4 from MrLotU/MetricsLib
Browse files Browse the repository at this point in the history
Implement swift-metrics
  • Loading branch information
MrLotU authored Jul 5, 2019
2 parents 19756ae + bb6db84 commit 43baf55
Show file tree
Hide file tree
Showing 107 changed files with 979 additions and 23,814 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
/.build
/Packages
/*.xcodeproj
build/
build/
.swiftpm/
6 changes: 0 additions & 6 deletions .travis.yml

This file was deleted.

34 changes: 34 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"object": {
"pins": [
{
"package": "swift-metrics",
"repositoryURL": "https://github.com/apple/swift-metrics.git",
"state": {
"branch": null,
"revision": "8e5110dcd6584ff5acfec40e31263a8d07cba783",
"version": "1.1.0"
}
},
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "ba7970fe396e8198b84c6c1b44b38a1d4e2eb6bd",
"version": "1.14.1"
}
},
{
"package": "swift-nio-zlib-support",
"repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git",
"state": {
"branch": null,
"revision": "37760e9a52030bb9011972c5213c3350fa9d41fd",
"version": "1.0.0"
}
}
]
},
"version": 1
}
13 changes: 10 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,22 @@ let package = Package(
name: "SwiftPrometheus",
targets: ["Prometheus"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-metrics.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-nio.git", Version("1.0.0") ..< Version("3.0.0")),
],
targets: [
.target(
name: "Prometheus",
dependencies: []),
dependencies: ["NIOConcurrencyHelpers"]),
.target(
name: "PrometheusMetrics",
dependencies: ["Prometheus", "CoreMetrics"]),
.target(
name: "PrometheusExample",
dependencies: ["Prometheus"]),
dependencies: ["PrometheusMetrics", "Metrics"]),
.testTarget(
name: "SwiftPrometheusTests",
dependencies: ["Prometheus"]),
dependencies: ["Prometheus", "PrometheusMetrics"]),
]
)
53 changes: 32 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![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)
[![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)

# SwiftPrometheus, Prometheus client for Swift

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

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

First, we have to create an instance of our `PrometheusClient`:
```swift
import Prometheus
let myProm = PrometheusClient()
```

## Usage with Swift-Metrics
_For more details about swift-metrics, check the GitHub repo [here](https://github.com/apple/swift-metrics)_

To use SwiftPrometheus with swift-metrics, all the setup required is this:
```swift
import PrometheusMetrics // Auto imports Prometheus too, but adds the swift-metrics compatibility
let myProm = PrometheusClient()
MetricsSystem.bootstrap(myProm)
```

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`:
```swift
// This is the same instance was used in `.bootstrap()` earlier.
let promInstance = try MetricsSystem.prometheus()
```
You can than use the same APIs that are layed out in the rest of this README

## Counter

Counters go up, and reset when the process restarts.

```swift
let prom = PrometheusClient()

let counter = prom.createCounter(forType: Int.self, named: "my_counter")
let counter = myProm.createCounter(forType: Int.self, named: "my_counter")
counter.inc() // Increment by 1
counter.inc(12) // Increment by given value
```
Expand All @@ -25,9 +46,7 @@ counter.inc(12) // Increment by given value
Gauges can go up and down

```swift
let prom = PrometheusClient()

let gauge = prom.createGauge(forType: Int.self, named: "my_gauge")
let gauge = myProm.createGauge(forType: Int.self, named: "my_gauge")
gauge.inc() // Increment by 1
gauge.dec(19) // Decrement by given value
gauge.set(12) // Set to a given value
Expand All @@ -38,9 +57,7 @@ gauge.set(12) // Set to a given value
Histograms track the size and number of events in buckets. This allows for aggregatable calculation of quantiles.

```swift
let prom = PrometheusClient()

let histogram = prom.createHistogram(forType: Double.self, named: "my_histogram")
let histogram = myProm.createHistogram(forType: Double.self, named: "my_histogram")
histogram.observe(4.7) // Observe the given value
```

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

```swift
let prom = PrometheusClient()

let summary = prom.createSummary(forType: Double.self, named: "my_summary")
let summary = myProm.createSummary(forType: Double.self, named: "my_summary")
summary.observe(4.7) // Observe the given value
```

Expand All @@ -72,7 +87,7 @@ struct MyInfoStruct: MetricLabels {
}
}

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

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

Expand All @@ -88,7 +103,7 @@ struct RouteLabels: MetricLabels {
var route: String = "*"
}

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

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

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

This could look something like this:
```swift
router.get("/metrics") { request -> Future<String> in
let promise = req.eventLoop.newPromise(String.self)
prom.getMetrics {
promise.succeed(result: $0)
}
return promise.futureResult
router.get("/metrics") { request -> String in
return myProm.collect()
}
```
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.
Expand Down
42 changes: 26 additions & 16 deletions Sources/Prometheus/MetricTypes/Counter.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import NIOConcurrencyHelpers

/// Prometheus Counter metric
///
/// See https://prometheus.io/docs/concepts/metric_types/#counter
public class Counter<NumType: Numeric, Labels: MetricLabels>: Metric, PrometheusHandled {
/// See: https://prometheus.io/docs/concepts/metric_types/#counter
public class PromCounter<NumType: Numeric, Labels: MetricLabels>: PromMetric, PrometheusHandled {
/// Prometheus instance that created this Counter
internal let prometheus: PrometheusClient
internal weak var prometheus: PrometheusClient?

/// 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: MetricType = .counter
public let _type: PromMetricType = .counter

/// Current value of the counter
internal var value: NumType

/// Initial value of the counter
private var initialValue: NumType
private let initialValue: NumType

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

/// Lock used for thread safety
internal let lock: Lock

/// Creates a new instance of a Counter
///
/// - Parameters:
Expand All @@ -35,14 +40,15 @@ public class Counter<NumType: Numeric, Labels: MetricLabels>: Metric, Prometheus
self.initialValue = initialValue
self.value = initialValue
self.prometheus = p
self.lock = Lock()
}

/// Gets the metric string for this counter
///
/// - Returns:
/// Newline seperated Prometheus formatted metric string
public func getMetric(_ done: @escaping (String) -> Void) {
prometheusQueue.async(flags: .barrier) {
public func collect() -> String {
return self.lock.withLock {
var output = [String]()

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

done(output.joined(separator: "\n"))
return output.joined(separator: "\n")
}
}


/// Increments the Counter
///
/// - Parameters:
/// - amount: Amount to increment the counter with
/// - labels: Labels to attach to the value
///
public func inc(_ amount: NumType = 1, _ labels: Labels? = nil, _ done: @escaping (NumType) -> Void = { _ in }) {
prometheusQueue.async(flags: .barrier) {
@discardableResult
public func inc(_ amount: NumType = 1, _ labels: Labels? = nil) -> NumType {
return self.lock.withLock {
if let labels = labels {
var val = self.metrics[labels] ?? self.initialValue
val += amount
self.metrics[labels] = val
done(val)
return val
} else {
self.value += amount
done(self.value)
return self.value
}
}
}
Expand All @@ -88,10 +96,12 @@ public class Counter<NumType: Numeric, Labels: MetricLabels>: Metric, Prometheus
///
/// - Returns: The value of the Counter attached to the provided labels
public func get(_ labels: Labels? = nil) -> NumType {
if let labels = labels {
return self.metrics[labels] ?? initialValue
} else {
return self.value
return self.lock.withLock {
if let labels = labels {
return self.metrics[labels] ?? initialValue
} else {
return self.value
}
}
}
}
Expand Down
Loading

0 comments on commit 43baf55

Please sign in to comment.