เรากำลังพัฒนาอินเทอร์เฟซที่สะดวกที่สุดในโลก* สำหรับการดูบันทึก

เรากำลังพัฒนาอินเทอร์เฟซที่สะดวกที่สุดในโลก* สำหรับการดูบันทึก หากคุณเคยใช้อินเทอร์เฟซเว็บเพื่อดูบันทึก คุณอาจสังเกตเห็นว่าตามกฎแล้วอินเทอร์เฟซเหล่านี้ยุ่งยากและ (บ่อยครั้ง) ไม่สะดวกและตอบสนองมากนัก คุณสามารถคุ้นเคยบางส่วนได้บางส่วนก็แย่มาก แต่สำหรับฉันแล้วดูเหมือนว่าสาเหตุของปัญหาทั้งหมดคือการที่เราเข้าใกล้งานดูบันทึกไม่ถูกต้อง: เราพยายามสร้างเว็บอินเตอร์เฟสที่ CLI (อินเทอร์เฟซบรรทัดคำสั่ง) ทำงานได้ดีขึ้น โดยส่วนตัวแล้วฉันรู้สึกสบายใจมากที่ได้ทำงานกับ tail, grep, awk และอื่นๆ ดังนั้นสำหรับฉัน อินเทอร์เฟซในอุดมคติสำหรับการทำงานกับบันทึกจะคล้ายกับ tail และ grep แต่ก็สามารถใช้เพื่ออ่านบันทึกที่มาจากเซิร์ฟเวอร์จำนวนมากได้ แน่นอนว่าอ่านได้จาก ClickHouse!

*ตามความคิดเห็นส่วนตัวของผู้ใช้ฮาบรา คุณร็อค

พบกับ logscli

ฉันไม่ได้ตั้งชื่ออินเทอร์เฟซของฉันขึ้นมา และบอกตามตรงว่ามันค่อนข้างมีอยู่ในรูปแบบของต้นแบบ แต่ถ้าคุณต้องการเห็นซอร์สโค้ดทันที คุณก็ยินดีต้อนรับ: https://github.com/YuriyNasretdinov/logscli (รหัส Go ที่เลือก 350 บรรทัด)

ความสามารถในการ

เป้าหมายของฉันคือการสร้างอินเทอร์เฟซที่ดูคุ้นเคยสำหรับผู้ที่คุ้นเคยกับ tail/grep นั่นคือเพื่อรองรับสิ่งต่อไปนี้:

  1. ดูบันทึกทั้งหมดโดยไม่ต้องกรอง
  2. ปล่อยให้บรรทัดมีสตริงย่อยคงที่ (flag -F у grep).
  3. เว้นบรรทัดที่ตรงกับนิพจน์ทั่วไป (flag -E у grep).
  4. ตามค่าเริ่มต้น การดูจะเรียงลำดับเวลาแบบย้อนกลับ เนื่องจากบันทึกล่าสุดมักเป็นที่สนใจก่อน
  5. แสดงบริบทถัดจากแต่ละบรรทัด (options -A, -B и -C у grepพิมพ์ N บรรทัดก่อน หลัง และรอบแต่ละบรรทัดที่ตรงกัน ตามลำดับ)
  6. ดูบันทึกที่เข้ามาแบบเรียลไทม์ โดยมีหรือไม่มีการกรอง (โดยพื้นฐานแล้ว tail -f | grep).
  7. อินเทอร์เฟซจะต้องเข้ากันได้กับ less, head, tail และอื่น ๆ - โดยค่าเริ่มต้น ควรส่งคืนผลลัพธ์โดยไม่มีข้อจำกัดด้านจำนวน เส้นจะถูกพิมพ์เป็นสตรีมตราบใดที่ผู้ใช้สนใจที่จะรับมัน สัญญาณ SIGPIPE ควรขัดจังหวะการสตรีมบันทึกอย่างเงียบๆ เช่นเดียวกับที่ทำ tail, grep และยูทิลิตี้ UNIX อื่นๆ

การดำเนินงาน

ฉันจะถือว่าคุณรู้วิธีส่งบันทึกไปยัง ClickHouse อยู่แล้ว ถ้าไม่ฉันแนะนำให้ลอง LSD и บ้านลูกแมวและ บทความเกี่ยวกับการจัดส่งบันทึกนี้.

