1
1
""" Functions for define and register metrics """
2
2
import time
3
- import threading
4
- from flask import request
5
- from prometheus_client import Counter , Histogram , Gauge
3
+ import atexit
4
+ from flask import request , current_app
5
+ from prometheus_client import Counter , Histogram , Gauge , CollectorRegistry
6
+ from apscheduler .schedulers .background import BackgroundScheduler
6
7
7
8
#
8
9
# Request callbacks
9
10
#
10
-
11
- METRICS_INFO = Gauge (
12
- "application_info" ,
13
- "records static application info such as it's semantic version number" ,
14
- ["version" ]
15
- )
16
-
17
- DEPENDENCY_UP = Gauge (
18
- 'dependency_up' ,
19
- 'records if a dependency is up or down. 1 for up, 0 for down' ,
20
- ["name" ]
21
- )
22
-
23
- def is_error (code ):
11
+ def _is_error_ (code ):
24
12
"""
25
13
Default status error checking
26
14
"""
@@ -30,7 +18,7 @@ def is_error(code):
30
18
#
31
19
# Metrics registration
32
20
#
33
- def register_metrics (app , buckets = None , error_fn = None ):
21
+ def register_metrics (app = current_app , buckets = None , error_fn = None , registry = None ):
34
22
"""
35
23
Register metrics middlewares
36
24
@@ -42,24 +30,46 @@ def register_metrics(app, buckets=None, error_fn=None):
42
30
Before CPython 3.6 dictionaries didn't guarantee keys order, so callbacks
43
31
could be executed in arbitrary order.
44
32
"""
33
+
34
+ if app .config .get ("METRICS_ENABLED" , False ):
35
+ return app , app .extensions .get ("registry" , registry )
36
+ app .config ["METRICS_ENABLED" ] = True
37
+ if not registry :
38
+ registry = app .extensions .get ("registry" , CollectorRegistry ())
39
+ app .extensions ["registry" ] = registry
40
+ app .logger .info ('Metrics enabled' )
41
+
45
42
buckets = [0.1 , 0.3 , 1.5 , 10.5 ] if buckets is None else buckets
43
+
44
+
46
45
# pylint: disable=invalid-name
47
- METRICS_REQUEST_LATENCY = Histogram (
46
+ metrics_info = Gauge (
47
+ "application_info" ,
48
+ "records static application info such as it's semantic version number" ,
49
+ ["version" , "name" ],
50
+ registry = registry
51
+ )
52
+
53
+ # pylint: disable=invalid-name
54
+ metrics_request_latency = Histogram (
48
55
"request_seconds" ,
49
56
"records in a histogram the number of http requests and their duration in seconds" ,
50
- ["type" , "status" , "isError" , "method" , "addr" ],
51
- buckets = buckets
57
+ ["type" , "status" , "isError" , "errorMessage" , "method" , "addr" ],
58
+ buckets = buckets ,
59
+ registry = registry
52
60
)
53
61
54
- METRICS_REQUEST_SIZE = Counter (
62
+ # pylint: disable=invalid-name
63
+ metrics_request_size = Counter (
55
64
"response_size_bytes" ,
56
65
"counts the size of each http response" ,
57
- ["type" , "status" , "isError" , "method" , "addr" ],
66
+ ["type" , "status" , "isError" , "errorMessage" , "method" , "addr" ],
67
+ registry = registry
58
68
)
59
69
# pylint: enable=invalid-name
60
70
61
71
app_version = app .config .get ("APP_VERSION" , "0.0.0" )
62
- METRICS_INFO .labels (app_version ).set (1 )
72
+ metrics_info .labels (app_version , app . name ).set (1 )
63
73
64
74
def before_request ():
65
75
"""
@@ -77,29 +87,91 @@ def after_request(response):
77
87
# pylint: disable=protected-access
78
88
request_latency = time .time () - request ._prometheus_metrics_request_start_time
79
89
# pylint: enable=protected-access
80
- error_status = is_error (response .status_code )
81
- METRICS_REQUEST_LATENCY \
82
- .labels ("http" , response .status_code , error_status , request .method , request .path ) \
90
+ error_status = _is_error_ (response .status_code )
91
+ metrics_request_latency \
92
+ .labels ("http" , response .status_code , error_status , "" , request .method , request .path ) \
83
93
.observe (request_latency )
84
- METRICS_REQUEST_SIZE .labels (
85
- "http" , response .status_code , error_status , request .method , request .path
94
+ metrics_request_size .labels (
95
+ "http" , response .status_code , error_status , "" , request .method , request .path
86
96
).inc (size_request )
87
97
return response
98
+
88
99
if error_fn is not None :
89
- is_error .__code__ = error_fn .__code__
100
+ _is_error_ .__code__ = error_fn .__code__
90
101
app .before_request (before_request )
91
102
app .after_request (after_request )
103
+ return app , registry
104
+
92
105
93
- def watch_dependencies (dependency , func , time_execution = 1500 ):
106
+ def watch_dependencies (dependency , func , time_execution = 15000 , registry = None , app = current_app ):
107
+ """
108
+ Register dependencies metrics up
109
+ """
110
+
111
+ if not registry :
112
+ registry = app .extensions .get ("registry" , CollectorRegistry ())
113
+ app .extensions ["registry" ] = registry
114
+
115
+ # pylint: disable=invalid-name
116
+ DEPENDENCY_UP = Gauge (
117
+ 'dependency_up' ,
118
+ 'records if a dependency is up or down. 1 for up, 0 for down' ,
119
+ ["name" ],
120
+ registry = registry
121
+ )
122
+ def register_dependecy ():
123
+ DEPENDENCY_UP .labels (dependency ).set (func ())
124
+
125
+ scheduler = BackgroundScheduler ()
126
+ scheduler .add_job (
127
+ func = register_dependecy ,
128
+ trigger = "interval" ,
129
+ seconds = time_execution / 1000 ,
130
+ max_instances = 1 ,
131
+ name = 'dependency' ,
132
+ misfire_grace_time = 2 ,
133
+ replace_existing = True
134
+ )
135
+ scheduler .start ()
136
+
137
+ # Shut down the scheduler when exiting the app
138
+ atexit .register (scheduler .shutdown )
139
+ return scheduler
140
+
141
+ # pylint: disable=too-many-arguments
142
+ def collect_dependency_time (
143
+ app , name , rtype = 'http' , status = 200 ,
144
+ is_error = False , error_message = '' ,
145
+ method = 'GET' , addr = '/' ,
146
+ elapsed = 0 ,
147
+ registry = None
148
+ ):
94
149
"""
95
150
Register dependencies metrics
96
151
"""
97
- def thread_function ():
98
- thread = threading .Timer (time_execution , lambda x : x + 1 , args = (1 ,))
99
- thread .start ()
100
- thread .join ()
101
- response = func ()
102
- DEPENDENCY_UP .labels (dependency ).set (response )
103
- thread_function ()
104
- thread = threading .Timer (time_execution , thread_function )
105
- thread .start ()
152
+
153
+ if not registry :
154
+ registry = app .extensions .get ("registry" , CollectorRegistry ())
155
+ app .extensions ["registry" ] = registry
156
+
157
+ dependency_up_latency = app .extensions .get (
158
+ "dependency_latency"
159
+ )
160
+ if not dependency_up_latency :
161
+ app .extensions ['dependency_latency' ] = dependency_up_latency = Histogram (
162
+ "dependency_request_seconds" ,
163
+ "records in a histogram the number of requests to dependency" ,
164
+ ["name" , "type" , "status" , "isError" , "errorMessage" , "method" , "addr" ],
165
+ registry = registry
166
+ )
167
+
168
+ dependency_up_latency \
169
+ .labels (
170
+ name ,
171
+ rtype .lower (),
172
+ status ,
173
+ "False" if is_error else "True" ,
174
+ error_message ,
175
+ method .upper (),
176
+ addr ) \
177
+ .observe (elapsed )
0 commit comments