Crate logwise

Crate logwise 

Source
Expand description

§logwise

logo

An opinionated logging library for Rust with structured logging, privacy-aware data handling, and hierarchical task-based context management.

§Development Status

logwise is experimental and the API may change.

§The Problem

Traditional logging crates like log offer generic log levels (error, warn, info, debug, trace) that are often too vague for real-world use cases. This leads to several problems:

  • Ambiguous levels: Is debug for print-style debugging in your library, or for users debugging their own code?
  • Build control: How do you compile out expensive logs by default but enable them when debugging user-reported issues?
  • Missing use cases: What level is appropriate for “this is slow and should be optimized”?

logwise solves these problems with opinionated, use-case-specific log levels.

§Core Philosophy

Think of log levels like module visibility. You have pub, pub(crate), pub(super), and private visibility for different use cases. logwise provides similar granular control for logging.

§Log Levels

logwise provides specific log levels for defined use cases:

LevelUse CaseBuild TypeThread Control
traceDetailed debuggingdebug onlyMust enable per-thread via Context::begin_trace()
debuginternalPrint-style debuggingdebug onlyOn by default in current crate, per-thread in downstream
infoSupporting downstream cratesdebug onlyOn by default
mandatoryPrintf-style debuggingall buildsAlways on
profileProfiling outputall buildsAlways on
perfwarnPerformance problems with analysisall buildsAlways on
warningSuspicious conditionsall buildsAlways on
errorLogging errors in Resultsall buildsAlways on
panicProgrammer errorsall buildsAlways on

§Quick Start

§Basic Logging

logwise::declare_logging_domain!();
// Simple structured logging
logwise::info_sync!("User logged in", user_id=42);

// With multiple parameters
logwise::warn_sync!("Request failed",
    status=404,
    path="/api/users"
);
logwise::declare_logging_domain!();
// Async logging for better performance
async fn handle_request() {
    logwise::info_async!("Processing request",
        method="GET",
        endpoint="/health"
    );
}

§Privacy-Aware Logging

logwise’s privacy system ensures sensitive data is handled appropriately:

logwise::declare_logging_domain!();
use logwise::privacy::{LogIt, IPromiseItsNotPrivate};

#[derive(Debug)]
struct User {
    id: u64,
    name: String,
    email: String,
}

// Complex types require explicit privacy handling
let user = User {
    id: 123,
    name: "Alice".into(),
    email: "alice@example.com".into()
};

// Use LogIt wrapper for complex types
logwise::info_sync!("User created", user=LogIt(&user));

// Mark explicitly non-private data when it's safe
let public_id = "PUBLIC-123";
logwise::info_sync!("Processing {id}",
    id=IPromiseItsNotPrivate(public_id)
);

§Context and Task Management

Track hierarchical tasks with automatic performance monitoring:

use logwise::context::Context;

// Create a new task
let ctx = Context::new_task(
    Some(Context::current()),
    "data_processing".to_string(),
    logwise::Level::Info,
    true,
);
ctx.clone().set_current();

// Enable detailed tracing for debugging
Context::begin_trace();

// Nested tasks automatically track hierarchy
let child_ctx = Context::new_task(
    Some(Context::current()),
    "parse_csv".to_string(),
    logwise::Level::Info,
    true,
);
child_ctx.clone().set_current();

// Task completion is logged automatically when dropped

§Performance Tracking

Use the perfwarn! macro to track and log slow operations:

logwise::declare_logging_domain!();
// Tracks execution time automatically
logwise::perfwarn!("database_query", {
    // Your expensive operation here
    perform_database_query()
});
// Logs warning if operation exceeds threshold

For conditional performance warnings that only log when a threshold is exceeded:

logwise::declare_logging_domain!();
// Only logs if operation takes longer than 100ms
let _interval = logwise::perfwarn_begin_if!(
    Duration::from_millis(100),
    "slow_operation"
);
fetch_data();
// Warning logged only if threshold exceeded

§Heartbeat Monitoring

Use heartbeat to monitor that operations complete within a deadline:

// Create a heartbeat that warns if not completed within 5 seconds
let _guard = logwise::heartbeat("critical_task", Duration::from_secs(5));
critical_task();
// Warning logged if guard is dropped after deadline

§Checking Log Level Enablement

Use log_enabled! to check if a log level is enabled before doing expensive work:

logwise::declare_logging_domain!();
use logwise::{Level, log_enabled};

// Skip expensive computation if the log level is disabled
if log_enabled!(Level::Trace) {
    let expensive_data = expensive_debug_computation();
    logwise::trace_sync!("Debug data: {data}", data=expensive_data);
}

§Mandatory Logging

Use mandatory_sync! or mandatory_async! for printf-style debugging that is always enabled, even in release builds:

logwise::declare_logging_domain!();
// This log will appear in all builds
logwise::mandatory_sync!("Debugging value: {value}", value=42);

§Profiling

Use profiling macros to track execution time and performance characteristics:

