Mercurial > hg > orthanc
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 ®istry, | 505 MetricsRegistry::SharedMetrics::SharedMetrics(MetricsRegistry ®istry, |
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 ®istry, | 548 MetricsRegistry::Timer::Timer(MetricsRegistry ®istry, |
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 ®istry, | 558 MetricsRegistry::Timer::Timer(MetricsRegistry ®istry, |
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 } |