Kyle McChesney
07/16/2026, 3:53 PMpython -V or otherwise in the logs for all flow runs.Marvin
07/16/2026, 3:53 PMMarvin
07/16/2026, 3:55 PMon_running flow hook that logs runtime info right when the flow enters Running.
Example:
python
import sys
import platform
from prefect import flow
from prefect.logging.loggers import flow_run_logger
def log_startup_info(flow, flow_run, state):
logger = flow_run_logger(flow_run=flow_run, flow=flow)
<http://logger.info|logger.info>(f"Python: {sys.version}")
<http://logger.info|logger.info>(f"Platform: {platform.platform()}")
<http://logger.info|logger.info>(f"Executable: {sys.executable}")
@flow(on_running=[log_startup_info])
def my_flow():
return "ok"
A shorter version if all you want is the version:
python
import sys
from prefect import flow
from prefect.logging.loggers import flow_run_logger
def log_python_version(flow, flow_run, state):
logger = flow_run_logger(flow_run=flow_run, flow=flow)
<http://logger.info|logger.info>(f"python -V => {sys.version.split()[0]}")
@flow(on_running=[log_python_version])
def my_flow():
pass
A few useful notes:
- Use flow_run_logger(...) inside the hook, not get_run_logger()
- These logs should show up in the flow run logs in the UI
- on_running fires before your flow body executes, so it’s a good place for startup diagnostics
If you want this on all flows, common patterns are:
1. Add the same hook to each flow
2. Wrap flow creation in a shared helper
3. Use with_options(on_running=[...]) to apply a hook to an existing flow
Example with `with_options`:
python
instrumented_flow = my_flow.with_options(on_running=[log_python_version])
If you want, I can also show you a clean pattern to apply this across an entire codebase during your migration.
Relevant docs:
- Write flows
- LoggingKyle McChesney
07/16/2026, 4:03 PMpython -V && $entrypoint?Marvin
07/16/2026, 4:03 PMMarvin
07/16/2026, 4:05 PMENTRYPOINT
- let Prefect continue supplying the run command
That way every run logs Python version before the Prefect flow command starts.
Example:
dockerfile
FROM prefecthq/prefect:3-latest
COPY prefect-entrypoint.sh /opt/prefect-entrypoint.sh
RUN chmod +x /opt/prefect-entrypoint.sh
ENTRYPOINT ["/opt/prefect-entrypoint.sh"]
bash
#!/bin/sh
set -e
echo "=== runtime debug ==="
python -V
python -c "import sys; print(sys.executable)"
echo "====================="
exec "$@"
Then Prefect can still launch its normal command, and your wrapper runs first.
A few important caveats:
1. Don’t hardcode the full Prefect command unless you have to
Prefect workers inject the command they want to run. If you replace that with your own fixed command, you can make upgrades or worker-specific behavior more brittle.
So this is better:
bash
exec "$@"
than this:
bash
python -V && prefect flow-run execute
2. Will it show up in Prefect logs?
Usually yes, if the worker is streaming container/process output. That startup stdout/stderr should appear at the top of the flow run logs for common worker types like Docker / Kubernetes / process workers.
3. ENTRYPOINT vs CMD
If Prefect sets the container command, your image ENTRYPOINT still wraps it nicely. That’s why ENTRYPOINT is a good place for this.
4. Prefer a script over shell-in-YAML hacks
You can wrap commands in a work pool/job template with sh -c 'python -V && ...', but a script in the image is much easier to maintain and debug.
If you’re using Kubernetes or Docker workers, this is probably the cleanest global solution.
If you want, I can give you the exact pattern for your worker type:
- Docker worker
- Kubernetes worker
- process worker
Relevant docs:
- Workers
- Work pools
- LoggingBring your towel and join one of the fastest growing data communities. Welcome to our second-generation open source orchestration platform, a completely rethought approach to dataflow automation.
Powered by