| // 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. |
| |
| //! The Archivist collects and stores diagnostic data from components. |
| |
| #![warn(missing_docs)] |
| |
| use { |
| anyhow::{Context, Error}, |
| archivist_lib::{archivist, configs, diagnostics, logs}, |
| argh::FromArgs, |
| fidl_fuchsia_diagnostics_internal::{ |
| DetectControllerMarker, LogStatsControllerMarker, SamplerControllerMarker, |
| }, |
| fidl_fuchsia_sys2::EventSourceMarker, |
| fidl_fuchsia_sys_internal::{ComponentEventProviderMarker, LogConnectorMarker}, |
| fuchsia_async as fasync, |
| fuchsia_component::client::connect_to_service, |
| fuchsia_component::server::MissingStartupHandle, |
| fuchsia_syslog, fuchsia_zircon as zx, |
| std::path::PathBuf, |
| tracing::{debug, error, info, warn}, |
| }; |
| |
| /// Monitor, collect, and store diagnostics from components. |
| #[derive(Debug, Default, FromArgs)] |
| pub struct Args { |
| /// disables proxying kernel logger |
| #[argh(switch)] |
| disable_klog: bool, |
| |
| /// disables log connector so that indivisual instances of |
| /// observer don't compete for log connector listener. |
| #[argh(switch)] |
| disable_log_connector: bool, |
| |
| /// whether to connecto to event source or not. |
| #[argh(switch)] |
| disable_event_source: bool, |
| |
| /// initializes syslog library with a log socket to itself |
| #[argh(switch)] |
| consume_own_logs: bool, |
| |
| /// send all logs to environment's LogSink |
| #[argh(switch)] |
| forward_logs: bool, |
| |
| /// serve fuchsia.diagnostics.test.Controller |
| #[argh(switch)] |
| install_controller: bool, |
| |
| /// retrieve a fuchsia.process.Lifecycle handle from the runtime and listen to shutdown events |
| #[argh(switch)] |
| listen_to_lifecycle: bool, |
| |
| /// connect to fuchsia.diagnostics.internal.DetectController |
| #[argh(switch)] |
| connect_to_detect: bool, |
| |
| /// connect to fuchsia.diagnostics.internal.LogStatsController |
| #[argh(switch)] |
| connect_to_log_stats: bool, |
| |
| /// connect to fuchsia.diagnostics.internal.SamplerController |
| #[argh(switch)] |
| connect_to_sampler: bool, |
| |
| /// path to a JSON configuration file |
| #[argh(option)] |
| config_path: PathBuf, |
| } |
| |
| fn main() -> Result<(), Error> { |
| let opt: Args = argh::from_env(); |
| |
| let log_name = "archivist"; |
| let mut log_server = None; |
| if opt.consume_own_logs { |
| let (log_client, server) = zx::Socket::create(zx::SocketOpts::DATAGRAM)?; |
| log_server = Some(server); |
| fuchsia_syslog::init_with_socket_and_name(log_client, log_name)?; |
| info!("Logging started."); |
| logs::redact::emit_canary(); |
| } else { |
| fuchsia_syslog::init_with_tags(&["embedded"])?; |
| } |
| |
| let mut executor = fasync::Executor::new()?; |
| |
| let legacy_event_provider = connect_to_service::<ComponentEventProviderMarker>() |
| .context("failed to connect to event provider")?; |
| |
| diagnostics::init(); |
| |
| let archivist_configuration: configs::Config = match configs::parse_config(&opt.config_path) { |
| Ok(config) => config, |
| Err(parsing_error) => panic!("Parsing configuration failed: {}", parsing_error), |
| }; |
| debug!("Configuration parsed."); |
| |
| let num_threads = archivist_configuration.num_threads; |
| |
| let mut archivist = archivist::Archivist::new(archivist_configuration)?; |
| debug!("Archivist initialized from configuration."); |
| |
| archivist.install_logger_services().add_event_source("v1", Box::new(legacy_event_provider)); |
| |
| if !opt.disable_event_source { |
| let event_source = connect_to_service::<EventSourceMarker>() |
| .context("failed to connect to event source")?; |
| archivist.add_event_source("v2", Box::new(event_source)); |
| } |
| if let Some(log_server) = log_server { |
| fasync::Task::spawn( |
| archivist.data_repo().clone().drain_internal_log_sink(log_server, log_name), |
| ) |
| .detach(); |
| } |
| |
| if opt.forward_logs { |
| archivist.data_repo().clone().forward_logs(); |
| } |
| |
| assert!( |
| !(opt.install_controller && opt.listen_to_lifecycle), |
| "only one shutdown mechanism can be specified." |
| ); |
| |
| if opt.install_controller { |
| archivist.install_controller_service(); |
| } |
| |
| if opt.listen_to_lifecycle { |
| archivist.install_lifecycle_listener(); |
| } |
| |
| if !opt.disable_log_connector { |
| let connector = connect_to_service::<LogConnectorMarker>()?; |
| let sender = archivist.log_sender().clone(); |
| fasync::Task::spawn(archivist.data_repo().clone().handle_log_connector(connector, sender)) |
| .detach(); |
| } |
| |
| if !opt.disable_klog { |
| let debuglog = executor |
| .run_singlethreaded(logs::KernelDebugLog::new()) |
| .context("Failed to read kernel logs")?; |
| fasync::Task::spawn(archivist.data_repo().clone().drain_debuglog(debuglog)).detach(); |
| } |
| |
| let _detect; |
| if opt.connect_to_detect { |
| info!("Starting detect service."); |
| _detect = connect_to_service::<DetectControllerMarker>(); |
| if let Err(e) = &_detect { |
| error!("Couldn't connect to detect: {}", e); |
| } |
| } |
| |
| let _stats; |
| if opt.connect_to_log_stats { |
| info!("Starting log stats service."); |
| _stats = connect_to_service::<LogStatsControllerMarker>(); |
| if let Err(e) = &_stats { |
| error!("Couldn't connect to log stats: {}", e); |
| } |
| } |
| |
| let _sampler; |
| if opt.connect_to_sampler { |
| info!("Starting sampler service."); |
| _sampler = connect_to_service::<SamplerControllerMarker>(); |
| |
| if let Err(e) = &_sampler { |
| error!("Couldn't connect to sampler: {}", e); |
| } |
| } |
| |
| let startup_handle = |
| fuchsia_runtime::take_startup_handle(fuchsia_runtime::HandleType::DirectoryRequest.into()) |
| .ok_or(MissingStartupHandle)?; |
| |
| debug!("Running executor with {} threads.", num_threads); |
| executor.run(archivist.run(zx::Channel::from(startup_handle)), num_threads)?; |
| |
| debug!("Exiting."); |
| Ok(()) |
| } |