How best to warn in Python: logs or `warnings`?

The question has come up on a code review whether we should use the warnings module or logging to warn in code that doesn’t otherwise use a logger. AFAICT, DM’s conventions don’t provide any guidance on this, and the fact that we’re discussing it on a code reviews means they probably should.

I see the arguments as:

  • warnings is the built-in module Python has for this, and as such it should probably be the default in the absence of a reason not to use it. That broad community also means it’s easier to find documentation and recipes for (such as how to test that warnings are emitted), and it’s more likely weird edge cases will have been encountered and made to work well already.

  • If we believe it’s good practice for all code (of the kind we work with) to be well-instrumented with debug logs anyway (a reasonable argument, but not one I am passionate about either way), once you’ve got that set up in a module, it’s easy to warn via logs, and no extra integration is needed to capture these warnings in our logging system.

  • Most of our code that already has logs (quite a bit; it’s all Tasks, and then some) pretty much exclusively uses logs to warn, so it’s probably the more common pattern in our codebase right now.

  • We also already use warnings pretty extensively, especially for deprecation.

  • Third party code that we call will use warnings, and we should probably make sure those appear in our logs no matter how we capture them, if they don’t already. (The same is true of built-in Python logging, but that’s beside the point, I think.)

  • It is easier to control warnings verbosity based on the kind of warning, and easier to control log-based warning verbosity based on the location on the warning (though one can abuse either “kind of warning” or “logger name” to mean the other).

What say other pundits?

Sorry to expand the scope, but it seems strange to have a hierarchy of log levels and then substitute another mechanism for one of the middle levels. Would we also replace logging at the error/fatal levels above warnings?

As I said in https://lsstc.slack.com/archives/C2JPT1KB7/p1608227287073400

The impression I have is that warnings was initially developed for use by the people developing the Python language and its libraries , not for people using Python. The usage transfer from the first group to the second has caused some issues.

In other words, warnings (like deprecations) are for developers (people writing code), logging is for users (people using code to do science).

I feel like warnings and logs are two different types of things.

I would expect warnings to be used for programming warnings: deprecated APIs, calling functions with invalid (but recoverable) arguments, using APIs in ways that may be erroneous, and that sort of thing. I would expect logs to be used for problems that are external to the code and aren’t a programming error: unwritable directories, missing configuration files, network services that are unavailable, or other problems with external resources or the execution environment.

2 Likes

This seems quite related to this earlier discussion that @kfindeisen and I caused: Warnings or other not-errors in the Stack. Although that was more about what to do with not-quite-failure conditions, it also proposed using warnings.warn.

I think my main worry now is what happens to those warnings.warn() messages in our logging and production environment. Would they just get swallowed up like print() does, or would they be captured, and how would you know which Task they came from?