w3resource

Bash Logging and Error Reporting Exercises, Solutions & Explanation

1.

Simple Logging:

Write a Bash script that logs a message to a file named log.txt.

Code:

#!/bin/bash

# Log message function
log_message() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[${timestamp}] ${message}" >> log.txt
}

# Main script
log_message "This is a log message."

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh
ad@DESKTOP-3KE0KU4:~$ cat log.txt
[2024-04-25 19:32:07] This is a log message.

Explanation:

In the exercise above,

The Bash script defines a function called "log_message()" that takes a message as input, adds a timestamp to it, and appends the formatted message to a file named 'log.txt'. The 'timestamp' variable is created using the "date" command to get the current date and time in the format "YYYY-MM-DD HH:MM:SS". Then, the function echoes the formatted message to 'log.txt'.

Finally, the script calls the "log_message()" function with the message "This is a log message."

2.

Verbose Logging:

Extend the previous script to include an option -v for verbose logging. When the -v option is provided, the script should log additional information.

Code:

#!/bin/bash

# Function to log verbose message
log_verbose() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[VERBOSE] [${timestamp}] ${message}" >> log.txt
}

# Function to log regular message
log_message() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[${timestamp}] ${message}" >> log.txt
}

# Main script

# Check if verbose option is provided
if [[ "$1" == "-v" ]]; then
    shift   # Shift command line arguments to ignore the first argument (-v)
    verbose=true
else
    verbose=false
fi

# Log a regular message
log_message "Starting script execution."

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh
ad@DESKTOP-3KE0KU4:~$ cat log.txt
[2020-04-25 19:39:38] Starting script execution.
[2020-04-25 19:39:38] Script execution completed.

Explanation:

In the exercise above,

  • log_verbose(): This function logs a verbose message to the "log.txt" file. It takes a message as input and appends it to the log file along with a timestamp.
  • log_message(): This function logs a regular message to the "log.txt" file. It also takes a message as input and appends it to the log file along with a timestamp.
  • Main script:
    • It first checks if the -v option is provided. If it is, it shifts the command line arguments to ignore the -v option, and sets a variable 'verbose' to true.
    • It then logs a regular message indicating the start of script execution.
    • If verbose logging is enabled ('verbose' is true), it logs a verbose message indicating that verbose logging is enabled.
    • Script logic can be added here. This part is empty.
    • Finally, it logs a regular message indicating script execution completion.

3.

Error Logging:

Write a Bash script that logs errors to a separate file named error.log. Any error encountered during script execution should be logged to this file.

Code:

#!/bin/bash

# Function to log errors
log_error() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[${timestamp}] ${message}" >> error.log
}

# Main script

# Example of an error (attempting to access a non-existent file)
file="non_existent_file.txt"

# Check if the file exists
if [ ! -f "$file" ]; then
    # Log the error
    log_error "Error: File '$file' not found."
fi

# Another example of an error (division by zero)
dividend=10
divisor=0

# Check if divisor is zero
if [ $divisor -eq 0 ]; then
    # Log the error
    log_error "Error: Division by zero."
fi

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh
ad@DESKTOP-3KE0KU4:~$ cat error.log
[2020-04-25 19:55:25] Error: File 'non_existent_file.txt' not found.
[2020-04-25 19:55:25] Error: Division by zero.

Explanation:

In the exercise above,

  • Function definition (log_error):
    • log_error() is a function that takes a message as an argument and logs it along with a timestamp to the "error.log" file.
  • Main script:
    • Two examples of errors are demonstrated:
      • Error 1: Trying to access a non-existent file.
      • Error 2: Attempting to divide by zero.
    • For each error:
      • It checks for error conditions.
      • If the condition is met, it logs an appropriate error message using the "log_error()" function.

4.

Debug Mode:

Write a Bash script that implements a debug mode in a Bash script using the -d option. When the -d option is provided, the script should log debugging information.

Code:

#!/bin/bash

# Function to log debug messages
log_debug() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[DEBUG] [${timestamp}] ${message}" >> debug.log
}

