Make log rotation also limited by size and controllable from settings. Resolves #2041

This commit is contained in:
Griatch 2020-01-29 23:16:53 +01:00
parent f124b3510b
commit ff16eb1bfe
13 changed files with 98 additions and 40 deletions

View file

@ -46,6 +46,8 @@ without arguments starts a full interactive Python console.
the new `raise_exception` boolean if ranting to raise KeyError on a missing key.
- Moved behavior of unmodified `Command` and `MuxCommand` `.func()` to new
`.get_command_info()` method for easier overloading and access. (Volund)
- Removed unused `CYCLE_LOGFILES` setting. Added `SERVER_LOG_DAY_ROTATION`
and `SERVER_LOG_MAX_SIZE` (and equivalent for PORTAL) to control log rotation.
## Evennia 0.9 (2018-2019)

View file

@ -1062,7 +1062,7 @@ class TestBuilding(CommandTest):
# Test valid dbref ranges with no search term
id1 = self.obj1.id
id2 = self.obj2.id
maxid = ObjectDB.objects.latest('id').id
maxid = ObjectDB.objects.latest("id").id
maxdiff = maxid - id1 + 1
mdiff = id2 - id1 + 1

View file

@ -441,11 +441,11 @@ class TBBasicTurnHandler(DefaultScript):
"""
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
character.db.combat_actionsleft = (
0
) # Actions remaining - start of turn adds to this, turn ends when it reaches 0
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
)
character.db.combat_turnhandler = (
self
) # Add a reference to this turn handler script to the character
self # Add a reference to this turn handler script to the character
)
character.db.combat_lastaction = "null" # Track last action taken in combat
def start_turn(self, character):

View file

@ -438,11 +438,11 @@ class TBEquipTurnHandler(DefaultScript):
"""
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
character.db.combat_actionsleft = (
0
) # Actions remaining - start of turn adds to this, turn ends when it reaches 0
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
)
character.db.combat_turnhandler = (
self
) # Add a reference to this turn handler script to the character
self # Add a reference to this turn handler script to the character
)
character.db.combat_lastaction = "null" # Track last action taken in combat
def start_turn(self, character):
@ -553,8 +553,8 @@ class TBEWeapon(DefaultObject):
self.db.damage_range = (15, 25) # Minimum and maximum damage on hit
self.db.accuracy_bonus = 0 # Bonus to attack rolls (or penalty if negative)
self.db.weapon_type_name = (
"weapon"
) # Single word for weapon - I.E. "dagger", "staff", "scimitar"
"weapon" # Single word for weapon - I.E. "dagger", "staff", "scimitar"
)
def at_drop(self, dropper):
"""

View file

@ -718,11 +718,11 @@ class TBItemsTurnHandler(DefaultScript):
"""
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
character.db.combat_actionsleft = (
0
) # Actions remaining - start of turn adds to this, turn ends when it reaches 0
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
)
character.db.combat_turnhandler = (
self
) # Add a reference to this turn handler script to the character
self # Add a reference to this turn handler script to the character
)
character.db.combat_lastaction = "null" # Track last action taken in combat
def start_turn(self, character):

View file

@ -470,11 +470,11 @@ class TBMagicTurnHandler(DefaultScript):
"""
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
character.db.combat_actionsleft = (
0
) # Actions remaining - start of turn adds to this, turn ends when it reaches 0
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
)
character.db.combat_turnhandler = (
self
) # Add a reference to this turn handler script to the character
self # Add a reference to this turn handler script to the character
)
character.db.combat_lastaction = "null" # Track last action taken in combat
def start_turn(self, character):

View file

@ -674,11 +674,11 @@ class TBRangeTurnHandler(DefaultScript):
"""
combat_cleanup(character) # Clean up leftover combat attributes beforehand, just in case.
character.db.combat_actionsleft = (
0
) # Actions remaining - start of turn adds to this, turn ends when it reaches 0
0 # Actions remaining - start of turn adds to this, turn ends when it reaches 0
)
character.db.combat_turnhandler = (
self
) # Add a reference to this turn handler script to the character
self # Add a reference to this turn handler script to the character
)
character.db.combat_lastaction = "null" # Track last action taken in combat
def start_turn(self, character):

View file

@ -96,6 +96,12 @@ def check_errors(settings):
"must now be either None or a dict "
"specifying the properties of the channel to create."
)
if hasattr(settings, "CYCLE_LOGFILES"):
raise DeprecationWarning(
"settings.CYCLE_LOGFILES is unused and should be removed. "
"Use PORTAL/SERVER_LOG_DAY_ROTATION and PORTAL/SERVER_LOG_MAX_SIZE "
"to control log cycling."
)
def check_warnings(settings):

View file

