# `Owl.System`
[🔗](https://github.com/fuelen/owl/blob/v0.13.1/lib/owl/system.ex#L1)

Alternatives to selected `System` functions with safer logging.

# `cmd`

```elixir
@spec cmd(
  binary(),
  [binary() | {:secret, binary()} | [binary() | {:secret, binary()}]],
  keyword()
) :: {Collectable.t(), exit_status :: non_neg_integer()}
```

A wrapper around `System.cmd/3` that additionally logs the executed `command`, `args`, and `env`.

If a URL is found in the logged message, the password in it is masked with asterisks.
You can explicitly mark environment values and arguments as secret for safe logging.
See the examples for details.

## Examples

    > Owl.System.cmd("echo", ["test"])
    # 10:25:34.252 [debug] $ echo test
    {"test\n", 0}

    # marking an argument as secret
    > Owl.System.cmd("echo", ["hello", secret: "world"])
    # 10:25:40.516 [debug] $ echo hello ********
    {"hello world\n", 0}

    # marking a part of an argument as secret
    > Owl.System.cmd("echo", ["hello", ["--password=", {:secret, "world"}]])
    # 10:25:40.516 [debug] $ echo hello --password=********
    {"hello --password=world\n", 0}

    # marking an env as secret
    > Owl.System.cmd("echo", ["hello", "world"], env: [{"PASSWORD", {:secret, "mypassword"}}])
    # 10:25:40.516 [debug] $ PASSWORD=******** sh -c "echo hello world"
    {"hello world\n", 0}

    > Owl.System.cmd("psql", ["postgresql://postgres:postgres@127.0.0.1:5432", "-tAc", "SELECT 1;"])
    # 10:25:50.947 [debug] $ psql postgresql://postgres:********@127.0.0.1:5432 -tAc 'SELECT 1;'
    {"1\n", 0}

# `daemon_cmd`

```elixir
@spec daemon_cmd(
  binary(),
  [binary() | {:secret, binary()} | [binary() | {:secret, binary()}]],
  (-&gt; result),
  prefix: Owl.Data.t(),
  device: IO.device(),
  ready_check: (String.t() -&gt; boolean()),
  env: [{binary(), binary() | {:secret, binary()} | nil}]
) :: result
when result: any()
```

Runs `command` as a daemon, executes `operation`, and then terminates the daemon.

Automatically writes messages from `stderr` and `stdout` to `device`, prefixing them with `prefix`.
Returns the result of invoking `operation`.

## Options

* `:prefix` - a prefix for `stderr` and `stdout` messages from the daemon. Defaults to `command` followed by a colon.
* `:device` - the device to which messages from `stderr` and `stdout` are written. Defaults to `:stdio`.
* `:ready_check` - a function that examines messages produced by `command` before writing to `device`.
  If set, execution of `operation` is blocked until `ready_check` returns `true`.
  By default this check is absent, and `operation` is invoked immediately without waiting for any message.
* `:env` - a list of environment key-value tuples. Behaviour is similar to the option described in `cmd/3`.

## Example

    ex> Owl.System.daemon_cmd("ping", ["8.8.8.8"], fn ->
    ..>   Process.sleep(3_000)
    ..>   2 + 2
    ..> end)
    # 00:36:33.963 [debug] $ ping 8.8.8.8
    #
    # 00:36:33.964 [debug] Started daemon ping with OS pid 576077
    # ping:  PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
    # ping:  64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=28.3 ms
    # ping:  64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=26.9 ms
    # ping:  64 bytes from 8.8.8.8: icmp_seq=3 ttl=118 time=28.6 ms

    # 00:36:36.965 [debug] $ kill 576077
    4

    ex> Owl.System.daemon_cmd(
    ..>   "kubectl",
    ..>   [
    ..>     "port-forward",
    ..>     "--namespace=my-app",
    ..>     "--kubeconfig",
    ..>     "~/.kube/myapp",
    ..>     "my-pod",
    ..>     "5432:5432"
    ..>   ],
    ..>   fn ->
    ..>     Logger.debug("Dump DB")
    ..>   end,
    ..>   prefix: "kubectl(my-pod): ",
    ..>   ready_check: &String.contains?(&1, "Forwarding from")
    ..> )
    # 12:08:01.382 [debug] $ kubectl port-forward --namespace=my-app --kubeconfig ~/.kube/myapp my-pod 5432:5432
    # 12:08:01.384 [debug] Started daemon kubectl with OS pid 166378
    # kubectl(my-pod): Forwarding from 127.0.0.1:5432 -> 5432
    # kubectl(my-pod): Forwarding from [::1]:5432 -> 5432
    # 12:08:02.484 [debug] Dump DB
    # 12:08:02.584 [debug] $ kill 166378
    :ok

# `shell`

```elixir
@spec shell(
  binary(),
  keyword()
) :: {Collectable.t(), exit_status :: non_neg_integer()}
```

A wrapper around `System.shell/2` that additionally logs the executed `command`.

Similar to `cmd/3`, it automatically hides passwords in URLs and allows manually hiding environment values.

## Examples

    > Owl.System.shell("echo hello world")
    # 22:36:01.440 [debug] $ sh -c "echo hello world"
    {"hello world\n", 0}

    > Owl.System.shell("echo postgresql://postgres:postgres@127.0.0.1:5432")
    # 22:36:51.797 [debug] $ sh -c "echo postgresql://postgres:********@127.0.0.1:5432"
    {"postgresql://postgres:postgres@127.0.0.1:5432\n", 0}

    > Owl.System.shell("echo $PASSWORD $USERNAME", env: [{"USERNAME", "john"}, {"PASSWORD", {:secret, "qwerty"}}])
    # 12:38:27.704 [debug] $ USERNAME=john PASSWORD=******** sh -c "echo \$PASSWORD \$USERNAME"
    {"qwerty john\n", 0}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
