วิธีที่ดีที่สุดในการติดตามบันทึกและดำเนินการคำสั่งเมื่อมีข้อความปรากฏในบันทึก


54

ฉันมีบันทึกเซิร์ฟเวอร์ที่แสดงบรรทัดข้อความเฉพาะลงในไฟล์บันทึกเมื่อเซิร์ฟเวอร์หยุดทำงาน ฉันต้องการรันคำสั่งเมื่อเซิร์ฟเวอร์ทำงานและด้วยเหตุนี้ให้ทำสิ่งต่อไปนี้:

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

วิธีที่ดีที่สุดในการทำเช่นนี้คืออะไร?


3
จะแน่ใจว่าจะใช้tail -Fในการจัดการเข้าสู่ระบบหมุน - คือmy.logจะเต็มและย้ายไปmy.log.1และขั้นตอนของคุณจะสร้างใหม่my.log
SG

คำตอบ:


34

วิธีง่าย ๆ จะ awk

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

และใช่ทั้งสองอย่างเป็นข้อความจริงจากบันทึกของเคอร์เนล Perl อาจจะดูดีกว่านี้เล็กน้อยสำหรับการใช้งานนี้และยังสามารถทดแทนความต้องการหางได้ หากใช้ Perl จะมีลักษณะดังนี้:

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");
    }
}

ฉันชอบawkวิธีแก้ปัญหาที่สั้นและง่ายต่อการทำในบรรทัดเดียว อย่างไรก็ตามหากคำสั่งที่ฉันต้องการเรียกใช้มีเครื่องหมายอัญประกาศนี่อาจเป็นบิตยุ่งยากมีทางเลือกอื่นอาจใช้คำสั่ง pipelines และคำสั่งผสมที่อนุญาตให้ใช้โซลูชัน one-liner ย่อ ๆ แต่ไม่ต้องการให้ส่งคำสั่งผลลัพธ์ เป็นสตริงหรือไม่?
เบื่อ

1
@jon คุณสามารถเขียน awk เป็นสคริปต์ได้ ใช้ "#! / usr / bin awk -f" เป็นบรรทัดแรกของสคริปต์ สิ่งนี้จะช่วยลดความจำเป็นในการใช้อัญประกาศด้านนอกในตัวอย่างของฉันและปล่อยให้ใช้ในsystem()คำสั่ง
penguin359

@ penguin359, จริง แต่ก็ยังน่าสนใจที่จะทำจากบรรทัดคำสั่งเช่นกัน ในกรณีของฉันมีหลายสิ่งหลายอย่างที่ฉันต้องการจะทำรวมถึงสิ่งต่าง ๆ ที่ฉันไม่สามารถคาดการณ์ได้ดังนั้นจึงสะดวกที่จะเริ่มเซิร์ฟเวอร์และทำได้ทั้งหมดในบรรทัดเดียว
เบื่อ

ฉันพบทางเลือกแม้ว่าฉันจะไม่ทราบว่ามันแข็งแกร่งเพียงใด:tail -f /path/to/serverLog | grep "server is up" | head -1 && do_some_command
354 jonderry jonderry

@ จอนดูเหมือนว่าจะเปราะบางเล็กน้อยเมื่อใช้หัวแบบนั้น ที่สำคัญไม่สามารถทำซ้ำได้เหมือนตัวอย่างของฉัน หาก "เซิร์ฟเวอร์ไม่ทำงาน" อยู่ในสิบบรรทัดสุดท้ายของไฟล์บันทึกจะเริ่มต้นคำสั่งและออกทันที หากคุณรีสตาร์ทมันจะเป็นไปได้มากที่จะเริ่มต้นและออกอีกครั้งเว้นแต่ว่าจะไม่มีการเพิ่ม "สิบเซิร์ฟเวอร์" ลงในบันทึก การปรับเปลี่ยนที่อาจใช้งานได้ดีกว่าคือtail -n 0 -f /path/to/serverLog จะอ่าน 0 บรรทัดสุดท้ายของไฟล์จากนั้นรออีกหลายบรรทัดเพื่อพิมพ์
penguin359

16

หากคุณกำลังมองหาความเป็นไปได้เพียงอย่างเดียวและต้องการอยู่ในเชลล์มากกว่าการใช้awkหรือperlคุณอาจทำสิ่งต่อไปนี้

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

... ซึ่งจะทำงานmy_commandทุกครั้งที่ " เซิร์ฟเวอร์ไม่ทำงาน " ปรากฏในไฟล์บันทึก สำหรับความเป็นไปได้หลายครั้งคุณอาจจะสามารถวางgrepและแทนที่จะใช้ภายในcasewhile

เมืองหลวง-Fบอกtailให้ดูไฟล์บันทึกที่จะหมุน เช่นหากไฟล์ปัจจุบันได้รับการเปลี่ยนชื่อและไฟล์อื่นที่มีชื่อเดียวกันเข้าแทนที่จะtailเปลี่ยนเป็นไฟล์ใหม่

