artdaq_utilities  v1_05_00
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 #include "fhiclcpp/types/Sequence.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 {
30 {
31 public:
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<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};
42  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>()};
44  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"}, ""};
46  fhicl::Atom<double> reporting_interval{fhicl::Name{"reporting_interval"}, fhicl::Comment{"How often recorded metrics are sent to the underlying metric storage"}, 15.0};
47  };
49  using Parameters = fhicl::WrappedTable<Config>;
50 
59  explicit MetricPlugin(fhicl::ParameterSet const& ps, std::string const& app_name)
60  : pset(ps)
61  , app_name_(app_name)
62  , inhibit_(false)
63  , level_mask_(0ULL)
64  {
65  if (pset.has_key("level"))
66  {
67  for (size_t ii = 0; ii <= pset.get<size_t>("level"); ++ii)
68  {
69  level_mask_[ii] = true;
70  }
71  }
72  if (pset.has_key("metric_levels"))
73  {
74  auto levels = pset.get<std::vector<size_t>>("metric_levels");
75  for (auto& l : levels)
76  {
77  level_mask_[l] = true;
78  }
79  }
80  if (pset.has_key("level_string"))
81  {
82  auto string = pset.get<std::string>("level_string");
83  std::stringstream ss(string);
84  std::string token;
85  while (std::getline(ss, token, ','))
86  {
87  auto it = token.find("-");
88  if (it == 0 || it == token.size() - 1) continue;
89 
90  if (it != std::string::npos)
91  {
92  auto minStr = token.substr(0, it);
93  auto maxStr = token.substr(it + 1);
94  auto min = std::stoi(minStr);
95  auto max = std::stoi(maxStr);
96 
97  if (min > max) std::swap(min, max);
98  if (min > 63) min = 63;
99  if (max > 63) max = 63;
100 
101  for (int ii = min; ii <= max; ++ii)
102  {
103  level_mask_[ii] = true;
104  }
105  }
106  else
107  {
108  auto level = std::stoi(token);
109  if (level >= 0 && level < 63) level_mask_[level] = true;
110  }
111  }
112  }
113  if (level_mask_.to_ullong() == 0)
114  {
115  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\"!";
116  }
117  accumulationTime_ = pset.get<double>("reporting_interval", 15.0);
118  }
119 
123  virtual ~MetricPlugin() = default;
124 
126  //
127  // Interface Functions: These should be reimplemented in plugin classes!
128  //
130 
134  virtual std::string getLibName() const { return "ERROR"; }
135 
136 protected:
145  virtual void sendMetric_(const std::string& name, const std::string& value, const std::string& unit) = 0;
146 
155  virtual void sendMetric_(const std::string& name, const int& value, const std::string& unit) = 0;
156 
165  virtual void sendMetric_(const std::string& name, const double& value, const std::string& unit) = 0;
166 
175  virtual void sendMetric_(const std::string& name, const float& value, const std::string& unit) = 0;
176 
185  virtual void sendMetric_(const std::string& name, const long unsigned int& value, const std::string& unit) = 0;
186 
192  virtual void startMetrics_() = 0;
193 
199  virtual void stopMetrics_() = 0;
200 
202  //
203  // Implementation Functions: These should be called from ARTDAQ code!
204  //
206 public:
211  void addMetricData(std::unique_ptr<MetricData> const& data)
212  {
213  if (data->Type == MetricType::StringMetric)
214  {
215  sendMetric_(data->Name, data->StringValue, data->Unit);
216  }
217  else
218  {
219  if (!metricRegistry_.count(data->Name))
220  {
221  metricRegistry_[data->Name] = *data;
222  }
223  metricData_[data->Name].push_back(*data);
224  //sendMetrics();
225  }
226  }
227 
236  void sendMetrics(bool forceSend = false,
237  std::chrono::steady_clock::time_point interval_end = std::chrono::steady_clock::now())
238  {
239  for (auto metric : metricData_)
240  {
241  auto* metricName = &metric.first;
242  if (readyToSend_(*metricName) || forceSend)
243  {
244  if (metricData_[*metricName].size() == 0 && metricRegistry_.count(*metricName))
245  {
246  sendZero_(metricRegistry_[*metricName]);
247  }
248  else if (metricData_[*metricName].size() > 0)
249  {
250  MetricData& data = metricData_[*metricName].front();
251  auto it = ++(metricData_[*metricName].begin());
252  while (it != metricData_[*metricName].end())
253  {
254  data.Add(*it);
255  it = metricData_[*metricName].erase(it);
256  }
257 
258  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
259  bool useSuffix = true;
260  if (modeSet.count() <= 1) useSuffix = false;
261 
262  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
263  {
264  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), data.Last, data.Unit, data.Type);
265  }
266  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
267  {
268  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), data.Value, data.Unit, data.Type);
269  }
270  if ((data.Mode & MetricMode::Average) != MetricMode::None)
271  {
272  double average = 0.0;
273  switch (data.Type)
274  {
276  average = data.Value.d / static_cast<double>(data.DataPointCount);
277  break;
279  average = data.Value.f / static_cast<double>(data.DataPointCount);
280  break;
282  average = data.Value.i / static_cast<double>(data.DataPointCount);
283  break;
285  average = data.Value.u / static_cast<double>(data.DataPointCount);
286  break;
287  default:
288  break;
289  }
290  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), average, data.Unit);
291  }
292  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
293  {
294  double duration = std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(
295  interval_end - interval_start_[*metricName])
296  .count();
297  double rate = 0.0;
298  switch (data.Type)
299  {
301  rate = data.Value.d / duration;
302  break;
304  rate = data.Value.f / duration;
305  break;
307  rate = data.Value.i / duration;
308  break;
310  rate = data.Value.u / duration;
311  break;
312  default:
313  break;
314  }
315  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), rate, data.Unit + "/s");
316  }
317  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
318  {
319  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), data.Min, data.Unit, data.Type);
320  }
321  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
322  {
323  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), data.Max, data.Unit, data.Type);
324  }
325 
326  metricData_[*metricName].clear();
327  }
328  interval_start_[*metricName] = interval_end;
329  }
330  }
331  }
332 
337 
342  void stopMetrics()
343  {
344  inhibit_ = true;
345  sendMetrics(true);
346  for (auto metric : metricRegistry_)
347  {
348  sendZero_(metric.second);
349  }
350  stopMetrics_();
351  inhibit_ = false;
352  }
353 
360  {
361  if (level > 63) level = 63;
362  if (level < 0) return true;
363  return level_mask_[level];
364  }
365 
366 protected:
367  fhicl::ParameterSet pset;
369  std::string app_name_;
370  bool inhibit_;
371  std::bitset<64> level_mask_;
372 
373 private:
374  std::unordered_map<std::string, std::list<MetricData>> metricData_;
375  std::unordered_map<std::string, MetricData> metricRegistry_;
376  std::unordered_map<std::string, std::chrono::steady_clock::time_point> lastSendTime_;
377  std::unordered_map<std::string, std::chrono::steady_clock::time_point> interval_start_;
378 
379  bool readyToSend_(std::string name)
380  {
381  auto now = std::chrono::steady_clock::now();
382  if (std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1>>>(now - lastSendTime_[name]).count() >=
384  {
385  lastSendTime_[name] = now;
386  return true;
387  }
388 
389  return false;
390  }
391 
392  void sendZero_(MetricData data)
393  {
394  std::bitset<32> modeSet(static_cast<uint32_t>(data.Mode));
395  bool useSuffix = true;
396  if (modeSet.count() <= 1) useSuffix = false;
397 
399  switch (data.Type)
400  {
402  zero.d = 0.0;
403  break;
405  zero.f = 0.0f;
406  break;
408  zero.i = 0;
409  break;
411  zero.u = 0;
412  break;
413  default:
414  break;
415  }
416 
417  if ((data.Mode & MetricMode::LastPoint) != MetricMode::None)
418  {
419  sendMetric_(data.Name + (useSuffix ? " - Last" : ""), zero, data.Unit, data.Type);
420  }
421  if ((data.Mode & MetricMode::Accumulate) != MetricMode::None)
422  {
423  sendMetric_(data.Name + (useSuffix ? " - Total" : ""), zero, data.Unit, data.Type);
424  }
425  if ((data.Mode & MetricMode::Average) != MetricMode::None)
426  {
427  sendMetric_(data.Name + (useSuffix ? " - Average" : ""), 0.0, data.Unit);
428  }
429  if ((data.Mode & MetricMode::Rate) != MetricMode::None)
430  {
431  sendMetric_(data.Name + (useSuffix ? " - Rate" : ""), 0.0, data.Unit + "/s");
432  }
433  if ((data.Mode & MetricMode::Minimum) != MetricMode::None)
434  {
435  sendMetric_(data.Name + (useSuffix ? " - Min" : ""), zero, data.Unit, data.Type);
436  }
437  if ((data.Mode & MetricMode::Maximum) != MetricMode::None)
438  {
439  sendMetric_(data.Name + (useSuffix ? " - Max" : ""), zero, data.Unit, data.Type);
440  }
441  }
442 
443  void sendMetric_(std::string name, MetricData::MetricDataValue data, std::string unit, MetricType type)
444  {
445  switch (type)
446  {
448  sendMetric_(name, data.d, unit);
449  break;
451  sendMetric_(name, data.f, unit);
452  break;
454  sendMetric_(name, data.i, unit);
455  break;
457  sendMetric_(name, data.u, unit);
458  break;
459  default:
460  break;
461  }
462  }
463 };
464 } //End namespace artdaq
465 
466 #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:29
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:38
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:46
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:59
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...
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:44
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:49
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:40
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:35
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:42
void addMetricData(std::unique_ptr< MetricData > const &data)
Send a metric value to the MetricPlugin.
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...