artdaq_utilities  v1_04_09
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 
8 #ifndef __METRIC_INTERFACE__
9 #define __METRIC_INTERFACE__
10 
11 #include <string>
12 #include <chrono>
13 #include <unordered_map>
14 #include "fhiclcpp/ParameterSet.h"
15 #include "fhiclcpp/types/Atom.h"
16 # include "fhiclcpp/types/ConfigurationTable.h"
17 
18 #include "artdaq-utilities/Plugins/MetricData.hh"
19 #include "cetlib/compiler_macros.h"
20 #ifndef FALLTHROUGH
21 #define FALLTHROUGH while(0)
22 #endif
23 
24 namespace artdaq
25 {
26  /*
27  * \brief The MetricPlugin class defines the interface that MetricManager uses to send metric data
28  * to the various metric plugins.
29  */
31  {
32  public:
33  /*
34  * \brief The Config struct defines the accepted configuration parameters for this class
35  */
36  struct Config
37  {
39  fhicl::Atom<std::string> metricPluginType{ fhicl::Name{"metricPluginType"}, fhicl::Comment{"The name of the metric plugin to load (may have additional configuration parameters"} };
41  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 };
43  fhicl::Atom<double> reporting_interval{ fhicl::Name{"reporting_interval"}, fhicl::Comment{"How often recorded metrics are sent to the underlying metric storage"}, 15.0 };
44  };
45  using Parameters = fhicl::WrappedTable<Config>;
46 
47  /*
48  * \brief MetricPlugin Constructor
49  * \param ps The ParameterSet used to configure this MetricPlugin instance
50  * \param app_name The Application name which can be used by the Metric Plugin for identification
51  *
52  * \verbatim
53  * MetricPlugin accepts the following parameters:
54  * "metricPluginType": The name of the plugin to load
55  * "level" (Default: 0): The verbosity level of the metric plugin. Higher number = fewer metrics sent to the metric storage
56  * "reporting_interval" (Default: 15.0): The interval, in seconds, which the metric plugin will accumulate values for.
57  * Calling sendMetric with the accumulate parameter set to false will bypass this accumulation and directly send the
58  * metric. String metrics cannot be accumulated.
59  * \endverbatim
60  */
61  explicit MetricPlugin(fhicl::ParameterSet const& ps, std::string const& app_name) : pset(ps)
62  , app_name_(app_name)
63  , inhibit_(false)
64  {
65  runLevel_ = pset.get<int>("level", 0);
66  accumulationTime_ = pset.get<double>("reporting_interval", 15.0);
67  }
68 
69  /*
70  * \brief Default virtual Desctructor
71  */
72  virtual ~MetricPlugin() = default;
73 
75  //
76  // Interface Functions: These should be reimplemented in plugin classes!
77  //
79 
80  /*
81  * \brief Return the name of the current MetricPlugin instance
82  */
83  virtual std::string getLibName() const { return "ERROR"; }
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(MetricData 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 the underlying metric storage
178  * \param forceSend (Default = false): Force sending metrics, even if reporting interval has not elapsed
179  * \param interval_end (Default = now): For calculating rates, when the current reporting interval ended (interval began at last value of interval_end)
180  */
181  void sendMetrics(bool forceSend = false, std::chrono::steady_clock::time_point interval_end = std::chrono::steady_clock::now())
182  {
183  double ds;
184  float fs;
185  int is;
186  unsigned long us;
187 
188  for (auto metric : metricData_)
189  {
190  auto *metricName = &metric.first;
191  if (readyToSend_(*metricName) || forceSend)
192  {
193  if (metricData_[*metricName].size() == 0 && metricRegistry_.count(*metricName))
194  {
195  sendZero_(metricRegistry_[*metricName]);
196  }
197  else if (metricData_[*metricName].size() > 0)
198  {
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  {
205  if (metricData_[*metricName].size() > 1)
206  {
207  metricData_[*metricName].erase(metricData_[*metricName].begin(), std::prev(metricData_[*metricName].end()));
208  }
209  sendMetric_(metricData_[*metricName].back());
210  }
211  else
212  {
213  switch (*metricType)
214  {
216  {
217  ds = 0.0;
218  for (auto& mv : metricData_[*metricName]) { ds += mv.DoubleValue; }
219  switch (*metricMode)
220  {
221  case MetricMode::Average: ds /= static_cast<double>(metricData_[*metricName].size()); break;
222  case MetricMode::Rate: ds /= std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count(); break;
223  case MetricMode::AccumulateAndRate: sendMetric_(*metricName + " - Rate", ds / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count(), *metricUnits + "/s"); break;
224  default:
225  break;
226  }
227  sendMetric_(*metricName, ds, *metricUnits);
228  }
229  break;
231  {
232  fs = 0.0;
233  for (auto& mv : metricData_[*metricName]) { fs += mv.FloatValue; }
234 
235  switch (*metricMode)
236  {
237  case MetricMode::Average:
238  ds = fs / static_cast<double>(metricData_[*metricName].size());
239  sendMetric_(*metricName, ds, *metricUnits);
240  break;
241  case MetricMode::Rate:
242  ds = fs / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count();
243  sendMetric_(*metricName, ds, *metricUnits);
244  break;
246  sendMetric_(*metricName + " - Rate", fs / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count(), *metricUnits + "/s");
247  FALLTHROUGH;
249  default:
250  sendMetric_(*metricName, fs, *metricUnits);
251  break;
252  }
253  }
254  break;
256  {
257  is = 0;
258  for (auto& mv : metricData_[*metricName]) { is += mv.IntValue; }
259 
260  switch (*metricMode)
261  {
262  case MetricMode::Average:
263  ds = is / static_cast<double>(metricData_[*metricName].size());
264  sendMetric_(*metricName, ds, *metricUnits);
265  break;
266  case MetricMode::Rate:
267  ds = is / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count();
268  sendMetric_(*metricName, ds, *metricUnits);
269  break;
271  sendMetric_(*metricName + " - Rate", is / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count(), *metricUnits + "/s");
272  FALLTHROUGH;
274  default:
275  sendMetric_(*metricName, is, *metricUnits);
276  break;
277  }
278  }
279  break;
281  {
282  us = 0UL;
283  for (auto& mv : metricData_[*metricName]) { us += mv.UnsignedValue; }
284 
285  switch (*metricMode)
286  {
287  case MetricMode::Average:
288  ds = us / static_cast<double>(metricData_[*metricName].size());
289  sendMetric_(*metricName, ds, *metricUnits);
290  break;
291  case MetricMode::Rate:
292  ds = us / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count();
293  sendMetric_(*metricName, ds, *metricUnits);
294  break;
296  sendMetric_(*metricName + " - Rate", us / std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(interval_end - interval_start_[*metricName]).count(), *metricUnits + "/s");
297  FALLTHROUGH;
299  default:
300  sendMetric_(*metricName, us, *metricUnits);
301  break;
302  }
303  }
304  break;
305  default:
306  break;
307  }
308  metricData_[*metricName].clear();
309  }
310  }
311  interval_start_[*metricName] = interval_end;
312  }
313  }
314  }
315 
316  /*
317  * \brief Perform startup actions. Simply calls the virtual startMetrics_ function
318  */
319  void startMetrics() { startMetrics_(); }
320 
321  /*
322  * \brief Perform shutdown actions. Zeroes out all accumulators, and sends zeros for each metric.
323  * Calls stopMetrics_() for any plugin-defined shutdown actions.
324  */
325  void stopMetrics()
326  {
327  inhibit_ = true;
328  sendMetrics(true);
329  for (auto metric : metricRegistry_)
330  {
331  sendZero_(metric.second);
332  }
333  stopMetrics_();
334  inhibit_ = false;
335  }
336 
337  /*
338  * \brief Set the threshold for sending metrics to the underlying storage.
339  * \param level The new threshold for sending metrics to the underlying storage. Metrics with level <= to runLevel_ will be sent.
340  */
341  void setRunLevel(int level) { runLevel_ = level; }
342  /*
343  * \brief Get the threshold for sending metrics to the underlying storage.
344  * \return The threshold for sending metrics to the underlying storage. Metrics with level <= to runLevel_ will be sent.
345  */
346  int getRunLevel() const { return runLevel_; }
347 
348  protected:
349  int runLevel_;
350  fhicl::ParameterSet pset;
352  std::string app_name_;
353  bool inhibit_;
354 
355  private:
356  std::unordered_map<std::string, std::list<MetricData>> metricData_;
357  std::unordered_map<std::string, MetricData> metricRegistry_;
358  std::unordered_map<std::string, std::chrono::steady_clock::time_point> lastSendTime_;
359  std::unordered_map<std::string, std::chrono::steady_clock::time_point> interval_start_;
360 
361  bool readyToSend_(std::string name)
362  {
363  auto now = std::chrono::steady_clock::now();
364  if (std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(now - lastSendTime_[name]).count() >= accumulationTime_)
365  {
366  lastSendTime_[name] = now;
367  return true;
368  }
369 
370  return false;
371  }
372 
373  void sendZero_(MetricData data)
374  {
375  switch (data.Type)
376  {
378  sendMetric_(data.Name, static_cast<double>(0.0), data.Unit);
379  break;
381  sendMetric_(data.Name, static_cast<float>(0.0), data.Unit);
382  break;
384  sendMetric_(data.Name, static_cast<int>(0), data.Unit);
385  break;
387  sendMetric_(data.Name, static_cast<unsigned long>(0), data.Unit);
388  break;
389  default:
390  break;
391  }
392 
394  {
395  sendMetric_(data.Name + " - Rate", static_cast<double>(0.0), data.Unit + "/s");
396  }
397  }
398 
399  void sendMetric_(MetricData data)
400  {
401  switch (data.Type)
402  {
404  sendMetric_(data.Name, data.DoubleValue, data.Unit);
405  break;
407  sendMetric_(data.Name, data.FloatValue, data.Unit);
408  break;
410  sendMetric_(data.Name, data.IntValue, data.Unit);
411  break;
413  sendMetric_(data.Name, data.UnsignedValue, data.Unit);
414  break;
415  default:
416  break;
417  }
418  }
419  };
420  } //End namespace artdaq
421 
422 #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:39
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:43
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:41
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.
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:42
Report only the last value recorded. Useful for event counters, run numbers, etc. ...
bool inhibit_
Whether to inhibit all metric sending.
int runLevel_
The threshold for sending metrics to the underlying storage. Metrics with level &lt;= to runLevel_ will ...