[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 {}