# Deployment: Logging
How can you customize your Prefect logs?
# Logging
Logging is a crucial aspect of any production environment. Prefect exposes a series of configuration settings and utility functions for customizing the behavior of your logs.
# Adding your own logs
The most common place you might want to add additional logs is within a custom Task. There are two places you can access your Task's logger, depending on how you created the Task:
- if your Task is implemented as a subclass of the
Task
class, theself.logger
attribute contains your Task's logger - if your Task is implemented via the
task
decorator, you can access your logger from context:logger = prefect.context.get("logger")
# Configuration
Your Prefect user configuration file provides one way to easily change how logs are presented to you when running locally. In your user config file, add a section for logging with the following structure:
[logging]
# The logging level: NOTSET, DEBUG, INFO, WARNING, ERROR, or CRITICAL
level = "INFO"
# The log format
format = "[%(asctime)s] %(levelname)s - %(name)s | %(message)s"
Alternatively, you can set the following environment variables:
export PREFECT__LOGGING__LEVEL="INFO"
export PREFECT__LOGGING__FORMAT="[%(asctime)s] %(levelname)s - %(name)s | %(message)s"
# Adding Handlers
In addition to changing how your logs are formatted, you can take it one step further by interacting with the logger objects directly prior to execution. For example, you can add new handlers to your logger (recall that logging handlers allow you to ship your logs to multiple configurable destinations).
This is easily accomplished via the get_logger
utility located in prefect.utilities.logging
. The root logger can be accessed by calling get_logger()
with no arguments. Note that the Task
and Flow
loggers are associated with loggers of the same names as the Task or Flow.
# An Example
Let's walk through a basic example. To begin, we will create a dummy webserver on our local machine that we will POST logs to:
# spins up a local webserver running at http://0.0.0.0:8000/
python3 -m http.server
Next, let's create a logger handler and add this handler to our Task's loggers.
import logging
import requests
import prefect
from prefect import task, Flow
from prefect.utilities.logging import get_logger
class MyHandler(logging.StreamHandler):
def emit(self, record):
requests.post("http://0.0.0.0:8000/", params=dict(msg=record.msg))
@task(name="Task A")
def task_a():
return 3
@task(name="Task B")
def task_b(x):
logger = prefect.context.get("logger")
logger.debug("Beginning to run Task B with input {}".format(x))
y = 3 * x + 1
logger.debug("Returning the value {}".format(y))
return y
with Flow("logging-example") as flow:
result = task_b(task_a)
# now attach our custom handler to Task B's logger
task_logger = get_logger("Task B")
task_logger.addHandler(MyHandler())
if __name__ == "__main__":
flow.run()
If we store this code in a file called logging_example.py
, we can update our log formats and levels using environment variables and kick off this flow run:
export PREFECT__LOGGING__LEVEL="DEBUG"
export PREFECT__LOGGING__FORMAT="%(levelname)s - %(name)s | %(message)s"
python logging_example.py
We should see our logs posted to stdout, as usual. But if we navigate to the window where our webserver is running, we will also see something like:
127.0.0.1 - "POST /?msg=Beginning+to+run+Task+B+with+input+3 HTTP/1.1" 501
127.0.0.1 - "POST /?msg=Returning+the+value+10 HTTP/1.1" 501
We are seeing 501 status codes because our webserver doesn't implement any routes.
# Further steps
You can take this example further by spinning up a real storage solution for your logs. Additionally, if running on Prefect Cloud, you can opt-in to having Prefect store your logs for you and expose them via a convenient GraphQL interface!