comparison Core/MetricsRegistry.cpp @ 3174:8ea7c4546c3a

primitives to collect metrics in Orthanc
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Jan 2019 15:15:48 +0100
parents
children 574890d14c92
comparison
equal deleted inserted replaced
3173:096f4a29f223 3174:8ea7c4546c3a
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "PrecompiledHeaders.h"
35 #include "MetricsRegistry.h"
36
37 #include "OrthancException.h"
38 #include "ChunkedBuffer.h"
39
40 namespace Orthanc
41 {
42 static const boost::posix_time::ptime GetNow()
43 {
44 return boost::posix_time::second_clock::universal_time();
45 }
46
47
48 class MetricsRegistry::Item
49 {
50 private:
51 MetricsType type_;
52 boost::posix_time::ptime time_;
53 bool hasValue_;
54 float value_;
55
56 void Touch(float value,
57 const boost::posix_time::ptime& now)
58 {
59 hasValue_ = true;
60 value_ = value;
61 time_ = now;
62 }
63
64 void Touch(float value)
65 {
66 Touch(value, GetNow());
67 }
68
69 void UpdateMax(float value,
70 int duration)
71 {
72 if (hasValue_)
73 {
74 const boost::posix_time::ptime now = GetNow();
75
76 if (value > value_ ||
77 (now - time_).total_seconds() > duration)
78 {
79 Touch(value, now);
80 }
81 }
82 else
83 {
84 Touch(value);
85 }
86 }
87
88 void UpdateMin(float value,
89 int duration)
90 {
91 if (hasValue_)
92 {
93 const boost::posix_time::ptime now = GetNow();
94
95 if (value < value_ ||
96 (now - time_).total_seconds() > duration)
97 {
98 Touch(value, now);
99 }
100 }
101 else
102 {
103 Touch(value);
104 }
105 }
106
107 public:
108 Item(MetricsType type) :
109 type_(type),
110 hasValue_(false)
111 {
112 }
113
114 MetricsType GetType() const
115 {
116 return type_;
117 }
118
119 void Update(float value)
120 {
121 switch (type_)
122 {
123 case MetricsType_Default:
124 Touch(value);
125 break;
126
127 case MetricsType_MaxOver10Seconds:
128 UpdateMax(value, 10);
129 break;
130
131 case MetricsType_MaxOver1Minute:
132 UpdateMax(value, 60);
133 break;
134
135 case MetricsType_MinOver10Seconds:
136 UpdateMin(value, 10);
137 break;
138
139 case MetricsType_MinOver1Minute:
140 UpdateMin(value, 60);
141 break;
142
143 default:
144 throw OrthancException(ErrorCode_NotImplemented);
145 }
146 }
147
148 bool HasValue() const
149 {
150 return hasValue_;
151 }
152
153 const boost::posix_time::ptime& GetTime() const
154 {
155 if (hasValue_)
156 {
157 return time_;
158 }
159 else
160 {
161 throw OrthancException(ErrorCode_BadSequenceOfCalls);
162 }
163 }
164
165 float GetValue() const
166 {
167 if (hasValue_)
168 {
169 return value_;
170 }
171 else
172 {
173 throw OrthancException(ErrorCode_BadSequenceOfCalls);
174 }
175 }
176 };
177
178
179 MetricsRegistry::~MetricsRegistry()
180 {
181 for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
182 {
183 assert(it->second != NULL);
184 delete it->second;
185 }
186 }
187
188
189 void MetricsRegistry::SetEnabled(bool enabled)
190 {
191 boost::mutex::scoped_lock lock(mutex_);
192 enabled_ = enabled;
193 }
194
195
196 void MetricsRegistry::Register(const std::string& name,
197 MetricsType type)
198 {
199 boost::mutex::scoped_lock lock(mutex_);
200
201 Content::iterator found = content_.find(name);
202
203 if (found == content_.end())
204 {
205 content_[name] = new Item(type);
206 }
207 else
208 {
209 assert(found->second != NULL);
210
211 // This metrics already exists: Only recreate it if there is a
212 // mismatch in the type of metrics
213 if (found->second->GetType() != type)
214 {
215 delete found->second;
216 found->second = new Item(type);
217 }
218 }
219 }
220
221
222 void MetricsRegistry::SetValueInternal(const std::string& name,
223 float value,
224 MetricsType type)
225 {
226 boost::mutex::scoped_lock lock(mutex_);
227
228 Content::iterator found = content_.find(name);
229
230 if (found == content_.end())
231 {
232 std::auto_ptr<Item> item(new Item(type));
233 item->Update(value);
234 content_[name] = item.release();
235 }
236 else
237 {
238 assert(found->second != NULL);
239 found->second->Update(value);
240 }
241 }
242
243
244 MetricsType MetricsRegistry::GetMetricsType(const std::string& name)
245 {
246 boost::mutex::scoped_lock lock(mutex_);
247
248 Content::const_iterator found = content_.find(name);
249
250 if (found == content_.end())
251 {
252 throw OrthancException(ErrorCode_InexistentItem);
253 }
254 else
255 {
256 assert(found->second != NULL);
257 return found->second->GetType();
258 }
259 }
260
261
262 void MetricsRegistry::ExportPrometheusText(std::string& s)
263 {
264 // https://www.boost.org/doc/libs/1_69_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch
265 static const boost::posix_time::ptime EPOCH(boost::gregorian::date(1970, 1, 1));
266
267 boost::mutex::scoped_lock lock(mutex_);
268
269 s.clear();
270
271 if (!enabled_)
272 {
273 return;
274 }
275
276 ChunkedBuffer buffer;
277
278 for (Content::const_iterator it = content_.begin();
279 it != content_.end(); ++it)
280 {
281 assert(it->second != NULL);
282
283 if (it->second->HasValue())
284 {
285 boost::posix_time::time_duration diff = it->second->GetTime() - EPOCH;
286
287 std::string line = (it->first + " " +
288 boost::lexical_cast<std::string>(it->second->GetValue()) + " " +
289 boost::lexical_cast<std::string>(diff.total_milliseconds()) + "\n");
290
291 buffer.AddChunk(line);
292 }
293 }
294
295 buffer.Flatten(s);
296 }
297
298
299 void MetricsRegistry::Timer::Start()
300 {
301 if (registry_.IsEnabled())
302 {
303 active_ = true;
304 start_ = GetNow();
305 }
306 else
307 {
308 active_ = false;
309 }
310 }
311
312
313 MetricsRegistry::Timer::~Timer()
314 {
315 if (active_)
316 {
317 boost::posix_time::time_duration diff = GetNow() - start_;
318 registry_.SetValue(name_, diff.total_milliseconds(), type_);
319 }
320 }
321 }