How does log::Metadata::target identify the module path for filtering log statements?

log::Metadata::target returns a string identifying the source of a log statement, typically the module path where the logging macro was invoked, enabling log consumers to filter messages by their origin through pattern matching on module names. The target defaults to the module path of the code calling the logging macro, but can be explicitly overridden to group related log statements across modules or to identify logs from specific subsystems.

Basic Target Behavior

use log::{info, Metadata};
 
// Default target: module path where log macro is used
// In module my_app::network, target would be "my_app::network"
 
fn default_target() {
    // In a real application, the target is automatically set
    // to the current module path when using log macros
    
    // If this code is in module my_app::utils:
    // info!("message"); -> target = "my_app::utils"
    
    // The target identifies WHERE the log originated
}

The target defaults to the module path of the logging statement.

Accessing Target in Log Implementations

use log::{Level, Metadata, Record};
 
struct SimpleLogger;
 
impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        // metadata.target() returns the module path
        // Filter based on target
        
        metadata.target().starts_with("my_app::network")
    }
    
    fn log(&self, record: &Record) {
        // Access target in the log record
        println!(
            "[{}] {}: {}",
            record.target(),  // Module path
            record.level(),
            record.args()
        );
    }
    
    fn flush(&self) {}
}

Log implementations use metadata.target() to filter by module origin.

Target as Module Path

// In src/network/connection.rs
mod network {
    pub mod connection {
        use log::info;
        
        pub fn connect() {
            // Target is automatically "my_app::network::connection"
            info!("Connecting to server");
        }
    }
}
 
// In src/database/query.rs
mod database {
    pub mod query {
        use log::info;
        
        pub fn execute() {
            // Target is automatically "my_app::database::query"
            info!("Executing query");
        }
    }
}
 
fn module_path_example() {
    // Each module's log statements have different targets:
    // network::connection::connect() -> target = "my_app::network::connection"
    // database::query::execute() -> target = "my_app::database::query"
    
    // This enables filtering by module
}

The target automatically reflects the module path where logging occurs.

Filtering by Target

use log::{LevelFilter, Metadata};
 
struct FilteredLogger {
    // Filter configuration
    allowed_targets: Vec<String>,
    max_level: LevelFilter,
}
 
impl log::Log for FilteredLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        // Check if this target should be logged
        if self.allowed_targets.is_empty() {
            return true;  // Allow all if no filter
        }
        
        // Allow if target starts with any allowed prefix
        self.allowed_targets.iter().any(|allowed| {
            metadata.target().starts_with(allowed)
        })
    }
    
    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            println!("[{}] {}", record.target(), record.args());
        }
    }
    
    fn flush(&self) {}
}
 
fn configure_filter() {
    let logger = FilteredLogger {
        allowed_targets: vec![
            "my_app::network".to_string(),  // All network modules
            "my_app::database::query".to_string(),  // Specific module
        ],
        max_level: LevelFilter::Debug,
    };
    
    // Now logs from my_app::network::* and my_app::database::query
    // will pass the enabled() check
}

Filter logs by matching target against allowed module paths.

Explicit Target Override

use log::info;
 
fn explicit_target() {
    // Default: target = current module path
    info!("Default target");
    
    // Explicit target: override module path
    log::info!(target: "my_custom_target", "Explicit target");
    
    // Useful for:
    // - Grouping related logs across modules
    // - Identifying specific subsystems
    // - Third-party library integration
}
 
// Example: Grouping database-related logs
mod database {
    use log::info;
    
    pub mod connection {
        pub fn connect() {
            // Override target to group database logs
            log::info!(target: "database", "Connected");
        }
    }
    
    pub mod query {
        pub fn execute() {
            // Same target across different modules
            log::info!(target: "database", "Query executed");
        }
    }
}
 
// Now both logs have target = "database"
// despite being in different modules

Override the default target to group logs across modules.

Hierarchical Filtering

use log::{Level, LevelFilter, Metadata};
 
struct HierarchicalLogger;
 
impl log::Log for HierarchicalLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        // Hierarchical filtering by target
        let target = metadata.target();
        
        // Match module hierarchy
        match target {
            // Enable all trace for my_app::network
            t if t.starts_with("my_app::network") => {
                metadata.level() <= Level::Trace
            }
            // Enable info for my_app::database
            t if t.starts_with("my_app::database") => {
                metadata.level() <= Level::Info
            }
            // Enable warn for everything else
            _ => metadata.level() <= Level::Warn
        }
    }
    
    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            println!("[{}][{}] {}", record.target(), record.level(), record.args());
        }
    }
    
    fn flush(&self) {}
}
 
