blob: d01a5acf19efb846dfe0c2f1d8b308d27da3c7a4 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/cobalt/bin/system-metrics/log_stats_fetcher_impl.h"
#include <lib/syslog/cpp/macros.h>
namespace cobalt {
namespace {
// A map from components urls in the approve-list to the corresponding Cobalt event
// codes (as defined in metrics.yaml).
const std::unordered_map<std::string, ComponentEventCode> kComponentCodeMap{
{"fuchsia-boot:///#meta/driver_manager.cm", ComponentEventCode::DriverManager},
{"fuchsia-pkg://fuchsia.com/a11y_manager#meta/a11y_manager.cmx",
ComponentEventCode::A11yManager},
{"fuchsia-pkg://fuchsia.com/audio_core_google#meta/audio_core.cmx",
ComponentEventCode::AudioCore},
{"fuchsia-pkg://fuchsia.com/appmgr#meta/appmgr.cm", ComponentEventCode::Appmgr},
{"fuchsia-pkg://fuchsia.com/brightness_manager#meta/brightness_manager.cmx",
ComponentEventCode::BrightnessManager},
{"fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx", ComponentEventCode::CastAgent},
{"fuchsia-pkg://fuchsia.com/cast_runner#meta/cast_runner.cmx", ComponentEventCode::CastRunner},
{"fuchsia-pkg://fuchsia.com/cobalt#meta/cobalt.cmx", ComponentEventCode::Cobalt},
{"fuchsia-pkg://fuchsia.com/dhcpd#meta/dhcpd.cmx", ComponentEventCode::Dhcpd},
{"fuchsia-pkg://fuchsia.com/hwinfo#meta/hwinfo.cmx", ComponentEventCode::Hwinfo},
{"fuchsia-pkg://fuchsia.com/mdns#meta/mdns.cmx", ComponentEventCode::Mdns},
{"fuchsia-pkg://fuchsia.com/netcfg#meta/netcfg.cmx", ComponentEventCode::Netcfg},
{"fuchsia-pkg://fuchsia.com/pkg-resolver#meta/pkg-resolver.cmx",
ComponentEventCode::PkgResolver},
{"fuchsia-pkg://fuchsia.com/root_presenter#meta/root_presenter.cmx",
ComponentEventCode::RootPresenter},
{"fuchsia-pkg://fuchsia.com/scenic#meta/scenic.cmx", ComponentEventCode::Scenic},
{"fuchsia-pkg://fuchsia.com/sessionmgr#meta/sessionmgr.cmx", ComponentEventCode::SessionMgr},
{"fuchsia-pkg://fuchsia.com/sysmgr#meta/sysmgr.cmx", ComponentEventCode::Sysmgr},
{"fuchsia-pkg://fuchsia.com/system-update-checker#meta/system-update-checker.cmx",
ComponentEventCode::SystemUpdateChecker},
{"fuchsia-pkg://fuchsia.com/web_engine#meta/context_provider.cmx",
ComponentEventCode::ContextProvider},
};
} // namespace
LogStatsFetcherImpl::LogStatsFetcherImpl(async_dispatcher_t* dispatcher,
sys::ComponentContext* context)
: executor_(dispatcher),
archive_reader_(context->svc()->Connect<fuchsia::diagnostics::ArchiveAccessor>(),
{"archivist.cmx:root/log_stats:error_logs",
"archivist.cmx:root/log_stats/by_component/*:error_logs"}) {
// This establishes a baseline for error counts so that the next call to FetchMetrics() only
// includes logs since the daemon started as opposed to since the boot time. This is especially
// important if the daemon had previously crashed and has been relaunched.
FetchMetrics([](const Metrics& metrics) {});
}
void LogStatsFetcherImpl::FetchMetrics(MetricsCallback metrics_callback) {
metrics_callback_ = std::move(metrics_callback);
auto promise = archive_reader_.GetInspectSnapshot()
.and_then([this](std::vector<inspect::contrib::DiagnosticsData>& data_vector) {
this->OnInspectSnapshotReady(data_vector);
})
.or_else([](std::string& error) {
FX_LOGS(WARNING) << "Error while fetching log stats: " << error;
});
executor_.schedule_task(std::move(promise));
}
void LogStatsFetcherImpl::OnInspectSnapshotReady(
const std::vector<inspect::contrib::DiagnosticsData>& data_vector) {
// Since we only asked for one component's inspect, there shouldn't be more
// than one entry in the result.
if (data_vector.size() != 1u) {
FX_LOGS(ERROR) << "Expected 1 archive, received " << data_vector.size();
return;
}
LogStatsFetcher::Metrics metrics;
// Populate per-component log stats.
const rapidjson::Value& component_list =
data_vector[0].GetByPath({"root", "log_stats", "by_component"});
if (!component_list.IsObject()) {
FX_LOGS(ERROR) << "root/log_stats/by_component doesn't exist or is not an object";
return;
}
for (auto& component_member : component_list.GetObject()) {
std::string component_url = component_member.name.GetString();
auto code_it = kComponentCodeMap.find(component_url);
// Ignore components not in the whitelist.
if (code_it == kComponentCodeMap.end())
continue;
ComponentEventCode component_code = code_it->second;
if (!component_member.value.IsObject()) {
FX_LOGS(ERROR) << "Component member must be an object: " << component_member.name.GetString();
return;
}
const auto& component_object = component_member.value.GetObject();
auto error_logs_it = component_object.FindMember("error_logs");
if (error_logs_it == component_object.MemberEnd()) {
FX_LOGS(ERROR) << component_url << " does not have error_logs";
return;
}
if (!error_logs_it->value.IsUint64()) {
FX_LOGS(ERROR) << "error_logs for component " << component_url << "is not uint64";
return;
}
uint64_t new_count = error_logs_it->value.GetUint64();
uint64_t last_count = per_component_error_count_[component_code];
// If the new error count is lower, the component must have restarted, so
// assume all errors are new.
uint64_t diff = new_count < last_count ? new_count : new_count - last_count;
if (diff > 0) {
FX_LOGS(DEBUG) << "Found " << diff << " new error logs for component " << component_url
<< ", total is " << new_count;
}
metrics.per_component_error_count[component_code] = diff;
per_component_error_count_[component_code] = new_count;
}
// Find the total error count.
const rapidjson::Value& new_count_value =
data_vector[0].GetByPath({"root", "log_stats", "error_logs"});
if (!new_count_value.IsUint64()) {
FX_LOGS(ERROR) << "error_logs doesn't exist or is not a uint64";
return;
}
uint64_t new_count = new_count_value.GetUint64();
// If the new error count is lower, Archivist must have restarted, so
// assume all errors are new.
if (new_count < last_reported_error_count_) {
metrics.error_count = new_count;
} else {
metrics.error_count = new_count - last_reported_error_count_;
}
FX_LOGS(DEBUG) << "Current aggregated error count: " << new_count
<< ", since last report: " << metrics.error_count;
last_reported_error_count_ = new_count;
metrics_callback_(metrics);
}
} // namespace cobalt