|
Pierre-Yves Chibon |
91b7a2 |
# -*- coding: utf-8 -*-
|
|
Pierre-Yves Chibon |
91b7a2 |
#
|
|
Pierre-Yves Chibon |
91b7a2 |
# Copyright Š 2014 Red Hat, Inc.
|
|
Pierre-Yves Chibon |
91b7a2 |
#
|
|
Pierre-Yves Chibon |
91b7a2 |
# This copyrighted material is made available to anyone wishing to use,
|
|
Pierre-Yves Chibon |
91b7a2 |
# modify, copy, or redistribute it subject to the terms and conditions
|
|
Pierre-Yves Chibon |
91b7a2 |
# of the GNU General Public License v.2, or (at your option) any later
|
|
Pierre-Yves Chibon |
91b7a2 |
# version. This program is distributed in the hope that it will be
|
|
Pierre-Yves Chibon |
91b7a2 |
# useful, but WITHOUT ANY WARRANTY expressed or implied, including the
|
|
Pierre-Yves Chibon |
91b7a2 |
# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
Pierre-Yves Chibon |
91b7a2 |
# PURPOSE. See the GNU General Public License for more details. You
|
|
Pierre-Yves Chibon |
91b7a2 |
# should have received a copy of the GNU General Public License along
|
|
Pierre-Yves Chibon |
91b7a2 |
# with this program; if not, write to the Free Software Foundation,
|
|
Pierre-Yves Chibon |
91b7a2 |
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Pierre-Yves Chibon |
91b7a2 |
#
|
|
Pierre-Yves Chibon |
91b7a2 |
# Any Red Hat trademarks that are incorporated in the source
|
|
Pierre-Yves Chibon |
91b7a2 |
# code or documentation are not subject to the GNU General Public
|
|
Pierre-Yves Chibon |
91b7a2 |
# License and may only be used or replicated with the express permission
|
|
Pierre-Yves Chibon |
91b7a2 |
# of Red Hat, Inc.
|
|
Pierre-Yves Chibon |
91b7a2 |
#
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
'''
|
|
Pierre-Yves Chibon |
91b7a2 |
Mail handler for logging.
|
|
Pierre-Yves Chibon |
91b7a2 |
'''
|
|
Pierre-Yves Chibon |
91b7a2 |
import logging
|
|
Pierre-Yves Chibon |
91b7a2 |
import logging.handlers
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
import inspect
|
|
Pierre-Yves Chibon |
91b7a2 |
import os
|
|
Pierre-Yves Chibon |
91b7a2 |
import socket
|
|
Pierre-Yves Chibon |
91b7a2 |
import traceback
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
psutil = None
|
|
Pierre-Yves Chibon |
91b7a2 |
try:
|
|
Pierre-Yves Chibon |
91b7a2 |
import psutil
|
|
Pierre-Yves Chibon |
91b7a2 |
except (OSError, ImportError):
|
|
Pierre-Yves Chibon |
91b7a2 |
# We run into issues when trying to import psutil from inside mod_wsgi on
|
|
Pierre-Yves Chibon |
91b7a2 |
# rhel7. If we hit that here, then just fail quietly.
|
|
Pierre-Yves Chibon |
91b7a2 |
# https://github.com/jmflinuxtx/kerneltest-harness/pull/17#issuecomment-48007837
|
|
Pierre-Yves Chibon |
91b7a2 |
pass
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
class ContextInjector(logging.Filter):
|
|
Pierre-Yves Chibon |
91b7a2 |
""" Logging filter that adds context to log records.
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
Filters are typically used to "filter" log records. They declare a filter
|
|
Pierre-Yves Chibon |
91b7a2 |
method that can return True or False. Only records with 'True' will
|
|
Pierre-Yves Chibon |
91b7a2 |
actually be logged.
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
Here, we somewhat abuse the concept of a filter. We always return true,
|
|
Pierre-Yves Chibon |
91b7a2 |
but we use the opportunity to hang important contextual information on the
|
|
Pierre-Yves Chibon |
91b7a2 |
log record to later be used by the logging Formatter. We don't normally
|
|
Pierre-Yves Chibon |
91b7a2 |
want to see all this stuff in normal log records, but we *do* want to see
|
|
Pierre-Yves Chibon |
91b7a2 |
it when we are emailed error messages. Seeing an error, but not knowing
|
|
Pierre-Yves Chibon |
91b7a2 |
which host it comes from, is not that useful.
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
http://docs.python.org/2/howto/logging-cookbook.html#filters-contextual
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
This code has been originally written by Ralph Bean for the fedmsg
|
|
Pierre-Yves Chibon |
91b7a2 |
project:
|
|
Pierre-Yves Chibon |
91b7a2 |
https://github.com/fedora-infra/fedmsg/
|
|
Pierre-Yves Chibon |
91b7a2 |
and can be found at:
|
|
Pierre-Yves Chibon |
91b7a2 |
https://infrastructure.fedoraproject.org/cgit/ansible.git/tree/roles/fedmsg/base/templates/logging.py.j2
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
"""
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
def filter(self, record):
|
|
Pierre-Yves Chibon |
91b7a2 |
current_process = ContextInjector.get_current_process()
|
|
Pierre-Yves Chibon |
91b7a2 |
current_hostname = socket.gethostname()
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
record.host = current_hostname
|
|
Pierre-Yves Chibon |
91b7a2 |
record.proc = current_process
|
|
Pierre-Yves Chibon |
91b7a2 |
record.pid = '-'
|
|
Pierre-Yves Chibon |
91b7a2 |
if not isinstance(current_process, str):
|
|
Pierre-Yves Chibon |
91b7a2 |
record.pid = current_process.pid
|
|
Pierre-Yves Chibon |
c0483c |
record.proc_name = current_process.name
|
|
Pierre-Yves Chibon |
c0483c |
record.command_line = " ".join(current_process.cmdline)
|
|
Pierre-Yves Chibon |
91b7a2 |
record.callstack = self.format_callstack()
|
|
Pierre-Yves Chibon |
91b7a2 |
return True
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
@staticmethod
|
|
Pierre-Yves Chibon |
91b7a2 |
def format_callstack():
|
|
Pierre-Yves Chibon |
91b7a2 |
for i, frame in enumerate(f[0] for f in inspect.stack()):
|
|
Pierre-Yves Chibon |
91b7a2 |
if '__name__' not in frame.f_globals:
|
|
Pierre-Yves Chibon |
91b7a2 |
continue
|
|
Pierre-Yves Chibon |
91b7a2 |
modname = frame.f_globals['__name__'].split('.')[0]
|
|
Pierre-Yves Chibon |
91b7a2 |
if modname != "logging":
|
|
Pierre-Yves Chibon |
91b7a2 |
break
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
def _format_frame(frame):
|
|
Pierre-Yves Chibon |
91b7a2 |
return ' File "%s", line %i in %s\n %s' % (frame)
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
stack = traceback.extract_stack()
|
|
Pierre-Yves Chibon |
91b7a2 |
stack = stack[:-i]
|
|
Pierre-Yves Chibon |
91b7a2 |
return "\n".join([_format_frame(frame) for frame in stack])
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
@staticmethod
|
|
Pierre-Yves Chibon |
91b7a2 |
def get_current_process():
|
|
Pierre-Yves Chibon |
91b7a2 |
mypid = os.getpid()
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
if not psutil:
|
|
Pierre-Yves Chibon |
91b7a2 |
return "Could not import psutil for %r" % mypid
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
for proc in psutil.process_iter():
|
|
Pierre-Yves Chibon |
91b7a2 |
if proc.pid == mypid:
|
|
Pierre-Yves Chibon |
91b7a2 |
return proc
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
# This should be impossible.
|
|
Pierre-Yves Chibon |
91b7a2 |
raise ValueError("Could not find process %r" % mypid)
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
MSG_FORMAT = """Process Details
|
|
Pierre-Yves Chibon |
91b7a2 |
---------------
|
|
Pierre-Yves Chibon |
91b7a2 |
host: %(host)s
|
|
Pierre-Yves Chibon |
91b7a2 |
PID: %(pid)s
|
|
Pierre-Yves Chibon |
91b7a2 |
name: %(proc_name)s
|
|
Pierre-Yves Chibon |
91b7a2 |
command: %(command_line)s
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
Message type: %(levelname)s
|
|
Pierre-Yves Chibon |
91b7a2 |
Location: %(pathname)s:%(lineno)d
|
|
Pierre-Yves Chibon |
91b7a2 |
Module: %(module)s
|
|
Pierre-Yves Chibon |
91b7a2 |
Function: %(funcName)s
|
|
Pierre-Yves Chibon |
91b7a2 |
Time: %(asctime)s
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
Message:
|
|
Pierre-Yves Chibon |
91b7a2 |
--------
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
%(message)s
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
Callstack that lead to the logging statement
|
|
Pierre-Yves Chibon |
91b7a2 |
--------------------------------------------
|
|
Pierre-Yves Chibon |
91b7a2 |
%(callstack)s
|
|
Pierre-Yves Chibon |
91b7a2 |
"""
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
|
|
Pierre-Yves Chibon |
91b7a2 |
def get_mail_handler(smtp_server, mail_admin):
|
|
Pierre-Yves Chibon |
91b7a2 |
""" Set up the handler sending emails for big exception
|
|
Pierre-Yves Chibon |
91b7a2 |
"""
|
|
Pierre-Yves Chibon |
91b7a2 |
mail_handler = logging.handlers.SMTPHandler(
|
|
Pierre-Yves Chibon |
91b7a2 |
smtp_server,
|
|
Pierre-Yves Chibon |
91b7a2 |
'nobody@fedoraproject.org',
|
|
Pierre-Yves Chibon |
91b7a2 |
mail_admin,
|
|
Pierre-Yves Chibon |
91b7a2 |
'Progit error')
|
|
Pierre-Yves Chibon |
91b7a2 |
mail_handler.setFormatter(logging.Formatter(MSG_FORMAT))
|
|
Pierre-Yves Chibon |
91b7a2 |
mail_handler.setLevel(logging.ERROR)
|
|
Pierre-Yves Chibon |
91b7a2 |
mail_handler.addFilter(ContextInjector())
|
|
Pierre-Yves Chibon |
91b7a2 |
return mail_handler
|