Skip to content

Commit 7ae2024

Browse files
authored
Merge pull request #41 from Mordil/40-metrics
Implement Basic Metrics (#40)
2 parents 0b28491 + 9df6539 commit 7ae2024

File tree

3 files changed

+102
-3
lines changed

3 files changed

+102
-3
lines changed

Sources/NIORedis/ChannelHandlers/RedisCommandHandler.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ extension RedisCommandHandler: ChannelInboundHandler {
6363
}
6464
leadPromise.fail(error)
6565
context.fireErrorCaught(error)
66+
RedisMetrics.commandFailureCount.increment()
6667
}
6768

6869
/// Invoked by NIO when a read has been fired from earlier in the response chain. This forwards the unwrapped
@@ -82,8 +83,13 @@ extension RedisCommandHandler: ChannelInboundHandler {
8283
assert(popped != nil)
8384

8485
switch value {
85-
case .error(let e): leadPromise.fail(e)
86-
default: leadPromise.succeed(value)
86+
case .error(let e):
87+
leadPromise.fail(e)
88+
RedisMetrics.commandFailureCount.increment()
89+
90+
default:
91+
leadPromise.succeed(value)
92+
RedisMetrics.commandSuccessCount.increment()
8793
}
8894
}
8995
}

Sources/NIORedis/RedisClient.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import struct Foundation.UUID
16+
import struct Dispatch.DispatchTime
1617
import Logging
18+
import Metrics
1719
import NIO
1820
import NIOConcurrencyHelpers
1921

@@ -107,6 +109,8 @@ public final class RedisConnection: RedisClient {
107109
self.logger[metadataKey: loggingKeyID] = "\(UUID())"
108110
self.logger.debug("Connection created.")
109111
self._state = .open
112+
RedisMetrics.activeConnectionCount += 1
113+
RedisMetrics.totalConnectionCount.increment()
110114
}
111115

112116
/// Sends a `QUIT` command, then closes the `Channel` this instance was initialized with.
@@ -126,7 +130,10 @@ public final class RedisConnection: RedisClient {
126130
self.channel.close(promise: promise)
127131
return promise.futureResult
128132
}
129-
.map { self.logger.debug("Connection closed.") }
133+
.map {
134+
self.logger.debug("Connection closed.")
135+
RedisMetrics.activeConnectionCount -= 1
136+
}
130137
.recover {
131138
self.logger.error("Encountered error during close(): \($0)")
132139
self.state = .open
@@ -162,7 +169,10 @@ public final class RedisConnection: RedisClient {
162169
promise: promise
163170
)
164171

172+
let startTime = DispatchTime.now().uptimeNanoseconds
165173
promise.futureResult.whenComplete { result in
174+
let duration = DispatchTime.now().uptimeNanoseconds - startTime
175+
RedisMetrics.commandRoundTripTime.recordNanoseconds(duration)
166176
guard case let .failure(error) = result else { return }
167177
self.logger.error("\(error.localizedDescription)")
168178
}

Sources/NIORedis/RedisMetrics.swift

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the NIORedis open source project
4+
//
5+
// Copyright (c) 2019 NIORedis project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of NIORedis project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Metrics
16+
17+
/// The system funnel for all `Metrics` interactions from the Redis library.
18+
///
19+
/// It is highly recommended to not interact with this directly, and to let the library
20+
/// use it how it sees fit.
21+
///
22+
/// There is a nested enum type of `RedisMetrics.Label` that is available to query, match, etc. the
23+
/// labels used for all of the `Metrics` types created by the Redis library.
24+
public struct RedisMetrics {
25+
/// An enumeration of all the labels used by the Redis library for various `Metrics` data points.
26+
///
27+
/// Each is backed by a raw string, and this type is `CustomStringConvertible` to receive a
28+
/// namespaced description in the form of `"NIORedis.<rawValue>"`.
29+
public enum Label: String, CustomStringConvertible {
30+
case totalConnectionCount
31+
case activeConnectionCount
32+
case commandSuccessCount
33+
case commandFailureCount
34+
case commandRoundTripTime
35+
36+
public var description: String {
37+
return "NIORedis.\(self.rawValue)"
38+
}
39+
}
40+
41+
private static let activeConnectionCountGauge = Gauge(label: .activeConnectionCount)
42+
/// The current number of connections this library has active.
43+
/// - Note: Changing this number will update the `Metrics.Gauge` stored for recording the new value.
44+
public static var activeConnectionCount: Int = 0 {
45+
didSet {
46+
activeConnectionCountGauge.record(activeConnectionCount)
47+
}
48+
}
49+
/// The `Metrics.Counter` that retains the number of connections made since application startup.
50+
public static let totalConnectionCount = Counter(label: .totalConnectionCount)
51+
/// The `Metrics.Counter` that retains the number of commands that successfully returned from Redis
52+
/// since application startup.
53+
public static let commandSuccessCount = Counter(label: .commandSuccessCount)
54+
/// The `Metrics.Counter` that retains the number of commands that failed from errors returned
55+
/// by Redis since application startup.
56+
public static let commandFailureCount = Counter(label: .commandFailureCount)
57+
/// The `Metrics.Timer` that receives command response times in nanoseconds from when a command
58+
/// is first sent through the `NIO.Channel`, to when the response is first resolved.
59+
public static let commandRoundTripTime = Timer(label: .commandRoundTripTime)
60+
61+
private init() { }
62+
}
63+
64+
extension Metrics.Counter {
65+
@inline(__always)
66+
convenience init(label: RedisMetrics.Label) {
67+
self.init(label: label.description)
68+
}
69+
}
70+
71+
extension Metrics.Gauge {
72+
@inline(__always)
73+
convenience init(label: RedisMetrics.Label) {
74+
self.init(label: label.description)
75+
}
76+
}
77+
78+
extension Metrics.Timer {
79+
@inline(__always)
80+
convenience init(label: RedisMetrics.Label) {
81+
self.init(label: label.description)
82+
}
83+
}

0 commit comments

Comments
 (0)