TimeUnits (#42)
motivation: some metrics backend prefer to be given a hint about the preferred display unit (seconds, milliseconds, etc) to drive the ux changes: add a `preferedUnit` to TimerHandler (and `TimeUnits`) to capture the prefer display unit
This commit is contained in:
parent
853893bbf2
commit
3fefedaaef
|
|
@ -5,3 +5,4 @@
|
||||||
.xcode
|
.xcode
|
||||||
.SourceKitten
|
.SourceKitten
|
||||||
*.orig
|
*.orig
|
||||||
|
.swiftpm
|
||||||
|
|
@ -172,6 +172,10 @@ public class Gauge: Recorder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum TimeUnit {
|
||||||
|
case nanoseconds, milliseconds, seconds, minutes, hours, days
|
||||||
|
}
|
||||||
|
|
||||||
public extension Timer {
|
public extension Timer {
|
||||||
/// Create a new `Timer`.
|
/// Create a new `Timer`.
|
||||||
///
|
///
|
||||||
|
|
@ -183,6 +187,18 @@ public extension Timer {
|
||||||
self.init(label: label, dimensions: dimensions, handler: handler)
|
self.init(label: label, dimensions: dimensions, handler: handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `Timer`.
|
||||||
|
///
|
||||||
|
/// - parameters:
|
||||||
|
/// - label: The label for the `Timer`.
|
||||||
|
/// - dimensions: The dimensions for the `Timer`.
|
||||||
|
/// - displayUnit: A hint to the backend responsible for presenting the data of the preferred display unit. This is not guaranteed to be supported by all backends.
|
||||||
|
convenience init(label: String, dimensions: [(String, String)] = [], preferredDisplayUnit displayUnit: TimeUnit) {
|
||||||
|
let handler = MetricsSystem.factory.makeTimer(label: label, dimensions: dimensions)
|
||||||
|
handler.preferDisplayUnit(displayUnit)
|
||||||
|
self.init(label: label, dimensions: dimensions, handler: handler)
|
||||||
|
}
|
||||||
|
|
||||||
/// Signal the underlying metrics library that this timer will never be updated again.
|
/// Signal the underlying metrics library that this timer will never be updated again.
|
||||||
/// In response the library MAY decide to eagerly release any resources held by this `Timer`.
|
/// In response the library MAY decide to eagerly release any resources held by this `Timer`.
|
||||||
@inlinable
|
@inlinable
|
||||||
|
|
@ -483,6 +499,18 @@ public protocol TimerHandler: AnyObject {
|
||||||
/// - parameters:
|
/// - parameters:
|
||||||
/// - value: Duration to record.
|
/// - value: Duration to record.
|
||||||
func recordNanoseconds(_ duration: Int64)
|
func recordNanoseconds(_ duration: Int64)
|
||||||
|
|
||||||
|
/// Set the preferred display unit for this TimerHandler.
|
||||||
|
///
|
||||||
|
/// - parameters:
|
||||||
|
/// - unit: A hint to the backend responsible for presenting the data of the preferred display unit. This is not guaranteed to be supported by all backends.
|
||||||
|
func preferDisplayUnit(_ unit: TimeUnit)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimerHandler {
|
||||||
|
public func preferDisplayUnit(_: TimeUnit) {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Predefined Metrics Handlers
|
// MARK: Predefined Metrics Handlers
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ extension MetricsExtensionsTests {
|
||||||
("testTimerBlock", testTimerBlock),
|
("testTimerBlock", testTimerBlock),
|
||||||
("testTimerWithTimeInterval", testTimerWithTimeInterval),
|
("testTimerWithTimeInterval", testTimerWithTimeInterval),
|
||||||
("testTimerWithDispatchTime", testTimerWithDispatchTime),
|
("testTimerWithDispatchTime", testTimerWithDispatchTime),
|
||||||
|
("testTimerUnits", testTimerUnits),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,32 @@ class MetricsExtensionsTests: XCTestCase {
|
||||||
XCTAssertEqual(testTimer.values.count, 5, "expected number of entries to match")
|
XCTAssertEqual(testTimer.values.count, 5, "expected number of entries to match")
|
||||||
XCTAssertEqual(testTimer.values[4].1, 0, "expected value to match")
|
XCTAssertEqual(testTimer.values[4].1, 0, "expected value to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testTimerUnits() throws {
|
||||||
|
let metrics = TestMetrics()
|
||||||
|
MetricsSystem.bootstrapInternal(metrics)
|
||||||
|
|
||||||
|
let name = "timer-\(NSUUID().uuidString)"
|
||||||
|
let value = Int64.random(in: 0 ... 1000)
|
||||||
|
|
||||||
|
let timer = Timer(label: name)
|
||||||
|
timer.recordNanoseconds(value)
|
||||||
|
|
||||||
|
let testTimer = timer.handler as! TestTimer
|
||||||
|
XCTAssertEqual(testTimer.values.count, 1, "expected number of entries to match")
|
||||||
|
XCTAssertEqual(testTimer.values.first!.1, value, "expected value to match")
|
||||||
|
XCTAssertEqual(metrics.timers.count, 1, "timer should have been stored")
|
||||||
|
|
||||||
|
let secondsName = "timer-seconds-\(NSUUID().uuidString)"
|
||||||
|
let secondsValue = Int64.random(in: 0 ... 1000)
|
||||||
|
let secondsTimer = Timer(label: secondsName, preferredDisplayUnit: .seconds)
|
||||||
|
secondsTimer.recordSeconds(secondsValue)
|
||||||
|
|
||||||
|
let testSecondsTimer = secondsTimer.handler as! TestTimer
|
||||||
|
XCTAssertEqual(testSecondsTimer.values.count, 1, "expected number of entries to match")
|
||||||
|
XCTAssertEqual(testSecondsTimer.retriveValueInPreferredUnit(atIndex: 0), secondsValue, "expected value to match")
|
||||||
|
XCTAssertEqual(metrics.timers.count, 2, "timer should have been stored")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://bugs.swift.org/browse/SR-6310
|
// https://bugs.swift.org/browse/SR-6310
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@ internal class TestRecorder: RecorderHandler, Equatable {
|
||||||
internal class TestTimer: TimerHandler, Equatable {
|
internal class TestTimer: TimerHandler, Equatable {
|
||||||
let id: String
|
let id: String
|
||||||
let label: String
|
let label: String
|
||||||
|
var displayUnit: TimeUnit?
|
||||||
let dimensions: [(String, String)]
|
let dimensions: [(String, String)]
|
||||||
|
|
||||||
let lock = NSLock()
|
let lock = NSLock()
|
||||||
|
|
@ -143,9 +144,33 @@ internal class TestTimer: TimerHandler, Equatable {
|
||||||
init(label: String, dimensions: [(String, String)]) {
|
init(label: String, dimensions: [(String, String)]) {
|
||||||
self.id = NSUUID().uuidString
|
self.id = NSUUID().uuidString
|
||||||
self.label = label
|
self.label = label
|
||||||
|
self.displayUnit = nil
|
||||||
self.dimensions = dimensions
|
self.dimensions = dimensions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func preferDisplayUnit(_ unit: TimeUnit) {
|
||||||
|
self.lock.withLock {
|
||||||
|
self.displayUnit = unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func retriveValueInPreferredUnit(atIndex i: Int) -> Int64 {
|
||||||
|
return self.lock.withLock {
|
||||||
|
let value = values[i].1
|
||||||
|
guard let displayUnit = self.displayUnit else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
switch displayUnit {
|
||||||
|
case .days: return (value / 1_000_000_000) * 60 * 60 * 24
|
||||||
|
case .hours: return (value / 1_000_000_000) * 60 * 60
|
||||||
|
case .minutes: return (value / 1_000_000_000) * 60
|
||||||
|
case .seconds: return value / 1_000_000_000
|
||||||
|
case .milliseconds: return value / 1_000_000
|
||||||
|
case .nanoseconds: return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func recordNanoseconds(_ duration: Int64) {
|
func recordNanoseconds(_ duration: Int64) {
|
||||||
self.lock.withLock {
|
self.lock.withLock {
|
||||||
values.append((Date(), duration))
|
values.append((Date(), duration))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue