artdaq_utilities  v1_05_11a
MetricPlugin.hh
1 // MetricPlugin.hh: Metric Plugin Interface
2 // Author: Eric Flumerfelt
3 // Last Modified: 11/05/2014 (Created)
4 //
5 // Defines the interface that any ARTDAQ metric plugin must implement
6 
7 #ifndef __METRIC_INTERFACE__
8 #define __METRIC_INTERFACE__
9 
10 #ifdef TRACE_NAME
11 #pragma push_macro("TRACE_NAME")
12 #undef TRACE_NAME
13 #define TRACE_NAME "MetricPlugin" /* a simple const char * */
14 #define TRACE_NAME_POP 1
15 #endif
16 #include "TRACE/trace.h" // TLOG(x,name)
17 
18 #include <chrono>
19 #include <string>
20 #include <unordered_map>
21 #include "fhiclcpp/ParameterSet.h"
22 #include "fhiclcpp/types/Atom.h"
23 #include "fhiclcpp/types/ConfigurationTable.h"
24 #include "fhiclcpp/types/Sequence.h"
25 
26 #include "artdaq-utilities/Plugins/MetricData.hh"
27 #include "cetlib/compiler_macros.h"
28 #ifndef FALLTHROUGH
29 #define FALLTHROUGH while (0)
30 #endif
31 
32 namespace artdaq {
38 {
39 public:
43  struct Config
44  {
46  fhicl::Atom<std::string> metricPluginType{fhicl::Name{"metricPluginType"}, fhicl::Comment{"The name of the metric plugin to load (may have additional configuration parameters"}};
48  fhicl::Atom<size_t> level{fhicl::Name{"level"}, fhicl::Comment{"The verbosity level threshold for this plugin. sendMetric calls with verbosity level greater than this will not be sent to the plugin. OPTIONAL"}, 0};
50  fhicl::Sequence<size_t> metric_levels{fhicl::Name{"metric_levels"}, fhicl::Comment{"A list of levels that should be enabled for this plugin. OPTIONAL"}, std::vector<size_t>()};
52  fhicl::Atom<std::string> level_string{fhicl::Name{"level_string"}, fhicl::Comment{"A string containing a comma-separated list of levels to enable. Ranges are supported. Example: \"1,2,4-10,11\" OPTIONAL"}, ""};
54  fhicl::Atom<double> reporting_interval{fhicl::Name{"reporting_interval"}, fhicl::Comment{"How often recorded metrics are sent to the underlying metric storage"}, 15.0};
56  fhicl::Atom<bool> send_zeros{fhicl::Name{"send_zeros"}, fhicl::Comment{"Whether zeros should be sent to the metric back-end when metrics are not reported in an interval and during shutdown"}, true};
57  };
59  using Parameters = fhicl::WrappedTable<Config>;
60 
69  explicit MetricPlugin(fhicl::ParameterSet const& ps, std::string const& app_name)
70  : pset(ps)
71  , app_name_(app_name)
72  , inhibit_(false)
73  , level_mask_(0ULL)
74  , sendZeros_(pset.get<bool>("send_zeros", true))
75  {
76  if (pset.has_key("level"))
77  {
78  for (size_t ii = 0; ii <= pset.get<size_t>("level"); ++ii)
79  {
80  level_mask_[ii] = true;
81  }
82  }
83  if (pset.has_key("metric_levels"))
84  {
85  auto levels = pset.get<std::vector<size_t>>("metric_levels");
86  for (auto& l : levels)
87  {
88  level_mask_[l] = true;
89  }
90  }
91  if (pset.has_key("level_string"))
92  {
93  auto string = pset.get<std::string>("level_string");
94  std::stringstream ss(string);
95  std::string token;
96  while (std::getline(ss, token, ','))
97  {
98  auto it = token.find("-");
99  if (it == 0 || it == token.size() - 1) continue;
100 
101  if (it != std::string::npos)
102  {
103  auto minStr = token.substr(0, it);
104  auto maxStr = token.substr(it + 1);
105  auto min = std::stoi(minStr);
106  auto max = std::stoi(maxStr);
107 
108  if (min > max) std::swap(min, max);
109  if (min > 63) min = 63;
110  if (max > 63) max = 63;
111 
112  for (int ii = min; ii <= max; ++ii)
113  {
114  level_mask_[ii] = true;
115  }
116  }
117  else
118  {
119  auto level = std::stoi(token);
120  if (level >= 0 && level < 63) level_mask_[level] = true;
121  }
122  }
123  }
124  if (level_mask_.to_ullong() == 0)
125  {
126  throw cet::exception("Configuration Error") // NOLINT(cert-err60-cpp)
127  << "No levels were enabled for this plugin! Please specify at least one of the following Parameters: \"level\", \"metric_levels\", or \"level_string\"!";
128  }
129  accumulationTime_ = pset.get<double>("reporting_interval", 15.0);
130  }
131 
135  virtual ~MetricPlugin() = default;
136 
138  //
139  // Interface Functions: These should be reimplemented in plugin classes!
140  //
142 
146  virtual std::string getLibName() const { return "ERROR"; }
147 
148 protected:
158  virtual void sendMetric_(const std::string& name, const std::string& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
159 
169  virtual void sendMetric_(const std::string& name, const int& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
170 
180  virtual void sendMetric_(const std::string& name, const double& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
181 
191  virtual void sendMetric_(const std::string& name, const float& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
192 
202  virtual void sendMetric_(const std::string& name, const uint64_t& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
203 
209  virtual void startMetrics_() = 0;
210 
216  virtual void stopMetrics_() = 0;
217 
219  //
220  // Implementation Functions: These should be called from ARTDAQ code!
221  //
223 public:
228  void addMetricData(std::unique_ptr<MetricData> const& data)
229  {
230  TLOG(22) << "Adding metric data for name " << data->Name;
231  if (data->Type == MetricType::StringMetric)
232  {
233  sendMetric_(data->Name, data->StringValue, data->Unit, std::chrono::system_clock::now());
234  }
235  else
236  {
237  if (metricRegistry_.count(data->Name) == 0)
238  {
239  metricRegistry_[data->Name] = *data;
240  }
241  metricData_[data->Name].push_back(*data);
242  TLOG(22) << "Current list size: " << metricData_[data->Name].size();
243  //sendMetrics();
244  }
245  }
246 
255  void sendMetrics(bool forceSend = false,
256  std::chrono::steady_clock::time_point interval_end = std::chrono::steady_clock::now())
257  {
258  TLOG(23) << "sendMetrics called" << std::endl;
259  for (auto& metric : metricData_)
260  {
261  if (readyToSend_(metric.first) || forceSend)
262  {
263  TLOG(24) << "Sending metric " << metric.first;
264  if (metric.second.empty() && metricRegistry_.count(metric.first))
265  {
266  TLOG(24) << "Sending zero";
267  sendZero_(metricRegistry_[metric.first]);
268  }
269  else if (!metric.second.empty())
270  {
271  TLOG(24) << "Aggregating " << metric.second.size() << " MetricData points";
272 
273  if ((metric.second.front().Mode & MetricMode::Persist) != MetricMode::None && metric.second.size() > 1)
274  {
275  TLOG(24) << "Metric is in Persist mode and multiple instances are present. Removing the first entry.";
276  metric.second.erase(metric.second.begin());
277  }
278 
279  MetricData& data = metric.second.front();
280 
281  auto it = ++(metric.second.begin());
282  while (it != metric.second.end())
283  {
284  data.Add(*it);
285  it = metric.second.erase(it);
286  }
287 
288  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
289  bool useSuffix = true;
290  if (modeSet.count() <= 1 || (modeSet.count() <= 2 && (data.Mode & MetricMode::Persist) != MetricMode::None)) useSuffix = false;
291 
292  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
293  {
294  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), data.Last, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
295  }
296  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
297  {
298  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), data.Value, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
299  }
300  if ((data.Mode & MetricMode::Average) != MetricMode::None)
301  {
302  double average = 0.0;
303  switch (data.Type)
304  {
306  average = data.Value.d / static_cast<double>(data.DataPointCount);
307  break;
309  average = data.Value.f / static_cast<double>(data.DataPointCount);
310  break;
312  average = data.Value.i / static_cast<double>(data.DataPointCount);
313  break;
315  average = data.Value.u / static_cast<double>(data.DataPointCount);
316  break;
317  default:
318  break;
319  }
320  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), average, data.Unit, to_system_clock(lastSendTime_[data.Name]));
321  }
322  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
323  {
324  double duration = std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
325  interval_end - interval_start_[metric.first])
326  .count();
327  double rate = 0.0;
328  switch (data.Type)
329  {
331  rate = data.Value.d / duration;
332  break;
334  rate = data.Value.f / duration;
335  break;
337  rate = data.Value.i / duration;
338  break;
340  rate = data.Value.u / duration;
341  break;
342  default:
343  break;
344  }
345  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), rate, data.Unit + "/s", to_system_clock(lastSendTime_[data.Name]));
346  }
347  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
348  {
349  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), data.Min, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
350  }
351  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
352  {
353  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), data.Max, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
354  }
355 
356  if ((data.Mode & MetricMode::Persist) == MetricMode::None)
357  {
358  TLOG(24) << "Clearing metric data list sz=" << metric.second.size();
359  metric.second.clear();
360  TLOG(24) << "Cleared metric data list sz=" << metricData_[metric.first].size();
361  }
362  else
363  {
364  TLOG(24) << "Metric is Persisted, leaving " << metricData_[metric.first].size() << " entries (should be 1)";
365  }
366  }
367  interval_start_[metric.first] = interval_end;
368  }
369  }
370  TLOG(23) << "sendMetrics done" << std::endl;
371  }
372 
377 
382  void stopMetrics()
383  {
384  inhibit_ = true;
385  sendMetrics(true);
386  for (auto const& metric : metricRegistry_)
387  {
388  sendZero_(metric.second);
389  }
390  stopMetrics_();
391  inhibit_ = false;
392  }
393 
400  {
401  if (level > 63) level = 63;
402  if (level < 0) return true;
403  return level_mask_[level];
404  }
405 
411  {
412  for (auto& metric : metricData_)
413  {
414  if (!metric.second.empty())
415  {
416  TLOG(TLVL_TRACE) << "Metric " << metric.first << " has " << metric.second.size() << " pending MetricData instances" << std::endl;
417  return true;
418  }
419  }
420 
421  return false;
422  }
423 
424 protected:
425  fhicl::ParameterSet pset;
427  std::string app_name_;
428  bool inhibit_;
429  std::bitset<64> level_mask_;
430  bool sendZeros_;
431 
432 private:
433  MetricPlugin(const MetricPlugin&) = delete;
434  MetricPlugin(MetricPlugin&&) = delete;
435  MetricPlugin& operator=(const MetricPlugin&) = delete;
436  MetricPlugin& operator=(MetricPlugin&&) = delete;
437 
438  std::unordered_map<std::string, std::list<MetricData>> metricData_;
439  std::unordered_map<std::string, MetricData> metricRegistry_;
440  std::unordered_map<std::string, std::chrono::steady_clock::time_point> lastSendTime_;
441  std::unordered_map<std::string, std::chrono::steady_clock::time_point> interval_start_;
442 
443  std::chrono::system_clock::time_point to_system_clock(std::chrono::steady_clock::time_point const& t)
444  {
445  auto pt = std::chrono::system_clock::now() + (t - std::chrono::steady_clock::now());
446  return std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::system_clock::duration>(pt.time_since_epoch()));
447  }
448 
449  bool readyToSend_(std::string const& name)
450  {
451  auto now = std::chrono::steady_clock::now();
452  if (std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(now - lastSendTime_[name]).count() >= accumulationTime_)
453  {
454  lastSendTime_[name] = now;
455  return true;
456  }
457 
458  return false;
459  }
460 
461  void sendZero_(MetricData data)
462  {
463  if (sendZeros_)
464  {
465  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
466  bool useSuffix = true;
467  if (modeSet.count() <= 1) useSuffix = false;
468 
469  MetricData::MetricDataValue zero;
470  switch (data.Type)
471  {
473  zero.d = 0.0;
474  break;
476  zero.f = 0.0f;
477  break;
479  zero.i = 0;
480  break;
482  zero.u = 0;
483  break;
484  default:
485  break;
486  }
487 
488  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
489  {
490  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
491  }
492  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
493  {
494  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
495  }
496  if ((data.Mode & MetricMode::Average) != MetricMode::None)
497  {
498  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), 0.0, data.Unit, std::chrono::system_clock::now());
499  }
500  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
501  {
502  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), 0.0, data.Unit + "/s", std::chrono::system_clock::now());
503  }
504  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
505  {
506  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
507  }
508  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
509  {
510  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
511  }
512  }
513  }
514 
515  void sendMetric_(std::string const& name, MetricData::MetricDataValue data, std::string const& unit, MetricType type, std::chrono::system_clock::time_point const& interval_end)
516  {
517  switch (type)
518  {
520  sendMetric_(name, data.d, unit, interval_end);
521  break;
523  sendMetric_(name, data.f, unit, interval_end);
524  break;
526  sendMetric_(name, data.i, unit, interval_end);
527  break;
529  sendMetric_(name, data.u, unit, interval_end);
530  break;
531  default:
532  break;
533  }
534  }
535 };
536 } //End namespace artdaq
537 
538 #ifdef TRACE_NAME_POP
539 #pragma pop_macro("TRACE_NAME")
540 #undef TRACE_NAME_POP
541 #endif
542 #endif //End ifndef __METRIC_INTERFACE__
bool IsLevelEnabled(int level)
Determine if the given level is enabled for this MetricPlugin instance.
virtual void startMetrics_()=0
Perform any start-up actions necessary for the metric plugin.
The MetricPlugin class defines the interface that MetricManager uses to send metric data to the vario...
Definition: MetricPlugin.hh:37
void startMetrics()
Perform startup actions. Simply calls the virtual startMetrics_ function.
size_t DataPointCount
Number of data points accumulated in this MetricData
Definition: MetricData.hh:178
Report the sum of all values. Use for counters to report accurate results.
fhicl::ParameterSet pset
The ParameterSet used to configure the MetricPlugin.
fhicl::Atom< std::string > metricPluginType
The name of the metric plugin to load (may have additional configuration parameters.
Definition: MetricPlugin.hh:46
std::string Unit
Units of the metric
Definition: MetricData.hh:158
float f
Value of the metric, if it is a MetricType::FloatMetric.
Definition: MetricData.hh:109
Metric is a std::string (not in union)
std::string Name
Name of the metric
Definition: MetricData.hh:96
fhicl::Atom< double > reporting_interval
&quot;reporting_interval&quot; (Default: 15.0): The interval, in seconds, which the metric plugin will accumula...
Definition: MetricPlugin.hh:54
MetricMode Mode
Accumulation mode of the metric
Definition: MetricData.hh:166
bool Add(MetricData other)
Add two MetricData instances together
Definition: MetricData.hh:261
MetricType
This enumeration is used to identify the type of the metric instance (which value should be extraced ...
Definition: MetricData.hh:16
MetricDataValue Max
Maximum recorded vaule of this MetricData.
Definition: MetricData.hh:149
void stopMetrics()
Perform shutdown actions. Zeroes out all accumulators, and sends zeros for each metric. Calls stopMetrics_() for any plugin-defined shutdown actions.
Reports the minimum value recorded.
std::string app_name_
Name of the application which is sending metrics to this plugin.
Metric is a long unsigned int.
void sendMetrics(bool forceSend=false, std::chrono::steady_clock::time_point interval_end=std::chrono::steady_clock::now())
For each known metric, determine whether the reporting interval has elapsed, and if so...
over. Use to create rates from counters.
MetricPlugin(fhicl::ParameterSet const &ps, std::string const &app_name)
MetricPlugin Constructor.
Definition: MetricPlugin.hh:69
Report only the last value recorded. Useful for event counters, run numbers, etc. ...
Repots the maximum value recorded.
std::bitset< 64 > level_mask_
Bitset indicating for each possible metric level, whether this plugin will receive those metrics...
bool metricsPending()
Determine if metrics are waiting to be sent.
virtual std::string getLibName() const
Return the name of the current MetricPlugin instance.
virtual void sendMetric_(const std::string &name, const std::string &value, const std::string &unit, const std::chrono::system_clock::time_point &interval_end)=0
Send a metric to the underlying metric storage (file, Graphite, Ganglia, etc.)
MetricDataValue Value
Accumulated value of this MetricData
Definition: MetricData.hh:146
virtual void stopMetrics_()=0
Perform any shutdown actions necessary for the metric plugin.
int i
Value of the metric, if it is a MetricType::IntMetric.
Definition: MetricData.hh:107
Keep previous metric value in memory.
MetricType Type
Type of the metric
Definition: MetricData.hh:154
fhicl::Atom< std::string > level_string
&quot;level_string&quot; (OPTIONAL): A string containing a comma-separated list of levels to enable...
Definition: MetricPlugin.hh:52
fhicl::Atom< bool > send_zeros
&quot;send_zeros&quot; (Default: true): Whether zeros should be sent to the metric back-end when metrics are no...
Definition: MetricPlugin.hh:56
fhicl::WrappedTable< Config > Parameters
Used for ParameterSet validation (if desired)
Definition: MetricPlugin.hh:59
MetricDataValue Last
Last value of this MetricData.
Definition: MetricData.hh:147
fhicl::Atom< size_t > level
&quot;level&quot; (OPTIONAL): The verbosity level threshold for this plugin. sendMetric calls with verbosity le...
Definition: MetricPlugin.hh:48
double accumulationTime_
The amount of time to average metric values; except for accumulate=false metrics, will be the interva...
Small structure used to hold a metric data point before sending to the metric plugins ...
Definition: MetricData.hh:65
The Config struct defines the accepted configuration parameters for this class.
Definition: MetricPlugin.hh:43
uint64_t u
Value of the metric, if it is a MetricType::UnsignedMetric.
Definition: MetricData.hh:110
Report the average of all values. Use for rates to report accurate results.
MetricDataValue Min
Minimum recorded value of this MetricData.
Definition: MetricData.hh:148
fhicl::Sequence< size_t > metric_levels
&quot;metric_levels&quot; (OPTIONAL): A list of levels that should be enabled for this plugin.
Definition: MetricPlugin.hh:50
void addMetricData(std::unique_ptr< MetricData > const &data)
Send a metric value to the MetricPlugin.
bool sendZeros_
Whether zeros should be sent to this metric backend when metric instances are missing or at the end o...
virtual ~MetricPlugin()=default
Default virtual Desctructor.
double d
Value of the metric, if it is a MetricType::DoubleMetric.
Definition: MetricData.hh:108
bool inhibit_
Flag to indicate that the MetricPlugin is being stopped, and any metric back-ends which do not have a...