mirror of
https://github.com/Wojtek242/pylg.git
synced 2024-11-23 08:05:25 +01:00
Release 1.2.0
This commit is contained in:
parent
142017c57f
commit
c29553985c
29
CHANGES.rst
Normal file
29
CHANGES.rst
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Changelog
|
||||||
|
=========
|
||||||
|
|
||||||
|
1.2.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
- Fixed bug that didn't preserve exception backtrace correctly.
|
||||||
|
|
||||||
|
- Function ``trace`` now automatically converts a message to a string.
|
||||||
|
|
||||||
|
- Added several new options that can now be set in
|
||||||
|
``pylg_settings.py``, see the README for details.
|
||||||
|
|
||||||
|
- Improved dummy implementation for the case when PyLg is disabled.
|
||||||
|
|
||||||
|
- User settings provided in ``pylg_settings.py`` are now checked for
|
||||||
|
errors. In the case of a failed import, PyLg will set all settings
|
||||||
|
to defaults. In case of an invalid individual value, only the
|
||||||
|
relevant setting will be reset to its default.
|
||||||
|
|
||||||
|
1.1.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
- PyLg can now be installed with pip.
|
||||||
|
|
||||||
|
1.0.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
- Initial PyLg version.
|
@ -1,2 +1,3 @@
|
|||||||
recursive-include pylg *.py
|
recursive-include pylg *.py
|
||||||
include LICENSE.txt
|
include LICENSE.txt
|
||||||
|
include CHANGES.rst
|
||||||
|
117
README.rst
117
README.rst
@ -55,8 +55,9 @@ To automatically log function entry and exit use the
|
|||||||
Despite the name, this works for both functions and methods.
|
Despite the name, this works for both functions and methods.
|
||||||
|
|
||||||
``@TraceFunction`` can take up to two optional arguments:
|
``@TraceFunction`` can take up to two optional arguments:
|
||||||
- trace_args - if ``True``, input parameters will be logged.
|
|
||||||
- trace_rv - if ``True``, the return value will be logged.
|
- ``trace_args`` - if ``True``, input parameters will be logged.
|
||||||
|
- ``trace_rv`` - if ``True``, the return value will be logged.
|
||||||
|
|
||||||
The default values for these arguments are set in a global settings
|
The default values for these arguments are set in a global settings
|
||||||
file.
|
file.
|
||||||
@ -73,7 +74,7 @@ These arguments have to specified explicitly by name. Some examples:
|
|||||||
def some_fuction():
|
def some_fuction():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@TraceFunction(trace_args = False, trace_args = False)
|
@TraceFunction(trace_args = False, trace_rv = False)
|
||||||
def some_fuction():
|
def some_fuction():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -88,38 +89,90 @@ User Settings
|
|||||||
-------------
|
-------------
|
||||||
|
|
||||||
The user can adjust several settings to suit their preferences. To do
|
The user can adjust several settings to suit their preferences. To do
|
||||||
so, create a file named ``pylg_settings.py`` in the top-level
|
so, create a file named ``pylg_settings.py`` somewhere in your path
|
||||||
directory and set any of the following variables to the desired values
|
and set any of the following variables to the desired values in order
|
||||||
in order to override the defaults. The settings.py file in the project
|
to override the defaults. The settings.py file in the project
|
||||||
directory contains all the default settings and can be used as a
|
directory contains all the default settings and can be used as a
|
||||||
template.
|
template.
|
||||||
|
|
||||||
- PYLG_ENABLE (default = True) - enable/disable logs.
|
- ``PYLG_ENABLE`` (default = ``True``) - enable/disable PyLg.
|
||||||
- PYLG (default = 'pylg.log') - the log file name.
|
|
||||||
- CLASS_NAME_RESOLUTION (default = False) - PyLg can also log the
|
- ``PYLG_FILE`` (default = ``'pylg.log'``) - the log file name.
|
||||||
class name along with the method name if one exists. However, for
|
|
||||||
this to work correctly the ``trace`` function cannot be called from
|
- ``EXCEPTION_WARNING`` (default = ``True``) - if ``True``, PyLg will
|
||||||
functions that are not decorated by ``@TraceFunction`` which is why
|
print a warning about every exception caught to stderr.
|
||||||
it is disabled by default.
|
|
||||||
- DEFAULT_TRACE_ARGS (default = True) - the default value for
|
- ``EXCEPTION_EXIT`` (default = ``False``) - if ``True``, PyLg will
|
||||||
``trace_args`` argument which can be passed to the ``@TraceFunction`
|
force the program to exit (and not just raise ``SystemExit``)
|
||||||
decorator. If ``trace_args`` is ``True`` all parameters passed to
|
whenever an exception occurs. This will happen even if the exception
|
||||||
the function will be logged. This can be overriden on an individual
|
would be handled at a later point.
|
||||||
function basis.
|
|
||||||
- DEFAULT_TRACE_RV (default = True) - the default value for trace_rv
|
- ``TRACE_TIME`` (default = ``TRUE``) - enable/disable time logging.
|
||||||
argument which can be passed to the ``@TraceFunction`` decorator. If
|
|
||||||
``trace_rv`` is ``True`` the function's return value will be
|
- ``TIME_FORMAT`` (default = ``"%Y-%m-%d %H:%M:%S.%f"``) - formatting
|
||||||
logged. This can be overriden on an individual function basis.
|
for the time trace. For a full list of options, see
|
||||||
- EXCEPTION_WARNING (default = True) - PyLg catches all exceptions in
|
https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior.
|
||||||
traced functions, logs them, and then re-raises them with the full
|
|
||||||
backtrace. This setting determines whether it should also produce a
|
- ``TRACE_FILENAME`` (default = ``True``) - enable/disable file name
|
||||||
warning for the user using the Python warning mechanism.
|
logging.
|
||||||
- FILENAME_COLUMN_WIDTH (default = 32) - the column width reserved for
|
|
||||||
the file name. Names that are too short will be padded with
|
- ``FILENAME_COLUMN_WIDTH`` (default = ``20``) - the column width for
|
||||||
whitespace and names that are too long will be truncated.
|
the file name. If a name is too long, it will be truncated.
|
||||||
- FUNCTION_COLUMN_WIDTH (default = 32) - the column width reserved for
|
|
||||||
the function name. Names that are too short will be padded with
|
- ``TRACE_LINENO`` (default = ``True``) - enable/disable the logging
|
||||||
whitespace and names that are too long will be truncated.
|
of the line number from which the trace call was made. For entry and
|
||||||
|
exit messages this logs the line in which the decorator is placed
|
||||||
|
(which should be directly above the function itself).
|
||||||
|
|
||||||
|
- ``LINENO_WIDTH`` (default = ``4``) - the minimum number of digits to
|
||||||
|
use to print the line number. If the number is too long, more digits
|
||||||
|
will be used.
|
||||||
|
|
||||||
|
- ``TRACE_FUNCTION`` (default = ``True``) - enable/disable the logging
|
||||||
|
of the function name from which the trace call was made. Entry/exit
|
||||||
|
logs refer to the function they enter into and exit from.
|
||||||
|
|
||||||
|
- ``FUNCTION_COLUMN_WIDTH`` (default = ``32``) - the column width for
|
||||||
|
the function name. If a name is too long, it will be truncated.
|
||||||
|
|
||||||
|
- ``CLASS_NAME_RESOLUTION`` (default = ``False``) - enable/disable
|
||||||
|
class name resolution. Function names will be printed with their
|
||||||
|
class names. IMPORTANT: If this setting is enabled, the trace
|
||||||
|
function should ONLY be called from within functions that have the
|
||||||
|
``@TraceFunction`` decorator OR outside of any function.
|
||||||
|
|
||||||
|
- ``TRACE_MESSAGE`` (default = ``True``) - enable/disable message
|
||||||
|
logging.
|
||||||
|
|
||||||
|
- ``MESSAGE_WIDTH`` (default = ``0``) - the column width for the
|
||||||
|
message. A width of zero means unlimited.
|
||||||
|
|
||||||
|
- ``MESSAGE_WRAP`` (default = ``True``) - if ``True``, PyLG will wrap
|
||||||
|
the message to fit within the column width. Otherwise, the message
|
||||||
|
will be truncated.
|
||||||
|
|
||||||
|
- ``MESSAGE_MARK_TRUNCATION`` (default = ``True``) - if ``True``,
|
||||||
|
truncated message lines should have the last character replaced with
|
||||||
|
``\``.
|
||||||
|
|
||||||
|
- ``TRACE_SELF`` (default = ``False``) - enable/disable logging of the
|
||||||
|
``self`` function argument.
|
||||||
|
|
||||||
|
- ``COLLAPSE_LISTS`` (default = ``False``) - if ``True`` lists will be
|
||||||
|
collapsed to ``[ len=x ]`` where ``x`` denotes the number of
|
||||||
|
elements in the list.
|
||||||
|
|
||||||
|
- ``COLLAPSE_DICTS`` (default = ``False``) - if ``True`` dictionaries
|
||||||
|
will be collapsed to ``{ len=x }`` where ``x`` denotes the number of
|
||||||
|
elements in the dictionary.
|
||||||
|
|
||||||
|
- ``DEFAULT_TRACE_ARGS`` (default = ``True``) - the default setting
|
||||||
|
for ``trace_args`` which determines whether TraceFunction should
|
||||||
|
trace function parameters on entry.
|
||||||
|
|
||||||
|
- ``DEFAULT_TRACE_RV`` (default = ``True``) - the default setting for
|
||||||
|
``trace_rv`` which determines whether TraceFunction should trace
|
||||||
|
function return values on exit.
|
||||||
|
|
||||||
Under development
|
Under development
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||||
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
||||||
#
|
#
|
||||||
@ -14,6 +14,11 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
from .pylg import TraceFunction, trace
|
from .loadSettings import PYLG_ENABLE
|
||||||
|
|
||||||
|
if PYLG_ENABLE:
|
||||||
|
from .pylg import TraceFunction, trace
|
||||||
|
else:
|
||||||
|
from .dummy import TraceFunction, trace
|
||||||
|
116
pylg/dummy.py
Normal file
116
pylg/dummy.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||||
|
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
||||||
|
#
|
||||||
|
# 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 3 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/>.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
|
||||||
|
class TraceFunction(object):
|
||||||
|
|
||||||
|
""" Dummy implementation of TraceFunction.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __get__(self, obj, objtype):
|
||||||
|
|
||||||
|
""" Support for instance functions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return partial(self.__call__, obj)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
|
""" Constructor for dummy TraceFunction. Note that the behaviour is
|
||||||
|
different depending on whether TraceFunction is passed any
|
||||||
|
parameters. For details see the non-dummy implementation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# Make sure this decorator is never called with no arguments.
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
assert args or kwargs
|
||||||
|
|
||||||
|
if args:
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# The function init_function will verify the input.
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
self.init_function(*args, **kwargs)
|
||||||
|
|
||||||
|
if kwargs:
|
||||||
|
|
||||||
|
trace_args_str = 'trace_args'
|
||||||
|
trace_rv_str = 'trace_rv'
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# If kwargs is non-empty, it should only contain trace_rv,
|
||||||
|
# trace_args, or both and args should be empty. Assert all
|
||||||
|
# this.
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
assert not args
|
||||||
|
assert (len(kwargs) > 0) and (len(kwargs) <= 2)
|
||||||
|
if len(kwargs) == 1:
|
||||||
|
assert (trace_rv_str in kwargs) or (trace_args_str in kwargs)
|
||||||
|
elif len(kwargs) == 2:
|
||||||
|
assert (trace_rv_str in kwargs) and (trace_args_str in kwargs)
|
||||||
|
|
||||||
|
self.function = None
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
|
||||||
|
""" The actual wrapper that is called when a call to a
|
||||||
|
decorated function is made. It also handles extra
|
||||||
|
initialisation when parameters are passed to
|
||||||
|
TraceFunction.
|
||||||
|
|
||||||
|
:return: The return value of the decorated function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.function is None:
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# For an explanation of the logic here, see the non-dummy
|
||||||
|
# implementations in pylg.py.
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
self.init_function(*args, **kwargs)
|
||||||
|
return self
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# The actual decorating. The dummy implementation doesn't do anything.
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
return self.function(*args, **kwargs)
|
||||||
|
|
||||||
|
def init_function(self, *args, **kwargs):
|
||||||
|
|
||||||
|
""" Function to initialise the TraceFunctionStruct kept by the
|
||||||
|
decorator.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# This function should only ever be called with one parameter
|
||||||
|
# - the function to be decorated. These checks are done here,
|
||||||
|
# rather than by the caller, as anything that calls this
|
||||||
|
# function should also have been called with the decorated
|
||||||
|
# function as its only parameter.
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
assert not kwargs
|
||||||
|
assert len(args) == 1
|
||||||
|
assert callable(args[0])
|
||||||
|
|
||||||
|
self.function = args[0]
|
||||||
|
|
||||||
|
|
||||||
|
def trace(message, function=None):
|
||||||
|
pass
|
332
pylg/loadSettings.py
Normal file
332
pylg/loadSettings.py
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||||
|
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
||||||
|
#
|
||||||
|
# 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 3 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 traceback
|
||||||
|
import warnings
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Import all the defaults first.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
from .settings import *
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# The filename of the user settings. It will be set once it can be
|
||||||
|
# determined after loading the module.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
PYLG_USER_FILE = None
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Attempt to load the module pylg_settings module. If successful, set
|
||||||
|
# PYLG_USER_FILE to the module's path. By attempting an import rather
|
||||||
|
# than checking if a file exists, we can handle the case of the user
|
||||||
|
# having the settings file elsewhere in their path.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# We import the module itself to be able to determine its source
|
||||||
|
# file before we import all the settings.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
import pylg_settings
|
||||||
|
from pylg_settings import *
|
||||||
|
PYLG_USER_FILE = inspect.getsourcefile(pylg_settings)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# The user settings don't exist. We assume the user is happy with
|
||||||
|
# the defaults.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
pass
|
||||||
|
|
||||||
|
except (NameError, SyntaxError):
|
||||||
|
|
||||||
|
warnings.warn("There was a problem importing user settings")
|
||||||
|
|
||||||
|
sys.stderr.write("\n")
|
||||||
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
sys.stderr.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Utility functions for sanity checking user settings. They raise an
|
||||||
|
# ImportError if something is wrong.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def pylg_check_bool(value, name):
|
||||||
|
|
||||||
|
if not isinstance(value, bool):
|
||||||
|
|
||||||
|
warning_msg = ("Invalid type for " + name + " in " +
|
||||||
|
PYLG_USER_FILE +
|
||||||
|
" - should be bool, is type " +
|
||||||
|
type(value).__name__)
|
||||||
|
|
||||||
|
warnings.warn(warning_msg)
|
||||||
|
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
|
||||||
|
def pylg_check_string(value, name):
|
||||||
|
|
||||||
|
if not isinstance(value, basestring):
|
||||||
|
|
||||||
|
warning_msg = ("Invalid type for " + name + " in " +
|
||||||
|
PYLG_USER_FILE +
|
||||||
|
" - should be a string, is type " +
|
||||||
|
type(value).__name__)
|
||||||
|
|
||||||
|
warnings.warn(warning_msg)
|
||||||
|
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
|
||||||
|
def pylg_check_int(value, name):
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# We check for bool as well as bools are an instance of int, but
|
||||||
|
# we don't want to let that go through.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
if not isinstance(value, int) or isinstance(value, bool):
|
||||||
|
|
||||||
|
warning_msg = ("Invalid type for " + name + " in " +
|
||||||
|
PYLG_USER_FILE +
|
||||||
|
" - should be int, is " +
|
||||||
|
type(value).__name__)
|
||||||
|
|
||||||
|
warnings.warn(warning_msg)
|
||||||
|
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
|
||||||
|
def pylg_check_nonneg_int(value, name):
|
||||||
|
|
||||||
|
pylg_check_int(value, name)
|
||||||
|
|
||||||
|
if value < 0:
|
||||||
|
|
||||||
|
warning_msg = ("Invalid value for " + name + " in " +
|
||||||
|
PYLG_USER_FILE +
|
||||||
|
" - should be non-negative, is " +
|
||||||
|
str(value))
|
||||||
|
|
||||||
|
warnings.warn(warning_msg)
|
||||||
|
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
|
||||||
|
def pylg_check_pos_int(value, name):
|
||||||
|
|
||||||
|
pylg_check_int(value, name)
|
||||||
|
|
||||||
|
if value <= 0:
|
||||||
|
|
||||||
|
warning_msg = ("Invalid value for " + name + " in " +
|
||||||
|
PYLG_USER_FILE +
|
||||||
|
" - should be positive, is " +
|
||||||
|
str(value))
|
||||||
|
|
||||||
|
warnings.warn(warning_msg)
|
||||||
|
|
||||||
|
raise ImportError
|
||||||
|
|
||||||
|
|
||||||
|
if PYLG_USER_FILE is not None:
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# If PYLG_USER_FILE is set, we have successfully imported user
|
||||||
|
# settings. Nowe, we need to sanity check them. If anything is
|
||||||
|
# wrong we reset the value to its default. At this stage a single
|
||||||
|
# error should not affect any other settings.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# PYLG_ENABLE - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(PYLG_ENABLE, "PYLG_ENABLE")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import PYLG_ENABLE
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# PYLG_FILE - string
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_string(PYLG_FILE, "PYLG_FILE")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import PYLG_FILE
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# EXCEPTION_WARNING - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(EXCEPTION_WARNING, "EXCEPTION_WARNING")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import EXCEPTION_WARNING
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# EXCEPTION_EXIT - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(EXCEPTION_EXIT, "EXCEPTION_EXIT")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import EXCEPTION_EXIT
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# TRACE_TIME - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(TRACE_TIME, "TRACE_TIME")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import TRACE_TIME
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# TIME_FORMAT - string
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_string(TIME_FORMAT, "TIME_FORMAT")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import TIME_FORMAT
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# TRACE_FILENAME - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(TRACE_FILENAME, "TRACE_FILENAME")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import TRACE_FILENAME
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# FILENAME_COLUMN_WIDTH - non-negative integer
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_pos_int(FILENAME_COLUMN_WIDTH, "FILENAME_COLUMN_WIDTH")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import FILENAME_COLUMN_WIDTH
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# TRACE_LINENO - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(TRACE_LINENO, "TRACE_LINENO")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import TRACE_LINENO
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# LINENO_WIDTH - non-negative integer
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_nonneg_int(LINENO_WIDTH, "LINENO_WIDTH")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import LINENO_WIDTH
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# TRACE_FUNCTION - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(TRACE_FUNCTION, "TRACE_FUNCTION")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import TRACE_FUNCTION
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# FUNCTION_COLUMN_WIDTH - non-negative integer
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_pos_int(FUNCTION_COLUMN_WIDTH, "FUNCTION_COLUMN_WIDTH")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import FUNCTION_COLUMN_WIDTH
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# CLASS_NAME_RESOLUTION - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(CLASS_NAME_RESOLUTION, "CLASS_NAME_RESOLUTION")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import CLASS_NAME_RESOLUTION
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# TRACE_MESSAGE - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(TRACE_MESSAGE, "TRACE_MESSAGE")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import TRACE_MESSAGE
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# MESSAGE_WIDTH - non-negative integer - note 0 denotes unlimited
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_nonneg_int(MESSAGE_WIDTH, "MESSAGE_WIDTH")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import MESSAGE_WIDTH
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# MESSAGE_WRAP - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(MESSAGE_WRAP, "MESSAGE_WRAP")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import MESSAGE_WRAP
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# MESSAGE_MARK_TRUNCATION - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(MESSAGE_MARK_TRUNCATION, "MESSAGE_MARK_TRUNCATION")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import MESSAGE_MARK_TRUNCATION
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# TRACE_SELF - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(TRACE_SELF, "TRACE_SELF")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import TRACE_SELF
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# COLLAPSE_LISTS - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(COLLAPSE_LISTS, "COLLAPSE_LISTS")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import COLLAPSE_LISTS
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# COLLAPSE_DICTS - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(COLLAPSE_DICTS, "COLLAPSE_DICTS")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import COLLAPSE_DICTS
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# DEFAULT_TRACE_ARGS - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(DEFAULT_TRACE_ARGS, "DEFAULT_TRACE_ARGS")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import DEFAULT_TRACE_ARGS
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# DEFAULT_TRACE_RV - bool
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
pylg_check_bool(DEFAULT_TRACE_RV, "DEFAULT_TRACE_RV")
|
||||||
|
except ImportError:
|
||||||
|
from .settings import DEFAULT_TRACE_RV
|
255
pylg/pylg.py
255
pylg/pylg.py
@ -1,4 +1,4 @@
|
|||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||||
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
||||||
#
|
#
|
||||||
@ -14,31 +14,22 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import traceback
|
||||||
import warnings
|
import warnings
|
||||||
|
import textwrap
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Load default settings.
|
# Load settings.
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
from .settings import *
|
from .loadSettings import *
|
||||||
|
|
||||||
try:
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Load user settings.
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
from pylg_settings import *
|
|
||||||
|
|
||||||
except ImportError:
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# User settings don't exist.
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
pass
|
|
||||||
|
|
||||||
class ClassNameStack(object):
|
class ClassNameStack(object):
|
||||||
|
|
||||||
@ -67,13 +58,14 @@ class ClassNameStack(object):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class PyLg(object):
|
class PyLg(object):
|
||||||
|
|
||||||
""" Class to handle the log file.
|
""" Class to handle the log file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
wfile = None
|
wfile = None
|
||||||
filename = PYLG
|
filename = PYLG_FILE
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_filename(new_filename):
|
def set_filename(new_filename):
|
||||||
@ -104,6 +96,7 @@ class PyLg(object):
|
|||||||
str(datetime.now()) + " ===\n\n")
|
str(datetime.now()) + " ===\n\n")
|
||||||
|
|
||||||
PyLg.wfile.write(string)
|
PyLg.wfile.write(string)
|
||||||
|
PyLg.wfile.flush()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def close():
|
def close():
|
||||||
@ -117,6 +110,7 @@ class PyLg(object):
|
|||||||
else:
|
else:
|
||||||
warnings.warn("PyLg wfile is not open - nothing to close")
|
warnings.warn("PyLg wfile is not open - nothing to close")
|
||||||
|
|
||||||
|
|
||||||
class TraceFunction(object):
|
class TraceFunction(object):
|
||||||
|
|
||||||
""" Class that serves as a decorator to trace entry and exit from
|
""" Class that serves as a decorator to trace entry and exit from
|
||||||
@ -152,9 +146,9 @@ class TraceFunction(object):
|
|||||||
parameters. For details see __call__ in this class.
|
parameters. For details see __call__ in this class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# Make sure this decorator is never called with no arguments.
|
# Make sure this decorator is never called with no arguments.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
assert args or kwargs
|
assert args or kwargs
|
||||||
|
|
||||||
if args:
|
if args:
|
||||||
@ -162,9 +156,9 @@ class TraceFunction(object):
|
|||||||
self.trace_args = DEFAULT_TRACE_ARGS
|
self.trace_args = DEFAULT_TRACE_ARGS
|
||||||
self.trace_rv = DEFAULT_TRACE_RV
|
self.trace_rv = DEFAULT_TRACE_RV
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# The function init_function will verify the input.
|
# The function init_function will verify the input.
|
||||||
#-------------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
self.init_function(*args, **kwargs)
|
self.init_function(*args, **kwargs)
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
@ -172,11 +166,11 @@ class TraceFunction(object):
|
|||||||
trace_args_str = 'trace_args'
|
trace_args_str = 'trace_args'
|
||||||
trace_rv_str = 'trace_rv'
|
trace_rv_str = 'trace_rv'
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# If kwargs is non-empty, it should only contain trace_rv,
|
# If kwargs is non-empty, it should only contain trace_rv,
|
||||||
# trace_args, or both and args should be empty. Assert all
|
# trace_args, or both and args should be empty. Assert all
|
||||||
# this.
|
# this.
|
||||||
#-------------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
assert not args
|
assert not args
|
||||||
assert (len(kwargs) > 0) and (len(kwargs) <= 2)
|
assert (len(kwargs) > 0) and (len(kwargs) <= 2)
|
||||||
if len(kwargs) == 1:
|
if len(kwargs) == 1:
|
||||||
@ -206,7 +200,7 @@ class TraceFunction(object):
|
|||||||
:return: The return value of the decorated function.
|
:return: The return value of the decorated function.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# __call__ has to behave differently depending on whether the
|
# __call__ has to behave differently depending on whether the
|
||||||
# decorator has been given any parameters. The reason for this
|
# decorator has been given any parameters. The reason for this
|
||||||
# is as follows:
|
# is as follows:
|
||||||
@ -226,10 +220,10 @@ class TraceFunction(object):
|
|||||||
# the first case, the callable object is an instance of
|
# the first case, the callable object is an instance of
|
||||||
# TraceFunction, in the latter case the return value of
|
# TraceFunction, in the latter case the return value of
|
||||||
# TraceFunction.__call__ is the callable object.
|
# TraceFunction.__call__ is the callable object.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
if self.function is None:
|
if self.function is None:
|
||||||
#-------------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# If the decorator has been passed a parameter, __init__
|
# If the decorator has been passed a parameter, __init__
|
||||||
# will not define self.function and __call__ will be
|
# will not define self.function and __call__ will be
|
||||||
# called immediately after __init__ with the decorated
|
# called immediately after __init__ with the decorated
|
||||||
@ -240,15 +234,13 @@ class TraceFunction(object):
|
|||||||
# object as the callable handle for the decorated
|
# object as the callable handle for the decorated
|
||||||
# function. This if block should be hit only once at most
|
# function. This if block should be hit only once at most
|
||||||
# and only during initialisation.
|
# and only during initialisation.
|
||||||
#-------------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
self.init_function(*args, **kwargs)
|
self.init_function(*args, **kwargs)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# The actual decorating.
|
# The actual decorating.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
if PYLG_ENABLE:
|
|
||||||
|
|
||||||
ClassNameStack.insert(self.function.classname)
|
ClassNameStack.insert(self.function.classname)
|
||||||
self.trace_entry(*args, **kwargs)
|
self.trace_entry(*args, **kwargs)
|
||||||
|
|
||||||
@ -257,18 +249,15 @@ class TraceFunction(object):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.trace_exception(e)
|
self.trace_exception(e)
|
||||||
|
|
||||||
exc_info = sys.exc_info()
|
if EXCEPTION_EXIT:
|
||||||
raise (exc_info[0], exc_info[1], exc_info[2])
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
raise
|
||||||
|
|
||||||
self.trace_exit(rv)
|
self.trace_exit(rv)
|
||||||
ClassNameStack.pop()
|
ClassNameStack.pop()
|
||||||
|
|
||||||
else:
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
# If PYLG is disabled, don't wrap anything.
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
rv = self.function.function(*args, **kwargs)
|
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def init_function(self, *args, **kwargs):
|
def init_function(self, *args, **kwargs):
|
||||||
@ -277,13 +266,13 @@ class TraceFunction(object):
|
|||||||
decorator.
|
decorator.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# This function should only ever be called with one parameter
|
# This function should only ever be called with one parameter
|
||||||
# - the function to be decorated. These checks are done here,
|
# - the function to be decorated. These checks are done here,
|
||||||
# rather than by the caller, as anything that calls this
|
# rather than by the caller, as anything that calls this
|
||||||
# function should also have been called with the decorated
|
# function should also have been called with the decorated
|
||||||
# function as its only parameter.
|
# function as its only parameter.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
assert not kwargs
|
assert not kwargs
|
||||||
assert len(args) == 1
|
assert len(args) == 1
|
||||||
assert callable(args[0])
|
assert callable(args[0])
|
||||||
@ -300,10 +289,10 @@ class TraceFunction(object):
|
|||||||
zip(argspec.args[-len(argspec.defaults):],
|
zip(argspec.args[-len(argspec.defaults):],
|
||||||
argspec.defaults))
|
argspec.defaults))
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# init_function is called from either __init__ or __call__ and
|
# init_function is called from either __init__ or __call__ and
|
||||||
# we want the frame before that.
|
# we want the frame before that.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
frames_back = 2
|
frames_back = 2
|
||||||
caller_frame = inspect.stack()[frames_back]
|
caller_frame = inspect.stack()[frames_back]
|
||||||
|
|
||||||
@ -319,9 +308,9 @@ class TraceFunction(object):
|
|||||||
trace.
|
trace.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# The ENTRY message.
|
# The ENTRY message.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
msg = "-> ENTRY"
|
msg = "-> ENTRY"
|
||||||
if args or kwargs:
|
if args or kwargs:
|
||||||
msg += ": "
|
msg += ": "
|
||||||
@ -329,25 +318,30 @@ class TraceFunction(object):
|
|||||||
n_args = len(args)
|
n_args = len(args)
|
||||||
if self.trace_args:
|
if self.trace_args:
|
||||||
for arg in range(n_args):
|
for arg in range(n_args):
|
||||||
|
|
||||||
|
if not TRACE_SELF and \
|
||||||
|
self.function.varnames[arg] == "self":
|
||||||
|
continue
|
||||||
|
|
||||||
msg += (self.function.varnames[arg] + " = " +
|
msg += (self.function.varnames[arg] + " = " +
|
||||||
str(args[arg]) + ", ")
|
self.get_value_string(args[arg]) + ", ")
|
||||||
|
|
||||||
for name in self.function.varnames[n_args:]:
|
for name in self.function.varnames[n_args:]:
|
||||||
msg += name + " = "
|
msg += name + " = "
|
||||||
if name in kwargs:
|
if name in kwargs:
|
||||||
msg += str(kwargs[name])
|
value = kwargs[name]
|
||||||
else:
|
else:
|
||||||
msg += str(self.function.defaults[name])
|
value = self.function.defaults[name]
|
||||||
msg += ", "
|
msg += self.get_value_string(value) + ", "
|
||||||
|
|
||||||
msg = msg[:-2]
|
msg = msg[:-2]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg += "---"
|
msg += "---"
|
||||||
|
|
||||||
trace(msg, function = self.function)
|
trace(msg, function=self.function)
|
||||||
|
|
||||||
def trace_exit(self, rv = None):
|
def trace_exit(self, rv=None):
|
||||||
|
|
||||||
""" Called on function exit to log the fact that a function has
|
""" Called on function exit to log the fact that a function has
|
||||||
finished executing.
|
finished executing.
|
||||||
@ -355,18 +349,18 @@ class TraceFunction(object):
|
|||||||
:param rv: The return value of the traced function.
|
:param rv: The return value of the traced function.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# The EXIT message.
|
# The EXIT message.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
msg = "<- EXIT "
|
msg = "<- EXIT "
|
||||||
if rv is not None:
|
if rv is not None:
|
||||||
msg += ": "
|
msg += ": "
|
||||||
if self.trace_rv:
|
if self.trace_rv:
|
||||||
msg += str(rv)
|
msg += self.get_value_string(rv)
|
||||||
else:
|
else:
|
||||||
msg += "---"
|
msg += "---"
|
||||||
|
|
||||||
trace(msg, function = self.function)
|
trace(msg, function=self.function)
|
||||||
return
|
return
|
||||||
|
|
||||||
def trace_exception(self, exception):
|
def trace_exception(self, exception):
|
||||||
@ -376,9 +370,9 @@ class TraceFunction(object):
|
|||||||
:param exception: The raised exception.
|
:param exception: The raised exception.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# The EXIT message.
|
# The EXIT message.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
core_msg = type(exception).__name__ + " RAISED"
|
core_msg = type(exception).__name__ + " RAISED"
|
||||||
msg = "<- EXIT : " + core_msg
|
msg = "<- EXIT : " + core_msg
|
||||||
|
|
||||||
@ -388,10 +382,29 @@ class TraceFunction(object):
|
|||||||
if EXCEPTION_WARNING:
|
if EXCEPTION_WARNING:
|
||||||
warnings.warn(core_msg, RuntimeWarning)
|
warnings.warn(core_msg, RuntimeWarning)
|
||||||
|
|
||||||
trace(msg, function = self.function)
|
trace(msg, function=self.function)
|
||||||
return
|
return
|
||||||
|
|
||||||
def trace(message, function = None):
|
def get_value_string(self, value):
|
||||||
|
|
||||||
|
""" Convert value to a string for the log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if isinstance(value, list) and COLLAPSE_LISTS:
|
||||||
|
return self.collapse_list(value)
|
||||||
|
elif isinstance(value, dict) and COLLAPSE_DICTS:
|
||||||
|
return self.collapse_dict(value)
|
||||||
|
else:
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
def collapse_list(self, ll):
|
||||||
|
return "[ len=" + str(len(ll)) + " ]"
|
||||||
|
|
||||||
|
def collapse_dict(self, dd):
|
||||||
|
return "{ len=" + str(len(dd)) + " }"
|
||||||
|
|
||||||
|
|
||||||
|
def trace(message, function=None):
|
||||||
|
|
||||||
""" Writes message to the log file. It will also log the time,
|
""" Writes message to the log file. It will also log the time,
|
||||||
filename, line number and function name.
|
filename, line number and function name.
|
||||||
@ -401,17 +414,11 @@ def trace(message, function = None):
|
|||||||
TraceFunction.
|
TraceFunction.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not PYLG_ENABLE:
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
# Don't do anything if PYLG is disabled
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
return
|
|
||||||
|
|
||||||
if function is None:
|
if function is None:
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
# If there is no function object, we need to work out
|
# If there is no function object, we need to work out
|
||||||
# where the trace call was made from.
|
# where the trace call was made from.
|
||||||
#-----------------------------------------------------------------------
|
# ---------------------------------------------------------------------
|
||||||
frames_back = 1
|
frames_back = 1
|
||||||
caller_frame = inspect.stack()[frames_back]
|
caller_frame = inspect.stack()[frames_back]
|
||||||
|
|
||||||
@ -424,25 +431,115 @@ def trace(message, function = None):
|
|||||||
lineno = function.lineno
|
lineno = function.lineno
|
||||||
functionname = function.functionname
|
functionname = function.functionname
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# If CLASS_NAME_RESOLUTION is enabled, the top element of the
|
# If CLASS_NAME_RESOLUTION is enabled, the top element of the
|
||||||
# stack should be the class name of the function from which this
|
# stack should be the class name of the function from which this
|
||||||
# trace call is made. This cannot be policed so the user must make
|
# trace call is made. This cannot be policed so the user must make
|
||||||
# sure this is the case by ensuring that trace is only called
|
# sure this is the case by ensuring that trace is only called
|
||||||
# outside of any function or from within functions that have the
|
# outside of any function or from within functions that have the
|
||||||
# @TraceFunction decorator.
|
# @TraceFunction decorator.
|
||||||
#---------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
classname = ClassNameStack.get()
|
classname = ClassNameStack.get()
|
||||||
if classname is not None and classname != "<module>":
|
if classname is not None and classname != "<module>":
|
||||||
functionname = classname + "." + functionname
|
functionname = classname + "." + functionname
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
# Generate the string based on the settings.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
msg = ""
|
||||||
|
|
||||||
|
if TRACE_TIME:
|
||||||
|
msg += datetime.now().strftime(TIME_FORMAT) + " "
|
||||||
|
|
||||||
|
if TRACE_FILENAME:
|
||||||
|
msg += '{filename:{w}.{w}} '.format(filename=filename,
|
||||||
|
w=FILENAME_COLUMN_WIDTH)
|
||||||
|
|
||||||
|
if TRACE_LINENO:
|
||||||
|
msg += '{lineno:0{w}}: '.format(lineno=lineno, w=LINENO_WIDTH)
|
||||||
|
|
||||||
|
if TRACE_FUNCTION:
|
||||||
|
msg += '{function:{w}.{w}} '.format(function=functionname,
|
||||||
|
w=FUNCTION_COLUMN_WIDTH)
|
||||||
|
|
||||||
|
if TRACE_MESSAGE:
|
||||||
|
|
||||||
|
message = str(message)
|
||||||
|
|
||||||
|
if MESSAGE_WIDTH > 0:
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# Get the length of the trace line so far
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
premsglen = len(msg)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# Wrap the text.
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
wrapped = textwrap.wrap(message, MESSAGE_WIDTH)
|
||||||
|
|
||||||
|
if wrapped:
|
||||||
|
if MESSAGE_WRAP:
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Print the first line. It gets special treatment
|
||||||
|
# as it doesn't need whitespace in front of it.
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
msg += wrapped[0]
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Print the remaining lines. Append whitespace to
|
||||||
|
# align it with the first line.
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
for line in wrapped[1:]:
|
||||||
|
msg += '\n' + '{:{w}}'.format('', w=premsglen) + line
|
||||||
|
|
||||||
|
else:
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# The message is not being wrapped.
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
|
if MESSAGE_MARK_TRUNCATION and wrapped[1:]:
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# We want to mark truncated lines so we need
|
||||||
|
# to determine if the line is being
|
||||||
|
# truncated. If it is we replace the last
|
||||||
|
# character with '\'.
|
||||||
|
# -----------------------------------------------------
|
||||||
|
|
||||||
|
if MESSAGE_WIDTH > 1:
|
||||||
|
wrapped = textwrap.wrap(wrapped[0],
|
||||||
|
MESSAGE_WIDTH - 1)
|
||||||
|
assert wrapped
|
||||||
|
|
||||||
|
msg += ('{m:{w}}'.format(m=wrapped[0],
|
||||||
|
w=MESSAGE_WIDTH - 1) +
|
||||||
|
'\\')
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert MESSAGE_WIDTH == 1
|
||||||
|
msg += '\\'
|
||||||
|
|
||||||
|
else:
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# Either the message is not being truncated or
|
||||||
|
# MESSAGE_MARK_TRUNCATION is False.
|
||||||
|
# -----------------------------------------------------
|
||||||
|
msg += wrapped[0]
|
||||||
|
|
||||||
|
else:
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# A MESSAGE_WIDTH of 0 denotes no limit.
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
assert MESSAGE_WIDTH == 0
|
||||||
|
msg += message
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Terminate the log line with a newline.
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
msg += "\n"
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
# Write the data to the log file.
|
# Write the data to the log file.
|
||||||
#---------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
PyLg.write(str(datetime.now()) + " " +
|
PyLg.write(msg)
|
||||||
'{filename:{w}.{w}} '.format(filename = filename,
|
|
||||||
w = FILENAME_COLUMN_WIDTH) +
|
|
||||||
'{0:04d}: '.format(lineno) +
|
|
||||||
'{function:{w}.{w}} '.format(function = functionname,
|
|
||||||
w = FUNCTION_COLUMN_WIDTH) +
|
|
||||||
message + "\n")
|
|
||||||
|
142
pylg/settings.py
142
pylg/settings.py
@ -1,4 +1,4 @@
|
|||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
# PyLg: module to facilitate and automate the process of writing runtime logs.
|
||||||
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
# Copyright (C) 2017 Wojciech Kozlowski <wojciech.kozlowski@vivaldi.net>
|
||||||
#
|
#
|
||||||
@ -14,43 +14,129 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Enable/disable PyLg.
|
# Enable/disable PyLg.
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
PYLG_ENABLE = True
|
PYLG_ENABLE = True
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Log file.
|
# The log file name.
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
PYLG = 'pylg.log'
|
PYLG_FILE = 'pylg.log'
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Enable class name resolution. Function names will be printed with
|
# If True, PyLg will print a warning about every exception caught to
|
||||||
# their class names.
|
# stderr.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
EXCEPTION_WARNING = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# If True, PyLg will force the program to exit (and not just raise
|
||||||
|
# SystemExit) whenever an exception occurs. This will happen even if
|
||||||
|
# the exception would be handled at a later point.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
EXCEPTION_EXIT = False
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Enable/disable time logging.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
TRACE_TIME = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Formatting for the time trace. For a full list of options, see
|
||||||
|
# https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
TIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Enable/disable file name logging.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
TRACE_FILENAME = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# The column width for the file name. If a name is too long, it will
|
||||||
|
# be truncated.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
FILENAME_COLUMN_WIDTH = 20
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Enable/disable the logging of the line number from which the trace
|
||||||
|
# call was made. For entry and exit messages this logs the line in
|
||||||
|
# which the decorator is placed (which should be directly above the
|
||||||
|
# function itself).
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
TRACE_LINENO = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# The minimum number of digits to use to print the line number. If the
|
||||||
|
# number is too long, more digits will be used.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
LINENO_WIDTH = 4
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Enable/disable the logging of the function name from which the trace
|
||||||
|
# call was made. Entry/exit logs refer to the function they enter into
|
||||||
|
# and exit from.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
TRACE_FUNCTION = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# The column width for the function name. If a name is too long, it
|
||||||
|
# will be truncated.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
FUNCTION_COLUMN_WIDTH = 32
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Enable/disable class name resolution. Function names will be printed
|
||||||
|
# with their class names.
|
||||||
#
|
#
|
||||||
# IMPORTANT: If this setting is enabled, the trace function should
|
# IMPORTANT: If this setting is enabled, the trace function should
|
||||||
# ONLY be called from within functions that have the @TraceFunction
|
# ONLY be called from within functions that have the @TraceFunction
|
||||||
# decorator OR outside of any function.
|
# decorator OR outside of any function.
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
CLASS_NAME_RESOLUTION = False
|
CLASS_NAME_RESOLUTION = False
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# The default for whether TraceFunction should trace function
|
# Enable/disable message logging.
|
||||||
# parameters and return values.
|
# -----------------------------------------------------------------------------
|
||||||
#-------------------------------------------------------------------------------
|
TRACE_MESSAGE = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# The column width for the message. A width of zero means unlimited.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
MESSAGE_WIDTH = 0
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# If True, PyLG will wrap the message to fit within the column
|
||||||
|
# width. Otherwise, the message will be truncated.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
MESSAGE_WRAP = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# If true, truncated message lines should have the last character
|
||||||
|
# replaced with '\'.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
MESSAGE_MARK_TRUNCATION = True
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Enable/disable logging of the 'self' function argument.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
TRACE_SELF = False
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# If True lists/dictionaries will be collapsed to '[ len=x ]' and '{
|
||||||
|
# len=x }' respectively, where x denotes the number of elements in the
|
||||||
|
# collection.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
COLLAPSE_LISTS = False
|
||||||
|
COLLAPSE_DICTS = False
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# The default settings for 'trace_args' and 'trace_rv' which determine
|
||||||
|
# whether TraceFunction should trace function parameters on entry and
|
||||||
|
# return values on exit.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
DEFAULT_TRACE_ARGS = True
|
DEFAULT_TRACE_ARGS = True
|
||||||
DEFAULT_TRACE_RV = True
|
DEFAULT_TRACE_RV = True
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# Whether to warn the user when an Exception has been raised in a
|
|
||||||
# traced funcion.
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
EXCEPTION_WARNING = True
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
# The column width for file and function names.
|
|
||||||
#-------------------------------------------------------------------------------
|
|
||||||
FILENAME_COLUMN_WIDTH = 32
|
|
||||||
FUNCTION_COLUMN_WIDTH = 32
|
|
||||||
|
18
setup.py
18
setup.py
@ -8,15 +8,15 @@ with open(path.join(pwd, 'README.rst'), encoding='utf-8') as f:
|
|||||||
long_description = f.read()
|
long_description = f.read()
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = 'PyLg',
|
name='PyLg',
|
||||||
version = '1.1.0',
|
version='1.2.0',
|
||||||
description = 'Python module to facilitate and automate the process of writing runtime logs.',
|
description='Python module to facilitate and automate the process of writing runtime logs.',
|
||||||
long_description = long_description,
|
long_description=long_description,
|
||||||
url = 'https://gitlab.wojciechkozlowski.eu/wojtek/PyLg',
|
url='https://gitlab.wojciechkozlowski.eu/wojtek/PyLg',
|
||||||
|
|
||||||
author = 'Wojciech Kozlowski',
|
author='Wojciech Kozlowski',
|
||||||
author_email = 'wojciech.kozlowski@vivaldi.net',
|
author_email='wojciech.kozlowski@vivaldi.net',
|
||||||
classifiers = [
|
classifiers=[
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 3 - Alpha',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'Topic :: Software Development :: Debuggers',
|
'Topic :: Software Development :: Debuggers',
|
||||||
@ -26,7 +26,7 @@ setup(
|
|||||||
],
|
],
|
||||||
|
|
||||||
keywords='development log debug trace',
|
keywords='development log debug trace',
|
||||||
include_package_data = True,
|
include_package_data=True,
|
||||||
|
|
||||||
packages=["pylg"]
|
packages=["pylg"]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user