Crate log_args

Crate log_args 

Source
Expand description

Β§log_args

A powerful procedural macro crate providing the #[params] attribute for automatic parameter logging and context propagation in Rust applications. Built on top of the tracing ecosystem, it enables truly automatic context inheritance across all boundaries including async/await, spawned tasks, closures, and WebSocket upgrades.

§✨ Key Features

§🎯 Automatic Context Inheritance

  • Zero Configuration: Child functions inherit parent context with just #[params]
  • Cross-Boundary: Works across closures, async spawns, WebSocket upgrades, and thread boundaries
  • Transparent: No manual context passing or management required

Β§πŸš€ Performance & Safety

  • Zero Runtime Overhead: All processing happens at compile-time via macro expansion
  • Memory Efficient: Only specified fields are cloned and logged
  • Async Safe: Proper handling of ownership in async contexts
  • Thread Safe: Context propagation uses thread-local and task-local storage

Β§πŸ”§ Flexible Configuration

  • Selective Logging: Choose exactly which parameters to log with fields(...)
  • Custom Fields: Add computed metadata and expressions with custom(...)
  • Span Propagation: Automatic context inheritance with span(...)
  • Nested Access: Support for deep field access like user.profile.settings.theme
  • Method Calls: Log results of method calls and expressions

Β§πŸ”’ Security & Privacy

  • Secure by Default: Sensitive parameters excluded unless explicitly specified
  • Fine-grained Control: Log only what’s needed for debugging
  • Compliance Ready: Selective logging helps meet privacy requirements
  • Production Safe: Configurable logging levels and field selection

Β§πŸš€ Quick Start

Add to your Cargo.toml:

[dependencies]
log_args = "0.1.4"
log-args-runtime = { version = "0.1.2", features = ["with_context"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["json"] }

Β§Basic Usage Examples

β“˜
use log_args::params;
use tracing::info;

// Default behavior: Only span propagation and function name logging
#[params]
fn process_request(user_id: String, data: String) {
    info!("Processing request");
    // Output: {"message": "Processing request", "target": "my_app::process_request"}
}

// Selective parameter logging (excludes sensitive data)
#[params(fields(user_id, action))]
fn user_action(user_id: String, action: String, password: String) {
    info!("User performed action");
    // Output: {"message": "User performed action", "user_id": "123", "action": "login"}
    // Note: password is excluded for security
}

// Span context propagation - the killer feature!
#[params(span(request_id, user_id))]
fn handle_api_request(request_id: String, user_id: String, payload: String) {
    info!("API request received");
    validate_request(payload); // Child function inherits context
    process_business_logic();   // This too!
}

// Child functions automatically inherit request_id and user_id
#[params]
fn validate_request(payload: String) {
    info!("Validating request");
    // Output: {"request_id": "req-123", "user_id": "user-456", "message": "Validating request"}
}

#[params]
fn process_business_logic() {
    info!("Processing business logic");
    // Output: {"request_id": "req-123", "user_id": "user-456", "message": "Processing business logic"}
}

Β§πŸ”§ Advanced Usage

Β§Custom Fields with Expressions

β“˜
#[params(
    fields(user.id, user.name),
    custom(
        email_count = user.emails.len(),
        is_premium = user.subscription.tier == "premium",
        timestamp = std::time::SystemTime::now()
    )
)]
fn analyze_user(user: User, api_key: String) {
    info!("Analyzing user account");
    // Output: {
    //   "message": "Analyzing user account",
    //   "user_id": 42,
    //   "user_name": "Alice",
    //   "email_count": 3,
    //   "is_premium": true,
    //   "timestamp": "2024-01-01T12:00:00Z"
    // }
}

Β§Async Task Processing

β“˜
#[params(span(job_id, user_id))]
async fn process_background_job(job_id: String, user_id: String, job_data: JobData) {
    info!("Background job started");
     
    // Spawn async tasks - they inherit context automatically
    let handle1 = tokio::spawn(async {
        validate_job_data().await;
    });
     
    let handle2 = tokio::spawn(async {
        send_notifications().await;
    });
     
    tokio::try_join!(handle1, handle2).unwrap();
    info!("Background job completed");
}

#[params]
async fn validate_job_data() {
    info!("Validating job data");
    // Automatically includes job_id and user_id from parent context
}

Β§πŸ”§ Setup & Configuration

Β§Tracing Subscriber Setup

For structured JSON logging with context fields at the top level:

β“˜
fn init_logging() {
    tracing_subscriber::fmt()
        .json()
        .flatten_event(true)  // Required for field flattening
        .init();
}

Β§Production Configuration

β“˜
fn init_prod_logging() {
    tracing_subscriber::fmt()
        .json()
        .flatten_event(true)
        .with_env_filter("info,my_app=debug")
        .with_target(false)
        .init();
}

Β§πŸ”’ Security Best Practices

Always use selective logging in production:

β“˜
// βœ… Good - Only logs safe fields
#[params(fields(user_id, operation_type))]
fn secure_operation(user_id: String, password: String, operation_type: String) {
    info!("Operation started");
    // password is excluded for security
}

// ❌ Bad - Logs everything including sensitive data
#[params(all)]
fn insecure_operation(user_id: String, password: String) {
    info!("Operation started"); // This would log the password!
}

Β§πŸ“š Attribute Reference

  • #[params] - Default: span propagation and function name logging only
  • #[params(all)] - Log all parameters (use carefully in production)
  • #[params(fields(param1, param2))] - Log only specified parameters
  • #[params(span(param1, param2))] - Propagate parameters as context to child functions
  • #[params(custom(key = expression))] - Add computed custom fields

§🚫 Limitations

  • Array indexing like users[0].name is not supported (use users.first().map(|u| &u.name) instead)
  • The macro redefines logging macros within function scope only
  • Complex expressions may not parse correctly (simplify or use custom fields)

Β§πŸ“š Examples

See the workspace examples for comprehensive demonstrations.

Attribute MacrosΒ§

params
A powerful procedural macro for automatic function argument logging with structured tracing.