When executing long-running commands through Ansible, we often need to capture the output in real-time rather than waiting for the entire execution to complete. The common workaround using tee
works but isn't the most elegant Ansible-native solution.
Ansible provides several native ways to handle command output logging:
- name: Run command with output logging
command: /path/to/long_running_script.sh
register: command_output
async: 3600 # run async for long tasks
poll: 0 # don't wait for completion
changed_when: false
notify: log_output
For real-time logging, we can combine async tasks with the lineinfile module:
- name: Execute and log in real-time
shell: |
/path/to/command.sh 2>&1 | while read -r line; do
echo "$(date '+%Y-%m-%d %H:%M:%S') - $line" >> /var/log/ansible_command.log
done
args:
executable: /bin/bash
async: 3600
poll: 0
For enterprise environments, creating a custom callback plugin provides the most flexible solution:
from ansible.plugins.callback import CallbackBase
class RealTimeLogging(CallbackBase):
def v2_runner_on_ok(self, result):
if 'cmd' in result._result:
with open('/var/log/ansible_tasks.log', 'a') as f:
f.write(result._result.get('stdout', '') + '\n')
def v2_runner_on_failed(self, result, ignore_errors=False):
with open('/var/log/ansible_errors.log', 'a') as f:
f.write(result._result.get('stderr', '') + '\n')
Each method has its advantages:
- tee method: Simple but requires shell piping
- lineinfile: More Ansible-native but complex syntax
- Callback plugin: Most powerful but requires Python knowledge
For production systems, consider these recommendations:
- Use log rotation to prevent disk space issues
- Include timestamps in log entries
- Implement proper error handling
- Consider log aggregation for distributed systems
When executing time-consuming commands through Ansible playbooks, administrators often need to capture real-time output for monitoring and debugging purposes. The common workaround using tee
works but feels like a workaround rather than a native Ansible solution.
Ansible provides several built-in mechanisms to handle command output redirection:
- name: Run command with direct output redirection
shell: "long_running_command > /var/log/output.log 2>&1"
args:
executable: /bin/bash
For more Ansible-native logging, consider these approaches:
- name: Execute command with registered output
command: long_running_command
register: cmd_output
async: 3600
poll: 0
- name: Write output incrementally
lineinfile:
path: /var/log/ansible_output.log
line: "{{ item }}"
create: yes
with_items: "{{ cmd_output.stdout_lines }}"
when: cmd_output.stdout_lines is defined
For enterprise environments, creating a custom callback plugin provides the most robust solution:
# In callback_plugins/real_time_log.py
from ansible.plugins.callback import CallbackBase
class CallbackModule(CallbackBase):
def runner_on_ok(self, result):
with open('/var/log/ansible_real_time.log', 'a') as f:
if 'stdout' in result._result:
f.write(result._result['stdout'] + '\n')
When dealing with high-frequency output:
- Use buffered writes for better I/O performance
- Consider log rotation for long-running tasks
- Evaluate filesystem performance on the target host
Remember to:
- Set appropriate file permissions (mode: 0640)
- Consider SELinux contexts if enabled
- Validate log file paths to prevent directory traversal