ก่อนอื่นคุณต้องตัดสินใจเกี่ยวกับโครงร่างพื้นฐาน เนื่องจากโดยปกติแล้วคุณต้องการรับบันทึกที่จัดเรียงตามเวลา จึงดูสมเหตุสมผลที่จะจัดเก็บบันทึกในลักษณะนั้น หากมีหมวดหมู่บันทึกหลายประเภทและเป็นประเภทเดียวกันทั้งหมด คุณสามารถสร้างหมวดหมู่บันทึกเป็นคอลัมน์แรกของคีย์หลักได้ ซึ่งจะช่วยให้คุณมีตารางเดียวแทนที่จะเป็นหลายตาราง ซึ่งจะเป็นประโยชน์อย่างมากเมื่อ การใส่ข้อมูลลงใน ClickHouse (บนเซิร์ฟเวอร์ที่มีฮาร์ดไดรฟ์ แนะนำให้ใส่ข้อมูลไม่เกิน ~1 ครั้งต่อวินาที สำหรับเซิร์ฟเวอร์ทั้งหมด).

นั่นคือเราต้องการโครงร่างตารางต่อไปนี้โดยประมาณ:

CREATE TABLE logs(
    category LowCardinality(String), -- категория логов (опционально)
    time DateTime, -- время события
    millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
    ..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
    message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)

น่าเสียดายที่ฉันไม่สามารถหาโอเพ่นซอร์สที่มีบันทึกจริงที่สามารถคว้าและดาวน์โหลดได้ในทันที ดังนั้นฉันจึงใช้สิ่งนี้เป็นตัวอย่างแทน รีวิวสินค้าจาก Amazon ก่อนปี 2015. แน่นอนว่าโครงสร้างของมันไม่เหมือนกันทุกประการกับบันทึกข้อความ แต่เพื่อจุดประสงค์ในการอธิบายสิ่งนี้ไม่สำคัญ

คำแนะนำในการอัพโหลดรีวิว Amazon ไปที่ ClickHouse

มาสร้างตารางกันเถอะ:

CREATE TABLE amazon(
   review_date Date,
   time DateTime DEFAULT toDateTime(toUInt32(review_date) * 86400 + rand() % 86400),
   millis UInt16 DEFAULT rand() % 1000,
   marketplace LowCardinality(String),
   customer_id Int64,
   review_id String,
   product_id LowCardinality(String),
   product_parent Int64,
   product_title String,
   product_category LowCardinality(String),
   star_rating UInt8,
   helpful_votes UInt32,
   total_votes UInt32,
   vine FixedString(1),
   verified_purchase FixedString(1),
   review_headline String,
   review_body String
)
ENGINE=MergeTree()
ORDER BY (time, millis)
SETTINGS index_granularity=8192

ในชุดข้อมูล Amazon มีเพียงวันที่สำหรับการตรวจสอบ แต่ไม่มีเวลาที่แน่นอน ดังนั้นเรามากรอกข้อมูลนี้ด้วยแรนดอนกันดีกว่า

คุณไม่จำเป็นต้องดาวน์โหลดไฟล์ tsv ทั้งหมดและจำกัดตัวเองไว้ที่ประมาณ 10-20 ไฟล์แรก เพื่อให้ได้ชุดข้อมูลที่ค่อนข้างใหญ่ซึ่งไม่พอดีกับ RAM ขนาด 16 GB ในการอัปโหลดไฟล์ TSV ฉันใช้คำสั่งต่อไปนี้:

for i in *.tsv; do
    echo $i;
    tail -n +2 $i | pv |
    clickhouse-client --input_format_allow_errors_ratio 0.5 --query='INSERT INTO amazon(marketplace,customer_id,review_id,product_id,product_parent,product_title,product_category,star_rating,helpful_votes,total_votes,vine,verified_purchase,review_headline,review_body,review_date) FORMAT TabSeparated'
done

