From 2546cdb7ab060c510da22925f099cf5ef90fdb60 Mon Sep 17 00:00:00 2001 From: "A. Cody Schuffelen" Date: Fri, 1 May 2026 14:10:47 -0700 Subject: [PATCH] cvd: Add support for logging to a file Implement timestamped log file creation for the cvd command. Logs are saved in PerUserDir() + "/logs/" with a name like cvd_YYYYMMDD_HHMMSS.ms.log. This ensures logs are preserved across runs. Assisted-by: Jetski Bug: b/507600500 --- .../cuttlefish/host/commands/cvd/BUILD.bazel | 1 + .../host/commands/cvd/cli/BUILD.bazel | 11 ++++ .../host/commands/cvd/cli/log_files.cpp | 57 +++++++++++++++++++ .../host/commands/cvd/cli/log_files.h | 32 +++++++++++ base/cvd/cuttlefish/host/commands/cvd/main.cc | 16 ++++-- 5 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 base/cvd/cuttlefish/host/commands/cvd/cli/log_files.cpp create mode 100644 base/cvd/cuttlefish/host/commands/cvd/cli/log_files.h diff --git a/base/cvd/cuttlefish/host/commands/cvd/BUILD.bazel b/base/cvd/cuttlefish/host/commands/cvd/BUILD.bazel index 94c919dc180..16901998632 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/cvd/BUILD.bazel @@ -35,6 +35,7 @@ cf_cc_binary( "//cuttlefish/common/libs/utils:flag_parser", "//cuttlefish/common/libs/utils:subprocess", "//cuttlefish/common/libs/utils:tee_logging", + "//cuttlefish/host/commands/cvd/cli:log_files", "//cuttlefish/host/commands/cvd/utils", "//cuttlefish/host/commands/cvd/version", "//cuttlefish/host/libs/vm_manager", diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/BUILD.bazel b/base/cvd/cuttlefish/host/commands/cvd/cli/BUILD.bazel index 70e88396071..34908c0eb5f 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/BUILD.bazel +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/BUILD.bazel @@ -51,6 +51,17 @@ cf_cc_library( ], ) +cf_cc_library( + name = "log_files", + srcs = ["log_files.cpp"], + hdrs = ["log_files.h"], + deps = [ + "//cuttlefish/common/libs/utils:files", + "//cuttlefish/host/commands/cvd/utils", + "@abseil-cpp//absl/strings", + ], +) + # Commands that may invoke other commands cf_cc_library( name = "nesting_commands", diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/log_files.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/log_files.cpp new file mode 100644 index 00000000000..8d974582802 --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/log_files.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2026 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/cvd/cli/log_files.h" + +#include +#include +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" + +#include "cuttlefish/common/libs/utils/files.h" +#include "cuttlefish/host/commands/cvd/utils/common.h" + +namespace cuttlefish { + +std::optional GetCvdLogFile() { + std::string log_dir = PerUserDir() + "/logs"; + if (!EnsureDirectoryExists(log_dir, 0777).ok()) { + std::cerr << "Failed to create log directory: " << log_dir + << ". Logging to file disabled." << std::endl; + return std::nullopt; + } + + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::time_t now_time_t = std::chrono::system_clock::to_time_t(now); + std::chrono::milliseconds now_ms = + std::chrono::duration_cast( + now.time_since_epoch()) % + 1000; + + std::tm tm_now; + localtime_r(&now_time_t, &tm_now); + + std::stringstream ss; + ss << std::put_time(&tm_now, "%Y%m%d_%H%M%S") << "." << std::setfill('0') + << std::setw(3) << now_ms.count(); + return absl::StrCat(log_dir, "/cvd_", ss.str(), ".log"); +} + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/log_files.h b/base/cvd/cuttlefish/host/commands/cvd/cli/log_files.h new file mode 100644 index 00000000000..7af40fe18f1 --- /dev/null +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/log_files.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2026 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 + +namespace cuttlefish { + +/** + * Returns the path to a new timestamped log file for the + * cvd command. The log file is created in PerUserDir() + "/logs/" with a name + * like cvd_YYYYMMDD_HHMMSS.ms.log. If the logs directory cannot be created, the + * function returns std::nullopt. + */ +std::optional GetCvdLogFile(); + +} // namespace cuttlefish diff --git a/base/cvd/cuttlefish/host/commands/cvd/main.cc b/base/cvd/cuttlefish/host/commands/cvd/main.cc index 866d48ecdcb..58cbac7e7b9 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/main.cc +++ b/base/cvd/cuttlefish/host/commands/cvd/main.cc @@ -26,17 +26,18 @@ #include #include -#include #include "absl/cleanup/cleanup.h" -#include "absl/strings/str_split.h" -#include #include "absl/log/log.h" +#include "absl/strings/str_split.h" +#include "android-base/file.h" +#include "fmt/format.h" #include "cuttlefish/common/libs/utils/environment.h" #include "cuttlefish/common/libs/utils/files.h" #include "cuttlefish/common/libs/utils/flag_parser.h" #include "cuttlefish/common/libs/utils/subprocess.h" #include "cuttlefish/common/libs/utils/tee_logging.h" +#include "cuttlefish/host/commands/cvd/cli/log_files.h" #include "cuttlefish/host/commands/cvd/cvd.h" #include "cuttlefish/host/commands/cvd/utils/common.h" #include "cuttlefish/host/commands/cvd/version/version.h" @@ -216,6 +217,7 @@ bool ValidateHostConfiguration() { } } // namespace + } // namespace cuttlefish int main(int argc, char** argv) { @@ -228,7 +230,13 @@ int main(int argc, char** argv) { cuttlefish::MetadataLevel metadata_level = isatty(0) ? cuttlefish::MetadataLevel::ONLY_MESSAGE : cuttlefish::MetadataLevel::FULL; - cuttlefish::LogToStderr("", metadata_level, verbosity); + + std::optional log_file = cuttlefish::GetCvdLogFile(); + std::vector log_files; + if (log_file) { + log_files.push_back(*log_file); + } + cuttlefish::LogToStderrAndFiles(log_files, "", metadata_level, verbosity); if (!cuttlefish::ValidateHostConfiguration()) { return -1;