multilog(8)

πŸ“¦
version 0.76 Β· 2 versions
  • 0.76 Β· debian bookworm, debian trixie, nixpkgs unstable, ubuntu jammy, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking (latest)
  • 1.11 Β· alpine 3.24
, in debian bookworm, debian trixie, nixpkgs unstable, ubuntu jammy, ubuntu noble, ubuntu questing, ubuntu resolute, ubuntu stonking

πŸ’– multilog(8) β€” the manual but make it a group chat πŸ’–

hiii bestie 🫢 welcome to multilog, section 8 of the System Manager's Manual. pull up a chair, grab a strawberry πŸ“, we're gonna get you logging like a main character.


✨ NAME ✨

multilog β€” reads a sequence of lines from stdin and appends selected lines to any number of logs. that's it, that's the whole job, and she does it flawlessly πŸ’…

✨ SYNOPSIS ✨

multilog script

✨ DESCRIPTION ✨

okay so script is literally just any number of arguments. every argument = one action ✨ and the actions are carried out in order, once per line of input. no skipping, no reordering, just vibes in sequence.

not me forgetting this: actions may contain shell metacharacters, so you have to quote them when you run multilog from a shell. quote your patterns bestie, protect your peace πŸ™ˆ

the whole lifecycle, laid out:

  • multilog exits 0 when it sees the end of stdin. if stdin has a partial final line, multilog inserts a final newline. tidy. iconic.
  • multilog writes a message to stderr and exits 111, without reading any input, if it runs out of memory, or if another multilog process is writing to one of the same automatically rotated logs. πŸ’€ so don't do that.
  • if multilog has trouble writing to disk after it starts reading input, it writes a message to stderr, pauses, and tries again, without losing any data. but lowkey be aware: this may block any program feeding input to multilog.
  • if multilog receives a TERM signal, it will read and process data until the next newline, and then exit, leaving stdin at the first byte of data it has not processed. graceful exit queen πŸ‘‘

🌸 SELECTING LINES 🌸

every line is initially selected. from there, your actions decide who stays and who goes.

  • -pattern β€” deselects the line if pattern matches the line.
  • +pattern β€” selects the line if pattern matches the line.

pattern is a string of stars and non-stars. it matches any concatenation of strings matched by all the stars and non-stars in the same order. a non-star matches itself. a star before the end of pattern matches any string that does not include the next character in pattern. a star at the end of pattern matches any string. yes bestie, it's giving wildcard energy πŸ¦‹

for example, the action

+hello

selects hello. it does not select hello world. picky? no, correct.

the action

-named[*]: Cleaned cache *

deselects named[135]: Cleaned cache of 3121 RRs. the first star matches any string that does not include a right bracket. see how that works? chef's kiss 😭

the action

-*

deselects every line. bye to everyone, no survivors, periodt.

to save memory, multilog actually checks pattern against only the first 1000 characters of each line. yep, only the first 1000. the rest is just there for the aesthetic.

🌈 ALERTS 🌈

the action

  • e β€” prints (the first 200 bytes of) each selected line to stderr.

that's it. that's the whole action. she's short, she's loud, she ate πŸ’…


πŸŽ€ STATUS FILES πŸŽ€

the action

  • =file β€” replaces the contents of file with (the first 1000 bytes of) each selected line, padded with newlines to 1001 bytes. there is no protection of file against power outages. ⚠️ like, none. so keep that in mind.

for example, the sequence of actions

-*
+STAT*
=log/status

maintains log/status as a copy of the most recent line starting with STAT. one file, always fresh, always the latest STAT. obsessed ✨


⭐ TIMESTAMPING ⭐

the action

  • t β€” inserts an @, a precise timestamp, and a space in front of each line, using the same format as tai64n(8). this is required to be the first action. like, required required. don't fight it πŸ‘‘

patterns apply to the line after the timestamp is inserted. so for example, if

multilog t '-*' '+* fatal: *' ./main

reads the line

fatal: out of memory

then it will log a line such as

@400000003b4a39c23294b13c fatal: out of memory

with the first * matching the timestamp. iykyk 😌

you can use tai64nlocal(8) to convert these timestamps to human-readable form. your future self will thank you.


πŸ’œ AUTOMATICALLY ROTATED LOGS πŸ’œ

if dir starts with a dot or slash then the action

  • dir β€” appends each selected line to a log named dir. if dir does not exist, multilog creates it. making directories out of thin air, main character energy.
🚨 do not attempt to write to one log from two simultaneous multilog processes, or two actions in one process. just don't. this is the one rule fr fr.

the log format is as follows: dir is a directory containing some number of old log files, a log file named current, and other files for multilog to keep track of its actions. each old log file has a name beginning with @, continuing with a precise timestamp showing when the file was finished, and ending with one of the following codes:

  • .s β€” this file is completely processed and safely written to disk. slay, done, finished πŸ’…
  • .u β€” this file was being created at the moment of an outage. it may have been truncated and has not been processed. 😭
beware that NFS, async filesystems, and softupdates filesystems may discard files that were not safely written to disk before an outage. so like... know your filesystem bestie.

while multilog is running, current has mode 644. if multilog sees the end of stdin, it writes current safely to disk, and sets the mode of current to 744. when it restarts, it sets the mode of current back to 644 and continues writing new lines. the cycle continues, iconic behavior.

when multilog decides that current is big enough, it writes current safely to disk, sets the mode of current to 744, and renames current as an old log file. the action

  • ssize β€” sets the maximum file size for subsequent dir actions. multilog will decide that current is big enough if current has size bytes. (multilog will also decide that current is big enough if it sees a newline within 2000 bytes of the maximum file size; it tries to finish log files at line boundaries. thoughtful πŸ₯Ή) size must be between 4096 and 16777215. the default maximum file size is 99999.
in versions 0.75 and above: if multilog receives an ALRM signal, it immediately decides that current is big enough, if current is nonempty. πŸ‘‘ noted.

the action

  • nnum β€” sets the number of log files for subsequent dir actions. after renaming current, if multilog sees num or more old log files, it removes the old log file with the smallest timestamp. num must be at least 2. the default number of log files is 10.

the action

  • !processor β€” sets a processor for subsequent dir actions. multilog will feed current through processor and save the output as an old log file instead of current. multilog will also save any output that processor writes to descriptor 5, and make that output readable on descriptor 4 when it runs processor on the next log file. for reliability, processor must exit nonzero if it has any trouble creating its output; multilog will then run it again. note that running processor may block any program feeding input to multilog. 😬 so, you know, keep an eye on it.

πŸ¦‹ SEE ALSO πŸ¦‹

supervise(8), svc(8), svok(8), svstat(8), svscanboot(8), svscan(8), readproctitle(8), fghack(8), pgrphack(8), tai64n(8), tai64nlocal(8), setuidgid(8), envuidgid(8), envdir(8), softlimit(8), setlock(8)

http://cr.yp.to/daemontools.html


okay that's the whole page bestie 🫢 the manual header says multilog(8) with System Manager's Manual up top, and the footer is just multilog(8) again β€” she's from the daemontools crew over at http://cr.yp.to/daemontools.html ✨ go read the friends, they're all linked above. ily, happy logging, don't run two writers on one log πŸ’–πŸŽ€πŸŒΈ

πŸ“¦ where this page comes from