"""Utilities related to logging."""
import contextlib
+import datetime
import logging
from logging.handlers import RotatingFileHandler, SysLogHandler
import os
+import pytz
import sys
import argparse_utils
import config
-import string_utils as su
-import thread_utils as tu
-parser = config.add_commandline_args(
+cfg = config.add_commandline_args(
f'Logging ({__file__})',
'Args related to logging')
-parser.add_argument(
+cfg.add_argument(
'--logging_config_file',
type=argparse_utils.valid_filename,
default=None,
metavar='FILENAME',
help='Config file containing the logging setup, see: https://docs.python.org/3/howto/logging.html#logging-advanced-tutorial',
)
-parser.add_argument(
+cfg.add_argument(
'--logging_level',
type=str,
default='INFO',
metavar='LEVEL',
help='The level below which to squelch log messages.',
)
-parser.add_argument(
+cfg.add_argument(
'--logging_format',
type=str,
default='%(levelname)s:%(asctime)s: %(message)s',
help='The format for lines logged via the logger module.'
)
-parser.add_argument(
+cfg.add_argument(
'--logging_date_format',
type=str,
- default='%Y/%m/%dT%H:%M:%S%z',
+ default='%Y/%m/%dT%H:%M:%S.%f%z',
metavar='DATEFMT',
help='The format of any dates in --logging_format.'
)
-parser.add_argument(
+cfg.add_argument(
'--logging_console',
action=argparse_utils.ActionNoYes,
default=True,
help='Should we log to the console (stderr)',
)
-parser.add_argument(
+cfg.add_argument(
'--logging_filename',
type=str,
default=None,
metavar='FILENAME',
help='The filename of the logfile to write.'
)
-parser.add_argument(
+cfg.add_argument(
'--logging_filename_maxsize',
type=int,
default=(1024*1024),
metavar='#BYTES',
help='The maximum size (in bytes) to write to the logging_filename.'
)
-parser.add_argument(
+cfg.add_argument(
'--logging_filename_count',
type=int,
default=2,
metavar='COUNT',
help='The number of logging_filename copies to keep before deleting.'
)
-parser.add_argument(
+cfg.add_argument(
'--logging_syslog',
action=argparse_utils.ActionNoYes,
default=False,
help='Should we log to localhost\'s syslog.'
)
-parser.add_argument(
+cfg.add_argument(
'--logging_debug_threads',
action=argparse_utils.ActionNoYes,
default=False,
help='Should we prepend pid/tid data to all log messages?'
)
-parser.add_argument(
+cfg.add_argument(
'--logging_info_is_print',
action=argparse_utils.ActionNoYes,
default=False,
return record.levelno == logging.INFO
+class MillisecondAwareFormatter(logging.Formatter):
+ converter = datetime.datetime.fromtimestamp
+
+ def formatTime(self, record, datefmt=None):
+ ct = self.converter(record.created, pytz.timezone("US/Pacific"))
+ if datefmt:
+ s = ct.strftime(datefmt)
+ else:
+ t = ct.strftime("%Y-%m-%d %H:%M:%S")
+ s = "%s,%03d" % (t, record.msecs)
+ return s
+
+
def initialize_logging(logger=None) -> logging.Logger:
assert config.has_been_parsed()
if logger is None:
# for k, v in encoded_priorities.items():
# handler.encodePriority(k, v)
handler.setFormatter(
- logging.Formatter(
+ MillisecondAwareFormatter(
fmt=fmt,
datefmt=config.config['logging_date_format'],
)
)
handler.setLevel(numeric_level)
handler.setFormatter(
- logging.Formatter(
+ MillisecondAwareFormatter(
fmt=fmt,
datefmt=config.config['logging_date_format'],
)
handler = logging.StreamHandler(sys.stderr)
handler.setLevel(numeric_level)
handler.setFormatter(
- logging.Formatter(
+ MillisecondAwareFormatter(
fmt=fmt,
datefmt=config.config['logging_date_format'],
)
def tprint(*args, **kwargs) -> None:
if config.config['logging_debug_threads']:
- print(f'{tu.current_thread_id()}', end="")
+ from thread_utils import current_thread_id
+ print(f'{current_thread_id()}', end="")
print(*args, **kwargs)
else:
pass
self.destination_bitv = destination_bitv
def print(self, *args, **kwargs):
+ from string_utils import sprintf, strip_escape_sequences
end = kwargs.pop("end", None)
if end is not None:
if not isinstance(end, str):
raise TypeError("sep must be None or a string")
if kwargs:
raise TypeError("invalid keyword arguments to print()")
- buf = su.sprintf(*args, end="", sep=sep)
+ buf = sprintf(*args, end="", sep=sep)
if sep is None:
sep = " "
if end is None:
if self.destination_bitv & self.FILENAME and self.f is not None:
self.f.write(buf.encode('utf-8'))
self.f.flush()
- buf = su.strip_escape_sequences(buf)
+ buf = strip_escape_sequences(buf)
if self.logger is not None:
if self.destination_bitv & self.LOG_DEBUG:
self.logger.debug(buf)