2019-07-06 22:40:52 +00:00
|
|
|
import logging
|
2024-10-24 16:01:41 +00:00
|
|
|
import sys
|
2023-04-09 14:48:18 +00:00
|
|
|
from logging import Formatter
|
|
|
|
from logging.handlers import RotatingFileHandler, SysLogHandler
|
2024-10-22 16:22:23 +00:00
|
|
|
from pathlib import Path
|
2019-07-06 22:40:52 +00:00
|
|
|
|
2022-09-18 11:20:36 +00:00
|
|
|
from freqtrade.constants import Config
|
2019-12-30 14:02:17 +00:00
|
|
|
from freqtrade.exceptions import OperationalException
|
2023-04-09 14:48:18 +00:00
|
|
|
from freqtrade.loggers.buffering_handler import FTBufferingHandler
|
2023-06-09 04:59:08 +00:00
|
|
|
from freqtrade.loggers.set_log_levels import set_loggers
|
2023-04-09 14:48:18 +00:00
|
|
|
from freqtrade.loggers.std_err_stream_handler import FTStdErrStreamHandler
|
2022-01-16 14:37:00 +00:00
|
|
|
|
|
|
|
|
2019-07-06 22:40:52 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2024-05-12 14:30:42 +00:00
|
|
|
LOGFORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
2019-07-06 22:40:52 +00:00
|
|
|
|
2020-08-14 13:44:36 +00:00
|
|
|
# Initialize bufferhandler - will be used for /log endpoints
|
2022-01-16 14:37:00 +00:00
|
|
|
bufferHandler = FTBufferingHandler(1000)
|
2020-08-14 13:44:36 +00:00
|
|
|
bufferHandler.setFormatter(Formatter(LOGFORMAT))
|
|
|
|
|
2019-07-06 22:40:52 +00:00
|
|
|
|
2020-11-25 13:31:34 +00:00
|
|
|
def get_existing_handlers(handlertype):
|
|
|
|
"""
|
|
|
|
Returns Existing handler or None (if the handler has not yet been added to the root handlers).
|
|
|
|
"""
|
|
|
|
return next((h for h in logging.root.handlers if isinstance(h, handlertype)), None)
|
|
|
|
|
|
|
|
|
2020-08-14 12:41:46 +00:00
|
|
|
def setup_logging_pre() -> None:
|
|
|
|
"""
|
2020-08-14 18:12:59 +00:00
|
|
|
Early setup for logging.
|
|
|
|
Uses INFO loglevel and only the Streamhandler.
|
2020-08-27 09:37:20 +00:00
|
|
|
Early messages (before proper logging setup) will therefore only be sent to additional
|
|
|
|
logging handlers after the real initialization, because we don't know which
|
|
|
|
ones the user desires beforehand.
|
2020-08-14 12:41:46 +00:00
|
|
|
"""
|
2020-08-14 12:53:21 +00:00
|
|
|
logging.basicConfig(
|
2024-05-12 14:30:42 +00:00
|
|
|
level=logging.INFO, format=LOGFORMAT, handlers=[FTStdErrStreamHandler(), bufferHandler]
|
2020-08-14 12:53:21 +00:00
|
|
|
)
|
2020-08-14 12:41:46 +00:00
|
|
|
|
|
|
|
|
2022-09-18 11:20:36 +00:00
|
|
|
def setup_logging(config: Config) -> None:
|
2019-07-06 22:40:52 +00:00
|
|
|
"""
|
2019-07-06 23:53:13 +00:00
|
|
|
Process -v/--verbose, --logfile options
|
2019-07-06 22:40:52 +00:00
|
|
|
"""
|
|
|
|
# Log level
|
2024-05-12 14:30:42 +00:00
|
|
|
verbosity = config["verbosity"]
|
2020-08-14 13:44:36 +00:00
|
|
|
logging.root.addHandler(bufferHandler)
|
2019-07-06 22:40:52 +00:00
|
|
|
|
2024-05-12 14:30:42 +00:00
|
|
|
logfile = config.get("logfile")
|
2020-08-14 17:50:56 +00:00
|
|
|
|
2019-10-25 06:50:37 +00:00
|
|
|
if logfile:
|
2024-05-12 14:30:42 +00:00
|
|
|
s = logfile.split(":")
|
|
|
|
if s[0] == "syslog":
|
2019-10-25 06:50:37 +00:00
|
|
|
# Address can be either a string (socket filename) for Unix domain socket or
|
|
|
|
# a tuple (hostname, port) for UDP socket.
|
|
|
|
# Address can be omitted (i.e. simple 'syslog' used as the value of
|
|
|
|
# config['logfilename']), which defaults to '/dev/log', applicable for most
|
|
|
|
# of the systems.
|
2024-05-12 14:30:42 +00:00
|
|
|
address = (s[1], int(s[2])) if len(s) > 2 else s[1] if len(s) > 1 else "/dev/log"
|
2020-11-25 13:31:34 +00:00
|
|
|
handler_sl = get_existing_handlers(SysLogHandler)
|
|
|
|
if handler_sl:
|
|
|
|
logging.root.removeHandler(handler_sl)
|
|
|
|
handler_sl = SysLogHandler(address=address)
|
2019-10-25 06:50:37 +00:00
|
|
|
# No datetime field for logging into syslog, to allow syslog
|
|
|
|
# to perform reduction of repeating messages if this is set in the
|
|
|
|
# syslog config. The messages should be equal for this.
|
2024-05-12 14:30:42 +00:00
|
|
|
handler_sl.setFormatter(Formatter("%(name)s - %(levelname)s - %(message)s"))
|
2020-11-25 13:31:34 +00:00
|
|
|
logging.root.addHandler(handler_sl)
|
2024-05-12 14:30:42 +00:00
|
|
|
elif s[0] == "journald": # pragma: no cover
|
2019-10-25 06:50:37 +00:00
|
|
|
try:
|
2023-02-19 18:30:27 +00:00
|
|
|
from cysystemd.journal import JournaldLogHandler
|
2019-10-25 06:50:37 +00:00
|
|
|
except ImportError:
|
2024-05-12 14:30:42 +00:00
|
|
|
raise OperationalException(
|
|
|
|
"You need the cysystemd python package be installed in "
|
|
|
|
"order to use logging to journald."
|
|
|
|
)
|
2020-11-25 13:31:34 +00:00
|
|
|
handler_jd = get_existing_handlers(JournaldLogHandler)
|
|
|
|
if handler_jd:
|
|
|
|
logging.root.removeHandler(handler_jd)
|
2020-08-15 06:15:18 +00:00
|
|
|
handler_jd = JournaldLogHandler()
|
2019-10-25 06:50:37 +00:00
|
|
|
# No datetime field for logging into journald, to allow syslog
|
|
|
|
# to perform reduction of repeating messages if this is set in the
|
|
|
|
# syslog config. The messages should be equal for this.
|
2024-05-12 14:30:42 +00:00
|
|
|
handler_jd.setFormatter(Formatter("%(name)s - %(levelname)s - %(message)s"))
|
2020-08-15 06:15:18 +00:00
|
|
|
logging.root.addHandler(handler_jd)
|
2019-10-25 06:50:37 +00:00
|
|
|
else:
|
2020-11-25 13:31:34 +00:00
|
|
|
handler_rf = get_existing_handlers(RotatingFileHandler)
|
|
|
|
if handler_rf:
|
|
|
|
logging.root.removeHandler(handler_rf)
|
2024-10-24 16:01:41 +00:00
|
|
|
try:
|
|
|
|
logfile_path = Path(logfile)
|
|
|
|
logfile_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
handler_rf = RotatingFileHandler(
|
|
|
|
logfile_path,
|
|
|
|
maxBytes=1024 * 1024 * 10, # 10Mb
|
|
|
|
backupCount=10,
|
|
|
|
)
|
|
|
|
except PermissionError:
|
|
|
|
logger.error(
|
|
|
|
f'Failed to create or access log file "{logfile_path.absolute()}". '
|
|
|
|
"Please make sure you have the write permission to the log file or its parent "
|
|
|
|
"directories. If you're running freqtrade using docker, you see this error "
|
|
|
|
"message probably because you've logged in as the root user, please switch to "
|
|
|
|
"non-root user, delete and recreate the directories you need, and then try "
|
|
|
|
"again."
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
2020-08-15 06:15:18 +00:00
|
|
|
handler_rf.setFormatter(Formatter(LOGFORMAT))
|
|
|
|
logging.root.addHandler(handler_rf)
|
2020-08-14 12:41:46 +00:00
|
|
|
|
|
|
|
logging.root.setLevel(logging.INFO if verbosity < 1 else logging.DEBUG)
|
2024-05-12 14:30:42 +00:00
|
|
|
set_loggers(verbosity, config.get("api_server", {}).get("verbosity", "info"))
|
2020-08-14 12:53:21 +00:00
|
|
|
|
2024-05-12 14:30:42 +00:00
|
|
|
logger.info("Verbosity set to %s", verbosity)
|