fn hierarchical_example() {
    // Targets enable hierarchical control:
    // "my_app" -> matches "my_app" and all submodules
    // "my_app::network" -> matches network and submodules
    // "my_app::network::tcp" -> matches specific submodule
    
    // This allows fine-grained control over log verbosity
}

Match target prefixes to enable hierarchical filtering.

Common Filter Patterns

use log::{Level, LevelFilter, Metadata};
 
struct ConfigurableLogger {
    filters: Vec<(String, LevelFilter)>,
}
 
impl ConfigurableLogger {
    fn parse_filters(spec: &str) -> Vec<(String, LevelFilter)> {
        // Parse "module_path=level" format
        // e.g., "my_app::network=debug,my_app::database=info"
        spec.split(',')
            .filter_map(|part| {
                let parts: Vec<&str> = part.trim().split('=').collect();
                if parts.len() == 2 {
                    let target = parts[0].to_string();
                    let level = parts[1].parse().ok()?;
                    Some((target, level))
                } else {
                    None
                }
            })
            .collect()
    }
}
 
impl log::Log for ConfigurableLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        // Find matching filter
        for (target_prefix, max_level) in &self.filters {
            if metadata.target().starts_with(target_prefix) {
                return metadata.level() <= *max_level;
            }
        }
        
        // Default: allow warn and above
        metadata.level() <= Level::Warn
    }
    
    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            println!("[{}] {}", record.level(), record.args());
        }
    }
    
    fn flush(&self) {}
}
 
fn filter_patterns() {
    let logger = ConfigurableLogger {
        filters: vec![
            ("my_app::network".to_string(), LevelFilter::Debug),
            ("my_app::database".to_string(), LevelFilter::Info),
            ("third_party_lib".to_string(), LevelFilter::Error),
        ],
    };
    
    // Enables debug for network, info for database, error for third_party_lib
}

Use target prefixes to configure log levels per module.

Target vs Level Filtering

use log::{Level, LevelFilter, Metadata};
 
struct DualFilterLogger {
    global_level: LevelFilter,
    module_levels: Vec<(String, LevelFilter)>,
}
 
impl log::Log for DualFilterLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        // First check global level
        if metadata.level() > self.global_level {
            return false;
        }
        
        // Then check module-specific level
        for (target_prefix, max_level) in &self.module_levels {
            if metadata.target().starts_with(target_prefix) {
                return metadata.level() <= *max_level;
            }
        }
        
        true
    }
    
    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            println!("[{}][{}] {}", record.target(), record.level(), record.args());
        }
    }
    
    fn flush(&self) {}
}
 
fn filtering_comparison() {
    // Target: filters by module origin (WHERE)
    // Level: filters by importance (WHAT)
    
    // Combined: "Show debug messages from network module,
    //           info messages from database module,
    //           warn messages everywhere else"
    
    let logger = DualFilterLogger {
        global_level: LevelFilter::Debug,
        module_levels: vec![
            ("my_app::network".to_string(), LevelFilter::Debug),
            ("my_app::database".to_string(), LevelFilter::Info),
        ],
    };
}

Target filtering controls which modules are logged; level controls importance.

Structured Logging with Target

use log::{info, Record};
 
struct StructuredLogger;
 
impl log::Log for StructuredLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool {
        true
    }
    
    fn log(&self, record: &Record) {
        // Use target as structured field
        let log_entry = serde_json::json!({
            "timestamp": chrono::Utc::now().to_rfc3339(),
            "level": record.level().to_string(),
            "target": record.target(),  // Module path
            "message": record.args().to_string(),
        });
        
        println!("{}", serde_json::to_string(&log_entry).unwrap());
    }
    
    fn flush(&self) {}
}
 
fn structured_output() {
    // Output:
    // {"timestamp":"2024-01-15T10:30:00Z","level":"INFO","target":"my_app::network::connection","message":"Connected"}
    // {"timestamp":"2024-01-15T10:30:01Z","level":"DEBUG","target":"my_app::database::query","message":"Query executed"}
    
    // Target enables routing to different destinations:
    // - "my_app::http::*" -> access.log
    // - "my_app::database::*" -> database.log
}

Target provides structured context for log aggregation and routing.

Third-Party Library Targets

// Third-party libraries have their own targets
// Based on their module structure
 
use log::info;
 
fn library_targets() {
    // Your code: target = "my_app::module"
    info!("My application log");
    
    // Third-party library: target = "hyper::client"
    // When hyper logs, its target is "hyper::client"
    
    // Third-party library: target = "sqlx::query"
    // When sqlx logs, its target is "sqlx::query"
    
    // This allows filtering third-party logs separately:
    let filter = vec![
        ("my_app".to_string(), LevelFilter::Debug),  // Your app: debug
        ("hyper".to_string(), LevelFilter::Warn),     // Hyper: warn only
        ("sqlx".to_string(), LevelFilter::Info),      // SQLx: info
    ];
}

Third-party libraries use their own targets, enabling separate filtering.

Environment-Based Configuration

use log::{LevelFilter, SetLoggerError};
 
fn env_configured_logging() -> Result<(), SetLoggerError> {
    // RUST_LOG environment variable is a common pattern
    // RUST_LOG=my_app=debug,hyper=info,sqlx=warn
    
    let rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "warn".to_string());
    
    // Parse RUST_LOG into filters
    let filters: Vec<(String, LevelFilter)> = rust_log
        .split(',')
        .filter_map(|spec| {
            let parts: Vec<&str> = spec.trim().split('=').collect();
            match parts.len() {
                1 => {
                    // Just a level: apply globally
                    let level = parts[0].parse().ok()?;
                    Some(("".to_string(), level))
                }
                2 => {
                    // module=level
                    let target = parts[0].to_string();
                    let level = parts[1].parse().ok()?;
                    Some((target, level))
                }
                _ => None
            }
        })
        .collect();
    
    // Configure logger with parsed filters
    
    Ok(())
}

Environment variables enable runtime configuration of target-based filtering.

Target and Module Macros

use log::info;
 
mod network {
    pub mod tcp {
        pub fn connect() {
            // Module: my_app::network::tcp
            // Target: "my_app::network::tcp"
            log::info!("TCP connection established");
        }
    }
    
    pub mod udp {
        pub fn bind() {
            // Module: my_app::network::udp
            // Target: "my_app::network::udp"
            log::info!("UDP socket bound");
        }
    }
}
 
fn module_targets() {
    network::tcp::connect();  // target = "my_app::network::tcp"
    network::udp::bind();      // target = "my_app::network::udp"
    
    // Both have "my_app::network" prefix
    // Filter "my_app::network" -> both included
    // Filter "my_app::network::tcp" -> only TCP
}

Module structure directly determines default targets.

Synthesis

Quick reference:

Aspect Description
Default target Module path where log macro is invoked
Explicit target Override with target: "custom" syntax
Purpose Filter logs by origin, route to destinations
Format Typically crate::module::submodule
Filtering Match target prefix for hierarchical control

Common patterns:

use log::{info, LevelFilter, Metadata};
 
fn patterns() {
    // Pattern 1: Filter by module prefix
    fn enabled(metadata: &Metadata) -> bool {
        metadata.target().starts_with("my_app::network")
    }
    
    // Pattern 2: Hierarchical level control
    fn enabled_hierarchical(metadata: &Metadata) -> bool {
        match metadata.target() {
            t if t.starts_with("my_app::network") => metadata.level() <= Level::Debug,
            t if t.starts_with("my_app::database") => metadata.level() <= Level::Info,
            _ => metadata.level() <= Level::Warn,
        }
    }
    
    // Pattern 3: Override target for grouping
    // log::info!(target: "api", "Request received");
    // log::info!(target: "api", "Response sent");
    // Both have target "api" despite different modules
    
    // Pattern 4: Filter third-party libraries
    fn enabled_third_party(metadata: &Metadata) -> bool {
        match metadata.target() {
            t if t.starts_with("my_app") => metadata.level() <= Level::Debug,
            t if t.starts_with("hyper") => metadata.level() <= Level::Warn,
            t if t.starts_with("sqlx") => metadata.level() <= Level::Info,
            _ => metadata.level() <= Level::Error,
        }
    }
}

Key insight: Metadata::target identifies the source of a log statement through its module path, enabling fine-grained filtering at the module level. The target defaults to the module path where the logging macro is used, creating an automatic association between code location and log origin. This default behavior means organizing code into modules directly organizes log filtering—placing related code in a module hierarchy groups their logs for filtering. Override the default target using target: "custom" in log macros to group logs across modules or identify specific subsystems like "database", "api", or "security". The target string supports hierarchical matching through prefix patterns: "my_app" matches all modules under my_app, while "my_app::network" matches only network modules. Log implementations use enabled(&Metadata) -> bool to check targets and levels, returning true if a log should be recorded. Combine target and level filtering to configure verbosity per module—for example, debug for development modules, warn for third-party libraries. Use target-based filtering in structured logging to route logs to different destinations based on origin, and parse environment variables like RUST_LOG=my_app=debug,hyper=info for runtime configuration.