# Main script
while getopts ":d" opt; do
    case ${opt} in
        d )
            debug_mode=true
            ;;
        \? )
            echo "Usage: $0 [-d]" >&2
            exit 1
            ;;
    esac
done

shift $((OPTIND -1))

# Example debug messages
if [[ $debug_mode ]]; then
    log_debug "Starting script"
fi 
# Your script commands go here

if [[ $debug_mode ]]; then
    log_debug "Script completed"
fi

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh -d
ad@DESKTOP-3KE0KU4:~$ cat debug.log
[DEBUG] [2024-04-25 20:06:19] Starting script
[DEBUG] [2024-04-25 20:06:19] Script completed

Explanation:

In the exercise above,

  • The script defines a function "log_debug()" to log debug messages to a file named 'debug.log'.
  • It uses "getopts" to parse the command-line options.
  • If the -d option is provided, the variable 'debug_mode' is set to true.
  • Debug messages are logged before and after the main script commands, depending on whether debug mode is enabled or not.
  • To run in debug mode, use the -d option:

5.

Timestamps:

Modify the logging functions in your script to include timestamps for each log entry.

Code:

#!/bin/bash

# Function to log messages with timestamp
log_message() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[${timestamp}] ${message}" >> log.txt
}

# Function to log debug messages with timestamp
log_debug() {
    local message="$1"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[DEBUG] [${timestamp}] ${message}" >> log.txt
}

# Main script
while getopts ":d" opt; do
    case ${opt} in
        d )
            debug_mode=true
            ;;
        \? )
            echo "Usage: $0 [-d]" >&2
            exit 1
            ;;
    esac
done
shift $((OPTIND -1))

# Example debug messages
if [[ $debug_mode ]]; then
    log_debug "Starting script"
fi

# Your script commands go here

if [[ $debug_mode ]]; then
    log_debug "Script completed"
fi

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh
ad@DESKTOP-3KE0KU4:~$ cat log.txt
[2020-04-25 19:39:38] Starting script execution.
[2020-04-25 19:39:38] Script execution completed

Explanation:

In the exercise above,

  • Logging functions:
    • log_message(): This function logs a message with a timestamp to a file named "log.txt".
    • log_debug(): This function logs a debug message with a timestamp to the same "log.txt" file, but it prepends "[DEBUG]" to the message.
  • Main script:
    • The script uses "getopts" to handle command-line options. Here, it looks for the -d option, which enables debug mode.
    • If the -d option is provided, it sets the variable 'debug_mode' to true.
    • The shift $((OPTIND -1)) line removes the processed options and their arguments from the command line arguments list.
  • Debug Mode:
    • If debug mode is enabled, debug messages are logged using "log_debug()".
    • The script commands that you want to debug should go between the debug log messages.
  • Example of Debug Messages:
    • These are just placeholder debug messages to demonstrate how to use the "log_debug()" function.
    • You can place these debug messages before and after the actual commands you want to debug.

6.

Log Rotation:

Write a Bash script that implements log rotation in a Bash script, where old log files are archived and new log files are created when a certain size limit is reached.

Code:

#!/bin/bash

# Log rotation function
rotate_logs() {
    local log_file="$1"
    local max_size="$2"
    local max_files="$3"
    
    echo "Rotating logs for $log_file..."
    
    # Check if the log file exists
    if [ -f "$log_file" ]; then
        # Get the size of the log file
        local size=$(du -b "$log_file" | cut -f1)
        
        # Check if the size exceeds the maximum size
        if [ "$size" -gt "$max_size" ]; then
            echo "Log file $log_file exceeds maximum size, rotating..."
            
            # Determine the next available log file name
            local next_file="$log_file.1"
            local count=2
            while [ -f "$next_file" ]; do
                next_file="$log_file.$count"
                ((count++))
            done
            
            # Rename the current log file to the next file
            mv "$log_file" "$next_file"
            
            # Compress the old log file
            gzip "$next_file"
            
            # Check the number of archived log files
            local archived_files=$(ls -1 "${log_file}.*.gz" 2>/dev/null | wc -l)
            
            # Remove old archived files if there are more than the maximum allowed
            if [ "$archived_files" -gt "$max_files" ]; then
                echo "Removing old archived log files..."
                oldest_file=$(ls -t "${log_file}.*.gz" | tail -n 1)
                rm "$oldest_file"
            fi
        else
            echo "Log file $log_file is within size limit."
        fi
    else
        echo "Log file $log_file not found, creating..."
    fi
    
    # Create a new log file
    touch "$log_file"
}

