Added IN_GAME_ERRORS setting for convenient debugging during development (False by default)

This commit is contained in:
Griatch 2016-04-05 22:04:16 +02:00
parent 4f02ec1cbe
commit 4cb19bec59
3 changed files with 106 additions and 39 deletions

View file

@ -35,6 +35,7 @@ command line. The processing of a command works as follows:
from collections import defaultdict from collections import defaultdict
from weakref import WeakValueDictionary from weakref import WeakValueDictionary
from traceback import format_exc
from copy import copy from copy import copy
from twisted.internet.defer import inlineCallbacks, returnValue from twisted.internet.defer import inlineCallbacks, returnValue
from django.conf import settings from django.conf import settings
@ -44,6 +45,8 @@ from evennia.utils.utils import string_suggestions, to_unicode
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
_IN_GAME_ERRORS = settings.IN_GAME_ERRORS
__all__ = ("cmdhandler",) __all__ = ("cmdhandler",)
_GA = object.__getattribute__ _GA = object.__getattribute__
_CMDSET_MERGE_CACHE = WeakValueDictionary() _CMDSET_MERGE_CACHE = WeakValueDictionary()
@ -76,46 +79,76 @@ CMD_LOGINSTART = "__unloggedin_look_command"
# Function for handling multiple command matches. # Function for handling multiple command matches.
_SEARCH_AT_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) _SEARCH_AT_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
# Output strings # Output strings. The first is the IN_GAME_ERRORS return, the second
# is the normal "production message to echo to the player.
_ERROR_UNTRAPPED = """ _ERROR_UNTRAPPED = (
An untrapped error occurred. Please file a bug report detailing the
steps to reproduce. Server log time stamp is '{timestamp}'.
""" """
An untrapped error occurred.
_ERROR_CMDSETS = """ """,
A cmdset merger error occurred. Please file a bug report detailing the
steps to reproduce. Server log time stamp is '{timestamp}'.
""" """
An untrapped error occurred. Please file a bug report detailing the steps to reproduce.
""")
_ERROR_NOCMDSETS = """ _ERROR_CMDSETS = (
"""
A cmdset merger-error occurred. This is often due to a syntax
error in one of the cmdsets to merge.
""",
"""
A cmdset merger-error occurred. Please file a bug report detailing the
steps to reproduce.
""")
_ERROR_NOCMDSETS = (
"""
No command sets found! This is a critical bug that can have
multiple causes.
""",
"""
No command sets found! This is a sign of a critical bug. If No command sets found! This is a sign of a critical bug. If
disconnecting/reconnecting doesn't" solve the problem, try to contact disconnecting/reconnecting doesn't" solve the problem, try to contact
the server admin through" some other means for assistance. Server log the server admin through" some other means for assistance.
time stamp is '{timestamp}'. """)
"""
_ERROR_CMDHANDLER = """ _ERROR_CMDHANDLER = (
A command handler bug occurred. Please file a bug report with the
Evennia project. Include the relvant traceback from the server log at
time stamp '{timestamp}'.
""" """
A command handler bug occurred. If this is not due to a local change,
please file a bug report with the Evennia project, including the
traceback and steps to reproduce.
""",
"""
A command handler bug occurred. Please notify staff - they should
likely file a bug report with the Evennia project.
""")
_ERROR_RECURSION_LIMIT = "Command recursion limit ({recursion_limit}) " \ _ERROR_RECURSION_LIMIT = "Command recursion limit ({recursion_limit}) " \
"reached for '{raw_string}' ({cmdclass})." "reached for '{raw_string}' ({cmdclass})."
def _msg_err(receiver, string): def _msg_err(receiver, stringtuple):
""" """
Helper function for returning an error to the caller. Helper function for returning an error to the caller.
Args: Args:
receiver (Object): object to get the error message. receiver (Object): object to get the error message.
string (str): string which will be shown to the user. stringtuple (tuple): tuple with two strings - one for the
_IN_GAME_ERRORS mode (with the traceback) and one with the
production string (with a timestamp) to be shown to the user.
""" """
receiver.msg(string.format(timestamp=logger.timeformat()).strip()) string = "{traceback}\n{errmsg}\n(Traceback was logged {timestamp})."
timestamp = logger.timeformat()
tracestring = format_exc()
#logger.log_trace()
if _IN_GAME_ERRORS:
receiver.msg(string.format(traceback=tracestring,
errmsg=stringtuple[0].strip(),
timestamp=timestamp).strip())
else:
receiver.msg(string.format(traceback=tracestring.splitlines()[-1],
errmsg=stringtuple[1].strip(),
timestamp=timestamp).strip())
# custom Exceptions # custom Exceptions
@ -177,7 +210,6 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype):
channel_cmdset = yield CHANNELHANDLER.get_cmdset(player) channel_cmdset = yield CHANNELHANDLER.get_cmdset(player)
returnValue(channel_cmdset) returnValue(channel_cmdset)
except Exception: except Exception:
logger.log_trace()
_msg_err(caller, _ERROR_CMDSETS) _msg_err(caller, _ERROR_CMDSETS)
raise ErrorReported raise ErrorReported
@ -221,7 +253,6 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype):
cset.duplicates = True if cset.duplicates is None else cset.duplicates cset.duplicates = True if cset.duplicates is None else cset.duplicates
returnValue(local_obj_cmdsets) returnValue(local_obj_cmdsets)
except Exception: except Exception:
logger.log_trace()
_msg_err(caller, _ERROR_CMDSETS) _msg_err(caller, _ERROR_CMDSETS)
raise ErrorReported raise ErrorReported
@ -235,7 +266,6 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype):
try: try:
yield obj.at_cmdset_get() yield obj.at_cmdset_get()
except Exception: except Exception:
logger.log_trace()
_msg_err(caller, _ERROR_CMDSETS) _msg_err(caller, _ERROR_CMDSETS)
raise ErrorReported raise ErrorReported
try: try:
@ -323,7 +353,6 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype):
except ErrorReported: except ErrorReported:
raise raise
except Exception: except Exception:
logger.log_trace()
_msg_err(caller, _ERROR_CMDSETS) _msg_err(caller, _ERROR_CMDSETS)
raise ErrorReported raise ErrorReported
@ -449,7 +478,6 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
returnValue(ret) returnValue(ret)
except Exception: except Exception:
logger.log_trace()
_msg_err(caller, _ERROR_UNTRAPPED) _msg_err(caller, _ERROR_UNTRAPPED)
raise ErrorReported raise ErrorReported
@ -579,10 +607,8 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess
except Exception: except Exception:
# We should not end up here. If we do, it's a programming bug. # We should not end up here. If we do, it's a programming bug.
logger.log_trace()
_msg_err(error_to, _ERROR_UNTRAPPED) _msg_err(error_to, _ERROR_UNTRAPPED)
except Exception: except Exception:
# This catches exceptions in cmdhandler exceptions themselves # This catches exceptions in cmdhandler exceptions themselves
logger.log_trace()
_msg_err(error_to, _ERROR_CMDHANDLER) _msg_err(error_to, _ERROR_CMDHANDLER)

