How to Monitor Log Files on macOS with a Custom Bash Script

In today’s IT landscape, efficiently monitoring and managing log files is crucial for maintaining system health and ensuring security. Whether you’re an IT professional or a Managed Service Provider (MSP), having the ability to swiftly search and alert on specific text within a log file can be a powerful tool in your arsenal. This blog post will explore a custom Bash script designed to alert users when specific text is detected in a log file on macOS.

The Importance of Log File Monitoring

Log files are a critical component in the operation and maintenance of IT systems. They contain records of events, processes, and errors that occur on a computer or network. By analyzing log files, IT professionals can identify issues, track down the root cause of problems, and ensure compliance with regulatory requirements. However, manually sifting through these files to find relevant information can be time-consuming and prone to human error.

This is where automation comes in. Automating the process of monitoring log files and triggering alerts when specific text is found can save time and reduce the risk of overlooking critical issues. The script we will discuss is a simple yet effective solution for achieving this on macOS.

The Script:

#!/usr/bin/env bash

# Description: Alert when the specified Text is found in a text file.
#
# Release Notes: Initial Release
#   By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
#   Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
#   Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
#   Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
#   Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
#   Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
#   Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
#   EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).
#
# Text to trigger on: [Alert]
#
# Below are all the valid parameters for this script.
# Preset Parameter: --file "/opt/MyLogFile.log" --text batman
#   Alerts when the text "batman" is found in the file /opt/MyLogFile.log
#    This is Case Sensitive
#     Example where it will alert: "I am batman!"
#     Example where it will alert: "Iambatman!"
#     Example where it will not alert: "IamBatman!"
#     Example where it will not alert: "I am Batman!"
#
# Preset Parameter: --file "/opt/MyLogFile.log" --text Batman --caseInsensitive true
#   Alerts when the text "Batman" is found in the file /opt/MyLogFile.log, but is case insensitive
#    This is Case Insensitive
#     Example where it will alert: "I am batman!"
#     Example where it will alert: "Iambatman!"
#
# Preset Parameter: --file "/opt/MyLogFile.log" --text Batman --wholeWord true
#   Alerts when the text "Batman" is found in the file /opt/MyLogFile.log, but only if it is a word in a sentence.
#    This is Case Sensitive
#     Example where it will alert: "I am Batman!"
#     Example where it will not alert: "IamBatman!"
#

# Determines whether or not help text is necessary and routes the output to stderr
die() {
    local _ret="${2:-1}"
    test "${_PRINT_HELP:-no}" = yes && print_help >&2
    echo "$1" >&2
    exit "${_ret}"
}

# Function that evaluates whether a value passed to it begins by a character
# that is a short option of an argument the script knows about.
# This is required in order to support getopts-like short options grouping.
begins_with_short_option() {
    local first_option all_short_options='ftiwh'
    first_option="${1:0:1}"
    test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}

# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_file=
_arg_text=
_arg_caseInsensitive="false"
_arg_wholeWord="false"

# Help text function for when invalid input is encountered
print_help() {
    printf '%s\n' "Alert when the specified Text is found in a text file."
    printf 'Usage: %s [-f|--file [path to file]] [-t|--text [text to search]] [-i|--caseInsensitive <true|false>] [-w|--wholeWord <true|false>] [-h|--help]\n' "$0"
    printf '\t%s\n' "-f, --file: path to a log file"
    printf '\t%s\n' "-t, --text: text to alert when found"
    printf '\t%s\n' "-i, --caseInsensitive: search text with case insensitivity (default: false)"
    printf '\t%s\n' "-w, --wholeWord: search for text as a whole word (default: false)"
    printf '\t%s\n' "-h, --help: Prints help"
}

