How to Log Ansible Task Output to Local File in Real-Time Without Using tee


5 views

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:

  1. Use log rotation to prevent disk space issues
  2. Include timestamps in log entries
  3. Implement proper error handling
  4. 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