1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
|
# Configuration
The focus of *structlog* has always been to be flexible to a fault.
The goal is that a user can use it with *any* logger of their own that is wrapped by *structlog*.
That's the reason why there's an overwhelming amount of knobs to tweak, but
– ideally – once you find your configuration, you don't touch it ever again and, more importantly:
don't see any of it in your application code.
---
Let's start at the end and introduce the ultimate convenience function that relies purely on configuration: {func}`structlog.get_logger`.
The goal is to reduce your per-file application logging boilerplate to:
```
import structlog
logger = structlog.get_logger()
```
To that end, you'll have to call {func}`structlog.configure` on app initialization.
You can call {func}`structlog.configure` repeatedly and only set one or more settings -- the rest will not be affected.
If necessary, you can always reset your global configuration back to default values using {func}`structlog.reset_defaults`.
That can be handy in tests.
At any time, you can check whether and how *structlog* is configured using {func}`structlog.is_configured` and {func}`structlog.get_config`}:
```pycon
>>> structlog.is_configured()
False
>>> structlog.configure(logger_factory=structlog.stdlib.LoggerFactory())
>>> structlog.is_configured()
True
>>> cfg = structlog.get_config()
>>> cfg["logger_factory"]
<class 'structlog.stdlib.LoggerFactory'>
```
:::{important}
Since you'll call {func}`structlog.get_logger` in module scope, it runs at import time *before* you had a chance to configure *structlog*.
Therefore it returns a **lazy proxy** that returns a correctly configured *bound logger* on its first call to one of the context-managing methods like `bind()`.
Thus, you must never call `new()` or `bind()` in module or class scope because , you will receive a logger configured with *structlog*'s default values.
Use {func}`~structlog.get_logger`'s `initial_values` to achieve pre-populated contexts.
To enable you to log with the module-global logger, it will create a temporary *bound logger* **on each call**.
Therefore if you have nothing to bind but intend to do lots of log calls in a function, it makes sense performance-wise to create a local logger by calling `bind()` or `new()` without any parameters.
See also {doc}`performance`.
:::
## What to configure
You can find the details in the API documentation of {func}`structlog.configure`, but let's introduce the most important ones at a high level first.
### Wrapper classes
You've met {doc}`bound-loggers` in the last chapter.
They're the objects returned by {func}`~structlog.get_logger` and allow to bind key-value pairs into their private context.
You can configure their type using the `wrapper_class` keyword.
Whenever you bind or unbind data to a *bound logger*, this class is instantiated with the new context and returned.
### Logger factories
We've already talked about wrapped loggers responsible for the output, but we haven't explained where they come from until now.
Unlike with *bound loggers*, you often need more flexibility when instantiating them.
Therefore you don't configure a class; you configure a *factory* using the `logger_factory` keyword.
It's a callable that returns the logger that gets wrapped and returned.
In the simplest case, it's a function that returns a logger -- or just a class.
But you can also pass in an instance of a class with a `__call__` method for more complicated setups.
The arguments you pass to `structlog.get_logger()` will be passed to the logger factory.
For example, if you use `structlog.get_logger("a name")` and configure *structlog* to use the standard library {class}`~structlog.stdlib.LoggerFactory`, which has support for positional parameters, the returned logger will have the name `"a name"`.
For the common cases of standard library logging and Twisted logging, *structlog* comes with two factories built right in:
- {class}`structlog.stdlib.LoggerFactory`
- {class}`structlog.twisted.LoggerFactory`
So all it takes to use standard library {mod}`logging` for output is:
```
>>> from structlog import get_logger, configure
>>> from structlog.stdlib import LoggerFactory
>>> configure(logger_factory=LoggerFactory())
>>> log = get_logger()
>>> log.critical("this is too easy!")
event='this is too easy!'
```
By using *structlog*'s {class}`structlog.stdlib.LoggerFactory`, it is also ensured that variables like function names and line numbers are expanded correctly in your log format.
See {doc}`standard-library` for more details.
Calling {func}`structlog.get_logger` without configuration gives you a perfectly useful {class}`structlog.PrintLogger`.
We don't believe silent loggers are a sensible default.
### Processors
You will meet {doc}`processors` in the next chapter.
They are configured using the `processors` keyword that takes an {class}`~collections.abc.Iterable` of callables that act as processors.
|