# Grabbing the parameters and parsing through them.
parse_commandLine() {
    while test $# -gt 0; do
        _key="$1"
        case "$_key" in
        -f | --file)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_file="$2"
            shift
            ;;
        --file=*)
            _arg_file="${_key##--file=}"
            ;;
        -f*)
            _arg_file="${_key##-f}"
            ;;
        -t | --text)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_text="$2"
            shift
            ;;
        --text=*)
            _arg_text="${_key##--text=}"
            ;;
        -t*)
            _arg_text="${_key##-t}"
            ;;
        -i | --caseInsensitive)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_caseInsensitive="$2"
            shift
            ;;
        --caseInsensitive=*)
            _arg_caseInsensitive="${_key##--caseInsensitive=}"
            ;;
        -i*)
            _arg_caseInsensitive="${_key##-i}"
            ;;
        -w | --wholeWord)
            test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
            _arg_wholeWord="$2"
            shift
            ;;
        --wholeWord=*)
            _arg_wholeWord="${_key##--wholeWord=}"
            ;;
        -w*)
            _arg_wholeWord="${_key##-w}"
            ;;
        -h | --help)
            print_help
            exit 0
            ;;
        -h*)
            print_help
            exit 0
            ;;
        *)
            _PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1
            ;;
        esac
        shift
    done
}

parse_commandLine "$@"

text=$_arg_text
file=$_arg_file
caseInsensitive=$_arg_caseInsensitive
wholeWord=$_arg_wholeWord

# Check if Script Variables where used and overwrite command line parameters
if [[ -n "${textToMatch}" ]]; then
    text=$textToMatch
fi
if [[ -n "${textFile}" ]]; then
    file=$textFile
fi
if [[ -n "${matchWholeWord}" ]]; then
    wholeWord=$matchWholeWord
fi
if [[ -n "${insensitiveToCase}" ]]; then
    caseInsensitive=$insensitiveToCase
fi

# Check if text is not an empty string
if [[ -z "${text}" ]]; then
    echo "[Error] Text not specified"
    exit 2
fi

# Check if text is not an empty string
if [[ -z "${file}" ]]; then
    echo "[Error] File not specified"
    exit 2
fi

# Does file exit and is readable
if [ -f "${file}" ]; then
    echo "[Info] File \"${file}\" exists"
    if [ -r "${file}" ]; then
        echo "[Info] File \"${file}\" is readable"
    else
        echo "[Error] File \"${file}\" is not readable"
        exit 2
    fi
else
    echo "[Error] File \"${file}\" does not exists"
    exit 2
fi

# Detect
count=0
if [[ "${wholeWord}" == "true" ]]; then
    if [[ "${caseInsensitive}" == "true" ]]; then
        count=$(grep -c -i -n -w "$text" "$file")
    else
        count=$(grep -c -n -w "$text" "$file")
    fi
else
    if [[ "${caseInsensitive}" == "true" ]]; then
        count=$(grep -c -i -n -e "$text" "$file")
    else
        count=$(grep -c -n -e "$text" "$file")
    fi
fi

# Alert
if ((count > 0)); then
    echo "[Alert] Found text in file"
    exit 1
else
    echo "[Info] Not found text in file"
    exit 0
fi

 

Access over 300+ scripts in the NinjaOne Dojo

Get Access

Understanding the Script

The provided script is a Bash script designed to search for specific text within a log file and trigger an alert if that text is found. It allows users to customize the search parameters, such as whether the search should be case-sensitive or if it should match whole words only. Here’s a detailed breakdown of how the script works.

Script Initialization

The script begins by defining a few functions to handle errors, check for valid arguments, and display help information. The die() function, for instance, is used to terminate the script and display an error message if something goes wrong. This function also prints the help text if the –help option is provided.

Default Parameter Setup

The script initializes several default parameters:

  • _arg_file: Stores the path to the log file to be searched.
  • _arg_text: Contains the text to search for within the log file.
  • _arg_caseInsensitive: Determines if the search should be case-insensitive.
  • _arg_wholeWord: Specifies if the search should match only whole words.

These parameters can be modified by the user via command-line arguments when running the script.

