artdaq_utilities  v1_04_10
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 #include <chrono>
11 #include <string>
12 #include <unordered_map>
13 #include "fhiclcpp/ParameterSet.h"
14 #include "fhiclcpp/types/Atom.h"
15 # include "fhiclcpp/types/ConfigurationTable.h"
16 
17 #include "artdaq-utilities/Plugins/MetricData.hh"
18 #include "cetlib/compiler_macros.h"
19 #ifndef FALLTHROUGH
20 #define FALLTHROUGH while(0)
21 #endif
22 
23 namespace artdaq
24 {
25  /*
26  * \brief The MetricPlugin class defines the interface that MetricManager uses to send metric data
27  * to the various metric plugins.
28  */
30  {
31  public:
32  /*
33  * \brief The Config struct defines the accepted configuration parameters for this class
34  */
35  struct Config
36  {
38  fhicl::Atom<std::string> metricPluginType{ fhicl::Name{"metricPluginType"}, fhicl::Comment{"The name of the metric plugin to load (may have additional configuration parameters"} };
40  fhicl::Atom<int> level{ fhicl::Name{"level"}, fhicl::Comment{"The verbosity level threshold for this plugin. Metrics with verbosity level greater than this will not be sent to the plugin"}, 0 };
42  fhicl::Atom<double> reporting_interval{ fhicl::Name{"reporting_interval"}, fhicl::Comment{"How often recorded metrics are sent to the underlying metric storage"}, 15.0 };
43  };
44  using Parameters = fhicl::WrappedTable<Config>;
45 
46  /*
47  * \brief MetricPlugin Constructor
48  * \param ps The ParameterSet used to configure this MetricPlugin instance
49  * \param app_name The Application name which can be used by the Metric Plugin for identification
50  *
51  * \verbatim
52  * MetricPlugin accepts the following parameters:
53  * "metricPluginType": The name of the plugin to load
54  * "level" (Default: 0): The verbosity level of the metric plugin. Higher number = fewer metrics sent to the metric storage
55  * "reporting_interval" (Default: 15.0): The interval, in seconds, which the metric plugin will accumulate values for.
56  * Calling sendMetric with the accumulate parameter set to false will bypass this accumulation and directly send the
57  * metric. String metrics cannot be accumulated.
58  * \endverbatim
59  */
60  explicit MetricPlugin(fhicl::ParameterSet const& ps, std::string const& app_name) : pset(ps)
61  , app_name_(app_name)
62  , inhibit_(false)
63  {
64  runLevel_ = pset.get<int>("level", 0);
65  accumulationTime_ = pset.get<double>("reporting_interval", 15.0);
66  }
67 
68  /*
69  * \brief Default virtual Desctructor
70  */
71  virtual ~MetricPlugin() = default;
72 
74  //
75  // Interface Functions: These should be reimplemented in plugin classes!
76  //
78 
79  /*
80  * \brief Return the name of the current MetricPlugin instance
81  */
82  virtual std::string getLibName() const { return "ERROR"; }
83 
84  protected:
85  /*
86  * \brief Send a metric to the underlying metric storage (file, Graphite, Ganglia, etc.)
87  * \param name Name of the metric
88  * \param value Value of the metric
89  * \param unit Units for the metric
90  *
91  * Note this is a pure virtual function, it should be overridden by implementation plugins
92  */
93  virtual void sendMetric_(const std::string& name, const std::string& value, const std::string& unit) = 0;
94 
95  /*
96  * \brief Send a metric to the underlying metric storage (file, Graphite, Ganglia, etc.)
97  * \param name Name of the metric
98  * \param value Value of the metric
99  * \param unit Units for the metric
100  *
101  * Note this is a pure virtual function, it should be overridden by implementation plugins
102  */
103  virtual void sendMetric_(const std::string& name, const int& value, const std::string& unit) = 0;
104 
105  /*
106  * \brief Send a metric to the underlying metric storage (file, Graphite, Ganglia, etc.)
107  * \param name Name of the metric
108  * \param value Value of the metric
109  * \param unit Units for the metric
110  *
111  * Note this is a pure virtual function, it should be overridden by implementation plugins
112  */
113  virtual void sendMetric_(const std::string& name, const double& value, const std::string& unit) = 0;
114 
115  /*
116  * \brief Send a metric to the underlying metric storage (file, Graphite, Ganglia, etc.)
117  * \param name Name of the metric
118  * \param value Value of the metric
119  * \param unit Units for the metric
120  *
121  * Note this is a pure virtual function, it should be overridden by implementation plugins
122  */
123  virtual void sendMetric_(const std::string& name, const float& value, const std::string& unit) = 0;
124 
125  /*
126  * \brief Send a metric to the underlying metric storage (file, Graphite, Ganglia, etc.)
127  * \param name Name of the metric
128  * \param value Value of the metric
129  * \param unit Units for the metric
130  *
131  * Note this is a pure virtual function, it should be overridden by implementation plugins
132  */
133  virtual void sendMetric_(const std::string& name, const long unsigned int& value, const std::string& unit) = 0;
134 
135  /*
136  * \brief Perform any start-up actions necessary for the metric plugin
137  *
138  * This is a pure virtual function, it should be overridden by implementation plugins
139  */
140  virtual void startMetrics_() = 0;
141 
142  /*
143  * \brief Perform any shutdown actions necessary for the metric plugin
144  *
145  * This is a pure virtual function, it should be overridden by implementation plugins
146  */
147  virtual void stopMetrics_() = 0;
148 
150  //
151  // Implementation Functions: These should be called from ARTDAQ code!
152  //
154  public:
155  /*
156  * \brief Send a metric value to the MetricPlugin
157  * \param data A MetricData struct containing the metric value
158  */
159  void addMetricData(std::unique_ptr<MetricData> const& data)
160  {
161  if (data->Type == MetricType::StringMetric)
162  {
163  sendMetric_(data->Name, data->StringValue, data->Unit);
164  }
165  else
166  {
167  if (!metricRegistry_.count(data->Name))
168  {
169  metricRegistry_[data->Name] = *data;
170  }
171  metricData_[data->Name].push_back(*data);
172  //sendMetrics();
173  }
174  }
175 
176  /*
177  * \brief For each known metric, determine whether the reporting interval has elapsed, and if so, report a value to
178  * the underlying metric storage
179  * \param forceSend (Default = false): Force sending metrics, even if reporting interval
180  * has not elapsed
181  * \param interval_end (Default = now): For calculating rates, when the current reporting interval
182  * ended (interval began at last value of interval_end)
183  */
184  void sendMetrics(bool forceSend = false,
185  std::chrono::steady_clock::time_point interval_end = std::chrono::steady_clock::now()) {
186  double ds;
187  float fs;
188  int is;
189  unsigned long us;
190  size_t count;
191 
192  for (auto metric : metricData_) {
193  auto *metricName = &metric.first;
194  count = 0;
195  if (readyToSend_(*metricName) || forceSend) {
196  if (metricData_[*metricName].size() == 0 && metricRegistry_.count(*metricName)) {
197  sendZero_(metricRegistry_[*metricName]);
198  } else if (metricData_[*metricName].size() > 0) {
199  auto metricMode = &metricData_[*metricName].back().Mode;
200  auto metricUnits = &metricData_[*metricName].back().Unit;
201  auto metricType = &metricData_[*metricName].back().Type;
202 
203  if (*metricMode == MetricMode::LastPoint) {
204  if (metricData_[*metricName].size() > 1) {
205  metricData_[*metricName].erase(metricData_[*metricName].begin(),
206  std::prev(metricData_[*metricName].end()));
207  }
208  sendMetric_(metricData_[*metricName].back());
209  } else {
210  switch (*metricType) {
212  ds = 0.0;
213  for (auto& mv : metricData_[*metricName]) {
214  ds += mv.DoubleValue;
215  count += mv.DataPointCount;
216  }
217  switch (*metricMode) {
218  case MetricMode::Average:
219  ds /= static_cast<double>(count);
220  break;
221  case MetricMode::Rate:
222  ds /= std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
223  interval_end - interval_start_[*metricName])
224  .count();
225  break;
227  sendMetric_(*metricName + " - Rate",
228  ds / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
229  interval_end - interval_start_[*metricName])
230  .count(),
231  *metricUnits + "/s");
232  break;
233  default:
234  break;
235  }
236  sendMetric_(*metricName, ds, *metricUnits);
237  } break;
239  fs = 0.0;
240  for (auto& mv : metricData_[*metricName]) {
241  fs += mv.FloatValue;
242  count += mv.DataPointCount;
243  }
244 
245  switch (*metricMode) {
246  case MetricMode::Average:
247  ds = fs / static_cast<double>(count);
248  sendMetric_(*metricName, ds, *metricUnits);
249  break;
250  case MetricMode::Rate:
251  ds = fs / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
252  interval_end - interval_start_[*metricName])
253  .count();
254  sendMetric_(*metricName, ds, *metricUnits);
255  break;
257  sendMetric_(*metricName + " - Rate",
258  fs / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
259  interval_end - interval_start_[*metricName])
260  .count(),
261  *metricUnits + "/s");
262  FALLTHROUGH;
264  default:
265  sendMetric_(*metricName, fs, *metricUnits);
266  break;
267  }
268  } break;
269  case MetricType::IntMetric: {
270  is = 0;
271  for (auto& mv : metricData_[*metricName]) {
272  is += mv.IntValue;
273  count += mv.DataPointCount;
274  }
275 
276  switch (*metricMode) {
277  case MetricMode::Average:
278  ds = is / static_cast<double>(count);
279  sendMetric_(*metricName, ds, *metricUnits);
280  break;
281  case MetricMode::Rate:
282  ds = is / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
283  interval_end - interval_start_[*metricName])
284  .count();
285  sendMetric_(*metricName, ds, *metricUnits);
286  break;
288  sendMetric_(*metricName + " - Rate",
289  is / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
290  interval_end - interval_start_[*metricName])
291  .count(),
292  *metricUnits + "/s");
293  FALLTHROUGH;
295  default:
296  sendMetric_(*metricName, is, *metricUnits);
297  break;
298  }
299  } break;
301  us = 0UL;
302  for (auto& mv : metricData_[*metricName]) {
303  us += mv.UnsignedValue;
304  count += mv.DataPointCount;
305  }
306 
307  switch (*metricMode) {
308  case MetricMode::Average:
309  ds = us / static_cast<double>(count);
310  sendMetric_(*metricName, ds, *metricUnits);
311  break;
312  case MetricMode::Rate:
313  ds = us / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
314  interval_end - interval_start_[*metricName])
315  .count();
316  sendMetric_(*metricName, ds, *metricUnits);
317  break;
319  sendMetric_(*metricName + " - Rate",
320  us / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
321  interval_end - interval_start_[*metricName])
322  .count(),
323  *metricUnits + "/s");
324  FALLTHROUGH;
326  default:
327  sendMetric_(*metricName, us, *metricUnits);
328  break;
329  }
330  } break;
331  default:
332  break;
333  }
334  metricData_[*metricName].clear();
335  }
336  }
337  interval_start_[*metricName] = interval_end;
338  }
339  }
340  }
341 
342  /*
343  * \brief Perform startup actions. Simply calls the virtual startMetrics_ function
344  */
345  void startMetrics() { startMetrics_(); }
346 
347  /*
348  * \brief Perform shutdown actions. Zeroes out all accumulators, and sends zeros for each metric.
349  * Calls stopMetrics_() for any plugin-defined shutdown actions.
350  */
351  void stopMetrics() {
352  inhibit_ = true;
353  sendMetrics(true);
354  for (auto metric : metricRegistry_) {
355  sendZero_(metric.second);
356  }
357  stopMetrics_();
358  inhibit_ = false;
359  }
360 
361  /*
362  * \brief Set the threshold for sending metrics to the underlying storage.
363  * \param level The new threshold for sending metrics to the underlying storage. Metrics with level <= to runLevel_
364  * will be sent.
365  */
366  void setRunLevel(int level) { runLevel_ = level; }
367  /*
368  * \brief Get the threshold for sending metrics to the underlying storage.
369  * \return The threshold for sending metrics to the underlying storage. Metrics with level <= to runLevel_ will be
370  * sent.
371  */
372  int getRunLevel() const { return runLevel_; }
373 
374  protected:
375  int runLevel_;
376  fhicl::ParameterSet pset;
379  std::string app_name_;
381  bool inhibit_;
382 
383  private:
384  std::unordered_map<std::string, std::list<MetricData>> metricData_;
385  std::unordered_map<std::string, MetricData> metricRegistry_;
386  std::unordered_map<std::string, std::chrono::steady_clock::time_point> lastSendTime_;
387  std::unordered_map<std::string, std::chrono::steady_clock::time_point> interval_start_;
388 
389  bool readyToSend_(std::string name) {
390  auto now = std::chrono::steady_clock::now();
391  if (std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(now - lastSendTime_[name]).count() >=
393  lastSendTime_[name] = now;
394  return true;
395  }
396 
397  return false;
398  }
399 
400  void sendZero_(MetricData data) {
401  switch (data.Type) {
403  sendMetric_(data.Name, static_cast<double>(0.0), data.Unit);
404  break;
406  sendMetric_(data.Name, static_cast<float>(0.0), data.Unit);
407  break;
409  sendMetric_(data.Name, static_cast<int>(0), data.Unit);
410  break;
412  sendMetric_(data.Name, static_cast<unsigned long>(0), data.Unit);
413  break;
414  default:
415  break;
416  }
417 
418  if (data.Mode == MetricMode::AccumulateAndRate) {
419  sendMetric_(data.Name + " - Rate", static_cast<double>(0.0), data.Unit + "/s");
420  }
421  }
422 
423  void sendMetric_(MetricData data) {
424  switch (data.Type) {
426  sendMetric_(data.Name, data.DoubleValue, data.Unit);
427  break;
429  sendMetric_(data.Name, data.FloatValue, data.Unit);
430  break;
432  sendMetric_(data.Name, data.IntValue, data.Unit);
433  break;
435  sendMetric_(data.Name, data.UnsignedValue, data.Unit);
436  break;
437  default:
438  break;
439  }
440  }
441  };
442  } //End namespace artdaq
443 
444 #endif //End ifndef __METRIC_INTERFACE__
Report the average of all values. Use for rates 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:38
std::string Unit
Units of the metric
Definition: MetricData.hh:97
Metric is a std::string (not in union)
std::string Name
Name of the metric
Definition: MetricData.hh:73
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:42
Reports the sum of all values, divided by the length of the time interval they were accumulated over...
MetricMode Mode
Accumulation mode of the metric
Definition: MetricData.hh:105
fhicl::Atom< int > level
&quot;level&quot; (Default: 0): The verbosity level of the metric plugin. Higher number = fewer metrics sent to...
Definition: MetricPlugin.hh:40
std::string app_name_
Name of the application which is sending metrics to this plugin.
Metric is a long unsigned int.
Sends both the Accumulate mode and Rate mode metric. (Rate mode metric will append &quot;/s&quot; to metric uni...
MetricType Type
Type of the metric
Definition: MetricData.hh:93
Report the sum of all values. Use for counters to report accurate results.
Small structure used to hold a metric data point before sending to the metric plugins ...
Definition: MetricData.hh:42
Report only the last value recorded. Useful for event counters, run numbers, etc. ...
bool inhibit_
Whether to inhibit all metric sending.