Skip to content

Implement structured logging with credential protection #65

Description

@unclesp1d3r

Implement Structured Logging Infrastructure with Credential Protection - Task #11

🎯 Epic Context

📊 Current State Analysis

Existing Infrastructure:

  • ✅ CLI framework with --verbose and --quiet flags (unused)
  • ✅ Error handling patterns established
  • ✅ URL redaction utility in src/tls.rs (redact_url function)
  • ❌ No logging dependencies - missing tracing ecosystem
  • ❌ No structured logging implementation
  • ❌ Verbose/quiet flags not connected to any logging

Security Context:

  • Must comply with WARP.md credential protection requirements
  • Existing redact_url() function available for DATABASE_URL sanitization
  • Zero credential leakage tolerance

🎯 Requirements Implementation

Requirement 7.1: Structured JSON Logging

  • Current: Plain stdout output only
  • Target: JSON-structured logs via tracing crate
  • Trigger: --verbose flag activation

Requirement 7.2: Credential Protection

  • Current: No logging, so no risk
  • Target: Integrate existing redact_url() function
  • Scope: DATABASE_URL, passwords, sensitive connection strings

Requirement 7.3: Multi-level Verbosity

  • Current: Single --verbose boolean flag
  • Target: Progressive levels based on flag count (-v, -vv, -vvv)
  • Mapping: 1=info, 2=debug, 3+=trace

Requirement 7.4: Quiet Mode

  • Current: Basic --quiet flag exists
  • Target: Suppress all output except errors
  • Behavior: Override verbose settings when active

🚀 Proposed Implementation

Phase 1: Dependencies & Infrastructure

# Cargo.toml additions
[dependencies]
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["json", "env-filter", "time"] }
serde = { version = "1.0", features = ["derive"] }

Phase 2: Core Logging Module (src/logging.rs)

use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, fmt};
use crate::tls::redact_url;

#[derive(Debug, Clone)]
pub struct LoggingConfig {
    pub verbose_count: u8,
    pub quiet: bool,
}

impl LoggingConfig {
    pub fn from_args(verbose_count: u8, quiet: bool) -> Self {
        Self { verbose_count, quiet }
    }
    
    pub fn init_structured_logging(&self) -> Result<(), Box<dyn std::error::Error>> {
        let level = if self.quiet {
            "error"
        } else {
            match self.verbose_count {
                0 => "warn",
                1 => "info", 
                2 => "debug",
                _ => "trace",
            }
        };
        
        let filter = EnvFilter::new(format!("gold_digger={}", level));
        
        tracing_subscriber::registry()
            .with(fmt::layer()
                .json()
                .with_current_span(false)
                .with_span_list(true))
            .with(filter)
            .init();
            
        tracing::info!(
            version = env!("CARGO_PKG_VERSION"),
            verbose_count = self.verbose_count,
            quiet = self.quiet,
            "Structured logging initialized"
        );
        
        Ok(())
    }
}

// Credential-safe logging macros
#[macro_export]
macro_rules! log_database_url {
    ($url:expr) => {
        tracing::debug!(database_url = %crate::tls::redact_url($url), "Database connection configured");
    };
}

Phase 3: Integration Points

Main Application (src/main.rs):

mod logging;
use logging::LoggingConfig;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();
    
    // Initialize structured logging FIRST
    let logging_config = LoggingConfig::from_args(args.verbose, args.quiet);
    logging_config.init_structured_logging()?;
    
    tracing::info!("Gold Digger starting");
    
    // Rest of application...
}

Database Operations:

  • Add #[tracing::instrument] to database functions
  • Log connection establishment, query execution time
  • Include row count milestones (every 1000 rows)
  • Use log_database_url! macro for safe URL logging

CLI Integration:

  • Enhance --verbose to accept multiple occurrences (-v, -vv, -vvv)
  • Ensure --quiet overrides all verbosity settings
  • Add --log-format option for JSON/text output selection

✅ Acceptance Criteria

Core Functionality

  • Dependencies: Add tracing and tracing-subscriber with JSON support
  • Initialization: Logging system initializes before any operations
  • CLI Integration: --verbose count maps to log levels (warn→info→debug→trace)
  • Quiet Mode: --quiet suppresses all logs except errors
  • JSON Output: Structured JSON logs when verbose mode active

Security & Credentials

  • Zero Leakage: No DATABASE_URL, passwords, or credentials in any log
  • Redaction: Uses existing redact_url() for URL sanitization
  • Audit: All log output manually audited for credential exposure
  • Testing: Automated tests verify no credential leakage

Performance & Integration

  • Zero Cost: No logging overhead when disabled (quiet/non-verbose)
  • Database Logging: Connection, query execution, row processing milestones
  • Error Context: Enhanced error messages with structured context
  • Instrumentation: Key functions decorated with #[instrument]

Documentation & Testing

  • Usage Examples: CLI examples for different verbosity levels
  • Unit Tests: Logging configuration and initialization tests
  • Integration Tests: End-to-end logging behavior verification
  • Security Tests: Credential redaction verification tests

🧪 Validation Steps

Development Testing

# Test different verbosity levels
cargo run -- --help
cargo run -- -q --database-url "$DATABASE_URL" --query "SELECT 1"
cargo run -- -v --database-url "$DATABASE_URL" --query "SELECT * FROM users LIMIT 5" 
cargo run -- -vvv --database-url "$DATABASE_URL" --query "SELECT COUNT(*) FROM large_table"

Security Validation

# Verify no credential leakage in any verbosity mode
export DATABASE_URL="mysql://secret_user:secret_pass@host:3306/db"
cargo run -- -vvv --database-url "$DATABASE_URL" --query "SELECT 1" 2>&1 | grep -i "secret"
# Should return no matches

Performance Testing

# Ensure no overhead when logging disabled
hyperfine 'cargo run -- -q --database-url "$DATABASE_URL" --query "SELECT 1"'
hyperfine 'cargo run -- -vvv --database-url "$DATABASE_URL" --query "SELECT 1"'

🔗 Related Resources

🎯 Definition of Done

  • All Acceptance Criteria verified ✅
  • CI/CD pipeline passes on all platforms
  • Code review completed and approved
  • Documentation updated with logging usage examples
  • Security audit confirms zero credential exposure
  • Performance benchmarks show acceptable overhead
  • Integration tests demonstrate end-to-end functionality

Epic: #52 | Task: #11 | Requirements: 7.1-7.4 | Priority: High

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions