[EventAggregator] Encode PerDeviceCountObservations Add a method to encode PerDeviceCountObservations. Also adds a method to encode ReportParticipationObservations, which will be used by the PerDeviceCount report generator to compute the number of unique devices contributing to a given PerDeviceCount report over a given aggregation window. Change-Id: I1d1e34425bd732efd6cf6bbe8cc205fc6299d26a
diff --git a/logger/encoder.cc b/logger/encoder.cc index 12ae75b..ec43c6e 100644 --- a/logger/encoder.cc +++ b/logger/encoder.cc
@@ -272,6 +272,28 @@ return result; } +Encoder::Result Encoder::EncodePerDeviceCountObservation( + MetricRef metric, const ReportDefinition* report, uint32_t day_index, + const std::string component, uint32_t event_code, int64_t count, + uint32_t window_size) const { + auto result = EncodeIntegerEventObservation(metric, report, day_index, + event_code, component, count); + auto* integer_event_observation = result.observation->release_numeric_event(); + auto* count_observation = result.observation->mutable_per_device_count(); + count_observation->set_allocated_integer_event_obs(integer_event_observation); + count_observation->set_window_size(window_size); + return result; +} + +Encoder::Result Encoder::EncodeReportParticipationObservation( + MetricRef metric, const ReportDefinition* report, + uint32_t day_index) const { + auto result = MakeObservation(metric, report, day_index); + auto* observation = result.observation.get(); + observation->mutable_report_participation(); + return result; +} + Encoder::Result Encoder::EncodeNullBasicRapporObservation( MetricRef metric, const ReportDefinition* report, uint32_t day_index, uint32_t num_categories) const {
diff --git a/logger/encoder.h b/logger/encoder.h index 02de824..bc9538e 100644 --- a/logger/encoder.h +++ b/logger/encoder.h
@@ -280,6 +280,49 @@ bool was_active, uint32_t window_size) const; + // Encodes an Observation of type PerDeviceCountObservation. + // + // metric: Provides access to the names and IDs of the customer, project and + // metric associated with the Observation being encoded. + // + // report: The definition of the Report associated with the Observation being + // encoded. + // + // day_index: The day index associated with the Observation being encoded. + // This is the last day (inclusive) of the rolling window associated with this + // Observation. + // + // component: The component associated with this Observation. The hash of this + // value will populate the Observation's |component_name_hash| field. + // + // event_code: The event code of the Event associated with this Observation. + // + // count: This will populate the |value| field of the the + // IntegerEventObservation wrapped by the PerDeviceCountObservation. + // + // window_size: The number of days in the window associated with the + // Observation. This should be one of the window sizes specified in |report|, + // but it is the caller's responsibility to ensure this. + Result EncodePerDeviceCountObservation(MetricRef metric, + const ReportDefinition* report, + uint32_t day_index, + const std::string component, + uint32_t event_code, int64_t count, + uint32_t window_size) const; + + // Encodes an Observation of type ReportParticipationObservation. + // + // metric: Provides access to the names and IDs of the customer, project, and + // metric associated with the Observation being encoded. + // + // report: The definition of the Report associated with the Observation being + // encoded. + // + // day_index: The day index associated with the Observation being encoded. + Result EncodeReportParticipationObservation(MetricRef metric, + const ReportDefinition* report, + uint32_t day_index) const; + private: // Encodes a BasicRapporObservation for a given |metric|, |report|, and // |day_index| in which the data field is a Basic RAPPOR encoding of a vector
diff --git a/logger/encoder_test.cc b/logger/encoder_test.cc index 99d1f85..e9ecb27 100644 --- a/logger/encoder_test.cc +++ b/logger/encoder_test.cc
@@ -143,6 +143,23 @@ } } +metric { + metric_name: "ConnectionFailures" + metric_type: EVENT_COUNT + customer_id: 1 + project_id: 1 + id: 10 + reports: { + report_name: "ConnectionFailures_PerDeviceCount" + id: 101 + report_type: PER_DEVICE_COUNT_STATS + window_size: 7 + window_size: 30 + system_profile_field: OS + system_profile_field: ARCH + } +} + )"; bool PopulateMetricDefinitions(MetricDefinitions* metric_definitions) { @@ -449,5 +466,50 @@ .size()); } +TEST_F(EncoderTest, EncodePerDeviceCountObservation) { + const char kMetricName[] = "ConnectionFailures"; + const char kReportName[] = "ConnectionFailures_PerDeviceCount"; + const uint32_t kExpectedMetricId = 10; + const uint32_t kExpectedReportId = 101; + const uint32_t kDayIndex = 111; + const char kComponent[] = "Some Component"; + const uint32_t kEventCode = 0; + const int64_t kCount = 1728; + const uint32_t kWindowSize = 7; + auto pair = GetMetricAndReport(kMetricName, kReportName); + + auto result = encoder_->EncodePerDeviceCountObservation( + project_context_->RefMetric(pair.first), pair.second, kDayIndex, + kComponent, kEventCode, kCount, kWindowSize); + CheckResult(result, kExpectedMetricId, kExpectedReportId, kDayIndex); + // In the SystemProfile only the OS and ARCH should be set. + CheckSystemProfile(result, SystemProfile::FUCHSIA, SystemProfile::ARM_64, "", + ""); + ASSERT_TRUE(result.observation->has_per_device_count()); + EXPECT_EQ(kWindowSize, result.observation->per_device_count().window_size()); + ASSERT_TRUE(result.observation->per_device_count().has_integer_event_obs()); + auto integer_obs = result.observation->per_device_count().integer_event_obs(); + EXPECT_EQ(kEventCode, integer_obs.event_code()); + EXPECT_EQ(32u, integer_obs.component_name_hash().size()); + EXPECT_EQ(kCount, integer_obs.value()); +} + +TEST_F(EncoderTest, EncodeReportParticipationObservation) { + const char kMetricName[] = "ConnectionFailures"; + const char kReportName[] = "ConnectionFailures_PerDeviceCount"; + const uint32_t kExpectedMetricId = 10; + const uint32_t kExpectedReportId = 101; + const uint32_t kDayIndex = 111; + auto pair = GetMetricAndReport(kMetricName, kReportName); + + auto result = encoder_->EncodeReportParticipationObservation( + project_context_->RefMetric(pair.first), pair.second, kDayIndex); + CheckResult(result, kExpectedMetricId, kExpectedReportId, kDayIndex); + // In the SystemProfile only the OS and ARCH should be set. + CheckSystemProfile(result, SystemProfile::FUCHSIA, SystemProfile::ARM_64, "", + ""); + ASSERT_TRUE(result.observation->has_report_participation()); +} + } // namespace logger } // namespace cobalt
diff --git a/observation2.proto b/observation2.proto index 720f431..479b546 100644 --- a/observation2.proto +++ b/observation2.proto
@@ -47,7 +47,7 @@ UniqueActivesObservation unique_actives = 6; PerDeviceCountObservation per_device_count = 7; CustomObservation custom = 1000; - ReportDeviceCountObservation report_device_count = 10000; + ReportParticipationObservation report_participation = 10000; } // A quasi-unique identifier for this observation. This is randomly generated @@ -218,14 +218,20 @@ map<string, CustomDimensionValue> values = 1; } -// Observations of this type are produced by the EventAggregator and consumed -// by the ReportGenerator for reports of type PER_DEVICE_COUNT_STATS. For each -// report of that type, for each window size of the report, each device sends -// one Observation of this type per day. The ReportGenerator uses the number -// of such Observations to compute the number of devices in the fleet on which -// a given pair (component, event code) did *not* occur during the aggregation -// window. -message ReportDeviceCountObservation { - // The size of the aggregation window. - uint32 window_size = 1; -} \ No newline at end of file +// Observations of this type are used to signal that a given device was +// collecting data for a given report, over some window of time. This +// Observation type has no required fields, although the size of an +// aggregation window may be provided. Observations are generated and +// rate-limited by the EventAggregator. +// +// Current usage: +// ReportParticipationObservations are produced by the EventAggregator and +// consumed by the ReportGenerator for reports of type PER_DEVICE_COUNT_STATS. +// For each report of that type, each device sends one Observation of this type +// per day, along with a PerDeviceCountObservation for tuple (component, event +// code, window size) for which at least one event with that code occurred +// during the window of that size, with that component. The ReportGenerator +// uses the number of each of the two kinds of Observations to compute the +// number of devices in the fleet on which a given pair (component, event +// code) did *not* occur during the aggregation window. +message ReportParticipationObservation {}