mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-22 21:00:44 +00:00
refactor logging and switch to python standard library
This commit is contained in:
parent
f92c77f5b8
commit
0ffdf36a0d
@ -16,11 +16,11 @@ from distutils.version import LooseVersion
|
|||||||
import ansible
|
import ansible
|
||||||
from appdirs import AppDirs
|
from appdirs import AppDirs
|
||||||
|
|
||||||
from ansiblelater.utils import (abort, error, get_property, info,
|
from . import logger
|
||||||
|
from .settings import Settings
|
||||||
|
from ansiblelater.utils import (get_property,
|
||||||
is_line_in_ranges, lines_ranges,
|
is_line_in_ranges, lines_ranges,
|
||||||
read_standards, standards_latest, warn)
|
read_standards, standards_latest)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Ansible 2.4 import of module loader
|
# Ansible 2.4 import of module loader
|
||||||
@ -31,13 +31,8 @@ except ImportError:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from ansible.utils import module_finder as module_loader
|
from ansible.utils import module_finder as module_loader
|
||||||
|
|
||||||
try:
|
settings = Settings()
|
||||||
import ConfigParser as configparser
|
logger = logger.get_logger(__name__, settings.config["logging"]["level"])
|
||||||
except ImportError:
|
|
||||||
import configparser
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Standard(object):
|
class Standard(object):
|
||||||
@ -281,13 +276,13 @@ def candidate_review(candidate, settings, lines=None):
|
|||||||
candidate.version = standards_latest(standards.standards)
|
candidate.version = standards_latest(standards.standards)
|
||||||
if candidate.expected_version:
|
if candidate.expected_version:
|
||||||
if isinstance(candidate, RoleFile):
|
if isinstance(candidate, RoleFile):
|
||||||
warn("%s %s is in a role that contains a meta/main.yml without a declared "
|
logger.warn("%s %s is in a role that contains a meta/main.yml without a declared "
|
||||||
"standards version. "
|
"standards version. "
|
||||||
"Using latest standards version %s" %
|
"Using latest standards version %s" %
|
||||||
(type(candidate).__name__, candidate.path, candidate.version),
|
(type(candidate).__name__, candidate.path, candidate.version),
|
||||||
settings)
|
settings)
|
||||||
else:
|
else:
|
||||||
warn("%s %s does not present standards version. "
|
logger.warn("%s %s does not present standards version. "
|
||||||
"Using latest standards version %s" %
|
"Using latest standards version %s" %
|
||||||
(type(candidate).__name__, candidate.path, candidate.version),
|
(type(candidate).__name__, candidate.path, candidate.version),
|
||||||
settings)
|
settings)
|
||||||
|
@ -1,56 +1,45 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import optparse
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from appdirs import AppDirs
|
from ansiblelater import __version__, settings, logger
|
||||||
from pkg_resources import resource_filename
|
from ansiblelater.utils import get_property
|
||||||
|
|
||||||
from ansiblelater import classify, settings
|
# from .settings import Settings
|
||||||
from ansiblelater.utils import get_property, info, warn
|
|
||||||
|
|
||||||
from .settings import Settings
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
config_dir = AppDirs("ansible-later").user_config_dir
|
parser = argparse.ArgumentParser(
|
||||||
default_config_file = os.path.join(config_dir, "config.yml")
|
description="Validate ansible files against best pratice guideline")
|
||||||
|
parser.add_argument('-c', dest='config_file',
|
||||||
parser = optparse.OptionParser("%prog playbook_file|role_file|inventory_file",
|
help="Location of configuration file: [%s]" % settings.config_file)
|
||||||
version="%prog " + get_property("__version__"))
|
parser.add_argument('-d', dest='rules.standards',
|
||||||
parser.add_option('-c', dest='config_file', default=default_config_file,
|
|
||||||
help="Location of configuration file: [%s]" % default_config_file)
|
|
||||||
parser.add_option('-d', dest='rules_dir',
|
|
||||||
help="Location of standards rules")
|
help="Location of standards rules")
|
||||||
parser.add_option('-q', dest='log_level', action="store_const",
|
parser.add_argument('-q', dest='logging.level', action="store_const",
|
||||||
const=logging.ERROR, help="Only output errors")
|
const=logging.ERROR, help="Only output errors")
|
||||||
parser.add_option('-s', dest='standards_filter', action='append',
|
parser.add_argument('-s', dest='rules.filter', action='append',
|
||||||
help="limit standards to specific names")
|
help="limit standards to specific names")
|
||||||
parser.add_option('-v', '--verbose', dest='log_level', action="count",
|
parser.add_argument('-v', '--verbose', dest='logging.level', action="count",
|
||||||
help="Show more verbose output")
|
help="Show more verbose output")
|
||||||
|
parser.add_argument('--version', action='version', version='%(prog)s {}'.format(__version__))
|
||||||
|
|
||||||
options, args = parser.parse_args(sys.argv[1:])
|
args = parser.parse_args().__dict__
|
||||||
|
|
||||||
settings = Settings(options)
|
# Override correct log level from argparse
|
||||||
|
levels = [logging.WARNING, logging.INFO, logging.DEBUG]
|
||||||
|
if args.get("logging.level"):
|
||||||
|
args["logging.level"] = levels[min(len(levels) - 1, args["logging.level"] - 1)]
|
||||||
|
|
||||||
# print(settings.rulesdir)
|
settings.set_args(args)
|
||||||
# settings = read_config(options.configfile)
|
|
||||||
|
# print(json.dumps(settings.config, indent=4, sort_keys=True))
|
||||||
|
# print(settings.config["logging"]["level"])
|
||||||
|
|
||||||
# # Merge CLI options with config options. CLI options override config options.
|
|
||||||
# for key, value in options.__dict__.items():
|
|
||||||
# if value:
|
|
||||||
# setattr(settings, key, value)
|
|
||||||
|
|
||||||
# if os.path.exists(settings.configfile):
|
|
||||||
# info("Using configuration file: %s" % settings.configfile, settings)
|
|
||||||
# else:
|
|
||||||
# warn("No configuration file found at %s" % settings.configfile, settings, file=sys.stderr)
|
|
||||||
# if not settings.rulesdir:
|
|
||||||
# rules_dir = os.path.join(resource_filename('ansiblelater', 'examples'))
|
|
||||||
# warn("Using example standards found at %s" % rules_dir, settings, file=sys.stderr)
|
|
||||||
# settings.rulesdir = rules_dir
|
|
||||||
|
|
||||||
# if len(args) == 0:
|
# if len(args) == 0:
|
||||||
# candidates = []
|
# candidates = []
|
||||||
|
111
ansiblelater/logger.py
Normal file
111
ansiblelater/logger.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import colorama
|
||||||
|
from pythonjsonlogger import jsonlogger
|
||||||
|
from ansible.module_utils.parsing.convert_bool import boolean as to_bool
|
||||||
|
|
||||||
|
|
||||||
|
def should_do_markup():
|
||||||
|
py_colors = os.environ.get('PY_COLORS', None)
|
||||||
|
if py_colors is not None:
|
||||||
|
return to_bool(py_colors, strict=False)
|
||||||
|
|
||||||
|
return sys.stdout.isatty() and os.environ.get('TERM') != 'dumb'
|
||||||
|
|
||||||
|
|
||||||
|
colorama.init(autoreset=True, strip=not should_do_markup())
|
||||||
|
|
||||||
|
|
||||||
|
class LogFilter(object):
|
||||||
|
"""
|
||||||
|
A custom log filter which excludes log messages above the logged
|
||||||
|
level.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, level):
|
||||||
|
self.__level = level
|
||||||
|
|
||||||
|
def filter(self, logRecord): # pragma: no cover
|
||||||
|
# https://docs.python.org/3/library/logging.html#logrecord-attributes
|
||||||
|
return logRecord.levelno <= self.__level
|
||||||
|
|
||||||
|
|
||||||
|
def get_logger(name=None, level=logging.DEBUG, json=False):
|
||||||
|
"""
|
||||||
|
Build a logger with the given name and returns the logger.
|
||||||
|
:param name: The name for the logger. This is usually the module
|
||||||
|
name, ``__name__``.
|
||||||
|
:return: logger object
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger = logging.getLogger(name)
|
||||||
|
logger.setLevel(level)
|
||||||
|
#handler = logging.StreamHandler()
|
||||||
|
#formatter = jsonlogger.JsonFormatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
#formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
||||||
|
#handler.setFormatter(formatter)
|
||||||
|
logger.addHandler(_get_error_handler(json=json))
|
||||||
|
logger.addHandler(_get_warn_handler(json=json))
|
||||||
|
logger.addHandler(_get_info_handler(json=json))
|
||||||
|
logger.propagate = False
|
||||||
|
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
|
def _get_error_handler(json=False):
|
||||||
|
handler = logging.StreamHandler(sys.stderr)
|
||||||
|
handler.setLevel(logging.ERROR)
|
||||||
|
handler.addFilter(LogFilter(logging.ERROR))
|
||||||
|
handler.setFormatter(logging.Formatter(error('%(message)s')))
|
||||||
|
|
||||||
|
if json:
|
||||||
|
handler.setFormatter(jsonlogger.JsonFormatter('%(message)s'))
|
||||||
|
|
||||||
|
return handler
|
||||||
|
|
||||||
|
|
||||||
|
def _get_warn_handler(json=False):
|
||||||
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
|
handler.setLevel(logging.WARN)
|
||||||
|
handler.addFilter(LogFilter(logging.WARN))
|
||||||
|
handler.setFormatter(logging.Formatter(warn('%(message)s')))
|
||||||
|
|
||||||
|
if json:
|
||||||
|
handler.setFormatter(jsonlogger.JsonFormatter('%(message)s'))
|
||||||
|
|
||||||
|
return handler
|
||||||
|
|
||||||
|
|
||||||
|
def _get_info_handler(json=False):
|
||||||
|
handler = logging.StreamHandler(sys.stderr)
|
||||||
|
handler.setLevel(logging.INFO)
|
||||||
|
handler.addFilter(LogFilter(logging.INFO))
|
||||||
|
handler.setFormatter(logging.Formatter(info('%(message)s')))
|
||||||
|
|
||||||
|
if json:
|
||||||
|
handler.setFormatter(jsonlogger.JsonFormatter('%(message)s'))
|
||||||
|
|
||||||
|
return handler
|
||||||
|
|
||||||
|
|
||||||
|
def abort(message, file=sys.stderr):
|
||||||
|
return color_text(colorama.Fore.RED, "FATAL: {}".format(message))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def error(message):
|
||||||
|
return color_text(colorama.Fore.RED, "ERROR: {}".format(message))
|
||||||
|
|
||||||
|
|
||||||
|
def warn(message):
|
||||||
|
return color_text(colorama.Fore.YELLOW, "WARN: {}".format(message))
|
||||||
|
|
||||||
|
|
||||||
|
def info(message):
|
||||||
|
return color_text(colorama.Fore.BLUE, "INFO: {}".format(message))
|
||||||
|
|
||||||
|
|
||||||
|
def color_text(color, msg):
|
||||||
|
return '{}{}{}'.format(color, msg, colorama.Style.RESET_ALL)
|
@ -1,42 +1,60 @@
|
|||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import six
|
||||||
|
|
||||||
import anyconfig
|
import anyconfig
|
||||||
|
from appdirs import AppDirs
|
||||||
from pkg_resources import resource_filename
|
from pkg_resources import resource_filename
|
||||||
|
|
||||||
from ansiblelater import utils
|
from ansiblelater import utils, logger
|
||||||
|
|
||||||
try:
|
config_dir = AppDirs("ansible-later").user_config_dir
|
||||||
import ConfigParser as configparser
|
default_config_file = os.path.join(config_dir, "config.yml")
|
||||||
except ImportError:
|
|
||||||
import configparser
|
logger = logger.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class NewInitCaller(type):
|
||||||
|
def __call__(cls, *args, **kwargs):
|
||||||
|
obj = type.__call__(cls, *args, **kwargs)
|
||||||
|
obj.after_init()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(NewInitCaller)
|
||||||
class Settings(object):
|
class Settings(object):
|
||||||
def __init__(self, args={}):
|
def __init__(self, args={}, config_file=default_config_file):
|
||||||
self.args = self._get_args(args)
|
self.args = args
|
||||||
|
self.config_file = config_file
|
||||||
self.config = self._get_config()
|
self.config = self._get_config()
|
||||||
|
|
||||||
def _get_args(self, args):
|
def set_args(self, args={}):
|
||||||
# Override correct log level from argparse
|
self.config_file = args.get("config_file") or default_config_file
|
||||||
levels = [logging.WARNING, logging.INFO, logging.DEBUG]
|
|
||||||
if args.log_level:
|
|
||||||
args.log_level = levels[min(len(levels) - 1, args.log_level - 1)]
|
|
||||||
|
|
||||||
args_dict = dict(filter(lambda item: item[1] is not None, args.__dict__.items()))
|
args.pop("config_file", None)
|
||||||
return args_dict
|
args = dict(filter(lambda item: item[1] is not None, args.items()))
|
||||||
|
|
||||||
|
args_dict = {}
|
||||||
|
for key, value in args.items():
|
||||||
|
args_dict = utils.add_dict_branch(args_dict, key.split("."), value)
|
||||||
|
|
||||||
|
self.args = args_dict
|
||||||
|
self.config = self._get_config()
|
||||||
|
self._validate()
|
||||||
|
|
||||||
def _get_config(self):
|
def _get_config(self):
|
||||||
defaults = self._get_defaults()
|
defaults = self._get_defaults()
|
||||||
config_file = self.args.get('config_file')
|
config_file = self.config_file
|
||||||
|
cli_options = self.args
|
||||||
|
|
||||||
if config_file and os.path.exists(config_file):
|
if config_file and os.path.exists(config_file):
|
||||||
with utils.open_file(config_file) as stream:
|
with utils.open_file(config_file) as stream:
|
||||||
s = stream.read()
|
s = stream.read()
|
||||||
anyconfig.merge(defaults, utils.safe_load(s), ac_merge=anyconfig.MS_DICTS)
|
anyconfig.merge(defaults, utils.safe_load(s), ac_merge=anyconfig.MS_DICTS)
|
||||||
|
|
||||||
print(json.dumps(defaults, indent=4, sort_keys=True))
|
if cli_options:
|
||||||
|
anyconfig.merge(defaults, cli_options, ac_merge=anyconfig.MS_DICTS)
|
||||||
|
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
def _get_defaults(self):
|
def _get_defaults(self):
|
||||||
@ -44,13 +62,21 @@ class Settings(object):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
'rules': {
|
'rules': {
|
||||||
'standards': self.args.get('rules_dir', rules_dir),
|
'standards': rules_dir,
|
||||||
'standards_filter': [],
|
'filter': [],
|
||||||
},
|
},
|
||||||
'logging': {
|
'logging': {
|
||||||
'level': self.args.get('log_level', logging.WARN),
|
'level': logging.WARN,
|
||||||
},
|
},
|
||||||
'ansible': {
|
'ansible': {
|
||||||
'custom_modules': [],
|
'custom_modules': [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def after_init(self):
|
||||||
|
self.config = self._get_config()
|
||||||
|
self._validate()
|
||||||
|
|
||||||
|
def _validate(self):
|
||||||
|
logger.setLevel(self.config["logging"]["level"])
|
||||||
|
|
||||||
|
@ -17,41 +17,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
|
|
||||||
def should_do_markup():
|
|
||||||
py_colors = os.environ.get('PY_COLORS', None)
|
|
||||||
if py_colors is not None:
|
|
||||||
return to_bool(py_colors, strict=False)
|
|
||||||
|
|
||||||
return sys.stdout.isatty() and os.environ.get('TERM') != 'dumb'
|
|
||||||
|
|
||||||
|
|
||||||
colorama.init(autoreset=True, strip=not should_do_markup())
|
|
||||||
|
|
||||||
|
|
||||||
def abort(message, file=sys.stderr):
|
|
||||||
return color_text(colorama.Fore.RED, "FATAL: {}".format(message))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def error(message, file=sys.stderr):
|
|
||||||
return color_text(colorama.Fore.RED, "ERROR: {}".format(message))
|
|
||||||
|
|
||||||
|
|
||||||
def warn(message, settings, file=sys.stdout):
|
|
||||||
if settings.log_level <= logging.WARNING:
|
|
||||||
return color_text(colorama.Fore.YELLOW, "WARN: {}".format(message))
|
|
||||||
|
|
||||||
|
|
||||||
def info(message, settings, file=sys.stdout):
|
|
||||||
if settings.log_level <= logging.INFO:
|
|
||||||
return color_text(colorama.Fore.BLUE, "INFO: {}".format(message))
|
|
||||||
|
|
||||||
|
|
||||||
def color_text(color, msg):
|
|
||||||
print('{}{}{}'.format(color, msg, colorama.Style.RESET_ALL))
|
|
||||||
|
|
||||||
|
|
||||||
def count_spaces(c_string):
|
def count_spaces(c_string):
|
||||||
leading_spaces = 0
|
leading_spaces = 0
|
||||||
trailing_spaces = 0
|
trailing_spaces = 0
|
||||||
@ -137,3 +102,13 @@ def open_file(filename, mode='r'):
|
|||||||
"""
|
"""
|
||||||
with open(filename, mode) as stream:
|
with open(filename, mode) as stream:
|
||||||
yield stream
|
yield stream
|
||||||
|
|
||||||
|
|
||||||
|
def add_dict_branch(tree, vector, value):
|
||||||
|
key = vector[0]
|
||||||
|
tree[key] = value \
|
||||||
|
if len(vector) == 1 \
|
||||||
|
else add_dict_branch(tree[key] if key in tree else {},
|
||||||
|
vector[1:],
|
||||||
|
value)
|
||||||
|
return tree
|
||||||
|
@ -11,7 +11,7 @@ max-line-length = 100
|
|||||||
inline-quotes = double
|
inline-quotes = double
|
||||||
exclude = .git,.tox,__pycache__,build,dist,tests,*.pyc,*.egg-info,.cache,.eggs
|
exclude = .git,.tox,__pycache__,build,dist,tests,*.pyc,*.egg-info,.cache,.eggs
|
||||||
application-import-names = ansiblelater
|
application-import-names = ansiblelater
|
||||||
format = ${cyan}%(path)s:%(row)d:%(col)d${reset}: ${red_bold}%(code)s${reset} %(text)s
|
#format = ${cyan}%(path)s:%(row)d:%(col)d${reset}: ${red_bold}%(code)s${reset} %(text)s
|
||||||
|
|
||||||
[isort]
|
[isort]
|
||||||
default_section = THIRDPARTY
|
default_section = THIRDPARTY
|
||||||
|
Loading…
Reference in New Issue
Block a user