Which file descriptor (STDOUT, STDERR, etc.) is my application writing to?


When developing ansible playbooks a common pattern is to run a command and use the output in a future task. Here is a simple example:

---
- hosts: localhost
  connection: local
  tasks:
  - name: Check if mlocate is installed
    command: dnf info mlocate
    register: mlocate_output

  - name: Update the locate database
    command: updatedb
    when: '"No matching Packages to list" in mlocate_output.stderr'

In the first task dnf will run and the output from the command will be placed in either STDOUT or STDERR. But how do you know which one? One way is to add a debug statement to your playbook:

---
- hosts: localhost
  connection: local
  tasks:
  - name: Check if mlocate is installed
    command: dnf info mlocate
    register: mlocate_output

  - name: Print the contents of mlocate_output
    debug:
      var: mlocate_output

Once the task runs you can view the stderr and stdout fields to see which of the two is populated:

TASK [Print the contents of mlocate_output]
ok: [localhost] => {
"mlocate_output": {
"changed": true,
"cmd": [
"dnf",
"info",
"mlocate"
],
"delta": "0:00:31.239145",
"end": "2017-09-27 16:39:46.919038",
"rc": 0,
"start": "2017-09-27 16:39:15.679893",
"stderr": "",
"stderr_lines": [],
"stdout": "Last metadata expiration check: 0:43:16 ago on Wed 27 Sep 2017 03:56:05 PM EDT.nInstalled PackagesnName : mlocatenVersion : 0.26nRelease : 16.fc26nArch : armv7hlnSize : 366 knSource : mlocate-0.26-16.fc26.src.rpmnRepo : @SystemnFrom repo : fedoranSummary : An utility for finding files by namenURL : https://fedorahosted.org/mlocate/nLicense : GPLv2nDescription : mlocate is a locate/updatedb implementation. It keeps a databasen : of all existing files and allows you to lookup files by name.n : n : The 'm' stands for "merging": updatedb reuses the existingn : database to avoid rereading most of the file system, which makesn : updatedb faster and does not trash the system caches as much asn : traditional locate implementations.",
.....

In the output above we can see that stderr is empty and stdout contains the output from the command. While this works fine it requires you to write a playbook and wait for it to run to get feedback. Strace can provide the same information and in most cases is a much quicker. To get the same information we can pass the command as as argument to strace and limit the output to just write(2) system calls:

$ strace -yy -s 8192 -e trace=write dnf info mlocate

.....
write(1, "Description : mlocate is a locate/updatedb implementation. It keeps a database ofn : all existing files and allows you to lookup files by name.n : n : The 'm' stands for "merging": updatedb reuses the existing database to avoidn : rereading most of the file system, which makes updatedb faster and does notn : trash the system caches as much as traditional locate implementations.", 442Description : mlocate is a locate/updatedb implementation. It keeps a database of
.....

The first argument to write(2) is the file descriptor being written to. In this case that’s STDOUT. This took less than 2 seconds to run and by observing the first argument to write you know which file descriptor the application is writing to.

This article was posted by Matty on 2017-09-29 09:07:00 -0400 EDT