Great ideas! Thanks for your thoughts. Try this version of weeutil/logger.py. It includes a shim that does the proper type conversion before passing on arguments to logging.handlers.RotatingFileHandler.
To use should be as simple as adding this to weewx.conf (alas, NOT TESTED): [Logging] [[loggers]] [[[root]]] handlers = syslog, rotate [[handlers]] [[[rotate]]] filename = /tmp/weewx.log By default, it logs to /var/log/weewx.log. Hence, the need to override that option if you want to log to /tmp/weewx.log. -tk On Sat, May 2, 2020 at 8:05 AM Graham Eddy <graham.e...@gmail.com> wrote: > i think we all agree that syslog on macos is a lost cause. > the corollary is that pointing python logging at syslog (via > logging.handlers.SysLogHandler) is doomed > > i accept that syslog works fine on most platforms, but it is not feasible > for macos > but there is the simple alternative for macos users of using a different > handler: > > i patched log_dict in bin/weeutil/logger.py to add a > logging.handlers.RotatingFileHandler and it worked fine. > adding it via weewx.conf gives a type conversion error that seems simple > to fix. > with the fix, i would suggest this be added to the macos installation > instructions for user to add to weewx.conf (or make it part of the > installation process) > > details follow... > > weewx.conf (yes, using that ‘root’ hack introduced to work around a > ConfigObj limitation): > > [Logging] > [[loggers]] > [[[root]]] > level = DEBUG > propogate = True > handlers = syslog, rotate > [[handlers]] > [[[rotate]]] > level = DEBUG > formatter = standard > class = logging.handlers.RotatingFileHandler > filename = /tmp/weewx.log > maxBytes = 10000000 > backupCount = 2 > > > gives the following error, which on the face of it would take a simple fix > of type conversions: > > Traceback (most recent call last): > File > "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/config.py", > line 563, in configure > handler = self.configure_handler(handlers[name]) > File > "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/config.py", > line 736, in configure_handler > result = factory(**kwargs) > File > "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/handlers.py", > line 146, in __init__ > if maxBytes > 0: > TypeError: '>' not supported between instances of 'str' and ‘int’ < - - - > SEE HER*E* > > The above exception was the direct cause of the following exception: > > Traceback (most recent call last): > File "bin/weewxd", line 261, in <module> > main() > File "bin/weewxd", line 136, in main > weeutil.logger.setup(options.log_label, config_dict) > File "/opt/weewx/bin/weeutil/logger.py", line 203, in setup > logging.config.dictConfig(log_dict) > File > "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/config.py", > line 800, in dictConfig > dictConfigClass(config).configure() > File > "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/logging/config.py", > line 571, in configure > '%r' % name) from e > ValueError: Unable to configure handler 'rotate' > > > the log_dict it produces before crashing seems correct: > > {'version': 1, 'disable_existing_loggers': False, 'loggers': {'': > {'level': 'DEBUG', 'propagate': True, 'handlers': ['syslog'], 'propogate': > 'True'}}, 'handlers': {'syslog': {'level': 'DEBUG', 'formatter': > 'standard', 'class': 'logging.handlers.SysLogHandler', 'address': > '/var/run/syslog', 'facility': 'local1'}, 'console': {'level': 'DEBUG', > 'formatter': 'verbose', 'class': 'logging.StreamHandler', 'stream': ' > ext://sys.stdout'}, 'rotate': {'level': 'DEBUG', 'formatter': 'standard', > 'class': 'logging.handlers.RotatingFileHandler', 'filename': > '/tmp/weewx.log', 'maxBytes': '10000000', 'backupCount': '2'}}, > 'formatters': {'simple': {'format': '%(levelname)s %(message)s'}, > 'standard': {'format': 'weewx[%(process)d] %(levelname)s %(name)s: > %(message)s'}, 'verbose': {'format': '%(asctime)s weewx[%(process)d] > %(levelname)s %(name)s: %(message)s', 'datefmt': '%Y-%m-%d %H:%M:%S'}}} > > > cheers > > On 2 May 2020, at 10:42 pm, Tom Keffer <tkef...@gmail.com> wrote: > > Logging from Python under MacOS High Sierra and later is a complete > mystery. I have been unable to get it to work and, it seems, neither have > other > <https://apple.stackexchange.com/questions/256769/how-to-use-logger-command-on-sierra> > users > <https://stackoverflow.com/questions/49805750/macos-high-sierra-syslog-does-not-work/51052538> > . > > Wish I could be of more help... > > -tk > > On Sat, May 2, 2020 at 5:26 AM Graham Eddy <graham.e...@gmail.com> wrote: > >> i reported this incorrectly. instead of giveing me an unchanged logger >> format, it gives me no lines at all (from the point the weewx.conf log >> format is parsed). even the following does the same (no logger output). >> commenting out the “format” line reverts to lines being produced, in >> original format of course >> >> [Logging] >> [[formatters]] >> [[[standard]]] >> format = '%(message)s' >> > > -- > You received this message because you are subscribed to the Google Groups > "weewx-user" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to weewx-user+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/weewx-user/85C23BE1-912C-4EAA-80AC-F7E81F8EE021%40gmail.com > <https://groups.google.com/d/msgid/weewx-user/85C23BE1-912C-4EAA-80AC-F7E81F8EE021%40gmail.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "weewx-user" group. To unsubscribe from this group and stop receiving emails from it, send an email to weewx-user+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/weewx-user/CAPq0zEACT7zvUHsqwj0TODGn2fWqC_Yd6QYSifZcXuOYQmWXYQ%40mail.gmail.com.
# # Copyright (c) 2019 Tom Keffer <tkef...@gmail.com> # # See the file LICENSE.txt for your full rights. # """WeeWX logging facility""" from __future__ import absolute_import import sys import logging.config from six.moves import StringIO import configobj import weewx from weeutil.weeutil import to_int, to_bool # These values are known only at runtime if sys.platform == "darwin": address = '/var/run/syslog' facility = 'local1' elif sys.platform.startswith('linux'): address = '/dev/log' facility = 'user' elif sys.platform.startswith('freebsd'): address = '/var/run/log' facility = 'user' elif sys.platform.startswith('netbsd'): address = '/var/run/log' facility = 'user' elif sys.platform.startswith('openbsd'): address = '/dev/log' facility = 'user' else: address = ('localhost', 514) facility = 'user' # The logging defaults. Note that two kinds of placeholders are used: # # {value}: these are plugged in by the function setup(). # %(value)s: these are plugged in by the Python logging module. # LOGGING_STR = """[Logging] version = 1 disable_existing_loggers = False [[loggers]] # Root logger [[[root]]] level = {log_level} propagate = 1 handlers = syslog, # Definitions of possible logging destinations [[handlers]] # System logger [[[syslog]]] level = DEBUG formatter = standard class = logging.handlers.SysLogHandler address = {address} facility = {facility} # Log to console [[[console]]] level = DEBUG formatter = verbose class = logging.StreamHandler # Alternate choice is 'ext://sys.stderr' stream = ext://sys.stdout # Log to a set of rotating files [[[rotate]]] level = DEBUG formatter = standard class = weeutil.logger.RotatingFileHandler filename = /var/log/weewx.log maxBytes = 10000000 backupCount = 4 # How to format log messages [[formatters]] [[[simple]]] format = "%(levelname)s %(message)s" [[[standard]]] format = "{process_name}[%(process)d] %(levelname)s %(name)s: %(message)s" [[[verbose]]] format = "%(asctime)s {process_name}[%(process)d] %(levelname)s %(name)s: %(message)s" # Format to use for dates and times: datefmt = %Y-%m-%d %H:%M:%S """ def setup(process_name, user_log_dict): """Set up the weewx logging facility""" # Create a ConfigObj from the default string. No interpolation (it interferes with the # interpolation directives embedded in the string). log_config = configobj.ConfigObj(StringIO(LOGGING_STR), interpolation=False, encoding='utf-8') # Turn off interpolation in the incoming dictionary. First save the old # value, then restore later. However, the incoming dictionary may be a simple # Python dictionary and not have interpolation. Hence the try block. try: old_interpolation = user_log_dict.interpolation user_log_dict.interpolation = False except AttributeError: old_interpolation = None # Merge in the user additions / changes: log_config.merge(user_log_dict) # Restore the old interpolation value if old_interpolation is not None: user_log_dict.interpolation = old_interpolation # Adjust the logging level in accordance with whether or not the 'debug' flag is on log_level = 'DEBUG' if weewx.debug else 'INFO' # Now we need to walk the structure, plugging in the values we know. # First, we need a function to do this: def _fix(section, key): if isinstance(section[key], (list, tuple)): # The value is a list or tuple section[key] = [item.format(log_level=log_level, address=address, facility=facility, process_name=process_name) for item in section[key]] else: # The value is a string section[key] = section[key].format(log_level=log_level, address=address, facility=facility, process_name=process_name) # Using the function, walk the 'Logging' part of the structure log_config['Logging'].walk(_fix) # Extract just the part used by Python's logging facility log_dict = log_config.dict().get('Logging', {}) # The root logger is denoted by an empty string by the logging facility. Unfortunately, # ConfigObj does not accept an empty string as a key. So, instead, we use this hack: try: log_dict['loggers'][''] = log_dict['loggers']['root'] del log_dict['loggers']['root'] except KeyError: pass # Make sure values are of the right type if 'version' in log_dict: log_dict['version'] = to_int(log_dict['version']) if 'disable_existing_loggers' in log_dict: log_dict['disable_existing_loggers'] = to_bool(log_dict['disable_existing_loggers']) if 'loggers' in log_dict: for logger in log_dict['loggers']: if 'propagate' in log_dict['loggers'][logger]: log_dict['loggers'][logger]['propagate'] = to_bool(log_dict['loggers'][logger]['propagate']) # Finally! The dictionary is ready. Set the defaults. logging.config.dictConfig(log_dict) def log_traceback(log_fn, prefix=''): """Log the stack traceback into a logger. log_fn: One of the logging.Logger logging functions, such as logging.Logger.warning. prefix: A string, which will be put in front of each log entry. Default is no string. """ import traceback sfd = StringIO() traceback.print_exc(file=sfd) sfd.seek(0) for line in sfd: log_fn("%s%s", prefix, line) class RotatingFileHandler(logging.handlers.RotatingFileHandler): """Shim for logging.handlers.RotatingFileHandler that does type conversions before passing on to superclass.""" def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False): "Initializer. Perform the necessary type conversions." super(RotatingFileHandler, self).__init__(filename, mode=mode, maxBytes=to_int(maxBytes), backupCount=to_int(backupCount), encoding=encoding, delay=to_bool(delay))