Timer metrics are metrics that measure nanosecond-precision operations within
the sentry, and present the results in an aggregated manner using distribution
bucketing.
They have a more convenient API than using distribution metrics directly, but
otherwise are just a convenient wrapper on top of them.
This requires exposing `runtime.nanotime()` from the Go runtime in order to
get the current time without causing system calls.
Intended usage:
```go
m := NewTimerMetric(...)
...
op := m.Start() // Starts measuring time from this point
... do something interesting...
op.Finish() // End the stopwatch.
```
Operation structs are meant to be self-contained to be able to be passed
around in code. Additionally, the timer metrics support multiple fields,
specifiable both at operation `Start` and `Finish` time.
This can be useful for measuring operations that can go down multiple
distinct branches, and creating aggregates that can selectively
differentiate between them. For example:
```go
m := NewTimerMetric("/packet_processing_time", ..., "protocol", "path")
...
func HandleTCPPacket(pkt TCPPacket) {
op := m.Start("tcp")
if fast path {
... do fast TCP handling...
op.Finish("fast")
return
}
doSlowTCPHandling(pkt, op) // `op` can be passed around
}
func doSlowTCPHandling(pkt TCPPacket, op TimerOperation) {
... do slow TCP handling...
op.Finish("slow")
}
func HandleUDPPacket(pkt UDPPacket) {
op := m.Start("udp")
... do UDP packet handling...
op.Finish("")
}
```
PiperOrigin-RevId: 436641265