إذا سبق لك استخدام واجهات الويب لعرض السجلات، فمن المحتمل أنك لاحظت كيف أن هذه الواجهات، كقاعدة عامة، مرهقة و(في كثير من الأحيان) ليست مريحة للغاية وسريعة الاستجابة. بعضها يمكنك التعود عليه، وبعضها فظيع تمامًا، ولكن يبدو لي أن سبب كل المشكلات هو أننا نتعامل مع مهمة عرض السجلات بشكل غير صحيح: نحاول إنشاء واجهة ويب حيث يكون CLI (واجهة سطر الأوامر) يعمل بشكل أفضل. أنا شخصيًا أشعر براحة شديدة في العمل مع tail وgrep وawk وغيرها، وبالتالي فإن الواجهة المثالية للعمل مع السجلات بالنسبة لي ستكون شيئًا مشابهًا لـ tail وgrep، ولكن يمكن استخدامها أيضًا لقراءة السجلات الواردة من العديد من الخوادم. وهذا هو، بطبيعة الحال، قراءتها من ClickHouse!
*حسب الرأي الشخصي لمستخدم الهبرة
تعرف على logscli
لم أتوصل إلى اسم للواجهة الخاصة بي، وبصراحة، فهي موجودة في شكل نموذج أولي، ولكن إذا كنت تريد رؤية الكود المصدري على الفور، فنحن نرحب بك: (350 سطرًا من رمز Go المحدد).
قدرات
كان هدفي هو إنشاء واجهة تبدو مألوفة لأولئك الذين اعتادوا على استخدام tail/grep، أي دعم الأشياء التالية:
- عرض كافة السجلات، دون تصفية.
- اترك أسطرًا تحتوي على سلسلة فرعية ثابتة (flag
-Fуgrep). - اترك الأسطر التي تطابق التعبير العادي (flag
-Eуgrep). - بشكل افتراضي، يتم العرض بترتيب زمني عكسي، نظرًا لأن أحدث السجلات عادة ما تكون ذات أهمية أولاً.
- إظهار السياق بجوار كل سطر (options
-A,-Bи-Cуgrep، طباعة خطوط N قبل كل سطر مطابق وبعده وحوله، على التوالي). - عرض السجلات الواردة في الوقت الحقيقي، مع أو بدون تصفية (بشكل أساسي
tail -f | grep). - يجب أن تكون الواجهة متوافقة مع
less,head,tailوغيرها - بشكل افتراضي، يجب إرجاع النتائج دون قيود على عددها؛ تتم طباعة الخطوط كتدفق طالما أن المستخدم مهتم بتلقيها؛ الإشارةSIGPIPEيجب أن يقاطعوا تدفق السجل بصمت، تمامًا كما يفعلونtail,grepوغيرها من المرافق UNIX.
تطبيق
سأفترض أنك تعرف بالفعل بطريقة أو بأخرى كيفية تسليم السجلات إلى ClickHouse. إذا لم يكن الأمر كذلك، أوصي بتجربته и و .
تحتاج أولاً إلى اتخاذ قرار بشأن المخطط الأساسي. نظرًا لأنك ترغب عادةً في تلقي السجلات مرتبة حسب الوقت، فمن المنطقي تخزينها بهذه الطريقة. إذا كان هناك العديد من فئات السجل وكلها من نفس النوع، فيمكنك إنشاء فئة سجل كعمود أول من المفتاح الأساسي - وهذا سيسمح لك بالحصول على جدول واحد بدلاً من عدة جدول، وهو ما سيكون إضافة كبيرة عندما الإدخال في ClickHouse (على الخوادم التي تحتوي على محركات أقراص ثابتة، يوصى بإدخال البيانات بما لا يزيد عن ~1 مرة في الثانية) للخادم بأكمله).
أي أننا نحتاج تقريبًا إلى مخطط الجدول التالي:
CREATE TABLE logs(
category LowCardinality(String), -- категория логов (опционально)
time DateTime, -- время события
millis UInt16, -- миллисекунды (могут быть и микросекунды, и т.д.): рекомендуется хранить, если событий много, чтобы было легче различать события между собой
..., -- ваши собственные поля, например имя сервера, уровень логирования, и так далее
message String -- текст сообщения
) ENGINE=MergeTree()
ORDER BY (category, time, millis)لسوء الحظ، لم أتمكن على الفور من العثور على أي مصادر مفتوحة تحتوي على سجلات واقعية يمكنني الحصول عليها وتنزيلها، لذلك أخذت هذا كمثال بدلاً من ذلك . بالطبع، هيكلها ليس هو نفسه تمامًا هيكل السجلات النصية، ولكن لأغراض التوضيح، هذا ليس مهمًا.
تعليمات لتحميل مراجعات أمازون إلى 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في مجموعة بيانات أمازون، لا يوجد سوى تاريخ للمراجعة، ولكن لا يوجد وقت محدد، لذلك دعونا نملأ هذه البيانات بترتيب عشوائي.
لا يتعين عليك تنزيل جميع ملفات tsv والاكتفاء بأول ~ 10-20 للحصول على مجموعة كبيرة إلى حد ما من البيانات التي لا تتناسب مع 16 جيجابايت من ذاكرة الوصول العشوائي. لتحميل ملفات 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على قرص ثابت قياسي (وهو عبارة عن محرك أقراص ثابتة) في Google Cloud بحجم 1000 جيجابايت (لقد أخذت هذا الحجم بشكل أساسي بحيث تكون السرعة أعلى قليلاً، على الرغم من أنه ربما يكون SSD بالحجم المطلوب أرخص) التحميل كانت السرعة حوالي 75 ميجابايت / ثانية تقريبًا على 4 مراكز.
- يجب أن أقوم بالحجز بأني أعمل في جوجل ولكني استخدمت حساب شخصي وهذا المقال ليس له علاقة بعملي في الشركة
سأقوم بإنتاج جميع الرسوم التوضيحية باستخدام مجموعة البيانات هذه، لأن هذا هو كل ما كان في متناول يدي.
عرض تقدم مسح البيانات
نظرًا لأننا في ClickHouse سنستخدم فحصًا كاملاً على جدول يحتوي على سجلات، ويمكن أن تستغرق هذه العملية وقتًا طويلاً وقد لا تؤدي إلى أي نتائج لفترة طويلة إذا تم العثور على عدد قليل من التطابقات، فمن المستحسن أن تكون قادرًا على عرض تقدم الاستعلام حتى يتم استلام الصفوف الأولى بالنتيجة. للقيام بذلك، هناك معلمة في واجهة HTTP تسمح لك بإرسال التقدم في رؤوس HTTP: send_progress_in_http_headers=1. لسوء الحظ، لا تستطيع مكتبة Go القياسية قراءة الرؤوس عند استلامها، ولكن واجهة HTTP 1.0 (يجب عدم الخلط بينها وبين 1.1!) مدعومة من ClickHouse، لذا يمكنك فتح اتصال 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 يحاول الكتابة إلى أنبوب مغلق، .
في 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 بإرجاع السطر المقابل، فإنه ينتهي في ذاكرة التخزين المؤقت وبشكل عام يتم تنفيذ الطلب بسرعة كبيرة ويستهلك القليل من وحدة المعالجة المركزية (عادةً ما يستغرق الطلب حوالي 6 مللي ثانية على جهازي الظاهري).
عرض الرسائل الجديدة في الوقت الحقيقي
من أجل إظهار الرسائل الواردة في الوقت الفعلي (تقريبًا)، نقوم ببساطة بتنفيذ الطلب مرة واحدة كل بضع ثوانٍ، مع تذكر آخر طابع زمني واجهناه من قبل.
أمثلة الأوامر
كيف تبدو أوامر logscli النموذجية عمليًا؟
إذا قمت بتنزيل مجموعة بيانات أمازون التي ذكرتها في بداية المقال، فيمكنك تشغيل الأوامر التالية:
# Показать строки, где встречается слово 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مراجع
رمز الأداة المساعدة (بدون وثائق) متاح على جيثب في . سأكون سعيدًا لسماع أفكارك حول فكرتي الخاصة بواجهة وحدة التحكم لعرض السجلات بناءً على ClickHouse.
المصدر: www.habr.com
