swift-metrics/Sources/CoreMetrics/Metrics.swift

229 lines
7.6 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Metrics API open source project
//
// Copyright (c) 2018-2019 Apple Inc. and the Swift Metrics API project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
public protocol Counter: AnyObject {
func increment<DataType: BinaryInteger>(_ value: DataType)
}
public extension Counter {
@inlinable
func increment() {
self.increment(1)
}
}
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 extension Timer {
@inlinable
func recordMicroseconds<DataType: BinaryInteger>(_ duration: DataType) {
self.recordNanoseconds(Int64(duration) * 1000)
}
@inlinable
func recordMicroseconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
self.recordNanoseconds(Int64(duration * 1000))
}
@inlinable
func recordMilliseconds<DataType: BinaryInteger>(_ duration: DataType) {
self.recordNanoseconds(Int64(duration) * 1_000_000)
}
@inlinable
func recordMilliseconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
self.recordNanoseconds(Int64(duration * 1_000_000))
}
@inlinable
func recordSeconds<DataType: BinaryInteger>(_ duration: DataType) {
self.recordNanoseconds(Int64(duration) * 1_000_000_000)
}
@inlinable
func recordSeconds<DataType: BinaryFloatingPoint>(_ duration: DataType) {
self.recordNanoseconds(Int64(duration * 1_000_000_000))
}
}
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 makeCounter(label: String) -> Counter {
return self.makeCounter(label: label, dimensions: [])
}
@inlinable
func makeRecorder(label: String, aggregate: Bool = true) -> Recorder {
return self.makeRecorder(label: label, dimensions: [], aggregate: aggregate)
}
@inlinable
func makeTimer(label: String) -> Timer {
return self.makeTimer(label: label, dimensions: [])
}
}
public extension MetricsHandler {
@inlinable
func makeGauge(label: String, dimensions: [(String, String)] = []) -> Recorder {
return self.makeRecorder(label: label, dimensions: dimensions, aggregate: false)
}
}
public extension MetricsHandler {
@inlinable
func withCounter(label: String, dimensions: [(String, String)] = [], then: (Counter) -> Void) {
then(self.makeCounter(label: label, dimensions: dimensions))
}
@inlinable
func withRecorder(label: String, dimensions: [(String, String)] = [], aggregate: Bool = true, then: (Recorder) -> Void) {
then(self.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate))
}
@inlinable
func withTimer(label: String, dimensions: [(String, String)] = [], then: (Timer) -> Void) {
then(self.makeTimer(label: label, dimensions: dimensions))
}
@inlinable
func withGauge(label: String, dimensions: [(String, String)] = [], then: (Recorder) -> Void) {
then(self.makeGauge(label: label, dimensions: dimensions))
}
}
public enum Metrics {
private static let lock = ReadWriteLock()
private static var _handler: MetricsHandler = NOOPMetricsHandler.instance
public static func bootstrap(_ handler: MetricsHandler) {
self.lock.withWriterLockVoid {
self._handler = handler
}
}
public static var global: MetricsHandler {
return self.lock.withReaderLock { self._handler }
}
}
public extension Metrics {
@inlinable
func makeCounter(label: String, dimensions: [(String, String)]) -> Counter {
return Metrics.global.makeCounter(label: label, dimensions: dimensions)
}
@inlinable
func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder {
return Metrics.global.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate)
}
@inlinable
func makeTimer(label: String, dimensions: [(String, String)]) -> Timer {
return Metrics.global.makeTimer(label: label, dimensions: dimensions)
}
}
public final class MultiplexMetricsHandler: MetricsHandler {
private let handlers: [MetricsHandler]
public init(handlers: [MetricsHandler]) {
self.handlers = handlers
}
public func makeCounter(label: String, dimensions: [(String, String)]) -> Counter {
return MuxCounter(handlers: self.handlers, label: label, dimensions: dimensions)
}
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder {
return MuxRecorder(handlers: self.handlers, label: label, dimensions: dimensions, aggregate: aggregate)
}
public func makeTimer(label: String, dimensions: [(String, String)]) -> Timer {
return MuxTimer(handlers: self.handlers, label: label, dimensions: dimensions)
}
private class MuxCounter: Counter {
let counters: [Counter]
public init(handlers: [MetricsHandler], label: String, dimensions: [(String, String)]) {
self.counters = handlers.map { $0.makeCounter(label: label, dimensions: dimensions) }
}
func increment<DataType: BinaryInteger>(_ value: DataType) {
self.counters.forEach { $0.increment(value) }
}
}
private class MuxRecorder: Recorder {
let recorders: [Recorder]
public init(handlers: [MetricsHandler], label: String, dimensions: [(String, String)], aggregate: Bool) {
self.recorders = handlers.map { $0.makeRecorder(label: label, dimensions: dimensions, aggregate: aggregate) }
}
func record<DataType: BinaryInteger>(_ value: DataType) {
self.recorders.forEach { $0.record(value) }
}
func record<DataType: BinaryFloatingPoint>(_ value: DataType) {
self.recorders.forEach { $0.record(value) }
}
}
private class MuxTimer: Timer {
let timers: [Timer]
public init(handlers: [MetricsHandler], label: String, dimensions: [(String, String)]) {
self.timers = handlers.map { $0.makeTimer(label: label, dimensions: dimensions) }
}
func recordNanoseconds(_ duration: Int64) {
self.timers.forEach { $0.recordNanoseconds(duration) }
}
}
}
public final class NOOPMetricsHandler: MetricsHandler, Counter, Recorder, Timer {
public static let instance = NOOPMetricsHandler()
private init() {}
public func makeCounter(label: String, dimensions: [(String, String)]) -> Counter {
return self
}
public func makeRecorder(label: String, dimensions: [(String, String)], aggregate: Bool) -> Recorder {
return self
}
public func makeTimer(label: String, dimensions: [(String, String)]) -> Timer {
return self
}
public func increment<DataType: BinaryInteger>(_: DataType) {}
public func record<DataType: BinaryInteger>(_: DataType) {}
public func record<DataType: BinaryFloatingPoint>(_: DataType) {}
public func recordNanoseconds(_: Int64) {}
}