diff --git a/base/cvd/cuttlefish/host/commands/metrics/BUILD.bazel b/base/cvd/cuttlefish/host/commands/metrics/BUILD.bazel index 53394637809..b8b6efdb46b 100644 --- a/base/cvd/cuttlefish/host/commands/metrics/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/metrics/BUILD.bazel @@ -59,6 +59,70 @@ cf_cc_binary( ], ) +cf_cc_library( + name = "metrics_conversion", + srcs = [ + "metrics_conversion.cc", + ], + hdrs = [ + "metrics_conversion.h", + ], + deps = [ + "//cuttlefish/common/libs/utils:result", + "//external_proto:clientanalytics_cc_proto", + "//external_proto:log_source_enum_cc_proto", + ], +) + +cf_cc_library( + name = "metrics_flags", + srcs = [ + "metrics_flags.cc", + ], + hdrs = [ + "metrics_flags.h", + ], + deps = [ + "//cuttlefish/common/libs/utils:flag_parser", + "//cuttlefish/common/libs/utils:result", + "//cuttlefish/host/libs/metrics:metrics_environment", + ], +) + +cf_cc_library( + name = "metrics_transmission", + srcs = [ + "metrics_transmission.cc", + ], + hdrs = [ + "metrics_transmission.h", + ], + deps = [ + "//cuttlefish/common/libs/utils:result", + "//cuttlefish/host/libs/web/http_client", + "//cuttlefish/host/libs/web/http_client:curl_global_init", + "//cuttlefish/host/libs/web/http_client:curl_http_client", + "//cuttlefish/host/libs/web/http_client:http_string", + "//cuttlefish/host/libs/metrics:metrics_environment", + "//external_proto:clientanalytics_cc_proto", + ], +) + +cf_cc_binary( + name = "metrics_transmitter", + srcs = [ + "metrics_transmitter.cc", + ], + deps = [ + "//cuttlefish/common/libs/utils:result", + "//cuttlefish/host/commands/metrics:metrics_conversion", + "//cuttlefish/host/commands/metrics:metrics_flags", + "//cuttlefish/host/commands/metrics:metrics_transmission", + "//external_proto:clientanalytics_cc_proto", + "//libbase", + ], +) + cf_cc_library( name = "send", srcs = ["send.cc"], diff --git a/base/cvd/cuttlefish/host/commands/metrics/metrics_conversion.cc b/base/cvd/cuttlefish/host/commands/metrics/metrics_conversion.cc new file mode 100644 index 00000000000..278463f24bd --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/metrics/metrics_conversion.cc @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#include "cuttlefish/host/commands/metrics/metrics_conversion.h" + +#include +#include + +#include "external_proto/clientanalytics.pb.h" +#include "external_proto/log_source_enum.pb.h" + +namespace cuttlefish { +namespace { + +using wireless_android_play_playlog::ClientInfo; +using wireless_android_play_playlog::LogEvent; +using wireless_android_play_playlog::LogRequest; +using wireless_android_play_playlog::LogSourceEnum::LogSource; + +constexpr LogSource kLogSourceId = LogSource::CUTTLEFISH_METRICS; +constexpr char kLogSourceStr[] = "CUTTLEFISH_METRICS"; +constexpr ClientInfo::ClientType kCppClientType = ClientInfo::CPLUSPLUS; + +} // namespace + +LogRequest BuildLogRequest(const std::string& serialized_cf_log_event) { + LogRequest log_request; + const auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + log_request.set_request_time_ms(now.count()); + log_request.set_log_source(kLogSourceId); + log_request.set_log_source_name(kLogSourceStr); + + ClientInfo& client_info = *log_request.mutable_client_info(); + client_info.set_client_type(kCppClientType); + + LogEvent& log_event = *log_request.add_log_event(); + log_event.set_event_time_ms(now.count()); + log_event.set_source_extension(serialized_cf_log_event); + return log_request; +} + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/metrics/metrics_conversion.h b/base/cvd/cuttlefish/host/commands/metrics/metrics_conversion.h new file mode 100644 index 00000000000..51be7767745 --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/metrics/metrics_conversion.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include + +#include "external_proto/clientanalytics.pb.h" + +namespace cuttlefish { + +wireless_android_play_playlog::LogRequest BuildLogRequest( + const std::string& serialized_cf_log_event); + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/metrics/metrics_flags.cc b/base/cvd/cuttlefish/host/commands/metrics/metrics_flags.cc new file mode 100644 index 00000000000..df6c8e7545a --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/metrics/metrics_flags.cc @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#include "cuttlefish/host/commands/metrics/metrics_flags.h" + +#include +#include + +#include "cuttlefish/common/libs/utils/flag_parser.h" +#include "cuttlefish/common/libs/utils/result.h" +#include "cuttlefish/host/libs/metrics/metrics_environment.h" + +namespace cuttlefish { +namespace { + +Flag EnvironmentGflagsCompatFlag(const std::string& name, + ClearcutEnvironment& value) { + return GflagsCompatFlag(name) + .Getter([&value]() { return EnvironmentToString(value); }) + .Setter([name, &value](const FlagMatch& match) -> Result { + if (match.value == kLocal) { + value = ClearcutEnvironment::Local; + } else if (match.value == kStaging) { + value = ClearcutEnvironment::Staging; + } else if (match.value == kProduction) { + value = ClearcutEnvironment::Production; + } else { + CF_ERRF("Unexpected environment value: \"{}\"", match.value); + } + return {}; + }); +} + +} // namespace + +// TODO: chadreynolds - add path flag to send logs to metrics/metrics.log +// TODO: chadreynolds - add debug flag to specify metrics file and transmit +// for convenient use with different transmission environments + +Result ProcessFlags(int argc, char** argv) { + MetricsFlags result; + std::vector flags; + flags.emplace_back( + EnvironmentGflagsCompatFlag("environment", result.environment) + .Help("Specify the environment to transmit to.")); + flags.emplace_back( + GflagsCompatFlag("serialized_proto", result.serialized_proto) + .Help("The serialized proto data to transmit.")); + flags.emplace_back(HelpFlag(flags)); + flags.emplace_back(UnexpectedArgumentGuard()); + std::vector args = + ArgsToVec(argc - 1, argv + 1); // Skip argv[0] + CF_EXPECT(ConsumeFlags(flags, args)); + return result; +} + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/metrics/metrics_flags.h b/base/cvd/cuttlefish/host/commands/metrics/metrics_flags.h new file mode 100644 index 00000000000..c283034d7da --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/metrics/metrics_flags.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include + +#include "cuttlefish/common/libs/utils/result.h" +#include "cuttlefish/host/libs/metrics/metrics_environment.h" + +namespace cuttlefish { + +struct MetricsFlags { + ClearcutEnvironment environment = ClearcutEnvironment::Production; + std::string serialized_proto; +}; + +Result ProcessFlags(int argc, char** argv); + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/metrics/metrics_transmission.cc b/base/cvd/cuttlefish/host/commands/metrics/metrics_transmission.cc new file mode 100644 index 00000000000..4267f76c5ba --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/metrics/metrics_transmission.cc @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#include "cuttlefish/host/commands/metrics/metrics_transmission.h" + +#include +#include + +#include "cuttlefish/common/libs/utils/result.h" +#include "cuttlefish/host/libs/metrics/metrics_environment.h" +#include "cuttlefish/host/libs/web/http_client/curl_global_init.h" +#include "cuttlefish/host/libs/web/http_client/curl_http_client.h" +#include "cuttlefish/host/libs/web/http_client/http_client.h" +#include "cuttlefish/host/libs/web/http_client/http_string.h" +#include "external_proto/clientanalytics.pb.h" + +namespace cuttlefish { +namespace { + +std::string ClearcutEnvironmentUrl(const ClearcutEnvironment environment) { + switch (environment) { + case ClearcutEnvironment::Local: + return "http://localhost:27910/log"; + case ClearcutEnvironment::Staging: + return "https://play.googleapis.com:443/staging/log"; + case ClearcutEnvironment::Production: + return "https://play.googleapis.com:443/log"; + } +} + +Result PostRequest(HttpClient& http_client, const std::string& output, + const ClearcutEnvironment server) { + const std::string clearcut_url = ClearcutEnvironmentUrl(server); + const HttpResponse response = + CF_EXPECT(HttpPostToString(http_client, clearcut_url, output)); + CF_EXPECTF(response.HttpSuccess(), "Metrics POST failed ({}): {}", + response.http_code, response.data); + return {}; +} + +} // namespace + +Result TransmitMetricsEvent( + const wireless_android_play_playlog::LogRequest& log_request, + ClearcutEnvironment environment) { + CurlGlobalInit curl_global_init; + const bool use_logging_debug_function = true; + std::unique_ptr http_client = + CurlHttpClient(use_logging_debug_function); + CF_EXPECT(http_client.get() != nullptr, + "Unable to create cURL client for metrics transmission"); + CF_EXPECT( + PostRequest(*http_client, log_request.SerializeAsString(), environment)); + return {}; +} + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/metrics/metrics_transmission.h b/base/cvd/cuttlefish/host/commands/metrics/metrics_transmission.h new file mode 100644 index 00000000000..fa4f266417f --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/metrics/metrics_transmission.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include "cuttlefish/common/libs/utils/result.h" +#include "cuttlefish/host/libs/metrics/metrics_environment.h" +#include "external_proto/clientanalytics.pb.h" + +namespace cuttlefish { + +Result TransmitMetricsEvent( + const wireless_android_play_playlog::LogRequest& log_request, + ClearcutEnvironment environment); + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/metrics/metrics_transmitter.cc b/base/cvd/cuttlefish/host/commands/metrics/metrics_transmitter.cc new file mode 100644 index 00000000000..fe6c283ad3d --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/metrics/metrics_transmitter.cc @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#include + +#include + +#include "cuttlefish/common/libs/utils/result.h" +#include "cuttlefish/host/commands/metrics/metrics_conversion.h" +#include "cuttlefish/host/commands/metrics/metrics_flags.h" +#include "cuttlefish/host/commands/metrics/metrics_transmission.h" +#include "external_proto/clientanalytics.pb.h" + +namespace cuttlefish { +namespace { + +MetricsFlags ProcessMetricsFlags(int argc, char** argv) { + Result process_result = ProcessFlags(argc, argv); + CHECK(process_result.ok()) << "Could not process command line flags: " + << process_result.error().FormatForEnv(); + return *process_result; +} + +int MetricsMain(const MetricsFlags& flags) { + const wireless_android_play_playlog::LogRequest log_request = + BuildLogRequest(flags.serialized_proto); + Result transmit_result = + TransmitMetricsEvent(log_request, flags.environment); + if (transmit_result.ok()) { + return EXIT_SUCCESS; + } else { + LOG(ERROR) << "Transmission failed with error message: " + << transmit_result.error().FormatForEnv(); + return EXIT_FAILURE; + } +} + +} // namespace +} // namespace cuttlefish + +int main(int argc, char** argv) { + ::android::base::InitLogging(argv, android::base::StderrLogger); + const cuttlefish::MetricsFlags flags = + cuttlefish::ProcessMetricsFlags(argc, argv); + return cuttlefish::MetricsMain(flags); +} diff --git a/base/cvd/cuttlefish/host/libs/config/known_paths.cpp b/base/cvd/cuttlefish/host/libs/config/known_paths.cpp index d8d3b98216b..93de6db65a4 100644 --- a/base/cvd/cuttlefish/host/libs/config/known_paths.cpp +++ b/base/cvd/cuttlefish/host/libs/config/known_paths.cpp @@ -103,6 +103,10 @@ std::string McopyBinary() { return HostBinaryPath("mcopy"); } std::string MetricsBinary() { return HostBinaryPath("metrics"); } +std::string MetricsTransmitterBinary() { + return HostBinaryPath("metrics-transmitter"); +} + std::string MkbootimgBinary() { return HostBinaryPath("mkbootimg.py"); } std::string MkfsFat() { return HostBinaryPath("mkfs.fat"); } diff --git a/base/cvd/cuttlefish/host/libs/config/known_paths.h b/base/cvd/cuttlefish/host/libs/config/known_paths.h index c84179c5a9c..9371d37d5a9 100644 --- a/base/cvd/cuttlefish/host/libs/config/known_paths.h +++ b/base/cvd/cuttlefish/host/libs/config/known_paths.h @@ -43,6 +43,7 @@ std::string KernelLogMonitorBinary(); std::string LogcatReceiverBinary(); std::string McopyBinary(); std::string MetricsBinary(); +std::string MetricsTransmitterBinary(); std::string MkbootimgBinary(); std::string MkfsFat(); std::string MkuserimgMke2fsBinary(); diff --git a/base/cvd/cuttlefish/host/libs/metrics/BUILD.bazel b/base/cvd/cuttlefish/host/libs/metrics/BUILD.bazel index f94efdac1cf..de04b6ce04b 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/BUILD.bazel +++ b/base/cvd/cuttlefish/host/libs/metrics/BUILD.bazel @@ -66,13 +66,21 @@ cf_cc_library( "//external_proto:cf_host_cc_proto", "//external_proto:cf_log_cc_proto", "//external_proto:cf_metrics_event_v2_cc_proto", - "//external_proto:clientanalytics_cc_proto", - "//external_proto:log_source_enum_cc_proto", "@fmt", "@protobuf//:timestamp_cc_proto", ], ) +cf_cc_library( + name = "metrics_environment", + srcs = [ + "metrics_environment.cc", + ], + hdrs = [ + "metrics_environment.h", + ], +) + cf_cc_library( name = "metrics_headers", hdrs = [ @@ -97,13 +105,14 @@ cf_cc_library( "//cuttlefish/host/commands/cvd/instances", "//cuttlefish/host/commands/cvd/metrics:is_enabled", "//cuttlefish/host/commands/cvd/version", + "//cuttlefish/host/libs/config:known_paths", "//cuttlefish/host/libs/metrics:event_type", "//cuttlefish/host/libs/metrics:guest_metrics", "//cuttlefish/host/libs/metrics:metrics_conversion", "//cuttlefish/host/libs/metrics:metrics_transmitter", "//cuttlefish/host/libs/metrics:metrics_writer", "//cuttlefish/host/libs/metrics:session_id", - "//external_proto:clientanalytics_cc_proto", + "//external_proto:cf_log_cc_proto", "//libbase", "@fmt", ], @@ -119,11 +128,11 @@ cf_cc_library( ], deps = [ "//cuttlefish/common/libs/utils:result", - "//cuttlefish/host/libs/web/http_client", - "//cuttlefish/host/libs/web/http_client:curl_global_init", - "//cuttlefish/host/libs/web/http_client:curl_http_client", - "//cuttlefish/host/libs/web/http_client:http_string", - "//external_proto:clientanalytics_cc_proto", + "//cuttlefish/common/libs/utils:subprocess", + "//cuttlefish/common/libs/utils:subprocess_managed_stdio", + "//cuttlefish/host/libs/config:known_paths", + "//cuttlefish/host/libs/metrics:metrics_environment", + "//external_proto:cf_log_cc_proto", ], ) @@ -141,7 +150,7 @@ cf_cc_library( "//cuttlefish/common/libs/utils:random", "//cuttlefish/common/libs/utils:result", "//cuttlefish/host/libs/metrics:event_type", - "//external_proto:clientanalytics_cc_proto", + "//external_proto:cf_log_cc_proto", "@fmt", "@protobuf", ], diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc b/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc index 19691fea343..a006a3110f5 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.cc @@ -27,8 +27,6 @@ #include "external_proto/cf_host.pb.h" #include "external_proto/cf_log.pb.h" #include "external_proto/cf_metrics_event_v2.pb.h" -#include "external_proto/clientanalytics.pb.h" -#include "external_proto/log_source_enum.pb.h" namespace cuttlefish { namespace { @@ -41,14 +39,6 @@ using logs::proto::wireless::android::cuttlefish::events:: using logs::proto::wireless::android::cuttlefish::events::CuttlefishHost; using logs::proto::wireless::android::cuttlefish::events::CuttlefishHost_OsType; using logs::proto::wireless::android::cuttlefish::events::MetricsEventV2; -using wireless_android_play_playlog::ClientInfo; -using wireless_android_play_playlog::LogEvent; -using wireless_android_play_playlog::LogRequest; -using wireless_android_play_playlog::LogSourceEnum::LogSource; - -static constexpr LogSource kLogSourceId = LogSource::CUTTLEFISH_METRICS; -static constexpr char kLogSourceStr[] = "CUTTLEFISH_METRICS"; -static constexpr ClientInfo::ClientType kCppClientType = ClientInfo::CPLUSPLUS; CuttlefishGuest_EventType ConvertEventType(EventType event_type) { switch (event_type) { @@ -96,6 +86,8 @@ CuttlefishHost_OsType ConvertHostOs(const HostInfo& host_info) { } } +} // namespace + CuttlefishLogEvent BuildCuttlefishLogEvent(const MetricsData& metrics_data) { CuttlefishLogEvent cf_log_event; cf_log_event.set_device_type(CuttlefishLogEvent::CUTTLEFISH_DEVICE_TYPE_HOST); @@ -122,27 +114,4 @@ CuttlefishLogEvent BuildCuttlefishLogEvent(const MetricsData& metrics_data) { return cf_log_event; } -LogRequest BuildLogRequest(std::chrono::milliseconds now, - const CuttlefishLogEvent& cf_log_event) { - LogRequest log_request; - log_request.set_request_time_ms(now.count()); - log_request.set_log_source(kLogSourceId); - log_request.set_log_source_name(kLogSourceStr); - - ClientInfo& client_info = *log_request.mutable_client_info(); - client_info.set_client_type(kCppClientType); - - LogEvent& log_event = *log_request.add_log_event(); - log_event.set_event_time_ms(now.count()); - log_event.set_source_extension(cf_log_event.SerializeAsString()); - return log_request; -} - -} // namespace - -LogRequest ConstructLogRequest(const MetricsData& metrics_data) { - CuttlefishLogEvent cf_log_event = BuildCuttlefishLogEvent(metrics_data); - return BuildLogRequest(metrics_data.now, cf_log_event); -} - } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.h b/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.h index 8cec632a3bf..3d58f4df422 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.h +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_conversion.h @@ -23,7 +23,7 @@ #include "cuttlefish/common/libs/utils/host_info.h" #include "cuttlefish/host/libs/metrics/event_type.h" #include "cuttlefish/host/libs/metrics/guest_metrics.h" -#include "external_proto/clientanalytics.pb.h" +#include "external_proto/cf_log.pb.h" namespace cuttlefish { @@ -36,7 +36,7 @@ struct MetricsData { std::vector guest_metrics; }; -wireless_android_play_playlog::LogRequest ConstructLogRequest( - const MetricsData& metrics_data); +logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent +BuildCuttlefishLogEvent(const MetricsData& metrics_data); } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_environment.cc b/base/cvd/cuttlefish/host/libs/metrics/metrics_environment.cc new file mode 100644 index 00000000000..3f215c0e09d --- /dev/null +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_environment.cc @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#include "cuttlefish/host/libs/metrics/metrics_environment.h" + +#include + +namespace cuttlefish { + +std::string EnvironmentToString(ClearcutEnvironment environment) { + switch (environment) { + case ClearcutEnvironment::Local: + return std::string(kLocal); + case ClearcutEnvironment::Staging: + return std::string(kStaging); + case ClearcutEnvironment::Production: + return std::string(kProduction); + } +} + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_environment.h b/base/cvd/cuttlefish/host/libs/metrics/metrics_environment.h new file mode 100644 index 00000000000..12afa43b4e2 --- /dev/null +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_environment.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * 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. + */ + +#pragma once + +#include + +namespace cuttlefish { + +inline constexpr std::string_view kLocal = "local"; +inline constexpr std::string_view kStaging = "staging"; +inline constexpr std::string_view kProduction = "production"; + +enum class ClearcutEnvironment { + Local, + Staging, + Production, +}; + +std::string EnvironmentToString(ClearcutEnvironment environment); + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_orchestration.cc b/base/cvd/cuttlefish/host/libs/metrics/metrics_orchestration.cc index dc0f92c8f62..276afbd9a8b 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_orchestration.cc +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_orchestration.cc @@ -34,18 +34,19 @@ #include "cuttlefish/host/commands/cvd/instances/local_instance_group.h" #include "cuttlefish/host/commands/cvd/metrics/is_enabled.h" #include "cuttlefish/host/commands/cvd/version/version.h" +#include "cuttlefish/host/libs/config/known_paths.h" #include "cuttlefish/host/libs/metrics/event_type.h" #include "cuttlefish/host/libs/metrics/guest_metrics.h" #include "cuttlefish/host/libs/metrics/metrics_conversion.h" #include "cuttlefish/host/libs/metrics/metrics_transmitter.h" #include "cuttlefish/host/libs/metrics/metrics_writer.h" #include "cuttlefish/host/libs/metrics/session_id.h" -#include "external_proto/clientanalytics.pb.h" +#include "external_proto/cf_log.pb.h" namespace cuttlefish { namespace { -using wireless_android_play_playlog::LogRequest; +using logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent; constexpr char kMetricsLogName[] = "metrics.log"; @@ -54,8 +55,7 @@ constexpr char kReadmeText[] = " not mean metrics are being transmitted, the data is always gathered and " "written out for debugging purposes. To enable metrics transmission " ""; + "step when it does>"; struct MetricsPaths { std::string metrics_directory; @@ -125,10 +125,10 @@ Result GatherMetrics(const MetricsPaths& metrics_paths, Result OutputMetrics(EventType event_type, const std::string& metrics_directory, const MetricsData& metrics_data) { - const LogRequest log_request = ConstructLogRequest(metrics_data); - CF_EXPECT(WriteMetricsEvent(event_type, metrics_directory, log_request)); - if (kEnableCvdMetrics) { - CF_EXPECT(TransmitMetricsEvent(log_request)); + const CuttlefishLogEvent cf_log_event = BuildCuttlefishLogEvent(metrics_data); + CF_EXPECT(WriteMetricsEvent(event_type, metrics_directory, cf_log_event)); + if (kEnableCvdMetrics && FileExists(MetricsTransmitterBinary())) { + CF_EXPECT(TransmitMetrics(cf_log_event)); } return {}; } diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.cc b/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.cc index 422ebe72b6b..22f4bb26c9b 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.cc +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.cc @@ -16,57 +16,37 @@ #include "cuttlefish/host/libs/metrics/metrics_transmitter.h" +#include + #include "cuttlefish/common/libs/utils/result.h" -#include "cuttlefish/host/libs/web/http_client/curl_global_init.h" -#include "cuttlefish/host/libs/web/http_client/curl_http_client.h" -#include "cuttlefish/host/libs/web/http_client/http_client.h" -#include "cuttlefish/host/libs/web/http_client/http_string.h" -#include "external_proto/clientanalytics.pb.h" +#include "cuttlefish/common/libs/utils/subprocess.h" +#include "cuttlefish/common/libs/utils/subprocess_managed_stdio.h" +#include "cuttlefish/host/libs/config/known_paths.h" +#include "cuttlefish/host/libs/metrics/metrics_environment.h" +#include "external_proto/cf_log.pb.h" namespace cuttlefish { namespace { -// TODO: chadreynolds - create a compilation or runtime flag to swap -// environments -enum class ClearcutEnvironment { - kLocal = 0, - kStaging = 1, - kProd = 2, -}; - -std::string ClearcutEnvironmentUrl(const ClearcutEnvironment environment) { - switch (environment) { - case ClearcutEnvironment::kLocal: - return "http://localhost:27910/log"; - case ClearcutEnvironment::kStaging: - return "https://play.googleapis.com:443/staging/log"; - case ClearcutEnvironment::kProd: - return "https://play.googleapis.com:443/log"; - } -} +using logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent; -Result PostRequest(HttpClient& http_client, const std::string& output, - const ClearcutEnvironment server) { - const std::string clearcut_url = ClearcutEnvironmentUrl(server); - HttpResponse response = - CF_EXPECT(HttpPostToString(http_client, clearcut_url, output)); - CF_EXPECTF(response.HttpSuccess(), "Metrics POST failed ({}): {}", - response.http_code, response.data); - return {}; +Command BuildCommand(const std::string& serialized_proto) { + Command command(MetricsTransmitterBinary()); + command.AddParameter("--environment"); + command.AddParameter(kProduction); + command.AddParameter("--serialized_proto"); + command.AddParameter(serialized_proto); + return command; } } // namespace -Result TransmitMetricsEvent( - const wireless_android_play_playlog::LogRequest& log_request) { - CurlGlobalInit curl_global_init; - const bool use_logging_debug_function = true; - std::unique_ptr http_client = - CurlHttpClient(use_logging_debug_function); - CF_EXPECT(http_client.get() != nullptr, - "Unable to create cURL client for metrics transmission"); - CF_EXPECT(PostRequest(*http_client, log_request.SerializeAsString(), - ClearcutEnvironment::kProd)); +Result TransmitMetrics( + const logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent& + cf_log_event) { + Command transmission_command = BuildCommand(cf_log_event.SerializeAsString()); + CF_EXPECT(RunAndCaptureStdout(std::move(transmission_command)), + "Failed to transmit metrics."); return {}; } diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.h b/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.h index 353f4ae3e67..d17f6e9ba8a 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.h +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_transmitter.h @@ -17,11 +17,12 @@ #pragma once #include "cuttlefish/common/libs/utils/result.h" -#include "external_proto/clientanalytics.pb.h" +#include "external_proto/cf_log.pb.h" namespace cuttlefish { -Result TransmitMetricsEvent( - const wireless_android_play_playlog::LogRequest& log_request); +Result TransmitMetrics( + const logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent& + cf_log_event); } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.cc b/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.cc index 044238ee9fd..d2beb75888b 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.cc +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.cc @@ -26,10 +26,13 @@ #include "cuttlefish/common/libs/utils/random.h" #include "cuttlefish/common/libs/utils/result.h" #include "cuttlefish/host/libs/metrics/event_type.h" +#include "external_proto/cf_log.pb.h" namespace cuttlefish { namespace { +using logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent; + std::string GenerateFilenameSuffix() { const std::string nums("0123456789"); return GenerateRandomString(nums, 10); @@ -37,14 +40,14 @@ std::string GenerateFilenameSuffix() { } // namespace -Result WriteMetricsEvent( - EventType event_type, const std::string& metrics_directory, - const wireless_android_play_playlog::LogRequest& log_request) { +Result WriteMetricsEvent(EventType event_type, + const std::string& metrics_directory, + const CuttlefishLogEvent& cf_log_event) { const std::string event_filepath = fmt::format( "{}/{}_{}_{}.txtpb", metrics_directory, EventTypeString(event_type), std::chrono::system_clock::now(), GenerateFilenameSuffix()); std::string text_proto_out; - google::protobuf::TextFormat::PrintToString(log_request, &text_proto_out); + google::protobuf::TextFormat::PrintToString(cf_log_event, &text_proto_out); CF_EXPECT(WriteNewFile(event_filepath, text_proto_out)); return {}; } diff --git a/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.h b/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.h index 73191fe49d1..b860655c60c 100644 --- a/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.h +++ b/base/cvd/cuttlefish/host/libs/metrics/metrics_writer.h @@ -20,12 +20,13 @@ #include "cuttlefish/common/libs/utils/result.h" #include "cuttlefish/host/libs/metrics/event_type.h" -#include "external_proto/clientanalytics.pb.h" +#include "external_proto/cf_log.pb.h" namespace cuttlefish { Result WriteMetricsEvent( EventType event_type, const std::string& metrics_directory, - const wireless_android_play_playlog::LogRequest& log_request); + const logs::proto::wireless::android::cuttlefish::CuttlefishLogEvent& + cf_log_event); } // namespace cuttlefish diff --git a/base/cvd/cuttlefish/package/BUILD.bazel b/base/cvd/cuttlefish/package/BUILD.bazel index bd5aa2f5d92..0fbc18b9942 100644 --- a/base/cvd/cuttlefish/package/BUILD.bazel +++ b/base/cvd/cuttlefish/package/BUILD.bazel @@ -64,6 +64,7 @@ package_files( "cuttlefish-common/bin/lz4": "@lz4//programs:lz4", "cuttlefish-common/bin/make_f2fs": "@f2fs_tools//:make_f2fs", "cuttlefish-common/bin/metrics_launcher": "//cuttlefish/host/commands/metrics_launcher", + "cuttlefish-common/bin/metrics_transmitter": "//cuttlefish/host/commands/metrics:metrics_transmitter", "cuttlefish-common/bin/metrics": "//cuttlefish/host/commands/metrics", "cuttlefish-common/bin/mkbootfs": "//mkbootfs", "cuttlefish-common/bin/mkbootimg.py": "@mkbootimg//:mkbootimg.py",