Compare commits
2 commits
b3be00574f
...
c985744ab0
| Author | SHA1 | Date | |
|---|---|---|---|
| c985744ab0 | |||
| cdc9357b5a |
8 changed files with 247 additions and 79 deletions
|
|
@ -78,7 +78,7 @@ impl Application {
|
|||
use colored::Colorize;
|
||||
use terminal_size::{terminal_size, Width};
|
||||
|
||||
if global::SETTINGS.app.startup_banner.to_lowercase() != "off" {
|
||||
if global::SETTINGS.app.startup_banner != global::StartupBanner::Off {
|
||||
// Nombre de la aplicación, ajustado al ancho del terminal si es necesario.
|
||||
let mut app_ff = String::new();
|
||||
let app_name = &global::SETTINGS.app.name;
|
||||
|
|
|
|||
|
|
@ -10,21 +10,11 @@ pub static FIGFONT: LazyLock<FIGfont> = LazyLock::new(|| {
|
|||
let speed = include_str!("speed.flf");
|
||||
let starwars = include_str!("starwars.flf");
|
||||
|
||||
FIGfont::from_content(
|
||||
match global::SETTINGS.app.startup_banner.to_lowercase().as_str() {
|
||||
"off" => slant,
|
||||
"slant" => slant,
|
||||
"small" => small,
|
||||
"speed" => speed,
|
||||
"starwars" => starwars,
|
||||
_ => {
|
||||
println!(
|
||||
"\n FIGfont \"{}\" not found for banner. Using \"Slant\". Check settings.",
|
||||
global::SETTINGS.app.startup_banner,
|
||||
);
|
||||
slant
|
||||
}
|
||||
},
|
||||
)
|
||||
FIGfont::from_content(match global::SETTINGS.app.startup_banner {
|
||||
global::StartupBanner::Off | global::StartupBanner::Slant => slant,
|
||||
global::StartupBanner::Small => small,
|
||||
global::StartupBanner::Speed => speed,
|
||||
global::StartupBanner::Starwars => starwars,
|
||||
})
|
||||
.unwrap()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
//! Opciones de configuración globales.
|
||||
|
||||
use crate::{include_config, AutoDefault};
|
||||
use crate::include_config;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
mod lang_negotiation;
|
||||
pub use lang_negotiation::LangNegotiation;
|
||||
|
||||
mod startup_banner;
|
||||
pub use startup_banner::StartupBanner;
|
||||
|
||||
mod log_rolling;
|
||||
pub use log_rolling::LogRolling;
|
||||
|
||||
mod log_format;
|
||||
pub use log_format::LogFormat;
|
||||
|
||||
// **< SETTINGS >***********************************************************************************
|
||||
|
||||
include_config!(SETTINGS: Settings => [
|
||||
|
|
@ -32,35 +44,6 @@ include_config!(SETTINGS: Settings => [
|
|||
"server.session_lifetime" => 604_800,
|
||||
]);
|
||||
|
||||
// **< LangNegotiation >****************************************************************************
|
||||
|
||||
/// Modos disponibles para negociar el idioma de una petición HTTP.
|
||||
///
|
||||
/// El ajuste [`global::SETTINGS.app.lang_negotiation`](crate::global::App::lang_negotiation)
|
||||
/// determina qué fuentes intervienen en la resolución del idioma efectivo utilizado por
|
||||
/// [`RequestLocale`](crate::locale::RequestLocale) y en la generación de URLs mediante
|
||||
/// [`Context::route()`](crate::core::component::Context::route).
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
|
||||
pub enum LangNegotiation {
|
||||
/// Usa todas las fuentes disponibles para determinar el idioma, en este orden: comprueba el
|
||||
/// parámetro `?lang` de la URL; si no está presente o no es válido, usa la cabecera HTTP
|
||||
/// `Accept-Language`; si tampoco está disponible o no es válido, usa el idioma configurado en
|
||||
/// [`global::SETTINGS.app.language`](crate::global::App::language) o, en su defecto, el idioma
|
||||
/// de respaldo. Es el comportamiento por defecto.
|
||||
#[default]
|
||||
Full,
|
||||
|
||||
/// Igual que `LangNegotiation::Full`, pero sin tener en cuenta el parámetro `?lang` de la URL.
|
||||
/// El idioma depende únicamente de la cabecera `Accept-Language` del navegador y, en última
|
||||
/// instancia, de la configuración o idioma de respaldo.
|
||||
NoQuery,
|
||||
|
||||
/// Usa sólo la configuración o, en su defecto, el idioma de respaldo; ignora la cabecera
|
||||
/// `Accept-Language` y el parámetro de la URL. Este modo proporciona un comportamiento estable
|
||||
/// con idioma fijo.
|
||||
ConfigOnly,
|
||||
}
|
||||
|
||||
// **< Settings >***********************************************************************************
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
@ -101,7 +84,7 @@ pub struct App {
|
|||
pub lang_negotiation: LangNegotiation,
|
||||
/// Banner ASCII mostrado al inicio: *"Off"* (desactivado), *"Slant"*, *"Small"*, *"Speed"* o
|
||||
/// *"Starwars"*.
|
||||
pub startup_banner: String,
|
||||
pub startup_banner: StartupBanner,
|
||||
/// Activa la página de bienvenida de PageTop.
|
||||
///
|
||||
/// Si está activada, se instala la extensión [`Welcome`](crate::base::extension::Welcome), que
|
||||
|
|
@ -137,13 +120,13 @@ pub struct Log {
|
|||
pub tracing: String,
|
||||
/// Muestra los mensajes de traza en el terminal (*"Stdout"*) o los vuelca en archivos con
|
||||
/// rotación: *"Daily"*, *"Hourly"*, *"Minutely"* o *"Endless"*.
|
||||
pub rolling: String,
|
||||
pub rolling: LogRolling,
|
||||
/// Directorio para los archivos de traza (si [`rolling`](Self::rolling) ≠ *"Stdout"*).
|
||||
pub path: String,
|
||||
/// Prefijo para los archivos de traza (si [`rolling`](Self::rolling) ≠ *"Stdout"*).
|
||||
pub prefix: String,
|
||||
/// Formato de salida de las trazas. Opciones: *"Full"*, *"Compact"*, *"Pretty"* o *"Json"*.
|
||||
pub format: String,
|
||||
pub format: LogFormat,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
|
|
|||
56
src/global/lang_negotiation.rs
Normal file
56
src/global/lang_negotiation.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use crate::AutoDefault;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
/// Modos disponibles para negociar el idioma de una petición HTTP.
|
||||
///
|
||||
/// El ajuste [`global::SETTINGS.app.lang_negotiation`](crate::global::App::lang_negotiation)
|
||||
/// determina qué fuentes intervienen en la resolución del idioma efectivo utilizado por
|
||||
/// [`RequestLocale`](crate::locale::RequestLocale) y en la generación de URLs mediante
|
||||
/// [`Context::route()`](crate::core::component::Context::route).
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum LangNegotiation {
|
||||
/// Usa todas las fuentes disponibles para determinar el idioma, en este orden: comprueba el
|
||||
/// parámetro `?lang` de la URL; si no está presente o no es válido, usa la cabecera HTTP
|
||||
/// `Accept-Language`; si tampoco está disponible o no es válido, usa el idioma configurado en
|
||||
/// [`global::SETTINGS.app.language`](crate::global::App::language) o, en su defecto, el idioma
|
||||
/// de respaldo. Es el comportamiento por defecto.
|
||||
#[default]
|
||||
Full,
|
||||
|
||||
/// Igual que `LangNegotiation::Full`, pero sin tener en cuenta el parámetro `?lang` de la URL.
|
||||
/// El idioma depende únicamente de la cabecera `Accept-Language` del navegador y, en última
|
||||
/// instancia, de la configuración o idioma de respaldo.
|
||||
NoQuery,
|
||||
|
||||
/// Usa sólo la configuración o, en su defecto, el idioma de respaldo; ignora la cabecera
|
||||
/// `Accept-Language` y el parámetro de la URL. Este modo proporciona un comportamiento estable
|
||||
/// con idioma fijo.
|
||||
ConfigOnly,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LangNegotiation {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let raw = String::deserialize(deserializer)?;
|
||||
let result = match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"full" => Self::Full,
|
||||
"noquery" => Self::NoQuery,
|
||||
"configonly" => Self::ConfigOnly,
|
||||
_ => {
|
||||
let default = Self::default();
|
||||
println!(
|
||||
concat!(
|
||||
"\nInvalid value \"{}\" for [app].lang_negotiation. ",
|
||||
"Using \"{:?}\". Check settings.",
|
||||
),
|
||||
raw, default,
|
||||
);
|
||||
default
|
||||
}
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
51
src/global/log_format.rs
Normal file
51
src/global/log_format.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
use crate::AutoDefault;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
/// Formatos disponibles para mostrar las trazas.
|
||||
///
|
||||
/// El valor se obtiene de [`global::SETTINGS.log.format`](crate::global::Log::format) y determina
|
||||
/// la representación textual de los eventos registrados por `tracing`.
|
||||
/// El valor configurado no distingue entre mayúsculas y minúsculas.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum LogFormat {
|
||||
/// Formato JSON estructurado.
|
||||
Json,
|
||||
|
||||
/// Formato completo con detalles adicionales. Es el valor por defecto.
|
||||
#[default]
|
||||
Full,
|
||||
|
||||
/// Formato más conciso y legible.
|
||||
Compact,
|
||||
|
||||
/// Formato human-friendly con colores y saltos de línea.
|
||||
Pretty,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LogFormat {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let raw = String::deserialize(deserializer)?;
|
||||
let result = match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"json" => Self::Json,
|
||||
"full" => Self::Full,
|
||||
"compact" => Self::Compact,
|
||||
"pretty" => Self::Pretty,
|
||||
_ => {
|
||||
let default = Self::default();
|
||||
println!(
|
||||
concat!(
|
||||
"\nInvalid value \"{}\" for [log].format. ",
|
||||
"Using \"{:?}\". Check settings.",
|
||||
),
|
||||
raw, default,
|
||||
);
|
||||
default
|
||||
}
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
51
src/global/log_rolling.rs
Normal file
51
src/global/log_rolling.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
use crate::AutoDefault;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
/// Modos de salida y rotación para el registro de trazas.
|
||||
///
|
||||
/// El valor se obtiene de [`global::SETTINGS.log.rolling`](crate::global::Log::rolling) y
|
||||
/// determina si las trazas se muestran por pantalla o se vuelcan en archivos con rotación.
|
||||
/// El valor configurado no distingue entre mayúsculas y minúsculas.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum LogRolling {
|
||||
/// Escribe las trazas en la salida estándar (sin rotación de archivos).
|
||||
Stdout,
|
||||
/// Rotación diaria de archivos de traza.
|
||||
#[default]
|
||||
Daily,
|
||||
/// Rotación horaria de archivos de traza.
|
||||
Hourly,
|
||||
/// Rotación por minutos de archivos de traza.
|
||||
Minutely,
|
||||
/// Archivo de traza "infinito", sin rotación.
|
||||
Endless,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LogRolling {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let raw = String::deserialize(deserializer)?;
|
||||
let result = match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"stdout" => Self::Stdout,
|
||||
"daily" => Self::Daily,
|
||||
"hourly" => Self::Hourly,
|
||||
"minutely" => Self::Minutely,
|
||||
"endless" => Self::Endless,
|
||||
_ => {
|
||||
let default = Self::default();
|
||||
println!(
|
||||
concat!(
|
||||
"\nInvalid value \"{}\" for [log].rolling. ",
|
||||
"Using \"{:?}\". Check settings.",
|
||||
),
|
||||
raw, default,
|
||||
);
|
||||
default
|
||||
}
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
50
src/global/startup_banner.rs
Normal file
50
src/global/startup_banner.rs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
use crate::AutoDefault;
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
/// Opciones para el *banner* ASCII mostrado al arrancar la aplicación.
|
||||
///
|
||||
/// Se obtiene de [`global::SETTINGS.app.startup_banner`](crate::global::App::startup_banner) y
|
||||
/// controla si se muestra un *banner* en la salida estándar al arrancar la aplicación.
|
||||
#[derive(AutoDefault, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum StartupBanner {
|
||||
/// No muestra ningún banner de inicio.
|
||||
Off,
|
||||
/// Banner en estilo "Slant". Es el comportamiento por defecto.
|
||||
#[default]
|
||||
Slant,
|
||||
/// Banner en estilo "Small".
|
||||
Small,
|
||||
/// Banner en estilo "Speed".
|
||||
Speed,
|
||||
/// Banner en estilo "Starwars".
|
||||
Starwars,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for StartupBanner {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let raw = String::deserialize(deserializer)?;
|
||||
let result = match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"off" => Self::Off,
|
||||
"slant" => Self::Slant,
|
||||
"small" => Self::Small,
|
||||
"speed" => Self::Speed,
|
||||
"starwars" => Self::Starwars,
|
||||
_ => {
|
||||
let default = Self::default();
|
||||
println!(
|
||||
concat!(
|
||||
"\nInvalid value \"{}\" for [app].startup_banner. ",
|
||||
"Using \"{:?}\". Check settings.",
|
||||
),
|
||||
raw, default,
|
||||
);
|
||||
default
|
||||
}
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
45
src/trace.rs
45
src/trace.rs
|
|
@ -14,6 +14,7 @@
|
|||
//! *spans* son estructurados, con la capacidad de registrar tipos de datos y mensajes de texto.
|
||||
|
||||
use crate::global;
|
||||
use crate::global::{LogFormat, LogRolling};
|
||||
|
||||
pub use tracing::{debug, error, info, trace, warn};
|
||||
pub use tracing::{debug_span, error_span, info_span, trace_span, warn_span};
|
||||
|
|
@ -33,7 +34,6 @@ use std::sync::LazyLock;
|
|||
/// 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(|| {
|
||||
if !global::SETTINGS.log.enabled || cfg!(test) || cfg!(feature = "testing") {
|
||||
// Tracing desactivado, se instala un subscriber nulo.
|
||||
|
|
@ -46,25 +46,19 @@ 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 rolling = global::SETTINGS.log.rolling;
|
||||
|
||||
let (non_blocking, guard) = match rolling.as_str() {
|
||||
"stdout" => tracing_appender::non_blocking(std::io::stdout()),
|
||||
let (non_blocking, guard) = match rolling {
|
||||
LogRolling::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)
|
||||
}
|
||||
match rolling {
|
||||
LogRolling::Daily => tracing_appender::rolling::daily(path, prefix),
|
||||
LogRolling::Hourly => tracing_appender::rolling::hourly(path, prefix),
|
||||
LogRolling::Minutely => tracing_appender::rolling::minutely(path, prefix),
|
||||
LogRolling::Endless => tracing_appender::rolling::never(path, prefix),
|
||||
LogRolling::Stdout => unreachable!("Stdout rolling already handled above"),
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
|
@ -72,20 +66,13 @@ pub(crate) static TRACING: LazyLock<WorkerGuard> = LazyLock::new(|| {
|
|||
let subscriber = tracing_subscriber::fmt()
|
||||
.with_env_filter(env_filter)
|
||||
.with_writer(non_blocking)
|
||||
.with_ansi(rolling.as_str() == "stdout");
|
||||
.with_ansi(matches!(rolling, LogRolling::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();
|
||||
}
|
||||
match global::SETTINGS.log.format {
|
||||
LogFormat::Json => subscriber.json().init(),
|
||||
LogFormat::Full => subscriber.init(),
|
||||
LogFormat::Compact => subscriber.compact().init(),
|
||||
LogFormat::Pretty => subscriber.pretty().init(),
|
||||
}
|
||||
|
||||
guard
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue