Cara terbaik untuk mengikuti log dan menjalankan perintah ketika beberapa teks muncul di log

54

Saya memiliki log server yang menampilkan baris teks tertentu ke file log-nya ketika server habis. Saya ingin menjalankan perintah setelah server menyala, dan karenanya lakukan sesuatu seperti berikut:

tail -f /path/to/serverLog | grep "server is up" ...(now, e.g., wget on server)?

Apa cara terbaik untuk melakukan ini?

jonderry
sumber
3
pastikan untuk menggunakan tail -Funtuk menangani rotasi log - yaitu my.logmenjadi penuh dan bergerak ke my.log.1dan proses Anda membuat yang barumy.log
sg

Jawaban:

34

Cara sederhana akan menjadi aneh.

tail -f /path/to/serverLog | awk '
                    /Printer is on fire!/ { system("shutdown -h now") }
                    /new USB high speed/  { system("echo \"New USB\" | mail admin") }'

Dan ya, keduanya adalah pesan nyata dari log kernel. Perl mungkin sedikit lebih elegan untuk digunakan untuk ini dan juga dapat menggantikan kebutuhan akan ekor. Jika menggunakan perl, akan terlihat seperti ini:

open(my $fd, "<", "/path/to/serverLog") or die "Can't open log";
while(1) {
    if(eof $fd) {
        sleep 1;
        $fd->clearerr;
        next;
    }
    my $line = <$fd>;
    chomp($line);
    if($line =~ /Printer is on fire!/) {
        system("shutdown -h now");
    } elsif($line =~ /new USB high speed/) {
        system("echo \"New USB\" | mail admin");
    }
}
penguin359
sumber
Saya suka awksolusi karena pendek dan mudah dilakukan dengan cepat dalam satu baris. Namun, jika perintah yang ingin saya jalankan memiliki tanda kutip, ini bisa sedikit rumit, apakah ada alternatif, mungkin menggunakan pipa dan perintah majemuk yang juga memungkinkan solusi satu-liner singkat tetapi tidak memerlukan perintah yang dihasilkan untuk dilewati sebagai string?
jonderry
1
@jon Anda dapat menulis awk sebagai skrip. Gunakan "#! / Usr / bin awk -f" sebagai baris pertama skrip. Ini akan menghilangkan kebutuhan untuk kutipan luar tunggal dalam contoh saya dan membebaskan mereka untuk digunakan di dalam system()perintah.
penguin359
@ penguin359, Benar, tetapi masih menarik untuk melakukannya dari baris perintah juga. Dalam kasus saya, ada berbagai hal berbeda yang ingin saya lakukan termasuk banyak hal yang tidak dapat saya ramalkan, jadi nyaman untuk dapat memulai server dan melakukan semuanya dalam satu baris.
jonderry
Saya menemukan alternatif, meskipun saya tidak tahu seberapa solid itu:tail -f /path/to/serverLog | grep "server is up" | head -1 && do_some_command
jonderry
@jon Tampaknya agak rapuh menggunakan kepala seperti itu. Lebih penting lagi, itu tidak berulang seperti contoh saya. Jika "server habis" ada di sepuluh baris terakhir dari log, itu akan menjalankan perintah dan segera keluar. Jika Anda me-restart itu, kemungkinan besar akan menyala dan keluar lagi kecuali sepuluh baris yang tidak mengandung "server" telah ditambahkan ke log. Modifikasi yang mungkin bekerja lebih baik adalah tail -n 0 -f /path/to/serverLog Itu akan membaca 0 baris terakhir file, lalu menunggu lebih banyak baris untuk dicetak.
penguin359
16

Jika Anda hanya mencari satu kemungkinan dan ingin tetap berada di shell daripada menggunakan awkatau perl, Anda bisa melakukan sesuatu seperti:

tail -F /path/to/serverLog | 
grep --line-buffered 'server is up' | 
while read ; do my_command ; done

... yang akan berjalan my_commandsetiap kali " server habis " muncul di file log. Untuk beberapa kemungkinan, Anda mungkin dapat menjatuhkan grepdan alih-alih menggunakan casedalam while.

Modal -Fmengatakan tailuntuk menonton file log menjadi diputar; yaitu jika file saat ini diganti namanya dan file lain dengan nama yang sama menggantikannya, tailakan beralih ke file baru.

The --line-bufferedpilihan memberitahu grepuntuk flush buffer setelah setiap baris; jika tidak, my_commandmungkin tidak dapat dijangkau secara tepat waktu (dengan asumsi log memiliki garis yang cukup besar).

