mirror of
https://github.com/JoelBender/bacpypes
synced 2025-09-28 22:15:23 +08:00
243 lines
7.6 KiB
Python
Executable File
243 lines
7.6 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
"""
|
|
Console Logging
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import logging
|
|
import logging.handlers
|
|
|
|
try:
|
|
from argparse import ArgumentParser as _ArgumentParser
|
|
_argparse_success = True
|
|
except:
|
|
class _ArgumentParser: pass
|
|
_argparse_success = False
|
|
|
|
from .debugging import bacpypes_debugging, LoggingFormatter, ModuleLogger
|
|
|
|
from ConfigParser import ConfigParser as _ConfigParser
|
|
|
|
# some debugging
|
|
_debug = 0
|
|
_log = ModuleLogger(globals())
|
|
|
|
# configuration
|
|
BACPYPES_INI = os.getenv('BACPYPES_INI', 'BACpypes.ini')
|
|
BACPYPES_DEBUG = os.getenv('BACPYPES_DEBUG', '')
|
|
BACPYPES_COLOR = os.getenv('BACPYPES_COLOR', None)
|
|
BACPYPES_MAXBYTES = int(os.getenv('BACPYPES_MAXBYTES', 1048576))
|
|
BACPYPES_BACKUPCOUNT = int(os.getenv('BACPYPES_BACKUPCOUNT', 5))
|
|
|
|
#
|
|
# ConsoleLogHandler
|
|
#
|
|
|
|
def ConsoleLogHandler(loggerRef='', handler=None, level=logging.DEBUG, color=None):
|
|
"""Add a handler to stderr with our custom formatter to a logger."""
|
|
if isinstance(loggerRef, logging.Logger):
|
|
pass
|
|
|
|
elif isinstance(loggerRef, str):
|
|
# check for root
|
|
if not loggerRef:
|
|
loggerRef = _log
|
|
|
|
# check for a valid logger name
|
|
elif loggerRef not in logging.Logger.manager.loggerDict:
|
|
raise RuntimeError("not a valid logger name: %r" % (loggerRef,))
|
|
|
|
# get the logger
|
|
loggerRef = logging.getLogger(loggerRef)
|
|
|
|
else:
|
|
raise RuntimeError("not a valid logger reference: %r" % (loggerRef,))
|
|
|
|
# see if this (or its parent) is a module level logger
|
|
if hasattr(loggerRef, 'globs'):
|
|
loggerRef.globs['_debug'] += 1
|
|
elif hasattr(loggerRef.parent, 'globs'):
|
|
loggerRef.parent.globs['_debug'] += 1
|
|
|
|
# make a handler if one wasn't provided
|
|
if not handler:
|
|
handler = logging.StreamHandler()
|
|
handler.setLevel(level)
|
|
|
|
# use our formatter
|
|
handler.setFormatter(LoggingFormatter(color))
|
|
|
|
# add it to the logger
|
|
loggerRef.addHandler(handler)
|
|
|
|
# make sure the logger has at least this level
|
|
loggerRef.setLevel(level)
|
|
|
|
#
|
|
# ArgumentParser
|
|
#
|
|
|
|
class ArgumentParser(_ArgumentParser):
|
|
|
|
"""
|
|
ArgumentParser extends the one with the same name from the argparse module
|
|
by adding the common command line arguments found in BACpypes applications.
|
|
|
|
--buggers list the debugging logger names
|
|
--debug [DEBUG [DEBUG ...]] attach a handler to loggers
|
|
--color debug in color
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
|
"""Follow normal initialization and add BACpypes arguments."""
|
|
if _debug: ArgumentParser._debug("__init__")
|
|
|
|
if not _argparse_success:
|
|
raise ImportError("No module named argparse, install via pip or easy_install: https://pypi.python.org/pypi/argparse\n")
|
|
|
|
_ArgumentParser.__init__(self, **kwargs)
|
|
|
|
# add a way to get a list of the debugging hooks
|
|
self.add_argument("--buggers",
|
|
help="list the debugging logger names",
|
|
action="store_true",
|
|
)
|
|
|
|
# add a way to attach debuggers
|
|
self.add_argument('--debug', nargs='*',
|
|
help="add a log handler to each debugging logger",
|
|
)
|
|
|
|
# add a way to turn on color debugging
|
|
self.add_argument("--color",
|
|
help="turn on color debugging",
|
|
action="store_true",
|
|
)
|
|
|
|
def parse_args(self, *args, **kwargs):
|
|
"""Parse the arguments as usual, then add default processing."""
|
|
if _debug: ArgumentParser._debug("parse_args")
|
|
|
|
# pass along to the parent class
|
|
result_args = _ArgumentParser.parse_args(self, *args, **kwargs)
|
|
|
|
# check to dump labels
|
|
if result_args.buggers:
|
|
loggers = sorted(logging.Logger.manager.loggerDict.keys())
|
|
for loggerName in loggers:
|
|
sys.stdout.write(loggerName + '\n')
|
|
sys.exit(0)
|
|
|
|
# check for debug
|
|
if result_args.debug is None:
|
|
# --debug not specified
|
|
result_args.debug = []
|
|
elif not result_args.debug:
|
|
# --debug, but no arguments
|
|
result_args.debug = ["__main__"]
|
|
|
|
# check for debugging from the environment
|
|
if BACPYPES_DEBUG:
|
|
result_args.debug.extend(BACPYPES_DEBUG.split())
|
|
if BACPYPES_COLOR:
|
|
result_args.color = True
|
|
|
|
# keep track of which files are going to be used
|
|
file_handlers = {}
|
|
|
|
# loop through the bug list
|
|
for i, debug_name in enumerate(result_args.debug):
|
|
color = (i % 6) + 2 if result_args.color else None
|
|
|
|
debug_specs = debug_name.split(':')
|
|
if len(debug_specs) == 1:
|
|
ConsoleLogHandler(debug_name, color=color)
|
|
else:
|
|
# the debugger name is just the first component
|
|
debug_name = debug_specs[0]
|
|
|
|
# if the file is already being used, use the already created handler
|
|
file_name = debug_specs[1]
|
|
if file_name in file_handlers:
|
|
handler = file_handlers[file_name]
|
|
else:
|
|
if len(debug_specs) >= 3:
|
|
maxBytes = int(debug_specs[2])
|
|
else:
|
|
maxBytes = BACPYPES_MAXBYTES
|
|
if len(debug_specs) >= 4:
|
|
backupCount = int(debug_specs[3])
|
|
else:
|
|
backupCount = BACPYPES_BACKUPCOUNT
|
|
|
|
# create a handler
|
|
handler = logging.handlers.RotatingFileHandler(
|
|
file_name, maxBytes=maxBytes, backupCount=backupCount,
|
|
)
|
|
handler.setLevel(logging.DEBUG)
|
|
|
|
# save it for more than one instance
|
|
file_handlers[file_name] = handler
|
|
|
|
# use this handler, no color
|
|
ConsoleLogHandler(debug_name, handler=handler)
|
|
|
|
# return what was parsed
|
|
return result_args
|
|
|
|
bacpypes_debugging(ArgumentParser)
|
|
|
|
#
|
|
# ConfigArgumentParser
|
|
#
|
|
|
|
class ConfigArgumentParser(ArgumentParser):
|
|
|
|
"""
|
|
ConfigArgumentParser extends the ArgumentParser with the functionality to
|
|
read in a configuration file.
|
|
|
|
--ini INI provide a separate INI file
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
|
"""Follow normal initialization and add BACpypes arguments."""
|
|
if _debug: ConfigArgumentParser._debug("__init__")
|
|
ArgumentParser.__init__(self, **kwargs)
|
|
|
|
# add a way to read a configuration file
|
|
self.add_argument('--ini',
|
|
help="device object configuration file",
|
|
default=BACPYPES_INI,
|
|
)
|
|
|
|
def parse_args(self, *args, **kwargs):
|
|
"""Parse the arguments as usual, then add default processing."""
|
|
if _debug: ConfigArgumentParser._debug("parse_args")
|
|
|
|
# pass along to the parent class
|
|
result_args = ArgumentParser.parse_args(self, *args, **kwargs)
|
|
|
|
# read in the configuration file
|
|
config = _ConfigParser()
|
|
config.read(result_args.ini)
|
|
if _debug: _log.debug(" - config: %r", config)
|
|
|
|
# check for BACpypes section
|
|
if not config.has_section('BACpypes'):
|
|
raise RuntimeError("INI file with BACpypes section required")
|
|
|
|
# convert the contents to an object
|
|
ini_obj = type('ini', (object,), dict(config.items('BACpypes')))
|
|
if _debug: _log.debug(" - ini_obj: %r", ini_obj)
|
|
|
|
# add the object to the parsed arguments
|
|
setattr(result_args, 'ini', ini_obj)
|
|
|
|
# return what was parsed
|
|
return result_args
|
|
|
|
bacpypes_debugging(ConfigArgumentParser)
|