Assigned filtering IDs to logger messages

Any ID has common scheme 'log-level.module.description', so it contains
three period-separated groups with possible dashes inside each group.

List of all IDs can be obtained by the following command (in an Unix
environment):

```
$ grep -R "'id': '" pelican | sed -e "s/.*'id': '\([^']\+\)'.*/\1/" | sort -u
```

Fixes #1594.
This commit is contained in:
Alexander Turenko 2016-08-31 10:44:31 +03:00
commit b3a5d87a9e
11 changed files with 196 additions and 97 deletions

View file

@ -107,7 +107,7 @@ Setting name (followed by default value, if any)
and the message to be ignored. and the message to be ignored.
For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]``
``LOG_FILTER_IDS = []`` A list of containing IDs of messages to be ignored. ``LOG_FILTER_IDS = []`` A list of containing IDs of messages to be ignored.
For example: ``['warn.img.alt.empty']`` For example: ``['warn.readers.empty-alt-attribute']``
``READERS = {}`` A dictionary of file extensions / Reader classes for Pelican to ``READERS = {}`` A dictionary of file extensions / Reader classes for Pelican to
process or ignore. For example, to avoid processing .html files, process or ignore. For example, to avoid processing .html files,
set: ``READERS = {'html': None}``. To add a custom reader for the set: ``READERS = {'html': None}``. To add a custom reader for the
@ -780,7 +780,7 @@ It's hard to ignore a group of messages containing dynamic substrings (e.g.
paths to files). Such messages can be ignored using the ``LOG_FILTER_IDS`` paths to files). Such messages can be ignored using the ``LOG_FILTER_IDS``
setting. setting.
For example: ``['warn.img.alt.empty']`` For example: ``['warn.readers.empty-alt-attribute']``
.. _reading_only_modified_content: .. _reading_only_modified_content:

View file