# Main script
log_file="log.txt"
max_size=1000000  # 1 MB
max_files=5       # Maximum number of archived log files

# Rotate logs
rotate_logs "$log_file" "$max_size" "$max_files"

# Log a message
timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$timestamp] This is a log message." >> "$log_file"

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh
Rotating logs for log.txt...
Log file log.txt is within size limit.
ad@DESKTOP-3KE0KU4:~$ cat log.txt
[2020-04-25 19:39:38] Starting script execution.
[2020-04-25 19:39:38] Script execution completed.
[2020-04-26 09:03:02] This is a log message.
[2020-04-26 09:07:07] This is a log message.

Explanation:

In the exercise above,

  • Function rotate_logs():
    • This function rotates logs. It takes three arguments: the log file name, the maximum size of the log file, and the maximum number of archived log files to keep.
    • It first checks if the log file exists. If not, it creates it.
    • Then it checks if the log file exceeds the maximum size. If it does, it renames the current log file with an incrementing number and compresses it. It also removes old archived log files if there are more than the specified maximum.
    • Finally, it creates a new log file.
  • Main script:
    • Defines the log file name, maximum size, and maximum number of archived log files.
    • Calls the "rotate_logs()" function to rotate logs.
    • Logs a message to the log file.

7.

Logging Levels:

Extend the logging functions to support different logging levels such as INFO, WARNING, and ERROR. Each log entry should include its corresponding logging level.

Code:

#!/bin/bash

# Log file name
LOG_FILE="log.txt"

# Maximum log file size in bytes
MAX_SIZE=1000000  # 1 MB

# Maximum number of archived log files to keep
MAX_ARCHIVED=5

# Function to rotate logs
rotate_logs() {
    local log_file="$1"
    local max_size="$2"
    local max_archived="$3"
    
    # Check if log file exists
    if [ ! -e "$log_file" ]; then
        touch "$log_file"
    fi
    
    # Check if log file exceeds maximum size
    if [ $(stat -c %s "$log_file") -gt "$max_size" ]; then
        # Rename current log file with incrementing number
        i=1
        while [ -e "${log_file}.${i}" ]; do
            ((i++))
        done
        mv "$log_file" "${log_file}.${i}"
        
        # Compress the old log file
        gzip "${log_file}.${i}"
        
        # Remove old archived log files if more than maximum
        for ((j=i-max_archived; j>0; j--)); do
            if [ -e "${log_file}.${j}.gz" ]; then
                rm "${log_file}.${j}.gz"
            fi
        done
        
        # Create a new log file
        touch "$log_file"
    fi
}

# Function to log messages
log_message() {
    local level="$1"
    local message="$2"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    echo "[${timestamp}] [${level}] ${message}" >> "$LOG_FILE"
}

# Function to log INFO messages
log_info() {
    log_message "INFO" "$1"
}

# Function to log WARNING messages
log_warning() {
    log_message "WARNING" "$1"
}

# Function to log ERROR messages
log_error() {
    log_message "ERROR" "$1"
}

# Main script

# Perform log rotation
rotate_logs "$LOG_FILE" "$MAX_SIZE" "$MAX_ARCHIVED"

# Log messages with different levels
log_info "This is an informational message."
log_warning "This is a warning message."
log_error "This is an error message."

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh
ad@DESKTOP-3KE0KU4:~$ cat log.txt
[2020-04-25 19:39:38] Starting script execution.
[2020-04-25 19:39:38] Script execution completed.
[2020-04-26 09:03:02] This is a log message.
[2020-04-26 09:07:07] This is a log message.
[2020-04-26 10:05:13] [INFO] This is an informational message.
[2020-04-26 10:05:13] [WARNING] This is a warning message.
[2020-04-26 10:05:13] [ERROR] This is an error message.

