Añade gestión de trazas y registro de eventos

- Reexporta macros esenciales de `tracing` para el registro de eventos.
- Inicializa el gestor de trazas en el servidor web.
This commit is contained in:
Manuel Cillero 2025-07-06 23:03:21 +02:00
parent 6b8eee46f9
commit 91139d43d3
8 changed files with 467 additions and 30 deletions

View file

@ -2,11 +2,12 @@
mod figfont;
use crate::{global, service};
use crate::{global, service, trace};
use substring::Substring;
use std::io::Error;
use std::sync::LazyLock;
pub struct Application;
@ -15,6 +16,10 @@ impl Application {
pub fn new() -> Self {
// Al arrancar muestra una cabecera para la aplicación.
Self::show_banner();
// Inicia gestión de trazas y registro de eventos (logging).
LazyLock::force(&trace::TRACING);
Self
}
@ -62,13 +67,15 @@ impl Application {
/// Ejecuta el servidor web de la aplicación.
pub fn run(self) -> Result<service::Server, Error> {
// Prepara el servidor web.
Ok(service::HttpServer::new(move || Self::service_app())
.bind(format!(
"{}:{}",
&global::SETTINGS.server.bind_address,
&global::SETTINGS.server.bind_port
))?
.run())
Ok(service::HttpServer::new(move || {
Self::service_app().wrap(tracing_actix_web::TracingLogger::default())
})
.bind(format!(
"{}:{}",
&global::SETTINGS.server.bind_address,
&global::SETTINGS.server.bind_port
))?
.run())
}
/// Prepara el servidor web de la aplicación para pruebas.

View file

@ -10,48 +10,65 @@ include_config!(SETTINGS: Settings => [
"app.description" => "Developed with the amazing PageTop framework.",
"app.startup_banner" => "Slant",
// [log]
"log.tracing" => "Info",
"log.rolling" => "Stdout",
"log.path" => "log",
"log.prefix" => "tracing.log",
"log.format" => "Full",
// [server]
"server.bind_address" => "localhost",
"server.bind_port" => 8080,
]);
#[derive(Debug, Deserialize)]
/// Ajustes de configuración para las secciones globales [`[app]`](App) y [`[server]`](Server).
/// Consulta [`SETTINGS`] para los valores por defecto.
/// Ajustes para las secciones globales [`[app]`](App), [`[log]`](Log) y [`[server]`](Server) de
/// [`SETTINGS`].
pub struct Settings {
pub app: App,
pub log: Log,
pub server: Server,
}
#[derive(Debug, Deserialize)]
/// Sección `[app]` de la configuración.
///
/// Forma parte de [`Settings`].
/// Sección `[app]` de la configuración. Forma parte de [`Settings`].
pub struct App {
/// Nombre de la aplicación.
/// Valor por defecto: *"Sample"*.
pub name: String,
/// Breve descripción de la aplicación.
/// Valor por defecto: *"Developed with the amazing PageTop framework."*.
pub description: String,
/// ASCII banner printed at startup: *"Off"*, *"Slant"*, *"Small"*, *"Speed"*, or *"Starwars"*.
/// Default: *"Slant"*.
/// Banner ASCII mostrado al inicio: *"Off"* (desactivado), *"Slant"*, *"Small"*, *"Speed"* o
/// *"Starwars"*.
pub startup_banner: String,
/// Modo de ejecución.
/// Valor por defecto: el definido por la variable de entorno
/// `PAGETOP_RUN_MODE`, o *"default"* si no está establecida.
/// Modo de ejecución, dado por la variable de entorno `PAGETOP_RUN_MODE`, o *"default"* si no
/// está definido.
pub run_mode: String,
}
#[derive(Debug, Deserialize)]
/// Sección `[server]` de la configuración.
///
/// Forma parte de [`Settings`].
/// Sección `[log]` de la configuración. Forma parte de [`Settings`].
pub struct Log {
/// Opciones, o combinación de opciones separadas por comas, para filtrar las trazas: *"Error"*,
/// *"Warn"*, *"Info"*, *"Debug"* o *"Trace"*.
/// Ejemplo: "Error,actix_server::builder=Info,tracing_actix_web=Debug".
pub tracing: String,
/// Muestra los mensajes de traza en el terminal (*"Stdout"*) o las registra en archivos con
/// rotación: *"Daily"*, *"Hourly"*, *"Minutely"* o *"Endless"*.
pub rolling: String,
/// Directorio para los archivos de traza (si `rolling` ≠ *"Stdout"*).
pub path: String,
/// Prefijo para los archivos de traza (si `rolling` ≠ *"Stdout"*).
pub prefix: String,
/// Formato de salida de las trazas. Opciones: *"Full"*, *"Compact"*, *"Pretty"* o *"Json"*.
pub format: String,
}
#[derive(Debug, Deserialize)]
/// Sección `[server]` de la configuración. Forma parte de [`Settings`].
pub struct Server {
/// Dirección de enlace para el servidor web.
/// Valor por defecto: *"localhost"*.
pub bind_address: String,
/// Puerto de escucha del servidor web.
/// Valor por defecto: *8088*.
pub bind_port: u16,
}

View file

@ -40,6 +40,8 @@ pub use pagetop_macros::{main, test};
pub mod config;
// Opciones de configuración globales.
pub mod global;
// Gestión de trazas y registro de eventos de la aplicación.
pub mod trace;
// Gestión del servidor y servicios web.
pub mod service;
// Prepara y ejecuta la aplicación.

View file

@ -13,6 +13,8 @@ pub use crate::include_config;
pub use crate::global;
pub use crate::trace;
pub use crate::service;
pub use crate::app::Application;

85
src/trace.rs Normal file
View file

@ -0,0 +1,85 @@
//! Gestión de trazas y registro de eventos de la aplicación.
//!
//! `PageTop` recopila información de diagnóstico de la aplicación de forma estructurada y basada en
//! eventos.
//!
//! En los sistemas asíncronos, interpretar los mensajes de log tradicionales suele volverse
//! complicado. Las tareas individuales se multiplexan en el mismo hilo y los eventos y registros
//! asociados se entremezclan, lo que dificulta seguir la secuencia lógica.
//!
//! `PageTop` usa [`tracing`](https://docs.rs/tracing) para registrar eventos estructurados y con
//! información adicional sobre la *temporalidad* y la *causalidad*. A diferencia de un mensaje de
//! log, un *span* (intervalo) tiene un momento de inicio y de fin, puede entrar y salir del flujo
//! de ejecución y puede existir dentro de un árbol anidado de *spans* similares. Además, estos
//! *spans* son estructurados, con la capacidad de registrar tipos de datos y mensajes de texto.
use crate::global;
pub use tracing::{debug, error, info, trace, warn};
pub use tracing::{debug_span, error_span, info_span, trace_span, warn_span};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::EnvFilter;
use std::sync::LazyLock;
/// Trazado y registro de eventos de la aplicación.
///
/// Para aumentar el rendimiento, un hilo dedicado utiliza un sistema de escritura no bloqueante que
/// actúa de forma periódica en lugar de enviar cada traza o evento al instante. Si el programa
/// termina abruptamente (por ejemplo, debido a un `panic!` o a una llamada a `std::process::exit`),
/// es posible que algunas trazas o eventos no se envíen.
///
/// Dado que las trazas o eventos registrados poco antes de un fallo suelen ser cruciales para
/// diagnosticar la causa, `Lazy<WorkerGuard>` garantiza que todos los registros almacenados se
/// envíen antes de finalizar la ejecución.
#[rustfmt::skip]
pub(crate) static TRACING: LazyLock<WorkerGuard> = LazyLock::new(|| {
let env_filter = EnvFilter::try_new(&global::SETTINGS.log.tracing)
.unwrap_or_else(|_| EnvFilter::new("Info"));
let rolling = global::SETTINGS.log.rolling.to_lowercase();
let (non_blocking, guard) = match rolling.as_str() {
"stdout" => tracing_appender::non_blocking(std::io::stdout()),
_ => tracing_appender::non_blocking({
let path = &global::SETTINGS.log.path;
let prefix = &global::SETTINGS.log.prefix;
match rolling.as_str() {
"daily" => tracing_appender::rolling::daily(path, prefix),
"hourly" => tracing_appender::rolling::hourly(path, prefix),
"minutely" => tracing_appender::rolling::minutely(path, prefix),
"endless" => tracing_appender::rolling::never(path, prefix),
_ => {
println!(
"Rolling value \"{}\" not valid. Using \"daily\". Check the settings file.",
global::SETTINGS.log.rolling,
);
tracing_appender::rolling::daily(path, prefix)
}
}
}),
};
let subscriber = tracing_subscriber::fmt()
.with_env_filter(env_filter)
.with_writer(non_blocking)
.with_ansi(rolling.as_str() == "stdout");
match global::SETTINGS.log.format.to_lowercase().as_str() {
"json" => subscriber.json().init(),
"full" => subscriber.init(),
"compact" => subscriber.compact().init(),
"pretty" => subscriber.pretty().init(),
_ => {
println!(
"Tracing format \"{}\" not valid. Using \"Full\". Check the settings file.",
global::SETTINGS.log.format,
);
subscriber.init();
}
}
guard
});