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 &registry, 509 MetricsRegistry::SharedMetrics::SharedMetrics(MetricsRegistry &registry,
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 &registry, 552 MetricsRegistry::Timer::Timer(MetricsRegistry &registry,
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 &registry, 562 MetricsRegistry::Timer::Timer(MetricsRegistry &registry,
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 }