--line-bufferedตัวเลือกที่จะบอกgrepล้างบัฟเฟอร์หลังจากทุกสาย มิฉะนั้นmy_commandอาจไม่สามารถเข้าถึงได้ในเวลาที่เหมาะสม (สมมติว่าบันทึกมีขนาดเส้นพอสมควร)


2
ฉันชอบคำตอบนี้จริงๆ แต่ก็ไม่ได้ผลสำหรับฉันในตอนแรก ฉันคิดว่าคุณจำเป็นต้องเพิ่ม--line-bufferedตัวเลือกให้grepหรือมิฉะนั้นให้แน่ใจว่ามันล้างข้อมูลระหว่างบรรทัด: ไม่อย่างนั้นมันแค่แฮงค์และmy_commandไม่ถึง หากคุณต้องการackมันมี--flushธง ถ้าคุณต้องการลองห่อด้วยag stackoverflow.com/questions/28982518/…stdbuf
doctaphred

do exit ;ฉันพยายามนี้โดยใช้ ดูเหมือนว่าจะทำงานได้ดี แต่หางไม่เคยเสร็จและสคริปต์ของเราไม่เคยขยับไปยังบรรทัดถัดไป มีวิธีหยุดหางในdoส่วนหรือไม่
Machtyn

14

ดูเหมือนว่าคำถามนี้จะได้รับคำตอบแล้ว แต่ฉันคิดว่ามันมีทางออกที่ดีกว่า

มากกว่าผมคิดว่าสิ่งที่คุณต้องการจริงๆคือtail | whatever swatchSwatch เป็นโปรแกรมที่ออกแบบมาอย่างชัดเจนสำหรับการทำสิ่งที่คุณขอดูไฟล์บันทึกและดำเนินการตามบรรทัดบันทึก การใช้tail|fooจะต้องให้คุณมีเทอร์มินัลกำลังทำงานเพื่อทำสิ่งนี้ สวอตช์ในทางกลับกันทำงานเป็น daemon และจะคอยดูบันทึกของคุณเสมอ Swatch มีให้บริการใน Linux distros ทั้งหมด

ฉันแนะนำให้คุณลองดู ในขณะที่คุณสามารถตอกตะปูกับด้านหลังของไขควงไม่ได้หมายความว่าคุณควร

บทเรียน 30 วินาทีที่ดีที่สุดสำหรับสวอตช์ฉันหาได้จากที่นี่: http://www.campin.net/newlogcheck.html


1
บทช่วยสอนนั่นคือ 404 ในขณะนี้: /
จาก

1
WebArchive เป็นเพื่อนของคุณ: web.archive.org/web/20140922041805/http://campin.net/…
อาทิตย์ที่

10

เป็นเรื่องแปลกที่ไม่มีใครพูดถึงmultitailยูทิลิตี้ที่มีฟังก์ชั่นนี้นอกกรอบ หนึ่งในตัวอย่างการใช้งาน:

แสดงเอาต์พุตของคำสั่ง ping และถ้ามันแสดงการหมดเวลาใช้งานให้ส่งข้อความไปยังผู้ใช้ทั้งหมดที่เข้าสู่ระบบในปัจจุบัน

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

ดูเพิ่มเติมตัวอย่างอื่นของmultitailการใช้งาน


+1, ฉันไม่รู้เลยว่า `มัลติทาสเลอร์มีทักษะแบบนินจาเหล่านั้นซ่อนตัวอยู่ ขอบคุณสำหรับการชี้ให้เห็นว่า
Caleb

8

สามารถทำงานได้ด้วยตัวเอง

ให้ดูว่ามันง่ายและอ่านได้ง่าย:

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)

ในขณะที่คุณไม่ได้ใช้ bash regexคุณสามารถทำได้อย่างรวดเร็ว!

แต่ + นั้นมีประสิทธิภาพและน่าสนใจ

แต่สำหรับเซิร์ฟเวอร์โหลดสูงและอย่างที่ฉันชอบsedเพราะมันรวดเร็วและปรับขนาดได้มากฉันมักจะใช้สิ่งนี้:

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;
  ...
')

6

นั่นคือวิธีที่ฉันเริ่มทำเช่นนี้ แต่มีความซับซ้อนมากขึ้นด้วย สองสิ่งที่ต้องคำนึงถึง:

  1. หากส่วนท้ายของบันทึกมี "server is up" อยู่แล้ว
  2. สิ้นสุดกระบวนการหางโดยอัตโนมัติเมื่อพบแล้ว

ฉันใช้บางสิ่งบางอย่างตามแนวนี้:

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}

มันทำงานได้โดยtailเปิดค้างไว้จนกว่า${RELEASE}ไฟล์จะมีข้อมูล

เมื่อgrepประสบความสำเร็จ:

  1. เขียนผลลัพธ์${RELEASE}ที่จะ
  2. ยุติ${wait_pid}กระบวนการเพื่อ
  3. ออกจาก tail

หมายเหตุ: sedสามารถมีความซับซ้อนมากขึ้นในการกำหนดจำนวนบรรทัดที่tailจะสร้างเมื่อเริ่มต้นและลบหมายเลขนั้น แต่โดยทั่วไปมันคือ 10

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.