Question: When do you choose which log level?
I'm currently writing an article about logging with pure Python. As a preparation, I try to find best practices in big projects.
I have some specific questions:
- Why do you use logging at all and which problem does it solve for you?
- When do you use log.warning vs log.error vs log.critical?
- When would you use log.debug vs log.info vs a debugger? Should log.debug be in master / production code?
- Are there any best-practices people often miss? Or anything you think is super important to get right about logging?
Below, I've copied some of the places where I've found different kinds of log-messages in meld.
log.debug
meld/meldapp.py-370- except ValueError as err:
meld/meldapp.py-371- error = err
meld/meldapp.py:372: log.debug("Couldn't open comparison: %s", error, exc_info=True)
log.warning
meld/undo.py-254- if self.group is None:
meld/undo.py:255: log.warning('Tried to end a non-existent group')
--
meld/sourceview.py-34- if not encoding:
meld/sourceview.py:35: log.warning('Invalid charset "%s" skipped', charset)
--
# This one is pretty confusing. Why does a warning start with "error"?
meld/filters.py-23- try:
meld/filters.py-24- compiled = re.compile(regex, flags)
meld/filters.py-25- except re.error:
meld/filters.py:26: log.warning(
meld/filters.py-27- 'Error compiling regex {!r} with flags {!r}'.format(regex, flags))
--
meld/meldbuffer.py-120- if not isinstance(value, str):
meld/meldbuffer.py:121: log.warning('Invalid label ignored "%r"', value)
meld/meldbuffer.py-122- return
log.error
bin/meld-341- # Set excepthook so that we get exceptions on Windows
bin/meld-342- def logging_except_hook(exc_type, exc_instance, tb):
bin/meld:343: log.error(
bin/meld-344- 'Unhandled exception', exc_info=(exc_type, exc_instance, tb))
--
meld/matchers/helpers.py-35- except Exception as e:
meld/matchers/helpers.py:36: log.error("Exception while running diff: %s", e)
--
meld/ui/gtkcompat.py-52- if name[0].isupper():
meld/ui/gtkcompat.py-53- gtype = GObject.GType.from_name(name)
meld/ui/gtkcompat.py-54- if gtype == GObject.TYPE_INVALID:
meld/ui/gtkcompat.py:55: log.error('Unknown type name "%s"', name)
--
meld/ui/gtkcompat.py:79: log.error('Unknown pseudo-class :%s', name)
--
meld/melddoc.py-42- if not any(replacements):
meld/melddoc.py-43- return [custom_command, path]
meld/melddoc.py-44- elif not all(r in (None, 'file', 'line') for r in replacements):
meld/melddoc.py:45: log.error("Unsupported fields found", )
--
(This one was in there multiple times)
meld/melddoc.py-186- if not action:
meld/melddoc.py:187: log.error(f'No action {action_name!r} found')
--
meld/vcview.py-681- """
meld/vcview.py-682- if not self.has_command(command):
meld/vcview.py:683: log.error("Couldn't understand command %s", command)
meld/vcview.py-684- return
meld/vcview.py-685-
meld/vcview.py-686- if not isinstance(files, list):
meld/vcview.py:687: log.error("Invalid files argument to '%s': %r", command, files)
--
meld/filediff.py-845- try:
meld/filediff.py-846- chunk_action = ChunkAction(action)
meld/filediff.py-847- except ValueError:
meld/filediff.py:848: log.error('Invalid chunk action %s', action)
log.critical
meld/ui/util.py-37- except AttributeError:
meld/ui/util.py-38- if i == 0:
meld/ui/util.py:39: log.critical(
meld/ui/util.py-40- f"Tried to map missing attribute {key}")
--
meld/chunkmap.py-77- if not self.adjustment:
meld/chunkmap.py:78: log.critical(
meld/chunkmap.py-79- f'{self.__gtype_name__} initialized without an adjustment')
meld/chunkmap.py-80- return Gtk.DrawingArea.do_realize(self)