Python
Python on Advantech Routers
This chapter describes how to install and use Python on Advantech routers by leveraging Python Router Apps (RAs). Python extends the router's capabilities, enabling custom scripting, automation, data processing, and more.
Introduction to Python Support
Python is a versatile and powerful high-level programming language known for its readability and extensive libraries. On Advantech routers, Python support is provided through installable Router Apps, transforming your router into a more flexible and programmable device. This allows for:
- Automation of router management tasks.
- Custom data collection and analysis directly on the edge.
- Development of IoT solutions and integrations.
- Enhanced network monitoring and diagnostics.
Two versions of the Python Router App are available to cater to different needs and resource constraints, as detailed in the next section.
Choosing Your Python Router App
Advantech offers two distinct Python Router Apps. Understanding their differences is key to selecting the appropriate one for your requirements.
Python 3 Router App (Full Version)
The Python 3 Router App delivers a full-featured Python 3 environment, empowering your router with advanced development tools. This version includes:
pip: The Python package installer, allowing you to easily install and manage third-party libraries from the Python Package Index (PyPI).- Native
hashlib: Provides faster cryptographic operations as it utilizes underlying C libraries, beneficial for performance-sensitive tasks involving hashing. - Full UNICODE support: Essential for applications dealing with international character sets and diverse text data.
venv: The standard tool for creating lightweight, isolated virtual environments, allowing you to manage dependencies for different projects separately.
This version transforms the router into a powerful platform suitable for complex network automation, data analysis, and sophisticated IoT solutions. It is ideal for developers who need a robust and flexible Python environment and where router resources (CPU, RAM, storage) are sufficient.
Python 3 Lite Router App
The Python 3 Lite Router App provides a lightweight Python 3 runtime, optimized for routers with limited resources or for scenarios where a minimal footprint is critical. Key features:
- Core Python functionality: Retains the essential Python 3 features for scripting and automation.
- No
pip: This version does not include thepippackage installer. Third-party libraries must be manually managed if needed, or pure-Python libraries can be bundled with your scripts. - Limited UNICODE support: UNICODE support might be limited compared to the full version, which could be a consideration for applications processing diverse text.
- Pure-Python
hashlib: Uses a pure-Python implementation ofhashlib. While fully functional, it may be slower for intensive cryptographic operations compared to the native version. This ensures compatibility and reduces dependencies in constrained environments.
The Lite version is best suited for simpler tasks such as log parsing, basic device monitoring, or straightforward automation scripts where minimal system impact and resource usage are paramount.
Comparison Summary
The table below summarizes the key differences between the two Python Router Apps:
| Feature | Python 3 Router App | Python 3 Lite Router App |
|---|---|---|
| Python Version | Python 3.x | Python 3.x |
pip | Included | Not Included |
hashlib | Native (faster) | Pure-Python (compatible) |
| UNICODE Support | Full | Limited |
venv | Included | Not Included |
| Resource Usage | Higher | Lower |
| Ideal Use Case | Complex tasks, development | Simple tasks, resource-constrained |
Comparison of Python Router Apps
Installing Python Router Apps
Python is installed on Advantech routers by installing the appropriate Python Router App package.
Prerequisites
Before installing a Python Router App, ensure:
- Sufficient Storage Space: Python and its libraries can consume significant storage. Available disk space can be checked in the Router App GUI.
- Internet Connectivity (Optional): If you plan to use
pip(with the full Python 3 Router App) to download packages, the router will need internet access. The Router App package itself might be obtained via download or provided by Advantech.
Installation Procedure
To install Python 3 or Python 3 Lite, follow the instructions in the manual, section Customization Pages → Router Apps.
Verifying the Installation
Once installed, you can verify that Python is available and running correctly:
- Access the router's command-line interface (CLI).
- Type the command to check the Python version:
python3 --versionThis should output the installed Python version, e.g., Python 3.x.y.
- (Optional, for Full Python 3 RA) Test
pip:
pip3 --version- (Optional) Try to import a standard module like
hashlibwithin Python:
python3 -c "import hashlib; print(hashlib)"This should print information about the hashlib module.
If these commands execute without error, Python is ready to use.
Accessing and Running Python
You can interact with Python on the router in two primary ways: through the interactive Python shell or by executing Python scripts.
Interactive Python Shell (REPL)
The Read-Eval-Print Loop (REPL) allows you to type Python code directly and see immediate results. This is useful for testing small snippets of code or exploring Python features.
- Log in to the router's CLI.
- Start the Python 3 interpreter:
python3
You should see the Python prompt (>>>).
- You can now type Python commands:
>>> print("Hello from Advantech Router!")
Hello from Advantech Router!
>>> a = 10
>>> b = 20
>>> print(a + b)
30
>>> import os
>>> os.uname()
(system information will be displayed here)- To exit the Python REPL, type:
>>> exit()Or press Ctrl-D.
Executing Python Scripts
For more complex tasks, you will write Python code into .py files and execute them as scripts.
Creating/Transferring Scripts:
- Using
vi: You can create or edit scripts directly on the router using thevitext editor:vi myscript.py, refer to Busybox vi tutorial. - Transferring Scripts: You can write scripts on your development computer and transfer them to the router using
scp(Secure Copy Protocol) or by mounting a USB drive. Example usingscpfrom your computer to the router's/tmpdirectory:scp myscript.py admin@ROUTER_IP_ADDRESS:/tmp/. Note that the/tmpdirectory is deleted upon router restart.
Script Structure and Execution:
- Shebang Line (Recommended): Start your script with a "shebang" line to specify the interpreter. This allows the script to be executed directly.
#!/usr/bin/python3
# Your Python code follows
print("This is my Python script.")- Make the script executable:
chmod +x /path/to/your/myscript.py- Run the script:
If you used a shebang line and made the script executable:
You can always run it by explicitly calling the Python interpreter:
python3 /path/to/your/myscript.pyPassing Command-Line Arguments
You can pass arguments to your Python scripts from the command line. These are accessible within Python via the sys.argv list. Here's an example script myscript_args.py:
#!/usr/bin/python3
import sys
print(f"Script name: {sys.argv[0]}")
if len(sys.argv) > 1:
print(f"First argument: {sys.argv[1]}")
if len(sys.argv) > 2:
print(f"Second argument: {sys.argv[2]}")To run this script with arguments, execute:
python3 myscript_args.py arg1 "another argument"The output of this script will be:
Script name: myscript_args.py
First argument: arg1
Second argument: another argumentIntroduction to Python Scripting
This section provides a very brief overview of Python concepts particularly relevant for scripting. It is not a comprehensive Python tutorial. For in-depth learning, refer to the official Python documentation at https://docs.python.org/3/.
Core Concepts:
- Variables and Data Types:
- Strings:
my_string = "hello" - Numbers: Integers (
count = 10), Floats (pi = 3.14) - Lists: Ordered collections, mutable.
my_list = [1, "two", 3.0] - Dictionaries: Key-value pairs, unordered, mutable.
my_dict = {"name": "router", "ip": "192.168.1.1"} - Booleans:
True,False
- Strings:
- Control Flow:
- Note the code block definition by indentation (usually 4 spaces).
if/elif/else: Conditional execution.
if status == "up":
print("System is operational.")
elif status == "down":
print("System is down!")
else:
print("Status unknown.")forloops: Iterate over sequences.
for item in my_list:
print(item)whileloops: Repeat as long as a condition is true.
count = 0
while count < 5:
print(count)
count += 1- Functions: Define reusable blocks of code (note the indentation).
def greet(name):
print(f"Hello, {name}!")
greet("Advantech User")- Importing Modules: Use Python's extensive standard library or third-party modules.
import os
import subprocess
import sys
import re
import datetime
import socket
import json
print(os.name)
print(sys.version)
# For the full Python RA, you might import libraries installed via pip
# import requestsStandard Library Modules for Scripting:
Here are examples of some standard library modules preinstalled with Python on the router:
os: Interacting with the operating system (e.g.,os.system(),os.environ, file system operations).subprocess: Recommended for running external shell commands and managing their input/output, especially in BusyBox environments (e.g.,subprocess.run(),subprocess.check_output()).sys: Access to system-specific parameters and functions, like command-line arguments (sys.argv), exit codes (sys.exit()).re: Regular expressions for powerful text pattern matching and manipulation (e.g., parsing log files or command output).datetime: For working with dates and times (e.g., timestamping logs).socket: For low-level network operations if needed.json: For parsing and generating JSON data, common in APIs and configurations.
Non-standard Library Modules for Scripting:
You can install additional modules using pip3, refer to the section on using pip to install third-party libraries.
Advanced Features
The full Python 3 Router App includes additional tools that enhance development capabilities. These features are not available in the Python 3 Lite Router App.
Using pip to Install Third-Party Libraries
pip, or pip3 for Python3, is the standard package installer for Python. It allows you to download and install packages from the Python Package Index (PyPI) and other repositories.
Basic Usage:
- Install a package:
pip3 install package_nameFor example, to install the popular requests library for making HTTP requests:
pip3 install requests- Install a specific version of a package:
pip3 install package_name==1.2.3- Upgrade an installed package:
pip3 install --upgrade package_name- List installed packages:
pip3 list- Show information about an installed package:
pip3 show package_name- Uninstall a package:
pip3 uninstall package_nameConsiderations:
- Storage Space: Third-party libraries can consume significant storage space. Be mindful of the router's limited resources. Install only necessary packages.
- Internet Connectivity: The router needs internet access to download packages from PyPI.
- Compilation: Some Python packages may require compilation of C/C++ extensions during installation. The router environment may lack the necessary compilers or development headers. Prioritize packages that are pure Python or provide pre-compiled "wheels" for ARM Linux (or the router's architecture).
- Permissions: You typically need root or administrative privileges to install packages globally.
Using venv for Isolated Virtual Environments
venv is a module used to create isolated Python virtual environments. Each virtual environment has its own Python binary (or a link to it) and can have its own independent set of installed Python packages in its site directories.
Benefits:
- Dependency Management: Avoids conflicts between projects that require different versions of the same library.
- Clean Global Environment: Keeps your global Python site-packages directory clean.
- Reproducibility: Makes it easier to replicate a project's environment.
Basic Usage:
- Create a virtual environment:
Navigate to your project directory (or where you want to create the environment) and run:
python3 -m venv my_project_envThis will create a directory named my_project_env (or your chosen name) containing the virtual environment.
- Activate the virtual environment:
Before you can use the virtual environment, you need to activate it. The activation script is located in the environment'sbindirectory.
source my_project_env/bin/activateYour shell prompt will usually change to indicate that the virtual environment is active (e.g., (my_project_env) user@router:~$).
- Install packages within the environment:
Once activated, anypip3 installcommands will install packages into the active virtual environment, not globally.
(my_project_env) $ pip3 install requestsRun Python scripts:
Python scripts run while the environment is active will use the environment's Python interpreter and its installed packages.Deactivate the virtual environment:
When you are finished working in the virtual environment, you can deactivate it:
(my_project_env) $ deactivateYour shell prompt will return to normal.
- Delete the virtual environment:
To completely delete the whole environment, just delete its directory:
rm -rf my_project_envConsiderations on Routers:
- Storage: Each virtual environment duplicates some files or creates symlinks, and stores its own packages, consuming additional storage space. Use judiciously on resource-constrained routers.
- Activation: Remember to activate the correct environment before running scripts that depend on its specific packages. For automated scripts (e.g., cron jobs), you'll need to source the activate script or call the Python interpreter from within the venv directly (e.g.,
/path/to/my_project_env/bin/python your_script.py).
Router-Specific Python Development Notes
Developing Python scripts for Advantech routers requires consideration of their specific operating environment, which is typically based on BusyBox and has resource constraints. Here are key points to keep in mind:
Limited Shell Commands and BusyBox Environment: BusyBox provides a compact set of Unix utilities that often have fewer options and may exhibit slightly different behavior compared to their full GNU counterparts found on standard Linux distributions. Python scripts calling external commands should account for these differences.
Robust Command Execution with
subprocess: Python'ssubprocessmodule is highly recommended for running external shell commands. It offers superior control over input/output streams, error handling, and overall execution flow compared to older methods likeos.system(). Usingsubprocesscan also enhance script portability when dealing with variations in command behavior or availability.On-Router Text Editing: The primary text editor available directly on the router is usually
vi(or a similar minimalist editor likenanoon some builds). Familiarity withviis beneficial for quick on-router script modifications. A helpful BusyBox vi tutorial can be found online.File System Paths and Volatility: Be mindful of the router's file system structure. Temporary files should typically be written to
/tmp, which is often a RAM disk (tmpfs) and its contents are cleared on reboot. Locations for persistent storage depend on the router model and configuration (e.g.,/opt,/mnt/user, or an attached USB drive).Permissions and Privileges: Standard Linux file permissions apply. Your Python scripts will need execute permissions (
chmod +x script.py) to be run directly. Accessing certain system files, network interfaces, or performing privileged operations (like using raw sockets or modifying system configuration) may require root privileges. Run scripts with the minimum necessary privileges.Resource Constraints (CPU, RAM, Storage): Embedded routers inherently have limited CPU power, RAM, and persistent storage compared to desktop systems. Write efficient Python code. Avoid memory-intensive operations, very large libraries, or frequent disk writes if possible, especially when using the "Lite" version of the Python Router App, which has a smaller footprint. Monitor resource usage during development and testing.
Router-Specific SDK Python Module (
um): For interacting with router-specific hardware and software features—such as accessing GPIOs, reading system parameters (e.g., cellular signal strength, device temperature), managing expansion port configurations, or generating HTML content for the router's web interface—Advantech provides a specialized Python module namedum. This module is included as part of the Advantech SDK. Always consult the SDK documentation for your target platform (refer to the section on SDK) for details on the availability, specific features, and usage of theummodule.Cross-Compilation for Python Packages with C Extensions: If your Python project requires packages that include C extensions (which are common for performance-critical libraries or those interfacing with C libraries),
piprunning on the router might be unable to build them directly if a C compiler and the necessary development headers are not available on the router (which is typical). In such cases, you may need to:- Cross-compile the package on a development machine using a toolchain that targets the router's architecture (e.g., ARM). This involves setting up a cross-compilation environment for Python extensions.
- Find pre-compiled wheels (
.whlfiles) for the package that are specifically built for the router's architecture and Python version. - Consider if a pure-Python alternative to the package exists.
This is an advanced topic and generally falls outside simple scripting.
By leveraging Python's strengths in scripting and its subprocess module, you can effectively overcome many of the limitations of a basic BusyBox shell and create powerful automation and management tools for your Advantech router.
Practical Python Script Examples
The following examples demonstrate simple use cases for Python on your Advantech router. These scripts assume they are run from the router's CLI.
Gathering System Information
This script uses the subprocess module to run BusyBox commands and display system information. This script pings a list of IP addresses to check their reachability. Save the code below into a file named system_info.py.
#!/usr/bin/python3
import subprocess
print("Gathering Basic System Information...\n")
print("--- System Uptime ---")
try:
with open("/proc/uptime", "r") as f:
uptime_seconds = float(f.readline().split()[0])
days = int(uptime_seconds // (24 * 3600))
uptime_seconds %= (24 * 3600)
hours = int(uptime_seconds // 3600)
uptime_seconds %= 3600
minutes = int(uptime_seconds // 60)
seconds = int(uptime_seconds % 60)
print(f"System has been up for: {days} days, {hours} hours, {minutes} minutes, {seconds} seconds.")
except Exception as e:
print(f"Could not get uptime: {e}")
print()
print("--- Memory Usage ---")
try:
result = subprocess.run("free", shell=True, capture_output=True, text=True, check=True)
print(result.stdout.strip())
print("(Output is typically in kilobytes)")
except Exception as e:
print(f"Could not get memory usage: {e}")
print()
print("--- Disk Space ---")
try:
result = subprocess.run("df -k", shell=True, capture_output=True, text=True, check=True)
print(result.stdout.strip())
print("(Output is in 1K-blocks/kilobytes)")
except Exception as e:
print(f"Could not get disk space: {e}")
print()
print("--- Hostname ---")
try:
with open("/proc/sys/kernel/hostname", "r") as f:
hostname = f.read().strip()
print(hostname)
except Exception as e:
print(f"Could not get hostname: {e}")
print()How It Works
This script gathers and displays basic system information from an Advantech router.
The script begins with a shebang
#!/usr/bin/python3, indicating it should be executed using the Python 3 interpreter located at/usr/bin/python3.It imports the
subprocessmodule: This standard Python module is used to run external shell commands, specifically to execute thefreecommand.The script then proceeds sequentially to gather and print different pieces of system information, each within its own section. For error handling, each section uses a
try...except Exception as e:block, which catches any general error that occurs and prints a user-friendly message.System Uptime:
- A header
"--- System Uptime ---"is printed. - The script reads uptime information directly from the
/proc/uptimefile. This is a standard virtual file in Linux systems that provides uptime statistics without needing an externaluptimecommand.with open("/proc/uptime", "r") as f:: This opens the file in read mode ("r"). Thewithstatement ensures the file is automatically closed even if errors occur.f.readline().split(): Reads the first line from the file, splits this line into a list of strings (words), and takes the first element (``), which is the total system uptime in seconds.float(...): Converts the uptime string to a floating-point number.
- The total uptime in seconds is then converted into a more human-readable format of days, hours, minutes, and seconds using integer division (
//) and modulo (%) operations. - The formatted uptime string is printed to the console.
- A header
Memory Usage:
- A header
"--- Memory Usage ---"is printed. - The
subprocess.run()function is used to execute the external shell commandfree.- Command:
"free"(displays amount of free and used memory in the system). shell=True: Indicates that the command should be executed through the system's shell.capture_output=True: Specifies that the standard output and standard error of the command should be captured.text=True: Decodes the captured output as a text string.check=True: If the command returns a non-zero exit status (indicating an error), aCalledProcessErrorexception is raised.
- Command:
result.stdout.strip(): The captured standard output from thefreecommand is printed after removing any leading or trailing whitespace.- A note
"(Output is typically in kilobytes)"is appended, as this is a common default for the BusyBox version offree.
- A header
Disk Space:
- A header
"--- Disk Space ---"is printed. - Again,
subprocess.run()is used, this time to execute the commanddf -k.- Command:
"df -k"(reports file system disk space usage). The-koption ensures the output is in 1K-blocks (kilobytes), which is generally supported by BusyBoxdfand avoids issues with unsupported options like-h.
- Command:
- The standard output from
df -kis printed. - A note
"(Output is in 1K-blocks/kilobytes)"clarifies the units of the displayed sizes.
- A header
Hostname:
- A header
"--- Hostname ---"is printed. - The system's hostname is read directly from the
/proc/sys/kernel/hostnamefile.with open("/proc/sys/kernel/hostname", "r") as f:: Opens this virtual file for reading.f.read().strip(): Reads the entire content of the file (which is the hostname) and removes any leading/trailing whitespace.
- The retrieved hostname is printed to the console.
- A header
After each section's output (or error message),
print()is called to produce a blank line, improving the readability of the overall output in the terminal.
Script Testing
Below is the console output, displaying the script's creation (using vi) and its testing.
/var/scripts # vi system_info.py
(...create and save sripty with pi...)
/var/scripts # chmod +x system_info.py
/var/scripts # ./system_info.py
Gathering Basic System Information...
--- System Uptime ---
System has been up for: 0 days, 1 hours, 10 minutes, 34 seconds.
--- Memory Usage ---
total used free shared buff/cache available
Mem: 503840 38604 448744 184 16492 456496
Swap: 0 0 0
(Output is typically in kilobytes)
--- Disk Space ---
Filesystem 1024-blocks Used Available Use% Mounted on
/dev/root 64512 23544 40968 36% /
devtmpfs 251408 0 251408 0% /dev
none 251920 0 251920 0% /tmp
none 50384 184 50200 0% /var
/dev/mtdblock7 131072 19812 111260 15% /opt
/dev/mtdblock8 128 36 92 28% /var/data
(Output is in 1K-blocks/kilobytes)
--- Hostname ---
Router
/var/scripts #Basic Network Reachability Test
This script pings a list of IP addresses to check their reachability. Save the code below into a file named network_test.py.
#!/usr/bin/python3
import subprocess
import sys
def ping_host(host_ip, count=1):
"""Pings a host and returns True if reachable, False otherwise."""
command = ["ping", "-c", str(count), "-W", "1", host_ip]
try:
process = subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
process.communicate()
return process.returncode == 0
except FileNotFoundError:
print(f"Error: 'ping' command not found.", file=sys.stderr)
return False
except Exception as e:
print(f"Error pinging {host_ip}: {e}", file=sys.stderr)
return False
if __name__ == "__main__":
hosts_to_check = ["8.8.8.8", "1.1.1.1", "192.168.1.254"]
if len(sys.argv) > 1:
hosts_to_check = sys.argv[1:]
print("--- Network Reachability Test ---")
for host in hosts_to_check:
print(f"Pinging {host}... ", end="")
if ping_host(host):
print("Reachable")
else:
print("Unreachable")How It Works
This script checks the network reachability of one or more specified hosts by sending ICMP ECHO_REQUEST packets (commonly known as "pinging" them).
The script starts with a shebang
#!/usr/bin/python3, which tells the system to use thepython3interpreter found in the user's environment PATH to execute the script.It imports two standard Python modules:
subprocess: Used to create and manage child processes, specifically to run the externalpingcommand.sys: Provides access to system-specific parameters and functions, such as command-line arguments (sys.argv) and standard error stream (sys.stderr).
The
ping_host(host_ip, count=1)Function:
This function is responsible for pinging a single host and determining if it is reachable.- It takes two arguments:
host_ip: The IP address or hostname of the target to ping.count=1: An optional argument specifying the number of ping packets to send. It defaults to 1.
- A list named
commandis constructed, representing thepingcommand and its arguments:["ping", "-c", str(count), "-W", "1", host_ip]"ping": The ping utility."-c", str(count): Sends a specificcountof packets. (e.g.,-c 1sends one packet)."-W", "1": Sets a timeout of 1 second to wait for each reply. This is crucial for BusyBox ping, which might behave differently from other ping versions regarding timeout for the whole operation vs. per-packet.host_ip: The target host.
- The
try...exceptblock handles potential errors during the ping process:subprocess.Popen(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL): This starts thepingcommand as a new process.stdout=subprocess.DEVNULLandstderr=subprocess.DEVNULL: The standard output and standard error streams of thepingcommand are redirected toDEVNULL, meaning its output will not be displayed on the console. The script only cares about the success or failure (exit code).
process.communicate(): Waits for thepingcommand to complete.return process.returncode == 0: Checks the exit code of thepingcommand. An exit code of0typically indicates success (host is reachable). The function returnsTrueif successful,Falseotherwise.except FileNotFoundError:: If thepingcommand itself is not found on the system, this error is caught. An error message is printed tosys.stderr, and the function returnsFalse.except Exception as e:: Catches any other exceptions that might occur during the ping attempt. An error message including the specific exception is printed tosys.stderr, and the function returnsFalse.
- It takes two arguments:
Main Execution Block (
if __name__ == "__main__":):
This part of the script runs when the script is executed directly.hosts_to_check = ["8.8.8.8", "1.1.1.1", "192.168.1.254"]: A default list of IP addresses/hostnames is defined. These are public DNS servers and a common local gateway IP.- Command-Line Argument Handling:
if len(sys.argv) > 1:: Checks if the script was run with any command-line arguments (the script name itself issys.argv).hosts_to_check = sys.argv[1:]: If arguments are provided, the default list is replaced with the list of arguments supplied by the user. For example, runningpython3 script.py google.com example.comwould sethosts_to_checkto["google.com", "example.com"].
- A header
"--- Network Reachability Test ---"is printed. - The script then iterates through each
hostin thehosts_to_checklist:print(f"Pinging {host}... ", end=""): Prints a message indicating which host is currently being pinged. Theend=""argument prevents a newline, so the "Reachable" or "Unreachable" status will appear on the same line.if ping_host(host):: Calls theping_hostfunction with the current host.- Based on the boolean return value of
ping_host(), it prints either"Reachable"or"Unreachable"to the console.
Script Testing
Below is the console output, displaying the script's creation (using vi) and its testing.
/var/scripts # vi network_test.py
(...create and save sripty with pi...)
/var/scripts # chmod +x network_test.py
/var/scripts # ./network_test.py
--- Network Reachability Test ---
Pinging 8.8.8.8... Reachable
Pinging 1.1.1.1... Reachable
Pinging 192.168.1.254... Unreachable
/var/scripts #Simple Log File Monitoring
This script demonstrates reading a log file (e.g., /var/log/messages) and searching for specific keywords. Save the code below into a file named log_monitoring.py.
Caution
Continuously reading large log files can impact performance. This example is illustrative. For production use, consider more optimized log monitoring tools or techniques if available or develop a more efficient file tailing mechanism in Python.
#!/usr/bin/python3
import sys
import re
import time
def monitor_log(log_file_path, keywords, interval_seconds=5):
"""Monitors a log file for lines containing specified keywords."""
print(f"Monitoring {log_file_path} for keywords: {keywords}")
print(f"Checking every {interval_seconds} seconds. Press Ctrl+C to stop.")
patterns = [re.compile(keyword, re.IGNORECASE) for keyword in keywords]
try:
last_lines_seen = set()
while True:
current_lines = set()
try:
with open(log_file_path, 'r') as f:
for line_number, line in enumerate(f, 1):
line = line.strip()
current_lines.add(line)
if line in last_lines_seen:
continue
for pattern in patterns:
if pattern.search(line):
print(f"[MATCH] Line {line_number}: {line}")
break
except FileNotFoundError:
print(f"Error: Log file '{log_file_path}' not found.", file=sys.stderr)
return
except Exception as e:
print(f"Error reading log file: {e}", file=sys.stderr)
last_lines_seen.update(current_lines)
time.sleep(interval_seconds)
except KeyboardInterrupt:
print("\nLog monitoring stopped.")
if __name__ == "__main__":
log_file = "/var/log/messages"
search_keywords = ["error", "warn", "dhcp"]
if len(sys.argv) > 1:
log_file = sys.argv[1]
if len(sys.argv) > 2:
search_keywords = sys.argv[2:]
try:
with open(log_file, 'r') as f:
pass
except Exception as e:
print(f"Cannot access log file {log_file}: {e}", file=sys.stderr)
sys.exit(1)
monitor_log(log_file, search_keywords)How It Works
This script monitors a specified log file for new entries containing specific keywords and prints matching lines.
Setup:
- The script starts with a shebang line (
#!/usr/bin/python3), indicating it should be executed using the Python 3 interpreter. - It imports two standard Python modules:
sys: Provides access to system-specific parameters and functions, such as command-line arguments (sys.argv) and standard error stream (sys.stderr).re: Provides support for regular expressions, which are used for matching patterns in text.time: Provides time-related functions, used here to pause the script between checks.
- The script starts with a shebang line (
The
monitor_log(log_file_path, keywords, interval_seconds=5)Function:- This function monitors the specified log file for new lines containing any of the given keywords.
- It prints the log file being monitored and the keywords.
- It compiles the keywords into regular expression patterns for efficient matching.
- It enters an infinite loop (
while True) to continuously check the log file. - Inside the loop:
- It attempts to open and read the log file. If successful, it processes each line:
- It strips leading and trailing whitespace from the line.
- It checks if the line is already in
last_lines_seen(to avoid processing the same line multiple times). - For new lines, it checks if any of the keyword patterns match the line.
- If a match is found, it prints the matching line's number and content.
- If the log file is not found or another error occurs, it prints an error message and stops monitoring.
- After processing the log file, it updates
last_lines_seenwith the current lines to keep track of seen lines. - It sleeps for the specified interval before checking the log file again.
- It attempts to open and read the log file. If successful, it processes each line:
Main Execution Block (
if __name__ == "__main__":)- This part of the script runs when the script is executed directly.
- It sets default values for the log file and search keywords.
- It checks for command-line arguments to override the default log file and keywords.
- It attempts to open the log file to verify accessibility.
- It calls the
monitor_logfunction to start monitoring the log file.
Script Testing
Below is the console output, displaying the script's creation (using vi) and its testing.
/var/scripts # vi log_monitoring.py
(...create and save sripty with pi...)
/var/scripts # chmod +x log_monitoring.py
/var/scripts # ./log_monitoring.py
Monitoring /var/log/messages for keywords: ['error', 'warn', 'dhcp']
Checking every 5 seconds. Press Ctrl+C to stop.
[MATCH] Line 15: 2025-05-22 12:34:43 [info] dhcpd: Wrote 0 leases to leases file.
[MATCH] Line 21: 2025-05-22 12:34:48 [warning] totd[1431]: Disabling rescanning of network interfaces
^C
Log monitoring stopped.