Parsing Command-Line Arguments

The parse_commandLine() function is responsible for parsing the command-line arguments passed to the script. It supports several options, such as –file to specify the log file, –text to define the search text, and –caseInsensitiveand –wholeWord to customize the search behavior.

If any required parameters are missing, the script will exit with an error message. This ensures that the script runs with all the necessary information.

Validating Inputs and File Accessibility

Before performing the search, the script validates the inputs to ensure they are not empty and checks if the specified file exists and is readable. If the file does not exist or is not accessible, the script exits with an error message, preventing unnecessary execution.

Searching the Log File

The core functionality of the script lies in its ability to search the log file based on the parameters provided. The script uses the grep command, a powerful tool for searching text in files, to perform the search. Depending on the options selected, grep will search for the text with case sensitivity or insensitivity, and either match the entire word or part of a word.

  • If –wholeWord is set to true, the script adds the -w flag to grep, ensuring only whole words are matched.
  • If –caseInsensitive is true, the script uses the -i flag to ignore case during the search.

Triggering Alerts

Once the search is complete, the script counts the number of matches found in the log file. If any matches are detected, the script triggers an alert by printing [Alert] Found text in file. If no matches are found, it prints [Info] Not found text in file. This straightforward approach ensures that users are immediately informed if the text they are monitoring appears in the log file.

Real-World Application

Imagine an IT professional managing a server farm where it’s crucial to monitor for specific error messages in log files. By deploying this script, they can automatically be alerted whenever a particular error code or message appears in the logs, allowing them to take immediate action. For example, if the text “disk failure” is found in the logs, the script can trigger an alert, enabling the IT team to address the issue before it escalates into a critical system failure.

Comparison to Other Methods

While this script provides a simple and effective way to monitor log files, other methods can achieve similar results. For instance, more advanced log management systems like Splunk or ELK stack offer comprehensive log analysis, including real-time monitoring, complex queries, and integrations with other systems. However, these solutions often require more resources and may be overkill for smaller environments or specific use cases where a simple Bash script is sufficient.

Frequently Asked Questions

1. Can this script be used on operating systems other than macOS?

Yes, this script is written in Bash, which is available on most Unix-like operating systems, including Linux. However, some adjustments may be necessary depending on the specific environment.

2. How can I modify the script to search multiple files at once?

You can modify the script to loop through multiple files by expanding the –file option to accept a list of files or directories. The script can then iterate through each file and perform the search.

3. What happens if the log file is very large?

For very large log files, performance may become an issue. In such cases, consider optimizing the search by limiting the number of lines searched or using more advanced tools like awk or log management systems.

Implications for IT Security

By automating log file monitoring, this script can help improve IT security by ensuring that critical issues are detected and addressed promptly. For example, detecting unauthorized access attempts or malware activity in logs can help prevent security breaches and reduce the risk of data loss.

Best Practices

When using this script, consider the following best practices:

  • Regularly update the script to ensure compatibility with your environment.
  • Use clear and specific text search parameters to avoid false positives.
  • Implement additional logging or alerting mechanisms to complement the script.

Final Thoughts

For IT professionals and MSPs, tools like this script are invaluable for automating routine tasks and ensuring system health. By integrating such scripts into your workflow, you can focus on more strategic activities while maintaining confidence that critical issues will not go unnoticed. NinjaOne offers a suite of tools that can further enhance your IT management capabilities, providing additional layers of automation, monitoring, and security to support your IT operations.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service deliver tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Categories:

You might also like

Watch Demo×
×

See NinjaOne in action!

By submitting this form, I accept NinjaOne's privacy policy.

NinjaOne Terms & Conditions

By clicking the “I Accept” button below, you indicate your acceptance of the following legal terms as well as our Terms of Use:

  • Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms.
  • Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party.
  • Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library belonging to or under the control of any other software provider.
  • Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations.
  • Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks.
  • Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script.
  • EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).