View file

@ -66,6 +66,7 @@ the 'Fishing' set. Fishing from a boat? No problem!
from builtins import object from builtins import object
from future.utils import raise_ from future.utils import raise_
import sys import sys
from traceback import format_exc
from importlib import import_module from importlib import import_module
from inspect import trace from inspect import trace
from django.conf import settings from django.conf import settings
@ -78,6 +79,29 @@ __all__ = ("import_cmdset", "CmdSetHandler")
_CACHED_CMDSETS = {} _CACHED_CMDSETS = {}
_CMDSET_PATHS = utils.make_iter(settings.CMDSET_PATHS) _CMDSET_PATHS = utils.make_iter(settings.CMDSET_PATHS)
_IN_GAME_ERRORS = settings.IN_GAME_ERRORS
# Output strings
_ERROR_CMDSET_IMPORT = _(
"""{traceback}
Error loading cmdset '{path}'
(Traceback was logged {timestamp})""")
_ERROR_CMDSET_KEYERROR = _(
"""Error loading cmdset: No cmdset class '{classname}' in '{path}'.
(Traceback was logged {timestamp})""")
_ERROR_CMDSET_SYNTAXERROR = _(
"""{traceback}
SyntaxError encountered when loading cmdset '{path}'.
(Traceback was logged {timestamp})""")
_ERROR_CMDSET_EXCEPTION = _(
"""{traceback}
Compile/Run error when loading cmdset '{path}'.",
(Traceback was logged {timestamp})""")
class _ErrorCmdSet(CmdSet): class _ErrorCmdSet(CmdSet):
""" """
@ -160,25 +184,34 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
cmdsetclass = cmdsetclass(cmdsetobj) cmdsetclass = cmdsetclass(cmdsetobj)
errstring = "" errstring = ""
return cmdsetclass return cmdsetclass
except ImportError as e: except ImportError as err:
logger.log_trace() logger.log_trace()
errstring += _("\nError loading cmdset {path}: \"{error}\"") errstring += _ERROR_CMDSET_IMPORT
errstring = errstring.format(path=python_path, error=e) if _IN_GAME_ERRORS:
errstring = errstring.format(path=python_path, traceback=format_exc(), timestamp=logger.timeformat())
else:
errstring = errstring.format(path=python_path, traceback=err, timestamp=logger.timeformat())
break break
except KeyError: except KeyError:
logger.log_trace() logger.log_trace()
errstring += _("\nError in loading cmdset: No cmdset class '{classname}' in {path}.") errstring += _ERROR_CMDSET_KEYERROR
errstring = errstring.format(classname=classname, path=python_path) errstring = errstring.format(classname=classname, path=python_path, timestamp=logger.timeformat())
break break
except SyntaxError as e: except SyntaxError as err:
logger.log_trace() logger.log_trace()
errstring += _("\nSyntaxError encountered when loading cmdset '{path}': \"{error}\".") errstring += _ERROR_CMDSET_SYNTAXERROR
errstring = errstring.format(path=python_path, error=e) if _IN_GAME_ERRORS:
errstring = errstring.format(path=python_path, traceback=format_exc(), timestamp=logger.timeformat())
else:
errstring = errstring.format(path=python_path, traceback=err, timestamp=logger.timeformat())
break break
except Exception as e: except Exception as err:
logger.log_trace() logger.log_trace()
errstring += _("\nCompile/Run error when loading cmdset '{path}': \"{error}\".") errstring += _ERROR_CMDSET_EXCEPTION
errstring = errstring.format(path=python_path, error=e) if _IN_GAME_ERRORS:
errstring = errstring.format(path=python_path, traceback=format_exc(), timestamp=logger.timeformat())
else:
errstring = errstring.format(path=python_path, traceback=err, timestamp=logger.timeformat())
break break
if errstring: if errstring:
@ -189,7 +222,7 @@ def import_cmdset(path, cmdsetobj, emit_to_obj=None, no_logging=False):
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"): if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
emit_to_obj.msg(errstring) emit_to_obj.msg(errstring)
err_cmdset = _ErrorCmdSet() err_cmdset = _ErrorCmdSet()
err_cmdset.errmessage = errstring + _("\n (See log for details.)") err_cmdset.errmessage = errstring
return err_cmdset return err_cmdset
# classes # classes

