pyledriver/sharedLogging.py

123 lines
3.6 KiB
Python

'''
Sets up root logger for whole program, including console, gluster, and gmail
Logger conventions
- CRITICAL: for things that cause crashes. only level with gmail
- ERROR: for things that cause startup/shutdown issues
- WARNING: for recoverable issues that may cause future problems
- INFO: state changes and sensor readings
- DEBUG: all extraneous crap
'''
import logging, os
from subprocess import run, PIPE, CalledProcessError
from logging.handlers import TimedRotatingFileHandler, SMTPHandler
from auxilary import mkdirSafe
def _formatConsole(gluster = False):
'''
formats console output depending on whether we have gluster
'''
c = '' if gluster else '[CONSOLE ONLY] '
fmt = logging.Formatter('[%(name)s] [%(levelname)s] ' + c + '%(message)s')
console.setFormatter(fmt)
class GlusterFSHandler(TimedRotatingFileHandler):
'''
Logic to mount timed rotating file within a gluster volume. Note that this
class will mount itself automatically. Note that the actual filepaths for
logging are hardcoded here
'''
def __init__(self, server, volume, mountpoint, options=None):
if not os.path.exists(mountpoint):
raise FileNotFoundError
self.mountpoint = mountpoint
self._server = server
self._volume = volume
self._options = options
self._mount()
logdest = mountpoint + '/logs'
mkdirSafe(logdest, logger)
super().__init__(logdest + '/pyledriver-log', when='midnight')
fmt = logging.Formatter('[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s')
self.setFormatter(fmt)
def _mount(self):
if os.path.ismount(self.mountpoint):
# this assumes that the already-mounted device is the one intended
logger.warning('Device already mounted at {}'.format(self.mountpoint))
else:
dst = self._server + ':/' + self._volume
cmd = ['mount', '-t', 'glusterfs', dst, self.mountpoint]
if self._options:
cmd[1:1] = ['-o', self._options]
self._run(cmd)
self.isMounted = True
def _unmount(self):
self._run(['umount', self.mountpoint])
self.isMounted = False
def _run(self, cmd):
try:
run(cmd, check=True, stdout=PIPE, stderr=PIPE)
except CalledProcessError as e:
stderr = e.stderr.decode('ascii').rstrip()
logger.error(stderr)
raise SystemExit
def close(self):
'''
Close file and dismount (must be in this order). Called when
'removeHandler' is invoked
'''
TimedRotatingFileHandler.close(self)
self._unmount()
'''
Init sequence (order is very essential)
'''
# 1) init console output (this will go to journald) and format as console only
console = logging.StreamHandler()
_formatConsole(gluster = False)
rootLogger = logging.getLogger()
rootLogger.setLevel(logging.DEBUG)
rootLogger.addHandler(console)
# 2) init the module level logger so we can log anything that happens as we build the other loggers
logger = logging.getLogger(__name__)
# 3) mount glusterfs, any errors here will go to console output
gluster = GlusterFSHandler(
server = '192.168.11.39',
volume = 'pyledriver',
mountpoint = '/mnt/glusterfs/pyledriver',
options = 'backupvolfile-server=192.168.11.48'
)
# 4) once gluster is mounted, add to root logger and remove "console only" warning from console
rootLogger.addHandler(gluster)
_formatConsole(gluster = True)
# 5) import gmail, this must come here as it uses loggers for some of its setup
from gmail import gmail, GmailHandler
# 6) init gmail handler
gmail = GmailHandler(gmail['username'], gmail['passwd'], gmail['recipientList'],
'harrison4hegemon - critical error')
gmail.setLevel(logging.CRITICAL)
rootLogger.addHandler(gmail)
'''
Clean up
'''
def unmountGluster():
rootLogger.removeHandler(gluster)
_formatConsole(gluster = False)