Jander
sumber
2
Saya sangat suka jawaban ini, tetapi pada awalnya tidak berhasil. Saya pikir Anda perlu menambahkan --line-bufferedopsi ke grep, atau memastikan itu memerah output antara baris: jika tidak, itu hanya hang, dan my_commandtidak pernah tercapai. Jika Anda suka ack, itu memiliki --flushbendera; jika Anda suka ag, coba balut dengan stdbuf. stackoverflow.com/questions/28982518/…
doctaphred
Saya mencoba ini, menggunakan do exit ;. Tampaknya berfungsi dengan baik, tetapi ekor tidak pernah selesai dan skrip kami tidak pernah pindah ke baris berikutnya. Apakah ada cara untuk menghentikan ekor di dobagian itu?
Machtyn
14

Pertanyaan ini tampaknya sudah dijawab, tetapi saya pikir ada solusi yang lebih baik.

Daripada tail | whatever, saya pikir apa yang Anda inginkan sebenarnya swatch. Swatch adalah program yang dirancang secara eksplisit untuk melakukan apa yang Anda minta, menonton file log, dan mengeksekusi tindakan berdasarkan baris log. Menggunakan tail|fooakan mengharuskan Anda memiliki terminal yang aktif berjalan untuk melakukan ini. Swatch di sisi lain berjalan sebagai daemon dan akan selalu mengawasi log Anda. Swatch tersedia di semua distro Linux,

Saya mendorong Anda untuk mencobanya. Meskipun Anda dapat menumbuk paku dengan sisi belakang obeng bukan berarti Anda harus melakukannya.

Tutorial 30 detik terbaik tentang carikan yang bisa saya temukan ada di sini: http://www.campin.net/newlogcheck.html

bahamat
sumber
1
Tutorial itu adalah 404 sekarang: /
dari sana
1
WebArchive adalah teman Anda: web.archive.org/web/20140922041805/http://campin.net/…
Sun
10

Sungguh aneh bahwa tidak ada yang menyebutkan tentang multitailutilitas yang memiliki fungsi ini out-of-box. Salah satu contoh penggunaan:

Tampilkan output dari perintah ping dan jika itu menampilkan batas waktu, kirim pesan ke semua pengguna yang saat ini masuk

multitail -ex timeout "echo timeout | wall" -l "ping 192.168.0.1"

Lihat juga contoh lain dari multitailpenggunaan.

php-coder
sumber
+1, saya tidak tahu `multitail memiliki keterampilan ninja semacam itu tersimpan. Terima kasih telah menunjukkannya.
Caleb
8

bisa melakukan pekerjaan sendiri

Mari kita lihat betapa sederhananya dan mudah dibaca:

mylog() {
    echo >>/path/to/myscriptLog "$@"
}

while read line;do
    case "$line" in
        *"Printer on fire"* )
            mylog Halting immediately
            shutdown -h now
            ;;
        *DHCPREQUEST* )
            [[ "$line" =~ DHCPREQUEST\ for\ ([^\ ]*)\  ]]
            mylog Incomming or refresh for ${BASH_REMATCH[1]}
            $HOME/SomethingWithNewClient ${BASH_REMATCH[1]}
            ;;
        * )
            mylog "untrapped entry: $line"
            ;;
    esac
  done < <(tail -f /path/to/logfile)

Meskipun Anda tidak menggunakan bash regex, ini bisa tetap sangat cepat!

Tapi + adalah tandem yang sangat efisien dan menarik

Tetapi untuk server dengan beban tinggi, dan seperti yang saya suka sedkarena sangat cepat dan sangat terukur, saya sering menggunakan ini:

while read event target lost ; do
    case $event in
        NEW )
            ip2int $target intTarget
            ((count[intTarget]++))
        ...

    esac
done < <(tail -f /path/logfile | sed -une '
  s/^.*New incom.*from ip \([0-9.]\+\) .*$/NEW \1/p;
  s/^.*Auth.*ip \([0-9.]\+\) failed./FAIL \1/p;
  ...
')
F. Hauri
sumber
6

Begitulah cara saya mulai melakukan ini, tetapi menjadi jauh lebih canggih dengannya. Beberapa hal yang perlu diperhatikan:

  1. Jika ekor log sudah berisi "server sudah habis".
  2. Secara otomatis mengakhiri proses ekor setelah ditemukan.

Saya menggunakan sesuatu seperti ini:

RELEASE=/tmp/${RANDOM}$$
(
  trap 'false' 1
  trap "rm -f ${RELEASE}" 0
  while ! [ -s ${RELEASE} ]; do sleep 3; done
  # You can put code here if you want to do something
  # once the grep succeeds.
) & wait_pid=$!
tail --pid=${wait_pid} -F /path/to/serverLog \
| sed "1,10d" \
| grep "server is up" > ${RELEASE}

Ia bekerja dengan menahan tailterbuka sampai ${RELEASE}file berisi data.

Setelah grepberhasil:

  1. menulis output ${RELEASE}yang akan
  2. menghentikan ${wait_pid}proses untuk
  3. keluar dari tail

Catatan: Ini sedbisa lebih canggih untuk benar-benar menentukan jumlah baris yang tailakan diproduksi saat startup dan menghapus nomor itu. Tetapi secara umum, ini 10.

nix
sumber