// balm_metric.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALM_METRIC #define INCLUDED_BALM_METRIC #include <bsls_ident.h> BSLS_IDENT("$Id: balm_metric.h,v 1.7 2008/04/17 21:22:34 hversche Exp $") //@PURPOSE: Provide helper classes for recording metric values. // //@CLASSES: // balm::Metric: container for recording metric values // //@SEE_ALSO: balm_metricsmanager, balm_defaultmetricsmanager, // balm_integermetric, balm_metrics // //@DESCRIPTION: This component provides a class, 'balm::Metric', to simplify // the process of collecting metrics. A metric records the number of times an // event occurs, as well as an associated measurement value. This component // does *not* define what constitutes an event or what the associated value // represents. A metric maintains a count of event occurrences and the // aggregated minimum, maximum, and total of the measured metric-event values. // // The 'balm::Metric' class, defined in this component, has in-core value // semantics. Each 'balm::Metric' object holds a pointer to a // 'balm::Collector' that collects values for a particular metric. The // 'balm::Collector' is either supplied at construction, or else obtained from // a 'balm::MetricsManager' object's 'balm::CollectorRepository'. If the // supplied 'balm::MetricsManager' is 0, the metric will use the default // metrics manager instance ('balm::DefaultMetricsManager::instance()'), if // initialized; otherwise, the metric is placed in the inactive state (i.e., // 'isActive()' is 'false') and operations that would otherwise update the // metric will have no effect. // ///Alternative Systems for Telemetry ///--------------------------------- // Bloomberg software may alternatively use the GUTS telemetry API, which is // integrated into Bloomberg infrastructure. // ///Choosing between 'balm::Metric' and Macros ///------------------------------------------ // The 'balm::Metric' class and the macros defined in 'balm_metrics' provide // the same basic functionality. Clients may find 'balm::Metric' objects // better suited to collecting metrics associated with a particular instance // of a stateful object, while macros are better suited to collecting metrics // associated with a particular code path (rather than an object instance). // In most instances, however, choosing between the two is simply a matter of // taste. // ///Thread Safety ///------------- // 'balm::Metric' is fully *thread-safe*, meaning that all non-creator // operations on a given instance can be safely invoked simultaneously from // multiple threads. // ///Usage ///----- // The following examples demonstrate how to configure, collect, and publish // metrics. // ///Example 1: Metric Collection With 'balm::Metric' /// - - - - - - - - - - - - - - - - - - - - - - - - // We can use 'balm::Metric' objects to record metric values. In this // example we implement a hypothetical event manager object. We use // 'balm::Metric' objects to record metrics for the size of the request, the // elapsed processing time, and the number of failures. //.. // class EventManager { // // // DATA // balm::Metric d_messageSize; // balm::Metric d_elapsedTime; // balm::Metric d_failedRequests; // // public: // // // CREATORS // EventManager() // : d_messageSize("MyCategory", "EventManager/size") // , d_elapsedTime("MyCategory", "EventManager/elapsedTime") // , d_failedRequests("MyCategory", "EventManager/failedRequests") // {} // // // 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 handling the event. // { // (void)eventId; // // int returnCode = 0; // // d_messageSize.update(static_cast<double>(eventMessage.size())); // bsls::Stopwatch stopwatch; // stopwatch.start(); // // // Process 'data' ('returnCode' may change). // // if (0 != returnCode) { // d_failedRequests.increment(); // } // // d_elapsedTime.update(stopwatch.elapsedTime()); // return returnCode; // } // // // ... // }; //.. // ///Example 2: Create and Access the Default 'balm::MetricsManager' Instance /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // This example demonstrates how to create the default 'balm::MetricManager' // instance and perform a trivial configuration. // // First we create a 'balm::DefaultMetricsManagerScopedGuard', which manages // the lifetime of the default metrics manager instance. At construction, we // provide the scoped guard an output stream ('stdout') that it will publish // metrics to. Note that the default metrics manager is intended to be created // and destroyed by the *owner* of 'main'. An instance of the manager should // be created during the initialization of an application (while the task has a // single thread) and destroyed just prior to termination (when there is // similarly a single thread). //.. // int main(int argc, char *argv[]) // { // // ... // // balm::DefaultMetricsManagerScopedGuard managerGuard(bsl::cout); //.. // Once the default instance has been created, it can be accessed using the // 'instance' operation. //.. // balm::MetricsManager *manager = // balm::DefaultMetricsManager::instance(); // assert(0 != manager); //.. // Note that the default metrics manager will be released when 'managerGuard' // exits this scoped and is destroyed. Clients that choose to explicitly call // 'balm::DefaultMetricsManager::create' must also explicitly call // 'balm::DefaultMetricsManager::release()'. // // Now that we have created a 'balm::MetricsManager' instance, we can use the // instance to publish metrics collected using the event manager described in // {Example 1}: //.. // EventManager eventManager; // // eventManager.handleEvent(0, "ab"); // eventManager.handleEvent(0, "abc"); // eventManager.handleEvent(0, "abc"); // eventManager.handleEvent(0, "abdef"); // // manager->publishAll(); // // eventManager.handleEvent(0, "ab"); // eventManager.handleEvent(0, "abc"); // eventManager.handleEvent(0, "abc"); // eventManager.handleEvent(0, "abdef"); // // eventManager.handleEvent(0, "a"); // eventManager.handleEvent(0, "abc"); // eventManager.handleEvent(0, "abc"); // eventManager.handleEvent(0, "abdefg"); // // manager->publishAll(); // } //.. #include <balscm_version.h> #include <balm_collector.h> #include <balm_collectorrepository.h> #include <balm_defaultmetricsmanager.h> #include <balm_metricid.h> #include <balm_metricsmanager.h> #include <balm_publicationtype.h> #include <bsls_atomic.h> namespace BloombergLP { namespace balm { // ============ // class Metric // ============ class Metric { // This class provides an in-core value semantic type for recording and // aggregating the values of a metric. The value of a 'Metric' object is // characterized by the 'Collector' object it uses to collect metric-event // values. Each instance of this class establishes (at construction) an // association to a 'Collector' object to which the metric delegates. A // 'Metric' value is constant after construction (i.e., it does not support // assignment or provide manipulators that modify its collector value) so // that synchronization primitives are not required to protect its data // members. Note that if a collector or metrics manager is not supplied at // construction, and if the default metrics manager has not been // instantiated, then the metric will be inactive (i.e., // 'isActive() == false') and the manipulator methods of the metric object // will have no effect. // DATA Collector *d_collector_p; // collected metric data (held, not owned); may // be 0, but cannot be invalid const bsls::AtomicInt *d_isEnabled_p; // memo for isActive() // NOT IMPLEMENTED Metric& operator=(Metric& ); public: // CLASS METHODS static Collector *lookupCollector(const char *category, const char *name, MetricsManager *manager = 0); // Return a collector corresponding to the specified metric 'category' // and 'name'. Optionally specify a metrics 'manager' used to provide // the collector. If 'manager' is 0, use the default metrics manager // if initialized; if 'manager' is 0 and the default metrics manager // has not been initialized, return 0. The behavior is undefined // unless 'category' and 'name' are null-terminated. static Collector *lookupCollector(const MetricId& metricId, MetricsManager *manager = 0); // Return a collector for the specified 'metricId'. Optionally specify // a metrics 'manager' used to provide the collector. If 'manager' is // 0, use the default metrics manager, if initialized; if 'manager' is // 0 and the default metrics manager has not been initialized, return // 0. The behavior is undefined unless 'metricId' is a valid metric // id supplied by the 'MetricsRegistry' of the indicated metrics // manager. // CREATORS Metric(const char *category, const char *name, MetricsManager *manager = 0); // Create a metric object to collect values for the metric identified // by the specified null-terminated strings 'category' and 'name'. // Optionally specify a metrics 'manager' used to provide a collector // for the indicated metric. If 'manager' is 0, use the default // metrics manager, if initialized; if 'manager' is 0 and the default // metrics manager has not been initialized, place this metric object // in the inactive state (i.e., 'isActive()' is 'false') in which case // instance methods that would otherwise update the metric will have no // effect. explicit Metric(const MetricId& metricId, MetricsManager *manager = 0); // Create a metric object to collect values for the specified // 'metricId'. Optionally specify a metrics 'manager' used to provide // a collector for 'metricId'. If 'manager' is 0, use the default // metrics manager, if initialized; if 'manager' is 0 and the default // metrics manager has not been initialized, place this metric object // in the inactive state (i.e., 'isActive()' is 'false') in which case // instance methods that would otherwise update the metric will have // no effect. The behavior is undefined unless 'metricId' is a valid // id returned by the 'MetricRepository' object owned by the indicated // metrics manager. explicit Metric(Collector *collector); // Create a metric object to collect values for the metric implied by // the specified 'collector' (i.e., 'collector->metricId()'). The // behavior is undefined unless 'collector' is a valid address of a // 'Collector' object and 'collector' has a valid id (i.e., // 'collector->metricId().isValid()'). Metric(const Metric& original); // Create a metric object that will record values for the same metric // (i.e., using the same 'Collector' object) as the specified // 'original' metric. If the 'original' metric is inactive (i.e., // 'isActive()' is 'false'), then this metric will be similarly // inactive. // ~Metric(); // Destroy this metric. Note that this trivial destructor is generated // by the compiler. // MANIPULATORS void increment(); // Increase the count and total of this metric by 1; if 1.0 is less // than the current minimum recorded value of the metric, set the new // minimum value to be 1.0; if 1.0 is greater than the current maximum // recorded value, set the new maximum value to be 1.0. If, however, // this metric is not active (i.e., 'isActive()' is 'false') then this // method has no effect. Note that this method is functionally // equivalent to 'update(1)'. void update(double value); // Increase the event count by 1 and add the specified 'value' to the // total recorded value; if 'value' is less than the current minimum // recorded value of the metric, set the new minimum value to be // 'value'; if 'value' is greater than the current maximum recorded // value, set the new maximum value to be 'value'. If, however, this // metric is inactive (i.e., 'isActive()' is 'false'), then this method // has no effect. void accumulateCountTotalMinMax(int count, double total, double min, double max); // Increase the event count by the specified 'count' and add the // specified 'total' to the accumulated total; if the specified 'min' // is less than the current minimum recorded value of the metric, set // the new minimum value to be 'min'; if the specified 'max' is // greater than the current maximum recorded value, set the new // maximum value to be 'max'. If, however, this metric is inactive // (i.e., 'isActive()' is 'false'), then this method has no effect. Collector *collector(); // Return the address of the modifiable collector for this metric. // ACCESSORS const Collector *collector() const; // Return the address of the non-modifiable collector for this metric. MetricId metricId() const; // Return a 'MetricId' object identifying this metric. If this metric // was not supplied a valid collector at construction then the returned // id will be invalid (i.e., 'metricId().isValid() == false'). bool isActive() const; // Return 'true' if this metric will actively record metrics, and // 'false' otherwise. If the returned value is 'false', the // manipulator operations will have no effect. A metric will be // inactive if either (1) it was not initialized with a valid metric // identifier or (2) the associated metric category has been disabled // (see the 'MetricsManager' method 'setCategoryEnabled'). Note that // invoking this method is logically equivalent to the expression // '0 != collector() && metricId().category()->enabled()'. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // FREE OPERATORS inline bool operator==(const Metric& lhs, const Metric& rhs); // Return 'true' if the specified 'lhs' and 'rhs' metrics have the same // value and 'false' otherwise. Two metrics have the same value if they // record measurements using the same collector object or if they both // have a null collector (i.e., 'collector()' is 0). inline bool operator!=(const Metric& lhs, const Metric& rhs); // Return 'true' if the specified 'lhs' and 'rhs' metrics do not have the // same value and 'false' otherwise. Two metrics do not have the same // value if they record measurements using different collector objects or // if one, but not both, have a null collector (i.e., 'collector()' is 0). // ===================== // class Metric_MacroImp // ===================== struct Metric_MacroImp { // This structure provides a namespace for functions used to implement the // macros defined by this component. // // This is an implementation type of this component and *must* *not* be // used by clients of the 'balm' package. // CLASS METHODS static void getCollector(Collector **collector, CategoryHolder *holder, const char *category, const char *metric); // Load the specified 'collector' with the address of the default // collector (from the default metrics manager) for the specified // 'metric' identified by the specified 'category' and 'name', and // register the specified 'holder' for 'category'. Note that // '*collector' must be assigned *before* registering 'holder' to // ensure that the macros always have a valid 'collector' when // 'holder->enabled()' is 'true'. static void getCollector(Collector **collector, CategoryHolder *holder, const char *category, const char *metric, PublicationType::Value preferredPublicationType); // Load the specified 'collector' with the address of the default // collector (from the default metrics manager) for the specified // 'metric' identified by the specified 'category' and 'name', register // the specified 'holder' for 'category', and set the identified // metric's preferred publication type to the specified // 'preferredPublicationType'. Note that '*collector' must be // assigned before 'holder' to ensure that the macros always have a // valid 'collector' when 'holder->enabled()' is 'true'. }; // ============================================================================ // INLINE FUNCTION DEFINITIONS // ============================================================================ // ------------ // class Metric // ------------ // CLASS METHODS inline Collector *Metric::lookupCollector(const char *category, const char *name, MetricsManager *manager) { manager = DefaultMetricsManager::manager(manager); return manager ? manager->collectorRepository().getDefaultCollector(category, name) : 0; } inline Collector *Metric::lookupCollector(const MetricId& metricId, MetricsManager *manager) { manager = DefaultMetricsManager::manager(manager); return manager ? manager->collectorRepository().getDefaultCollector(metricId) : 0; } // CREATORS inline Metric::Metric(const char *category, const char *name, MetricsManager *manager) : d_collector_p(lookupCollector(category, name, manager)) { d_isEnabled_p = (d_collector_p ? &d_collector_p->metricId().category()->isEnabledRaw() : 0); } inline Metric::Metric(const MetricId& metricId, MetricsManager *manager) : d_collector_p(lookupCollector(metricId, manager)) { d_isEnabled_p = (d_collector_p ? &d_collector_p->metricId().category()->isEnabledRaw() : 0); } inline Metric::Metric(Collector *collector) : d_collector_p(collector) { d_isEnabled_p = &d_collector_p->metricId().category()->isEnabledRaw(); } inline Metric::Metric(const Metric& original) : d_collector_p(original.d_collector_p) , d_isEnabled_p(original.d_isEnabled_p) { } // MANIPULATORS inline void Metric::increment() { if (isActive()) { d_collector_p->update(1.0); } } inline void Metric::update(double value) { if (isActive()) { d_collector_p->update(value); } } inline void Metric::accumulateCountTotalMinMax(int count, double total, double min, double max) { if (isActive()) { d_collector_p->accumulateCountTotalMinMax(count, total, min, max); } } inline Collector *Metric::collector() { return d_collector_p; } // ACCESSORS inline const Collector *Metric::collector() const { return d_collector_p; } inline MetricId Metric::metricId() const { return d_collector_p ? d_collector_p->metricId() : MetricId(); } inline bool Metric::isActive() const { return d_isEnabled_p && d_isEnabled_p->loadRelaxed(); } } // close package namespace // FREE OPERATORS inline bool balm::operator==(const Metric& lhs, const Metric& rhs) { return lhs.collector() == rhs.collector(); } inline bool balm::operator!=(const Metric& lhs, const Metric& rhs) { return !(lhs == rhs); } } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------