wcs/wcs/qommon/logger.py

145 lines
4.8 KiB
Python

# w.c.s. - web application for online forms
# Copyright (C) 2005-2010 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import logging
import os
from django.utils import six
from quixote import get_publisher, get_session, get_request
from quixote.logger import DefaultLogger
class ApplicationLogger(DefaultLogger):
def log_internal_error(self, error_summary, error_msg, tech_id=None):
self.log('exception caught')
self.error_log.write(error_msg)
if self.error_email:
from .emails import email
headers = {}
if tech_id:
headers['References'] = '<%s@%s>' % (tech_id, os.path.basename(get_publisher().app_dir))
email(subject='[ERROR] %s' % error_summary,
mail_body=error_msg,
email_from=self.error_email,
email_rcpt=[self.error_email],
want_html=False,
fire_and_forget=True,
extra_headers=headers,
ignore_mail_redirection=True)
class BotFilter(logging.Filter):
def filter(self, record):
if record.levelno >= logging.ERROR:
# always log errors
return 1
if self.is_bot():
return 0
return 1
@classmethod
def is_bot(cls):
botfile = os.path.join(get_publisher().data_dir, 'webbots')
if os.path.exists(botfile) and get_request():
user_agent = get_request().get_environ('HTTP_USER_AGENT', '')
with open(botfile) as fd:
for bot_ua_string in [x.strip() for x in fd.readlines()]:
if bot_ua_string in user_agent:
return True
return False
class Formatter(logging.Formatter):
def format(self, record):
request = get_request()
if request:
record.address = request.get_environ('REMOTE_ADDR', '-')
record.path = request.get_path()
user = request.user
if user:
if isinstance(user, six.string_types + six.integer_types):
user_id = user
else:
user_id = user.id
if type(user_id) is str and user_id.startswith('anonymous-'):
# legacy; kept for ancient log entries
user_id = 'anonymous'
else:
user_id = 'unlogged'
if BotFilter.is_bot():
user_id = 'bot'
record.user_id = user_id
else:
record.address = '-'
record.path = '-'
record.user_id = 'unlogged'
record.session_id = (get_request() and get_session() and \
get_session().get_session_id()) or '[nosession]'
return logging.Formatter.format(self, record) \
.replace('\n', '\n ')
def parse_logstream(stream):
'''
Parse a stream of lines making a log file, each log line start with a
non-blank character continue until the lines does not start with a blank
character
'''
line = next(stream)
while True:
r = readline(line)
try:
# Skip badly formatted lines
if r is None:
line = next(stream)
continue
while True:
line = next(stream)
if line.startswith(' '):
# Append the line without the first blank
r['message'] = r['message'] + line[1:]
continue
break
except StopIteration:
# Ont last line return the current one
if r is not None:
r['message'] = r['message'].strip()
yield r
break
r['message'] = r['message'].strip()
yield r
def readline(line):
if not line:
return None
try:
date, hour, level, ip, session_id, url, user_id, dash, message = line.split(' ', 8)
except ValueError:
try:
date, hour, level, message = line.split(' ', 3)
except ValueError:
return None # misformatted line
del line
return locals()
if dash != '-':
return None
del line
return locals()