@ -55,7 +55,8 @@ class Pelican(object):
def init_path(self): def init_path(self):
if not any(p in sys.path for p in ['', os.curdir]): if not any(p in sys.path for p in ['', os.curdir]):
logger.debug("Adding current directory to system path") logger.debug("Adding current directory to system path",
extra={'id': 'debug.init.adding-cwd-to-system-path'})
sys.path.insert(0, '') sys.path.insert(0, '')
def init_plugins(self): def init_plugins(self):
@ -67,7 +68,8 @@ class Pelican(object):
for plugin in self.settings['PLUGINS']: for plugin in self.settings['PLUGINS']:
# if it's a string, then import it # if it's a string, then import it
if isinstance(plugin, six.string_types): if isinstance(plugin, six.string_types):
logger.debug("Loading plugin `%s`", plugin) logger.debug("Loading plugin `%s`", plugin,
extra={'id': 'debug.init.loading-plugin'})
try: try:
plugin = __import__(plugin, globals(), locals(), plugin = __import__(plugin, globals(), locals(),
str('module')) str('module'))
@ -76,10 +78,12 @@ class Pelican(object):
"Cannot load plugin `%s`\n%s", plugin, e) "Cannot load plugin `%s`\n%s", plugin, e)
continue continue
logger.debug("Registering plugin `%s`", plugin.__name__) logger.debug("Registering plugin `%s`", plugin.__name__,
extra={'id': 'debug.init.registering-plugin'})
plugin.register() plugin.register()
self.plugins.append(plugin) self.plugins.append(plugin)
logger.debug('Restoring system path') logger.debug('Restoring system path',
extra={'id': 'debug.init.restoring-system-path'})
sys.path = _sys_path sys.path = _sys_path
def _handle_deprecation(self): def _handle_deprecation(self):
@ -87,7 +91,8 @@ class Pelican(object):
if self.settings.get('CLEAN_URLS', False): if self.settings.get('CLEAN_URLS', False):
logger.warning('Found deprecated `CLEAN_URLS` in settings.' logger.warning('Found deprecated `CLEAN_URLS` in settings.'
' Modifying the following settings for the' ' Modifying the following settings for the'
' same behaviour.') ' same behaviour.',
extra={'id': 'warn.init.clean-urls-deprected'})
self.settings['ARTICLE_URL'] = '{slug}/' self.settings['ARTICLE_URL'] = '{slug}/'
self.settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/' self.settings['ARTICLE_LANG_URL'] = '{slug}-{lang}/'
@ -96,17 +101,21 @@ class Pelican(object):
for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL', for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_URL',
'PAGE_LANG_URL'): 'PAGE_LANG_URL'):
logger.warning("%s = '%s'", setting, self.settings[setting]) logger.warning("%s = '%s'", setting, self.settings[setting],
extra={'id': 'warn.init.clean-urls-deprected'})
if self.settings.get('AUTORELOAD_IGNORE_CACHE'): if self.settings.get('AUTORELOAD_IGNORE_CACHE'):
logger.warning('Found deprecated `AUTORELOAD_IGNORE_CACHE` in ' logger.warning('Found deprecated `AUTORELOAD_IGNORE_CACHE` in '
'settings. Use --ignore-cache instead.') 'settings. Use --ignore-cache instead.',
extra={'id': 'warn.init.autoreload-ignore-cache'})
self.settings.pop('AUTORELOAD_IGNORE_CACHE') self.settings.pop('AUTORELOAD_IGNORE_CACHE')
if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False): if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False):
logger.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in' logger.warning(
' settings. Modifying the following settings for' 'Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in'
' the same behaviour.') ' settings. Modifying the following settings for'
' the same behaviour.',
extra={'id': 'warn.init.article-permalink-structure'})
structure = self.settings['ARTICLE_PERMALINK_STRUCTURE'] structure = self.settings['ARTICLE_PERMALINK_STRUCTURE']
@ -126,7 +135,9 @@ class Pelican(object):
'PAGE_SAVE_AS', 'PAGE_LANG_SAVE_AS'): 'PAGE_SAVE_AS', 'PAGE_LANG_SAVE_AS'):
self.settings[setting] = os.path.join(structure, self.settings[setting] = os.path.join(structure,
self.settings[setting]) self.settings[setting])
logger.warning("%s = '%s'", setting, self.settings[setting]) logger.warning(
"%s = '%s'", setting, self.settings[setting],
extra={'id': 'warn.init.article-permalink-structure'})
for new, old in [('FEED', 'FEED_ATOM'), ('TAG_FEED', 'TAG_FEED_ATOM'), for new, old in [('FEED', 'FEED_ATOM'), ('TAG_FEED', 'TAG_FEED_ATOM'),
('CATEGORY_FEED', 'CATEGORY_FEED_ATOM'), ('CATEGORY_FEED', 'CATEGORY_FEED_ATOM'),
@ -137,7 +148,8 @@ class Pelican(object):
'to %(old)s in your settings and theme for the same ' 'to %(old)s in your settings and theme for the same '
'behavior. Temporarily setting %(old)s for backwards ' 'behavior. Temporarily setting %(old)s for backwards '
'compatibility.', 'compatibility.',
{'new': new, 'old': old} {'new': new, 'old': old},
extra={'id': 'warn.init.feed-setting-deprecated'}
) )
self.settings[old] = self.settings[new] self.settings[old] = self.settings[new]
@ -229,7 +241,8 @@ class Pelican(object):
for v in value: for v in value:
if isinstance(v, type): if isinstance(v, type):
logger.debug('Found generator: %s', v) logger.debug('Found generator: %s', v,
extra={'id': 'debug.init.found-generator'})
generators.append(v) generators.append(v)
# StaticGenerator must run last, so it can identify files that # StaticGenerator must run last, so it can identify files that
@ -247,11 +260,13 @@ class Pelican(object):
else: else:
writer = writers[0] writer = writers[0]
if writers_found == 1: if writers_found == 1:
logger.debug('Found writer: %s', writer) logger.debug('Found writer: %s', writer,
extra={'id': 'debug.init.found-writer'})
else: else:
logger.warning( logger.warning(
'%s writers found, using only first one: %s', '%s writers found, using only first one: %s',
writers_found, writer) writers_found, writer,
extra={'id': 'debug.init.many-writers-found'})
return writer(self.output_path, settings=self.settings) return writer(self.output_path, settings=self.settings)
@ -385,8 +400,10 @@ def main():
args = parse_arguments() args = parse_arguments()
init(args.verbosity, args.fatal) init(args.verbosity, args.fatal)
logger.debug('Pelican version: %s', __version__) logger.debug('Pelican version: %s', __version__,
logger.debug('Python version: %s', sys.version.split()[0]) extra={'id': 'debug.init.pelican-version'})
logger.debug('Python version: %s', sys.version.split()[0],
extra={'id': 'debug.init.python-version'})
try: try:
pelican, settings = get_instance(args) pelican, settings = get_instance(args)
@ -455,33 +472,42 @@ def main():
', '.join(k for k, v in modified.items() if v))) ', '.join(k for k, v in modified.items() if v)))
if modified['content'] is None: if modified['content'] is None:
logger.warning('No valid files found in content.') logger.warning(
'No valid files found in content.',
extra={'id': 'warn.init.no-valid-content'})
if modified['theme'] is None: if modified['theme'] is None:
logger.warning('Empty theme folder. Using `basic` ' logger.warning(
'theme.') 'Empty theme folder. Using `basic` theme.',
extra={'id': 'warn.init.empty-theme-folder'})
pelican.run() pelican.run()
except KeyboardInterrupt: except KeyboardInterrupt:
logger.warning("Keyboard interrupt, quitting.") logger.warning(
"Keyboard interrupt, quitting.",
extra={'id': 'warn.init.keyboard-interrupt'})
break break
except Exception as e: except Exception as e:
if (args.verbosity == logging.DEBUG): if (args.verbosity == logging.DEBUG):
raise raise
logger.warning( logger.warning(
'Caught exception "%s". Reloading.', e) 'Caught exception "%s". Reloading.', e,
extra={'id': 'warn.init.caught-exception'})
finally: finally:
time.sleep(.5) # sleep to avoid cpu load time.sleep(.5) # sleep to avoid cpu load
else: else:
if next(watchers['content']) is None: if next(watchers['content']) is None:
logger.warning('No valid files found in content.') logger.warning(
'No valid files found in content.',
extra={'id': 'warn.init.no-valid-content-files'})
if next(watchers['theme']) is None: if next(watchers['theme']) is None:
logger.warning('Empty theme folder. Using `basic` theme.') logger.warning('Empty theme folder. Using `basic` theme.',
extra={'id': 'warn.init.empty-theme-folder'})
pelican.run() pelican.run()

View file

