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:
- Threat Model - Security threats including TM-LOG-*
- API Documentation - Full API reference
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:
| Level | Target | When Used |
|---|---|---|
| ERROR | bashkit::session | Unrecoverable failures, script errors, security violations |
| WARN | bashkit::parser | Parse errors, recoverable issues |
| INFO | bashkit::session | Session start/end, execution completion |
| DEBUG | bashkit::parser, bashkit::interpreter | Parsing, command execution flow |
| TRACE | bashkit::interpreter | Detailed 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
-
Environment Variables (TM-LOG-001)
- Variables matching patterns: PASSWORD, SECRET, TOKEN, KEY, AUTH, etc.
- Custom patterns added via
redact_env()
-
Script Content (TM-LOG-002)
- By default, only script metadata (lines, bytes) is logged
- Full content is never logged unless explicitly enabled
-
URL Credentials (TM-LOG-003)
https://user:pass@host.com→https://[REDACTED]@host.com
-
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
| Target | Description |
|---|---|
bashkit::session | Script lifecycle (start, complete, error) |
bashkit::parser | Parsing operations and errors |
bashkit::interpreter | Command execution flow |
bashkit::config | Configuration 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
- Ensure the
loggingfeature is enabled - Initialize a tracing subscriber before creating
Bash - Set
RUST_LOGenvironment variable appropriately
Sensitive Data in Logs
- Check if
unsafe_disable_redaction()was called - Verify custom env var patterns with
redact_env() - Ensure script content logging is disabled (default)
Too Much Log Output
- Use
EnvFilterto limit to specific targets - Set higher log level (e.g.,
RUST_LOG=bashkit=warn) - Reduce
max_value_lengthinLogConfig