如何在 systemd 日志之上实现日志监视功能?

lkr*_*aav 7 security syslog systemd error-handling

Logwatch 定期扫描日志文件,收集错误和内容,然后将其邮寄给管理员。这是在 syslog 和 logfiles 时代。systemd的journal对应的解决方案是什么?

Tim*_*ier 2

据我所知,到目前为止,日志监视功能还没有替代品。然而,对于某人来说,使用systemd.journal python 模块来制作自己的模块是很容易的。

几个月前,我写了一篇带有示例的博文。

例子:

#!/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.compile("session-[a-z]?\d+.scope")
sshUnit = re.compile("sshd@[0-9a-f.:]*")

# Those will match the logged message itself:
sshSocketStart = re.compile("(Strating|Stopping) OpenSSH Per-Connection Daemon.*")
sshAcceptPublicKey = re.compile("Accepted publickey for (bob|alice).*")
sshReceivedDisconnect = re.compile("Received disconnect from.*")
logindNewSession = re.compile("New session [a-z]?\d+ of user (bob|alice).*")
sshdSessionClosed = re.compile(".*session closed for user (bob|alice).*")
sessionOpenedRoot = re.compile(".*session opened for user root.*")
suSessionClosedGit = re.compile(".*session opened for user git.*")
anacronNormalExit = re.compile("Normal exit (\d+ jobs run).*")
postfixStatistics = re.compile("statistics:.*")
postfixHostnameDoesNotResolve = re.compile("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()
Run Code Online (Sandbox Code Playgroud)