// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/feedback/system_logs/log_sources/lacros_log_files_log_source.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/task/thread_pool.h"
#include "components/feedback/feedback_util.h"
#include "content/public/browser/browser_thread.h"
namespace system_logs {
namespace {
// Maximum buffer size for user logs in bytes.
const int64_t kMaxLogSize = 1024 * 1024;
constexpr char kLogTruncated[] = "<earlier logs truncated>\n";
constexpr char kNotAvailable[] = "<not available>";
} // namespace
LacrosLogFilesLogSource::LacrosLogFilesLogSource(
const base::FilePath& log_base_path,
const std::string& log_key_base)
: SystemLogsSource("UserLoggedFiles"),
log_base_path_(log_base_path),
log_key_base_(log_key_base) {}
LacrosLogFilesLogSource::~LacrosLogFilesLogSource() = default;
void LacrosLogFilesLogSource::Fetch(SysLogsSourceCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!callback.is_null());
auto response = std::make_unique<SystemLogsResponse>();
auto* response_ptr = response.get();
base::ThreadPool::PostTaskAndReply(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&LacrosLogFilesLogSource::FindFiles,
weak_ptr_factory_.GetWeakPtr(), log_base_path_,
log_key_base_, response_ptr),
base::BindOnce(std::move(callback), std::move(response)));
}
base::FilePath LacrosLogFilesLogSource::FindPreviousLogPath(
const base::FilePath& log_base_path) {
base::FileEnumerator log_enum(log_base_path,
/*recursive=*/false,
base::FileEnumerator::FILES, "lacros_*.log");
// Find the most recent timestamped log - that's the previous one, if any.
base::FilePath previous_log_path = log_enum.Next();
for (base::FilePath log_path = log_enum.Next(); !log_path.empty();
log_path = log_enum.Next()) {
// Lacros log files have the following format:
// - lacros_YYMMDD-HHMMSS.log
// As specified in |logging::GenerateTimestampedName|.
// Hence a lexicographic comparison is sufficient to determine
// the most recent log file.
if (log_path.value() > previous_log_path.value())
previous_log_path = log_path;
}
return previous_log_path;
}
void LacrosLogFilesLogSource::FindFiles(const base::FilePath& log_base_path,
const std::string& log_key_base,
SystemLogsResponse* response) {
// Current log.
base::FilePath log_path = log_base_path.Append("lacros.log");
ReadFile(log_path, log_key_base, response);
// Previous log.
std::string previous_log_key = log_key_base + "_previous";
base::FilePath previous_log_path = FindPreviousLogPath(log_base_path);
if (previous_log_path.empty()) {
response->emplace(previous_log_key, kNotAvailable);
return;
}
ReadFile(previous_log_path, previous_log_key, response);
}
void LacrosLogFilesLogSource::ReadFile(const base::FilePath& log_file_path,
const std::string& log_key,
SystemLogsResponse* response) {
std::optional<std::string> maybe_value =
feedback_util::ReadEndOfFile(log_file_path, kMaxLogSize);
if (maybe_value.has_value() && maybe_value.value().size() == kMaxLogSize) {
maybe_value.value().replace(0, strlen(kLogTruncated), kLogTruncated);
LOG(WARNING) << "Large log file was likely truncated: " << log_file_path;
}
response->emplace(log_key,
(maybe_value.has_value() && !maybe_value.value().empty())
? std::move(maybe_value.value())
: kNotAvailable);
}
} // namespace system_logs