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};

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