# Metrics

**URL:** <https://forums.swift.org/t/metrics/19353>\
**Category:** Pitches\
**Created:** [January 8, 2019, 9:30pm UTC](https://forums.swift.org/t/metrics/19353 "2019-01-08T21:30:27Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tomerd/32/3402_2.png) [@tomerd](https://forums.swift.org/u/tomerd)\
**Post date:** [January 8, 2019, 9:30pm UTC](https://forums.swift.org/t/metrics/19353/1 "2019-01-08T21:30:27Z")

</div>

Another area the Swift Server working group is looking to improve is server-side metrics. Currently, there is no standard metrics solution which works across frameworks and libraries.

We are interested to collect ideas and requirements people have, especially where they are informed by experience in other language ecosystems, or experience deploying server-side applications in production today.

To kick the discussion off, I'll reply to this message with some initials thoughts of mine.

---

<div class="post-metadata">

**Author:** ![tomerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tomerd/32/3402_2.png) [@tomerd](https://forums.swift.org/u/tomerd)\
**Post date:** [January 8, 2019, 9:44pm UTC](https://forums.swift.org/t/metrics/19353/2 "2019-01-08T21:44:41Z")

</div>

wSimilar to what we are defining for [logging](https://forums.swift.org/t/feedback-server-logging-api-with-revisions/), we would like to suggest an abstract metrics API which will allow application owners to plug-in different metrics backends, and library developers to emit metrics without getting in the way. Reasoning for such architecture is summarized in [here](https://forums.swift.org/t/server-logging-api/18834/17), that post is about logging but the same is true for metrics as well.

An example for such abstract API could look something like:

```swift
public protocol Counter: AnyObject {
    func increment<DataType: BinaryInteger>(_ value: DataType)
}

public protocol Recorder: AnyObject {
    func record<DataType: BinaryInteger>(_ value: DataType)
    func record<DataType: BinaryFloatingPoint>(_ value: DataType)
}

public protocol Timer: AnyObject {
    func recordNanoseconds(_ duration: Int64)
}

public protocol MetricsHandler {
    func makeCounter(label: String, dimensions: [(String, String)]) -> Counter
    func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder
    func makeTimer(label: String, dimensions: [(String, String)]) -> Timer
}

public extension MetricsHandler {
    @inlinable
    func makeGauge(label: String, dimensions: [(String, String)] = []) -> Recorder {
        return self.makeRecorder(label: label, dimensions: dimensions, aggregate: false)
    }
}

```

this means:

1. the actual metrics library (eg [https://forums.swift.org/t/client-side-prometheus-implementation](https://forums.swift.org/t/client-side-prometheus-implementation)) will implement `MetricsHandler`

2. the application owner bootstrap the with metrics library of choice (exact API tbd)

3. application and libraries developers call something like the below when they want to emit metrics

```swift
let counter = Metrics.makeCounter("foo")
...
counter.increment()` 

```

---

<div class="post-metadata">

**Author:** ![tanner0101](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tanner0101/32/675_2.png) [@tanner0101](https://forums.swift.org/u/tanner0101)\
**Post date:** [January 10, 2019, 6:39pm UTC](https://forums.swift.org/t/metrics/19353/3 "2019-01-10T18:39:01Z")

</div>

This looks like a great start. A couple questions:

Why do the protocols conform to `AnyObject`? Is this just another way of restricting the protocol to a class, or does it mean something more? Side note, maybe it would be nice to have a protocol for this like `MetricsObject` or something that unites them.

How will you register which `MetricsHandler` you want to use? Will that be exactly like how logging works? Something like

```swift
Metrics.bootstrap(PrometheusMetricsHandler.init)

```

How will using shared metrics objects work? It seems that with the current design, if you wanted to use a shared counter, recorder, and timer you would need to pass those all separately (i.e., `req.counter.increment()`, `req.recorder.record(...)`). Comparing to logging, we have:

- `Metrics` = `Logging`
- `MetricsHandler` = `LogHandler`
- `AnyObject` (`MetricsObject`) = `Logger`, but more than one

Maybe there should be a separate, unified type like `Logger` in this package. With Logging, you just pass around the `Logger` and you can log any type of message (i.e., `req.logger.log(...)`).  
But I'm not really sure if this difference is a bad thing. Maybe it's a common use case to only want to pass around one type of metrics object.

---

<div class="post-metadata">

**Author:** ![tomerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tomerd/32/3402_2.png) [@tomerd](https://forums.swift.org/u/tomerd)\
**Post date:** [January 10, 2019, 9:59pm UTC](https://forums.swift.org/t/metrics/19353/4 "2019-01-10T21:59:43Z")

</div>

thanks for the feedback @tanner0101

> Why do the protocols conform to `AnyObject` ? Is this just another way of restricting the protocol to a class, or does it mean something more?

by forcing them to be classes, we can reduce their memory footprint, and there is no real advantage of allowing them to be structs as they don't carry state

> How will you register which `MetricsHandler` you want to use? Will that be exactly like how logging works?

exactly! you would "bind" your backend of choice at application bootstrap time, just as you described

> Maybe there should be a separate, unified type like `Logger` in this package. With Logging, you just pass around the `Logger` and you can log any type of message

the main reason to pass loggers around (for example, expose them at the request/context object) is that they carry contextual state (metadata) which you want to preserve. with metrics, you dont typically carry such state, so its less often the case you want to pass them around. having said that, the dimensions are typically contextual so an argument could be made to pass some metrics object that carries them around and from which you can create named timers, counters, etc. good direction to explore further, not sure of it should be part of the core api or implemented on top by frameworks that want to operate in that manner

---

<div class="post-metadata">

**Author:** ![tanner0101](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tanner0101/32/675_2.png) [@tanner0101](https://forums.swift.org/u/tanner0101)\
**Post date:** [January 11, 2019, 8:46pm UTC](https://forums.swift.org/t/metrics/19353/5 "2019-01-11T20:46:41Z")

</div>

> [@tomerd](#):
>
> with metrics, you dont typically carry such state, so its less often the case you want to pass them around.

Ah okay, that makes sense then. Thanks!

---

<div class="post-metadata">

**Author:** ![Blejwi](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/blejwi/32/5818_2.png) [@Blejwi](https://forums.swift.org/u/Blejwi)\
**Post date:** [January 14, 2019, 12:51pm UTC](https://forums.swift.org/t/metrics/19353/6 "2019-01-14T12:51:42Z")

</div>

Hello,

Great work with the proposal!

I'm thinking about a couple of usability improvements for `Timer` class, which may be very useful for future `Metrics` users.  
I've just posted GitHub issue with my idea, here is the link: [Metrics Timer usability improvement idea · Issue #2 · tomerd/swift-server-metrics-api-proposal · GitHub](https://github.com/tomerd/swift-server-metrics-api-proposal/issues/2)

Didn't want to copy-paste the whole issue, if it's more convenient for you just let me know and I will move it here from GitHub.

Thanks!

---

<div class="post-metadata">

**Author:** ![tomerd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tomerd/32/3402_2.png) [@tomerd](https://forums.swift.org/u/tomerd)\
**Post date:** [March 25, 2019, 1:04am UTC](https://forums.swift.org/t/metrics/19353/7 "2019-03-25T01:04:53Z")

</div>

Closed on March 25, 2019, 1:04am UTC


