
# HG changeset patch # User Zach Welch zach@mandolincreekfarm.com
Add chirp.logger module (#2347)
This patch adds the chirp.logger module, using it in chirpc and chirpw. It adds a handful of optional command line arguments to control the new logging features. In addition, CHIRP_DEBUG, CHIRP_LOG, and CHIRP_LOG_LEVEL can be used to control the logging features from the environment.
It also makes the version string reusable between the GUI and CLI, and it prints that string at the start of the log file (if in use).
diff --git a/chirp/logger.py b/chirp/logger.py new file mode 100644 index 0000000..4cf8fa2 --- /dev/null +++ b/chirp/logger.py @@ -0,0 +1,155 @@ +# Copyright 2015 Zachary T Welch zach@mandolincreekfarm.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + + +r""" +The chirp.logger module provides the core logging facilties for CHIRP. +It sets up the console and (optionally) a log file. For early debugging, +it checks the CHIRP_DEBUG, CHIRP_LOG, and CHIRP_LOG_LEVEL environment +variables. +""" + +import os +import sys +import logging +import argparse +import platform +from chirp import CHIRP_VERSION + + +def version_string(): + args = (CHIRP_VERSION, + platform.get_platform().os_version_string(), + sys.version.split()[0]) + return "CHIRP %s on %s (Python %s)" % args + + +class VersionAction(argparse.Action): + def __call__(self, parser, namespace, value, option_string=None): + print version_string() + sys.exit(1) + + +def add_version_argument(parser): + parser.add_argument("--version", action=VersionAction, nargs=0, + help="Print version and exit") + +#: Map human-readable logging levels to their internal values. +log_level_names = {"critical": logging.CRITICAL, + "error": logging.ERROR, + "warn": logging.WARNING, + "info": logging.INFO, + "debug": logging.DEBUG, + } + + +class Logger(object): + def __init__(self): + # create root logger + self.logger = logging.getLogger() + self.logger.setLevel(logging.DEBUG) + + self.LOG = logging.getLogger(__name__) + + # Set CHIRP_DEBUG in environment for early console debugging. + # It can be a number or a name; otherwise, level is set to 'debug' + # in order to maintain backward compatibility. + CHIRP_DEBUG = os.getenv("CHIRP_DEBUG") + level = logging.WARNING + if CHIRP_DEBUG: + try: + level = int(CHIRP_DEBUG) + except ValueError: + try: + level = log_level_names[CHIRP_DEBUG] + except KeyError: + level = logging.DEBUG + + self.console = logging.StreamHandler() + self.console.setLevel(level) + format_str = '%(levelname)s: %(message)s' + self.console.setFormatter(logging.Formatter(format_str)) + self.logger.addHandler(self.console) + + # Set CHIRP_LOG in environment to the name of log file. + logname = os.getenv("CHIRP_LOG") + self.logfile = None + if logname is not None: + self.create_log_file(logname) + level = os.getenv("CHIRP_LOG_LEVEL") + if level is not None: + self.set_log_verbosity(level) + else: + self.set_log_level(logging.DEBUG) + + def create_log_file(self, name): + if self.logfile is None: + self.logname = name + lf = file(name, "w") + print >>lf, version_string() + lf.close() + self.logfile = logging.FileHandler(name) + format_str = '[%(created)s] %(name)s - %(levelname)s: %(message)s' + self.logfile.setFormatter(logging.Formatter(format_str)) + self.logger.addHandler(self.logfile) + + else: + self.logger.error("already logging to " + self.logname) + + def set_verbosity(self, level): + if level > logging.CRITICAL: + level = logging.CRITICAL + self.console.setLevel(level) + self.LOG.debug("verbosity=%d", level) + + def set_log_level(self, level): + if level > logging.CRITICAL: + level = logging.CRITICAL + self.logfile.setLevel(level) + self.LOG.debug("log level=%d", level) + + def set_log_level_by_name(self, level): + self.set_log_level(log_level_names[level]) + + instance = None + +Logger.instance = Logger() + + +def add_arguments(parser): + parser.add_argument("-q", "--quiet", action="count", default=0, + help="Decrease verbosity") + parser.add_argument("-v", "--verbose", action="count", default=0, + help="Increase verbosity") + parser.add_argument("--log", dest="log_file", action="store", default=0, + help="Log messages to a file") + parser.add_argument("--log-level", action="store", default="debug", + help="Log file verbosity (critical, error, warn, " + + "info, debug). Defaults to 'debug'.") + + +def handle_options(options): + logger = Logger.instance + + if options.verbose or options.quiet: + logger.set_verbosity(30 + 10 * (options.quiet - options.verbose)) + + if options.log_file: + logger.create_log_file(options.log_file) + try: + level = int(options.log_level) + logger.set_log_level(level) + except ValueError: + logger.set_log_level_by_name(options.log_level) diff --git a/chirpc b/chirpc index 6a25e50..16c7006 100755 --- a/chirpc +++ b/chirpc @@ -19,10 +19,14 @@ import serial import sys import argparse +import logging
+from chirp import logger from chirp import * from chirp import chirp_common, errors, idrp, directory, util
+LOG = logging.getLogger("chirpc") + def fail_unsupported(): print "Operation not supported by selected radio" sys.exit(1) @@ -62,6 +66,7 @@ class DTCSPolarityAction(argparse.Action):
if __name__ == "__main__": parser = argparse.ArgumentParser() + logger.add_version_argument(parser) parser.add_argument("-s", "--serial", dest="serial", default="mmap", help="Serial port (default: mmap)") @@ -151,8 +156,10 @@ if __name__ == "__main__": action="store_true", default=False, help="Upload memory map to radio") + logger.add_arguments(parser) parser.add_argument("args", metavar="arg", nargs='*', help="Some commands require additional arguments") + if len(sys.argv) <= 1: parser.print_help() sys.exit(0) @@ -160,6 +167,8 @@ if __name__ == "__main__": options = parser.parse_args() args = options.args
+ logger.handle_options(options) + if options.list_radios: print "Supported Radios:\n\t", "\n\t".join(sorted(RADIOS.keys())) sys.exit(0) diff --git a/chirpw b/chirpw index a3a316c..b11ab0f 100755 --- a/chirpw +++ b/chirpw @@ -17,8 +17,9 @@
import os
+from chirp import logger from chirp import elib_intl -from chirp import platform, CHIRP_VERSION +from chirp import platform from chirpui import config
# Hack to setup environment @@ -40,10 +41,6 @@ elif not os.isatty(0): sys.stdout = log sys.stderr = log
-print "CHIRP %s on %s (Python %s)" % (CHIRP_VERSION, - platform.get_platform().os_version_string(), - sys.version.split()[0]) - execpath = platform.get_platform().executable_path() localepath = os.path.abspath(os.path.join(execpath, "locale")) if not os.path.exists(localepath): @@ -127,10 +124,14 @@ from chirpui import mainapp, config
parser = argparse.ArgumentParser() parser.add_argument("files", metavar="file", nargs='*', help="File to open") +logger.add_version_argument(parser) parser.add_argument("--profile", action="store_true", help="Enable profiling") +logger.add_arguments(parser) args = parser.parse_args()
+logger.handle_options(args) + a = mainapp.ChirpMain()
for i in args.files: