artdaq_utilities  v1_05_03
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") << "No levels were enabled for this plugin! Please specify at least one of the following Parameters: \"level\", \"metric_levels\", or \"level_string\"!";
127  }
128  accumulationTime_ = pset.get<double>("reporting_interval", 15.0);
129  }
130 
134  virtual ~MetricPlugin() = default;
135 
137  //
138  // Interface Functions: These should be reimplemented in plugin classes!
139  //
141 
145  virtual std::string getLibName() const { return "ERROR"; }
146 
147 protected:
156  virtual void sendMetric_(const std::string& name, const std::string& value, const std::string& unit) = 0;
157 
166  virtual void sendMetric_(const std::string& name, const int& value, const std::string& unit) = 0;
167 
176  virtual void sendMetric_(const std::string& name, const double& value, const std::string& unit) = 0;
177 
186  virtual void sendMetric_(const std::string& name, const float& value, const std::string& unit) = 0;
187 
196  virtual void sendMetric_(const std::string& name, const long unsigned int& value, const std::string& unit) = 0;
197 
203  virtual void startMetrics_() = 0;
204 
210  virtual void stopMetrics_() = 0;
211 
213  //
214  // Implementation Functions: These should be called from ARTDAQ code!
215  //
217 public:
222  void addMetricData(std::unique_ptr<MetricData> const& data)
223  {
224  TLOG(22) << "Adding metric data for name " << data->Name;
225  if (data->Type == MetricType::StringMetric)
226  {
227  sendMetric_(data->Name, data->StringValue, data->Unit);
228  }
229  else
230  {
231  if (!metricRegistry_.count(data->Name))
232  {
233  metricRegistry_[data->Name] = *data;
234  }
235  metricData_[data->Name].push_back(*data);
236  TLOG(22) << "Current list size: " << metricData_[data->Name].size();
237  //sendMetrics();
238  }
239  }
240 
249  void sendMetrics(bool forceSend = false,
250  std::chrono::steady_clock::time_point interval_end = std::chrono::steady_clock::now())
251  {
252  TLOG(23) << "sendMetrics called" << std::endl;
253  for (auto& metric : metricData_)
254  {
255  if (readyToSend_(metric.first) || forceSend)
256  {
257  TLOG(24) << "Sending metric " << metric.first;
258  if (metric.second.size() == 0 && metricRegistry_.count(metric.first))
259  {
260  TLOG(24) << "Sending zero";
261  sendZero_(metricRegistry_[metric.first]);
262  }
263  else if (metric.second.size() > 0)
264  {
265  TLOG(24) << "Aggregating " << metric.second.size() << " MetricData points";
266  MetricData& data = metric.second.front();
267  auto it = ++(metric.second.begin());
268  while (it != metric.second.end())
269  {
270  data.Add(*it);
271  it = metric.second.erase(it);
272  }
273 
274  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
275  bool useSuffix = true;
276  if (modeSet.count() <= 1) useSuffix = false;
277 
278  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
279  {
280  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), data.Last, data.Unit, data.Type);
281  }
282  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
283  {
284  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), data.Value, data.Unit, data.Type);
285  }
286  if ((data.Mode & MetricMode::Average) != MetricMode::None)
287  {
288  double average = 0.0;
289  switch (data.Type)
290  {
292  average = data.Value.d / static_cast<double>(data.DataPointCount);
293  break;
295  average = data.Value.f / static_cast<double>(data.DataPointCount);
296  break;
298  average = data.Value.i / static_cast<double>(data.DataPointCount);
299  break;
301  average = data.Value.u / static_cast<double>(data.DataPointCount);
302  break;
303  default:
304  break;
305  }
306  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), average, data.Unit);
307  }
308  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
309  {
310  double duration = std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
311  interval_end - interval_start_[metric.first])
312  .count();
313  double rate = 0.0;
314  switch (data.Type)
315  {
317  rate = data.Value.d / duration;
318  break;
320  rate = data.Value.f / duration;
321  break;
323  rate = data.Value.i / duration;
324  break;
326  rate = data.Value.u / duration;
327  break;
328  default:
329  break;
330  }
331  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), rate, data.Unit + "/s");
332  }
333  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
334  {
335  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), data.Min, data.Unit, data.Type);
336  }
337  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
338  {
339  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), data.Max, data.Unit, data.Type);
340  }
341 
342  TLOG(24) << "Clearing metric data list sz=" << metric.second.size();
343  metric.second.clear();
344  TLOG(24) << "Cleared metric data list sz=" << metricData_[metric.first].size();
345  }
346  interval_start_[metric.first] = interval_end;
347  }
348  }
349  TLOG(23) << "sendMetrics done" << std::endl;
350  }
351 
356 
361  void stopMetrics()
362  {
363  inhibit_ = true;
364  sendMetrics(true);
365  for (auto metric : metricRegistry_)
366  {
367  sendZero_(metric.second);
368  }
369  stopMetrics_();
370  inhibit_ = false;
371  }
372 
379  {
380  if (level > 63) level = 63;
381  if (level < 0) return true;
382  return level_mask_[level];
383  }
384 
390  {
391  for (auto& metric : metricData_)
392  {
393  if (metric.second.size() > 0)
394  {
395  TLOG(TLVL_TRACE) << "Metric " << metric.first << " has " << metric.second.size() << " pending MetricData instances" << std::endl;
396  return true;
397  }
398  }
399 
400  return false;
401  }
402 
403 protected:
404  fhicl::ParameterSet pset;
406  std::string app_name_;
407  bool inhibit_;
408  std::bitset<64> level_mask_;
409  bool sendZeros_;
410 
411 private:
412  std::unordered_map<std::string, std::list<MetricData>> metricData_;
413  std::unordered_map<std::string, MetricData> metricRegistry_;
414  std::unordered_map<std::string, std::chrono::steady_clock::time_point> lastSendTime_;
415  std::unordered_map<std::string, std::chrono::steady_clock::time_point> interval_start_;
416 
417  bool readyToSend_(std::string name)
418  {
419  auto now = std::chrono::steady_clock::now();
420  if (std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(now - lastSendTime_[name]).count() >= accumulationTime_)
421  {
422  lastSendTime_[name] = now;
423  return true;
424  }
425 
426  return false;
427  }
428 
429  void sendZero_(MetricData data)
430  {
431  if (sendZeros_)
432  {
433  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
434  bool useSuffix = true;
435  if (modeSet.count() <= 1) useSuffix = false;
436 
438  switch (data.Type)
439  {
441  zero.d = 0.0;
442  break;
444  zero.f = 0.0f;
445  break;
447  zero.i = 0;
448  break;
450  zero.u = 0;
451  break;
452  default:
453  break;
454  }
455 
456  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
457  {
458  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), zero, data.Unit, data.Type);
459  }
460  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
461  {
462  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), zero, data.Unit, data.Type);
463  }
464  if ((data.Mode & MetricMode::Average) != MetricMode::None)
465  {
466  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), 0.0, data.Unit);
467  }
468  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
469  {
470  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), 0.0, data.Unit + "/s");
471  }
472  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
473  {
474  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), zero, data.Unit, data.Type);
475  }
476  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
477  {
478  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), zero, data.Unit, data.Type);
479  }
480  }
481  }
482 
483  void sendMetric_(std::string name, MetricData::MetricDataValue data, std::string unit, MetricType type)
484  {
485  switch (type)
486  {
488  sendMetric_(name, data.d, unit);
489  break;
491  sendMetric_(name, data.f, unit);
492  break;
494  sendMetric_(name, data.i, unit);
495  break;
497  sendMetric_(name, data.u, unit);
498  break;
499  default:
500  break;
501  }
502  }
503 };
504 } //End namespace artdaq
505 
506 #ifdef TRACE_NAME_POP
507 #pragma pop_macro("TRACE_NAME")
508 #undef TRACE_NAME_POP
509 #endif
510 #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:177
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
virtual void sendMetric_(const std::string &name, const std::string &value, const std::string &unit)=0
Send a metric to the underlying metric storage (file, Graphite, Ganglia, etc.)
std::string Unit
Units of the metric
Definition: MetricData.hh:157
float f
Value of the metric, if it is a MetricType::FloatMetric.
Definition: MetricData.hh:108
Metric is a std::string (not in union)
std::string Name
Name of the metric
Definition: MetricData.hh:95
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:165
bool Add(MetricData other)
Add two MetricData instances together
Definition: MetricData.hh:260
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:148
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...
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.
MetricDataValue Value
Accumulated value of this MetricData
Definition: MetricData.hh:145
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:106
MetricType Type
Type of the metric
Definition: MetricData.hh:153
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
long unsigned int u
Value of the metric, if it is a MetricType::UnsignedMetric.
Definition: MetricData.hh:109
fhicl::WrappedTable< Config > Parameters
Used for ParameterSet validation (if desired)
Definition: MetricPlugin.hh:59
This union holds the values for all other metric types
Definition: MetricData.hh:104
MetricDataValue Last
Last value of this MetricData.
Definition: MetricData.hh:146
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:64
The Config struct defines the accepted configuration parameters for this class.
Definition: MetricPlugin.hh:43
Report the average of all values. Use for rates to report accurate results.
MetricDataValue Min
Minimum recorded value of this MetricData.
Definition: MetricData.hh:147
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:107
bool inhibit_
Flag to indicate that the MetricPlugin is being stopped, and any metric back-ends which do not have a...