mirror of
https://github.com/codex-storage/deluge.git
synced 2025-02-27 18:40:36 +00:00
[#1949] [UI] Allow setting max size for rotating log file
This commit is contained in:
parent
c90af1ce6c
commit
6300f9154a
@ -13,8 +13,10 @@ import base64
|
||||
import gettext
|
||||
import locale
|
||||
import logging
|
||||
import numbers
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
@ -465,6 +467,82 @@ def fdate(seconds, date_only=False, precision_secs=False):
|
||||
return time.strftime("%x %H:%M", time.localtime(seconds))
|
||||
|
||||
|
||||
def tokenize(text):
|
||||
"""
|
||||
Tokenize a text into numbers and strings.
|
||||
|
||||
Args:
|
||||
text (str): The text to tokenize (a string).
|
||||
|
||||
Returns:
|
||||
list: A list of strings and/or numbers.
|
||||
|
||||
This function is used to implement robust tokenization of user input
|
||||
It automatically coerces integer and floating point numbers, ignores
|
||||
whitespace and knows how to separate numbers from strings even without
|
||||
whitespace.
|
||||
"""
|
||||
tokenized_input = []
|
||||
for token in re.split(r'(\d+(?:\.\d+)?)', text):
|
||||
token = token.strip()
|
||||
if re.match(r'\d+\.\d+', token):
|
||||
tokenized_input.append(float(token))
|
||||
elif token.isdigit():
|
||||
tokenized_input.append(int(token))
|
||||
elif token:
|
||||
tokenized_input.append(token)
|
||||
return tokenized_input
|
||||
|
||||
|
||||
size_units = (dict(prefix='b', divider=1, singular='byte', plural='bytes'),
|
||||
dict(prefix='KiB', divider=1024**1),
|
||||
dict(prefix='MiB', divider=1024**2),
|
||||
dict(prefix='GiB', divider=1024**3),
|
||||
dict(prefix='TiB', divider=1024**4),
|
||||
dict(prefix='PiB', divider=1024**5),
|
||||
dict(prefix='KB', divider=1000**1),
|
||||
dict(prefix='MB', divider=1000**2),
|
||||
dict(prefix='GB', divider=1000**3),
|
||||
dict(prefix='TB', divider=1000**4),
|
||||
dict(prefix='PB', divider=1000**5),
|
||||
dict(prefix='m', divider=1000**2))
|
||||
|
||||
|
||||
class InvalidSize(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def parse_human_size(size):
|
||||
"""
|
||||
Parse a human readable data size and return the number of bytes.
|
||||
|
||||
Args:
|
||||
size (str): The human readable file size to parse (a string).
|
||||
|
||||
Returns:
|
||||
int: The corresponding size in bytes.
|
||||
|
||||
Raises:
|
||||
InvalidSize: when the input can't be parsed.
|
||||
|
||||
"""
|
||||
tokens = tokenize(size)
|
||||
if tokens and isinstance(tokens[0], numbers.Number):
|
||||
# If the input contains only a number, it's assumed to be the number of bytes.
|
||||
if len(tokens) == 1:
|
||||
return int(tokens[0])
|
||||
# Otherwise we expect to find two tokens: A number and a unit.
|
||||
if len(tokens) == 2 and isinstance(tokens[1], basestring):
|
||||
normalized_unit = tokens[1].lower()
|
||||
# Try to match the first letter of the unit.
|
||||
for unit in size_units:
|
||||
if normalized_unit.startswith(unit['prefix'].lower()):
|
||||
return int(tokens[0] * unit['divider'])
|
||||
# We failed to parse the size specification.
|
||||
msg = "Failed to parse size! (input %r was tokenized as %r)"
|
||||
raise InvalidSize(msg % (size, tokens))
|
||||
|
||||
|
||||
def is_url(url):
|
||||
"""
|
||||
A simple test to check if the URL is valid
|
||||
|
@ -107,15 +107,19 @@ levels = {
|
||||
}
|
||||
|
||||
|
||||
def setup_logger(level="error", filename=None, filemode="w"):
|
||||
def setup_logger(level="error", filename=None, filemode="w", logrotate=None):
|
||||
"""
|
||||
Sets up the basic logger and if `:param:filename` is set, then it will log
|
||||
to that file instead of stdout.
|
||||
|
||||
:param level: str, the level to log
|
||||
:param filename: str, the file to log to
|
||||
Args:
|
||||
level (str): The log level to use (Default: "error")
|
||||
filename (str, optional): The log filename. Default is None meaning log
|
||||
to terminal
|
||||
filemode (str): The filemode to use when opening the log file
|
||||
logrotate (int, optional): The size of the logfile in bytes when enabling
|
||||
log rotation (Default is None meaning disabled)
|
||||
"""
|
||||
|
||||
if logging.getLoggerClass() is not Logging:
|
||||
logging.setLoggerClass(Logging)
|
||||
logging.addLevelName(5, "TRACE")
|
||||
@ -125,19 +129,16 @@ def setup_logger(level="error", filename=None, filemode="w"):
|
||||
|
||||
root_logger = logging.getLogger()
|
||||
|
||||
if filename and filemode == "a":
|
||||
if filename and logrotate:
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
filename, filemode,
|
||||
maxBytes=50 * 1024 * 1024, # 50 Mb
|
||||
backupCount=3,
|
||||
encoding="utf-8",
|
||||
delay=0
|
||||
filename, maxBytes=logrotate,
|
||||
backupCount=5, encoding="utf-8"
|
||||
)
|
||||
elif filename and filemode == "w":
|
||||
handler = getattr(
|
||||
logging.handlers, "WatchedFileHandler", logging.FileHandler)(
|
||||
filename, filemode, "utf-8", delay=0
|
||||
)
|
||||
filename, mode=filemode, encoding="utf-8"
|
||||
)
|
||||
else:
|
||||
handler = logging.StreamHandler(stream=sys.stdout)
|
||||
|
||||
|
@ -74,3 +74,21 @@ class CommonTestCase(unittest.TestCase):
|
||||
self.failUnless(VersionSplit("1.4.0") > VersionSplit("1.4.0.dev123"))
|
||||
self.failUnless(VersionSplit("1.4.0.dev1") < VersionSplit("1.4.0"))
|
||||
self.failUnless(VersionSplit("1.4.0a1") < VersionSplit("1.4.0"))
|
||||
|
||||
def test_parse_human_size(self):
|
||||
from deluge.common import parse_human_size
|
||||
sizes = [("1", 1),
|
||||
("10 bytes", 10),
|
||||
("2048 bytes", 2048),
|
||||
("1MiB", 2**(10 * 2)),
|
||||
("1 MiB", 2**(10 * 2)),
|
||||
("1 GiB", 2**(10 * 3)),
|
||||
("1 GiB", 2**(10 * 3)),
|
||||
("1M", 10**6),
|
||||
("1MB", 10**6),
|
||||
("1 GB", 10**9),
|
||||
("1 TB", 10**12)]
|
||||
|
||||
for human_size, byte_size in sizes:
|
||||
parsed = parse_human_size(human_size)
|
||||
self.assertEquals(parsed, byte_size, "Mismatch when converting '%s'" % human_size)
|
||||
|
@ -14,21 +14,21 @@ import platform
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import deluge.common
|
||||
import deluge.configmanager
|
||||
import deluge.log
|
||||
from deluge import common
|
||||
from deluge.log import setup_logger
|
||||
|
||||
|
||||
def get_version():
|
||||
version_str = "%s\n" % (deluge.common.get_version())
|
||||
version_str = "%s\n" % (common.get_version())
|
||||
try:
|
||||
from deluge._libtorrent import lt
|
||||
version_str += "libtorrent: %s\n" % lt.version
|
||||
except ImportError:
|
||||
pass
|
||||
version_str += "Python: %s\n" % platform.python_version()
|
||||
version_str += "OS: %s %s\n" % (platform.system(), " ".join(deluge.common.get_os_version()))
|
||||
version_str += "OS: %s %s\n" % (platform.system(), " ".join(common.get_os_version()))
|
||||
return version_str
|
||||
|
||||
|
||||
@ -93,6 +93,7 @@ class BaseArgParser(argparse.ArgumentParser):
|
||||
kwargs["formatter_class"] = lambda prog: DelugeTextHelpFormatter(prog, max_help_position=33, width=90)
|
||||
super(BaseArgParser, self).__init__(*args, add_help=False, **kwargs)
|
||||
|
||||
self.common_setup = False
|
||||
self.group = self.add_argument_group('Common Options')
|
||||
self.group.add_argument('--version', action='version', version='%(prog)s ' + get_version(),
|
||||
help="Show program's version number and exit")
|
||||
@ -104,37 +105,43 @@ class BaseArgParser(argparse.ArgumentParser):
|
||||
help="Set the log level: %s" % ", ".join(deluge.log.levels))
|
||||
self.group.add_argument("-q", "--quiet", action="store_true", default=False,
|
||||
help="Sets the log level to 'none', this is the same as `-L none`")
|
||||
self.group.add_argument("-r", "--rotate-logs", action="store_true", default=False,
|
||||
help="Rotate logfiles.")
|
||||
self.group.add_argument("--log-rotate", action="store", nargs="?", const="50M",
|
||||
help="Enable logfile rotation (optional max file size, default: %(const)s)."
|
||||
"Log file rotate count is 5")
|
||||
self.group.add_argument("--profile", metavar="<results file>", action="store", nargs="?", default=False,
|
||||
help="Profile %(prog)s with cProfile. Prints results to stdout"
|
||||
"unless a filename is specififed.")
|
||||
"unless a filename is specififed")
|
||||
self.group.add_argument("-h", "--help", action=HelpAction, help='Show this help message and exit')
|
||||
|
||||
def parse_args(self, *args):
|
||||
options, remaining = super(BaseArgParser, self).parse_known_args(*args)
|
||||
options.remaining = remaining
|
||||
|
||||
# Setup the logger
|
||||
if options.quiet:
|
||||
options.loglevel = "none"
|
||||
if options.loglevel:
|
||||
options.loglevel = options.loglevel.lower()
|
||||
if not self.common_setup:
|
||||
self.common_setup = True
|
||||
# Setup the logger
|
||||
if options.quiet:
|
||||
options.loglevel = "none"
|
||||
if options.loglevel:
|
||||
options.loglevel = options.loglevel.lower()
|
||||
|
||||
logfile_mode = 'w'
|
||||
if options.rotate_logs:
|
||||
logfile_mode = 'a'
|
||||
logfile_mode = 'w'
|
||||
logrotate = options.log_rotate
|
||||
if options.log_rotate:
|
||||
logfile_mode = 'a'
|
||||
logrotate = common.parse_human_size(options.log_rotate)
|
||||
|
||||
# Setup the logger
|
||||
setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode)
|
||||
# Setup the logger
|
||||
setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode,
|
||||
logrotate=logrotate)
|
||||
|
||||
if options.config:
|
||||
if not deluge.configmanager.set_config_dir(options.config):
|
||||
log = logging.getLogger(__name__)
|
||||
log.error("There was an error setting the config dir! Exiting..")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not os.path.exists(deluge.common.get_default_config_dir()):
|
||||
os.makedirs(deluge.common.get_default_config_dir())
|
||||
if options.config:
|
||||
if not deluge.configmanager.set_config_dir(options.config):
|
||||
log = logging.getLogger(__name__)
|
||||
log.error("There was an error setting the config dir! Exiting..")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not os.path.exists(common.get_default_config_dir()):
|
||||
os.makedirs(common.get_default_config_dir())
|
||||
|
||||
return options
|
||||
|
@ -24,6 +24,7 @@ log = logging.getLogger(__name__)
|
||||
# defined in setup.py
|
||||
#
|
||||
|
||||
|
||||
def load_commands(command_dir):
|
||||
|
||||
def get_command(name):
|
||||
|
Loading…
x
Reference in New Issue
Block a user