diff --git a/Cargo.toml b/Cargo.toml index 16a863d..dd6cca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ # License identifier (SPDX format, e.g. MIT, Apache-2.0) license = "MIT" # Current crate version (Semantic Versioning compliant) - version = "0.2.2" + version = "1.0.0" # Rust edition (modern syntax and features) edition = "2024" diff --git a/readme.md b/readme.md index e69de29..9f225f6 100644 --- a/readme.md +++ b/readme.md @@ -0,0 +1,38 @@ +## About + +**Rustwatch — idiomatic, fearless logging library for Rust applications.** + +Rustwatch is a lightweight yet comprehensive logging library built on a processor → handler +pipeline. It delivers safe, predictable, and extensible logging across all scales — from +simple tools to high‑traffic systems — while preserving flexibility, efficiency, and +developer freedom. + +### Features + +- **Thread‑safe design** — Concurrent logging across async tasks and multi‑threaded workloads using `Arc>`. +- **Handler isolation** — Guarded execution prevents failures from propagating through the pipeline. +- **Fluent builder API** — Chainable configuration that feels natural in Rust. +- **Context processors** — Structured metadata enrichment (user, trace identifiers, performance metrics). +- **Resilient pipeline** — Hardened for concurrency, ensuring consistent behavior under heavy load. + +### Purpose + +Rustwatch unifies logging and runtime visibility by offering : + +- **Structured logging** — JSON, line, or custom formatters. +- **Monitoring signals** — Performance metrics, fiber depth, cycle detection. +- **Flexible outputs** — Console, file, remote sinks, or custom endpoints. + +Rustwatch is built to scale from lightweight applications to demanding production systems, delivering +fearless logging and deep insights wherever you deploy — without compromising on flexibility, efficiency, +or developer autonomy. + +## Installation + +## Quickstart + +## Roadmap + +## Security + +## License \ No newline at end of file diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs index 5329b8b..075b686 100644 --- a/src/formatter/mod.rs +++ b/src/formatter/mod.rs @@ -1,31 +1,36 @@ use crate::Record; -/// Core trait for formatting log records. +/// Core trait for converting a [`Record`] into its output representation. /// -/// A `Formatter` is responsible for converting a structured [`Record`] -/// into its final string representation suitable for output sinks such as -/// stdout, files, or network transports. +/// A `Formatter` is stateless and receives a shared reference to the +/// record — it must not mutate it. Formatting is a pure transformation. /// -/// Implementations may produce different output formats such as -/// plain text, JSON, or custom structured encodings. +/// **Custom** +/// ```rust,ignore +/// use rustwatch::{Record, Formatter}; +/// +/// struct CsvFormatter; +/// +/// impl Formatter for CsvFormatter { +/// fn format(&self, record: &Record) -> String { +/// format!( +/// "{},{},{},\"{}\"", +/// record.timestamp().to_rfc3339(), +/// record.level(), +/// record.channel(), +/// record.message().replace('"', "\"\""), +/// ) +/// } +/// } +/// ``` pub trait Formatter: Send + Sync { - /// Formats a log `Record` into a string representation. - /// - /// **Parameters** - /// - `record` - The log record containing message, level, timestamp, - /// and additional contextual metadata. - /// - /// **Returns** - /// - `String` - The formatted log output as a string. + /// Renders the record into a string suitable for a handler's destination. fn format(&self, record: &Record) -> String; - /// Returns the execution order of this formatter. - /// - /// Lower values are executed first, allowing deterministic ordering - /// of formatters within a logger pipeline. - fn order(&self) -> usize { - 0 - } + /// Optional sort weight for deterministic ordering when multiple + /// formatters are evaluated in sequence (currently unused by the + /// built-in pipeline; available for custom orchestration). + fn order(&self) -> usize { 0 } } // Internal modules for formatter implementations diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 80651bc..e01a181 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -2,27 +2,54 @@ use crate::Record; /// Core trait that all log handlers must implement. /// -/// A `Handler` is responsible for writing a log record to a destination -/// such as a file, console, socket, or external service. +/// A `Handler` receives a [`Record`] that has already been enriched by +/// the processor chain and decides whether to write it to its destination +/// (file, console, socket, external service, …). /// -/// Each handler may optionally declare an `order` weight used for execution -/// within a logger. Lower values are executed first. +/// **Contract** +/// - `log` returns `true` → record accepted; propagation **stops** (unless +/// wrapped in a [`BubbleHandler`] with `bubble = true`). +/// - `log` returns `false` → record declined; the logger tries the next handler. /// -/// The `log` method determines whether a record is accepted by the handler. -/// If it returns `true`, the record is considered handled and propagation -/// may stop depending on the logger’s bubbling configuration. +/// **Safety** +/// +/// All handlers must be `Send + Sync` so they can be shared via +/// `Arc>` or sent to worker threads. +/// +/// **Ordering** +/// +/// Handlers are sorted by [`Handler::order`] ascending before the first +/// log call. Implement `order()` to control execution priority. +/// +/// **Custom** +/// ```rust,ignore +/// use rustwatch::{Record, Level}; +/// use rustwatch::handler::Handler; +/// +/// struct StderrHandler { min_level: Level } +/// +/// impl Handler for StderrHandler { +/// fn log(&mut self, record: &Record) -> bool { +/// if record.level().is_at_least(&self.min_level) { +/// eprintln!("{}", record.message()); +/// true +/// } else { +/// false +/// } +/// } +/// } +/// ``` pub trait Handler: Send + Sync { /// Processes the given log record. /// /// **Returns** - /// - `true` → record was handled/accepted by this handler - /// - `false` → handler did not accept the record; next handler may try + /// - `true` → record was handled; propagation stops (unless bubbling). + /// - `false` → handler filtered or declined the record; try the next. fn log(&mut self, record: &Record) -> bool; - /// Returns the execution order of this handler. + /// Returns the execution priority of this handler. /// - /// Lower values are executed first, allowing deterministic ordering - /// of handlers within a logger pipeline. + /// Lower values execute first. Defaults to `0`. fn order(&self) -> usize { 0 } diff --git a/src/lib.rs b/src/lib.rs index 973a363..5042446 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,46 @@ +//! # rustwatch +//! +//! **Enterprise-grade, thread-safe structured logging for Rust.** +//! +//! rustwatch is a complete port of PHP's [Monolog](https://github.com/Seldaek/monolog) +//! to idiomatic Rust, adding thread-safety, `Arc>` sharing, panic-safe +//! handler isolation, and a fluent builder API on top of the battle-tested +//! processor → handler pipeline architecture. +//! +//! ## Features +//! +//! - **Thread-safe** — `Logger` is `Clone + Send + Sync`; share it freely across threads. +//! - **Panic-safe** — handler panics are caught and reported to stderr; the pipeline continues. +//! - **Fluent builder** — `Logger::builder("name").timezone("UTC").handler(…).build()`. +//! - **10 handlers** — console, file, rotating file, null, stack, bubble, buffer, memory, socket, syslog. +//! - **6 formatters** — line, JSON, normalizer, scalar, Logstash, HTML. +//! - **11 processors** — context, tags, uid, hostname, PID, memory, memory peak, PSR-3, introspection, git, web. +//! - **RFC 5424** severity levels with `Ord`, `Display`, `FromStr`, and `Serialize`. +//! +//! ## Quick start +//! +//! ```rust +//! use rustwatch::{Logger, Level}; +//! use rustwatch::handlers::{ConsoleHandler, RotatingFileHandler}; +//! use rustwatch::formatters::{LineFormatter, JsonFormatter}; +//! use rustwatch::processors::{HostnameProcessor, UidProcessor}; +//! +//! let logger = Logger::builder("app") +//! .timezone("UTC") +//! .processor(Box::new(HostnameProcessor::new())) +//! .processor(Box::new(UidProcessor::new())) +//! .handler(Box::new(ConsoleHandler::new(Box::new(LineFormatter), Level::Debug))) +//! .handler(Box::new(RotatingFileHandler::new( +//! "/tmp/app.log", Box::new(JsonFormatter), Level::Warning, 10_000_000, 5 +//! ))) +//! .build(); +//! +//! logger.info("Server started", None); +//! logger.error("DB timeout", Some(serde_json::json!({"ms": 5000}))); +//! ``` + + + // Internal module declarations for core logging components mod formatter; mod handler; diff --git a/src/processor/mod.rs b/src/processor/mod.rs index 722ba69..ad2debc 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -1,27 +1,35 @@ use crate::Record; -/// Core trait for processing log records. +/// Core trait for enriching a [`Record`] before it reaches any handler. /// -/// A `Processor` is responsible for mutating a [`Record`] before it is -/// passed to handlers or formatters in the logging pipeline. +/// Processors run in registration order after the record is created and +/// before it is passed to the handler chain. They mutate the record +/// **in place** — typically adding keys to `record.context`. /// -/// It can enrich the record with additional contextual metadata such as -/// request identifiers, hostname, environment information, or runtime -/// metrics. Processors are executed in registration order. +/// **Safety** +/// +/// All processors must be `Send + Sync` because the logger stores them +/// behind an `Arc>`. +/// +/// **Custom** +/// ```rust,ignore +/// use rustwatch::{Record, Processor}; +/// use serde_json::json; +/// +/// struct RequestIdProcessor { id: String } +/// +/// impl Processor for RequestIdProcessor { +/// fn process(&self, record: &mut Record) { +/// record.set_context_value("request_id", json!(self.id)); +/// } +/// } +/// ``` pub trait Processor: Send + Sync { - /// Mutates the given `Record` by attaching additional metadata. - /// - /// **Parameters** - /// - `record` - The log record that will be modified in place. + /// Mutates `record` in place to attach additional metadata. fn process(&self, record: &mut Record); - /// Returns the execution order of this processor. - /// - /// Lower values are executed first, allowing fine-grained control - /// over processor ordering within the pipeline. - fn order(&self) -> usize { - 0 - } + /// Execution priority. Lower values run first. Defaults to `0`. + fn order(&self) -> usize { 0 } } // Internal modules for processor implementations