How to configure logging in your charm

Logging in charms is useful for many reasons, from debugging as the charm developer, to tracking the progress and status of deployed charms as an administrator. There are no special logging constructs in the Ops library, you can simply use the standard Python logging module.

The Juju command-line tool provides many options to customise its log output, which may be useful when debugging specific applications and services.

The default template initialised by charmcraft init already includes the required set up for this, and an example of its use:

import logging
# ...
logger = logging.getLogger(__name__)

class HelloOperatorCharm(ops.CharmBase):
    # ...

    def _on_config_changed(self, _):
        current = self.config["thing"]
        if current not in self._stored.things:
            # Note the use of the logger here
            logger.debug("found a new thing: %r", current)
            self._stored.things.append(current)

As with any standard invocation of the Python logging module, there are several log levels available depending on the type of information you wish to convey. Keep developer specific logging to a minimum, and use logger.debug() for such output.

The default logging level for a Juju model is INFO. To see DEBUG level messages, you should change the model configuration: juju model-config logging-config="<root>=DEBUG"

Note that some logging is performed automatically by the Juju controller, for example when an event handler is called. More information about Juju’s logging can be found in the Juju docs. Try not to replicate this behaviour in your own code. If you are debugging a problem, ensure you comment out or remove large information dumps (such as config files, etc.) from the logging once you are finished.

When passing messages to the logger, do not build the strings yourself. Allow the logger to do this for you as required by the specified log level. An example is shown below:

# Do this!
logger.info("Got some information %s", info)
# Don't do this
logger.info("Got some information {}".format(info))
# Or this ...
logger.info(f"Got some more information {more_info}")

When passing messages to the logger, do not build the strings yourself. Allow the logger to do this for you as required by the specified log level. An example is shown below:

This part could do with some clarification. It says it can do this as required by the specified log level, but doesn’t clarify how the specified log level will change the outcome.

Will it sometimes include or not include the interpolated strings, or, is it just a guide to use the loggers inbuilt “printf” instead of the other functions so that it doesn’t have to spend time actually building the string if the log level isn’t active?

This :slight_smile: Thanks for the feedback :slight_smile:

To help clarify potential confusion about why printf-style formatting is preferred over f-strings, it might be helpful to also source some information from blog posts out there such as the following one: https://blog.pilosus.org/posts/2020/01/24/python-f-strings-in-logging/

I’m thinking this page could be a good place for including guidelines for log levels. I imagine it would make admin life easier knowing what kind of stuff to expect, generally, from different logging levels.

Here’s a draft. Wdyt? CC: @tmihoc @0x12b @mthaddon @przemek-lal

Intentionally suppress

  • DEBUG logs from imported libs such as httpx and httpcore.

DEBUG

  • Workload manifest changes due to pebble operations (e.g. pushed a new cert, updated pebble layer).
  • Dump charm config options on config-changed.

INFO

  • Charm-code-initiated service restart.
  • Relation created/broken and config changed. Currently ops gives us Emit logs for all hooks at DEBUG level, but relation created/broken and config-changed can be elevated to INFO for easier troubleshooting.

Warning

TODO

Error

  • Messages that accompany BlockedStatus.

I agree having some standards/recommendations on what should be logged for different levels makes sense, and I think we should add it to https://juju.is/docs/sdk/styleguide#heading--logging once we have an agreement on it.

The specifics of what to log at each level does seems quite tricky though. Perhaps we should look at how this is done in some particular charms and then we can decide on whether they seem right, need adjustment, and what patterns we can extract from them to apply to other charms.