@ -38,14 +38,16 @@ class FileDataCacher(object):
except (IOError, OSError) as err: except (IOError, OSError) as err:
logger.debug('Cannot load cache %s (this is normal on first ' logger.debug('Cannot load cache %s (this is normal on first '
'run). Proceeding with empty cache.\n%s', 'run). Proceeding with empty cache.\n%s',
self._cache_path, err) self._cache_path, err,
extra={'id': 'debug.cache.load'})
self._cache = {} self._cache = {}
except pickle.PickleError as err: except pickle.PickleError as err:
logger.warning('Cannot unpickle cache %s, cache may be using ' logger.warning('Cannot unpickle cache %s, cache may be using '
'an incompatible protocol (see pelican ' 'an incompatible protocol (see pelican '
'caching docs). ' 'caching docs). '
'Proceeding with empty cache.\n%s', 'Proceeding with empty cache.\n%s',
self._cache_path, err) self._cache_path, err,
extra={'id': 'warn.cache.unpickle'})
self._cache = {} self._cache = {}
else: else:
self._cache = {} self._cache = {}
@ -71,7 +73,8 @@ class FileDataCacher(object):
pickle.dump(self._cache, fhandle) pickle.dump(self._cache, fhandle)
except (IOError, OSError, pickle.PicklingError) as err: except (IOError, OSError, pickle.PicklingError) as err:
logger.warning('Could not save cache %s\n ... %s', logger.warning('Could not save cache %s\n ... %s',
self._cache_path, err) self._cache_path, err,
extra={'id': 'warn.cache.save'})
class FileStampDataCacher(FileDataCacher): class FileStampDataCacher(FileDataCacher):
@ -100,7 +103,8 @@ class FileStampDataCacher(FileDataCacher):
self._filestamp_func = filestamp_func self._filestamp_func = filestamp_func
except AttributeError as err: except AttributeError as err:
logger.warning('Could not get hashing function\n\t%s', err) logger.warning('Could not get hashing function\n\t%s', err,
extra={'id': 'warn.cache.hash'})
self._filestamp_func = None self._filestamp_func = None
def cache_data(self, filename, data): def cache_data(self, filename, data):
@ -122,7 +126,8 @@ class FileStampDataCacher(FileDataCacher):
return self._filestamp_func(filename) return self._filestamp_func(filename)
except (IOError, OSError, TypeError) as err: except (IOError, OSError, TypeError) as err:
logger.warning('Cannot get modification stamp for %s\n\t%s', logger.warning('Cannot get modification stamp for %s\n\t%s',
filename, err) filename, err,
extra={'id': 'warn.cache.get-timestamp'})
return '' return ''
def get_cached_data(self, filename, default=None): def get_cached_data(self, filename, default=None):

View file

@ -241,7 +241,8 @@ class Content(object):
logger.warning( logger.warning(
"%s used {attach} link syntax on a " "%s used {attach} link syntax on a "
"non-static file. Use {filename} instead.", "non-static file. Use {filename} instead.",
self.get_relative_source_path()) self.get_relative_source_path(), extra={
'id': 'warn.contents.non-static-attach'})
origin = '/'.join((siteurl, linked_content.url)) origin = '/'.join((siteurl, linked_content.url))
origin = origin.replace('\\', '/') # for Windows paths. origin = origin.replace('\\', '/') # for Windows paths.
else: else:
@ -249,7 +250,8 @@ class Content(object):
"Unable to find `%s`, skipping url replacement.", "Unable to find `%s`, skipping url replacement.",
value.geturl(), extra={ value.geturl(), extra={
'limit_msg': ("Other resources were not found " 'limit_msg': ("Other resources were not found "
"and their urls not replaced")}) "and their urls not replaced"),
'id': 'warn.contents.unable-to-find-url'})
elif what == 'category': elif what == 'category':
origin = '/'.join((siteurl, Category(path, self.settings).url)) origin = '/'.join((siteurl, Category(path, self.settings).url))
elif what == 'tag': elif what == 'tag':
@ -262,7 +264,8 @@ class Content(object):
logger.warning( logger.warning(
"Replacement Indicator '%s' not recognized, " "Replacement Indicator '%s' not recognized, "
"skipping replacement", "skipping replacement",
what) what, extra={
'id': 'warn.contents.unknown-replacement-indicator'})
# keep all other parts, such as query, fragment, etc. # keep all other parts, such as query, fragment, etc.
parts = list(value) parts = list(value)
@ -313,7 +316,8 @@ class Content(object):
"""deprecated function to access summary""" """deprecated function to access summary"""
logger.warning('_get_summary() has been deprecated since 3.6.4. ' logger.warning('_get_summary() has been deprecated since 3.6.4. '
'Use the summary decorator instead') 'Use the summary decorator instead',
extra={'id': 'warn.contents.get-summary-deprecated'})
return self.summary return self.summary
@summary.setter @summary.setter
@ -447,7 +451,8 @@ class Static(Page):
"{filename} link behavior instead.", "{filename} link behavior instead.",
content.get_relative_source_path(), content.get_relative_source_path(),
self.get_relative_source_path(), reason, self.get_relative_source_path(), reason,
extra={'limit_msg': "More {attach} warnings silenced."}) extra={'limit_msg': "More {attach} warnings silenced.",
'id': 'warn.contents.link-relocation-failed'})
# We never override an override, because we don't want to interfere # We never override an override, because we don't want to interfere
# with user-defined overrides that might be in EXTRA_PATH_METADATA. # with user-defined overrides that might be in EXTRA_PATH_METADATA.

View file

