logparse / parsers / cron_journald.pyon commit rename parsers, better journald integration (e1f7605)
   1# -*- coding: utf-8 -*-
   2
   3"""
   4List the logged (executed) cron jobs and their commands (uses journald/logfile)
   5NOTE: If using journald, the log level for cron.service should be at least 2 
   6(default is 1). This can be changed with `sudo systemctl edit cron --full`, 
   7and ammend `-L 2` to the ExecStart command.
   8TODO: also output a list of scheduled (future) jobs
   9"""
  10
  11from systemd import journal
  12import datetime
  13
  14from logparse import config
  15from logparse.formatting import *
  16from logparse.load_parsers import Parser
  17from logparse.parsers.cron import CronCommand
  18
  19
  20class CronJournald(Parser):
  21
  22    def __init__(self):
  23        super().__init__()
  24        self.name = "cron_journald"
  25        self.info = "List the logged (executed) cron jobs and their commands " \
  26                "(uses journald module)"
  27
  28    def parse_log(self):
  29
  30        logger.debug("Starting cron section")
  31        section = Section("cron")
  32
  33        # Initiate journald reader
  34        j = journal.Reader()
  35        j.this_machine()
  36        j.log_level(journal.LOG_INFO)
  37        j.add_match(_COMM="cron")
  38        j.seek_realtime(section.period.startdate)
  39
  40        logger.info("Obtaining cron logs")
  41
  42        records = [entry for entry in j
  43                if "MESSAGE" in entry and " CMD " in entry["MESSAGE"]]
  44
  45        if len(records) == 0:
  46            logger.warning("Couldn't find any cron commands")
  47            return 1
  48
  49        logger.info("Found {0} log records".format(len(records)))
  50
  51        logger.debug("Analysing cron commands for each user")
  52        command_objects = []
  53        users = {}
  54        for record in records:
  55            if record["_SOURCE_REALTIME_TIMESTAMP"] < section.period.startdate:
  56                logger.warning("Discarding log record from {0} - was "
  57                        "seek_realtime set properly?".format(
  58                            record["_SOURCE_REALTIME_TIMESTAMP"]))
  59                continue
  60            try:
  61                cmd_obj = CronCommand(record)
  62                if config.prefs.getboolean("cron", "truncate-commands"):
  63                    cmd_obj.truncate() 
  64                if not (cmd_obj.match_user(config.prefs.get("cron", "users")
  65                    .split()) and cmd_obj.match_cmd(config.prefs.get(
  66                        "cron", "commands").split())):
  67                    logger.debug("Ignoring cron session by {0} with command "
  68                        "{1} due to config".format(cmd_obj.user, cmd_obj.cmd))
  69                    continue
  70                command_objects.append(cmd_obj)
  71                if not cmd_obj.user in users:
  72                    users[cmd_obj.user] = []
  73                users[cmd_obj.user].append(cmd_obj.cmd)
  74            except Exception as e:
  75                logger.warning("Malformed cron log message: {0}. "
  76                    "Error message: {1}".format(record["MESSAGE"], str(e)))
  77                continue
  78
  79        logger.info("Found {0} valid cron sessions".format(len(command_objects)))
  80
  81        if config.prefs.getboolean("cron", "summary"):
  82            summary_data = Data()
  83            summary_data.subtitle = "Total of " + plural("cron session",
  84                    len(command_objects)) + " for " + plural("user",
  85                            len(users))
  86            summary_data.items = ["{}: `{}`".format(c.user, c.cmd) 
  87                    for c in command_objects]
  88            summary_data.orderbyfreq()
  89            summary_data.truncl(config.prefs.getint("logparse", "maxcmd"))
  90            section.append_data(summary_data)
  91
  92        if config.prefs.getboolean("cron", "list-users"):
  93            for user, cmdlist in users.items():
  94                user_data = Data()
  95                user_data.subtitle = plural("session", len(cmdlist)) \
  96                        + " for " + user + (" (" + plural("unique command",
  97                            len(set(cmdlist))) + ")" if len(set(cmdlist)) > 1
  98                            else "")
  99                user_data.items = ["`{0}`".format(cmd) for cmd in cmdlist]
 100                user_data.orderbyfreq()
 101                user_data.truncl(config.prefs.getint("logparse", "maxcmd"))
 102                section.append_data(user_data)
 103                logger.debug("Found {0} cron sessions for user {1} "
 104                        "({2} unique commands): {3}".format(
 105                            len(cmdlist), user, 
 106                            len(set(cmdlist)), user_data.items))
 107
 108
 109        logger.info("Finished cron section")
 110
 111        return section