8

I'm currently redirecting the output of a monitoring tool to a file, however what I'd like to do, is to redirect this output to a new file on my request (using a keybinding), without stopping the said tool.

Something like

monitor_program | handle_stdout

Where handle_stdout allows me to define a new file where to put the log at certain point.

I know I could easily write it, but I'm wondering if there's any tool that already allows this.

Braiam
  • 35,991
Treviño
  • 182
  • You could probably run logrotate with a custom config file manually, depending on the behaviour of your monitor_program, but that's somewhat hackish. – Ulrich Schwarz May 04 '16 at 12:31
  • It seems like your edit is actually an answer to the question. In that case, please post it as an answer (self-answers are encouraged!) and remove the answer from your question. – cat May 04 '16 at 16:07

5 Answers5

9

I'll suggest a named pipe.

  1. Create a pipe mkfifo p (call it whatever you want, if not 'p')

  2. Create a "reader" script that reads from the pipe and writes wherever you like

  3. Tell the monitoring program to write its logs to the named pipe

Here's a sample reader script that reads from a named pipe 'p' and writes the data to an indexed 'mylog' file:

#!/bin/sh

INDEX=0

switchlog() {
  read INDEX < newindex
  echo now writing to "mylog.$INDEX"
}

trap switchlog USR1

while :
do
  cat p >> mylog."$INDEX"
done
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
  • 2
    You could also trap a signal (USR1 for example) in the reader script as the signal to switch logs. – Jeff Schaller May 04 '16 at 13:09
  • This starts to be something I'd need... However the reader script should be able to change the output based on a my kebinding. And potentially ask me for a new file name to use (although this is optional, even having sequential log names would be fine) – Treviño May 04 '16 at 13:14
  • I edited the sample script to show an example of log-switching with kill -USR1. echo a new number into the 'newindex' file for it to pick up. – Jeff Schaller May 04 '16 at 13:19
  • 1
    just another idea -- if you keep a window open for the running "reader" script, you could trap 'INT' instead of USR1 and just hit Control-C in that window to notify it of a new log filename. – Jeff Schaller May 04 '16 at 13:22
  • 1
    Why read and printf instead of just cat <p >> mylog."$INDEX"? – Wildcard May 04 '16 at 16:01
  • @Wildcard only because I didn't think of it! I had to test it to convince myself that it works. That's a great idea! Can I steal it, or would you like to post it separately? – Jeff Schaller May 04 '16 at 17:15
  • Last time I looked things (before systemd), logrotate and syslogd worked together by having syslogd always write to the same file. logrotate would rename the current file that syslogd has open, then send it a signal. At this point, syslogd would close the current output fd and re-open its output by name. So all the name-choosing logic is in logrotate, not the program that receives SIGUSR1, and you can manually do whatever you want. – Peter Cordes May 04 '16 at 18:38
  • @JeffSchaller, go for it. But it is slightly better to use <p instead of p in this particular case, so you get the error from the shell instead of from cat if p is not readable. – Wildcard May 04 '16 at 22:04
7

Building up on your SIGINT idea, here using SIGQUIT (Ctrl+\) to you can still use Ctrl+C to stop the whole thing:

(trap '' QUIT; monitor_command) | (
   trap : QUIT
   ulimit -c 0 # prevent core dump so SIGQUIT behaves like SIGINT
               # for cat
   n=0; while n=$((n+1)); file=output.$n.log; do
     printf 'Outputting to "%s"\n' "$file"
     cat > "$file"
   done)

That assumes cat is not a builtin in your shell (so it does get interrupted by you press Ctrl+\).

Note that like in your approach, there's a chance that the SIGQUIT be delivered at the wrong time (in the write system call) causing some data to be lost.

1

You could probably use less and save from there by typing s then the file name you want to save to, then Enter. From How do I write all lines from less to a file?.

BenjaminH
  • 298
  • Unfortunately it's not possible to user more log files, once you've set one you can't redefine a new one. Also you can't clear the log once done. So... Good starting point (thanks), but no enough yet. – Treviño May 04 '16 at 13:24
0

Without knowing more about your "request" its not really possible to answer. If it were based on file size or interval, then rotatelogs (which should come bundled with Apache httpd) would work.

symcbean
  • 5,540
  • Sorry, I didn't say that explicitly, I'm assuming a keybinding that would allow to change the log name. – Treviño May 04 '16 at 13:11
0

Thanks to Jeff Schaller's answer, I ended up with something like this, which basically does what I need, let's call it reader.sh:

#!/bin/sh

INDEX=0
LOGNAME="$INDEX.log"

switchlog() {
  local custom_name
  read -p "Add log name: " custom_name
  INDEX=$((INDEX+1))
  LOGNAME="$(printf "%03d" $INDEX).$custom_name.log"
  echo now writing to $LOGNAME
}

trap switchlog INT
switchlog

while :
do
  read foo < p
  printf "%s\n" "$foo" >> "$LOGNAME"
done

Then it's just about creating a named pipe with mkfifo p, and using two terminals where monitor_program > p and reader.sh are running. I can then stop the reader to set a new log by using Ctrl+C, and enter a new name. Ctrl+Z, as usual then to stop and kill it.

Treviño
  • 182