View file

@ -23,6 +23,9 @@ import sys
# This is the name of your game. Make it catchy! # This is the name of your game. Make it catchy!
SERVERNAME = "Evennia" SERVERNAME = "Evennia"
# Lockdown mode will cut off the game from any external connections
# and only allow connections from localhost. Requires a cold reboot.
LOCKDOWN_MODE = False
# Activate telnet service # Activate telnet service
TELNET_ENABLED = True TELNET_ENABLED = True
# A list of ports the Evennia telnet server listens on Can be one or many. # A list of ports the Evennia telnet server listens on Can be one or many.
@ -204,6 +207,11 @@ MAX_CONNECTION_RATE = 2
MAX_COMMAND_RATE = 80 MAX_COMMAND_RATE = 80
# The warning to echo back to users if they send commands too fast # The warning to echo back to users if they send commands too fast
COMMAND_RATE_WARNING ="You entered commands too fast. Wait a moment and try again." COMMAND_RATE_WARNING ="You entered commands too fast. Wait a moment and try again."
# If this is true, errors and tracebacks from the engine will be
# echoed as text in-game as well as to the log. This can speed up
# debugging. Showing full tracebacks to regular users could be a
# security problem - this should *not* be active in a production game!
IN_GAME_ERRORS = False
###################################################################### ######################################################################
# Evennia Database config # Evennia Database config