ansible/ansible
Callback plugins
Callback plugins are event consumers. The TaskQueueManager emits events as the run progresses (play start, task start, task ok, task failed, host unreachable, etc.); each loaded callback plugin can implement zero or more event handlers. Callbacks drive on-screen output, JUnit XML, log files, custom integrations.
Files
ansible-core ships five callbacks:
lib/ansible/plugins/callback/
├── __init__.py # CallbackBase
├── default.py # The colored, human-readable output you see when running ansible-playbook
├── minimal.py # Spartan one-line-per-result
├── oneline.py # Variant of minimal — used by ansible (ad-hoc)
├── junit.py # Emit JUnit XML at end of run for CI consumption
└── tree.py # Write per-host JSON files into a directory treeMany more live in collections (community.general has slack, mail, syslog_json, profile_tasks, timer, etc.).
CallbackBase
Every callback inherits from lib/ansible/plugins/callback/__init__.py:CallbackBase. Implement only the events you care about:
class CallbackModule(CallbackBase):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'mycorp.simple'
def v2_runner_on_ok(self, result):
host = result._host.get_name()
task = result._task.get_name()
self._display.display(f"OK: {host} {task}")
def v2_runner_on_failed(self, result, ignore_errors=False):
...Key class attributes:
CALLBACK_VERSION = 2.0— locks in the v2 event API. Older 1.x callbacks are not loaded.CALLBACK_TYPE—'stdout'(only one such callback is loaded; controls main user output),'aggregate'(multiple allowed; secondary output),'notification'(e.g., chat integrations).CALLBACK_NAME— the FQCN-style name used inANSIBLE_STDOUT_CALLBACKconfig orcallbacks_enabled.
Event taxonomy
The full v2 event list (from CallbackBase) covers play, task, runner, and meta events:
| Group | Events |
|---|---|
| Play | v2_playbook_on_play_start, v2_playbook_on_no_hosts_matched |
| Task | v2_playbook_on_task_start, v2_playbook_on_handler_task_start, v2_playbook_on_include |
| Runner | v2_runner_on_start, v2_runner_on_ok, v2_runner_on_failed, v2_runner_on_unreachable, v2_runner_on_skipped, v2_runner_on_async_poll, v2_runner_on_async_ok, v2_runner_on_async_failed, v2_runner_retry, v2_runner_item_on_ok, v2_runner_item_on_failed, v2_runner_item_on_skipped |
| Stats | v2_playbook_on_stats |
| File | v2_playbook_on_notify, v2_playbook_on_no_hosts_remaining |
| Setup / Vars | v2_playbook_on_vars_prompt |
| Misc | v2_playbook_on_start, v2_playbook_on_setup |
Each handler receives a structured event object. The most common is the TaskResult (from lib/ansible/executor/task_result.py), with attributes:
_host— theHostobject._task— theTaskobject._result— the dict the module returned (or that the action plugin synthesized).is_changed(),is_failed(),is_unreachable(),is_skipped()— predicates.
Multiple stdout callbacks aren't a thing
Only one CALLBACK_TYPE = 'stdout' callback is loaded — the one named in ANSIBLE_STDOUT_CALLBACK (default default). All other loaded callbacks are 'aggregate' or 'notification' and run in parallel.
The callbacks_enabled config option (or CALLBACKS_ENABLED env) controls which non-stdout callbacks are loaded. Many collection callbacks ship disabled by default and require explicit enabling.
How events get to callbacks
graph LR
Worker[Worker process<br/>TaskExecutor] -->|FinalQueue| TQM[TQM main loop]
TQM --> ROUTE[Route by event type]
ROUTE --> ALLCB[For each loaded callback]
ALLCB -->|hasattr v2_runner_on_ok| CB1[default.py: format and display]
ALLCB -->|hasattr v2_runner_on_ok| CB2[junit.py: append to XML buffer]
ALLCB -->|no handler defined| SKIP[skip]Workers send CallbackSend messages over the result queue rather than calling callbacks directly — callbacks run in the controller process so they share state across hosts/workers.
The default callback
lib/ansible/plugins/callback/default.py is the one you see by default. It does:
- Colored output: green for ok, red for failed, yellow for changed, magenta for unreachable, cyan for skipped.
- Per-host suppression of
verbosetask results when verbosity is 0. - The end-of-run "PLAY RECAP" with stats per host.
--diffoutput rendering (when the task result includes adifffield).
Customize via env vars (ANSIBLE_DISPLAY_OK_HOSTS, ANSIBLE_DISPLAY_FAILED_STDERR, ANSIBLE_DISPLAY_SKIPPED_HOSTS, ANSIBLE_NOCOLOR, ANSIBLE_FORCE_COLOR).
minimal vs. oneline
minimal.py is "default minus a lot": no diffing, no banner. Used for cleaner CI output.
oneline.py is one-line-per-result, intended for tools that parse output. The ansible ad-hoc CLI defaults to it.
junit
lib/ansible/plugins/callback/junit.py builds an in-memory XML structure as tasks complete and writes it to disk in v2_playbook_on_stats. Each play becomes a JUnit <testsuite> and each task a <testcase>. Used by CI to surface test results in dashboards.
Configurable via:
ANSIBLE_JUNIT_OUTPUT_DIRANSIBLE_JUNIT_TASK_CLASS(whether to make each task its own class)ANSIBLE_JUNIT_FAIL_ON_CHANGEANSIBLE_JUNIT_FAIL_ON_IGNORE
tree
lib/ansible/plugins/callback/tree.py writes one JSON file per host into a configured directory. Used for diagnostic and post-run analysis when you want structured per-host output.
Integration points
- Loaded by:
lib/ansible/executor/task_queue_manager.py:TaskQueueManager.load_callbacks(). - Configured by:
ANSIBLE_STDOUT_CALLBACK(single, type=stdout),CALLBACKS_ENABLED/ANSIBLE_CALLBACKS_ENABLED(list, types aggregate+notification). - Imported by: anything that wants to write event consumers — but normally the user just installs a collection that ships them.
Entry points for modification
- A new logging integration — write a callback in a collection. Set
CALLBACK_TYPE = 'aggregate'or'notification'. Implementv2_runner_on_*handlers. - Replacing default output — write a
'stdout'callback. SetANSIBLE_STDOUT_CALLBACK=mycorp.cool_stdout. - Adding a new event to the framework requires changes in
CallbackBase(default no-op handlers),task_queue_manager.py(dispatch), and probably the strategy plugins (where the event would fire). Don't do this lightly.
Cross-links
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.