Alternatives to selected System functions with safer logging.
Summary
Functions
A wrapper around System.cmd/3 that additionally logs the executed command, args, and env.
Runs command as a daemon, executes operation, and then terminates the daemon.
A wrapper around System.shell/2 that additionally logs the executed command.
Functions
@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}
@spec daemon_cmd( binary(), [binary() | {:secret, binary()} | [binary() | {:secret, binary()}]], (-> result), prefix: Owl.Data.t(), device: IO.device(), ready_check: (String.t() -> 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 forstderrandstdoutmessages from the daemon. Defaults tocommandfollowed by a colon.:device- the device to which messages fromstderrandstdoutare written. Defaults to:stdio.:ready_check- a function that examines messages produced bycommandbefore writing todevice. If set, execution ofoperationis blocked untilready_checkreturnstrue. By default this check is absent, andoperationis invoked immediately without waiting for any message.:env- a list of environment key-value tuples. Behaviour is similar to the option described incmd/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
@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}