Fix logging system.
patch f7068578a6b2806e38c5c5bfc1c03b8e59a14455
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Dec 20 01:50:56 CET 2023
* Fix logging system.
Remove logging options from tornado.log that were not working correctly in this setup
and use the new options from irgramd ("log_file" and "log_level").
Defer first logs to be included in log file opened later.
Improve option error handling.
diff -rN -u old-irgramd/irgramd new-irgramd/irgramd
--- old-irgramd/irgramd 2024-05-18 07:34:41.846030352 +0200
+++ new-irgramd/irgramd 2024-05-18 07:34:41.850030361 +0200
@@ -20,6 +20,7 @@
from irc import IRCHandler
from telegram import TelegramHandler
+from utils import parse_loglevel
# IRC Telegram Daemon
@@ -65,7 +66,16 @@
# Main Execution
if __name__ == '__main__':
- logger = logging.getLogger()
+ # Remove tornado.log options (ugly hacks but these must not be defined)
+ tornado.options.options.logging = None
+ tornado_log_options = tuple(x for x in tornado.options.options._options.keys() if x != 'help' and x != 'logging')
+ for opt in tornado_log_options:
+ del tornado.options.options._options[opt]
+ # and reuse "--logging" to document empty "--" ;)
+ tornado.options.options._options['logging'].help = 'Stop parsing options'
+ for att in ('name', 'metavar', 'group_name', 'default'):
+ setattr(tornado.options.options._options['logging'], att, '')
+ # Define irgramd options
tornado.options.define('api_hash', default=None, metavar='HASH', help='Telegram API Hash for your account (obtained from https://my.telegram.org/apps)')
tornado.options.define('api_id', type=int, default=None, metavar='ID', help='Telegram API ID for your account (obtained from https://my.telegram.org/apps)')
tornado.options.define('ask_code', default=False, help='Ask authentication code (sent by Telegram) in console instead of "code" service command in IRC')
@@ -82,6 +92,8 @@
tornado.options.define('irc_nicks', type=str, multiple=True, metavar='nick,..', help='List of nicks allowed for IRC, if `pam` and optionally `pam_group` are set, PAM authentication will be used instead')
tornado.options.define('irc_password', default='', metavar='PASSWORD', help='Password for IRC authentication, if `pam` is set, PAM authentication will be used instead')
tornado.options.define('irc_port', type=int, default=None, metavar='PORT', help='Port to listen on for IRC. (default 6667, default with TLS 6697)')
+ tornado.options.define('log_file', default=None, metavar='PATH', help='File where logs are appended, if not set will be stderr')
+ tornado.options.define('log_level', default='INFO', metavar='DEBUG|INFO|WARNING|ERROR|CRITICAL|NONE', help='The log level (and any higher to it) that will be logged')
tornado.options.define('media_dir', default=None, metavar='PATH', help='Directory where Telegram media files are downloaded, default "media" in `config_dir`')
tornado.options.define('media_url', default=None, metavar='BASE_URL', help='Base URL for media files, should be configured in the external (to irgramd) webserver')
tornado.options.define('pam', default=False, help='Use PAM for IRC authentication, if not set you should set `irc_password`')
@@ -97,27 +109,51 @@
tornado.options.define('tls', default=False, help='Use TLS/SSL encrypted connection for IRC server')
tornado.options.define('tls_cert', default=None, metavar='CERTFILE', help='IRC server certificate chain for TLS/SSL, also can contain private key if not defined with `tls_key`')
tornado.options.define('tls_key', default=None, metavar='KEYFILE', help='IRC server private key for TLS/SSL')
- # parse cmd line first time to get --config and --config_dir
- tornado.options.parse_command_line()
+ try:
+ # parse cmd line first time to get --config and --config_dir
+ tornado.options.parse_command_line()
+ except Exception as exc:
+ print(exc)
+ exit(1)
config_file = os.path.expanduser(tornado.options.options.config)
config_dir = os.path.expanduser(tornado.options.options.config_dir)
if not os.path.exists(config_dir):
os.makedirs(config_dir)
- logger.info('Configuration Directory: %s', config_dir)
+ defered_logs = [(logging.INFO, 'Configuration Directory: %s', config_dir)]
if not os.path.isabs(config_file):
config_file = os.path.join(config_dir, config_file)
if os.path.isfile(config_file):
- logger.info('Using configuration file: %s', config_file)
- tornado.options.parse_config_file(config_file)
+ defered_logs.append((logging.INFO, 'Using configuration file: %s', config_file))
+ try:
+ tornado.options.parse_config_file(config_file)
+ except Exception as exc:
+ print(exc)
+ exit(1)
else:
- logger.warning('Configuration file not present, using only command line options and defaults')
+ defered_logs.append((logging.WARNING, 'Configuration file not present, using only command line options and defaults'))
# parse cmd line second time to override file options
tornado.options.parse_command_line()
options = tornado.options.options.as_dict()
options['config_dir'] = config_dir
+ # configure logging
+ loglevel = parse_loglevel(options['log_level'])
+ if loglevel == False:
+ print("Option 'log_level' requires one of these values: {}".format(tornado.options.options._options['log-level'].metavar))
+ exit(1)
+ logger_formats = { 'datefmt':'%Y-%m-%d %H:%M:%S', 'format':'[%(levelname).1s %(asctime)s %(module)s:%(lineno)d] %(message)s' }
+ logger = logging.getLogger()
+ if options['log_file']:
+ logging.basicConfig(filename=options['log_file'], level=loglevel, **logger_formats)
+ else:
+ logging.basicConfig(level=loglevel, **logger_formats)
+
+ for log in defered_logs:
+ logger.log(*log)
+
+ # main loop
irc_server = IRCTelegramd(logger, options)
loop = asyncio.new_event_loop()
loop.run_until_complete(irc_server.run(options))
diff -rN -u old-irgramd/utils.py new-irgramd/utils.py
--- old-irgramd/utils.py 2024-05-18 07:34:41.850030361 +0200
+++ new-irgramd/utils.py 2024-05-18 07:34:41.850030361 +0200
@@ -13,6 +13,7 @@
import datetime
import zoneinfo
import difflib
+import logging
# Constants
@@ -214,3 +215,13 @@
def format_timestamp(format, tz, date):
date_local = date.astimezone(zoneinfo.ZoneInfo(tz))
return date_local.strftime(format)
+
+def parse_loglevel(level):
+ levelu = level.upper()
+ if levelu == 'NONE':
+ l = None
+ elif levelu in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'):
+ l = getattr(logging, levelu)
+ else:
+ l = False
+ return l