Search code examples
loggingcronsystemd-journald

Programmaticaly parse journalctl to initiate an action


When my 3d printer runs out of filament, it pauses without sending any alert message. The only entry I can find is a message that on journalctl like this one: Jan 22 04:36:39 ultimakersystem-flipflop_wsgi.py[2884]: INF - root:80 - PrintJobStatus updated! 6e408dcf-9887, paused

I absolutely need to be alerted when the printer pauses, or else the print will be ruined. Is there a way to poll/parse journalctl programmatically, e.g. via a bash or python script, and send an email as appropriate?


Solution

  • There are likely other ways to do this more elegantly than polling, but FWIW:

    1. journalctl can time-filter its output; e.g. to see the last 30 minutes activity:
    journalctl --since "30 min ago" --until "now"
    
    1. cron can run jobs at various intervals; e.g. to run the above journalctl job every 30 minutes, put this line in your crontab:
    0,30 * * * * /bin/journalctl --since "30 min ago" --until "now"
    
    1. The journalctl output above must be checked for a match with the log entry of interest. From the error message you posted, I don't know exactly what key words tip off an empty filament bin, but let's assume for now that they are ultimakersystem and paused. awk provides an extensible way to find matching words & phrases: awk '/pattern1/ && /pattern2/' matches when both patterns occur in the same line of journalctl output, so add a pipe from the journalctl output to awk; the cron job above is modified as follows:
    0,30 * * * * /bin/journalctl --since "30 min ago" --until "now" | awk '/ultimakersystem/ && /paused/'
    
    1. The final step is to test if awk found a match, and if so, send a message to the designated user. In the event of one or more matches, only one email need be sent, and so the match test will be performed in an END section. The cron job is updated again as follows:
    0,30 * * * * /bin/journalctl --since "30 min ago" --until "now" | awk '/ultimakersystem/ && /paused/ {m=1}; END { if (m == 1){system("cat warn.txt | mail -s FILAMENT_NEEDED $USER")} }'
    

    awk uses a pattern-action paradigm; /ultimakersystem/ && /paused/ is the pattern, and {m=1} is the action. After awk has processed all the journalctl output, the END section is executed. A simple if (m == 1) test determines if one or more matches occurred, and if so, the mail command is invoked to send the message defined in warn.txt. GNU awk's system function REF provides access to the system's repertoire of commands.

    Among many other possibilities for changing this recipe, a date-time stamp can be inserted into the body of the email by replacing cat warn.txt with date, and sending mail to a someone other than $USER. See man mail, or your email client's documentation.