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:
Jari (LotU) 2019-09-09 18:53:03 +02:00 committed by tomer doron
parent 853893bbf2
commit 3fefedaaef
5 changed files with 81 additions and 0 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
.xcode
.SourceKitten
*.orig
.swiftpm

View File

@ -172,6 +172,10 @@ public class Gauge: Recorder {
}
}
public enum TimeUnit {
case nanoseconds, milliseconds, seconds, minutes, hours, days
}
public extension Timer {
/// Create a new `Timer`.
///
@ -183,6 +187,18 @@ public extension Timer {
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.
/// In response the library MAY decide to eagerly release any resources held by this `Timer`.
@inlinable
@ -483,6 +499,18 @@ public protocol TimerHandler: AnyObject {
/// - parameters:
/// - value: Duration to record.
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

View File

@ -28,6 +28,7 @@ extension MetricsExtensionsTests {
("testTimerBlock", testTimerBlock),
("testTimerWithTimeInterval", testTimerWithTimeInterval),
("testTimerWithDispatchTime", testTimerWithDispatchTime),
("testTimerUnits", testTimerUnits),
]
}
}

View File

@ -77,6 +77,32 @@ class MetricsExtensionsTests: XCTestCase {
XCTAssertEqual(testTimer.values.count, 5, "expected number of entries 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

View File

@ -135,6 +135,7 @@ internal class TestRecorder: RecorderHandler, Equatable {
internal class TestTimer: TimerHandler, Equatable {
let id: String
let label: String
var displayUnit: TimeUnit?
let dimensions: [(String, String)]
let lock = NSLock()
@ -143,9 +144,33 @@ internal class TestTimer: TimerHandler, Equatable {
init(label: String, dimensions: [(String, String)]) {
self.id = NSUUID().uuidString
self.label = label
self.displayUnit = nil
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) {
self.lock.withLock {
values.append((Date(), duration))