artdaq_utilities  v1_07_02_01
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) Note:
17 // MEtricTLOG
18 #define METLOG(lvl) TLOG(lvl) << metric_name_ << ": "
19 #define METLOG_P(lvl) TLOG(lvl, "MetricPlugin") << metric_name_ << ": "
20 
21 #include <bitset>
22 #include <chrono>
23 #include <string>
24 #include <unordered_map>
25 #include "fhiclcpp/ParameterSet.h"
26 #include "fhiclcpp/types/Atom.h"
27 #include "fhiclcpp/types/ConfigurationTable.h"
28 #include "fhiclcpp/types/Sequence.h"
29 
30 #include "artdaq-utilities/Plugins/MetricData.hh"
31 #include "cetlib/compiler_macros.h"
32 #ifndef FALLTHROUGH
33 #define FALLTHROUGH while (0)
34 #endif
35 
36 namespace artdaq {
42 {
43 public:
47  struct Config
48  {
50  fhicl::Atom<std::string> metricPluginType{fhicl::Name{"metricPluginType"}, fhicl::Comment{"The name of the metric plugin to load (may have additional configuration parameters"}};
52  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};
54  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>()};
56  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"}, ""};
58  fhicl::Atom<double> reporting_interval{fhicl::Name{"reporting_interval"}, fhicl::Comment{"How often recorded metrics are sent to the underlying metric storage"}, 15.0};
60  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};
61  };
63  using Parameters = fhicl::WrappedTable<Config>;
64 
74  explicit MetricPlugin(fhicl::ParameterSet const& ps, std::string const& app_name, std::string const& metric_name)
75  : pset(ps)
76  , app_name_(app_name)
77  , metric_name_(metric_name)
78  , inhibit_(false)
79  , level_mask_(0ULL)
80  , sendZeros_(pset.get<bool>("send_zeros", true))
81  {
82  METLOG_P(TLVL_TRACE) << "MetricPlugin ctor start";
83  if (pset.has_key("level"))
84  {
85  for (size_t ii = 0; ii <= pset.get<size_t>("level"); ++ii)
86  {
87  level_mask_[ii] = true;
88  }
89  }
90  if (pset.has_key("metric_levels"))
91  {
92  auto levels = pset.get<std::vector<size_t>>("metric_levels");
93  for (auto& l : levels)
94  {
95  level_mask_[l] = true;
96  }
97  }
98  if (pset.has_key("level_string"))
99  {
100  auto string = pset.get<std::string>("level_string");
101  std::stringstream ss(string);
102  std::string token;
103  while (std::getline(ss, token, ','))
104  {
105  auto it = token.find("-");
106  if (it == 0 || it == token.size() - 1) continue;
107 
108  if (it != std::string::npos)
109  {
110  auto minStr = token.substr(0, it);
111  auto maxStr = token.substr(it + 1);
112  auto min = std::stoi(minStr);
113  auto max = std::stoi(maxStr);
114 
115  if (min > max) std::swap(min, max);
116  if (min > 63) min = 63;
117  if (max > 63) max = 63;
118 
119  for (int ii = min; ii <= max; ++ii)
120  {
121  level_mask_[ii] = true;
122  }
123  }
124  else
125  {
126  auto level = std::stoi(token);
127  if (level >= 0 && level < 63) level_mask_[level] = true;
128  }
129  }
130  }
131  if (level_mask_.to_ullong() == 0)
132  {
133  throw cet::exception("Configuration Error") // NOLINT(cert-err60-cpp)
134  << "No levels were enabled for this plugin! Please specify at least one of the following Parameters: \"level\", \"metric_levels\", or \"level_string\"!";
135  }
136  accumulationTime_ = pset.get<double>("reporting_interval", 15.0);
137  }
138 
142  virtual ~MetricPlugin() = default;
143 
145  //
146  // Interface Functions: These should be reimplemented in plugin classes!
147  //
149 
153  virtual std::string getLibName() const { return "ERROR"; }
154 
155 protected:
165  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;
166 
176  virtual void sendMetric_(const std::string& name, const int& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
177 
187  virtual void sendMetric_(const std::string& name, const double& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
188 
198  virtual void sendMetric_(const std::string& name, const float& value, const std::string& unit, const std::chrono::system_clock::time_point& interval_end) = 0;
199 
209  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;
210 
216  virtual void startMetrics_() = 0;
217 
223  virtual void stopMetrics_() = 0;
224 
226  //
227  // Implementation Functions: These should be called from ARTDAQ code!
228  //
230 public:
235  void addMetricData(std::unique_ptr<MetricData> const& data)
236  {
237  METLOG_P(TLVL_DEBUG + 42) << "Adding metric data for name " << data->Name;
238  if (data->Type == MetricType::StringMetric)
239  {
240  sendMetric_(data->Name, data->StringValue, data->Unit, std::chrono::system_clock::now());
241  }
242  else
243  {
244  if (metricRegistry_.count(data->Name) == 0)
245  {
246  metricRegistry_[data->Name] = *data;
247  }
248  metricData_[data->Name].push_back(*data);
249  METLOG_P(TLVL_DEBUG + 42) << "Current list size: " << metricData_[data->Name].size();
250  //sendMetrics();
251  }
252  }
253 
262  void sendMetrics(bool forceSend = false,
263  std::chrono::steady_clock::time_point interval_end = std::chrono::steady_clock::now())
264  {
265  METLOG_P(TLVL_DEBUG + 43) << "sendMetrics called" << std::endl;
266  for (auto& metric : metricData_)
267  {
268  if (readyToSend_(metric.first) || forceSend)
269  {
270  METLOG_P(TLVL_DEBUG + 44) << "Sending metric " << metric.first;
271  if (metric.second.empty() && metricRegistry_.count(metric.first))
272  {
273  METLOG_P(TLVL_DEBUG + 44) << "Sending zero";
274  sendZero_(metricRegistry_[metric.first]);
275  }
276  else if (!metric.second.empty())
277  {
278  METLOG_P(TLVL_DEBUG + 44) << "Aggregating " << metric.second.size() << " MetricData points";
279 
280  if ((metric.second.front().Mode & MetricMode::Persist) != MetricMode::None && metric.second.size() > 1)
281  {
282  TLOG(TLVL_DEBUG + 44) << "Metric is in Persist mode and multiple instances are present. Removing the first entry.";
283  metric.second.erase(metric.second.begin());
284  }
285 
286  MetricData& data = metric.second.front();
287 
288  auto it = ++(metric.second.begin());
289  while (it != metric.second.end())
290  {
291  data.Add(*it);
292  it = metric.second.erase(it);
293  }
294 
295  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
296  bool useSuffix = true;
297  if (modeSet.count() <= 1 || (modeSet.count() <= 2 && (data.Mode & MetricMode::Persist) != MetricMode::None)) useSuffix = false;
298 
299  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
300  {
301  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), data.Last, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
302  }
303  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
304  {
305  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), data.Value, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
306  }
307  if ((data.Mode & MetricMode::Average) != MetricMode::None)
308  {
309  double average = 0.0;
310  switch (data.Type)
311  {
313  average = data.Value.d / static_cast<double>(data.DataPointCount);
314  break;
316  average = data.Value.f / static_cast<double>(data.DataPointCount);
317  break;
319  average = data.Value.i / static_cast<double>(data.DataPointCount);
320  break;
322  average = data.Value.u / static_cast<double>(data.DataPointCount);
323  break;
324  default:
325  break;
326  }
327  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), average, data.Unit, to_system_clock(lastSendTime_[data.Name]));
328  }
329  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
330  {
331  double duration = std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
332  interval_end - interval_start_[metric.first])
333  .count();
334  double rate = 0.0;
335  switch (data.Type)
336  {
338  rate = data.Value.d / duration;
339  break;
341  rate = data.Value.f / duration;
342  break;
344  rate = data.Value.i / duration;
345  break;
347  rate = data.Value.u / duration;
348  break;
349  default:
350  break;
351  }
352  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), rate, data.Unit + "/s", to_system_clock(lastSendTime_[data.Name]));
353  }
354  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
355  {
356  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), data.Min, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
357  }
358  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
359  {
360  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), data.Max, data.Unit, data.Type, to_system_clock(lastSendTime_[data.Name]));
361  }
362 
363  if ((data.Mode & MetricMode::Persist) == MetricMode::None)
364  {
365  METLOG_P(TLVL_DEBUG + 44) << "Clearing metric data list sz=" << metric.second.size();
366  metric.second.clear();
367  METLOG_P(TLVL_DEBUG + 44) << "Cleared metric data list sz=" << metricData_[metric.first].size();
368  }
369  else
370  {
371  TLOG(TLVL_DEBUG + 44) << "Metric is Persisted, leaving " << metricData_[metric.first].size() << " entries (should be 1)";
372  }
373  }
374  interval_start_[metric.first] = interval_end;
375  }
376  }
377  METLOG_P(TLVL_DEBUG + 43) << "sendMetrics done" << std::endl;
378  }
379 
384 
389  void stopMetrics()
390  {
391  inhibit_ = true;
392  sendMetrics(true);
393  for (auto const& metric : metricRegistry_)
394  {
395  sendZero_(metric.second);
396  }
397  stopMetrics_();
398  inhibit_ = false;
399  }
400 
407  {
408  if (level > 63) level = 63;
409  if (level < 0) return true;
410  return level_mask_[level];
411  }
412 
418  {
419  for (auto& metric : metricData_)
420  {
421  if (!metric.second.empty())
422  {
423  METLOG_P(TLVL_DEBUG + 33) << "Metric " << metric.first << " has " << metric.second.size() << " pending MetricData instances" << std::endl;
424  return true;
425  }
426  }
427 
428  return false;
429  }
430 
431 protected:
432  fhicl::ParameterSet pset;
434  std::string app_name_;
435  std::string metric_name_;
436  bool inhibit_;
437  std::bitset<64> level_mask_;
438  bool sendZeros_;
439 
440 private:
441  MetricPlugin(const MetricPlugin&) = delete;
442  MetricPlugin(MetricPlugin&&) = delete;
443  MetricPlugin& operator=(const MetricPlugin&) = delete;
444  MetricPlugin& operator=(MetricPlugin&&) = delete;
445 
446  std::unordered_map<std::string, std::list<MetricData>> metricData_;
447  std::unordered_map<std::string, MetricData> metricRegistry_;
448  std::unordered_map<std::string, std::chrono::steady_clock::time_point> lastSendTime_;
449  std::unordered_map<std::string, std::chrono::steady_clock::time_point> interval_start_;
450 
451  std::chrono::system_clock::time_point to_system_clock(std::chrono::steady_clock::time_point const& t)
452  {
453  auto pt = std::chrono::system_clock::now() + (t - std::chrono::steady_clock::now());
454  return std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::system_clock::duration>(pt.time_since_epoch()));
455  }
456 
457  bool readyToSend_(std::string const& name)
458  {
459  auto now = std::chrono::steady_clock::now();
460  if (std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(now - lastSendTime_[name]).count() >= accumulationTime_)
461  {
462  lastSendTime_[name] = now;
463  return true;
464  }
465 
466  return false;
467  }
468 
469  void sendZero_(MetricData data)
470  {
471  if (sendZeros_)
472  {
473  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
474  bool useSuffix = true;
475  if (modeSet.count() <= 1) useSuffix = false;
476 
477  MetricData::MetricDataValue zero;
478  switch (data.Type)
479  {
481  zero.d = 0.0;
482  break;
484  zero.f = 0.0f;
485  break;
487  zero.i = 0;
488  break;
490  zero.u = 0;
491  break;
492  default:
493  break;
494  }
495 
496  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
497  {
498  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
499  }
500  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
501  {
502  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
503  }
504  if ((data.Mode & MetricMode::Average) != MetricMode::None)
505  {
506  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), 0.0, data.Unit, std::chrono::system_clock::now());
507  }
508  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
509  {
510  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), 0.0, data.Unit + "/s", std::chrono::system_clock::now());
511  }
512  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
513  {
514  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
515  }
516  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
517  {
518  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), zero, data.Unit, data.Type, std::chrono::system_clock::now());
519  }
520  }
521  }
522 
523  void sendMetric_(std::string const& name, MetricData::MetricDataValue data, std::string const& unit, MetricType type, std::chrono::system_clock::time_point const& interval_end)
524  {
525  switch (type)
526  {
528  sendMetric_(name, data.d, unit, interval_end);
529  break;
531  sendMetric_(name, data.f, unit, interval_end);
532  break;
534  sendMetric_(name, data.i, unit, interval_end);
535  break;
537  sendMetric_(name, data.u, unit, interval_end);
538  break;
539  default:
540  break;
541  }
542  }
543 };
544 } //End namespace artdaq
545 
546 #ifdef TRACE_NAME_POP
547 #pragma pop_macro("TRACE_NAME")
548 #undef TRACE_NAME_POP
549 #endif
550 #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:41
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:50
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
std::string metric_name_
Name of this MetricPlugin instance.
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:58
MetricPlugin(fhicl::ParameterSet const &ps, std::string const &app_name, std::string const &metric_name)
MetricPlugin Constructor.
Definition: MetricPlugin.hh:74
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.
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:56
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:60
fhicl::WrappedTable< Config > Parameters
Used for ParameterSet validation (if desired)
Definition: MetricPlugin.hh:63
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:52
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:47
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:54
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...