diff --git a/crates/solti-observe/src/lib.rs b/crates/solti-observe/src/lib.rs index fe8a6d0..3a5f176 100644 --- a/crates/solti-observe/src/lib.rs +++ b/crates/solti-observe/src/lib.rs @@ -1,3 +1,28 @@ +//! Observability primitives for the Solti task execution system. +//! +//! ## Modules +//! +//! | Module | Feature | Description | +//! |------------------------------------|-----------------|----------------------------------------------------------| +//! | [`LoggerConfig`] / [`init_logger`] | - | Configurable tracing subscriber (text / json / journald) | +//! | [`TracingEventSubscriber`] | `subscriber` | Logs taskvisor events via [`tracing`] | +//! | [`timezone_sync`] | `timezone-sync` | Periodic task that re-detects the local UTC offset | +//! +//! ## Quick start +//! +//! ```rust,ignore +//! use solti_observe::{LoggerConfig, LoggerLevel, init_local_offset, init_logger}; +//! +//! // Must be called before spawning threads (tokio runtime). +//! init_local_offset(); +//! +//! let cfg = LoggerConfig { +//! level: LoggerLevel::new("info")?, +//! ..Default::default() +//! }; +//! init_logger(&cfg)?; +//! ``` + mod logger; pub use logger::*; diff --git a/crates/solti-observe/src/logger/error.rs b/crates/solti-observe/src/logger/error.rs index e72b8a6..77454a6 100644 --- a/crates/solti-observe/src/logger/error.rs +++ b/crates/solti-observe/src/logger/error.rs @@ -23,5 +23,3 @@ pub enum LoggerError { #[error("Invalid log level: {0}")] InvalidLevel(String), } - -pub type LoggerResult = Result; diff --git a/crates/solti-observe/src/logger/log.rs b/crates/solti-observe/src/logger/log.rs index 8924634..5a1c74d 100644 --- a/crates/solti-observe/src/logger/log.rs +++ b/crates/solti-observe/src/logger/log.rs @@ -1,32 +1,30 @@ use tracing::Subscriber; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; -use crate::logger::{ - config::LoggerConfig, - error::{LoggerError, LoggerResult}, - object::LoggerRfc3339, -}; +use crate::logger::{config::LoggerConfig, error::LoggerError, object::LoggerRfc3339}; /// Initializes text logger. -pub fn logger_text(cfg: &LoggerConfig) -> LoggerResult<()> { +pub fn logger_text(cfg: &LoggerConfig) -> Result<(), LoggerError> { let filter = cfg.level.to_env_filter(); + let timer = LoggerRfc3339::new(cfg.tz); let fmt_layer = fmt::layer() .with_ansi(cfg.should_use_color()) .with_target(cfg.with_targets) - .with_timer(LoggerRfc3339); + .with_timer(timer); let subscriber = tracing_subscriber::registry().with(filter).with(fmt_layer); init_subscriber(subscriber) } /// Initializes JSON (structured) logger. -pub fn logger_json(cfg: &LoggerConfig) -> LoggerResult<()> { +pub fn logger_json(cfg: &LoggerConfig) -> Result<(), LoggerError> { let filter = cfg.level.to_env_filter(); + let timer = LoggerRfc3339::new(cfg.tz); let fmt_layer = fmt::layer() .json() .with_ansi(false) .with_target(cfg.with_targets) - .with_timer(LoggerRfc3339); + .with_timer(timer); let subscriber = tracing_subscriber::registry().with(filter).with(fmt_layer); init_subscriber(subscriber) @@ -34,7 +32,7 @@ pub fn logger_json(cfg: &LoggerConfig) -> LoggerResult<()> { /// Initializes journald logger (Linux only). #[cfg(target_os = "linux")] -pub fn logger_journald(cfg: &LoggerConfig) -> LoggerResult<()> { +pub fn logger_journald(cfg: &LoggerConfig) -> Result<(), LoggerError> { let filter = cfg.level.to_env_filter(); let journald = tracing_journald::layer().map_err(|e| LoggerError::JournaldInitFailed(e.to_string()))?; @@ -45,12 +43,12 @@ pub fn logger_journald(cfg: &LoggerConfig) -> LoggerResult<()> { /// Stub for journald on non-Linux platforms. #[cfg(not(all(target_os = "linux")))] -pub fn logger_journald(_cfg: &LoggerConfig) -> LoggerResult<()> { +pub fn logger_journald(_cfg: &LoggerConfig) -> Result<(), LoggerError> { Err(LoggerError::JournaldNotSupported) } /// Installs the subscriber as the global default. -fn init_subscriber(subscriber: S) -> LoggerResult<()> +fn init_subscriber(subscriber: S) -> Result<(), LoggerError> where S: Subscriber + Send + Sync + 'static, { diff --git a/crates/solti-observe/src/logger/object/format.rs b/crates/solti-observe/src/logger/object/format.rs index 579cbcb..e85788c 100644 --- a/crates/solti-observe/src/logger/object/format.rs +++ b/crates/solti-observe/src/logger/object/format.rs @@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize, Serializer}; use crate::logger::LoggerError; /// Output format for the logger. -/// - `Text` — human-friendly, colored (when enabled) text logs. -/// - `Json` — structured JSON logs for machines / log collectors. +/// - `Text` - colored (when enabled) text logs. +/// - `Json` — structured JSON logs. /// - `Journald` — logs are sent to systemd-journald (Linux only). #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] @@ -29,6 +29,7 @@ impl FromStr for LoggerFormat { type Err = LoggerError; fn from_str(s: &str) -> Result { let norm = s.trim().to_ascii_lowercase(); + match norm.as_str() { "text" => Ok(Self::Text), "json" => Ok(Self::Json), diff --git a/crates/solti-observe/src/logger/object/rfc3339.rs b/crates/solti-observe/src/logger/object/rfc3339.rs index c77dda1..785179e 100644 --- a/crates/solti-observe/src/logger/object/rfc3339.rs +++ b/crates/solti-observe/src/logger/object/rfc3339.rs @@ -1,30 +1,40 @@ use std::fmt; -use time::{OffsetDateTime, format_description::well_known::Rfc3339}; +use time::{OffsetDateTime, UtcOffset, format_description::well_known::Rfc3339}; use tracing_subscriber::fmt::{format::Writer, time::FormatTime}; -use crate::logger::object::timezone::get_or_detect_local_offset; +use crate::logger::object::timezone::{LoggerTimeZone, get_or_detect_local_offset}; -/// Dynamic RFC3339 timestamp formatter with local timezone support. +/// Dynamic RFC3339 timestamp formatter that respects [`LoggerTimeZone`]. /// -/// Reads the current local offset on every invocation, allowing timezone -/// changes to be reflected in logs without subscriber reinitialization. -/// -/// Falls back to UTC if offset detection fails. +/// - [`LoggerTimeZone::Utc`] — always formats as `…+00:00` +/// - [`LoggerTimeZone::Local`] — reads the cached local offset on every call, +/// so DST changes picked up by [`crate::timezone_sync`] are reflected +/// without subscriber reinitialization. #[derive(Debug, Clone, Copy)] -pub struct LoggerRfc3339; +pub struct LoggerRfc3339 { + tz: LoggerTimeZone, +} + +impl LoggerRfc3339 { + /// Create a formatter for the given timezone setting. + pub fn new(tz: LoggerTimeZone) -> Self { + Self { tz } + } +} impl FormatTime for LoggerRfc3339 { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { - let local = OffsetDateTime::now_utc().to_offset(get_or_detect_local_offset()); + let offset = match self.tz { + LoggerTimeZone::Utc => UtcOffset::UTC, + LoggerTimeZone::Local => get_or_detect_local_offset(), + }; + + let ts = OffsetDateTime::now_utc().to_offset(offset); - match local.format(&Rfc3339) { - Ok(ts) => { - write!(w, "{} ", ts) - } - Err(_) => { - write!(w, " ") - } + match ts.format(&Rfc3339) { + Ok(s) => write!(w, "{s} "), + Err(_) => write!(w, " "), } } } diff --git a/crates/solti-observe/src/logger/object/timezone.rs b/crates/solti-observe/src/logger/object/timezone.rs index dc7de34..4653680 100644 --- a/crates/solti-observe/src/logger/object/timezone.rs +++ b/crates/solti-observe/src/logger/object/timezone.rs @@ -90,9 +90,14 @@ pub fn init_local_offset() { if let Ok(mut guard) = LOCAL_OFFSET.write() { *guard = offset; } + let _ = INIT_DONE.set(()); } -/// Synchronizes local offset. +/// Re-detects the system UTC offset and updates the global cache. +/// +/// Called periodically by the [`crate::timezone_sync`] task. +/// If detection fails (common in multi-threaded contexts on Unix), +/// the existing cached offset is preserved and no error is returned. pub(crate) fn sync_local_offset() -> Result<(), LoggerError> { match UtcOffset::current_local_offset() { Ok(new_offset) => { @@ -118,7 +123,11 @@ pub(crate) fn sync_local_offset() -> Result<(), LoggerError> { } } -/// Returns current local offset for timestamp formatting. +/// Returns the cached local offset for timestamp formatting. +/// +/// On first call (if [`init_local_offset`] was never called) attempts a +/// one-shot detection. On failure prints a warning to stderr and falls +/// back to UTC. pub(crate) fn get_or_detect_local_offset() -> UtcOffset { INIT_DONE.get_or_init(|| match UtcOffset::current_local_offset() { Ok(detected) => { diff --git a/crates/solti-observe/src/logger/tasks/timezone_sync.rs b/crates/solti-observe/src/logger/tasks/timezone_sync.rs index 8386bff..3847050 100644 --- a/crates/solti-observe/src/logger/tasks/timezone_sync.rs +++ b/crates/solti-observe/src/logger/tasks/timezone_sync.rs @@ -8,27 +8,33 @@ use tracing::debug; use crate::logger::object::timezone::sync_local_offset; -/// Logical slot name used for timezone sync task. -/// -/// Ensures that only one sync operation runs at any time. +/// Logical slot name for the timezone sync task. pub const TZ_SYNC_SLOT: &str = "solti-logger-tz-sync"; -/// Per-attempt timeout in milliseconds. -/// -/// If syncing the timezone offset takes longer than this limit, -/// the task is considered failed and restart/backoff logic applies. +/// Per-attempt timeout (ms). pub const TZ_SYNC_TIMEOUT_MS: u64 = 60_000; -/// Delay between successful sync attempts in milliseconds. -/// -/// This defines the periodic nature of the timezone-sync task. -pub const TZ_SYNC_RETRY_MS: u64 = 3_600_000; +/// Interval between successful sync attempts (ms). +pub const TZ_SYNC_PERIOD_MS: u64 = 3_600_000; + +/// Initial backoff delay on failure (ms). +const BACKOFF_FIRST_MS: u64 = 5_000; -/// Build the timezone sync task and its model-level specification. +/// Maximum backoff delay on repeated failures (ms). +const BACKOFF_MAX_MS: u64 = 300_000; + +/// Backoff multiplier per consecutive failure. +const BACKOFF_FACTOR: f64 = 2.0; + +/// Builds the timezone sync task and its supervision specification. +/// +/// Returns a `(TaskRef, CreateSpec)` pair ready to be submitted to a [`taskvisor::Supervisor`] via `submit_with_task`. /// -/// Returns: -/// - [`TaskRef`] — executable task body. -/// - [`CreateSpec`] — restart/backoff/admission policy and slot binding. +/// # Behaviour +/// - On **success**: next run is scheduled after [`TZ_SYNC_PERIOD_MS`]. +/// - On **failure**: exponential backoff from 5 s to 5 min with equal jitter. +/// - **Admission**: [`AdmissionStrategy::Replace`] — duplicate submissions +/// cancel the in-flight attempt and reschedule. pub fn timezone_sync() -> (TaskRef, CreateSpec) { let task: TaskRef = TaskFn::arc(TZ_SYNC_SLOT, |ctx: CancellationToken| async move { debug!("timezone sync started"); @@ -49,18 +55,21 @@ pub fn timezone_sync() -> (TaskRef, CreateSpec) { let backoff = BackoffStrategy { jitter: JitterStrategy::Equal, - first_ms: TZ_SYNC_TIMEOUT_MS, - max_ms: TZ_SYNC_TIMEOUT_MS, - factor: 1.0, + first_ms: BACKOFF_FIRST_MS, + max_ms: BACKOFF_MAX_MS, + factor: BACKOFF_FACTOR, }; + let spec = CreateSpec { + restart: RestartStrategy::periodic(TZ_SYNC_PERIOD_MS), slot: TZ_SYNC_SLOT.to_string(), timeout_ms: TZ_SYNC_TIMEOUT_MS, - restart: RestartStrategy::periodic(TZ_SYNC_RETRY_MS), - backoff, + admission: AdmissionStrategy::Replace, - kind: TaskKind::None, labels: RunnerLabels::default(), + kind: TaskKind::None, + backoff, }; + (task, spec) } diff --git a/crates/solti-observe/src/subscriber/mod.rs b/crates/solti-observe/src/subscriber/mod.rs index 2de81de..8921cef 100644 --- a/crates/solti-observe/src/subscriber/mod.rs +++ b/crates/solti-observe/src/subscriber/mod.rs @@ -1,9 +1,23 @@ #![cfg(feature = "subscriber")] -//! Event logging subscriber for Taskvisor. +//! Taskvisor event logger built on the [`tracing`] framework. //! -//! Maps Taskvisor events to structured tracing logs with appropriate severity levels. -//! Processes events asynchronously via bounded queue to avoid blocking the event system. +//! [`TracingEventSubscriber`] implements [`Subscribe`] and maps every +//! [`EventKind`] to the appropriate tracing severity level with structured +//! fields (`task`, `attempt`, `delay_ms`, `reason`, …). +//! +//! Events are consumed asynchronously via a bounded queue so the +//! subscriber never blocks the supervision loop. +//! +//! ## Log level mapping +//! +//! | Level | Events | +//! |---------|--------------------------------------------------------------| +//! | `trace` | TaskAddRequested, TaskRemoveRequested, TaskRemoved, TaskStopped, ControllerSubmitted | +//! | `debug` | TaskAdded, ActorExhausted, BackoffScheduled, ControllerSlotTransition | +//! | `info` | TaskStarting, ShutdownRequested, AllStoppedWithinGrace | +//! | `warn` | GraceExceeded, TimeoutHit, ControllerRejected | +//! | `error` | TaskFailed, ActorDead, SubscriberPanicked, SubscriberOverflow | use std::borrow::Borrow; @@ -11,51 +25,67 @@ use async_trait::async_trait; use taskvisor::{Event, EventKind, Subscribe}; use tracing::{debug, error, info, trace, warn}; -/// Subscriber that logs all Taskvisor events using the tracing framework. +/// Taskvisor event subscriber that logs every event via [`tracing`]. +/// +/// Register as a [`Subscribe`] implementation alongside other subscribers +/// (e.g. [`crate::PrometheusSubscriber`]) so that supervision events appear +/// in the application log output. +/// +/// # Example /// -/// Events are processed asynchronously with structured fields (task, attempt, etc.). -/// Queue overflow results in `SubscriberOverflow` events being emitted. +/// ```rust,ignore +/// use std::sync::Arc; +/// use solti_observe::TracingEventSubscriber; +/// use taskvisor::Subscribe; +/// +/// let subscribers: Vec> = vec![ +/// Arc::new(TracingEventSubscriber), +/// ]; +/// ``` #[derive(Default)] -pub struct Subscriber; +pub struct TracingEventSubscriber; /// Queue capacity sized for ~2K events/sec burst with sub-millisecond processing. -/// On overflow, events are dropped and `SubscriberOverflow` event is emitted (non-blocking). -const SUBSCRIBER_QUEUE_CAPACITY: usize = 2048; +/// +/// On overflow events are dropped and a [`EventKind::SubscriberOverflow`] event +/// is emitted by taskvisor (non-blocking). +const QUEUE_CAPACITY: usize = 2048; #[async_trait] -impl Subscribe for Subscriber { +impl Subscribe for TracingEventSubscriber { async fn on_event(&self, event: &Event) { log_event(event); } fn name(&self) -> &'static str { - "subscriber" + "tracing" } fn queue_capacity(&self) -> usize { - SUBSCRIBER_QUEUE_CAPACITY + QUEUE_CAPACITY } } -/// Logs an event with appropriate tracing level and structured fields. +/// Logs a single event at the appropriate tracing level with structured fields. /// -/// This is public to allow custom subscribers to reuse the same logging logic. -fn log_event(e: E) { +/// Accepts anything that implements [`Borrow`], so both `&Event` and +/// owned `Event` work transparently. +pub fn log_event(e: E) { let msg = message_for(e.kind()); match e.kind() { - // Management - trace level for routine operations + // Management — trace level for routine operations EventKind::TaskRemoveRequested => trace!(task = e.as_task(), "{msg}"), EventKind::TaskAddRequested => trace!(task = e.as_task(), "{msg}"), EventKind::TaskRemoved => trace!(task = e.as_task(), "{msg}"), EventKind::TaskAdded => debug!(task = e.as_task(), "{msg}"), - // Shutdown - info/warn for lifecycle events + // Shutdown — info/warn for lifecycle events EventKind::ShutdownRequested => info!("{msg}"), EventKind::AllStoppedWithinGrace => info!("{msg}"), EventKind::GraceExceeded => warn!("{msg}"), - // Subscriber errors - always error level + // Subscriber errors — always error level EventKind::SubscriberPanicked => { error!(task = e.as_task(), reason = e.as_reason(), "{msg}") } @@ -63,7 +93,7 @@ fn log_event(e: E) { error!(task = e.as_task(), reason = e.as_reason(), "{msg}") } - // Terminal states - debug for exhausted, error for dead + // Terminal states — debug for exhausted, error for dead EventKind::ActorExhausted => { debug!(task = e.as_task(), reason = e.as_reason(), "{msg}") } @@ -71,7 +101,7 @@ fn log_event(e: E) { error!(task = e.as_task(), reason = e.as_reason(), "{msg}") } - // Lifecycle events + // Lifecycle EventKind::TimeoutHit => { warn!(task = e.as_task(), timeout_ms = e.timeout_ms(), "{msg}") } @@ -88,7 +118,7 @@ fn log_event(e: E) { "{msg}" ), - // Backoff - differentiate retry vs scheduled next run + // Backoff — differentiate retry vs scheduled next run EventKind::BackoffScheduled => { if e.has_reason() { debug!( @@ -108,7 +138,7 @@ fn log_event(e: E) { } } - // Controller events + // Controller EventKind::ControllerRejected => { warn!(task = e.as_task(), reason = e.as_reason(), "{msg}") } @@ -123,14 +153,22 @@ fn log_event(e: E) { /// Helper trait for extracting event fields with sensible defaults. /// -/// This is internal to reduce boilerplate in `log_event`. -trait View { +/// Blanket-implemented for anything that implements [`Borrow`]. +/// Used internally by [`log_event`] to reduce boilerplate. +pub trait View { + /// Task name, or `"unknown"` if absent. fn as_task(&self) -> &str; + /// Reason string, or `"unknown"` if absent. fn as_reason(&self) -> &str; + /// Attempt number, or `0` if absent. fn attempt(&self) -> u32; + /// Backoff delay in milliseconds, or `0` if absent. fn delay_ms(&self) -> u32; + /// Timeout in milliseconds, or `0` if absent. fn timeout_ms(&self) -> u32; + /// The event kind. fn kind(&self) -> EventKind; + /// Whether the event carries a reason field. fn has_reason(&self) -> bool; } @@ -174,9 +212,9 @@ where } } -/// Returns a human-readable description for each event kind. +/// Human-readable description for each event kind. /// -/// These messages are used as the primary log message, with structured fields providing additional context. +/// Used as the primary log message; structured fields provide additional context. #[inline] fn message_for(kind: EventKind) -> &'static str { match kind { diff --git a/crates/solti-prometheus/src/lib.rs b/crates/solti-prometheus/src/lib.rs index f82316e..600f2fa 100644 --- a/crates/solti-prometheus/src/lib.rs +++ b/crates/solti-prometheus/src/lib.rs @@ -8,8 +8,6 @@ //! | [`PrometheusSubscriber`] | `solti_sv_*` | [`taskvisor::Subscribe`] | //! | `taskvisor::Controller` (feature-gated) | `solti_ctrl_*` | [`taskvisor::Subscribe`] | //! -//! All share a single [`Registry`] for a unified `/metrics` endpoint. -//! //! ## Example //! ```rust,ignore //! use std::sync::Arc; diff --git a/examples/agentd/src/main.rs b/examples/agentd/src/main.rs index 84d9d8e..dc91ba5 100644 --- a/examples/agentd/src/main.rs +++ b/examples/agentd/src/main.rs @@ -10,7 +10,9 @@ use solti_exec::subprocess::register_subprocess_runner_with_backend; use solti_exec::{CgroupLimits, CpuMax, LinuxCapability, RlimitConfig, SecurityConfig}; -use solti_observe::{LoggerConfig, LoggerLevel, Subscriber, init_logger, timezone_sync}; +use solti_observe::{ + LoggerConfig, LoggerLevel, TracingEventSubscriber, init_logger, timezone_sync, +}; use solti_model::{ AdmissionStrategy, BackoffStrategy, CreateSpec, Flag, JitterStrategy, RestartStrategy, @@ -28,7 +30,7 @@ async fn main() -> anyhow::Result<()> { info!("logger initialized"); // 2) subscribers - let subscribers: Vec> = vec![Arc::new(Subscriber)]; + let subscribers: Vec> = vec![Arc::new(TracingEventSubscriber)]; // 3) router + runners with DIFFERENT security profiles let mut router = RunnerRouter::new(); diff --git a/examples/discovery/src/main.rs b/examples/discovery/src/main.rs index 3dcfd3a..045a6a0 100644 --- a/examples/discovery/src/main.rs +++ b/examples/discovery/src/main.rs @@ -12,18 +12,28 @@ use solti_model::{ AdmissionStrategy, BackoffStrategy, CreateSpec, Flag, JitterStrategy, RestartStrategy, RunnerLabels, TaskEnv, TaskKind, }; -use solti_observe::{LoggerConfig, LoggerLevel, Subscriber, init_logger, timezone_sync}; +use solti_observe::{ + LoggerConfig, LoggerLevel, LoggerTimeZone, TracingEventSubscriber, init_local_offset, + init_logger, timezone_sync, +}; use solti_prometheus::{PrometheusMetrics, PrometheusSubscriber}; use taskvisor::{ControllerConfig, Subscribe, SupervisorConfig}; const AGENT_HTTP_ADDR: &str = "0.0.0.0:8085"; const CONTROL_PLANE_ENDPOINT: &str = "http://localhost:8082"; -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { + // Must be called before spawning threads (tokio runtime). + init_local_offset(); + + tokio::runtime::Runtime::new()?.block_on(async_main()) +} + +async fn async_main() -> Result<(), Box> { // 1) Logger let cfg = LoggerConfig { level: LoggerLevel::new("info")?, + tz: LoggerTimeZone::Local, ..Default::default() }; init_logger(&cfg)?; @@ -44,7 +54,7 @@ async fn main() -> Result<(), Box> { // 4) Supervisor let subscribers: Vec> = - vec![Arc::new(Subscriber), Arc::new(prom_subscriber)]; + vec![Arc::new(TracingEventSubscriber), Arc::new(prom_subscriber)]; let supervisor = SupervisorApi::new( SupervisorConfig::default(), ControllerConfig::default(), diff --git a/examples/grpc-server/src/main.rs b/examples/grpc-server/src/main.rs index 251a2ad..515870a 100644 --- a/examples/grpc-server/src/main.rs +++ b/examples/grpc-server/src/main.rs @@ -10,7 +10,9 @@ use solti_model::{ AdmissionStrategy, BackoffStrategy, CreateSpec, Flag, JitterStrategy, RestartStrategy, RunnerLabels, TaskEnv, TaskKind, }; -use solti_observe::{LoggerConfig, LoggerLevel, Subscriber, init_logger, timezone_sync}; +use solti_observe::{ + LoggerConfig, LoggerLevel, TracingEventSubscriber, init_logger, timezone_sync, +}; use taskvisor::{ControllerConfig, Subscribe, SupervisorConfig}; #[tokio::main] @@ -29,7 +31,7 @@ async fn main() -> Result<(), Box> { info!("registered default subprocess runner"); // 3) Create supervisor - let subscribers: Vec> = vec![Arc::new(Subscriber)]; + let subscribers: Vec> = vec![Arc::new(TracingEventSubscriber)]; let supervisor = SupervisorApi::new( SupervisorConfig::default(), ControllerConfig::default(), diff --git a/examples/http-server/src/main.rs b/examples/http-server/src/main.rs index 9511c16..07521fb 100644 --- a/examples/http-server/src/main.rs +++ b/examples/http-server/src/main.rs @@ -10,7 +10,9 @@ use solti_model::{ AdmissionStrategy, BackoffStrategy, CreateSpec, Flag, JitterStrategy, RestartStrategy, RunnerLabels, TaskEnv, TaskKind, }; -use solti_observe::{LoggerConfig, LoggerLevel, Subscriber, init_logger, timezone_sync}; +use solti_observe::{ + LoggerConfig, LoggerLevel, TracingEventSubscriber, init_logger, timezone_sync, +}; use solti_prometheus::{PrometheusMetrics, PrometheusSubscriber}; use taskvisor::{ControllerConfig, Subscribe, SupervisorConfig}; @@ -39,7 +41,7 @@ async fn main() -> Result<(), Box> { // 4) Create supervisor let subscribers: Vec> = - vec![Arc::new(Subscriber), Arc::new(prom_subscriber)]; + vec![Arc::new(TracingEventSubscriber), Arc::new(prom_subscriber)]; let supervisor = SupervisorApi::new( SupervisorConfig::default(), ControllerConfig::default(),