บน Persistent Disk มาตรฐาน (ซึ่งเป็น HDD) ใน Google Cloud ที่มีขนาด 1000 GB (ฉันใช้ขนาดนี้เป็นหลักเพื่อให้ความเร็วสูงขึ้นเล็กน้อย แม้ว่าบางที SSD ที่มีขนาดที่ต้องการอาจมีราคาถูกกว่าก็ตาม) การอัปโหลด ความเร็วอยู่ที่ประมาณ ~ 75 MB/วินาที บน 4 คอร์

  • ฉันต้องจองว่าฉันทำงานที่ Google แต่ฉันใช้บัญชีส่วนตัวและบทความนี้ไม่เกี่ยวข้องกับงานของฉันที่บริษัท

ฉันจะสร้างภาพประกอบทั้งหมดด้วยชุดข้อมูลนี้ เนื่องจากนี่คือทั้งหมดที่ฉันมี

แสดงความคืบหน้าในการสแกนข้อมูล

เนื่องจากใน ClickHouse เราจะใช้การสแกนแบบเต็มบนโต๊ะพร้อมบันทึก และการดำเนินการนี้อาจใช้เวลานานมากและอาจไม่ให้ผลลัพธ์ใดๆ เป็นเวลานานหากพบรายการที่ตรงกันน้อย ขอแนะนำให้แสดง ความคืบหน้าของแบบสอบถามจนกระทั่งได้รับแถวแรกพร้อมผลลัพธ์ เมื่อต้องการทำเช่นนี้ มีพารามิเตอร์ในอินเทอร์เฟซ HTTP ที่ช่วยให้คุณสามารถส่งความคืบหน้าในส่วนหัว HTTP: send_progress_in_http_headers=1. น่าเสียดายที่ไลบรารี Go มาตรฐานไม่สามารถอ่านส่วนหัวตามที่ได้รับ แต่ ClickHouse รองรับอินเทอร์เฟซ HTTP 1.0 (เพื่อไม่ให้สับสนกับ 1.1!) ดังนั้นคุณจึงสามารถเปิดการเชื่อมต่อ TCP แบบดิบไปยัง ClickHouse และส่งไปที่นั่นได้ GET /?query=... HTTP/1.0nn และรับส่วนหัวและเนื้อหาการตอบกลับโดยไม่มีการหลบหนีหรือการเข้ารหัส ดังนั้นในกรณีนี้ เราไม่จำเป็นต้องใช้ไลบรารีมาตรฐานด้วยซ้ำ

บันทึกการสตรีมจาก ClickHouse

ClickHouse มีการเพิ่มประสิทธิภาพสำหรับข้อความค้นหาด้วย ORDER BY มาเป็นเวลานาน (ตั้งแต่ปี 2019?) ดังนั้นข้อความค้นหาเช่น

SELECT time, millis, message
FROM logs
WHERE message LIKE '%something%'
ORDER BY time DESC, millis DESC

จะเริ่มส่งคืนบรรทัดที่มีสตริงย่อย "บางอย่าง" ในข้อความทันที โดยไม่ต้องรอให้การสแกนเสร็จสิ้น

นอกจากนี้ จะสะดวกมากหาก ClickHouse เองยกเลิกคำขอเมื่อการเชื่อมต่อถูกปิด แต่นี่ไม่ใช่พฤติกรรมเริ่มต้น คุณสามารถเปิดใช้งานการยกเลิกคำขออัตโนมัติได้โดยใช้ตัวเลือก cancel_http_readonly_queries_on_client_close=1.

การจัดการ SIGPIPE ใน Go อย่างถูกต้อง

เมื่อคุณดำเนินการให้พูดคำสั่ง some_cmd | head -n 10ตรงตามคำสั่งอย่างไร some_cmd หยุดการดำเนินการเมื่อ head ลบ 10 บรรทัด? คำตอบนั้นง่าย: เมื่อใด head สิ้นสุด ไปป์ปิด และ stdout ของคำสั่ง some_cmd จะเริ่มชี้ "ไม่มีที่ไหนเลย" ตามเงื่อนไข เมื่อไร some_cmd พยายามเขียนไปยังไปป์ปิด รับสัญญาณ SIGPIPE ซึ่งจะยุติโปรแกรมโดยค่าเริ่มต้น.

