diff --git a/docs/settings.rst b/docs/settings.rst index fbf99b1c..5059adf5 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -107,7 +107,7 @@ Setting name (followed by default value, if any) and the message to be ignored. For example: ``[(logging.WARN, 'TAG_SAVE_AS is set to False')]`` ``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 process or ignore. For example, to avoid processing .html files, 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`` setting. -For example: ``['warn.img.alt.empty']`` +For example: ``['warn.readers.empty-alt-attribute']`` .. _reading_only_modified_content: diff --git a/pelican/__init__.py b/pelican/__init__.py index 30b3c2f8..003764af 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -55,7 +55,8 @@ class Pelican(object): def init_path(self): 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, '') def init_plugins(self): @@ -67,7 +68,8 @@ class Pelican(object): for plugin in self.settings['PLUGINS']: # if it's a string, then import it 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: plugin = __import__(plugin, globals(), locals(), str('module')) @@ -76,10 +78,12 @@ class Pelican(object): "Cannot load plugin `%s`\n%s", plugin, e) continue - logger.debug("Registering plugin `%s`", plugin.__name__) + logger.debug("Registering plugin `%s`", plugin.__name__, + extra={'id': 'debug.init.registering-plugin'}) plugin.register() 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 def _handle_deprecation(self): @@ -87,7 +91,8 @@ class Pelican(object): if self.settings.get('CLEAN_URLS', False): logger.warning('Found deprecated `CLEAN_URLS` in settings.' ' 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_LANG_URL'] = '{slug}-{lang}/' @@ -96,17 +101,21 @@ class Pelican(object): for setting in ('ARTICLE_URL', 'ARTICLE_LANG_URL', 'PAGE_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'): 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') if self.settings.get('ARTICLE_PERMALINK_STRUCTURE', False): - logger.warning('Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in' - ' settings. Modifying the following settings for' - ' the same behaviour.') + logger.warning( + 'Found deprecated `ARTICLE_PERMALINK_STRUCTURE` in' + ' settings. Modifying the following settings for' + ' the same behaviour.', + extra={'id': 'warn.init.article-permalink-structure'}) structure = self.settings['ARTICLE_PERMALINK_STRUCTURE'] @@ -126,7 +135,9 @@ class Pelican(object): 'PAGE_SAVE_AS', 'PAGE_LANG_SAVE_AS'): self.settings[setting] = os.path.join(structure, 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'), ('CATEGORY_FEED', 'CATEGORY_FEED_ATOM'), @@ -137,7 +148,8 @@ class Pelican(object): 'to %(old)s in your settings and theme for the same ' 'behavior. Temporarily setting %(old)s for backwards ' 'compatibility.', - {'new': new, 'old': old} + {'new': new, 'old': old}, + extra={'id': 'warn.init.feed-setting-deprecated'} ) self.settings[old] = self.settings[new] @@ -229,7 +241,8 @@ class Pelican(object): for v in value: 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) # StaticGenerator must run last, so it can identify files that @@ -247,11 +260,13 @@ class Pelican(object): else: writer = writers[0] if writers_found == 1: - logger.debug('Found writer: %s', writer) + logger.debug('Found writer: %s', writer, + extra={'id': 'debug.init.found-writer'}) else: logger.warning( '%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) @@ -385,8 +400,10 @@ def main(): args = parse_arguments() init(args.verbosity, args.fatal) - logger.debug('Pelican version: %s', __version__) - logger.debug('Python version: %s', sys.version.split()[0]) + logger.debug('Pelican version: %s', __version__, + extra={'id': 'debug.init.pelican-version'}) + logger.debug('Python version: %s', sys.version.split()[0], + extra={'id': 'debug.init.python-version'}) try: pelican, settings = get_instance(args) @@ -455,33 +472,42 @@ def main(): ', '.join(k for k, v in modified.items() if v))) 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: - 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() except KeyboardInterrupt: - logger.warning("Keyboard interrupt, quitting.") + logger.warning( + "Keyboard interrupt, quitting.", + extra={'id': 'warn.init.keyboard-interrupt'}) break except Exception as e: if (args.verbosity == logging.DEBUG): raise logger.warning( - 'Caught exception "%s". Reloading.', e) + 'Caught exception "%s". Reloading.', e, + extra={'id': 'warn.init.caught-exception'}) finally: time.sleep(.5) # sleep to avoid cpu load else: 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: - 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() diff --git a/pelican/cache.py b/pelican/cache.py index e6c10cb9..a918c59c 100644 --- a/pelican/cache.py +++ b/pelican/cache.py @@ -38,14 +38,16 @@ class FileDataCacher(object): except (IOError, OSError) as err: logger.debug('Cannot load cache %s (this is normal on first ' 'run). Proceeding with empty cache.\n%s', - self._cache_path, err) + self._cache_path, err, + extra={'id': 'debug.cache.load'}) self._cache = {} except pickle.PickleError as err: logger.warning('Cannot unpickle cache %s, cache may be using ' 'an incompatible protocol (see pelican ' 'caching docs). ' 'Proceeding with empty cache.\n%s', - self._cache_path, err) + self._cache_path, err, + extra={'id': 'warn.cache.unpickle'}) self._cache = {} else: self._cache = {} @@ -71,7 +73,8 @@ class FileDataCacher(object): pickle.dump(self._cache, fhandle) except (IOError, OSError, pickle.PicklingError) as err: 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): @@ -100,7 +103,8 @@ class FileStampDataCacher(FileDataCacher): self._filestamp_func = filestamp_func 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 def cache_data(self, filename, data): @@ -122,7 +126,8 @@ class FileStampDataCacher(FileDataCacher): return self._filestamp_func(filename) except (IOError, OSError, TypeError) as err: logger.warning('Cannot get modification stamp for %s\n\t%s', - filename, err) + filename, err, + extra={'id': 'warn.cache.get-timestamp'}) return '' def get_cached_data(self, filename, default=None): diff --git a/pelican/contents.py b/pelican/contents.py index 9b6aa971..e55191cc 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -241,7 +241,8 @@ class Content(object): logger.warning( "%s used {attach} link syntax on a " "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 = origin.replace('\\', '/') # for Windows paths. else: @@ -249,7 +250,8 @@ class Content(object): "Unable to find `%s`, skipping url replacement.", value.geturl(), extra={ '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': origin = '/'.join((siteurl, Category(path, self.settings).url)) elif what == 'tag': @@ -262,7 +264,8 @@ class Content(object): logger.warning( "Replacement Indicator '%s' not recognized, " "skipping replacement", - what) + what, extra={ + 'id': 'warn.contents.unknown-replacement-indicator'}) # keep all other parts, such as query, fragment, etc. parts = list(value) @@ -313,7 +316,8 @@ class Content(object): """deprecated function to access summary""" 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 @summary.setter @@ -447,7 +451,8 @@ class Static(Page): "{filename} link behavior instead.", content.get_relative_source_path(), 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 # with user-defined overrides that might be in EXTRA_PATH_METADATA. diff --git a/pelican/generators.py b/pelican/generators.py index d9923e35..8a1b3341 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -71,7 +71,8 @@ class Generator(object): 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 self.env.filters.update({'strftime': DateFormatter()}) @@ -728,7 +729,8 @@ class StaticGenerator(Generator): save_as = os.path.join(self.output_path, sc.save_as) mkdir_p(os.path.dirname(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): @@ -743,7 +745,8 @@ class SourceFileGenerator(Generator): copy(obj.source_path, dest) 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']): self._create_source(obj) for obj_trans in obj.translations: diff --git a/pelican/readers.py b/pelican/readers.py index 0e19a857..e8cb111e 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -269,7 +269,8 @@ class MarkdownReader(BaseReader): logger.warning( 'Duplicate definition of `%s` ' '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]) elif len(value) > 1: # handle list metadata as list of string @@ -389,9 +390,11 @@ class HTMLReader(BaseReader): if name is None: attr_list = ['{}="{}"'.format(k, v) for k, v in attrs] attr_serialized = ', '.join(attr_list) - logger.warning("Meta tag in file %s does not have a 'name' " - "attribute, skipping. Attributes: %s", - self._filename, attr_serialized) + logger.warning( + "Meta tag in file %s does not have a 'name' " + "attribute, skipping. Attributes: %s", + self._filename, attr_serialized, + extra={'id': 'warn.readers.unknown-meta-tag-attribute'}) return name = name.lower() contents = self._attr_value(attrs, 'content', '') @@ -404,7 +407,8 @@ class HTMLReader(BaseReader): self._filename, extra={'limit_msg': "Other files have meta tag " "attribute 'contents' that should " - "be changed to 'content'"}) + "be changed to 'content'", + 'id': 'warn.readers.contents-meta-tag-used'}) if name == 'keywords': name = 'tags' @@ -444,8 +448,10 @@ class Readers(FileStampDataCacher): for cls in [BaseReader] + BaseReader.__subclasses__(): if not cls.enabled: - logger.debug('Missing dependencies for %s', - ', '.join(cls.file_extensions)) + logger.debug( + 'Missing dependencies for %s', + ', '.join(cls.file_extensions), + extra={'id': 'debug.readers.missing-dependencies'}) continue for ext in cls.file_extensions: @@ -484,7 +490,8 @@ class Readers(FileStampDataCacher): source_path = posixize_path(os.path.relpath(path, base_path)) logger.debug( 'Read file %s -> %s', - source_path, content_class.__name__) + source_path, content_class.__name__, + extra={'id': 'debug.readers.read-file'}) if not fmt: _, ext = os.path.splitext(os.path.basename(path)) @@ -497,7 +504,8 @@ class Readers(FileStampDataCacher): if preread_signal: logger.debug( '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) reader = self.readers[fmt] @@ -556,7 +564,8 @@ class Readers(FileStampDataCacher): logger.debug( 'Signal %s.send(%s, )', context_signal.name, - context_sender) + context_sender, + extra={'id': 'debug.readers.signal-send-metadata'}) context_signal.send(context_sender, 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', os.path.basename(match[1] + match[5]), path, 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): diff --git a/pelican/settings.py b/pelican/settings.py index 5cff3f1d..18181561 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -156,13 +156,17 @@ def read_settings(path=None, override=None): local_settings[p] = absp if 'PLUGIN_PATH' in local_settings: - logger.warning('PLUGIN_PATH setting has been replaced by ' - 'PLUGIN_PATHS, moving it to the new setting name.') + logger.warning( + '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'] del local_settings['PLUGIN_PATH'] if isinstance(local_settings['PLUGIN_PATHS'], six.string_types): - logger.warning("Defining PLUGIN_PATHS setting as string " - "has been deprecated (should be a list)") + logger.warning( + "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']] elif local_settings['PLUGIN_PATHS'] is not None: def getabs(path, pluginpath): @@ -267,7 +271,8 @@ def configure_settings(settings): logger.warn( 'Detected misconfigured %s (%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. locales = settings.get('LOCALE', DEFAULT_CONFIG['LOCALE']) @@ -279,14 +284,17 @@ def configure_settings(settings): except locale.Error: pass 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 has a trailing slash, remove it and provide a warning siteurl = settings['SITEURL'] if (siteurl.endswith('/')): 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, # set FEED_DOMAIN to SITEURL if 'FEED_DOMAIN' not in settings: @@ -298,7 +306,8 @@ def configure_settings(settings): settings.get('WITH_FUTURE_DATES', False): logger.warning( "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 feed_keys = [ @@ -313,14 +322,16 @@ def configure_settings(settings): if any(settings.get(k) for k in feed_keys): if not settings.get('SITEURL'): 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: logger.warning( 'No timezone information specified in the settings. Assuming' ' your timezone is UTC for feed generation. Check ' '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 from pelican.paginator import PaginationRule @@ -342,7 +353,8 @@ def configure_settings(settings): if old_key in settings: logger.warning( '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 del settings[old_key] @@ -367,15 +379,18 @@ def configure_settings(settings): if isinstance(settings[PATH_KEY], six.string_types): logger.warning("Detected misconfiguration with %s setting " "(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] # Save people from declaring MD_EXTENSIONS as a list rather than a dict if not isinstance(settings.get('MD_EXTENSIONS', {}), dict): - logger.warning('The format of the MD_EXTENSIONS setting has ' - 'changed. It should now be a dict mapping ' - 'fully-qualified extension names to their ' - 'configurations. Falling back to the default.') + logger.warning( + 'The format of the MD_EXTENSIONS setting has ' + 'changed. It should now be a dict mapping ' + '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'] # Add {PAGE,ARTICLE}_PATHS to {ARTICLE,PAGE}_EXCLUDES @@ -401,6 +416,7 @@ def configure_settings(settings): old, new) if doc: message += ', see {} for details'.format(doc) - logger.warning(message) + logger.warning(message, extra={ + 'id': 'warn.settings.setting-was-removed'}) return settings diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 204ab7b0..8ace0965 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -152,7 +152,9 @@ def wp2fields(xml, wp_custpost=False): title = unescape(item.title.contents[0]) except IndexError: 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 post_id = item.find('post_id').string @@ -671,7 +673,9 @@ def download_attachments(output_path, urls): locations.append(os.path.join(localpath, filename)) except (URLError, IOError) as e: # 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 diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index 7659471d..65f3e71f 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -97,7 +97,8 @@ class URLWrapper(object): setting = "%s_%s" % (self.__class__.__name__.upper(), key) value = self.settings[setting] 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 else: if get_page_name: diff --git a/pelican/utils.py b/pelican/utils.py index 4e729361..080e587d 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -206,9 +206,11 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None): message.append( ' and will be removed by version {}'.format(version)) 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 - in traceback.format_stack())) + in traceback.format_stack()), + extra={'id': 'debug.utils.attribute-deprecated'}) def fget(self): _warn() @@ -318,7 +320,8 @@ def copy(source, destination, ignores=None): def walk_error(err): 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)) 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) 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 if os.path.isfile(source_): dst_dir = os.path.dirname(destination_) 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) - 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_) elif os.path.isdir(source_): 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_) if not os.path.isdir(destination_): 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 for src_dir, subdirs, others in os.walk(source_): @@ -358,7 +366,8 @@ def copy(source, destination, ignores=None): for i in ignores)) 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. os.mkdir(dst_dir) @@ -366,19 +375,22 @@ def copy(source, destination, ignores=None): src_path = os.path.join(src_dir, o) dst_path = os.path.join(dst_dir, o) 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) else: logger.warning('Skipped copy %s (not a file or ' 'directory) to %s', - src_path, dst_path) + src_path, dst_path, + extra={'id': 'warn.utils.skipped-copy'}) def clean_output_dir(path, retention): """Remove all files from output directory except those in retention list""" 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 if not os.path.isdir(path): @@ -393,18 +405,21 @@ def clean_output_dir(path, retention): file = os.path.join(path, filename) if any(filename == retain for retain in retention): 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): try: 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: logger.error("Unable to delete directory %s; %s", file, e) elif os.path.isfile(file) or os.path.islink(file): try: 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: logger.error("Unable to delete file %s; %s", file, e) else: @@ -629,10 +644,14 @@ def process_translations(content_list, order_by=None): lang_items = list(lang_items) len_ = len(lang_items) if len_ > 1: - logger.warning('There are %s variants of "%s" with lang %s', - len_, slug, lang) + logger.warning( + 'There are %s variants of "%s" with lang %s', + len_, slug, lang, + extra={'id': 'warn.utils.many-translation-variants'}) 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 default_lang_items = list(filter( @@ -647,7 +666,8 @@ def process_translations(content_list, order_by=None): logger.warning( 'Empty slug for %s. You can fix this by ' '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) translations.extend([x for x in items if x not in default_lang_items]) for a in items: @@ -677,11 +697,13 @@ def process_translations(content_list, order_by=None): except AttributeError: logger.warning( '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: logger.warning( '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 @@ -704,7 +726,9 @@ def folder_watcher(path, extensions, ignores=[]): try: yield os.stat(os.path.join(root, f)).st_mtime 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 while True: @@ -727,7 +751,9 @@ def file_watcher(path): try: mtime = os.stat(path).st_mtime except OSError as e: - logger.warning('Caught Exception: %s', e) + logger.warning( + 'Caught Exception: %s', e, + extra={'id': 'warn.utils.caugh-exception'}) continue if mtime > LAST_MTIME: diff --git a/pelican/writers.py b/pelican/writers.py index 1f9262b1..f94afdd9 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -74,11 +74,13 @@ class Writer(object): raise RuntimeError('File %s is set to be overridden twice' % filename) else: - logger.info('Skipping %s', filename) + logger.info('Skipping %s', filename, + extra={'id': 'info.writers.skip'}) filename = os.devnull elif filename in self._written_files: if override: - logger.info('Overwriting %s', filename) + logger.info('Overwriting %s', filename, + extra={'id': 'info.writers.overwrite'}) else: raise RuntimeError('File %s is to be overwritten' % filename) if override: @@ -129,7 +131,8 @@ class Writer(object): encoding = 'utf-8' if six.PY3 else None with self._open_w(complete_path, encoding, override_output) as fp: 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( complete_path, context=context, feed=feed) @@ -174,7 +177,8 @@ class Writer(object): with self._open_w(path, 'utf-8', override=override) as f: 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 # local context.