Like tee
, but with dynamically-attached socket clients
./teesocket.pl -s /tmp/my.sock
./teesocket.pl -c /tmp/my.sock
$ (while true; do sleep 1; echo $((n++)); done) | ./teesocket.pl -s /tmp/my.log.sock >>/tmp/my.log &
[1] 18012
$ tail -5 /tmp/my.log
54
55
56
57
58
$ ./teesocket.pl -c /tmp/my.log.sock
61
62
63
64
65
^C
$
This also works when output is piped to some other process such as rotatelogs
:
$ (while true; do sleep 1; echo $((n++)); done) | ./teesocket.pl -s /tmp/my.log.sock | rotatelogs /tmp/my.log 60 &
[1] 16405
$ # ...wait a few minutes here so we can see that rotatelogs is working...
$ ls -l /tmp/my.log.*
-rw-r--r-- 1 root root 38 Jan 25 02:33 /tmp/my.log.1611538380
-rw-r--r-- 1 root root 180 Jan 25 02:34 /tmp/my.log.1611538440
-rw-r--r-- 1 root root 82 Jan 25 02:35 /tmp/my.log.1611538500
srwxr-xr-x 1 root root 0 Jan 25 02:33 /tmp/my.log.sock
$ ./teesocket.pl -c /tmp/my.log.sock
./teesocket.pl -c /tmp/my.log.sock
157
158
159
160
161
^C
$
Create a "popcorn" service
by writing the output of the date
command to teesocket
once a second. The primary output
of teesocket.pl is directed to /dev/null
, and client connections can be made to the unix
socket to begin observing the stream at any time.
$ setsid sh -c '(while true; do sleep 1; date; done) | ./teesocket.pl -s /tmp/popcorn.sock >/dev/null'
$ ./teesocket.pl -c /tmp/popcorn.sock
Mon Jan 25 02:15:35 CET 2021
Mon Jan 25 02:15:36 CET 2021
Mon Jan 25 02:15:37 CET 2021
Mon Jan 25 02:15:38 CET 2021
Mon Jan 25 02:15:39 CET 2021
^C
$
Sometimes you may need to attach this utility to the output of a process which normally writes to a file. By creating a FIFO at the location where the process will write, you can capture the stream and handle it.
In this example, we create a FIFO, attach teesocket
to read from it and write to /dev/null
, then we start
a process which logs to the filename of the FIFO. The output is discarded, until we use teesocket
to
begin observing. (note: the tail -f
layer is used in order to insulate teesocket
from the EOF event
which would otherwise occur each time the FIFO gets closed by one of the wget
processes)
$ mkfifo /tmp/my.fifo
$ tail -f /tmp/my.fifo | ./teesocket.pl -s /tmp/my.sock >/dev/null &
$ while true; do sleep 1; wget -a /tmp/my.fifo http://example.com; done &
[1] 4640
$ tail -f /tmp/my.fifo | ./teesocket.pl -s /tmp/my.sock
Resolving example.com (example.com)... 2606:2800:220:1:248:1893:25c8:1946, 93.184.216.34
Connecting to example.com (example.com)|2606:2800:220:1:248:1893:25c8:1946|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1256 (1.2K) [text/html]
Saving to: ‘index.html.30’
0K . 100% 105M=0s
2021-01-25 03:46:02 (105 MB/s) - ‘index.html.30’ saved [1256/1256]
--2021-01-25 03:46:03-- http://example.com/
Resolving example.com (example.com)... 2606:2800:220:1:248:1893:25c8:1946, 93.184.216.34
Connecting to example.com (example.com)|2606:2800:220:1:248:1893:25c8:1946|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1256 (1.2K) [text/html]
Saving to: ‘index.html.31’
0K . 100% 101M=0s
2021-01-25 03:46:03 (101 MB/s) - ‘index.html.31’ saved [1256/1256]
^C
$
When using STDOUT as the log output of a Docker container, such that the Docker Engine is responsible for storing or forwarding the log stream, it is sometimes necessary for processes within the container to sample this stream. Since the container itself may not know where the stream is stored or sent by the Engine, in order to maintain a separation of concerns, it is useful to sample this stream before it leaves the container.
Within a Docker container, the STDOUT which is passed to the Docker Engine logdriver is the
STDOUT of the primary process, i.e. /proc/1/fd/1
Knowing this, we can set up teesocket
with a FIFO for input as in the above example, and with this specific file descriptor as
output (or just the default output if it is started at a point in the container when its
output is still connected), and use the FIFO as the log target of all other processes within
the contianer.
$ docker build -t teesocket - <<'EOF'
FROM debian:10-slim
ADD https://gist.githubusercontent.com/p120ph37/9c4f566869f0ced3cca3c7b54a435e6c/raw/teesocket.pl /teesocket.pl
RUN chmod 755 /teesocket.pl
RUN echo "#!/bin/sh\n\
mkfifo /tmp/container-output.fifo\n\
setsid sh -c 'tail -f /tmp/container-output.fifo | ./teesocket.pl -s /tmp/container-output.sock' &\n\
while true; do sleep 1; date >/tmp/container-output.fifo; done" >/startup.sh \
&& chmod 755 /startup.sh
CMD ["/startup.sh"]
EOF
...
Successfully built b52ecdcbd1c7
Successfully tagged teesocket:latest
$ docker run --name teesocket -d teesocket
1b39a4639ae2d74d04d3bdcdcf5ad999fc5fbcd19748051be8041705139aba43
$ docker logs teesocket
Mon Jan 25 18:05:11 UTC 2021
Mon Jan 25 18:05:12 UTC 2021
$ docker exec -ti teesocket /bin/bash
root@1b39a4639ae2:/# ./teesocket.pl -c /tmp/container-output.sock
Mon Jan 25 18:05:35 UTC 2021
Mon Jan 25 18:05:36 UTC 2021
Mon Jan 25 18:05:37 UTC 2021
^C
root@1b39a4639ae2:/# exit
$
socat
is a general-purpose socket connection tool. Because the socket produced by teesocket
is a generic UNIX socket, it can be observed not only by teesocket
itself, but you can
also use socat
to observe it:
$ (while true; do sleep 1; echo $((n++)); done) | ./teesocket.pl -s /tmp/my.log.sock >>/tmp/my.log &
[1] 8709
$ tail -5 /tmp/my.log
4
5
6
7
8
$ socat -u UNIX-CONNECT:/tmp/my.log.sock STDOUT
32
33
34
35
^C
$