import logparse
from .config import *
from logparse import formatting, mail, config
-from .parsers import load_parsers, sudo, sshd, cron, httpd, smbd, postfix, zfs, temperature
+from .parsers import load_parsers
def rotate():
# rotate logs using systemd logrotate
- if parser.parse_args().function is None:
+ if argparser.parse_args().function is None:
if (prefs['rotate'] == 'y'):
subprocess.call("/usr/sbin/logrotate -f /etc/logrotate.conf", shell=True)
logger.info("rotated logfiles")
def main():
# Get arguments
- parser = argparse.ArgumentParser(description='grab logs of some common services and send them by email')
- parser.add_argument('-t','--to', help='mail recipient (\"to\" address)', required=False)
- parser.add_argument('-c', '--config', help='path to config file', required=False)
- parser.add_argument('-p', '--print', help='print HTML to stdout', required=False, dest='printout', action='store_true', default=False)
- parser.add_argument('-d', '--destination', help='file to output HTML', required=False)
- parser.add_argument('-f', '--overwrite', help='force overwrite an existing output file', required=False, action='store_true', default=False)
- parser.add_argument('-v', '--verbose', help='verbose console/syslog output (for debugging)', required=False, default=False, action='store_true')
- parser.add_argument('-r', '--rotate', help='force rotate log files using systemd logrotate', required=False, default=False, action='store_true')
- parser.add_argument('-nr', '--no-rotate', help='do not rotate logfiles (overrides logparse.conf)', required=False, default=False, action='store_true')
- parser.add_argument('-l', '--logs', help='services to analyse', required=False)
+ argparser = argparse.ArgumentParser(description='grab logs of some common services and send them by email')
+ argparser.add_argument('-t','--to', help='mail recipient (\"to\" address)', required=False)
+ argparser.add_argument('-c', '--config', help='path to config file', required=False)
+ argparser.add_argument('-p', '--print', help='print HTML to stdout', required=False, dest='printout', action='store_true', default=False)
+ argparser.add_argument('-d', '--destination', help='file to output HTML', required=False)
+ argparser.add_argument('-f', '--overwrite', help='force overwrite an existing output file', required=False, action='store_true', default=False)
+ argparser.add_argument('-v', '--verbose', help='verbose console/syslog output (for debugging)', required=False, default=False, action='store_true')
+ argparser.add_argument('-r', '--rotate', help='force rotate log files using systemd logrotate', required=False, default=False, action='store_true')
+ argparser.add_argument('-nr', '--no-rotate', help='do not rotate logfiles (overrides logparse.conf)', required=False, default=False, action='store_true')
+ argparser.add_argument('-l', '--logs', help='services to analyse', required=False)
+ argparser.add_argument('-nl', '--ignore-logs', help='skip these services (takes precedence over -l)', required=False)
+ argparser.add_argument('-es', '--embed-styles', help='make CSS rules inline rather than linking the file', required=False, default=False, action='store_true')
# Load config
- if parser.parse_args().config:
- config.prefs = config.loadconf(parser.parse_args().config, parser)
+ if argparser.parse_args().config:
+ config.prefs = config.loadconf(argparser.parse_args().config, argparser)
else:
- config.prefs = config.loadconf(argparser=parser)
+ config.prefs = config.loadconf(argparser=argparser)
prefs = config.prefs
# Set up logging
loghandler = logging.handlers.SysLogHandler(address = '/dev/log')
loghandler.setFormatter(logging.Formatter(fmt='logparse.py[' + str(os.getpid()) + ']: %(message)s'))
loghandler.setLevel(logging.WARNING) # don't spam syslog with debug messages
- if parser.parse_args().verbose or (config.prefs['verbose'] == 'y' or config.prefs['verbose'] == 'yes'):
- print("Verbose mode is on")
+ if argparser.parse_args().verbose or (config.prefs['verbose'] == 'y' or config.prefs['verbose'] == 'yes'):
logging.basicConfig(level=logging.DEBUG)
logger.debug("Verbose mode turned on")
else:
logger.info("Beginning log analysis at {0} {1}".format(start.strftime(formatting.DATEFMT), start.strftime(formatting.TIMEFMT)))
logger.debug("This is {0} version {1}, running on Python {2}".format(logparse.__name__, logparse.__version__, sys.version.replace('\n', '')))
-# for l in parser.parse_args().logs.split(' '):
-# eval(l)
-# sys.exit()
+ # Write HTML header
- print(load_parsers.search());
- # Write HTML document
global output_html
output_html = formatting.header(prefs['header'])
- output_html += sudo.parse_log()
- output_html += sshd.parse_log()
- output_html += cron.parse_log()
- output_html += httpd.parse_log()
- output_html += smbd.parse_log()
- output_html += postfix.parse_log()
- output_html += zfs.parse_log()
- output_html += temperature.parse_log()
- output_html += formatting.closetag('body') + formatting.closetag('html')
- if parser.parse_args().printout:
+ output_html += formatting.opentag('div', id='main')
+
+ # Find parsers
+
+ parser_providers = []
+ if argparser.parse_args().logs:
+ log_src = argparser.parse_args().logs.split()
+ elif len(prefs['parsers']) > 0:
+ log_src = prefs['parsers']
+ else:
+ log_src = load_parsers.default_parsers
+
+ for parser_name in log_src:
+ parser = load_parsers.search(parser_name)
+ if parser == None:
+ logger.warning("Can't find parser {0}".format(parser_name))
+ continue
+ else:
+ parser_providers.append(load_parsers.load(parser))
+
+ if argparser.parse_args().ignore_logs or len(prefs['ignore-parsers']) > 0:
+ if argparser.parse_args().ignore_logs:
+ ignore_src = argparser.parse_args().ignore_logs.split()
+ else:
+ ignore_src = prefs['ignore-parsers']
+ for parser_name in ignore_src:
+ if parser_name in [x.__name__.rpartition('.')[2] for x in parser_providers]:
+ logger.info("Ignoring default parser {0}".format(parser_name))
+ parser_providers_new = []
+ for p in parser_providers:
+ if p.__name__.rpartition('.')[2] != parser_name:
+ parser_providers_new.append(p)
+ parser_providers = parser_providers_new
+ continue
+
+ # Execute parsers
+
+ logger.debug(str(parser_providers))
+ for parser in parser_providers:
+ output_html += parser.parse_log()
+
+ # Write HTML footer
+
+ output_html += formatting.closetag('div') + formatting.closetag('body') + formatting.closetag('html')
+
+ if argparser.parse_args().printout:
print(output_html)
- if parser.parse_args().destination:
- logger.debug("Outputting to {0}".format(parser.parse_args().destination))
- if not os.path.isfile(parser.parse_args().destination) and not parser.parse_args().overwrite:
- with open(parser.parse_args().destination, 'w') as f:
+ if argparser.parse_args().destination or prefs['output']:
+ if argparser.parse_args().destination:
+ dest_path = argparser.parse_args().destination
+ else:
+ dest_path = prefs['output']
+ logger.debug("Outputting to {0}".format(dest_path))
+ if argparser.parse_args().embed_styles or prefs['embed-styles']:
+ output_html = mail.mailprep(output_html, prefs['css'])
+ if not os.path.isfile(dest_path) and not (argparser.parse_args().overwrite or config['overwrite']):
+ with open(dest_path, 'w') as f:
f.write(output_html)
- logger.info("Written output to {}".format(parser.parse_args().destination))
+ logger.info("Written output to {}".format(dest_path))
else:
logger.warning("Destination file already exists")
- if input("Would you like to overwrite {0}? (y/n) [n] ".format(parser.parse_args().destination)) == 'y':
- with open(parser.parse_args().destination, 'w') as f:
+ if input("Would you like to overwrite {0}? (y/n) [n] ".format(dest_path)) == 'y':
+ with open(dest_path, 'w') as f:
f.write(output_html)
- logger.debug("Written output to {}".format(parser.parse_args().destination))
+ logger.debug("Written output to {}".format(dest_path))
else:
logger.warning("No output written")
- if parser.parse_args().to:
- mail.sendmail(mailbin=prefs['mail']['mailbin'], body=output_html, recipient=parser.parse_args().to, subject="logparse test")
+ if argparser.parse_args().to:
+ mail.sendmail(mailbin=prefs['mail']['mailbin'], body=mail.mailprep(output_html, prefs['css']), recipient=argparser.parse_args().to, subject=formatting.fsubject(config.prefs['mail']['subject']))
# Print end message
finish = datetime.now()
#
import imp
+import importlib
import os
import glob
+from pathlib import Path
+from sys import path
+from typing import NamedTuple
parser_dir = "/usr/share/logparse/"
-main_module = "__init__.py"
+main_module = "__init__"
+default_parsers = ["cron", "httpd", "postfix", "smbd", "sshd", "sudo", "temperature", "zfs"]
import logging
logger = logging.getLogger(__name__)
-def search():
+class Parser():
+ def __init__(self, name, path=None, info=None):
+ self.name = str(name)
+ self.path = Path(path) if path else None
+ self.info = dict(info) if info else None
+
+def findall():
logger.debug("Searching for parsers in {0}".format(parser_dir))
+ path.append(os.path.abspath(parser_dir))
parsers = []
- parser_candidates = glob.glob(os.path.join(os.path.dirname(parser_dir), "*"))
- logger.debug("Found parser candidates {0}".format(str(parser_candidates)))
- for p in parser_candidates:
- location = os.path.join(parser_dir, p)
- if not os.path.isdir(parser_dir) or not main_module + '.py' in os.listdir(location):
+ parser_candidates = os.listdir(parser_dir)
+ for parser_name in parser_candidates:
+ location = os.path.join(parser_dir, parser_name)
+ if not os.path.isdir(location) or not main_module + '.py' in os.listdir(location):
+ logger.warning("Rejecting parser {0} due to invalid structure".format(location))
continue
info = imp.find_module(main_module, [location])
- parsers.append({"name": p, "info": info})
+ parser_obj = Parser(parser_name, location, info)
+ parsers.append(parser_obj)
+ logger.debug("Added parser {0}".format(parser_obj.name))
return parsers
-def load(parser):
- logger.debug("Loading {0}".format(parser["name"]))
- return imp.load_module(parser["name"], *parser["info"])
+def search(name):
+ logger.debug("Searching for parser {0}".format(name))
+ if name in default_parsers:
+ logger.debug("Found parser {0} in default modules".format(name))
+ return Parser('.'.join(__name__.split('.')[:-1] + [name]))
+ else:
+ return None
+def load(parser):
+ logger.debug("Loading parser {0} from {1}".format(parser.name, parser.path if parser.path != None else "defaults"))
+ return importlib.import_module(parser.name)