-# Copyright (C) 2014 Ipsilon Project Contributors
-#
-# See the file named COPYING for the project license
+# Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
import cherrypy
import cStringIO
import inspect
import os
import traceback
+import logging
+
def log_request_response():
'''Log the contents of the request and subsequent response.
'''
- #--- Begin local functions ---
+ # --- Begin local functions ---
def indent_text(text, level=0, indent=' '):
'''
f.close()
return string
+ def print_param(name, value):
+ f = cStringIO.StringIO()
+
+ # Might be a multipart Part object, if so format it
+ if isinstance(value, cherrypy._cpreqbody.Part): # pylint:disable=W0212
+ f.write(indent_text("%s:\n" % (name)))
+ f.write(indent_text(print_part(value), 1))
+ else:
+ # Not a mulitpart, just write it as a string
+ f.write(indent_text("%s: %s\n" % (name, value)))
+
+ string = f.getvalue()
+ f.close()
+ return string
+
def collapse_body(body):
'''The cherrypy response body can be:
f.close()
return string
- #--- End local functions ---
+ # --- End local functions ---
f = cStringIO.StringIO()
request = cherrypy.serving.request
#
# Log the Request
#
- f.write(indent_text("<Request> [%s] %s\n" % \
- (remote.name or remote.ip, request.request_line), 0))
+ f.write(indent_text("<Request> [%s] %s\n" %
+ (remote.name or remote.ip, request.request_line), 0))
# Request Headers
if request.headers:
# Multi-valued paramater is in a list
if isinstance(value, list):
for i, item in enumerate(value):
- # Might be a multipart Part object, if so format it
- if isinstance(item, cherrypy._cpreqbody.Part):
- f.write(indent_text("%s[%s]:\n" % (name, i), 2))
- f.write(indent_text(print_part(item), 3))
- else:
- # Not a mulitpart, just write it as a string
- f.write(indent_text("%s[%s]: %s\n" % (name, i, item), 2))
+ f.write(indent_text(print_param("%s[%d]" % (name, i),
+ item), 2))
else:
- # Just a string value
- f.write(indent_text("%s: %s\n" % (name, value), 2))
+ f.write(indent_text(print_param(name, value), 2))
# If the body is multipart format each of the parts
if request.body.parts:
f.close()
print string
-cherrypy.tools.log_request_response = cherrypy.Tool('on_end_resource', log_request_response)
+cherrypy.tools.log_request_response = cherrypy.Tool('on_end_resource',
+ log_request_response)
class Log(object):
args, _, _, value_dict = inspect.getargvalues(frame_obj)
# Is the functions first parameter named 'self'?
if len(args) and args[0] == 'self':
- # in that case, 'self' will be referenced in value_dict
+ # in that case, 'self' will be referenced in value_dict
instance = value_dict.get('self', None)
if instance:
# return its class
@staticmethod
def call_location():
- frame = inspect.stack()[2]
- frame_obj = frame[0]
- filename = frame[1]
- line_number = frame[2]
- func = frame[3]
+ """Return string describing where in the code a logging call was made.
+
+ When reading a log file it is very helpful to know where in the
+ code the message was emitted from.
+
+ Returns a string of the form:
+ "filename:line_number class"
+
+ The filename is a path truncated to the last 3 pathname
+ components to give just enough context without being too verbose.
+
+ This function walks up the stack bypassing any functions that
+ appear to be part of the logging operation itself. The first
+ such frame not part of logging is assumed to be the location
+ of the code that emitted the log message.
+
+ :return: string describing code location of logging call
+ """
+
+ # Each frame is a 6-tuple:
+ #
+ # the frame object,
+ # the filename,
+ # the line number of the current line,
+ # the function name,
+ # a list of lines of context from the source code,
+ # the index of the current line within that list
+
+ # Walk up the stack until we find a function name which is not
+ # a logging function, that frame is the logging caller and hence
+ # the location where the call to logging was made.
+ try:
+ frame = None
+ frame_obj = None
+ stack = inspect.stack()
+ for frame in stack:
+ if frame[3] not in ('call_location', 'log', 'debug', 'error'):
+ break
+
+ if not frame:
+ frame = stack[0]
+
+ frame_obj = frame[0]
+ filename = frame[1]
+ line_number = frame[2]
+ func = frame[3]
+
+ # Only report the last 3 components of the path
+ filename = os.sep.join(filename.split(os.sep)[-3:])
+
+ cls = Log.get_class_from_frame(frame_obj)
+ finally:
+ # Don't keep reference to frame object
+ del frame_obj
+ del stack
- # Only report the last 3 components of the path
- filename = os.sep.join(filename.split(os.sep)[-3:])
-
- cls = Log.get_class_from_frame(frame_obj)
if cls:
location = '%s:%s %s.%s()' % \
(filename, line_number, cls.__name__, func)
location = '%s:%s %s()' % (filename, line_number, func)
return location
-
def debug(self, fact):
if cherrypy.config.get('debug', False):
location = Log.call_location()
cherrypy.log(fact)
def error(self, fact):
- cherrypy.log.error('ERROR: %s' % fact)
+ cherrypy.log.error('ERROR: %s' % fact, severity=logging.ERROR)
if cherrypy.config.get('stacktrace_on_error', False):
cherrypy.log.error(Log.stacktrace())