Cara mengakhiri perintah tee Linux tanpa mematikan aplikasi yang diterimanya

19

Saya memiliki skrip bash yang berjalan selama mesin Linux dihidupkan. Saya memulainya seperti yang ditunjukkan di bawah ini:

( /mnt/apps/start.sh 2>&1 | tee /tmp/nginx/debug_log.log ) &

Setelah lauches, saya dapat melihat perintah tee di output ps saya seperti yang ditunjukkan di bawah ini:

$ ps | grep tee
  418 root       0:02 tee /tmp/nginx/debug_log.log
3557 root       0:00 grep tee

Saya memiliki fungsi yang memantau ukuran log yang diproduksi tee dan membunuh perintah tee ketika log mencapai ukuran tertentu:

monitor_debug_log_size() {
                ## Monitor the file size of the debug log to make sure it does not get too big
                while true; do
                                cecho r "CHECKING DEBUG LOG SIZE... "
                                debugLogSizeBytes=$(stat -c%s "/tmp/nginx/debug_log.log")
                                cecho r "DEBUG LOG SIZE: $debugLogSizeBytes"
                                if [ $((debugLogSizeBytes)) -gt 100000 ]; then
                                                cecho r "DEBUG LOG HAS GROWN TO LARGE... "
                                                sleep 3
                                                #rm -rf /tmp/nginx/debug_log.log 1>/dev/null 2>/dev/null
                                                kill -9 `pgrep -f tee`
                                fi
                                sleep 30
                done
}

Yang mengejutkan saya, membunuh perintah tee juga membunuh dengan start.sh misalnya. Kenapa ini? Bagaimana saya bisa mengakhiri perintah tee tetapi start.sh saya terus berjalan? Terima kasih.

PhilBot
sumber

Jawaban:

34

Ketika teeberakhir, perintah makan itu akan terus berjalan, sampai ia mencoba untuk menulis lebih banyak output. Maka akan mendapatkan SIGPIPE (13 pada kebanyakan sistem) untuk mencoba menulis ke pipa tanpa pembaca.

Jika Anda memodifikasi skrip Anda untuk menjebak SIGPIPE dan mengambil beberapa tindakan yang sesuai (seperti, berhenti menulis keluaran), maka Anda harus dapat melanjutkannya setelah tee dihentikan.


Lebih baik lagi, daripada membunuh tee sama sekali, gunakan logrotatedengan copytruncateopsi untuk kesederhanaan.

Mengutip logrotate(8):

copytruncate

Potong file log asli di tempat setelah membuat salinan, alih-alih memindahkan file log lama dan secara opsional membuat yang baru. Ini dapat digunakan ketika beberapa program tidak dapat diberitahu untuk menutup logfile-nya dan dengan demikian dapat terus menulis (menambahkan) ke file log sebelumnya selamanya. Perhatikan bahwa ada irisan waktu yang sangat kecil antara menyalin file dan memotongnya, sehingga beberapa data logging mungkin hilang. Ketika opsi ini digunakan, opsi buat tidak akan berpengaruh, karena file log lama tetap di tempatnya.

Wildcard
sumber
9
Anda juga ingin menggunakan tee -auntuk teemembuka file dalam mode append, jika tidak, tee akan melanjutkan penulisan ke dalam file pada offset yang sama setelah Anda memotongnya (dan pada sistem yang tidak mendukung file jarang seperti pada macOS yang akan realokasi bagian file yang mengarah ke posisi itu, mengambil ruang disk dua kali lebih banyak).
Stéphane Chazelas
4
Pilihan lain adalah untuk pipa ke logger -suntuk syslog untuk mengurus logging ( -sjuga mencetak pada stderr).
Stéphane Chazelas
1
+1 untuk logrotate. Program hebat
Dmitry Kudriavtsev
2
Atau pada sistem yang menggunakan systemd dan journald, systemd-cat bukannya logger. Kemudian Anda mendapatkan banyak penyaringan keluaran dan rotasi secara gratis.
Zan Lynx
3

Menjelaskan "Mengapa"

Singkatnya: Jika penulisan gagal tidak menyebabkan program keluar (secara default), kami akan mengalami kekacauan. Pertimbangkan find . | head -n 10- Anda tidak ingin findterus berjalan, memindai sisa hard drive Anda, setelah headmengambil 10 baris yang diperlukan dan melanjutkan.

Melakukannya dengan Lebih Baik: Putar di Dalam Pencatat Anda

Pertimbangkan yang berikut, yang tidak digunakan teesama sekali, sebagai contoh demonstratif:

#!/usr/bin/env bash

file=${1:-debug.log}                     # filename as 1st argument
max_size=${2:-100000}                    # max size as 2nd argument
size=$(stat --format=%s -- "$file") || exit  # Use GNU stat to retrieve size
exec >>"$file"                           # Open file for append

while IFS= read -r line; do              # read a line from stdin
  size=$(( size + ${#line} + 1 ))        # add line's length + 1 to our counter
  if (( size > max_size )); then         # and if it exceeds our maximum...
    mv -- "$file" "$file.old"            # ...rename the file away...
    exec >"$file"                        # ...and reopen a new file as stdout
    size=0                               # ...resetting our size counter
  fi
  printf '%s\n' "$line"                  # regardless, append to our current stdout
done

Jika dijalankan sebagai:

/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log

... ini akan dimulai dengan menambahkan ke /tmp/nginx/debug_log, mengganti nama file /tmp/nginx/debug_log.oldketika lebih dari 100KB konten hadir. Karena logger itu sendiri sedang melakukan rotasi, tidak ada pipa yang rusak, tidak ada kesalahan, dan tidak ada jendela kehilangan data ketika rotasi terjadi - setiap baris akan ditulis ke satu file atau yang lain.

Tentu saja, mengimplementasikan ini dalam bash asli tidak efisien, tetapi di atas adalah contoh ilustrasi. Ada banyak program yang tersedia yang akan menerapkan logika di atas untuk Anda. Mempertimbangkan:

  • svlogd, logger layanan dari suite Runit.
  • s6-log, alternatif yang dikelola secara aktif dari skanet suite.
  • multilog dari DJB Daemontools, kakek dari keluarga alat pengawasan dan pemantauan ini.
Charles Duffy
sumber