Merge pull request #1540 from s0undt3ch/master

Improved logging stuff
This commit is contained in:
Thomas S Hatch 2012-06-30 15:57:40 -07:00
commit 7a2973a10a
6 changed files with 189 additions and 42 deletions

View File

@ -230,9 +230,21 @@
#log_file: /var/log/salt/master
#
# The level of messages to send to the log file.
# One of 'info', 'quiet', 'critical', 'error', 'debug', 'warning'.
# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'.
# Default: 'warning'
#log_level: warning
#log_level_logfile:
#
# The date and time format used in log messages. Allowed date/time formating
# can be seen here:
# http://docs.python.org/library/time.html#time.strftime
#log_datefmt: '%H:%M:%S'
#
# The format of the console logging messages. Allowed formatting options can
# be seen here:
# http://docs.python.org/library/logging.html#logrecord-attributes
#log_fmt_console: '[%(levelname)-8s] %(message)s'
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
#
# Logger levels can be used to tweak specific loggers logging levels.
# For example, if you want to have the salt library at the 'warning' level,

View File

@ -150,7 +150,7 @@
# the master but used to reference a local directory on the minion.
# Set the file client, the client defaults to looking on the master server for
# files, but can be directed to look at the local file directory setting
# files, but can be directed to look at the local file directory setting
# defined below by setting it to local.
#file_client: remote
@ -207,9 +207,19 @@
#log_file: /var/log/salt/minion
#
# The level of messages to send to the log file.
# One of 'info', 'quiet', 'critical', 'error', 'debug', 'warning'.
# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'.
# Default: 'warning'
#log_level: warning
#log_level_logfile:
#
# The date and time format used in log messages. Allowed date/time formating
# can be seen on http://docs.python.org/library/time.html#time.strftime
#log_datefmt: '%H:%M:%S'
#
# The format of the console logging messages. Allowed formatting options can
# be seen on http://docs.python.org/library/logging.html#logrecord-attributes
#log_fmt_console: '[%(levelname)-8s] %(message)s'
#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
#
# Logger levels can be used to tweak specific loggers logging levels.
# For example, if you want to have the salt library at the 'warning' level,

View File