@ -71,7 +71,8 @@ class Generator(object):
extensions=self.settings['JINJA_EXTENSIONS'], extensions=self.settings['JINJA_EXTENSIONS'],
) )
logger.debug('Template list: %s', self.env.list_templates()) logger.debug('Template list: %s', self.env.list_templates(),
extra={'id': 'debug.generators.template-list'})
# provide utils.strftime as a jinja filter # provide utils.strftime as a jinja filter
self.env.filters.update({'strftime': DateFormatter()}) self.env.filters.update({'strftime': DateFormatter()})
@ -728,7 +729,8 @@ class StaticGenerator(Generator):
save_as = os.path.join(self.output_path, sc.save_as) save_as = os.path.join(self.output_path, sc.save_as)
mkdir_p(os.path.dirname(save_as)) mkdir_p(os.path.dirname(save_as))
shutil.copy2(source_path, save_as) shutil.copy2(source_path, save_as)
logger.info('Copying %s to %s', sc.source_path, sc.save_as) logger.info('Copying %s to %s', sc.source_path, sc.save_as,
extra={'id': 'info.generators.copying'})
class SourceFileGenerator(Generator): class SourceFileGenerator(Generator):
@ -743,7 +745,8 @@ class SourceFileGenerator(Generator):
copy(obj.source_path, dest) copy(obj.source_path, dest)
def generate_output(self, writer=None): def generate_output(self, writer=None):
logger.info('Generating source files...') logger.info('Generating source files...',
extra={'id': 'info.generators.generating-source-files'})
for obj in chain(self.context['articles'], self.context['pages']): for obj in chain(self.context['articles'], self.context['pages']):
self._create_source(obj) self._create_source(obj)
for obj_trans in obj.translations: for obj_trans in obj.translations:

View file

@ -269,7 +269,8 @@ class MarkdownReader(BaseReader):
logger.warning( logger.warning(
'Duplicate definition of `%s` ' 'Duplicate definition of `%s` '
'for %s. Using first one.', 'for %s. Using first one.',
name, self._source_path) name, self._source_path,
extra={'id': 'warn.readers.duplicate-metadata-def'})
output[name] = self.process_metadata(name, value[0]) output[name] = self.process_metadata(name, value[0])
elif len(value) > 1: elif len(value) > 1:
# handle list metadata as list of string # handle list metadata as list of string
@ -389,9 +390,11 @@ class HTMLReader(BaseReader):
if name is None: if name is None:
attr_list = ['{}="{}"'.format(k, v) for k, v in attrs] attr_list = ['{}="{}"'.format(k, v) for k, v in attrs]
attr_serialized = ', '.join(attr_list) attr_serialized = ', '.join(attr_list)
logger.warning("Meta tag in file %s does not have a 'name' " logger.warning(
"attribute, skipping. Attributes: %s", "Meta tag in file %s does not have a 'name' "
self._filename, attr_serialized) "attribute, skipping. Attributes: %s",
self._filename, attr_serialized,
extra={'id': 'warn.readers.unknown-meta-tag-attribute'})
return return
name = name.lower() name = name.lower()
contents = self._attr_value(attrs, 'content', '') contents = self._attr_value(attrs, 'content', '')
@ -404,7 +407,8 @@ class HTMLReader(BaseReader):
self._filename, self._filename,
extra={'limit_msg': "Other files have meta tag " extra={'limit_msg': "Other files have meta tag "
"attribute 'contents' that should " "attribute 'contents' that should "
"be changed to 'content'"}) "be changed to 'content'",
'id': 'warn.readers.contents-meta-tag-used'})
if name == 'keywords': if name == 'keywords':
name = 'tags' name = 'tags'
@ -444,8 +448,10 @@ class Readers(FileStampDataCacher):
for cls in [BaseReader] + BaseReader.__subclasses__(): for cls in [BaseReader] + BaseReader.__subclasses__():
if not cls.enabled: if not cls.enabled:
logger.debug('Missing dependencies for %s', logger.debug(
', '.join(cls.file_extensions)) 'Missing dependencies for %s',
', '.join(cls.file_extensions),
extra={'id': 'debug.readers.missing-dependencies'})
continue continue
for ext in cls.file_extensions: for ext in cls.file_extensions:
@ -484,7 +490,8 @@ class Readers(FileStampDataCacher):
source_path = posixize_path(os.path.relpath(path, base_path)) source_path = posixize_path(os.path.relpath(path, base_path))
logger.debug( logger.debug(
'Read file %s -> %s', 'Read file %s -> %s',
source_path, content_class.__name__) source_path, content_class.__name__,
extra={'id': 'debug.readers.read-file'})
if not fmt: if not fmt:
_, ext = os.path.splitext(os.path.basename(path)) _, ext = os.path.splitext(os.path.basename(path))
@ -497,7 +504,8 @@ class Readers(FileStampDataCacher):
if preread_signal: if preread_signal:
logger.debug( logger.debug(
'Signal %s.send(%s)', 'Signal %s.send(%s)',
preread_signal.name, preread_sender) preread_signal.name, preread_sender,
extra={'id': 'debug.readers.signal-send'})
preread_signal.send(preread_sender) preread_signal.send(preread_sender)
reader = self.readers[fmt] reader = self.readers[fmt]
@ -556,7 +564,8 @@ class Readers(FileStampDataCacher):
logger.debug( logger.debug(
'Signal %s.send(%s, <metadata>)', 'Signal %s.send(%s, <metadata>)',
context_signal.name, context_signal.name,
context_sender) context_sender,
extra={'id': 'debug.readers.signal-send-metadata'})
context_signal.send(context_sender, metadata=metadata) context_signal.send(context_sender, metadata=metadata)
return content_class(content=content, metadata=metadata, return content_class(content=content, metadata=metadata,
@ -593,7 +602,7 @@ def find_empty_alt(content, path):
'Empty alt attribute for image %s in %s', 'Empty alt attribute for image %s in %s',
os.path.basename(match[1] + match[5]), path, os.path.basename(match[1] + match[5]), path,
extra={'limit_msg': 'Other images have empty alt attributes', extra={'limit_msg': 'Other images have empty alt attributes',
'id': 'warn.img.alt.empty'}) 'id': 'warn.readers.empty-alt-attribute'})
def default_metadata(settings=None, process=None): def default_metadata(settings=None, process=None):

