comparison OrthancFramework/Sources/MetricsRegistry.cpp @ 5337:b376abae664a

Metrics can be stored either as floating-point numbers, or as integers
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 27 Jun 2023 17:55:09 +0200
parents dd9795dc380d
children f41e1f635c4d
comparison
equal deleted inserted replaced
5336:dd9795dc380d 5337:b376abae664a
26 26
27 #include "ChunkedBuffer.h" 27 #include "ChunkedBuffer.h"
28 #include "Compatibility.h" 28 #include "Compatibility.h"
29 #include "OrthancException.h" 29 #include "OrthancException.h"
30 30
31 #include <boost/math/special_functions/round.hpp>
32
31 namespace Orthanc 33 namespace Orthanc
32 { 34 {
33 static const boost::posix_time::ptime GetNow() 35 static const boost::posix_time::ptime GetNow()
34 { 36 {
35 return boost::posix_time::microsec_clock::universal_time(); 37 return boost::posix_time::microsec_clock::universal_time();
36 } 38 }
37 39
40 namespace
41 {
42 template <typename T>
43 class TimestampedValue : public boost::noncopyable
44 {
45 private:
46 boost::posix_time::ptime time_;
47 bool hasValue_;
48 T value_;
49
50 void SetValue(const T& value,
51 const boost::posix_time::ptime& now)
52 {
53 hasValue_ = true;
54 value_ = value;
55 time_ = now;
56 }
57
58 bool IsLargerOverPeriod(const T& value,
59 int duration,
60 const boost::posix_time::ptime& now) const
61 {
62 if (hasValue_)
63 {
64 return (value > value_ ||
65 (now - time_).total_seconds() > duration /* old value has expired */);
66 }
67 else
68 {
69 return true; // No value yet
70 }
71 }
72
73 bool IsSmallerOverPeriod(const T& value,
74 int duration,
75 const boost::posix_time::ptime& now) const
76 {
77 if (hasValue_)
78 {
79 return (value < value_ ||
80 (now - time_).total_seconds() > duration /* old value has expired */);
81 }
82 else
83 {
84 return true; // No value yet
85 }
86 }
87
88 public:
89 explicit TimestampedValue() :
90 hasValue_(false),
91 value_(0)
92 {
93 }
94
95 void Update(const T& value,
96 const MetricsUpdatePolicy& policy)
97 {
98 const boost::posix_time::ptime now = GetNow();
99
100 switch (policy)
101 {
102 case MetricsUpdatePolicy_Directly:
103 SetValue(value, now);
104 break;
105
106 case MetricsUpdatePolicy_MaxOver10Seconds:
107 if (IsLargerOverPeriod(value, 10, now))
108 {
109 SetValue(value, now);
110 }
111 break;
112
113 case MetricsUpdatePolicy_MaxOver1Minute:
114 if (IsLargerOverPeriod(value, 60, now))
115 {
116 SetValue(value, now);
117 }
118 break;
119
120 case MetricsUpdatePolicy_MinOver10Seconds:
121 if (IsSmallerOverPeriod(value, 10, now))
122 {
123 SetValue(value, now);
124 }
125 break;
126
127 case MetricsUpdatePolicy_MinOver1Minute:
128 if (IsSmallerOverPeriod(value, 60, now))
129 {
130 SetValue(value, now);
131 }
132 break;
133
134 default:
135 throw OrthancException(ErrorCode_NotImplemented);
136 }
137 }
138
139 void Increment(const T& delta)
140 {
141 if (hasValue_)
142 {
143 value_ += delta;
144 }
145 else
146 {
147 value_ = delta;
148 }
149 }
150
151 bool HasValue() const
152 {
153 return hasValue_;
154 }
155
156 const boost::posix_time::ptime& GetTime() const
157 {
158 if (hasValue_)
159 {
160 return time_;
161 }
162 else
163 {
164 throw OrthancException(ErrorCode_BadSequenceOfCalls);
165 }
166 }
167
168 const T& GetValue() const
169 {
170 if (hasValue_)
171 {
172 return value_;
173 }
174 else
175 {
176 throw OrthancException(ErrorCode_BadSequenceOfCalls);
177 }
178 }
179 };
180 }
181
182
38 class MetricsRegistry::Item : public boost::noncopyable 183 class MetricsRegistry::Item : public boost::noncopyable
39 { 184 {
40 private: 185 private:
41 MetricsUpdate update_; 186 MetricsUpdatePolicy policy_;
42 boost::posix_time::ptime time_; 187
43 bool hasValue_;
44 int64_t value_;
45
46 void SetValue(int64_t value,
47 const boost::posix_time::ptime& now)
48 {
49 hasValue_ = true;
50 value_ = value;
51 time_ = now;
52 }
53
54 bool IsLargerOverPeriod(int64_t value,
55 int duration,
56 const boost::posix_time::ptime& now) const
57 {
58 if (hasValue_)
59 {
60 return (value > value_ ||
61 (now - time_).total_seconds() > duration /* old value has expired */);
62 }
63 else
64 {
65 return true; // No value yet
66 }
67 }
68
69 bool IsSmallerOverPeriod(int64_t value,
70 int duration,
71 const boost::posix_time::ptime& now) const
72 {
73 if (hasValue_)
74 {
75 return (value < value_ ||
76 (now - time_).total_seconds() > duration /* old value has expired */);
77 }
78 else
79 {
80 return true; // No value yet
81 }
82 }
83
84 public: 188 public:
85 explicit Item(MetricsUpdate update) : 189 Item(MetricsUpdatePolicy policy) :
86 update_(update), 190 policy_(policy)
87 hasValue_(false), 191 {
88 value_(0) 192 }
89 { 193
90 } 194 virtual ~Item()
91 195 {
92 MetricsUpdate GetUpdate() const 196 }
93 { 197
94 return update_; 198 MetricsUpdatePolicy GetPolicy() const
95 } 199 {
96 200 return policy_;
97 void Update(int64_t value) 201 }
98 { 202
99 const boost::posix_time::ptime now = GetNow(); 203 virtual void UpdateFloat(float value) = 0;
100 204
101 switch (update_) 205 virtual void UpdateInteger(int64_t value) = 0;
102 { 206
103 case MetricsUpdate_Directly: 207 virtual void IncrementInteger(int64_t delta) = 0;
104 SetValue(value, now); 208
105 break; 209 virtual MetricsDataType GetDataType() const = 0;
106 210
107 case MetricsUpdate_MaxOver10Seconds: 211 virtual bool HasValue() const = 0;
108 if (IsLargerOverPeriod(value, 10, now)) 212
109 { 213 virtual const boost::posix_time::ptime& GetTime() const = 0;
110 SetValue(value, now); 214
111 } 215 virtual std::string FormatValue() const = 0;
112 break; 216 };
113 217
114 case MetricsUpdate_MaxOver1Minute: 218
115 if (IsLargerOverPeriod(value, 60, now)) 219 class MetricsRegistry::FloatItem : public Item
116 { 220 {
117 SetValue(value, now); 221 private:
118 } 222 TimestampedValue<float> value_;
119 break; 223
120 224 public:
121 case MetricsUpdate_MinOver10Seconds: 225 FloatItem(MetricsUpdatePolicy policy) :
122 if (IsSmallerOverPeriod(value, 10, now)) 226 Item(policy)
123 { 227 {
124 SetValue(value, now); 228 }
125 } 229
126 break; 230 virtual void UpdateFloat(float value) ORTHANC_OVERRIDE
127 231 {
128 case MetricsUpdate_MinOver1Minute: 232 value_.Update(value, GetPolicy());
129 if (IsSmallerOverPeriod(value, 60, now)) 233 }
130 { 234
131 SetValue(value, now); 235 virtual void UpdateInteger(int64_t value) ORTHANC_OVERRIDE
132 } 236 {
133 break; 237 value_.Update(static_cast<float>(value), GetPolicy());
134 238 }
135 default: 239
136 throw OrthancException(ErrorCode_NotImplemented); 240 virtual void IncrementInteger(int64_t delta) ORTHANC_OVERRIDE
137 } 241 {
138 } 242 value_.Increment(static_cast<float>(delta));
139 243 }
140 bool HasValue() const 244
141 { 245 virtual MetricsDataType GetDataType() const ORTHANC_OVERRIDE
142 return hasValue_; 246 {
143 } 247 return MetricsDataType_Float;
144 248 }
145 const boost::posix_time::ptime& GetTime() const 249
146 { 250 virtual bool HasValue() const ORTHANC_OVERRIDE
147 if (hasValue_) 251 {
148 { 252 return value_.HasValue();
149 return time_; 253 }
150 } 254
151 else 255 virtual const boost::posix_time::ptime& GetTime() const ORTHANC_OVERRIDE
152 { 256 {
153 throw OrthancException(ErrorCode_BadSequenceOfCalls); 257 return value_.GetTime();
154 } 258 }
155 } 259
156 260 virtual std::string FormatValue() const ORTHANC_OVERRIDE
157 int64_t GetValue() const 261 {
158 { 262 return boost::lexical_cast<std::string>(value_.GetValue());
159 if (hasValue_) 263 }
160 { 264 };
161 return value_; 265
162 } 266
163 else 267 class MetricsRegistry::IntegerItem : public Item
164 { 268 {
165 throw OrthancException(ErrorCode_BadSequenceOfCalls); 269 private:
166 } 270 TimestampedValue<int64_t> value_;
271
272 public:
273 IntegerItem(MetricsUpdatePolicy policy) :
274 Item(policy)
275 {
276 }
277
278 virtual void UpdateFloat(float value) ORTHANC_OVERRIDE
279 {
280 value_.Update(boost::math::llround(value), GetPolicy());
281 }
282
283 virtual void UpdateInteger(int64_t value) ORTHANC_OVERRIDE
284 {
285 value_.Update(value, GetPolicy());
286 }
287
288 virtual void IncrementInteger(int64_t delta) ORTHANC_OVERRIDE
289 {
290 value_.Increment(delta);
291 }
292
293 virtual MetricsDataType GetDataType() const ORTHANC_OVERRIDE
294 {
295 return MetricsDataType_Integer;
296 }
297
298 virtual bool HasValue() const ORTHANC_OVERRIDE
299 {
300 return value_.HasValue();
301 }
302
303 virtual const boost::posix_time::ptime& GetTime() const ORTHANC_OVERRIDE
304 {
305 return value_.GetTime();
306 }
307
308 virtual std::string FormatValue() const ORTHANC_OVERRIDE
309 {
310 return boost::lexical_cast<std::string>(value_.GetValue());
167 } 311 }
168 }; 312 };
169 313
170 314
171 MetricsRegistry::~MetricsRegistry() 315 MetricsRegistry::~MetricsRegistry()
189 enabled_ = enabled; 333 enabled_ = enabled;
190 } 334 }
191 335
192 336
193 void MetricsRegistry::Register(const std::string& name, 337 void MetricsRegistry::Register(const std::string& name,
194 MetricsUpdate update) 338 MetricsUpdatePolicy policy,
339 MetricsDataType type)
195 { 340 {
196 boost::mutex::scoped_lock lock(mutex_); 341 boost::mutex::scoped_lock lock(mutex_);
197 342
343 if (content_.find(name) != content_.end())
344 {
345 throw OrthancException(ErrorCode_BadSequenceOfCalls, "Cannot register twice the same metrics: " + name);
346 }
347 else
348 {
349 GetItemInternal(name, policy, type);
350 }
351 }
352
353
354 MetricsRegistry::Item& MetricsRegistry::GetItemInternal(const std::string& name,
355 MetricsUpdatePolicy policy,
356 MetricsDataType type)
357 {
198 Content::iterator found = content_.find(name); 358 Content::iterator found = content_.find(name);
199 359
200 if (found == content_.end()) 360 if (found == content_.end())
201 { 361 {
202 content_[name] = new Item(update); 362 Item* item = NULL;
203 } 363
204 else 364 switch (type)
205 { 365 {
206 assert(found->second != NULL); 366 case MetricsDataType_Float:
207 367 item = new FloatItem(policy);
208 // This metrics already exists: Only recreate it if there is a 368 break;
209 // mismatch in the type of metrics 369
210 if (found->second->GetUpdate() != update) 370 case MetricsDataType_Integer:
211 { 371 item = new IntegerItem(policy);
212 delete found->second; 372 break;
213 found->second = new Item(update); 373
214 } 374 default:
215 } 375 throw OrthancException(ErrorCode_ParameterOutOfRange);
216 } 376 }
217 377
218
219 MetricsRegistry::Item& MetricsRegistry::GetItemInternal(const std::string& name,
220 MetricsUpdate update)
221 {
222 Content::iterator found = content_.find(name);
223
224 if (found == content_.end())
225 {
226 Item* item = new Item(update);
227 content_[name] = item; 378 content_[name] = item;
228 return *item; 379 return *item;
229 } 380 }
230 else 381 else
231 { 382 {
238 enabled_(true) 389 enabled_(true)
239 { 390 {
240 } 391 }
241 392
242 393
243 void MetricsRegistry::SetValue(const std::string &name, 394 void MetricsRegistry::SetFloatValue(const std::string& name,
244 int64_t value, 395 float value,
245 MetricsUpdate update) 396 MetricsUpdatePolicy policy)
246 { 397 {
247 // Inlining to avoid loosing time if metrics are disabled 398 // Inlining to avoid loosing time if metrics are disabled
248 if (enabled_) 399 if (enabled_)
249 { 400 {
250 boost::mutex::scoped_lock lock(mutex_); 401 boost::mutex::scoped_lock lock(mutex_);
251 GetItemInternal(name, update).Update(value); 402 GetItemInternal(name, policy, MetricsDataType_Float).UpdateFloat(value);
252 } 403 }
253 } 404 }
254 405
255 406
256 void MetricsRegistry::IncrementValue(const std::string &name, 407 void MetricsRegistry::SetIntegerValue(const std::string &name,
257 int64_t delta) 408 int64_t value,
409 MetricsUpdatePolicy policy)
258 { 410 {
259 // Inlining to avoid loosing time if metrics are disabled 411 // Inlining to avoid loosing time if metrics are disabled
260 if (enabled_) 412 if (enabled_)
261 { 413 {
262 boost::mutex::scoped_lock lock(mutex_); 414 boost::mutex::scoped_lock lock(mutex_);
263 Item& item = GetItemInternal(name, MetricsUpdate_Directly); 415 GetItemInternal(name, policy, MetricsDataType_Integer).UpdateInteger(value);
264 416 }
265 if (item.HasValue()) 417 }
266 { 418
267 item.Update(item.GetValue() + delta); 419
268 } 420 void MetricsRegistry::IncrementIntegerValue(const std::string &name,
269 else 421 int64_t delta)
270 { 422 {
271 item.Update(delta); 423 // Inlining to avoid loosing time if metrics are disabled
272 } 424 if (enabled_)
273 } 425 {
274 } 426 boost::mutex::scoped_lock lock(mutex_);
275 427 GetItemInternal(name, MetricsUpdatePolicy_Directly, MetricsDataType_Integer).IncrementInteger(delta);
276 428 }
277 MetricsUpdate MetricsRegistry::GetMetricsUpdate(const std::string& name) 429 }
430
431
432 MetricsUpdatePolicy MetricsRegistry::GetUpdatePolicy(const std::string& metrics)
278 { 433 {
279 boost::mutex::scoped_lock lock(mutex_); 434 boost::mutex::scoped_lock lock(mutex_);
280 435
281 Content::const_iterator found = content_.find(name); 436 Content::const_iterator found = content_.find(metrics);
282 437
283 if (found == content_.end()) 438 if (found == content_.end())
284 { 439 {
285 throw OrthancException(ErrorCode_InexistentItem); 440 throw OrthancException(ErrorCode_InexistentItem);
286 } 441 }
287 else 442 else
288 { 443 {
289 assert(found->second != NULL); 444 assert(found->second != NULL);
290 return found->second->GetUpdate(); 445 return found->second->GetPolicy();
446 }
447 }
448
449
450 MetricsDataType MetricsRegistry::GetDataType(const std::string& metrics)
451 {
452 boost::mutex::scoped_lock lock(mutex_);
453
454 Content::const_iterator found = content_.find(metrics);
455
456 if (found == content_.end())
457 {
458 throw OrthancException(ErrorCode_InexistentItem);
459 }
460 else
461 {
462 assert(found->second != NULL);
463 return found->second->GetDataType();
291 } 464 }
292 } 465 }
293 466
294 467
295 void MetricsRegistry::ExportPrometheusText(std::string& s) 468 void MetricsRegistry::ExportPrometheusText(std::string& s)
316 if (it->second->HasValue()) 489 if (it->second->HasValue())
317 { 490 {
318 boost::posix_time::time_duration diff = it->second->GetTime() - EPOCH; 491 boost::posix_time::time_duration diff = it->second->GetTime() - EPOCH;
319 492
320 std::string line = (it->first + " " + 493 std::string line = (it->first + " " +
321 boost::lexical_cast<std::string>(it->second->GetValue()) + " " + 494 it->second->FormatValue() + " " +
322 boost::lexical_cast<std::string>(diff.total_milliseconds()) + "\n"); 495 boost::lexical_cast<std::string>(diff.total_milliseconds()) + "\n");
323 496
324 buffer.AddChunk(line); 497 buffer.AddChunk(line);
325 } 498 }
326 } 499 }
329 } 502 }
330 503
331 504
332 MetricsRegistry::SharedMetrics::SharedMetrics(MetricsRegistry &registry, 505 MetricsRegistry::SharedMetrics::SharedMetrics(MetricsRegistry &registry,
333 const std::string &name, 506 const std::string &name,
334 MetricsUpdate update) : 507 MetricsUpdatePolicy policy) :
335 registry_(registry), 508 registry_(registry),
336 name_(name), 509 name_(name),
337 value_(0) 510 value_(0)
338 { 511 {
339 } 512 }
340 513
341 void MetricsRegistry::SharedMetrics::Add(int64_t delta) 514 void MetricsRegistry::SharedMetrics::Add(int64_t delta)
342 { 515 {
343 boost::mutex::scoped_lock lock(mutex_); 516 boost::mutex::scoped_lock lock(mutex_);
344 value_ += delta; 517 value_ += delta;
345 registry_.SetValue(name_, value_); 518 registry_.SetIntegerValue(name_, value_);
346 } 519 }
347 520
348 521
349 MetricsRegistry::ActiveCounter::ActiveCounter(MetricsRegistry::SharedMetrics &metrics) : 522 MetricsRegistry::ActiveCounter::ActiveCounter(MetricsRegistry::SharedMetrics &metrics) :
350 metrics_(metrics) 523 metrics_(metrics)
374 547
375 MetricsRegistry::Timer::Timer(MetricsRegistry &registry, 548 MetricsRegistry::Timer::Timer(MetricsRegistry &registry,
376 const std::string &name) : 549 const std::string &name) :
377 registry_(registry), 550 registry_(registry),
378 name_(name), 551 name_(name),
379 update_(MetricsUpdate_MaxOver10Seconds) 552 policy_(MetricsUpdatePolicy_MaxOver10Seconds)
380 { 553 {
381 Start(); 554 Start();
382 } 555 }
383 556
384 557
385 MetricsRegistry::Timer::Timer(MetricsRegistry &registry, 558 MetricsRegistry::Timer::Timer(MetricsRegistry &registry,
386 const std::string &name, 559 const std::string &name,
387 MetricsUpdate update) : 560 MetricsUpdatePolicy policy) :
388 registry_(registry), 561 registry_(registry),
389 name_(name), 562 name_(name),
390 update_(update) 563 policy_(policy)
391 { 564 {
392 Start(); 565 Start();
393 } 566 }
394 567
395 568
396 MetricsRegistry::Timer::~Timer() 569 MetricsRegistry::Timer::~Timer()
397 { 570 {
398 if (active_) 571 if (active_)
399 { 572 {
400 boost::posix_time::time_duration diff = GetNow() - start_; 573 boost::posix_time::time_duration diff = GetNow() - start_;
401 registry_.SetValue( 574 registry_.SetIntegerValue(name_, static_cast<int64_t>(diff.total_milliseconds()), policy_);
402 name_, static_cast<int64_t>(diff.total_milliseconds()), update_);
403 } 575 }
404 } 576 }
405 } 577 }