ใน Go สิ่งนี้จะเกิดขึ้นตามค่าเริ่มต้นเช่นกัน แต่ตัวจัดการสัญญาณ SIGPIPE จะพิมพ์ "สัญญาณ: SIGPIPE" หรือข้อความที่คล้ายกันในตอนท้ายด้วย และเพื่อล้างข้อความนี้ เราเพียงแค่ต้องจัดการ SIGPIPE ด้วยตัวเองในแบบที่เราต้องการ นั่นคือ เพียงแค่เงียบ ๆ ออก:

ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGPIPE)
go func() {
    <-ch
    os.Exit(0)
}()

แสดงบริบทข้อความ

บ่อยครั้งคุณต้องการดูบริบทที่เกิดข้อผิดพลาด (เช่น คำขอใดที่ทำให้เกิดความตื่นตระหนก หรือมองเห็นปัญหาที่เกี่ยวข้องก่อนเกิดข้อขัดข้อง) และใน grep ซึ่งทำได้โดยใช้ตัวเลือก -A, -B และ -C ซึ่งแสดงจำนวนบรรทัดที่ระบุหลัง ก่อน และรอบๆ ข้อความ ตามลำดับ

น่าเสียดายที่ฉันไม่พบวิธีง่ายๆ ในการทำสิ่งเดียวกันใน ClickHouse ดังนั้นเพื่อแสดงบริบท คำขอเพิ่มเติมเช่นนี้จะถูกส่งไปยังแต่ละบรรทัดของผลลัพธ์ (รายละเอียดขึ้นอยู่กับการเรียงลำดับและบริบทจะแสดงก่อนหรือไม่ หรือหลัง):

SELECT time,millis,review_body FROM amazon
WHERE (time = 'ВРЕМЯ_СОБЫТИЯ' AND millis < МИЛЛИСЕКУНДЫ_СОБЫТИЯ) OR (time < 'ВРЕМЯ_СОБЫТИЯ')
ORDER BY time DESC, millis DESC
LIMIT КОЛИЧЕСТВО_СТРОК_КОНТЕКСТА
SETTINGS max_threads=1

เนื่องจากคำขอถูกส่งเกือบจะในทันทีหลังจากที่ ClickHouse ส่งคืนบรรทัดที่เกี่ยวข้อง มันจะจบลงในแคช และโดยทั่วไปคำขอจะถูกดำเนินการค่อนข้างเร็วและใช้ CPU เพียงเล็กน้อย (โดยปกติแล้วคำขอจะใช้เวลาประมาณ ~ 6 ms บนเครื่องเสมือนของฉัน)

แสดงข้อความใหม่แบบเรียลไทม์

เพื่อแสดงข้อความขาเข้า (เกือบ) แบบเรียลไทม์ เราเพียงดำเนินการตามคำขอทุกๆ สองสามวินาที โดยจดจำการประทับเวลาล่าสุดที่เราพบก่อนหน้านี้

ตัวอย่างคำสั่ง

คำสั่ง logscli ทั่วไปมีลักษณะอย่างไรในทางปฏิบัติ?

หากคุณดาวน์โหลดชุดข้อมูล Amazon ที่ฉันกล่าวถึงในตอนต้นของบทความ คุณสามารถเรียกใช้คำสั่งต่อไปนี้ได้:

# Показать строки, где встречается слово walmart
$ logscli -F 'walmart' | less

# Показать самые свежие 10 строк, где встречается "terrible"
$ logscli -F terrible -limit 10

# То же самое без -limit:
$ logscli -F terrible | head -n 10

# Показать все строки, подходящие под /times [0-9]/, написанные для vine и у которых высокий рейтинг
$ logscli -E 'times [0-9]' -where="vine='Y' AND star_rating>4" | less

# Показать все строки со словом "panic" и 3 строки контекста вокруг
$ logscli -F 'panic' -C 3 | less

# Непрерывно показывать новые строки со словом "5-star"
$ logscli -F '5-star' -tailf

การอ้างอิง

รหัสยูทิลิตี้ (ไม่มีเอกสารประกอบ) มีอยู่ใน github ที่ https://github.com/YuriyNasretdinov/logscli. ฉันยินดีที่จะได้ยินความคิดเห็นของคุณเกี่ยวกับแนวคิดของฉันเกี่ยวกับอินเทอร์เฟซคอนโซลสำหรับการดูบันทึกตาม ClickHouse

ที่มา: will.com

เพิ่มความคิดเห็น