View file

@ -156,13 +156,17 @@ def read_settings(path=None, override=None):
local_settings[p] = absp local_settings[p] = absp
if 'PLUGIN_PATH' in local_settings: if 'PLUGIN_PATH' in local_settings:
logger.warning('PLUGIN_PATH setting has been replaced by ' logger.warning(
'PLUGIN_PATHS, moving it to the new setting name.') 'PLUGIN_PATH setting has been replaced by '
'PLUGIN_PATHS, moving it to the new setting name.',
extra={'id': 'warn.settings.plugin-path-deprecated'})
local_settings['PLUGIN_PATHS'] = local_settings['PLUGIN_PATH'] local_settings['PLUGIN_PATHS'] = local_settings['PLUGIN_PATH']
del local_settings['PLUGIN_PATH'] del local_settings['PLUGIN_PATH']
if isinstance(local_settings['PLUGIN_PATHS'], six.string_types): if isinstance(local_settings['PLUGIN_PATHS'], six.string_types):
logger.warning("Defining PLUGIN_PATHS setting as string " logger.warning(
"has been deprecated (should be a list)") "Defining PLUGIN_PATHS setting as string "
"has been deprecated (should be a list)",
extra={'id': 'warn.settings.plugin-paths-is-a-string'})
local_settings['PLUGIN_PATHS'] = [local_settings['PLUGIN_PATHS']] local_settings['PLUGIN_PATHS'] = [local_settings['PLUGIN_PATHS']]
elif local_settings['PLUGIN_PATHS'] is not None: elif local_settings['PLUGIN_PATHS'] is not None:
def getabs(path, pluginpath): def getabs(path, pluginpath):
@ -267,7 +271,8 @@ def configure_settings(settings):
logger.warn( logger.warn(
'Detected misconfigured %s (%s), ' 'Detected misconfigured %s (%s), '
'falling back to the default (%s)', 'falling back to the default (%s)',
key, value, DEFAULT_CONFIG[key]) key, value, DEFAULT_CONFIG[key],
extra={'id': 'warn.settings.misconfigured-setting'})
# try to set the different locales, fallback on the default. # try to set the different locales, fallback on the default.
locales = settings.get('LOCALE', DEFAULT_CONFIG['LOCALE']) locales = settings.get('LOCALE', DEFAULT_CONFIG['LOCALE'])
@ -279,14 +284,17 @@ def configure_settings(settings):
except locale.Error: except locale.Error:
pass pass
else: else:
logger.warning("LOCALE option doesn't contain a correct value") logger.warning("LOCALE option doesn't contain a correct value",
extra={'id': 'warn.settings.incorrect-locale'})
if ('SITEURL' in settings): if ('SITEURL' in settings):
# If SITEURL has a trailing slash, remove it and provide a warning # If SITEURL has a trailing slash, remove it and provide a warning
siteurl = settings['SITEURL'] siteurl = settings['SITEURL']
if (siteurl.endswith('/')): if (siteurl.endswith('/')):
settings['SITEURL'] = siteurl[:-1] settings['SITEURL'] = siteurl[:-1]
logger.warning("Removed extraneous trailing slash from SITEURL.") logger.warning(
"Removed extraneous trailing slash from SITEURL.",
extra={'id': 'warn.settings.siteurl-trailing-slash'})
# If SITEURL is defined but FEED_DOMAIN isn't, # If SITEURL is defined but FEED_DOMAIN isn't,
# set FEED_DOMAIN to SITEURL # set FEED_DOMAIN to SITEURL
if 'FEED_DOMAIN' not in settings: if 'FEED_DOMAIN' not in settings:
@ -298,7 +306,8 @@ def configure_settings(settings):
settings.get('WITH_FUTURE_DATES', False): settings.get('WITH_FUTURE_DATES', False):
logger.warning( logger.warning(
"WITH_FUTURE_DATES conflicts with CONTENT_CACHING_LAYER " "WITH_FUTURE_DATES conflicts with CONTENT_CACHING_LAYER "
"set to 'generator', use 'reader' layer instead") "set to 'generator', use 'reader' layer instead",
extra={'id': 'warn.settings.conflicting-cache-setting'})
# Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined # Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined
feed_keys = [ feed_keys = [
@ -313,14 +322,16 @@ def configure_settings(settings):
if any(settings.get(k) for k in feed_keys): if any(settings.get(k) for k in feed_keys):
if not settings.get('SITEURL'): if not settings.get('SITEURL'):
logger.warning('Feeds generated without SITEURL set properly may' logger.warning('Feeds generated without SITEURL set properly may'
' not be valid') ' not be valid',
extra={'id': 'warn.settings.not-set-siteurl'})
if 'TIMEZONE' not in settings: if 'TIMEZONE' not in settings:
logger.warning( logger.warning(
'No timezone information specified in the settings. Assuming' 'No timezone information specified in the settings. Assuming'
' your timezone is UTC for feed generation. Check ' ' your timezone is UTC for feed generation. Check '
'http://docs.getpelican.com/en/latest/settings.html#timezone ' 'http://docs.getpelican.com/en/latest/settings.html#timezone '
'for more information') 'for more information',
extra={'id': 'warn.settings.no-timezone'})
# fix up pagination rules # fix up pagination rules
from pelican.paginator import PaginationRule from pelican.paginator import PaginationRule
@ -342,7 +353,8 @@ def configure_settings(settings):
if old_key in settings: if old_key in settings:
logger.warning( logger.warning(
'Deprecated setting %s, moving it to %s list', 'Deprecated setting %s, moving it to %s list',
old_key, new_key) old_key, new_key,
extra={'id': 'warn.settings.dir-paths-setting-deprecated'})
settings[new_key] = [settings[old_key]] # also make a list settings[new_key] = [settings[old_key]] # also make a list
del settings[old_key] del settings[old_key]
@ -367,15 +379,18 @@ def configure_settings(settings):
if isinstance(settings[PATH_KEY], six.string_types): if isinstance(settings[PATH_KEY], six.string_types):
logger.warning("Detected misconfiguration with %s setting " logger.warning("Detected misconfiguration with %s setting "
"(must be a list), falling back to the default", "(must be a list), falling back to the default",
PATH_KEY) PATH_KEY,
extra={'id': 'warn.settings.setting-must-be-list'})
settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY] settings[PATH_KEY] = DEFAULT_CONFIG[PATH_KEY]
# Save people from declaring MD_EXTENSIONS as a list rather than a dict # Save people from declaring MD_EXTENSIONS as a list rather than a dict
if not isinstance(settings.get('MD_EXTENSIONS', {}), dict): if not isinstance(settings.get('MD_EXTENSIONS', {}), dict):
logger.warning('The format of the MD_EXTENSIONS setting has ' logger.warning(
'changed. It should now be a dict mapping ' 'The format of the MD_EXTENSIONS setting has '
'fully-qualified extension names to their ' 'changed. It should now be a dict mapping '
'configurations. Falling back to the default.') 'fully-qualified extension names to their '
'configurations. Falling back to the default.',
extra={'id': 'warn.settings.bad-format-of-md-extensions'})
settings['MD_EXTENSIONS'] = DEFAULT_CONFIG['MD_EXTENSIONS'] settings['MD_EXTENSIONS'] = DEFAULT_CONFIG['MD_EXTENSIONS']
# Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES # Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES
@ -401,6 +416,7 @@ def configure_settings(settings):
old, new) old, new)
if doc: if doc:
message += ', see {} for details'.format(doc) message += ', see {} for details'.format(doc)
logger.warning(message) logger.warning(message, extra={
'id': 'warn.settings.setting-was-removed'})
return settings return settings

