// Copyright 2017 Google Inc. All Rights Reserved. // // This file contains the declarations for the EventMetric class and related // types. #ifndef WVCDM_METRICS_EVENT_METRIC_H_ #define WVCDM_METRICS_EVENT_METRIC_H_ #include #include #include #include #include #include #include #include #include #include "lock.h" #include "distribution.h" #include "metric_publisher.h" namespace wvcdm { namespace metrics { class EventMetricTest; // This base class provides the common defintion used by all templated // instances of EventMetric. class BaseEventMetric : public MetricPublisher { public: // Publish metric values to the MetricNotification. |subscriber| must // not be null and is owned by the caller. virtual void Publish(MetricNotification* subscriber); protected: // Instantiates a BaseEventMetric. BaseEventMetric(const std::string& metric_name) : metric_name_(metric_name) {} virtual ~BaseEventMetric(); // Record will look for an existing instance of the Distribution identified // by the field_names_values string and update it. If the instance does // not exist, this will create it. void Record(const std::string& field_names_values, double value); private: friend class EventMetricTest; const std::string metric_name_; // value_map_ contains a mapping from the field name/value pairs to the // distribution instance which holds the metric information (min, max, sum, // etc.). std::map value_map_; /* * This locks the internal state of the event metric to ensure safety across * multiple threads preventing the caller from worrying about locking. */ Lock internal_lock_; }; // This is a placeholder type for unused type parameters. struct Unused { // Required for compilation. Should never be used. inline friend std::ostream& operator<< (std::ostream& out, const Unused&) { return out; } }; // This class converts the size_t value into the highest power of two // below the value. E.g. for 7, the value is 4. For 11, the value is 8. // This class is intended to simplify the use of EventMetric Fields that may // have many possible values, but we want to bucket them into a small set of // numbers (32 or 64). class Pow2Bucket { public: explicit Pow2Bucket(size_t value) : value_(GetLowerBucket(value)) {} Pow2Bucket(const Pow2Bucket& value) : value_(value.value_) {} // Support for converting to string. friend std::ostream& operator<<(std::ostream& os, const Pow2Bucket& log) { return os << log.value_; } private: inline size_t GetLowerBucket(size_t value) { if (!value) { return 0; } size_t log = 0; while (value) { log++; value >>= 1; } return 1 << (log - 1); } size_t value_; }; // The EventMetric class is used to capture statistics about an event such as // a method call. EventMetric keeps track of the count, mean, min, max and // variance for the value being measured. For example, we may want to track the // latency of a particular operation. Each time the operation is run, the time // is calculated and provided to the EventMetric by using the // EventMetric::Record method to capture and maintain statistics about the // latency (max, min, count, mean, variance). // // The EventMetric class supports the ability to breakdown the statistics based // on certain "field" values. For example, if a particular operation can run in // one of two modes, it's useful to track the latency of the operation in each // mode separately. You can use Fields to define how to breakdown the // statistics. Each Field is a separate dimention. The statistics for each // combination of field values are tracked independently. // // Example usage: // // EventMetric my_metric("multiple/fields/metric", // "request_type", "error_code", // Field names); // // my_metric.Record(1, 7, 23); // (latency value, request type, error code). // // A MetricNotification may be used to allow the EventMetric instance to // notify that an update occurred and provide the updated value. // // class MyMetricNotification : public MetricNotification { // // Add implementation here. // } // // MyMetricNotification notification; // my_metric.Publish(notification); template class EventMetric : public BaseEventMetric { public: // Create an EventMetric instance with the name |metric_name|. EventMetric(const std::string& metric_name); // Overloaded constructors with variable field name arguments. The number // of |field_name| arguments must match the number of used Field type // arguments. EventMetric(const std::string& metric_name, const char* field_name); EventMetric(const std::string& metric_name, const char* field_name1, const char* field_name2); EventMetric(const std::string& metric_name, const char* field_name1, const char* field_name2, const char* field_name3); EventMetric(const std::string& metric_name, const char* field_name1, const char* field_name2, const char* field_name3, const char* field_name4); // Record will update the statistics of the EventMetric broken down by the // given field values. void Record(double value, F1 field1 = Unused(), F2 field2 = Unused(), F3 field3 = Unused(), F4 field4 = Unused()); private: friend class EventMetricTest; std::vector field_names_; }; // This is an internal namespace for helper functions only. namespace impl { // This method formats the collection of field name/value pairs. // The format of the string is: // // [{field:value[&field:value]*}] // // If there are no pairs, returns a blank string. // // TODO(blueeyes): Add a check for the count of field_names and the count // of field values (check that the fields are not type Unused). template std::string MakeFieldNameString(const std::vector& field_names, const F1 field1, const F2 field2, const F3 field3, const F4 field4) { std::stringstream field_name_and_values; std::vector::const_iterator field_name_iterator = field_names.begin(); if (field_name_iterator == field_names.end()) { return field_name_and_values.str(); } // There is at least one name/value pair. prepend open brace. field_name_and_values << "{"; field_name_and_values << *field_name_iterator << ':' << field1; if (++field_name_iterator == field_names.end()) { field_name_and_values << "}"; return field_name_and_values.str(); } field_name_and_values << '&' << *field_name_iterator << ':' << field2; if (++field_name_iterator == field_names.end()) { field_name_and_values << "}"; return field_name_and_values.str(); } field_name_and_values << '&' << *field_name_iterator << ':' << field3; if (++field_name_iterator == field_names.end()) { field_name_and_values << "}"; return field_name_and_values.str(); } field_name_and_values << '&' << *field_name_iterator << ':' << field4; field_name_and_values << "}"; return field_name_and_values.str(); } // This specialization of the helper method is a shortcut for EventMetric // instances with no fields. template<> inline std::string MakeFieldNameString( const std::vector& field_names, const Unused unused1, const Unused unused2, const Unused unused3, const Unused unused4) { return ""; } // This helper function appends the field names to a vector of strings. inline void AppendFieldNames(std::vector* field_name_vector, int field_count, ...) { va_list field_names; va_start(field_names, field_count); for (int x = 0; x < field_count; x++) { field_name_vector->push_back(va_arg(field_names, const char*)); } va_end(field_names); } } // namespace impl // Overloaded template constructor implementations for EventMetric. template EventMetric::EventMetric( const std::string& metric_name) : BaseEventMetric(metric_name) {} template EventMetric::EventMetric( const std::string& metric_name, const char* field_name) : BaseEventMetric(metric_name) { impl::AppendFieldNames(&field_names_, 1, field_name); } template EventMetric::EventMetric( const std::string& metric_name, const char* field_name1, const char* field_name2) : BaseEventMetric(metric_name) { impl::AppendFieldNames(&field_names_, 2, field_name1, field_name2); } template EventMetric::EventMetric( const std::string& metric_name, const char* field_name1, const char* field_name2, const char* field_name3) : BaseEventMetric(metric_name) { impl::AppendFieldNames(&field_names_, 3, field_name1, field_name2, field_name3); } template EventMetric::EventMetric( const std::string& metric_name, const char* field_name1, const char* field_name2, const char* field_name3, const char* field_name4) : BaseEventMetric(metric_name) { impl::AppendFieldNames(&field_names_, 4, field_name1, field_name2, field_name3, field_name4); } template void EventMetric::Record( double value, F1 field1, F2 field2, F3 field3, F4 field4) { std::string field_name_values = impl::MakeFieldNameString(field_names_, field1, field2, field3, field4); BaseEventMetric::Record(field_name_values, value); } } // namespace metrics } // namespace wvcdm #endif // WVCDM_METRICS_EVENT_METRIC_H_