Test Failed
Push — master ( e380d0...f5671d )
by W
02:58
created

st2common/st2common/metrics/base.py (1 issue)

1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
from __future__ import absolute_import
17
18
from functools import wraps
19
import logging
20
21
from oslo_config import cfg
22
from oslo_config.cfg import NoSuchOptError
23
from stevedore.exception import NoMatches, MultipleMatches
24
25
from st2common.metrics.utils import check_key
26
from st2common.util.loader import get_plugin_instance
27
from st2common.util.date import get_datetime_utc_now
28
from st2common.exceptions.plugins import PluginLoadError
29
30
__all__ = [
31
    'BaseMetricsDriver',
32
33
    'Timer',
34
    'Counter',
35
    'CounterWithTimer',
36
37
    'metrics_initialize',
38
    'get_driver'
39
]
40
41
if not hasattr(cfg.CONF, 'metrics'):
42
    from st2common.config import register_opts
43
    register_opts()
44
45
LOG = logging.getLogger(__name__)
46
47
PLUGIN_NAMESPACE = 'st2common.metrics.driver'
48
49
# Stores reference to the metrics driver class instance.
50
# NOTE: This value is populated lazily on the first get_driver() function call
51
METRICS = None
52
53
54
class BaseMetricsDriver(object):
55
    """
56
    Base class for driver implementations for metric collection
57
    """
58
59
    def time(self, key, time):
60
        """
61
        Timer metric
62
        """
63
        pass
64
65
    def inc_counter(self, key, amount=1):
66
        """
67
        Increment counter
68
        """
69
        pass
70
71
    def dec_counter(self, key, amount=1):
72
        """
73
        Decrement metric
74
        """
75
        pass
76
77
    def set_gauge(self, key, value):
78
        """
79
        Set gauge value.
80
        """
81
        pass
82
83
    def inc_gauge(self, key, amount=1):
84
        """
85
        Increment gauge value.
86
        """
87
        pass
88
89
    def dec_gauge(self, key, amount=1):
90
        """
91
        Decrement gauge value.
92
        """
93
        pass
94
95
96
class Timer(object):
97
    """
98
    Timer context manager for easily sending timer statistics.
99
    """
100
    def __init__(self, key, include_parameter=False):
101
        check_key(key)
102
103
        self.key = key
104
        self._metrics = get_driver()
105
        self._include_parameter = include_parameter
106
        self._start_time = None
107
108
    def send_time(self, key=None):
109
        """
110
        Send current time from start time.
111
        """
112
        time_delta = self.get_time_delta()
113
114
        if key:
115
            check_key(key)
116
            self._metrics.time(key, time_delta.total_seconds())
117
        else:
118
            self._metrics.time(self.key, time_delta.total_seconds())
119
120
    def get_time_delta(self):
121
        """
122
        Get current time delta.
123
        """
124
125
        return get_datetime_utc_now() - self._start_time
126
127
    def __enter__(self):
128
        self._start_time = get_datetime_utc_now()
129
        return self
130
131
    def __exit__(self, *args):
132
        self.send_time()
133
134
    def __call__(self, func):
135
        @wraps(func)
136
        def wrapper(*args, **kw):
137
            with self as metrics_timer:
138
                if self._include_parameter:
139
                    kw['metrics_timer'] = metrics_timer
140
                return func(*args, metrics_timer=metrics_timer, **kw)
141
        return wrapper
142
143
144
class Counter(object):
145
    """
146
    Counter context manager for easily sending counter statistics.
147
    """
148
    def __init__(self, key):
149
        check_key(key)
150
        self.key = key
151
        self._metrics = get_driver()
152
153
    def __enter__(self):
154
        self._metrics.inc_counter(self.key)
155
        return self
156
157
    def __exit__(self, *args):
158
        pass
159
160
    def __call__(self, func):
161
        @wraps(func)
162
        def wrapper(*args, **kw):
163
            with self:
164
                return func(*args, **kw)
165
        return wrapper
166
167
168
class CounterWithTimer(object):
169
    """
170
    Timer and counter context manager for easily sending counter statistics
171
    with builtin timer.
172
    """
173
174
    def __init__(self, key, include_parameter=False):
175
        check_key(key)
176
        self.key = key
177
        self._metrics = get_driver()
178
        self._include_parameter = include_parameter
179
        self._start_time = None
180
181
    def send_time(self, key=None):
182
        """
183
        Send current time from start time.
184
        """
185
        time_delta = self.get_time_delta()
186
187
        if key:
188
            check_key(key)
189
            self._metrics.time(key, time_delta.total_seconds())
190
        else:
191
            self._metrics.time(self.key, time_delta.total_seconds())
192
193
    def get_time_delta(self):
194
        """
195
        Get current time delta.
196
        """
197
        return get_datetime_utc_now() - self._start_time
198
199
    def __enter__(self):
200
        self._metrics.inc_counter(self.key)
201
        self._start_time = get_datetime_utc_now()
202
        return self
203
204
    def __exit__(self, *args):
205
        self.send_time()
206
207
    def __call__(self, func):
208
        @wraps(func)
209
        def wrapper(*args, **kw):
210
            with self as counter_with_timer:
211
                if self._include_parameter:
212
                    kw['metrics_counter_with_timer'] = counter_with_timer
213
                return func(*args, **kw)
214
        return wrapper
215
216
217
def metrics_initialize():
218
    """
219
    Initialize metrics constant
220
    """
221
    global METRICS
0 ignored issues
show
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
222
223
    try:
224
        METRICS = get_plugin_instance(PLUGIN_NAMESPACE, cfg.CONF.metrics.driver)
225
    except (NoMatches, MultipleMatches, NoSuchOptError) as error:
226
        raise PluginLoadError('Error loading metrics driver. Check configuration: %s', error)
227
228
    return METRICS
229
230
231
def get_driver():
232
    """
233
    Return metrics driver instance
234
    """
235
    if not METRICS:
236
        return metrics_initialize()
237
238
    return METRICS
239