@ -18,15 +18,12 @@ except ImportError as e:
if e.args[0] != 'No module named _msgpack':
raise
log_format = '%(asctime)s,%(msecs)03.0f [%(name)-15s][%(levelname)-8s] %(message)s'
class Master(object):
'''
Creates a master server
'''
def __init__(self):
self.cli = self.__parse_cli()
self.opts = salt.config.master_config(self.cli['config'])
# command line overrides config
if self.cli['user']:
self.opts['user'] = self.cli['user']
@ -62,14 +59,23 @@ class Master(object):
parser.add_option('-l',
'--log-level',
dest='log_level',
default='warning',
choices=list(salt.log.LOG_LEVELS),
help='Console log level. One of %s. For the logfile settings '
'see the config file. Default: \'%%default\'.' %
'see the config file. Default: \'warning\'.' %
', '.join([repr(l) for l in salt.log.LOG_LEVELS]))
options, args = parser.parse_args()
salt.log.setup_console_logger(options.log_level, log_format=log_format)
self.opts = salt.config.master_config(options.config)
if not options.log_level:
options.log_level = self.opts['log_level']
salt.log.setup_console_logger(
options.log_level,
log_format=self.opts['log_fmt_console'],
date_format=self.opts['log_datefmt']
)
cli = {'daemon': options.daemon,
'config': options.config,
@ -92,12 +98,17 @@ class Master(object):
self.opts['sock_dir'],
],
self.opts['user'])
import salt.log
salt.log.setup_logfile_logger(
self.opts['log_file'], self.opts['log_level']
self.opts['log_file'],
self.opts['log_level_logfile'] or self.opts['log_level'],
log_format=self.opts['log_fmt_logfile'],
date_format=self.opts['log_datefmt']
)
for name, level in self.opts['log_granular_levels'].items():
salt.log.set_logger_level(name, level)
import logging
log = logging.getLogger(__name__)
# Late import so logging works correctly
@ -129,7 +140,6 @@ class Minion(object):
'''
def __init__(self):
self.cli = self.__parse_cli()
self.opts = salt.config.minion_config(self.cli['config'])
# command line overrides config
if self.cli['user']:
self.opts['user'] = self.cli['user']
@ -163,14 +173,23 @@ class Minion(object):
parser.add_option('-l',
'--log-level',
dest='log_level',
default='warning',
choices=list(salt.log.LOG_LEVELS),
help='Console log level. One of %s. For the logfile settings '
'see the config file. Default: \'%%default\'.' %
'see the config file. Default: \'warning\'.' %
', '.join([repr(l) for l in salt.log.LOG_LEVELS]))
options, args = parser.parse_args()
salt.log.setup_console_logger(options.log_level, log_format=log_format)
self.opts = salt.config.minion_config(options.config)
if not options.log_level:
options.log_level = self.opts['log_level']
salt.log.setup_console_logger(
options.log_level,
log_format=self.opts['log_fmt_console'],
date_format=self.opts['log_datefmt']
)
cli = {'daemon': options.daemon,
'config': options.config,
@ -189,12 +208,17 @@ class Minion(object):
os.path.dirname(self.opts['log_file']),
],
self.opts['user'])
import salt.log
salt.log.setup_logfile_logger(
self.opts['log_file'], self.opts['log_level']
self.opts['log_file'],
self.opts['log_level_logfile'] or self.opts['log_level'],
log_format=self.opts['log_fmt_logfile'],
date_format=self.opts['log_datefmt']
)
for name, level in self.opts['log_granular_levels'].items():
salt.log.set_logger_level(name, level)
import logging
# Late import so logging works correctly
import salt.minion
@ -203,18 +227,18 @@ class Minion(object):
# Late import so logging works correctly
import salt.utils
# If the minion key has not been accepted, then Salt enters a loop
# waiting for it, if we daemonize later then the minion cound halt
# waiting for it, if we daemonize later then the minion could halt
# the boot process waiting for a key to be accepted on the master.
# This is the latest safe place to daemonize
salt.utils.daemonize()
minion = salt.minion.Minion(self.opts)
set_pidfile(self.cli['pidfile'])
if check_user(self.opts['user'], log):
try:
try:
minion = salt.minion.Minion(self.opts)
set_pidfile(self.cli['pidfile'])
if check_user(self.opts['user'], log):
minion.tune_in()
except KeyboardInterrupt:
log.warn('Stopping the Salt Minion')
raise SystemExit('\nExiting on Ctrl-c')
except KeyboardInterrupt:
log.warn('Stopping the Salt Minion')
raise SystemExit('\nExiting on Ctrl-c')
class Syndic(object):
@ -223,7 +247,6 @@ class Syndic(object):
'''
def __init__(self):
self.cli = self.__parse_cli()
self.opts = self.__prep_opts()
# command line overrides config
if self.cli['user']:
self.opts['user'] = self.cli['user']
@ -283,14 +306,22 @@ class Syndic(object):
parser.add_option('-l',
'--log-level',
dest='log_level',
default='warning',
choices=list(salt.log.LOG_LEVELS),
help='Console log level. One of %s. For the logfile settings '
'see the config file. Default: \'%%default\'.' %
'see the config file. Default: \'warning\'.' %
', '.join([repr(l) for l in salt.log.LOG_LEVELS]))
options, args = parser.parse_args()
salt.log.setup_console_logger(options.log_level, log_format=log_format)
self.opts = self.__prep_opts()
if not options.log_level:
options.log_level = self.opts['log_level']
salt.log.setup_console_logger(
options.log_level,
log_format=self.opts['log_fmt_console'],
date_format=self.opts['log_datefmt']
)
cli = {'daemon': options.daemon,
'minion_config': options.minion_config,

View File

@ -26,6 +26,9 @@ from salt.exceptions import SaltClientError
log = logging.getLogger(__name__)
__dflt_log_datefmt = '%H:%M:%S'
__dflt_log_fmt_console = '[%(levelname)-8s] %(message)s'
__dflt_log_fmt_logfile = '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s'
def _validate_file_roots(file_roots):
'''
@ -86,8 +89,12 @@ def load_config(opts, path, env_var):
opts.update(_read_conf_file(path))
opts['conf_file'] = path
except Exception as e:
import salt.log
msg = 'Error parsing configuration file: {0} - {1}'
log.warn(msg.format(path, e))
if salt.log.is_console_configured():
log.warn(msg.format(path, e))
else:
print msg.format(path, e)
else:
log.debug('Missing configuration file: {0}'.format(path))
@ -178,6 +185,10 @@ def minion_config(path):
'sub_timeout': 60,
'log_file': '/var/log/salt/minion',
'log_level': 'warning',
'log_level_logfile': None,
'log_datefmt': __dflt_log_datefmt,
'log_fmt_console': __dflt_log_fmt_console,
'log_fmt_logfile': __dflt_log_fmt_logfile,
'log_granular_levels': {},
'test': False,
'cython_enable': False,
@ -256,6 +267,10 @@ def master_config(path):
'job_cache': True,
'log_file': '/var/log/salt/master',
'log_level': 'warning',
'log_level_logfile': None,
'log_datefmt': __dflt_log_datefmt,
'log_fmt_console': __dflt_log_fmt_console,
'log_fmt_logfile': __dflt_log_fmt_logfile,
'log_granular_levels': {},
'pidfile': '/var/run/salt-master.pid',
'cluster_masters': [],

View File

@ -5,16 +5,17 @@
This is where Salt's logging gets set up.
:copyright: 2011 :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: 2011-2012 :email:`Pedro Algarvio (pedro@algarvio.me)`
:license: Apache 2.0, see LICENSE for more details.
'''
import re
import sys
import logging
import logging.handlers
TRACE = 5
GARBAGE = 1
TRACE = logging.TRACE = 5
GARBAGE = logging.GARBAGE = 1
LOG_LEVELS = {
'debug': logging.DEBUG,
@ -28,13 +29,65 @@ LOG_LEVELS = {
LoggingLoggerClass = logging.getLoggerClass()
MODNAME_PATTERN = re.compile(r'(?P<name>%%\(name\)(?P<digits>\-(?:[\d]+))?s)')
MAX_LOGGER_MODNAME_LENGTH = 4
__CONSOLE_CONFIGURED = False
__LOGFILE_CONFIGURED = False
def is_console_configured():
global __CONSOLE_CONFIGURED
return __CONSOLE_CONFIGURED
def is_logfile_configured():
global __LOGFILE_CONFIGURED
return __LOGFILE_CONFIGURED
class Logging(LoggingLoggerClass):
def __new__(cls, logger_name, *args, **kwargs):
global MAX_LOGGER_MODNAME_LENGTH
# This makes module name padding increase to the biggest module name
# so that logs keep readability.
#
# This code will only run when a new logger is created, ie:
#
# logging.getLogger(__name__)
#
instance = super(Logging, cls).__new__(cls)
try:
max_logger_name = max(logging.Logger.manager.loggerDict.keys())
if len(max_logger_name) > MAX_LOGGER_MODNAME_LENGTH:
MAX_LOGGER_MODNAME_LENGTH = len(max_logger_name)
for handler in logging.getLogger().handlers:
if not handler.lock:
handler.createLock()
handler.acquire()
formatter = handler.formatter
fmt = formatter._fmt.replace('%', '%%')
match = MODNAME_PATTERN.search(fmt)
if match:
fmt = fmt.replace(match.group('name'), '%%(name)-%ds')
formatter = logging.Formatter(
fmt % MAX_LOGGER_MODNAME_LENGTH,
datefmt=formatter.datefmt
)
handler.setFormatter(formatter)
handler.release()
except ValueError:
# There are no registered loggers yet
pass
return instance
def garbage(self, msg, *args, **kwargs):
return LoggingLoggerClass.log(self, 1, msg, *args, **kwargs)
return LoggingLoggerClass.log(self, GARBAGE, msg, *args, **kwargs)
def trace(self, msg, *args, **kwargs):
return LoggingLoggerClass.log(self, 5, msg, *args, **kwargs)
return LoggingLoggerClass.log(self, TRACE, msg, *args, **kwargs)
def getLogger(name):
@ -49,16 +102,20 @@ def init():
'''
if logging.getLoggerClass() is not Logging:
logging.setLoggerClass(Logging)
logging.addLevelName(5, 'TRACE')
logging.addLevelName(1, 'GARBAGE')
logging.addLevelName(TRACE, 'TRACE')
logging.addLevelName(GARBAGE, 'GARBAGE')
# Set the root logger at the lowest level possible
logging.getLogger().setLevel(1)
logging.getLogger().setLevel(GARBAGE)
def setup_console_logger(log_level='error', log_format=None, date_format=None):
'''
Setup the console logger
'''
if is_console_configured():
logging.getLogger(__name__).warning("Console logging already configured")
return
init()
level = LOG_LEVELS.get(log_level.lower(), logging.ERROR)
@ -78,11 +135,20 @@ def setup_console_logger(log_level='error', log_format=None, date_format=None):
handler.setFormatter(formatter)
rootLogger.addHandler(handler)
global __CONSOLE_CONFIGURED
__CONSOLE_CONFIGURED = True
def setup_logfile_logger(log_path, log_level='error'):
def setup_logfile_logger(log_path, log_level='error', log_format=None,
date_format=None):
'''
Setup the logfile logger
'''
if is_logfile_configured():
logging.getLogger(__name__).warning("Logfile logging already configured")
return
init()
level = LOG_LEVELS.get(log_level.lower(), logging.ERROR)
@ -99,13 +165,21 @@ def setup_logfile_logger(log_path, log_level='error'):
sys.exit(2)
handler.setLevel(level)
formatter = logging.Formatter(
'%(asctime)s [%(name)-15s][%(levelname)-8s] %(message)s',
)
# Set the default console formatter config
if not log_format:
log_format = '%(asctime)s [%(name)-15s][%(levelname)-8s] %(message)s'
if not date_format:
date_format = '%H:%M:%S'
formatter = logging.Formatter(log_format, datefmt=date_format)
handler.setFormatter(formatter)
rootLogger.addHandler(handler)
global __LOGFILE_CONFIGURED
__LOGFILE_CONFIGURED = True
def set_logger_level(logger_name, log_level='error'):
'''

View File

@ -312,11 +312,16 @@ def dns_check(addr, safe=False):
try:
addr = socket.gethostbyname(addr)
except socket.gaierror:
err = ('This master address: {0} was previously resolvable but '
err = ('This master address: \'{0}\' was previously resolvable but '
'now fails to resolve! The previously resolved ip addr '
'will continue to be used').format(addr)
if safe:
log.error(err)
import salt.log
if salt.log.is_console_configured():
# If logging is not configured it also means that either
# the master or minion instance calling this hasn't even
# started running
log.error(err)
raise SaltClientError
else:
err = err.format(addr)