Explanation:

In the exercise above,

  • Variables:
    • LOG_FILE: Name of the log file.
    • MAX_SIZE: Maximum size of the log file in bytes.
    • MAX_ARCHIVED: Maximum number of archived log files to keep.
  • Function rotate_logs():
    • Rotates log files by renaming the current log file with an incrementing number and compressing the old log file.
    • Removes old archived log files if more than the maximum allowed.
    • Creates a new log file if the current log file exceeds the maximum size.
  • Functions for Logging:
    • log_message(): Logs a message with a specified level and appends it to the log file.
    • log_info(), log_warning(), log_error(): Convenience functions to log messages with different levels.
  • Main Script:
    • Calls rotate_logs() to rotate log files if needed.
    • Logs messages with different levels using the logging functions.

8.

Custom Log Format:

Write a Bash script that allows users to specify a custom log format using a command-line option (e.g., -f "%timestamp - %message"). The script should support different placeholders for the log format.

Code:

#!/bin/bash

# Default log format
LOG_FORMAT="%timestamp - %level: %message"

# Function to print help message
print_help() {
    echo "Usage: $0 [-f <format>]"
    echo "Options:"
    echo "  -f, --format <format>    Set custom log format (e.g., \"%timestamp - %level: %message\")"
    echo "  -h, --help               Display this help message"
}

# Parse command line options
while [[ $# -gt 0 ]]; do
    case "$1" in
        -f|--format)
            shift
            if [[ -n "$1" ]]; then
                LOG_FORMAT="$1"
            else
                echo "Error: No log format specified."
                print_help
                exit 1
            fi
            ;;
        -h|--help)
            print_help
            exit 0
            ;;
        *)
            echo "Error: Invalid option '$1'."
            print_help
            exit 1
            ;;
    esac
    shift
done

# Function to format log messages
format_log_message() {
    local level="$1"
    local message="$2"
    local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
    local formatted_log="${LOG_FORMAT//\%timestamp/$timestamp}"
    formatted_log="${formatted_log//\%level/$level}"
    formatted_log="${formatted_log//\%message/$message}"
    echo "$formatted_log"
}

# Function to log messages
log_message() {
    local level="$1"
    local message="$2"
    echo "$(format_log_message "$level" "$message")"
}

# Main script
log_message "INFO" "This is an informational message."
log_message "WARNING" "This is a warning message."
log_message "ERROR" "This is an error message." 

Output:

ad@DESKTOP-3KE0KU4:~$ ./test1.sh
2020-04-26 10:24:39 - INFO: This is an informational message.
2020-04-26 10:24:39 - WARNING: This is a warning message.
2020-04-26 10:24:39 - ERROR: This is an error message.

Explanation:

In the exercise above,

  • Default log format: LOG_FORMAT="%timestamp - %level: %message"
    • Defines the default log format using placeholders %timestamp, %level, and %message.
  • print_help() function:
    • Displays the script's usage and available options.
    • Syntax: print_help() { ... }
  • Parsing command line options:
    • Uses a while loop to iterate over the command line options.
    • Handles -f or --format option to specify a custom log format.
    • Handles -h or --help option to display help.
    • Syntax: while [[ $# -gt 0 ]]; do ... done
  • format_log_message() function:
    • Formats log messages based on the provided log level and message.
    • Replaces placeholders in the log format with actual values.
    • Syntax: format_log_message() { ... }
  • log_message() function:
    • Logs messages using the specified log level and message.
    • Calls format_log_message() to format the log message.
    • Syntax: log_message() { ... }
  • Main script:
    • Calls log_message() with different log levels and messages.
    • Example log messages: informational, warning, and error.
    • Syntax: log_message "INFO" "message"

Bash Editor:


More to Come !

Do not submit any solution of the above exercises at here, if you want to contribute go to the appropriate exercise page.



Follow us on Facebook and Twitter for latest update.