Creare log in Rust e Axum con tracing

Mattepuffo's logo
Creare log in Rust e Axum con tracing

Creare log in Rust e Axum con tracing

In questo articolo vediamo come usare tracing per creare dei log dentro la nostra applicazione Axum.

Salveremo i warning e gli error su file ed imposteremo anche una rotazione.

Queste le dipendenze che dobbiamo aggiungere a cargo:

cargo add tracing tracing-subscriber tracing-appender

A questo punto vi posto tutto il mio main.rs, dove c'è anche la funzione init_tracing che si occupa di inizializzare i log:

mod config;
mod controllers;
mod middleware;
mod models;
mod routes;
mod services;
mod state;

use crate::routes::anagrafiche_routes::anagrafiche_routes;
use axum::routing::get;
use axum::{Json, Router};
use config::Config;
use routes::{auth_routes::auth_routes, utenti_routes::utenti_routes};
use sqlx::mysql::MySqlPoolOptions;
use state::AppState;
use std::net::SocketAddr;
use tower_http::cors::CorsLayer;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_subscriber::Layer;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

pub struct LogGuards {
    _warn: WorkerGuard,
}

pub fn init_tracing() -> LogGuards {
    std::fs::create_dir_all("logs").ok();

    let warn_appender = RollingFileAppender::new(Rotation::DAILY, "logs", "error.log");
    let (warn_writer, guard_warn) = tracing_appender::non_blocking(warn_appender);

    let warn_layer = tracing_subscriber::fmt::layer()
        .with_writer(warn_writer)
        .with_ansi(false)
        .with_filter(tracing_subscriber::filter::LevelFilter::WARN);

    let console_layer = tracing_subscriber::fmt::layer()
        .with_span_events(FmtSpan::CLOSE)
        .with_filter(tracing_subscriber::filter::LevelFilter::INFO);

    tracing_subscriber::registry()
        .with(warn_layer)
        .with(console_layer)
        .init();

    LogGuards {
        _warn: guard_warn,
    }
}

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
    let _guards = init_tracing();
    tracing::warn!("Attenzione, prova warn");
    tracing::error!("Errore di prova");

    dotenvy::dotenv().ok();

    let config = Config::from_env();

    let pool = MySqlPoolOptions::new()
        .max_connections(5)
        .connect(&config.database_url)
        .await?;

    let state = AppState { pool };

    let app = Router::new()
        .route("/", get(root))
        .merge(utenti_routes())
        .merge(auth_routes())
        .merge(anagrafiche_routes())
        .layer(CorsLayer::permissive())
        .with_state(state);

    let addr: SocketAddr = config.server_addr.parse()?;

    tracing::info!("Server avviato: http://{}", addr);

    axum::serve(tokio::net::TcpListener::bind(addr).await?, app).await?;

    Ok(())
}

async fn root() -> Json<String> {
    let version = env!("CARGO_PKG_VERSION");
    Json(format!("App version: {}", version))
}

Vi ho messo degli errori di prova.

Ma volendo potete fare così in altre parti della vostra applicazione:

pub async fn get_by_utente(
    State(state): State<AppState>,
    user: AuthenticatedUser,
) -> Json<Vec<Anagrafica>> {
    let user_id = user.claims.data.id;
    match anagrafiche_service::get_by_utente(&state, user_id).await {
        Ok(data) => Json(data),
        Err(e) => {
            error!("Errore in get_by_utente (utente_id={}): {:?}", user_id, e);
            Json(vec![])
        }
    }
}

Enjoy!


Condividi

Commentami!