@ -1153,7 +1153,7 @@ def tail_log_files(filename1, filename2, start_lines1=20, start_lines2=20, rate=
# this happens if the file was cycled or manually deleted/edited.
print(
" ** Log file {filename} has cycled or been edited. "
"Restarting log. ".format(filehandle.name)
"Restarting log. ".format(filename=filehandle.name)
)
new_linecount = 0
old_linecount = 0

View file

@ -213,7 +213,8 @@ application = service.Application("Portal")
if "--nodaemon" not in sys.argv:
logfile = logger.WeeklyLogFile(
os.path.basename(settings.PORTAL_LOG_FILE), os.path.dirname(settings.PORTAL_LOG_FILE)
os.path.basename(settings.PORTAL_LOG_FILE), os.path.dirname(settings.PORTAL_LOG_FILE),
day_rotation=settings.PORTAL_LOG_DAY_ROTATION, max_size=settings.PORTAL_LOG_MAX_SIZE
)
application.setComponent(ILogObserver, logger.PortalLogObserver(logfile).emit)

View file

@ -616,7 +616,8 @@ application = service.Application("Evennia")
if "--nodaemon" not in sys.argv:
# custom logging, but only if we are not running in interactive mode
logfile = logger.WeeklyLogFile(
os.path.basename(settings.SERVER_LOG_FILE), os.path.dirname(settings.SERVER_LOG_FILE)
os.path.basename(settings.SERVER_LOG_FILE), os.path.dirname(settings.SERVER_LOG_FILE),
day_rotation=settings.SERVER_LOG_DAY_ROTATION, max_size=settings.SERVER_LOG_MAX_SIZE
)
application.setComponent(ILogObserver, logger.ServerLogObserver(logfile).emit)

View file

@ -135,17 +135,19 @@ else:
break
os.chdir(os.pardir)
# Place to put log files
# Place to put log files, how often to rotate the log and how big each log file
# may become before rotating.
LOG_DIR = os.path.join(GAME_DIR, "server", "logs")
SERVER_LOG_FILE = os.path.join(LOG_DIR, "server.log")
SERVER_LOG_DAY_ROTATION = 7
SERVER_LOG_MAX_SIZE = 1000000
PORTAL_LOG_FILE = os.path.join(LOG_DIR, "portal.log")
PORTAL_LOG_DAY_ROTATION = 7
PORTAL_LOG_MAX_SIZE = 1000000
# The http log is usually only for debugging since it's very spammy
HTTP_LOG_FILE = os.path.join(LOG_DIR, "http_requests.log")
# if this is set to the empty string, lockwarnings will be turned off.
LOCKWARNING_LOG_FILE = os.path.join(LOG_DIR, "lockwarnings.log")
# Rotate log files when server and/or portal stops. This will keep log
# file sizes down. Turn off to get ever growing log files and never
# lose log info.
CYCLE_LOGFILES = True
# Number of lines to append to rotating channel logs when they rotate
CHANNEL_LOG_NUM_TAIL_LINES = 20
# Max size (in bytes) of channel log files before they rotate

View file

@ -16,6 +16,7 @@ log_typemsg(). This is for historical, back-compatible reasons.
import os
import time
import glob
from datetime import datetime
from traceback import format_exc
from twisted.python import log, logfile
@ -76,33 +77,78 @@ def timeformat(when=None):
class WeeklyLogFile(logfile.DailyLogFile):
"""
Log file that rotates once per week. Overrides key methods to change format
Log file that rotates once per week by default. Overrides key methods to change format.
"""
day_rotation = 7
def __init__(self, name, directory, defaultMode=None, day_rotation=7, max_size=1000000):
"""
Args:
name (str): Name of log file.
directory (str): Directory holding the file.
defaultMode (str): Permissions used to create file. Defaults to
current permissions of this file if it exists.
day_rotation (int): How often to rotate the file.
max_size (int): Max size of log file before rotation (regardless of
time). Defaults to 1M.
"""
self.day_rotation = day_rotation
self.max_size = max_size
self.size = 0
logfile.DailyLogFile.__init__(self, name, directory, defaultMode=defaultMode)
def _openFile(self):
logfile.DailyLogFile._openFile(self)
self.size = self._file.tell()
def shouldRotate(self):
"""Rotate when the date has changed since last write"""
# all dates here are tuples (year, month, day)
now = self.toDate()
then = self.lastDate
return now[0] > then[0] or now[1] > then[1] or now[2] > (then[2] + self.day_rotation)
return (now[0] > then[0] or
now[1] > then[1] or
now[2] > (then[2] + self.day_rotation) or
self.size >= self.max_size)
def suffix(self, tupledate):
"""Return the suffix given a (year, month, day) tuple or unixtime.
Format changed to have 03 for march instead of 3 etc (retaining unix file order)
Format changed to have 03 for march instead of 3 etc (retaining unix
file order)
If we get duplicate suffixes in location (due to hitting size limit),
we append __1, __2 etc.
Examples:
server.log.2020_01_29
server.log.2020_01_29__1
server.log.2020_01_29__2
"""
try:
return "_".join(["{:02d}".format(part) for part in tupledate])
except Exception:
# try taking a float unixtime
return "_".join(["{:02d}".format(part) for part in self.toDate(tupledate)])
suffix = ""
copy_suffix = 0
while True:
try:
suffix = "_".join(["{:02d}".format(part) for part in tupledate])
except Exception:
# try taking a float unixtime
suffix = "_".join(["{:02d}".format(part) for part in self.toDate(tupledate)])
suffix += f"__{copy_suffix}" if copy_suffix else ""
if os.path.exists(f"{self.path}.{suffix}"):
# Append a higher copy_suffix to try to break the tie (starting from 2)
copy_suffix += 1
else:
break
return suffix
def write(self, data):
"Write data to log file"
logfile.BaseLogFile.write(self, data)
self.lastDate = max(self.lastDate, self.toDate())
self.size += len(data)
class PortalLogObserver(log.FileLogObserver):