The Bash Logging Module allows you to customize the format of log messages using special placeholders.
- Default Format
- Format Placeholders
- Setting Format at Initialization
- Format Examples
- Timezone Display
- Configuration File Format Setting
- Changing Format at Runtime
- Format Best Practices
- Format Examples by Use Case
- Cloud and Structured Logging Formats
- Special Characters in Messages
- Escaping in Format Strings
- Testing Your Format
- Format and Color Output
- Related Documentation
The default log message format is:
%d [%l] [%s] %m
Which produces output like:
2026-01-16 14:30:45 [INFO] [myscript.sh] Application started
| Placeholder | Description | Example |
|---|---|---|
%d |
Date and time | 2026-01-16 14:30:45 |
%l |
Log level | INFO, ERROR, DEBUG |
%s |
Script name | myscript.sh |
%m |
Log message | Application started |
%z |
Timezone | UTC or LOCAL |
Use the --format option when initializing the logger:
# Custom format
init_logger --format "[%l] %d %z [%s] %m"
# Minimal format
init_logger --format "%l: %m"
# Detailed format
init_logger --format "%d %z [%l] %s: %m"init_logger --format "%l: %m"Output:
INFO: Application started
ERROR: Failed to open file
DEBUG: Variable value: count=42
When to use:
- Quick debugging
- When context is obvious
- Console-only logging
- Minimal output requirements
init_logger --format "%d [%l] [%s] %m"Output:
2026-01-16 14:30:45 [INFO] [myscript.sh] Application started
2026-01-16 14:30:46 [ERROR] [myscript.sh] Failed to open file
When to use:
- General purpose logging
- File logging
- Production environments
- Most use cases
init_logger --format "%d %z [%l] [%s] %m"Output:
2026-01-16 14:30:45 UTC [INFO] [myscript.sh] Application started
2026-01-16 14:30:46 UTC [ERROR] [myscript.sh] Failed to open file
When to use:
- Multi-timezone deployments
- Coordinated operations
- Compliance/audit requirements
- When UTC timestamps are used
init_logger --format "%d %s[$$]: [%l] %m"Output:
2026-01-16 14:30:45 myscript.sh[12345]: [INFO] Application started
Note: $$ is a Bash variable for the current process ID.
When to use:
- System logs
- Service logs
- Process tracking
- Syslog integration
init_logger --format "[%l] %m"Output:
[INFO] Application started
[ERROR] Failed to open file
When to use:
- Development
- Short-lived scripts
- When timestamp isn't needed
- Interactive use
init_logger --format "$(date +%H:%M:%S) [%l] %m"Output:
14:30:45 [INFO] Application started
14:30:46 [ERROR] Failed to open file
When to use:
- Same-day logs
- Performance monitoring
- Short-term debugging
- When date is implied
The %z placeholder shows the timezone mode:
# Local time (default)
init_logger --format "%d %z [%l] %m"
# Output: 2026-01-16 14:30:45 LOCAL [INFO] Message
# UTC time
init_logger --utc --format "%d %z [%l] %m"
# Output: 2026-01-16 19:30:45 UTC [INFO] MessageIf you don't include %z, the timezone isn't displayed (but timestamps are still affected by the --utc flag):
# Local time without indicator
init_logger --format "%d [%l] %m"
# Output: 2026-01-16 14:30:45 [INFO] Message
# UTC time without indicator
init_logger --utc --format "%d [%l] %m"
# Output: 2026-01-16 19:30:45 [INFO] MessageSet format in configuration files:
[logging]
# Standard format
format = %d [%l] [%s] %m
# Detailed format
format = %d %z [%l] [%s] %m
# Minimal format
format = [%l] %m
# Custom format
format = %d [%s] %l: %mSee Configuration for more details.
Use the set_log_format function to change format during script execution:
#!/bin/bash
source /path/to/logging.sh
# Start with default format
init_logger
log_info "Starting processing"
# Switch to minimal format for detailed debug output
set_log_format "[%l] %m"
log_debug "Processing item 1"
log_debug "Processing item 2"
# Return to detailed format
set_log_format "%d [%l] [%s] %m"
log_info "Processing complete"See Runtime Configuration for more details.
Always include %d when logging to files:
init_logger --log "/var/log/app.log" --format "%d [%l] [%s] %m"Use %s when multiple scripts log to the same file:
init_logger --log "/var/log/shared.log" --format "%d [%l] [%s] %m"Use %z for systems spanning multiple timezones:
init_logger --utc --format "%d %z [%l] [%s] %m"Keep it simple for interactive development:
init_logger --format "[%l] %m"Standardize format across your organization:
# Company standard format
init_logger --format "%d [%l] [%s] %m"If using log analysis tools, choose a format they can parse:
# JSON-like format for structured logging
# Note: This module doesn't produce actual JSON, but you can make it parseable
init_logger --format '%d|%l|%s|%m'# Quick debugging - minimal format
init_logger --format "[%l] %m"# Comprehensive logging with timezone
init_logger --log "/var/log/app/app.log" \
--format "%d %z [%l] [%s] %m" \
--utc# Syslog-style format with process ID
init_logger --journal --format "%d %s[$$]: %m"# Clear timezone indication
init_logger --utc --format "%d %z [%l] [%s] %m"# Include script name to distinguish sources
init_logger --log "/var/log/shared/all.log" --format "%d [%s] [%l] %m"# Minimal format, all to stderr
init_logger --stderr-level DEBUG --format "%l: %m"Modern cloud platforms and log aggregation services often prefer structured or semi-structured log formats. Here are examples for common platforms:
CloudWatch Logs works well with structured formats that can be parsed:
# CloudWatch-friendly format with clear structure
init_logger --format "%d [%l] [%s] %m" --utc
# CloudWatch with key-value pairs for easy parsing
init_logger --format "timestamp=%d level=%l script=%s message=%m" --utcOutput:
2026-01-16 14:30:45 [INFO] [myscript.sh] Application started
timestamp=2026-01-16 14:30:45 level=INFO script=myscript.sh message=Application started
When to use:
- AWS Lambda functions
- EC2 instances with CloudWatch agent
- ECS/Fargate containers
- Any AWS service that sends logs to CloudWatch
Best practices for CloudWatch:
- Always use UTC time (
--utc) - Include log level for filtering
- Keep format consistent across services
- Consider adding request IDs in the message for tracing
While the logging module doesn't produce true JSON, you can create a JSON-like format for log parsers:
# JSON-like format
init_logger --format '{"timestamp":"%d", "level":"%l", "script":"%s", "message":"%m"}' --utcOutput:
{"timestamp":"2026-01-16 14:30:45", "level":"INFO", "script":"myscript.sh", "message":"Application started"}
When to use:
- Log aggregation platforms (ELK, Splunk, Datadog)
- When logs need to be parsed programmatically
- Cloud-native applications
- Microservices architectures
Note: This creates JSON-like output but is not guaranteed to be valid JSON if the message contains special characters like quotes. For production use with strict JSON requirements, consider post-processing logs or using dedicated JSON logging tools.
A simpler structured format that's easier to parse:
# Pipe-delimited format
init_logger --format "%d|%l|%s|%m" --utcOutput:
2026-01-16 14:30:45|INFO|myscript.sh|Application started
2026-01-16 14:30:46|ERROR|myscript.sh|Failed to open file
When to use:
- Easy parsing with
cutorawk - CSV-like log analysis
- Simple log aggregation
- When JSON is overkill
For containerized applications, a format that works well with container log drivers:
# Container-friendly format
init_logger --format "%d %z %l %s: %m" --utc
# With structured fields
init_logger --format "time=%d tz=%z level=%l source=%s msg=%m" --utcOutput:
2026-01-16 14:30:45 UTC INFO myscript.sh: Application started
time=2026-01-16 14:30:45 tz=UTC level=INFO source=myscript.sh msg=Application started
When to use:
- Docker containers
- Kubernetes pods
- Container orchestration platforms
- When logs are collected by container runtime
Format optimized for Splunk ingestion:
# Splunk-friendly format with clear field extraction
init_logger --format "%d source=\"%s\" level=%l %m" --utcOutput:
2026-01-16 14:30:45 source="myscript.sh" level=INFO Application started
When to use:
- Splunk log ingestion
- When using Splunk Universal Forwarder
- Enterprise log management with Splunk
Choose format based on deployment environment:
#!/bin/bash
source /path/to/logging.sh
# Determine environment
ENV="${DEPLOY_ENV:-development}"
case "$ENV" in
aws|cloudwatch)
# AWS CloudWatch format
init_logger \
--format "timestamp=%d level=%l script=%s message=%m" \
--utc \
--log "/var/log/app.log"
;;
kubernetes|k8s)
# Kubernetes format
init_logger \
--format "time=%d tz=%z level=%l source=%s msg=%m" \
--utc \
--stderr-level DEBUG
;;
json)
# JSON-like format
init_logger \
--format '{"timestamp":"%d", "level":"%l", "script":"%s", "message":"%m"}' \
--utc \
--log "/var/log/app.json"
;;
development)
# Human-readable format
init_logger \
--format "[%l] %d - %m" \
--color
;;
*)
# Default format
init_logger \
--format "%d [%l] [%s] %m" \
--log "/var/log/app.log"
;;
esac
log_info "Application started in $ENV environment"The logging module handles special characters in log messages:
log_info "Processing file: /path/to/file.txt"
log_info "Status: 100% complete"
log_info "Command: grep -r 'pattern' /dir/"
log_info "Line break test\nSecond line"Most special characters are displayed as-is. For complex formatting in messages, consider using printf-style formatting:
# Format numbers, padding, etc. before logging
formatted_msg=$(printf "Processing: %-20s [%3d%%]" "$filename" "$percent")
log_info "$formatted_msg"Format placeholders are literal strings. If you need to include a literal % character, you can work around it:
# This works - % not followed by a format letter
init_logger --format "%d [%l] %m (100% complete)"
# This is interpreted as format string
# init_logger --format "%d [%l] %m %d" # Second %d would be replacedCreate a test script to see how your format looks:
#!/bin/bash
source /path/to/logging.sh
# Test your format
init_logger --format "[%l] %d %z - %s: %m"
log_debug "Debug message"
log_info "Info message"
log_notice "Notice message"
log_warn "Warning message"
log_error "Error message"
log_critical "Critical message"Format and color codes work together. Colors are applied to the entire line based on log level:
init_logger --format "[%l] %m" --color
log_info "This line will be colored"
log_error "This line will be colored differently"Color codes are automatically stripped when output is redirected to a file:
./script.sh > output.log # No color codes in file- Initialization - Setting format at startup
- Runtime Configuration - Changing format during execution
- Configuration - Format in config files
- Examples - Complete format examples