Muhammad Ridwan
05/15/2026, 9:25 AMMarvin
05/15/2026, 9:25 AMMarvin
05/15/2026, 9:27 AMtqdm is designed for interactive terminals and uses carriage returns / terminal control characters. Prefect task logs are line-oriented, so the animated bar usually won’t render correctly in task logs.
What works best in Prefect 3.x
- use get_run_logger().info(...) for periodic progress updates, or
- use print(...) with log_prints=True
Example:
python
from prefect import flow, task
from prefect.logging import get_run_logger
from tqdm import tqdm
import time
@task
def work():
logger = get_run_logger()
items = range(100)
for i, item in enumerate(tqdm(items, disable=True), start=1):
time.sleep(0.05)
if i % 10 == 0:
<http://logger.info|logger.info>(f"Progress: {i}/{len(items)} complete")
@flow
def my_flow():
work()
if __name__ == "__main__":
my_flow()
If you want print() statements to appear in logs:
python
from prefect import flow, task
import time
@task(log_prints=True)
def work():
for i in range(100):
time.sleep(0.05)
if (i + 1) % 10 == 0:
print(f"Progress: {i + 1}/100 complete")
@flow
def my_flow():
work()
A practical pattern if you still want tqdm locally:
- enable tqdm only when running interactively
- disable it in Prefect runs, and log checkpoints instead
python
import sys
from tqdm import tqdm
use_tqdm = sys.stdout.isatty()
for i in tqdm(range(100), disable=not use_tqdm):
...
Why this happens
- Prefect does not turn arbitrary streaming terminal output into a proper progress bar in logs
- log_prints=True captures print() output, not the dynamic terminal behavior that tqdm relies on
Recommended approach
- for Prefect UI/task logs: use <http://logger.info|logger.info>(...)
- for local scripts: use tqdm
- if you need both: disable tqdm in Prefect and emit periodic log lines
Relevant docs:
- Prefect logging docs
- Prefect flows
- Prefect tasks
If you want, I can show you a small helper that wraps tqdm and automatically logs every N steps in a Prefect-friendly way.Muhammad Ridwan
05/15/2026, 9:49 AMMarvin
05/15/2026, 9:49 AMMarvin
05/15/2026, 9:49 AMfrom prefect.logging import get_run_logger
- Use standard logging only if you specifically configure it and understand it may not automatically attach Prefect run context the way native logs do
Example:
python
from prefect import flow, task
from prefect.logging import get_run_logger
@task
def my_task():
logger = get_run_logger()
<http://logger.info|logger.info>("This log is tied to this task run")
@flow
def my_flow():
logger = get_run_logger()
<http://logger.info|logger.info>("This log is tied to this flow run")
my_task()
With Python’s basic logging module:
python
import logging
logger = logging.getLogger(__name__)
<http://logger.info|logger.info>("hello")
that log may go to stdout or your configured handlers, but it is not the best choice if your goal is:
- seeing it in Prefect task logs
- having it clearly attributed to the correct task run
- preserving Prefect metadata like run IDs
Rule of thumb
- Need logs in Prefect UI per task? → use get_run_logger()
- Need app/library logging outside Prefect too? → you can still use logging, but often it’s best to bridge it into Prefect or log through Prefect within task/flow code
Also:
- print() can be captured with log_prints=True
- but structured task logging is better with get_run_logger()
Docs:
- Prefect logging
- Prefect loggers API
If you want, I can show:
1. how to forward standard Python logging into Prefect logs, or
2. a best-practice logging setup for tasks + libraries.Muhammad Ridwan
05/15/2026, 9:51 AMMarvin
05/15/2026, 9:51 AMMarvin
05/15/2026, 9:51 AMget_run_logger().
python
from prefect import task, flow
from prefect.logging import get_run_logger
def helper_script_logic():
logger = get_run_logger()
<http://logger.info|logger.info>("This will be attached to the current task run")
@task
def my_task():
helper_script_logic()
@flow
def my_flow():
my_task()
if __name__ == "__main__":
my_flow()
That works because get_run_logger() pulls the current run context at execution time.
Important caveat
If you instantiate a logger at import time or outside a running flow/task context, Prefect may not have task context available.
Example of what to avoid:
python
from prefect.logging import get_run_logger
logger = get_run_logger() # bad if module imported before task is running
def helper():
<http://logger.info|logger.info>("May fail or not map correctly")
Instead, do this:
python
from prefect.logging import get_run_logger
def helper():
logger = get_run_logger()
<http://logger.info|logger.info>("Mapped to the current task")
Using standard Python logging inside helper scripts
If your helper script does this:
python
import logging
logger = logging.getLogger(__name__)
then those logs are not guaranteed to be mapped to the Prefect task in the UI the same way. They may show up in worker/stdout logs depending on configuration, but not as clean task-run logs.
So the answer is:
- Yes — if the logger is created inside code called by the task, and you use get_run_logger()
- No guarantee — if you use plain logging.getLogger(...) and expect Prefect task attribution automatically
A good pattern for reusable scripts is to accept an optional logger:
python
from prefect import task, flow
from prefect.logging import get_run_logger
def do_work(logger=None):
logger = logger or get_run_logger()
<http://logger.info|logger.info>("Doing work")
@task
def my_task():
logger = get_run_logger()
do_work(logger=logger)
@flow
def my_flow():
my_task()
That makes the script reusable both:
- inside Prefect
- outside Prefect, where you can pass a normal Python logger
Docs:
- Prefect logging
- get_run_logger
If you want, I can give you a pattern for a separate module/script file that works both in Prefect tasks and as a standalone Python script.