View file

@ -152,7 +152,9 @@ def wp2fields(xml, wp_custpost=False):
title = unescape(item.title.contents[0]) title = unescape(item.title.contents[0])
except IndexError: except IndexError:
title = 'No title [%s]' % item.find('post_name').string title = 'No title [%s]' % item.find('post_name').string
logger.warning('Post "%s" is lacking a proper title', title) logger.warning(
'Post "%s" is lacking a proper title', title,
extra={'id': 'warn.pelican-import.post-no-proper-title'})
filename = item.find('post_name').string filename = item.find('post_name').string
post_id = item.find('post_id').string post_id = item.find('post_id').string
@ -671,7 +673,9 @@ def download_attachments(output_path, urls):
locations.append(os.path.join(localpath, filename)) locations.append(os.path.join(localpath, filename))
except (URLError, IOError) as e: except (URLError, IOError) as e:
# Python 2.7 throws an IOError rather Than URLError # Python 2.7 throws an IOError rather Than URLError
logger.warning("No file could be downloaded from %s\n%s", url, e) logger.warning(
"No file could be downloaded from %s\n%s", url, e,
extra={'id': 'warn.pelican-import.no-attachments-downloaded'})
return locations return locations

View file

@ -97,7 +97,8 @@ class URLWrapper(object):
setting = "%s_%s" % (self.__class__.__name__.upper(), key) setting = "%s_%s" % (self.__class__.__name__.upper(), key)
value = self.settings[setting] value = self.settings[setting]
if not isinstance(value, six.string_types): if not isinstance(value, six.string_types):
logger.warning('%s is set to %s', setting, value) logger.warning('%s is set to %s', setting, value, extra={
'id': 'warn.urlwrappers.setting-is-not-a-string'})
return value return value
else: else:
if get_page_name: if get_page_name:

View file

