The Bash Logging Module can integrate with systemd's journal logging system, making it ideal for system services and applications running on modern Linux distributions.
- Overview
- Requirements
- Enabling Journal Logging
- Journal Tags
- Log Level Mapping
- Viewing Journal Logs
- Use Cases
- Journal vs. File Logging
- Sensitive Data and Journal
- Journal Storage
- Troubleshooting
- Performance Considerations
- Related Documentation
Journal logging sends log messages to the systemd journal using the logger command. This provides:
- Centralized logging with systemd
- Integration with system logs
- Structured log metadata
- Persistent storage (when configured)
- Powerful querying with
journalctl - Automatic log rotation
- systemd (standard on most modern Linux distributions)
loggercommand (typically from theutil-linuxpackage)
# Check if logger is installed
which logger
# Check if systemd is running
systemctl --versionIf logger is not installed:
# Debian/Ubuntu
sudo apt-get install util-linux
# RHEL/CentOS/Fedora
sudo dnf install util-linux
# Arch Linux
sudo pacman -S util-linux# Enable journal logging
init_logger --journal
# With custom tag
init_logger --journal --tag "myapp"
# Combined with file logging
init_logger --journal --log "/var/log/myapp.log" --tag "myapp"[logging]
journal = true
tag = myapp
level = INFOSee Configuration for more details.
# Enable journal logging after initialization
set_journal_logging true
# Change the tag
set_journal_tag "new-app-name"
# Disable journal logging
set_journal_logging falseSee Runtime Configuration for more details.
Tags help identify log messages from different applications in the journal.
By default, the script name is used as the tag:
# Script: /usr/local/bin/backup.sh
init_logger --journal
# Tag will be: backup.shSpecify a custom tag:
# Use application name as tag
init_logger --journal --tag "myapp"
# Use service name
init_logger --journal --tag "backup-service"
# Use component name
init_logger --journal --tag "database-monitor"- Use application names - Consistent naming across deployments
- Keep it short - Easier to type in journalctl commands
- Use hyphens - Better than spaces for command-line use
- Be descriptive - Make it clear what's logging
Log levels are mapped to syslog priorities:
| Module Level | Syslog Priority | journalctl Filter |
|---|---|---|
| DEBUG | debug | PRIORITY=7 |
| INFO | info | PRIORITY=6 |
| NOTICE | notice | PRIORITY=5 |
| WARN | warning | PRIORITY=4 |
| ERROR | err | PRIORITY=3 |
| CRITICAL | crit | PRIORITY=2 |
| ALERT | alert | PRIORITY=1 |
| EMERGENCY | emerg | PRIORITY=0 |
# View all logs with specific tag
journalctl -t myapp
# Follow logs in real-time
journalctl -f -t myapp
# View logs from current boot
journalctl -b -t myapp
# View logs from today
journalctl -t myapp --since today
# View logs from last hour
journalctl -t myapp --since "1 hour ago"# Show only errors and above (PRIORITY <= 3)
journalctl -t myapp -p err
# Show warnings and above
journalctl -t myapp -p warning
# Show info and above
journalctl -t myapp -p info# Logs since specific date/time
journalctl -t myapp --since "2026-01-16 14:00:00"
# Logs until specific date/time
journalctl -t myapp --until "2026-01-16 15:00:00"
# Logs between times
journalctl -t myapp --since "10:00:00" --until "11:00:00"
# Last 100 lines
journalctl -t myapp -n 100
# Follow (like tail -f)
journalctl -t myapp -f# Detailed output (default)
journalctl -t myapp
# Short output (similar to syslog)
journalctl -t myapp -o short
# JSON output (for parsing)
journalctl -t myapp -o json
# JSON output (pretty-printed)
journalctl -t myapp -o json-pretty
# Only the message field
journalctl -t myapp -o cat#!/bin/bash
# /usr/local/bin/myservice.sh
source /usr/local/lib/logging.sh
init_logger --journal --tag "myservice" --level INFO --utc
log_info "Service starting"
# Service logic here
log_info "Service stopped"Systemd unit file:
[Unit]
Description=My Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myservice.sh
Restart=always
[Install]
WantedBy=multi-user.targetView logs:
journalctl -u myservice.service -f
# or
journalctl -t myservice -f#!/bin/bash
# /usr/local/bin/backup-script.sh
source /usr/local/lib/logging.sh
init_logger --journal --tag "backup-cron" --level INFO
log_info "Backup started"
# Backup logic here
if [[ $? -eq 0 ]]; then
log_info "Backup completed successfully"
else
log_error "Backup failed"
fiView logs:
journalctl -t backup-cron --since today#!/bin/bash
source /usr/local/lib/logging.sh
init_logger --journal --log "/var/log/monitor.log" \
--tag "monitor-daemon" --level INFO
log_info "Daemon starting"
while true; do
# Monitoring logic
log_debug "Checking system status"
if [[ $status != "OK" ]]; then
log_warn "Status check failed"
fi
sleep 60
doneLog to both journal and file:
#!/bin/bash
source /usr/local/lib/logging.sh
# Logs go to both journal and file
init_logger \
--journal --tag "myapp" \
--log "/var/log/myapp/app.log" \
--level INFO
log_info "Application started"Benefits:
- Journal for system integration and queries
- File for backups and long-term archival
- File for easy grepping and analysis
- System services and daemons
- Applications running under systemd
- When you need structured metadata
- When you want centralized system logs
- When log rotation is handled by systemd
- When you need powerful log queries
- Application-specific logs
- When you need custom log rotation
- When logs need to be shipped elsewhere
- When you need simple grep/awk access
- When running on systems without systemd
- When you need guaranteed log format
- Critical applications requiring redundancy
- When different teams need different formats
- When you want local files and system integration
- For compliance requiring multiple log stores
Important: Sensitive data logged with log_sensitive is NOT sent to the journal:
init_logger --journal --tag "myapp"
log_info "User logged in" # Goes to journal
log_sensitive "API Key: $API_KEY" # Does NOT go to journal
log_info "Request processed" # Goes to journalSee Sensitive Data for more details.
# Show journal disk usage
journalctl --disk-usage
# Show journal statistics
journalctl --verifyEdit /etc/systemd/journald.conf:
[Journal]
# Limit journal size
SystemMaxUse=500M
# Keep logs for 30 days
MaxRetentionSec=30d
# Keep at least 100M of logs
SystemKeepFree=100MRestart journald:
sudo systemctl restart systemd-journald# Check if logger is installed
which logger
# Install if missing (Debian/Ubuntu)
sudo apt-get install util-linuxWarning: logger found at unexpected location
This security warning appears when logger is not in one of the trusted system directories:
Warning: logger found at unexpected location: /home/user/bin/logger
Expected: /bin, /usr/bin, /usr/local/bin, /sbin, or /usr/sbin
Journal logging disabled for security
Resolution:
-
Check your PATH: Ensure system directories appear before custom paths
echo $PATH # Should show /usr/bin before custom directories
-
Verify logger location:
command -v logger readlink -f $(command -v logger)
-
Use system logger: Ensure
/usr/bin/loggeror/bin/loggerexists/usr/bin/logger -t test "Direct test"
-
Temporary fix: Adjust PATH to prioritize system directories
export PATH="/usr/bin:/bin:/usr/local/bin:$PATH"
Why this check exists: This security feature prevents PATH manipulation attacks where a malicious logger
executable could intercept your journal logs. See Security Policy
for details.
# Check if systemd is running
systemctl --version
# Check journal service status
systemctl status systemd-journald
# Test logger manually
logger -t test "Test message"
journalctl -t test -n 5The logger command typically doesn't require special permissions, but check:
# Test as current user
logger -t test "Test from $USER"
journalctl -t test -n 1#!/bin/bash
source /path/to/logging.sh
init_logger --journal --tag "test-$(date +%s)"
log_info "Test message 1"
log_error "Test error 1"
# Check immediately
journalctl -t "test-*" -n 5Journal logging is generally fast, but consider:
- Asynchronous by default -
loggerreturns immediately - No blocking - Won't slow down your script
- Buffering - systemd handles buffering and writes
- Network logging - Can be slower if sending to remote journal
To avoid oversized journal entries and reduce DoS risk, log messages are capped by default. The
journal limit matches the console/file limit unless you override it with --max-journal-length
or max_journal_length in config files (set to 0 to disable limits).
For high-throughput applications:
# Use file logging for performance-critical sections
set_journal_logging false
# ... high-throughput operations ...
set_journal_logging true- Initialization - Enabling journal logging at startup
- Configuration - Journal settings in config files
- Runtime Configuration - Changing journal settings dynamically
- Sensitive Data - Understanding what doesn't go to the journal
- Examples - Complete journal logging examples