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.
wSimilar to what we are defining for logging, 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, that post is about logging but the same is true for metrics as well.
An example for such abstract API could look something like:
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
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.
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