@ -206,9 +206,11 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None):
message.append( message.append(
' and will be removed by version {}'.format(version)) ' and will be removed by version {}'.format(version))
message.append('. Use {} instead.'.format(new)) message.append('. Use {} instead.'.format(new))
logger.warning(''.join(message)) logger.warning(''.join(message), extra={
'id': 'warn.utils.attribute-deprecated'})
logger.debug(''.join(six.text_type(x) for x logger.debug(''.join(six.text_type(x) for x
in traceback.format_stack())) in traceback.format_stack()),
extra={'id': 'debug.utils.attribute-deprecated'})
def fget(self): def fget(self):
_warn() _warn()
@ -318,7 +320,8 @@ def copy(source, destination, ignores=None):
def walk_error(err): def walk_error(err):
logger.warning("While copying %s: %s: %s", logger.warning("While copying %s: %s: %s",
source_, err.filename, err.strerror) source_, err.filename, err.strerror,
extra={'id': 'warn.utils.copy-walk-error'})
source_ = os.path.abspath(os.path.expanduser(source)) source_ = os.path.abspath(os.path.expanduser(source))
destination_ = os.path.abspath(os.path.expanduser(destination)) destination_ = os.path.abspath(os.path.expanduser(destination))
@ -328,24 +331,29 @@ def copy(source, destination, ignores=None):
if any(fnmatch.fnmatch(os.path.basename(source), ignore) if any(fnmatch.fnmatch(os.path.basename(source), ignore)
for ignore in ignores): for ignore in ignores):
logger.info('Not copying %s due to ignores', source_) logger.info('Not copying %s due to ignores', source_,
extra={'id': 'info.utils.not-copying-ignore'})
return return
if os.path.isfile(source_): if os.path.isfile(source_):
dst_dir = os.path.dirname(destination_) dst_dir = os.path.dirname(destination_)
if not os.path.exists(dst_dir): if not os.path.exists(dst_dir):
logger.info('Creating directory %s', dst_dir) logger.info('Creating directory %s', dst_dir,
extra={'id': 'info.utils.creating-directory'})
os.makedirs(dst_dir) os.makedirs(dst_dir)
logger.info('Copying %s to %s', source_, destination_) logger.info('Copying %s to %s', source_, destination_,
extra={'id': 'info.utils.copying'})
shutil.copy2(source_, destination_) shutil.copy2(source_, destination_)
elif os.path.isdir(source_): elif os.path.isdir(source_):
if not os.path.exists(destination_): if not os.path.exists(destination_):
logger.info('Creating directory %s', destination_) logger.info('Creating directory %s', destination_,
extra={'id': 'info.utils.creating-directory'})
os.makedirs(destination_) os.makedirs(destination_)
if not os.path.isdir(destination_): if not os.path.isdir(destination_):
logger.warning('Cannot copy %s (a directory) to %s (a file)', logger.warning('Cannot copy %s (a directory) to %s (a file)',
source_, destination_) source_, destination_,
extra={'id': 'warn.utils.cannot-copy-dir-to-file'})
return return
for src_dir, subdirs, others in os.walk(source_): for src_dir, subdirs, others in os.walk(source_):
@ -358,7 +366,8 @@ def copy(source, destination, ignores=None):
for i in ignores)) for i in ignores))
if not os.path.isdir(dst_dir): if not os.path.isdir(dst_dir):
logger.info('Creating directory %s', dst_dir) logger.info('Creating directory %s', dst_dir,
extra={'id': 'warn.utils.creating-directory'})
# Parent directories are known to exist, so 'mkdir' suffices. # Parent directories are known to exist, so 'mkdir' suffices.
os.mkdir(dst_dir) os.mkdir(dst_dir)
@ -366,19 +375,22 @@ def copy(source, destination, ignores=None):
src_path = os.path.join(src_dir, o) src_path = os.path.join(src_dir, o)
dst_path = os.path.join(dst_dir, o) dst_path = os.path.join(dst_dir, o)
if os.path.isfile(src_path): if os.path.isfile(src_path):
logger.info('Copying %s to %s', src_path, dst_path) logger.info('Copying %s to %s', src_path, dst_path,
extra={'id': 'info.utils.copying'})
shutil.copy2(src_path, dst_path) shutil.copy2(src_path, dst_path)
else: else:
logger.warning('Skipped copy %s (not a file or ' logger.warning('Skipped copy %s (not a file or '
'directory) to %s', 'directory) to %s',
src_path, dst_path) src_path, dst_path,
extra={'id': 'warn.utils.skipped-copy'})
def clean_output_dir(path, retention): def clean_output_dir(path, retention):
"""Remove all files from output directory except those in retention list""" """Remove all files from output directory except those in retention list"""
if not os.path.exists(path): if not os.path.exists(path):
logger.debug("Directory already removed: %s", path) logger.debug("Directory already removed: %s", path,
extra={'id': 'warn.utils.directory-already-removed'})
return return
if not os.path.isdir(path): if not os.path.isdir(path):
@ -393,18 +405,21 @@ def clean_output_dir(path, retention):
file = os.path.join(path, filename) file = os.path.join(path, filename)
if any(filename == retain for retain in retention): if any(filename == retain for retain in retention):
logger.debug("Skipping deletion; %s is on retention list: %s", logger.debug("Skipping deletion; %s is on retention list: %s",
filename, file) filename, file,
extra={'id': 'debug.utils.skipping-deletion'})
elif os.path.isdir(file): elif os.path.isdir(file):
try: try:
shutil.rmtree(file) shutil.rmtree(file)
logger.debug("Deleted directory %s", file) logger.debug("Deleted directory %s", file,
extra={'id': 'debug.utils.deleted-directory'})
except Exception as e: except Exception as e:
logger.error("Unable to delete directory %s; %s", logger.error("Unable to delete directory %s; %s",
file, e) file, e)
elif os.path.isfile(file) or os.path.islink(file): elif os.path.isfile(file) or os.path.islink(file):
try: try:
os.remove(file) os.remove(file)
logger.debug("Deleted file/link %s", file) logger.debug("Deleted file/link %s", file,
extra={'id': 'debug.utils.deleted-file'})
except Exception as e: except Exception as e:
logger.error("Unable to delete file %s; %s", file, e) logger.error("Unable to delete file %s; %s", file, e)
else: else:
@ -629,10 +644,14 @@ def process_translations(content_list, order_by=None):
lang_items = list(lang_items) lang_items = list(lang_items)
len_ = len(lang_items) len_ = len(lang_items)
if len_ > 1: if len_ > 1:
logger.warning('There are %s variants of "%s" with lang %s', logger.warning(
len_, slug, lang) 'There are %s variants of "%s" with lang %s',
len_, slug, lang,
extra={'id': 'warn.utils.many-translation-variants'})
for x in lang_items: for x in lang_items:
logger.warning('\t%s', x.source_path) logger.warning(
'\t%s', x.source_path,
extra={'id': 'warn.utils.many-translation-variants'})
# find items with default language # find items with default language
default_lang_items = list(filter( default_lang_items = list(filter(
@ -647,7 +666,8 @@ def process_translations(content_list, order_by=None):
logger.warning( logger.warning(
'Empty slug for %s. You can fix this by ' 'Empty slug for %s. You can fix this by '
'adding a title or a slug to your content', 'adding a title or a slug to your content',
default_lang_items[0].source_path) default_lang_items[0].source_path,
extra={'id': 'warn.utils.empty-slug'})
index.extend(default_lang_items) index.extend(default_lang_items)
translations.extend([x for x in items if x not in default_lang_items]) translations.extend([x for x in items if x not in default_lang_items])
for a in items: for a in items:
@ -677,11 +697,13 @@ def process_translations(content_list, order_by=None):
except AttributeError: except AttributeError:
logger.warning( logger.warning(
'There is no "%s" attribute in the item ' 'There is no "%s" attribute in the item '
'metadata. Defaulting to slug order.', order_by) 'metadata. Defaulting to slug order.', order_by,
extra={'id': 'warn.utils.translations-index-sorting'})
else: else:
logger.warning( logger.warning(
'Invalid *_ORDER_BY setting (%s).' 'Invalid *_ORDER_BY setting (%s).'
'Valid options are strings and functions.', order_by) 'Valid options are strings and functions.', order_by,
extra={'id': 'warn.utils.order-by-is-invalid'})
return index, translations return index, translations
@ -704,7 +726,9 @@ def folder_watcher(path, extensions, ignores=[]):
try: try:
yield os.stat(os.path.join(root, f)).st_mtime yield os.stat(os.path.join(root, f)).st_mtime
except OSError as e: except OSError as e:
logger.warning('Caught Exception: %s', e) logger.warning(
'Caught Exception: %s', e,
extra={'id': 'warn.utils.caugh-exception'})
LAST_MTIME = 0 LAST_MTIME = 0
while True: while True:
@ -727,7 +751,9 @@ def file_watcher(path):
try: try:
mtime = os.stat(path).st_mtime mtime = os.stat(path).st_mtime
except OSError as e: except OSError as e:
logger.warning('Caught Exception: %s', e) logger.warning(
'Caught Exception: %s', e,
extra={'id': 'warn.utils.caugh-exception'})
continue continue
if mtime > LAST_MTIME: if mtime > LAST_MTIME:

View file

@ -74,11 +74,13 @@ class Writer(object):
raise RuntimeError('File %s is set to be overridden twice' raise RuntimeError('File %s is set to be overridden twice'
% filename) % filename)
else: else:
logger.info('Skipping %s', filename) logger.info('Skipping %s', filename,
extra={'id': 'info.writers.skip'})
filename = os.devnull filename = os.devnull
elif filename in self._written_files: elif filename in self._written_files:
if override: if override:
logger.info('Overwriting %s', filename) logger.info('Overwriting %s', filename,
extra={'id': 'info.writers.overwrite'})
else: else:
raise RuntimeError('File %s is to be overwritten' % filename) raise RuntimeError('File %s is to be overwritten' % filename)
if override: if override:
@ -129,7 +131,8 @@ class Writer(object):
encoding = 'utf-8' if six.PY3 else None encoding = 'utf-8' if six.PY3 else None
with self._open_w(complete_path, encoding, override_output) as fp: with self._open_w(complete_path, encoding, override_output) as fp:
feed.write(fp, 'utf-8') feed.write(fp, 'utf-8')
logger.info('Writing %s', complete_path) logger.info('Writing %s', complete_path,
extra={'id': 'info.writers.write-feed'})
signals.feed_written.send( signals.feed_written.send(
complete_path, context=context, feed=feed) complete_path, context=context, feed=feed)
@ -174,7 +177,8 @@ class Writer(object):
with self._open_w(path, 'utf-8', override=override) as f: with self._open_w(path, 'utf-8', override=override) as f:
f.write(output) f.write(output)
logger.info('Writing %s', path) logger.info('Writing %s', path,
extra={'id': 'into.writers.render-template-to-file'})
# Send a signal to say we're writing a file with some specific # Send a signal to say we're writing a file with some specific
# local context. # local context.