RedisMetrics.swift 5.16 KB
Newer Older
Nathan Harris's avatar
Nathan Harris committed
1
2
//===----------------------------------------------------------------------===//
//
3
// This source file is part of the RediStack open source project
Nathan Harris's avatar
Nathan Harris committed
4
//
Nathan Harris's avatar
Nathan Harris committed
5
// Copyright (c) 2019-2020 RediStack project authors
Nathan Harris's avatar
Nathan Harris committed
6
7
8
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
9
// See CONTRIBUTORS.txt for the list of RediStack project authors
Nathan Harris's avatar
Nathan Harris committed
10
11
12
13
14
15
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Metrics
16
import NIOConcurrencyHelpers
Nathan Harris's avatar
Nathan Harris committed
17
18
19
20
21
22
23
24
25
26
27
28

/// The system funnel for all `Metrics` interactions from the Redis library.
///
/// It is highly recommended to not interact with this directly, and to let the library
/// use it how it sees fit.
///
/// There is a nested enum type of `RedisMetrics.Label` that is available to query, match, etc. the
/// labels used for all of the `Metrics` types created by the Redis library.
public struct RedisMetrics {
    /// An enumeration of all the labels used by the Redis library for various `Metrics` data points.
    ///
    /// Each is backed by a raw string, and this type is `CustomStringConvertible` to receive a
29
    /// namespaced description in the form of `"RediStack.<rawValue>"`.
Nathan Harris's avatar
Nathan Harris committed
30
31
32
    public enum Label: String, CustomStringConvertible {
        case totalConnectionCount
        case activeConnectionCount
Nathan Harris's avatar
Nathan Harris committed
33
34
35
        case activeChannelSubscriptions
        case activePatternSubscriptions
        case subscriptionMessagesReceivedCount
Nathan Harris's avatar
Nathan Harris committed
36
37
38
39
40
        case commandSuccessCount
        case commandFailureCount
        case commandRoundTripTime

        public var description: String {
41
            return "RediStack.\(self.rawValue)"
Nathan Harris's avatar
Nathan Harris committed
42
43
44
        }
    }

45
    /// The wrapped `Metrics.Gauge` maintaining the current number of connections this library has active.
Nathan Harris's avatar
Nathan Harris committed
46
47
48
49
50
    public static var activeConnectionCount = IncrementalGauge(.activeConnectionCount)
    /// The wrapped `Metrics.Gauge` maintaining the current number of subscriptions to channels.
    public static var activeChannelSubscriptions = IncrementalGauge(.activeChannelSubscriptions)
    /// The wrapped `Metrics.Gauge` maintaining the current number of subscriptions to channel patterns.
    public static var activePatternSubscriptions = IncrementalGauge(.activePatternSubscriptions)
Nathan Harris's avatar
Nathan Harris committed
51
52
    /// The `Metrics.Counter` that retains the number of connections made since application startup.
    public static let totalConnectionCount = Counter(label: .totalConnectionCount)
Nathan Harris's avatar
Nathan Harris committed
53
54
    /// The `Metrics.Counter` that retains the number of subscription messages that have been received.
    public static let subscriptionMessagesReceivedCount = Counter(label: .subscriptionMessagesReceivedCount)
Nathan Harris's avatar
Nathan Harris committed
55
56
57
58
59
60
61
62
63
64
65
66
67
    /// The `Metrics.Counter` that retains the number of commands that successfully returned from Redis
    /// since application startup.
    public static let commandSuccessCount = Counter(label: .commandSuccessCount)
    /// The `Metrics.Counter` that retains the number of commands that failed from errors returned
    /// by Redis since application startup.
    public static let commandFailureCount = Counter(label: .commandFailureCount)
    /// The `Metrics.Timer` that receives command response times in nanoseconds from when a command
    /// is first sent through the `NIO.Channel`, to when the response is first resolved.
    public static let commandRoundTripTime = Timer(label: .commandRoundTripTime)

    private init() { }
}

Nathan Harris's avatar
Nathan Harris committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
extension RedisMetrics {
    /// A specialized wrapper class for working with `Metrics.Gauge` objects for the purpose of an incrementing or decrementing count of active objects.
    public class IncrementalGauge {
        private let gauge: Gauge
        private let count = NIOAtomic<Int>.makeAtomic(value: 0)
        
        /// The number of the objects that are currently reported as active.
        public var currentCount: Int { return count.load() }
        
        internal init(_ label: Label) {
            self.gauge = .init(label: label)
        }
        
        /// Increments the current count by the amount specified.
        /// - Parameter amount: The number to increase the current count by. Default is `1`.
        public func increment(by amount: Int = 1) {
            _ = self.count.add(amount)
            self.gauge.record(self.count.load())
        }
        
        /// Decrements the current count by the amount specified.
        /// - Parameter amount: The number to decrease the current count by. Default is `1`.
        public func decrement(by amount: Int = 1) {
            _ = self.count.sub(amount)
            self.gauge.record(self.count.load())
        }
        
        /// Resets the current count to `0`.
        public func reset() {
            _ = self.count.exchange(with: 0)
            self.gauge.record(self.count.load())
        }
100
101
102
    }
}

Nathan Harris's avatar
Nathan Harris committed
103
104
// MARK: SwiftMetrics Convenience

Nathan Harris's avatar
Nathan Harris committed
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
extension Metrics.Counter {
    @inline(__always)
    convenience init(label: RedisMetrics.Label) {
        self.init(label: label.description)
    }
}

extension Metrics.Gauge {
    @inline(__always)
    convenience init(label: RedisMetrics.Label) {
        self.init(label: label.description)
    }
}

extension Metrics.Timer {
    @inline(__always)
    convenience init(label: RedisMetrics.Label) {
        self.init(label: label.description)
    }
}