| Crates.io | logwise |
| lib.rs | logwise |
| version | 0.3.0 |
| created_at | 2024-10-14 02:23:26.237347+00 |
| updated_at | 2025-08-12 20:00:09.287146+00 |
| description | an opinionated logging library for Rust |
| homepage | https://sealedabstract.com/code/logwise |
| repository | https://github.com/drewcrawford/logwise |
| max_upload_size | |
| id | 1407846 |
| size | 1,496,701 |

An opinionated logging library for Rust with structured logging, privacy-aware data handling, and hierarchical task-based context management.
logwise is experimental and the API may change.
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:
debug for print-style debugging in your library, or for users
debugging their own code?logwise solves these problems with opinionated, use-case-specific log levels.
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.
logwise provides specific log levels for defined use cases:
| Level | Use Case | Build Type | Thread Control |
|---|---|---|---|
trace |
Detailed debugging | debug only | Must enable per-thread via Context::begin_trace() |
debuginternal |
Print-style debugging | debug only | On by default in current crate, per-thread in downstream |
info |
Supporting downstream crates | debug only | On by default |
perfwarn |
Performance problems with analysis | all builds | Always on |
warning |
Suspicious conditions | all builds | Always on |
error |
Logging errors in Results | all builds | Always on |
panic |
Programmer errors | all builds | Always on |
// 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"
);
// Async logging for better performance
async fn handle_request() {
logwise::info_async!("Processing request",
method="GET",
endpoint="/health"
);
}
logwise's privacy system ensures sensitive data is handled appropriately:
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)
);
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()
);
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()
);
child_ctx.clone().set_current();
// Task completion is logged automatically when dropped
Use the perfwarn! macro to track and log slow operations:
// Tracks execution time automatically
logwise::perfwarn!("database_query", {
// Your expensive operation here
perform_database_query()
});
// Logs warning if operation exceeds threshold
Logger: The trait defining logging backendsLogRecord: Structured log entry with metadataLevel: Enumeration of available log levelscontext module: Thread-local hierarchical task managementprivacy module: Privacy-aware data handling systemglobal_logger module: Global logger registration and managementLogRecord with source location metadataprivacy::Loggable trait determines how values are loggedLogger implementationsuse logwise::{context::Context, privacy::LogIt};
#[derive(Debug)]
struct Config {
database_url: String,
port: u16,
}
fn main() {
// Initialize root context
Context::reset("application".to_string());
// Log application startup
logwise::info_sync!("Starting application", version="1.0.0");
// Create task for initialization
let init_ctx = Context::new_task(
Some(Context::current()),
"initialization".to_string()
);
init_ctx.clone().set_current();
// 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);
}
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));
logwise is designed for concurrent and async environments:
Logger implementations must be Send + Synccontext::ApplyContext preserves context across async boundaries*_async!) in async contexts for better performanceperfwarn! macro includes automatic interval trackinglogwise includes WebAssembly support with browser-specific features:
Uses web-time for time operations
Console output via web-sys
Batched logging with InMemoryLogger::periodic_drain_to_console