Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component balm_metricsmanager
[Package balm]

Provide a manager for recording and publishing metric data. More...

Namespaces

namespace  balm

Detailed Description

Outline
Purpose:
Provide a manager for recording and publishing metric data.
Classes:
balm::MetricsManager manager for recording and publishing metric data
See also:
Component balm_publisher, Component balm_collectorrepository, Component balm_metricregistry, Component balm_metric, Component balm_defaultmetricsmanager, Component balm_publicationscheduler
Description:
This component provides a balm::MetricsManager class for managing the recording and publishing of metric data. The metrics manager retrieves balm::MetricRecords from both the collector repository it owns as well as any RecordsCollectionCallbacks registered with it. The metrics manager also provides methods to register balm::Publisher objects. The publish method collects metrics for a category (or set of categories) and then sends the collected metrics to the publishers associated with that category (or set of categories).
Note that a metric in this context is an event associated with a measured value. This component does not define what constitutes an event or what the associated value represents. A collected metric contains the count of event occurrences along with the total, minimum, and maximum aggregates of the measured values.
Alternative Systems for Telemetry:
Bloomberg software may alternatively use the GUTS telemetry API, which is integrated into Bloomberg infrastructure.
Thread Safety:
balm::MetricsManager is fully thread-safe, meaning that all non-creator operations on a given instance can be safely invoked simultaneously from multiple threads. To avoid synchronization problems with user functions invoked by balm::MetricsManager, special consideration must be taken when implementing these functions as specified below.
Registered Concrete balm::Publisher Implementations:
Concrete implementations of the balm::Publisher protocol (pure abstract base-class) registered with a balm::MetricsManager object must not call (either directly or indirectly) the publish method on the balm::MetricsManager object with which they are registered.
Registered RecordsCollectionCallback Implementations:
Implementations of balm::MetricsManager::RecordsCollectionCallback registered with a balm::MetricsManager will be invoked by a function holding mutex locks that provide synchronized access to data in that balm::MetricsManager. Therefore registered implementations of RecordsCollectionCallback must not make any re-entrant calls (either directly or indirectly) to member functions of the balm::MetricManager object with which they are registered.
Usage:
The following examples demonstrate how to configure, collect, and publish metrics.
Example 1: Initialize a balm::MetricsManager:
This example demonstrates how to create and configure a balm::MetricsManager that we will use to record and publish metric values. We first create a balm::MetricsManager object and a SimpleStreamPublisher object. Note that SimpleStreamPublisher is an example implementation of the balm::Publisher protocol defined in the balm_publisher component. In practice, clients typically use a standard publisher class (e.g., balm::StreamPublisher).
  int main(int argc, char *argv[]) {
  {

      // ...

      bslma::Allocator   *allocator = bslma::Default::allocator(0);
      balm::MetricsManager manager(allocator);

      bsl::shared_ptr<balm::Publisher> publisher(
                          new (*allocator) SimpleStreamPublisher(bsl::cout),
                          allocator);
      manager.addGeneralPublisher(publisher);

      // ...
Example 2: Recording Metric Values with balm::Collector:
This second example demonstrates using balm::Collector objects (obtained from a metrics manager's collector repository) to collect metrics related to a hypothetical EventHandler class. On construction, the event handler obtains references to balm::Collector objects from the metrics manager's collector repository. On each handled event, the EventHandler, updates its collectors with the appropriate metric values.
Note that the balm_metric component provides both classes and macros to reduce the code required for collecting metric values.
  class EventHandler {
      // Provide an example event-handling mechanism that records metrics
      // for (1) the size of the processed event messages and (2) the number
      // of failures, using 'balm::Collector' objects provided by a
      // 'balm::MetricsManager'.

      // PRIVATE DATA
      balm::Collector *d_eventMessageSizes_p;  // collect the message sizes

      balm::Collector *d_eventFailures_p;      // collect the number of
                                               // failures

  // ...

    public:
      // CREATORS
We obtain the addresses of the respective balm::Collector objects that we will use to collect metrics values from the metrics managers' collector repository. Note that, in practice, clients can use the balm::DefaultMetricManager (see balm_defaultmetricsmanager and balm_metric) rather than explicitly pass the address of a metrics manager.
      EventHandler(balm::MetricsManager *manager)
      : d_eventMessageSizes_p(
             manager->collectorRepository().getDefaultCollector(
                                              "MyCategory", "messageSizes"))
      , d_eventFailures_p(
             manager->collectorRepository().getDefaultCollector(
                                              "MyCategory", "eventFailures"))
      {}

      // MANIPULATORS
Then, when processing an "event", we update the balm::Collector objects with the appropriate metric values for the event.
      int handleEvent(int eventId, const bsl::string& eventMessage)
          // Process the event described by the specified 'eventId' and
          // 'eventMessage' .  Return 0 on success, and a non-zero value
          // if there was an error handling the event.
      {
         int returnCode = 0;
         d_eventMessageSizes_p->update(
                                   static_cast<double>(eventMessage.size()));

  // ...    (Process the event)
         (void)eventId;

         if (0 != returnCode) {
             d_eventFailures_p->update(1);
         }
         return returnCode;
      }

  // ...

  };
Example 3: Recording Metric Values with a Callback:
The metrics manager provides a facility to register a callback that will report metric values. A callback should be used if clients want to customize how a metric, or group of metrics, are recorded. In the following example, the EventHandlerWithCallback class maintains a metric for the average number of events per second that it reports through a balm::MetricsManager::MetricsCollectionCallback.
  // eventhandlerwithcallback.h

  class EventHandlerWithCallback {
      // Provide an example event handling mechanism that records a
      // metric for the number of events per second and reports that metric
      // using a 'balm::MetricsManager::RecordsCollectionCallback'.

      // PRIVATE DATA
      bsls::AtomicInt       d_numEvents;         // number of events

      bsls::TimeInterval    d_periodStart;       // start of the current
                                                 // period

      balm::MetricId        d_eventsPerSecId;    // identifies the events-
                                                 // per-second metric
      balm::MetricsManager::CallbackHandle
                            d_callbackHandle;    // identifies the callback

      balm::MetricsManager *d_metricsManager_p;  // metrics manager (held,
                                                 // but not owned)
   // ...

      // PRIVATE MANIPULATORS
      void collectMetricsCb(bsl::vector<balm::MetricRecord> *records,
                            bool                             resetFlag);
          // Append to the specified 'records' the aggregated values of the
          // metrics recorded by this event handler and, if 'resetFlag' is
          // 'true', reset those metric values to their default state.  Note
          // that this method is intended to be used as a callback, and is
          // consistent with the
          // 'balm::MetricsManager::RecordsCollectionCallback' function
          // prototype.

    public:
      // CREATORS
      EventHandlerWithCallback(balm::MetricsManager *manager,
                               bslma::Allocator    *basicAllocator = 0);
          // Initialize this object to use the specified 'manager' to record
          // and publish metrics.  Optionally specify a 'basicAllocator'
          // used to supply memory.  If 'basicAllocator' is 0, the currently
          // installed default allocator is used.

      ~EventHandlerWithCallback();
          // Destroy this event handler.

      // MANIPULATORS
      int handleEvent(int eventId, const bsl::string& eventMessage);
          // Process the event described by the specified 'eventId' and
          // 'eventMessage'.  Return 0 on success, and a non-zero value if
          // there was an error processing the event.

  // ...

  };
In the implementation of EventHandlerWithCallback below, we ensure that the callback is registered on construction and removed before the object is destroyed.
  // eventhandlerwithcallback.cpp

  namespace {

  const char *METRIC_CATEGORY = "MyCategory";

  }
The callback creates metric records and populates them with data collected by the event handler.
  // PRIVATE MANIPULATORS
  void EventHandlerWithCallback::collectMetricsCb(
                                  bsl::vector<balm::MetricRecord> *records,
                                  bool                             resetFlag)
  {
      int numEvents = resetFlag ?
                      (int)d_numEvents.swap(0) :
                      (int)d_numEvents;
      bsls::TimeInterval now         = bdlt::CurrentTime::now();
      bsls::TimeInterval elapsedTime = now - d_periodStart;
      d_periodStart = now;

      balm::MetricRecord record(d_eventsPerSecId);
      record.count() = 1;
      record.total() = numEvents / elapsedTime.totalSecondsAsDouble();

      records->push_back(record);
  }
In the constructor, we initialize a metric id from the specified manager object's metric registry. We will also register the collection callback (collectMetricsCb) with the supplied manager.
  // CREATORS
  EventHandlerWithCallback::EventHandlerWithCallback(
                                        balm::MetricsManager *manager,
                                        bslma::Allocator     *basicAllocator)
  : d_numEvents(0)
  , d_periodStart(bdlt::CurrentTime::now())
  , d_eventsPerSecId()
  , d_callbackHandle(balm::MetricsManager::e_INVALID_HANDLE)
  , d_metricsManager_p(manager)
  {
      (void)basicAllocator;

      d_eventsPerSecId = d_metricsManager_p->metricRegistry().getId(
                                        METRIC_CATEGORY, "eventsPerSecond");
We now register the callback function collectMetricsCb with the metrics manager. We use bdlf::BindUtil to bind the member function to a bsl::function matching the balm::MetricsManager::RecordsCollectionCallback function prototype. The private data member d_callbackHandle is used to store the balm::MetricsManager::CallbackHandle returned for the registered callback; we will use this handle later to remove the callback from the metrics manager.
      d_callbackHandle =
         d_metricsManager_p->registerCollectionCallback(
            METRIC_CATEGORY,
            bdlf::BindUtil::bindA(basicAllocator,
                                 &EventHandlerWithCallback::collectMetricsCb,
                                 this,
                                 bdlf::PlaceHolders::_1,
                                 bdlf::PlaceHolders::_2));
  }
In the destructor we use the balm::MetricsManager::CallbackHandle, stored in d_callbackHandle, to remove the callback from the metrics manager. This prevents the metrics manager from invoking the callback method on an object that has already been destroyed.
  EventHandlerWithCallback::~EventHandlerWithCallback()
  {
      int rc =
             d_metricsManager_p->removeCollectionCallback(d_callbackHandle);
      assert(0 == rc);
  }

  // MANIPULATORS
  int EventHandlerWithCallback::handleEvent(int                eventId,
                                            const bsl::string& eventMessage)
  {
We increment the atomic integer d_numEvents to keep track of the number events handled by the handleEvent method. If collecting a metric is expensive (e.g., metrics requiring a system call to collect), clients should test whether the metric is enabled before performing the collection operation.
      // We don't test 'd_eventsPerSecId.category()->enabled())' before
      // incrementing 'd_numEvents' because, in this instance, it will not
      // improve performance.
      ++d_numEvents;

  // ...    (Process the event)
      (void)eventId;
      (void)eventMessage;

      return 0;
   }

  // ...
Example 4: Publishing a Metric:
The metrics manager provides a publish operation to publish metrics for a category or set of categories. In this example we will use the metrics manager's publishAll operation to publish all the metrics managed by the metrics manager. We will record metrics for "MyCategory" using instances of the EventHandler and EventHandlerWithCallback classes (defined above). This example assumes that an instance, manager, of the balm::MetricsManager class has been initialized as in example 1. Note that, in practice the publish operation is normally tied to a scheduling mechanism (e.g., see balm_publicationscheduler).
  EventHandler             eventHandler(&manager);
  EventHandlerWithCallback eventHandlerWithCallback(&manager);

  eventHandler.handleEvent(0, "A 28 character event message");
  eventHandlerWithCallback.handleEvent(1, "A different event message");
  manager.publishAll();

  eventHandler.handleEvent(0, "A 28 character event message");
  eventHandler.handleEvent(0, "A 28 character event message");
  eventHandlerWithCallback.handleEvent(1, "A different event message");
  manager.publishAll();
Executing the example should result in two samples being published to standard output. Each sample should contain 3 metrics belonging to the metric category "MyCategory". The metric "eventsPerSecond" is collected by the EventHandlerWithCallback, while "messageSizes", and "eventFailures" (both collected by EventHandler).
 09FEB2009_18:52:51.093+0000 3 Records
         Elapsed Time: 0.001358s
         MyCategory.eventsPerSecond [count = 1, total = 2267.57, ... ]
         MyCategory.messageSizes [count = 1, total = 28, min = 28, max = 28]
         MyCategory.eventFailures [count = 0, total = 0, ... ]
 09FEB2009_18:52:51.096+0000 3 Records
         Elapsed Time: 0.002217s
         MyCategory.eventsPerSecond [count = 1, total = 453.721, ... ]
         MyCategory.messageSizes [count = 2, total = 56, min = 28, max = 28]
         MyCategory.eventFailures [count = 0, total = 0, ... ]