modio-logger 0.5.2

modio-logger Dbus service
Documentation
// Author: D.S. Ljungmark <spider@skuggor.se>, Modio AB
// SPDX-License-Identifier: AGPL-3.0-or-later
use std::error::Error;
use std::path::PathBuf;

use argh::FromArgs;
use log::{info, warn};

use mocklogger::logger::call_periodic;
use mocklogger::logger::{Logger, LoggerPing};
use mocklogger::logger1::Logger1;
use mocklogger::sd_notify;
use mocklogger::submit1::Submit1;
use mocklogger::timefail;
use mocklogger::LOGGER_NAME;
use mocklogger::LOGGER_PATH;
use modio_logger_db::{Datastore, SqlitePool, SqlitePoolBuilder};

const VERSION: &str = env!("CARGO_PKG_VERSION");

/// The modio-logger
///
/// This is the modio-logger, replacing the python version.
/// It will use databases in /modio/mytemp.sqlite  or development/mytemp.sqlite
#[derive(Debug, FromArgs)]
struct Config {
    /// use session bus
    ///
    /// By default we use the DBus System bus, add this flag to use the Session bus, or "user" bus
    /// instead. Mainly useful when developing.
    #[argh(switch)]
    session: bool,

    /// verbosity
    ///
    /// Add more -v -v -v in order to get a higher log level.
    #[argh(switch, short = 'v')]
    verbose: u32,
}

async fn make_pool(session: bool) -> Result<SqlitePool, Box<dyn Error>> {
    use async_std::fs::File;

    let database_path = if session {
        PathBuf::from("development/mytemp.sqlite")
    } else {
        PathBuf::from("/modio/mytemp.sqlite")
    };

    if !database_path.exists() {
        warn!("DB does not exist. creating {:?}", database_path);
        let _file = File::create(&database_path).await?;
    }
    let pool = SqlitePoolBuilder::new()
        .db_path(&database_path)
        .migrate(true)
        .build()
        .await?;
    Ok(pool)
}

async fn make_logger(session: bool, pool: SqlitePool) -> Result<Logger, Box<dyn Error>> {
    let timefail = timefail::Timefail::session(session);
    let ds = Datastore::new(pool).await?;
    let logger = Logger::new(timefail, ds).await?;
    Ok(logger)
}

const fn verbosity(verbose: u32) -> log::LevelFilter {
    match verbose {
        0 => log::LevelFilter::Error,
        1 => log::LevelFilter::Warn,
        2 => log::LevelFilter::Info,
        3 => log::LevelFilter::Debug,
        _ => log::LevelFilter::Trace,
    }
}

#[async_std::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // Parse commandline options
    let opt: Config = argh::from_env();

    // Set up logging,
    {
        let verbose = verbosity(opt.verbose);
        // sqlx and some others
        let other_verbose = verbosity(opt.verbose.saturating_sub(2));
        // trace level is very spammy for the event loop.
        // Add more -v to see those.
        let spam_verbose = verbosity(opt.verbose.saturating_sub(4));
        env_logger::Builder::new()
            .filter_level(spam_verbose)
            .filter_module("sqlx::query", other_verbose)
            .filter_module("mocklogger", verbose)
            .filter_module("modio_logger_db", verbose)
            .filter_module(module_path!(), verbose)
            .format_timestamp_secs()
            .write_style(env_logger::WriteStyle::Auto)
            .init();
    };

    info!("Configuration: {:#?}", opt);
    info!("Version: {:?}", VERSION);

    let connection = if opt.session {
        zbus::Connection::session().await?
    } else {
        zbus::Connection::system().await?
    };

    let pool = make_pool(opt.session).await?;
    let modio_submit1 = Submit1::builder()
        .datastore_pool(&pool)
        .await
        .development(opt.session)
        .customer(false)
        .build()
        .await?;

    let cust_submit1 = Submit1::builder()
        .datastore_pool(&pool)
        .await
        .development(opt.session)
        .customer(true)
        .build()
        .await?;

    let logger1 = Logger1::builder()
        .datastore_pool(&pool)
        .await
        .development(opt.session)
        .build()
        .await?;
    let logger = make_logger(opt.session, pool).await?;

    let loggerp = LoggerPing {};
    let restful_prime = std::time::Duration::from_millis(29959);

    connection.object_server().at(LOGGER_PATH, logger).await?;
    connection.object_server().at(LOGGER_PATH, loggerp).await?;
    connection.object_server().at(LOGGER_PATH, logger1).await?;
    connection
        .object_server()
        .at("/se/modio/logger/modio", modio_submit1)
        .await?;
    connection
        .object_server()
        .at("/se/modio/logger/customer", cust_submit1)
        .await?;
    connection.request_name(LOGGER_NAME).await?;
    sd_notify::sd_ready().await?;

    loop {
        sd_notify::ping_watchdog().await?;
        // Sleep for a while.
        async_std::task::sleep(restful_prime).await;
        // This takes the interface via the bus
        // See: https://docs.rs/zbus/2.0.0-beta.7/zbus/struct.ObjectServer.html#examples
        // It is needed since the Logger object is now owned by the connection, so we cannot access
        // it directly
        // Instead, we regularly call it here.
        {
            let iface_ref = connection
                .object_server()
                .interface::<_, Logger>(LOGGER_PATH)
                .await?;
            let iface = iface_ref.get_mut().await;
            call_periodic(iface).await?;
        }
    }
}