Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
38 changes: 38 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -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<Mutex<_>>`.
- **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
47 changes: 26 additions & 21 deletions src/formatter/mod.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
51 changes: 39 additions & 12 deletions src/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Mutex<Logger>>` 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
}
Expand Down
43 changes: 43 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Mutex<_>>` 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;
Expand Down
42 changes: 25 additions & 17 deletions src/processor/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Mutex<_>>`.
///
/// **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
Expand Down