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