Comment implémenter la fonctionnalité logwatch sur le journal systemd?

Logwatch parsing les files journaux à intervalles réguliers, recueille les erreurs et autres choses, puis les envoie à l'administrateur. C'était de return dans les jours syslog et logfiles. Quelle est la solution correspondante pour le journal de systemd?

Basé sur le post de blog par @Siosm, j'ai créé un outil avec lequel vous pouvez écrire une configuration simple avec des templates, puis vous êtes informé de tout ce qui ne correspond pas à ces templates: journal.

Vous pouvez en find plus dans le forum Archlinux .

Il semble y avoir aussi un patch et un hack disponible dans le tracker logwatch maintenant.

Pour autant que je sache, il n'y a pas de rlocation pour la fonctionnalité logwatch à partir d'aujourd'hui. Cependant, il est assez facile pour quelqu'un de le faire en utilisant le module python systemd.journal .

J'ai écrit un article de blog avec un exemple il y a quelques mois.

Exemple:

#!/usr/bin/env python3 from systemd import journal from datetime import datetime,timedelta import re import smtplib from email.mime.text import MIMEText # First, let's define patterns to ignore. # Those are for matching dynamically named units: session = re.comstack("session-[az]?\d+.scope") sshUnit = re.comstack("sshd@[0-9a-f.:]*") # Those will match the logged message itself: sshSocketStart = re.comstack("(Strating|Stopping) OpenSSH Per-Connection Daemon.*") sshAcceptPublicKey = re.comstack("Accepted publickey for (bob|alice).*") sshReceivedDisconnect = re.comstack("Received disconnect from.*") logindNewSession = re.comstack("New session [az]?\d+ of user (bob|alice).*") sshdSessionClosed = re.comstack(".*session closed for user (bob|alice).*") sessionOpenedRoot = re.comstack(".*session opened for user root.*") suSessionClosedGit = re.comstack(".*session opened for user git.*") anacronNormalExit = re.comstack("Normal exit (\d+ jobs run).*") postfixStatistics = re.comstack("statistics:.*") postfixHostnameDoesNotResolve = re.comstack("warning: hostname .* does not resolve to address .*: Name or service not known") # Open the journal for reading, set log level and go back one day and 10 minutes j = journal.Reader() j.log_level(journal.LOG_INFO) yesterday = datetime.now() - timedelta(days=1, minutes=10) j.seek_realtime(yesterday) # We'll store messages in this variable mailContent = [] # Filter and store output for entry in j: # Special cases for logs without a message if 'MESSAGE' not in entry: mailContent.append( 'U %s %s[%s]: EMPTY!' % ( datetime.ctime(entry['__REALTIME_TIMESTAMP']), entry['PRIORITY'], entry['SYSLOG_IDENTIFIER'], )) # With systemd unit name elif '_SYSTEMD_UNIT' in entry: if entry['PRIORITY'] > 4: if entry['_SYSTEMD_UNIT'] == "wtcomments.service": pass elif entry['_SYSTEMD_UNIT'] == "ffsync.service": pass elif session.match(entry['_SYSTEMD_UNIT']): pass elif sshUnit.match(entry['_SYSTEMD_UNIT']): if sshAcceptPublicKey.match(entry['MESSAGE']): pass elif sshReceivedDisconnect.match(entry['MESSAGE']): pass elif entry['_SYSTEMD_UNIT'] == "systemd-logind.service": if logindNewSession.match(entry['MESSAGE']): pass elif entry['_SYSTEMD_UNIT'] == "postfix.service": if postfixHostnameDoesNotResolve.match(entry['MESSAGE']): pass else: mailContent.append( 'U %s %s %s %s[%s]: %s' % ( datetime.ctime(entry['__REALTIME_TIMESTAMP']), entry['PRIORITY'], entry['_SYSTEMD_UNIT'], entry['SYSLOG_IDENTIFIER'], entry['_PID'], entry['MESSAGE'] )) # With syslog identifier only elif entry['SYSLOG_IDENTIFIER'] == "systemd": if sshSocketStart.match(entry['MESSAGE']): pass elif firewalldStart.match(entry['MESSAGE']): pass elif entry['SYSLOG_IDENTIFIER'] == "sshd": if sshdSessionClosed.match(entry['MESSAGE']): pass elif entry['SYSLOG_IDENTIFIER'] == "sudo": if sessionOpenedRoot.match(entry['MESSAGE']): pass elif entry['SYSLOG_IDENTIFIER'] == "CROND": if sessionOpenedRoot.match(entry['MESSAGE']): pass elif entry['SYSLOG_IDENTIFIER'] == "anacron": if anacronNormalExit.match(entry['MESSAGE']): pass elif entry['SYSLOG_IDENTIFIER'] == "postfix/anvil": if postfixStatistics.match(entry['MESSAGE']): pass elif entry['SYSLOG_IDENTIFIER'] == "su": if suSessionClosedGit.match(entry['MESSAGE']): pass else: mailContent.append( 'S %s %s %s: %s' % ( datetime.ctime(entry['__REALTIME_TIMESTAMP']), entry['PRIORITY'], entry['SYSLOG_IDENTIFIER'], entry['MESSAGE'] )) # Send the content in a mail to root mail = MIMEText('\n'.join(mailContent)) mail['Subject'] = '[example.com] Logs from ' + datetime.ctime(yesterday) + ' to ' + datetime.ctime(datetime.now()) mail['From'] = 'journald@example.com' mail['To'] = 'root@example.com' server = smtplib.SMTP('localhost') server.send_message(mail) server.quit()