ansible/roles/anitya/fedmsg/templates/logging.py.j2

162 lines
5.1 KiB
Django/Jinja

# Setup fedmsg logging.
# All of these modules are just used by the ContextInjector below.
import inspect
import logging
import os
import socket
import traceback
psutil = None
try:
import psutil
except (OSError, ImportError):
# We run into issues when trying to import psutil from inside mod_wsgi on
# rhel7. If we hit that here, then just fail quietly.
# https://github.com/jmflinuxtx/kerneltest-harness/pull/17#issuecomment-48007837
pass
class ContextInjector(logging.Filter):
""" Logging filter that adds context to log records.
Filters are typically used to "filter" log records. They declare a filter
method that can return True or False. Only records with 'True' will
actually be logged.
Here, we somewhat abuse the concept of a filter. We always return true,
but we use the opportunity to hang important contextual information on the
log record to later be used by the logging Formatter. We don't normally
want to see all this stuff in normal log records, but we *do* want to see
it when we are emailed error messages. Seeing an error, but not knowing
which host it comes from, is not that useful.
http://docs.python.org/2/howto/logging-cookbook.html#filters-contextual
"""
def filter(self, record):
current_process = ContextInjector.get_current_process()
current_hostname = socket.gethostname()
record.host = current_hostname
record.proc = current_process
record.pid = current_process.pid
record.proc_name = current_process.name
record.command_line = current_process.cmdline
# These are callabls on more modern versions of psutil.
if callable(record.proc_name):
record.proc_name = record.proc_name()
if callable(record.command_line):
record.command_line = record.command_line()
record.command_line = " ".join(record.command_line)
record.callstack = self.format_callstack()
return True
@staticmethod
def format_callstack():
for i, frame in enumerate(f[0] for f in inspect.stack()):
if not '__name__' in frame.f_globals:
continue
modname = frame.f_globals['__name__'].split('.')[0]
if modname != "logging":
break
def _format_frame(frame):
return ' File "%s", line %i in %s\n %s' % (frame)
stack = traceback.extract_stack()
stack = stack[:-i]
return "\n".join([_format_frame(frame) for frame in stack])
@staticmethod
def get_current_process():
mypid = os.getpid()
if not psutil:
raise OSError("Could not import psutil for %r" % mypid)
for proc in psutil.process_iter():
if proc.pid == mypid:
return proc
# This should be impossible.
raise ValueError("Could not find process %r" % mypid)
@classmethod
def __json__(cls):
""" We need to be jsonifiable for "fedmsg-config" """
return {'name': 'ContextInjector'}
hefty_format = """Message
-------
[%(asctime)s][%(name)10s %(levelname)7s]
%(message)s
Process Details
---------------
host: %(host)s
PID: %(pid)s
name: %(proc_name)s
command: %(command_line)s
Callstack that lead to the logging statement
--------------------------------------------
%(callstack)s
"""
# See the following for constraints on this format http://bit.ly/Xn1WDn
config = dict(
logging=dict(
version=1,
formatters=dict(
bare={
"datefmt": "%Y-%m-%d %H:%M:%S",
"format": "[%(asctime)s][%(name)10s %(levelname)7s] %(message)s"
},
hefty={
"datefmt": "%Y-%m-%d %H:%M:%S",
"format": hefty_format,
},
),
filters=dict(
context={
# This "()" syntax in the stdlib doesn't seem to be documented
# anywhere. I had to read
# /usr/lib64/python2.7/logging/config.py to figure it out.
"()": ContextInjector,
},
),
handlers=dict(
console={
"class": "logging.StreamHandler",
"formatter": "bare",
"level": "INFO",
"stream": "ext://sys.stdout",
},
mailer={
"class": "logging.handlers.SMTPHandler",
"formatter": "hefty",
"filters": ["context"],
"level": "ERROR",
"mailhost": "bastion.vpn.fedoraproject.org",
"fromaddr": "fedmsg@fedoraproject.org",
"toaddrs": ["sysadmin-datanommer-members@fedoraproject.org"],
"subject": "fedmsg error log (anitya)",
},
),
loggers=dict(
fedmsg={
"level": "INFO",
"propagate": False,
"handlers": ["console", "mailer"],
},
moksha={
"level": "INFO",
"propagate": False,
"handlers": ["console", "mailer"],
},
),
),
)