$_ bashkit

Logging Guide

Bashkit provides optional structured logging via the tracing crate when the logging feature is enabled. This guide covers configuration, log levels, security considerations, and integration with your application.

See also:

Enabling Logging

Add the logging feature to your Cargo.toml:

[dependencies]
bashkit = { version = "0.1", features = ["logging"] }
tracing-subscriber = "0.3"  # or your preferred subscriber

Log Levels

Bashkit emits logs at five levels:

LevelTargetWhen Used
ERRORbashkit::sessionUnrecoverable failures, script errors, security violations
WARNbashkit::parserParse errors, recoverable issues
INFObashkit::sessionSession start/end, execution completion
DEBUGbashkit::parser, bashkit::interpreterParsing, command execution flow
TRACEbashkit::interpreterDetailed internal state (not currently emitted)

Example Log Output

INFO bashkit::session: Starting script execution script="[script: 3 lines, 45 bytes]"
DEBUG bashkit::parser: Parsing script input_len=45 max_ast_depth=100 max_operations=100000
DEBUG bashkit::parser: Parse completed successfully
DEBUG bashkit::interpreter: Starting interpretation
INFO bashkit::session: Script execution completed exit_code=0 stdout_len=12 stderr_len=0

Configuration

Basic Setup

use bashkit::{Bash, LogConfig};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};

fn main() {
    // Initialize tracing subscriber
    tracing_subscriber::registry()
        .with(fmt::layer())
        .with(EnvFilter::from_default_env())
        .init();

    // Bashkit will now emit logs
    let mut bash = Bash::builder()
        .log_config(LogConfig::new())
        .build();
}

Environment Variables

Control log levels via RUST_LOG:

# All Bashkit logs at debug level
RUST_LOG=bashkit=debug cargo run

# Only session lifecycle (info level)
RUST_LOG=bashkit::session=info cargo run

# Parser details only
RUST_LOG=bashkit::parser=debug cargo run

Custom Configuration

use bashkit::{Bash, LogConfig};

# fn main() {
let bash = Bash::builder()
    .log_config(LogConfig::new()
        // Add custom sensitive variable patterns
        .redact_env("MY_INTERNAL_SECRET")
        .redact_env("COMPANY_API_KEY")
        // Limit logged value lengths
        .max_value_length(100))
    .build();
# }

Security (TM-LOG-*)

Bashkit’s logging is designed with security as a priority. Sensitive data is redacted by default to prevent accidental exposure in logs.

What Gets Redacted

  1. Environment Variables (TM-LOG-001)

    • Variables matching patterns: PASSWORD, SECRET, TOKEN, KEY, AUTH, etc.
    • Custom patterns added via redact_env()
  2. Script Content (TM-LOG-002)

    • By default, only script metadata (lines, bytes) is logged
    • Full content is never logged unless explicitly enabled
  3. URL Credentials (TM-LOG-003)

    • https://user:pass@host.comhttps://[REDACTED]@host.com
  4. API Keys and Tokens (TM-LOG-004)

    • JWTs (three-part base64 strings)
    • API keys with common prefixes (sk-, ghp_, AKIA, etc.)
    • High-entropy strings that look like secrets

Log Injection Prevention (TM-LOG-005, TM-LOG-006)

Script content is sanitized to prevent log injection attacks:

// Attacker tries: "echo hello\n[ERROR] Security breach!"
// Logged as: "echo hello\\n[ERROR] Security breach!"

Control characters are filtered, and newlines are escaped.

Unsafe Options

For debugging in non-production environments only. These methods require the BASHKIT_UNSAFE_LOGGING=1 environment variable to take effect; without it they are no-ops that emit a warning:

// First: export BASHKIT_UNSAFE_LOGGING=1 in your shell
// WARNING: May expose sensitive data
let config = LogConfig::new()
    .unsafe_disable_redaction()  // Disable ALL redaction
    .unsafe_log_scripts();       // Log full script content

Never use these options in production.

Integration Examples

With tokio-console

use bashkit::Bash;
use console_subscriber;

fn main() {
    console_subscriber::init();

    let mut bash = Bash::new();
    // Logs will appear in tokio-console
}

With JSON Logging

use bashkit::Bash;
use tracing_subscriber::{fmt, prelude::*};

fn main() {
    tracing_subscriber::registry()
        .with(fmt::layer().json())
        .init();

    let mut bash = Bash::new();
    // Logs will be JSON formatted
}

Filtering by Component

use bashkit::Bash;
use tracing_subscriber::{fmt, prelude::*, EnvFilter};

fn main() {
    let filter = EnvFilter::new("")
        .add_directive("bashkit::session=info".parse().unwrap())
        .add_directive("bashkit::parser=warn".parse().unwrap());

    tracing_subscriber::registry()
        .with(fmt::layer())
        .with(filter)
        .init();

    let mut bash = Bash::new();
}

Log Targets

TargetDescription
bashkit::sessionScript lifecycle (start, complete, error)
bashkit::parserParsing operations and errors
bashkit::interpreterCommand execution flow
bashkit::configConfiguration and builder operations

Performance Considerations

  • Logging is compile-time optional via feature flag
  • When disabled (--no-default-features), zero overhead
  • When enabled but filtered (e.g., RUST_LOG=error), minimal overhead
  • Redaction operations are optimized to avoid unnecessary allocations

Troubleshooting

No Logs Appearing

  1. Ensure the logging feature is enabled
  2. Initialize a tracing subscriber before creating Bash
  3. Set RUST_LOG environment variable appropriately

Sensitive Data in Logs

  1. Check if unsafe_disable_redaction() was called
  2. Verify custom env var patterns with redact_env()
  3. Ensure script content logging is disabled (default)

Too Much Log Output

  1. Use EnvFilter to limit to specific targets
  2. Set higher log level (e.g., RUST_LOG=bashkit=warn)
  3. Reduce max_value_length in LogConfig