Logging#

Loggers record diagnostics: notes that help a developer or a user troubleshoot a problem, and warnings about conditions that are surprising but do not stop the command. Command results go through the reporter.

The conventions are:

  • Every module creates one logger named after itself, at import time:

    from logging import getLogger
    
    log = getLogger(__name__)
    
  • Library code emits records and does not configure handlers, levels, or propagation. When conda runs as a CLI, conda.cli.main.init_loggers attaches the handlers and sets the level from the verbosity flags. When another program imports conda, that program configures logging.

  • Use lazy %-style arguments rather than f-strings so formatting only happens if the record is emitted.

  • Pass exc_info=True to log.debug to record a traceback without showing it to the user.

Level

Use for

Visible when

debug

Internal state, decisions taken, caught-and-ignored exceptions

-vvv / --debug

info

Notable steps in normal operation

-vv

warning

Something is wrong or degraded, but conda can continue

Always (default level)

The -v flags map to levels in context.log_level:

Flags

context.verbosity

Logging level

(none)

0

WARNING

-v

1

WARNING (and more detailed command output)

-vv

2

INFO

-vvv / --debug

3

DEBUG

-vvvv / --trace

4

TRACE

From conda/core/envs_manager.py:

log.warning(
    "Unable to register environment. Could not create %s. Reason: %s",
    user_environments_txt_directory,
    exc,
)

A filter on the CLI handlers redacts channel tokens in URLs (/t/<token>/). Log those URLs as they are. Do not log other secrets.

See Choosing between a logger and the reporter for whether to log a message or render it.

How conda configures logging#

Module-level loggers named after __name__ form a hierarchy. conda.core.link sits under conda.core, which sits under conda. A parent’s level and handlers apply to its children unless a child overrides them.

conda.gateways.logging.initialize_logging sets the conda logger to WARNING and attaches a stderr handler with propagate = False, so conda records do not reach the root logger. conda.cli.main.init_loggers calls that function, then set_log_level(context.log_level). That sets the handler level from the verbosity flags and also attaches stderr handlers on the root logger and the requests loggers. Importing conda as a library does none of this. The embedding application configures logging.