logwise::declare_logging_domain!();
// Logs duration when the interval is dropped
let _interval = logwise::profile_begin!("data_processing");
process_data();
// Automatically logs completion time

For explicit profiling messages:

logwise::declare_logging_domain!();
logwise::profile_sync!("Computation took {ms} ms", ms=elapsed_ms);

§Architecture Overview

§Core Components

§Logging Flow

  1. Macros generate a LogRecord with source location metadata
  2. The privacy::Loggable trait determines how values are logged
  3. Records are dispatched to registered Logger implementations
  4. Loggers can process records synchronously or asynchronously

§Examples

§Complete Application Example

logwise::declare_logging_domain!();
use logwise::{context::Context, privacy::LogIt};

#[derive(Debug)]
struct Config {
    database_url: String,
    port: u16,
}

// Initialize root context
Context::reset("application".to_string());

// Log application startup
logwise::info_sync!("Starting application", version="1.0.0");

// Load configuration
let config = Config {
    database_url: "postgres://localhost/myapp".into(),
    port: 8080,
};

// Use LogIt for complex types
logwise::info_sync!("Configuration loaded",
    config=LogIt(&config)
);

// Track performance-critical operations
logwise::perfwarn!("database_connection", {
    // connect_to_database(&config.database_url)
});

logwise::info_sync!("Application ready", port=config.port);

§Custom Logger Implementation

use logwise::{Logger, LogRecord};
use std::sync::Arc;
use std::fmt::Debug;
use std::pin::Pin;
use std::future::Future;

#[derive(Debug)]
struct CustomLogger;

impl Logger for CustomLogger {
    fn finish_log_record(&self, record: LogRecord) {
        // Custom logging logic
        eprintln!("[CUSTOM] {}", record);
    }

    fn finish_log_record_async<'s>(&'s self, record: LogRecord) -> Pin<Box<dyn Future<Output = ()> + Send + 's>> {
        Box::pin(async move {
            // Async logging logic
            eprintln!("[CUSTOM ASYNC] {}", record);
        })
    }

    fn prepare_to_die(&self) {
        // Cleanup logic
    }
}

// Register the logger
logwise::add_global_logger(Arc::new(CustomLogger));

§Thread Safety and Async Support

logwise is designed for concurrent and async environments:

  • All Logger implementations must be Send + Sync
  • Context is thread-local with parent inheritance
  • context::ApplyContext preserves context across async boundaries
  • Both sync and async logging variants are available

§Performance Considerations

  • Use async variants (*_async!) in async contexts for better performance
  • The perfwarn! macro includes automatic interval tracking
  • Debug-only levels are compiled out in release builds
  • Thread-local caching reduces synchronization overhead

§WASM Support

logwise includes WebAssembly support with browser-specific features:

Re-exports§

pub use global_logger::add_global_logger;
pub use global_logger::global_loggers;
pub use global_logger::set_global_loggers;

Modules§

context
Thread-local context management for hierarchical, task-based logging.
global_logger
Global logger management for the logwise logging system.
interval
Defines our Interval types.
privacy
Privacy-aware logging system for logwise.

Macros§

debuginternal_async
Asynchronous debug-internal logging (debug builds only).
debuginternal_sync
Synchronous debug-internal logging (debug builds only).
declare_logging_domain
Declares the logging domain for the current crate.
error_async
Asynchronous error-level logging (all builds).
error_sync
Synchronous error-level logging (all builds).
info_async
Asynchronous info-level logging (debug builds only).
info_sync
Synchronous info-level logging (debug builds only).
log_enabled
Returns whether logging is enabled for a given Level.
mandatory_async
Asynchronous mandatory-level logging (all builds).
mandatory_sync
Synchronous mandatory-level logging (all builds).
perfwarn
Block-scoped performance warning interval (all builds).
perfwarn_begin
Begin a performance warning interval (all builds).
perfwarn_begin_if
Conditional performance warning interval.
profile_async
Asynchronous profile-level logging (all builds).
profile_begin
Begin a profile interval (all builds).
profile_sync
Synchronous profile-level logging (all builds).
trace_async
Asynchronous trace-level logging (debug builds only, per-thread activation).
trace_sync
Synchronous trace-level logging (debug builds only, per-thread activation).
warn_sync
Synchronous warning-level logging (all builds).

Structs§

Duration
A Duration type to represent a span of time, typically used for system timeouts.
HeartbeatGuard
RAII guard that ensures work completes before a specified deadline.
InMemoryLogger
An in-memory logger that stores log messages in a Vec<String>.
LogRecord
A log record.
LoggingDomain
Controls whether internal logging is enabled for a crate.

Enums§

Level
Log severity levels with opinionated use-case semantics.

Traits§

Logger
Core trait for implementing logging backends in logwise.

Functions§

heartbeat
Creates a HeartbeatGuard that will warn if it is not dropped before duration.

Attribute Macros§

profile
Attribute macro to automatically profile a function’s execution time.