Observability (structured logging)
Monolog-like conventions live in serenade-observability. The preferred stack is
tracing with tracing-subscriber and tracing-appender.
| Piece | Role |
|---|---|
LoggingConfig |
Log directory, environment stem, levels, stderr/file sinks, rotation |
init / LoggingGuard |
Install the process subscriber; keep the guard until shutdown |
| Channel constants | Tracing target values (serenade::app, serenade::request, …) |
Feature otel |
Optional OpenTelemetry bridge + OTLP/HTTP exporter (OtelConfig, init_with_otel) |
App vs framework
Section titled “App vs framework”| Owner | Responsibility |
|---|---|
| Application | Creates var/log/ (or another path), chooses when to call init, ships .gitignore for logs |
| Framework | Documents defaults, provides LoggingConfig::for_environment, channel names, rolling file helper |
There is no mandatory global logger inside every Serenade crate. Components may emit tracing events when the app has installed a subscriber.
Default layout
Section titled “Default layout”{project}/var/log/dev.log # Environment::Dev (daily rotation prefix){project}/var/log/prod.log{project}/var/log/test.logLoggingConfig::for_environment(&env, project.join("var/log")) sets:
- file stem
{env}.log - default level
DEBUGin debug environments (dev/test), elseINFO - stderr and file enabled
- daily rotation via
tracing-appender
Override filters with SERENADE_LOG (preferred) or RUST_LOG (same directive syntax as EnvFilter).
Init hook
Section titled “Init hook”Call once near the start of main, after resolving Environment and before (or just after) kernel boot:
use serenade_kernel::Environment;use serenade_observability::{init, LoggingConfig};
let environment = Environment::from_name("dev")?;let log_dir = std::path::PathBuf::from("var/log");let _logging = init(&LoggingConfig::for_environment(&environment, log_dir))?;
tracing::info!(target: serenade_observability::APP, "application starting");Keep _logging (LoggingGuard) in scope until process exit so the non-blocking file writer flushes.
OpenTelemetry (feature otel)
Section titled “OpenTelemetry (feature otel)”Enable the crate feature otel to bridge tracing spans into OpenTelemetry. Without an endpoint, spans stay in-process (useful for tests and local wiring). With an OTLP/HTTP collector URL, spans export over HTTP:
use serenade_kernel::Environment;use serenade_observability::{init_with_otel, LoggingConfig, OtelConfig};
let logging = LoggingConfig::for_environment(&Environment::Dev, "var/log");let otel = OtelConfig::new("my-service").with_endpoint("http://127.0.0.1:4318");let (_logging, _otel) = init_with_otel(&logging, &otel)?;Keep both guards until shutdown so the file writer and tracer provider flush.
Channels
Section titled “Channels”| Constant | Target | Typical use |
|---|---|---|
APP |
serenade::app |
Domain / application messages |
REQUEST |
serenade::request |
HTTP request lifecycle |
SECURITY |
serenade::security |
AuthN/Z |
MESSENGER |
serenade::messenger |
Bus dispatch |
KERNEL |
serenade::kernel |
Boot / shutdown |
Filter example: SERENADE_LOG=serenade::app=debug,serenade::security=info.
Messenger apps can implement serenade_messenger::LogSink with tracing::info!(target: serenade_observability::MESSENGER, …).
Profiler
Section titled “Profiler”The Web Debug Toolbar / Profiler is implemented in serenade-profiler (PROFILER.md). Install ProfilerLogLayer on the same subscriber as serenade-observability so request-scoped events fill the Logs panel. This crate still does not embed the toolbar; apps enable the profiler middleware explicitly.
Non-goals
Section titled “Non-goals”- Full Monolog port or PHP handler matrix
- Mandating a second crates.io logging facade forever (
logcrate bridge is app-optional) - ORM/SQL dump as the only surface (profiler DB collector)
