Add log-tailing to launcher options

This commit is contained in:
Griatch 2018-01-19 23:47:14 +01:00
parent ffa860eaad
commit 9aa4bec2ef
4 changed files with 125 additions and 8 deletions

View file

@ -75,6 +75,7 @@ PPROFILER_LOGFILE = None
TEST_MODE = False
ENFORCED_SETTING = False
TAIL_LOG_MODE = False
# communication constants
@ -88,7 +89,7 @@ SSTART = chr(15) # server start
PSHUTD = chr(16) # portal (+server) shutdown
SSHUTD = chr(17) # server-only shutdown
PSTATUS = chr(18) # ping server or portal status
SRESET = chr(19) # shutdown server in reset mode
SRESET = chr(19) # shutdown server in reset mode
# requirements
PYTHON_MIN = '2.7'
@ -624,10 +625,8 @@ def _get_twistd_cmdline(pprofiler, sprofiler):
"""
portal_cmd = [TWISTED_BINARY,
"--logfile={}".format(PORTAL_LOGFILE),
"--python={}".format(PORTAL_PY_FILE)]
server_cmd = [TWISTED_BINARY,
"--logfile={}".format(SERVER_LOGFILE),
"--python={}".format(SERVER_PY_FILE)]
if os.name != 'nt':
@ -915,6 +914,104 @@ def query_info():
send_instruction(PSTATUS, None, _portal_running, _portal_not_running)
def tail_server_log(filename, rate=1):
"""
Tail the server logfile interactively, printing to stdout
When first starting, this will display the tail of the log file. After
that it will poll the log file repeatedly and display changes.
Args:
filename (str): Path to log file.
rate (int, optional): How often to poll the log file.
"""
def _file_changed(filename, prev_size):
"Get size of file in bytes, get diff compared with previous size"
new_size = os.path.getsize(filename)
return new_size != prev_size, new_size
def _get_new_lines(filehandle, old_linecount):
"count lines, get the ones not counted before"
def _block(filehandle, size=65536):
"File block generator for quick traversal"
while True:
dat = filehandle.read(size)
if not dat:
break
yield dat
# count number of lines in file
new_linecount = sum(blck.count("\n") for blck in _block(filehandle))
if new_linecount < old_linecount:
# this could happen if the file was manually deleted or edited
print("Log file has shrunk. Restart log reader.")
sys.exit()
lines_to_get = max(0, new_linecount - old_linecount)
if not lines_to_get:
return [], old_linecount
lines_found = []
buffer_size = 4098
block_count = -1
while len(lines_found) < lines_to_get:
try:
# scan backwards in file, starting from the end
filehandle.seek(block_count * buffer_size, os.SEEK_END)
except IOError:
# file too small for current seek, include entire file
filehandle.seek(0)
lines_found = filehandle.readlines()
break
lines_found = filehandle.readlines()
block_count -= 1
# only actually return the new lines
return lines_found[-lines_to_get:], new_linecount
def _tail_file(filename, file_size, line_count, max_lines=None):
"""This will cycle repeatedly, printing new lines"""
# poll for changes
has_changed, file_size = _file_changed(filename, file_size)
if has_changed:
try:
with open(filename, 'r') as filehandle:
new_lines, line_count = _get_new_lines(filehandle, line_count)
except IOError:
# the log file might not exist yet. Wait a little, then try again ...
pass
else:
if max_lines:
# first startup
print(" Tailing logfile {} ...".format(filename))
new_lines = new_lines[-max_lines:]
# print to stdout without line break (log has its own line feeds)
sys.stdout.write("".join(new_lines))
sys.stdout.flush()
# set up the next poll
reactor.callLater(rate, _tail_file, filename, file_size, line_count, max_lines=100)
reactor.callLater(0, _tail_file, filename, 0, 0, max_lines=20)
reactor.run()
# ------------------------------------------------------------
#
# Environment setup
#
# ------------------------------------------------------------
def evennia_version():
"""
Get the Evennia version info from the main package.
@ -1572,6 +1669,7 @@ def main():
Run the evennia launcher main program.
"""
global TAIL_LOG_MODE
# set up argument parser
@ -1583,6 +1681,9 @@ def main():
parser.add_argument(
'--init', action='store', dest="init", metavar="<gamename>",
help="Creates a new gamedir 'name' at current location.")
parser.add_argument(
'--log', '-l', action='store_true', dest='tail_log', default=False,
help="Tail the server logfile to standard out.")
parser.add_argument(
'--list', nargs='+', action='store', dest='listsetting', metavar="all|<key>",
help=("List values for one or more server settings. Use 'all' to \n list all "
@ -1623,6 +1724,8 @@ def main():
# make sure we have everything
check_main_evennia_dependencies()
TAIL_LOG_MODE = args.tail_log
if not args:
# show help pane
print(CMDLINE_HELP)
@ -1740,10 +1843,19 @@ def main():
args = ", ".join(args)
kwargs = ", ".join(["--%s" % kw for kw in kwargs])
print(ERROR_INPUT.format(traceback=exc, args=args, kwargs=kwargs))
else:
elif not TAIL_LOG_MODE:
# no input; print evennia info
print(ABOUT_INFO)
if TAIL_LOG_MODE:
# start the log-tail last
if not SERVER_LOGFILE:
init_game_directory(CURRENT_DIR, check_db=False)
tail_server_log(SERVER_LOGFILE)
if __name__ == '__main__':
# start Evennia from the command line

View file

@ -184,8 +184,8 @@ class Portal(object):
application = service.Application('Portal')
# custom logging
logfile = logger.WeeklyLogFile(os.path.basename(settings.PORTAL_LOG_FILE),
os.path.dirname(settings.PORTAL_LOG_FILE))
logfile = logger.WeeklyLogFile(os.path.basename(settings.SERVER_LOG_FILE),
os.path.dirname(settings.SERVER_LOG_FILE))
application.setComponent(ILogObserver, logger.PortalLogObserver(logfile).emit)
# The main Portal server program. This sets up the database

View file

@ -534,8 +534,8 @@ ServerConfig.objects.conf("server_starting_mode", True)
application = service.Application('Evennia')
# custom logging
logfile = logger.WeeklyLogFile(os.path.basename(settings.PORTAL_LOG_FILE),
os.path.dirname(settings.PORTAL_LOG_FILE))
logfile = logger.WeeklyLogFile(os.path.basename(settings.SERVER_LOG_FILE),
os.path.dirname(settings.SERVER_LOG_FILE))
application.setComponent(ILogObserver, logger.ServerLogObserver(logfile).emit)
# The main evennia server program. This sets up the database

View file

@ -187,6 +187,11 @@ log_errmsg = log_err
def log_server(servermsg):
"""
This is for the Portal to log captured Server stdout messages (it's
usually only used during startup, before Server log is open)
"""
try:
servermsg = str(servermsg)
except Exception as e: