Làm thế nào để chấm dứt lệnh tee Linux mà không giết ứng dụng mà nó đang nhận từ


19

Tôi có một tập lệnh bash chạy miễn là máy Linux được bật. Tôi bắt đầu nó như hình dưới đây:

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

Sau khi nó hoạt động, tôi có thể thấy lệnh tee trong đầu ra ps của mình như dưới đây:

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

Tôi có một chức năng theo dõi kích thước của nhật ký mà tee tạo ra và giết lệnh tee khi nhật ký đạt đến một kích thước nhất định:

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
}

Trước sự ngạc nhiên của tôi, việc giết lệnh tee cũng giết chết cá thể start.sh. Tại sao lại thế này? Làm cách nào tôi có thể kết thúc lệnh tee nhưng để start.sh tiếp tục chạy? Cảm ơn.

Câu trả lời:


34

Khi teechấm dứt, lệnh cho ăn nó sẽ tiếp tục chạy, cho đến khi nó cố gắng ghi thêm đầu ra. Sau đó, nó sẽ nhận được SIGPIPE (13 trên hầu hết các hệ thống) để cố gắng ghi vào một đường ống không có người đọc.

Nếu bạn sửa đổi tập lệnh của mình để bẫy SIGPIPE và thực hiện một số hành động thích hợp (như, dừng viết đầu ra), thì bạn sẽ có thể tiếp tục tập lệnh đó sau khi tee bị chấm dứt.


Tốt hơn hết, thay vì giết chết tee tất cả, sử dụng logrotatevới copytruncatetùy chọn cho đơn giản.

Để trích dẫn logrotate(8) :

copytruncate

Cắt bớt tệp nhật ký gốc tại chỗ sau khi tạo bản sao, thay vì di chuyển tệp nhật ký cũ và tùy ý tạo tệp nhật ký mới. Nó có thể được sử dụng khi một số chương trình không thể được yêu cầu đóng logfile của nó và do đó có thể tiếp tục ghi (nối thêm) vào tệp nhật ký trước đó mãi mãi. Lưu ý rằng có một lát cắt thời gian rất nhỏ giữa việc sao chép tệp và cắt bớt nó, vì vậy một số dữ liệu đăng nhập có thể bị mất. Khi tùy chọn này được sử dụng, tùy chọn tạo sẽ không có hiệu lực, vì tệp nhật ký cũ vẫn giữ nguyên vị trí.


9
Bạn cũng muốn sử dụng tee -acho teeđể mở tập tin trong chế độ append, nếu không, tee sẽ tiếp tục viết vào tập tin cùng bù đắp sau khi bạn cắt nó (và trên các hệ thống không hỗ trợ các file thưa thớt như trên hệ điều hành MacOS rằng ý chí phân bổ lại phần của tập tin dẫn đến vị trí đó, chiếm gấp đôi dung lượng đĩa).
Stéphane Chazelas

4
Tùy chọn khác sẽ là chuyển sang logger -ssyslog để xử lý việc ghi nhật ký (-s để in trên stderr).
Stéphane Chazelas

1
+1 cho logrotate. Chương trình tuyệt vời
Dmitry Kudriavtsev

2
Hoặc trên một hệ thống sử dụng systemd và journald, systemd-cat thay vì logger. Sau đó, bạn nhận được rất nhiều bộ lọc đầu ra và xoay miễn phí.
Zan Lynx

3

Giải thích "Tại sao"

Nói tóm lại: Nếu việc ghi thất bại không khiến chương trình thoát ra (theo mặc định), chúng ta sẽ có một mớ hỗn độn. Hãy xem xét find . | head -n 10- bạn không muốn findtiếp tục chạy, quét phần còn lại của ổ cứng, sauhead đã thực hiện xong 10 dòng cần thiết và tiếp tục.

Làm điều đó tốt hơn: Xoay bên trong Logger của bạn

Hãy xem xét những điều sau đây, hoàn toàn không sử dụng tee, như một ví dụ minh họa:

#!/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

Nếu chạy như:

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

... điều này sẽ bắt đầu bằng cách nối thêm vào /tmp/nginx/debug_log , đổi tên tệp thành /tmp/nginx/debug_log.oldkhi có hơn 100KB nội dung. Bởi vì bản thân bộ ghi đang thực hiện xoay vòng, không có đường ống bị hỏng, không có lỗi và không có cửa sổ mất dữ liệu khi xoay vòng diễn ra - mỗi dòng sẽ được ghi vào tệp này hoặc tệp khác.

Tất nhiên, thực hiện điều này trong bash bản địa là không hiệu quả, nhưng ở trên là một ví dụ minh họa.Có rất nhiều chương trình có sẵn sẽ thực hiện logic trên cho bạn. Xem xét:

  • svlogd, bộ ghi dịch vụ từ bộ Runit.
  • s6-log, một sự thay thế được duy trì tích cực từ bộ skanet.
  • multilog từ DJB Daemontools, ông lớn của gia đình giám sát quy trình và công cụ giám sát này.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.