Skip to content

Commit

Permalink
Add metric configuration and alternative metric attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
DJAndries committed Dec 13, 2024
1 parent 10f779a commit aa6ae78
Show file tree
Hide file tree
Showing 19 changed files with 1,002 additions and 423 deletions.
16 changes: 16 additions & 0 deletions components/brave_stats/browser/brave_stats_updater_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ std::string GetPlatformIdentifier() {
#endif
}

std::string GetGeneralPlatformIdentifier() {
#if BUILDFLAG(IS_WIN)
return "windows";
#elif BUILDFLAG(IS_MAC)
return "macos";
#elif BUILDFLAG(IS_LINUX)
return "linux";
#elif BUILDFLAG(IS_IOS)
return "ios";
#elif BUILDFLAG(IS_ANDROID)
return "android";
#else
return std::string();
#endif
}

int GetIsoWeekNumber(const base::Time& time) {
char buffer[24];
time_t rawtime = time.ToTimeT();
Expand Down
2 changes: 2 additions & 0 deletions components/brave_stats/browser/brave_stats_updater_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ std::string GetDateAsYMD(const base::Time& time);

std::string GetPlatformIdentifier();

std::string GetGeneralPlatformIdentifier();

int GetIsoWeekNumber(const base::Time& time);

base::Time GetLastMondayTime(const base::Time& time);
Expand Down
4 changes: 4 additions & 0 deletions components/p3a/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static_library("p3a") {
"histograms_braveizer.h",
"message_manager.cc",
"message_manager.h",
"metric_config.h",
"metric_log_store.cc",
"metric_log_store.h",
"metric_log_type.cc",
Expand All @@ -44,6 +45,8 @@ static_library("p3a") {
"p3a_service.cc",
"p3a_service.h",
"pref_names.h",
"region.cc",
"region.h",
"rotation_scheduler.cc",
"rotation_scheduler.h",
"scheduler.cc",
Expand Down Expand Up @@ -100,6 +103,7 @@ source_set("unit_tests") {
"nitro_utils/cose_unittest.cc",
"p2a_protocols_unittest.cc",
"p3a_service_unittest.cc",
"region_unittest.cc",
"rotation_scheduler_unittest.cc",
"scheduler_unittest.cc",
"star_randomness_test_util.cc",
Expand Down
61 changes: 53 additions & 8 deletions components/p3a/constellation_helper_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "base/strings/string_util.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "brave/components/p3a/metric_config.h"
#include "brave/components/p3a/metric_log_type.h"
#include "brave/components/p3a/p3a_config.h"
#include "brave/components/p3a/p3a_message.h"
Expand Down Expand Up @@ -255,8 +256,8 @@ TEST_F(P3AConstellationHelperTest, GenerateBasicMessage) {
helper_->StartMessagePreparation(
kTestHistogramName, log_type,
GenerateP3AConstellationMessage(kTestHistogramName, test_epoch,
meta_info, kP3AUploadType, false,
false),
meta_info, kP3AUploadType,
std::nullopt),
false);
task_environment_.RunUntilIdle();

Expand All @@ -276,7 +277,7 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
meta_info.Init(&local_state_, "release", "2022-01-01");

std::string message_with_no_refcode = GenerateP3AConstellationMessage(
kTestHistogramName, 0, meta_info, kP3AUploadType, false, false);
kTestHistogramName, 0, meta_info, kP3AUploadType, std::nullopt);
std::vector<std::string> no_refcode_layers = base::SplitString(
message_with_no_refcode, kP3AMessageConstellationLayerSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
Expand All @@ -286,7 +287,8 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
EXPECT_FALSE(no_refcode_layers.at(7).starts_with("ref"));

std::string message_with_refcode = GenerateP3AConstellationMessage(
kTestHistogramName, 0, meta_info, kP3AUploadType, true, false);
kTestHistogramName, 0, meta_info, kP3AUploadType,
MetricConfig{.append_attributes = {MetricAttribute::kRef}});
std::vector<std::string> refcode_layers = base::SplitString(
message_with_refcode, kP3AMessageConstellationLayerSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
Expand All @@ -300,7 +302,8 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
meta_info.Init(&local_state_, "release", "2022-01-01");

message_with_refcode = GenerateP3AConstellationMessage(
kTestHistogramName, 0, meta_info, kP3AUploadType, true, false);
kTestHistogramName, 0, meta_info, kP3AUploadType,
MetricConfig{.append_attributes = {MetricAttribute::kRef}});
refcode_layers = base::SplitString(message_with_refcode,
kP3AMessageConstellationLayerSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
Expand All @@ -313,7 +316,8 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
meta_info.Init(&local_state_, "release", "2022-01-01");

message_with_refcode = GenerateP3AConstellationMessage(
kTestHistogramName, 0, meta_info, kP3AUploadType, true, false);
kTestHistogramName, 0, meta_info, kP3AUploadType,
MetricConfig{.append_attributes = {MetricAttribute::kRef}});
refcode_layers = base::SplitString(message_with_refcode,
kP3AMessageConstellationLayerSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
Expand All @@ -324,12 +328,53 @@ TEST_F(P3AConstellationHelperTest, IncludeRefcode) {
#endif // !BUILDFLAG(IS_IOS)
}

TEST_F(P3AConstellationHelperTest, CustomAttributes) {
MessageMetainfo meta_info;
meta_info.Init(&local_state_, "release", "2022-01-01");

// Test with custom attributes list
MetricConfig config{.attributes = MetricAttributes{
MetricAttribute::kAnswerIndex,
MetricAttribute::kVersion,
MetricAttribute::kPlatform,
MetricAttribute::kGeneralPlatform,
MetricAttribute::kChannel,
MetricAttribute::kRegion,
MetricAttribute::kSubregion,
}};

std::string message = GenerateP3AConstellationMessage(
kTestHistogramName, 7, meta_info, kP3AUploadType, config);

std::vector<std::string> layers =
base::SplitString(message, kP3AMessageConstellationLayerSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);

// Verify number of layers matches number of non-null attributes
EXPECT_EQ(layers.size(), 8U);

// Verify each layer's content
EXPECT_EQ(layers[0], base::StrCat({"metric_name|", kTestHistogramName}));
EXPECT_EQ(layers[1], "metric_value|7");
EXPECT_EQ(layers[2], "version|" + meta_info.version());
EXPECT_EQ(layers[3], "platform|" + meta_info.platform());
EXPECT_EQ(layers[4], "general_platform|" + meta_info.general_platform());
EXPECT_EQ(layers[5], "channel|release");
EXPECT_EQ(layers[6],
base::StrCat({"region|", meta_info.region_identifiers().region}));
EXPECT_EQ(
layers[7],
base::StrCat({"subregion|", meta_info.region_identifiers().sub_region}));
}

TEST_F(P3AConstellationHelperTest, NebulaMessage) {
MessageMetainfo meta_info;
meta_info.Init(&local_state_, "release", "2022-01-01");

std::string message = GenerateP3AConstellationMessage(
kTestHistogramName, 3, meta_info, kP3AUploadType, false, true);
kTestHistogramName, 3, meta_info, kP3AUploadType,
MetricConfig{.nebula = true});
std::vector<std::string> no_refcode_layers =
base::SplitString(message, kP3AMessageConstellationLayerSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
Expand All @@ -355,7 +400,7 @@ TEST_F(P3AConstellationHelperTest, NebulaSample) {
kTestNebulaHistogramName, MetricLogType::kTypical,
GenerateP3AConstellationMessage(kTestNebulaHistogramName,
kTestTypicalEpoch, meta_info,
kP3AUploadType, false, true),
kP3AUploadType, std::nullopt),
true);
task_environment_.RunUntilIdle();

Expand Down
47 changes: 33 additions & 14 deletions components/p3a/message_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,12 @@ void MessageManager::StartScheduledUpload(bool is_constellation,
is_constellation
? constellation_send_log_stores_[log_type]->staged_log_type()
: json_log_stores_[log_type]->staged_log_type();
const bool is_nebula = is_constellation
? p3a::kNebulaOnlyHistograms.contains(
constellation_send_log_stores_[log_type]
->staged_log_histogram_name())
: false;
bool is_nebula = false;
if (is_constellation) {
const auto* metric_config = GetMetricConfig(
constellation_send_log_stores_[log_type]->staged_log_histogram_name());
is_nebula = metric_config && *metric_config && (*metric_config)->nebula;
}

VLOG(2) << logging_prefix << " - Uploading " << log.size() << " bytes";
uploader_->UploadLog(log, upload_type, is_constellation, is_nebula, log_type);
Expand Down Expand Up @@ -352,9 +353,10 @@ void MessageManager::StartScheduledConstellationPrep(MetricLogType log_type) {
const std::string log_key = log_store->staged_log_key();
VLOG(2) << "MessageManager::StartScheduledConstellationPrep - Requesting "
"randomness for histogram: "
<< log_key;
<< log_key << " " << log;

const bool is_nebula = p3a::kNebulaOnlyHistograms.contains(log_key);
const auto* metric_config = GetMetricConfig(log_key);
bool is_nebula = metric_config && *metric_config && (*metric_config)->nebula;
if (is_nebula && !features::IsNebulaEnabled()) {
// Do not report if Nebula feature is not enabled,
// mark request as successful to avoid transmission.
Expand Down Expand Up @@ -394,12 +396,10 @@ std::string MessageManager::SerializeLog(std::string_view histogram_name,
message_meta_.Update();

if (is_constellation) {
const bool include_refcode =
p3a::kHistogramsWithRefcodeIncluded.contains(histogram_name);
const bool is_nebula = p3a::kNebulaOnlyHistograms.contains(histogram_name);
return GenerateP3AConstellationMessage(histogram_name, value, message_meta_,
upload_type, include_refcode,
is_nebula);
const auto* metric_config = GetMetricConfig(histogram_name);
return GenerateP3AConstellationMessage(
histogram_name, value, message_meta_, upload_type,
metric_config ? *metric_config : std::nullopt);
} else {
base::Value::Dict p3a_json_value = GenerateP3AMessageDict(
histogram_name, value, log_type, message_meta_, upload_type);
Expand All @@ -411,6 +411,23 @@ std::string MessageManager::SerializeLog(std::string_view histogram_name,
}
}

const std::optional<MetricConfig>* MessageManager::GetMetricConfig(
const std::string_view histogram_name) const {
const std::optional<MetricConfig>* metric_config = nullptr;

auto it = p3a::kCollectedTypicalHistograms.find(histogram_name);
if (it != p3a::kCollectedTypicalHistograms.end()) {
metric_config = &it->second;
} else if (it = p3a::kCollectedSlowHistograms.find(histogram_name);
it != p3a::kCollectedSlowHistograms.end()) {
metric_config = &it->second;
} else if (it = p3a::kCollectedExpressHistograms.find(histogram_name);
it != p3a::kCollectedExpressHistograms.end()) {
metric_config = &it->second;
}
return metric_config;
}

bool MessageManager::IsActualMetric(const std::string& histogram_name) const {
return p3a::kCollectedTypicalHistograms.contains(histogram_name) ||
p3a::kCollectedExpressHistograms.contains(histogram_name) ||
Expand All @@ -420,7 +437,9 @@ bool MessageManager::IsActualMetric(const std::string& histogram_name) const {

bool MessageManager::IsEphemeralMetric(
const std::string& histogram_name) const {
return p3a::kEphemeralHistograms.contains(histogram_name) ||
const auto* metric_config = GetMetricConfig(histogram_name);

return (metric_config && *metric_config && (*metric_config)->ephemeral) ||
delegate_->GetDynamicMetricLogType(histogram_name).has_value();
}

Expand Down
3 changes: 3 additions & 0 deletions components/p3a/message_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class MessageManager : public MetricLogStore::Delegate {
std::string_view histogram_name,
std::optional<bool> only_update_for_constellation = std::nullopt);

const std::optional<MetricConfig>* GetMetricConfig(
const std::string_view histogram_name) const;

private:
void StartScheduledUpload(bool is_constellation, MetricLogType log_type);
void StartScheduledConstellationPrep(MetricLogType log_type);
Expand Down
28 changes: 14 additions & 14 deletions components/p3a/message_manager_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -188,38 +188,38 @@ class P3AMessageManagerTest : public testing::Test,
std::vector<std::string> GetTestHistogramNames(MetricLogType log_type,
size_t p3a_count,
size_t p2a_count) {
auto histogram_names_begin = kCollectedExpressHistograms.cbegin();
auto histogram_names_end = kCollectedExpressHistograms.cend();
auto histograms_begin = kCollectedExpressHistograms.cbegin();
auto histograms_end = kCollectedExpressHistograms.cend();
std::vector<std::string> result;
size_t p3a_i = 0;
size_t p2a_i = 0;
switch (log_type) {
case MetricLogType::kExpress:
histogram_names_begin = kCollectedExpressHistograms.cbegin();
histogram_names_end = kCollectedExpressHistograms.cend();
histograms_begin = kCollectedExpressHistograms.cbegin();
histograms_end = kCollectedExpressHistograms.cend();
break;
case MetricLogType::kSlow:
histogram_names_begin = kCollectedSlowHistograms.cbegin();
histogram_names_end = kCollectedSlowHistograms.cend();
histograms_begin = kCollectedSlowHistograms.cbegin();
histograms_end = kCollectedSlowHistograms.cend();
break;
case MetricLogType::kTypical:
histogram_names_begin = kCollectedTypicalHistograms.cbegin();
histogram_names_end = kCollectedTypicalHistograms.cend();
histograms_begin = kCollectedTypicalHistograms.cbegin();
histograms_end = kCollectedTypicalHistograms.cend();
break;
default:
NOTREACHED();
}
for (auto histogram_name_i = histogram_names_begin;
histogram_name_i != histogram_names_end; histogram_name_i++) {
if (histogram_name_i->rfind(kP2APrefix, 0) == 0) {
for (auto histogram_i = histograms_begin; histogram_i != histograms_end;
histogram_i++) {
if (histogram_i->first.rfind(kP2APrefix, 0) == 0) {
if (p2a_i < p2a_count) {
result.push_back(std::string(*histogram_name_i));
result.push_back(std::string(histogram_i->first));
p2a_i++;
}
} else if (p3a_i < p3a_count &&
(histogram_name_i->starts_with("Brave.Core") ||
(histogram_i->first.starts_with("Brave.Core") ||
log_type == MetricLogType::kExpress)) {
result.push_back(std::string(*histogram_name_i));
result.push_back(std::string(histogram_i->first));
p3a_i++;
}

Expand Down
60 changes: 60 additions & 0 deletions components/p3a/metric_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* Copyright (c) 2024 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_COMPONENTS_P3A_METRIC_CONFIG_H_
#define BRAVE_COMPONENTS_P3A_METRIC_CONFIG_H_

#include <array>
#include <optional>

namespace p3a {

enum class MetricAttribute {
// Default attributes
kAnswerIndex,
kVersion,
kYoi,
kChannel,
kPlatform,
kCountryCode,
kWoi,
// Alternative attributes
kGeneralPlatform,
kRegion,
kSubregion,
kRef,
kMaxValue = kRef,
};

inline constexpr MetricAttribute kDefaultMetricAttributes[] = {
MetricAttribute::kAnswerIndex, MetricAttribute::kVersion,
MetricAttribute::kYoi, MetricAttribute::kChannel,
MetricAttribute::kPlatform, MetricAttribute::kCountryCode,
MetricAttribute::kWoi,
};

using MetricAttributes = std::array<std::optional<MetricAttribute>, 8>;
using MetricAttributesToAppend = std::array<std::optional<MetricAttribute>, 2>;

struct MetricConfig {
// Once the metric value has been sent, the value will be removed from the log
// store
bool ephemeral;
// Should only be sent via Constellation
bool constellation_only;
// Should only be sent via Nebula
bool nebula;
// Avoid reporting "other" for countries not included in the allowlist
// and rely on STAR to provide k-anonymity
bool disable_country_strip;
// Ordered attributes to be included with the metric
std::optional<MetricAttributes> attributes;
// Ordered attributes to be appended to the list of default attributes
MetricAttributesToAppend append_attributes;
};

} // namespace p3a

#endif // BRAVE_COMPONENTS_P3A_METRIC_CONFIG_H_
2 changes: 1 addition & 1 deletion components/p3a/metric_log_store_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class P3AMetricLogStoreTest : public testing::Test,
for (size_t i = 1; i <= message_count &&
histogram_it != p3a::kCollectedTypicalHistograms.end();
i++) {
log_store->UpdateValue(std::string(*histogram_it), 2);
log_store->UpdateValue(std::string(histogram_it->first), 2);
histogram_it++;
}
}
Expand Down
Loading

0 comments on commit aa6ae78

Please sign in to comment.