أرسل تسجيلاتك إلى لوكي

يركز أحد محادثاتي الحالية على الرؤية الشاملة فيما يتعلق بالأمراض والتتبع الموزع تحديدًا مع تنفيذ OpenTelemetry. في العرض التوضيحي، أوضح كيف يمكنك رؤية مسارات نظام موزع بسيط يتألف من بوابة API Apache APISIX، تطبيق Kotlin مع Spring Boot، تطبيق Python مع Flask، وتطبيق Rust مع Axum.

في وقت سابق من هذا العام، تحدثت وحضرت غرفة الرؤية الشاملة في FOSDEM. تظاهر إحدى المحادثات المكونات المعقدة لـ Grafana: Mimir للمقاييس، Tempo للمسارات، و Loki للسجلات. كنت متفاجئًا بكيفية الانتقال من واحدة إلى الأخرى. ولذلك، أردت تحقيق الشيء نفسه في عرضي التوضيحي ولكن من خلال OpenTelemetry لتجنب الارتباط بمكونات Grafana.

في هذه المدونة، أرغب في التركيز على السجلات و Loki.

أساسيات Loki وبرنامجنا الأول

في جوهرها، تعتبر Loki محرك تخزين للسجلات:

تُعتبر Loki نظامًا لتجميع السجلات ذا توافق أفقي واسع، متاح بشكل عالمي، ويدعم الإشراف المتعدد الأصول. تم تصميمها لتكون مفيدة للغاية وسهلة الاستخدام. فهي لا تفهم محتويات السجلات، ولكنها تفهم مجموعة من الملصقات لكل جريدة للسجل.

Loki

توفر Loki واجهة RESTful لتخزين وقراءة السجلات. دعونا ندفع سجل من تطبيق Java. يتوقع Loki الهيكل الناتج التالي:

I’ll use Java, but you can achieve the same result with a different stack. The most straightforward code is the following:

Java

 

public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
    var template = "'{' \"streams\": ['{' \"stream\": '{' \"app\": \"{0}\" '}', \"values\": [[ \"{1}\", \"{2}\" ]]'}']'}'"; //1
    var now = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant();
    var nowInEpochNanos = NANOSECONDS.convert(now.getEpochSecond(), SECONDS) + now.getNano();
    var payload = MessageFormat.format(template, "demo", String.valueOf(nowInEpochNanos), "Hello from Java App");           //1
    var request = HttpRequest.newBuilder()                                                                                  //2
            .uri(new URI("http://localhost:3100/loki/api/v1/push"))
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();
    HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());                                         //3
}

  1. هكذا فعلنا التفاف السلاسل في الأيام القديمة
  2. إنشاء الطلب
  3. إرساله

النموذج يعمل، كما هو موضح في Grafana:

ومع ذلك، يحتوي الكود على العديد من القيود:

  • التسمية مبرمجة بشكل صعب. يمكنك ويجب أن ترسل تسمية واحدة
  • كل شيء مبرمج بشكل صعب؛ لا شيء قابل للتكيف، مثلاً، الرابط
  • يرسل الكود طلبًا واحدًا لكل ملاحظة؛ إنه غير فعال تمامًا لعدم وجود تخزين مؤقت
  • عميل HTTP متزامن، وبالتالي يحجب الخط أثناء انتظار Loki
  • لا يوجد معالجة أخطاء على الإطلاق
  • تقدم Loki ضغط gzip و Protobuf؛ لا شيء من ذلك يدعمها الكود
  • أخيرًا، إنه غير متعلق على الإطلاق بكيفية استخدامنا للسجلات، على سبيل المثال:

    Java

     

    var logger = // الحصول على السجل
    logger.info("رسالتي بمعلمات {}, {}", foo, bar);

التسجيل العادي على الأسترويد

لاستخدام العبارة أعلاه، نحتاج إلى اختيار تنفيذ للتسجيل. لأنني أعرفه أكثر، سأستخدم SLF4J و Logback. لا تقلق؛ النهج نفسه يعمل لـ Log4J2.

نحتاج إلى إضافة المركبات ذات الصلة:

XML

 

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>             <!--1-->
    <version>2.0.7</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>       <!--2-->
    <version>1.4.8</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.github.loki4j</groupId>
    <artifactId>loki-logback-appender</artifactId> <!--3-->
    <version>1.4.0</version>
    <scope>runtime</scope>
</dependency>

  1. SLF4J هو واجهة
  2. Logback هو التنفيذ
  3. مضاف مُشرّف Logback مخصص لـ SLF4J

الآن، نضيف مُشرّف Loki محدد:

XML

 

<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">                   <!--1-->
    <http>
        <url>http://localhost:3100/loki/api/v1/push</url>                                 <!--2-->
    </http>
    <format>
        <label>
            <pattern>app=demo,host=${HOSTNAME},level=%level</pattern>                     <!--3-->
        </label>
        <message>
            <pattern>l=%level h=${HOSTNAME} c=%logger{20} t=%thread | %msg %ex</pattern>  <!--4-->
        </message>
        <sortByTime>true</sortByTime>
    </format>
</appender>
<root level="DEBUG">
    <appender-ref ref="STDOUT" />
</root>

  1. مُشرّف Loki
  2. عنوان URL Loki
  3. عدد كبير من العلامات المرغوبة
  4. نمط Logback العادي

تبسيط برنامجنا بشكل كبير:

Java

 

var who = //...
var logger = LoggerFactory.getLogger(Main.class.toString());
logger.info("Hello from {}!", who);

يعرض Grafana ما يلي:

تسجيل Docker

I’m running most of my demos on Docker Compose, so I’ll mention the Docker logging trick. When a container writes on the standard out, Docker saves it to a local file. The docker logs  command can access the file content.

ومع ذلك، تتوفر خيارات أخرى غير الحفظ في ملف محلي، على سبيل المثال، syslog، Google Cloud، Splunk، إلخ. لاختيار خيار مختلف، يتم تعيين برنامج تسجيل. يمكن تكوين السائق في المستوى العام لـ Docker أو لكل حاوية.

يقدم Loki برنامجًا مساعدًا الخاص به. لتثبيته:

Shell

 

docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions

في هذه المرحلة ، يمكن استخدامه في تطبيق حاويتنا:

YAML

 

services:
  app:
    build: .
    logging:
      driver: loki                                                    #1
      options:
        loki-url: http://localhost:3100/loki/api/v1/push              #2
        loki-external-labels: container_name={{.Name}},app=demo       #3

  1. سائق تسجيل Loki
  2. عنوان URL للدفع إليه
  3. علامات إضافية

النتيجة هي التالية. لاحظ العلامات الافتراضية.

الخاتمة

من وجهة نظر عن كثب، Loki ليس خارقًا: إنه محفظة تخزين بسيطة بوجود واجهة RESTful فوقها.

تتوفر عدة طرق لاستخدام الواجهة. أبعد من الطريقة السهلة، رأينا مُشرّف إطار سجلات Java و Docker. تشمل الطرق الأخرى قص الملفات السجل، على سبيل المثال، Promtail، عبر جناح Kubernetes. يمكنك أيضًا إضافة جهاز OpenTelemetry Collector بين تطبيقك و Loki لإجراء تحولات.

الخيارات غير محدودة على الإطلاق. احرص على اختيار الخيار الذي يناسب سياقك أفضل.

للمضي قدمًا:

Source:
https://dzone.com/articles/send-your-logs-to-loki