---
title: "التسجيل المنظّم بتنسيق سجل ومستويات سجل يمكنك البحث فيها حسب الحقل"
description: "يضع التسجيل المنظّم كل قيمة في حقلها الخاص بدل أن تكون داخل جملة. أرسلنا طلبًا واحدًا بـ3 طرق إلى LogBrew ونعرض أيها يمكنك العثور عليه مجددًا عبر order_id."
canonical: "https://logbrew.co/ar/guides/structured-logging"
last_updated: "2026-11-10"
---

# التسجيل المنظّم بتنسيق سجل ومستويات سجل يمكنك البحث فيها حسب الحقل

يضع التسجيل المنظّم كل قيمة في حقلها الخاص بدل أن تكون داخل جملة. أرسلنا طلبًا واحدًا بـ3 طرق إلى LogBrew ونعرض أيها يمكنك العثور عليه مجددًا عبر `order_id`.

اختُبر في 10 نوفمبر 2026 باستخدام Python 3.13.5 وlogbrew-sdk 0.1.29

بقلم [Furkan Erday](https://github.com/furkanerday). Furkan Erday مؤسس LogBrew، ويبني LogBrew كل يوم باستخدام Claude Code وCodex.

Canonical: https://logbrew.co/ar/guides/structured-logging

## أي سطر سجل يمكنك العثور عليه مجددًا عبر `order_id`

| أين وُضع المعرّف | بحث نصي | فلتر الحقل |
| --- | --- | --- |
| داخل نص الرسالة | وُجد | لم يوجد |
| في حقل `extra={}` | لم يوجد | وُجد |
| في dict داخل `extra={}` | لم يوجد | لم يوجد |

أرسلنا طلبًا واحدًا بـ3 طرق عبر معالج Python ثم بحثنا عنه. الكوب الممتلئ يعني أن البحث عثر على السطر.

## اختر تنسيق سجل يمكنك فلترته

### ثبّت الرسالة وضع القيم في حقول

يحتفظ الحقل باسمه ونوعه. أما القيمة داخل نص الرسالة فمجرد جزء من نص. في Python، مرّر الحقول عبر `extra={}`، وتبقى الرسالة نفسها لكل طلب.

```bash
log.info("order paid", extra={"order_id": "ord_42", "amount_cents": 1999})
```

### اكتب أسطر JSON عندما يقرأ السجلات برنامج

تحوّل فئة فرعية من `Formatter` كل record إلى كائن JSON واحد في كل سطر، مع الحقول القياسية وكل قيمة من `extra`. يحلّل جامع السجلات السطر دون تعابير نمطية.

```bash
import json
import logging

RECORD_FIELDS = set(vars(logging.LogRecord("", 0, "", 0, "", (), None))) | {"message", "asctime"}


class JsonFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        line = {
            "time": self.formatTime(record, "%Y-%m-%dT%H:%M:%S%z"),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
        }
        line.update({k: v for k, v in vars(record).items() if k not in RECORD_FIELDS})
        if record.exc_info:
            line["exception"] = self.formatException(record.exc_info)
        return json.dumps(line)


handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logging.basicConfig(level=logging.INFO, handlers=[handler])
log = logging.getLogger("taproom")

log.info("order paid", extra={"order_id": "ord_42", "amount_cents": 1999})
log.warning("keg below 10%", extra={"keg": "ipa", "percent": 8})
```

المخرجات

```text
{"time": "2026-11-10T08:34:04+0300", "level": "INFO", "logger": "taproom", "message": "order paid", "order_id": "ord_42", "amount_cents": 1999}
{"time": "2026-11-10T08:34:04+0300", "level": "WARNING", "logger": "taproom", "message": "keg below 10%", "keg": "ipa", "percent": 8}
```

### أرسل الحقول إلى LogBrew

يرسل `LogBrewLoggingHandler` كل record مع قيم `extra` الخاصة به كحقول. أرسلنا الطلب نفسه بـ3 طرق، داخل نص الرسالة، وكحقل `extra`، وكـdict داخل `extra`.

```bash
import logging
import os

from logbrew_sdk import HttpTransport, LogBrewClient, LogBrewLoggingHandler

client = LogBrewClient.create(api_key=os.environ["LOGBREW_SERVER_API_KEY"])
transport = HttpTransport()
log = logging.getLogger("taproom")
log.addHandler(LogBrewLoggingHandler(client, transport, metadata={"service": "taproom"}))
log.setLevel(logging.INFO)

logging.getLogger("taproom.text").info("order %s paid, %d cents", "ord_42", 1999)
logging.getLogger("taproom.fields").info(
    "order paid", extra={"order_id": "ord_42", "amount_cents": 1999}
)
logging.getLogger("taproom.nested").info(
    "order paid", extra={"order": {"id": "ord_42", "amount_cents": 1999}}
)

response = client.flush(transport)
print("HTTP", response.status_code, "accepted", response.accepted_events, "logs")
```

المخرجات

```text
HTTP 200 accepted 3 logs
```

### اعثر على السطر مجددًا عبر الحقل

يقبل `GET /api/logs` معامل `attribute_filters` مع مسار الحقل ونوعه والمعامل والقيمة. لا يعثر الفلتر على `order_id` إلا في السطر المرسل عبر `extra`. أما dict داخل `extra` فيُسقط، لأن المعالج لا يحتفظ إلا بالنصوص والأرقام والقيم المنطقية و`None`.

```bash
curl -sS -G "https://api.logbrew.co/api/logs" \
  -H "Authorization: Bearer $LOGBREW_TOKEN" \
  --data-urlencode "project_id=$LOGBREW_PROJECT" \
  --data-urlencode "since=2026-11-10T05:31:33Z" \
  --data-urlencode 'attribute_filters=[{"path":"/metadata/order_id","type":"string","operator":"eq","value":"ord_42"}]' \
  | jq -c '.[] | {message, order_id: .attributes.metadata.order_id}'
```

المخرجات

```text
{"message":"order paid","order_id":"ord_42"}
```

## استخدم مستويات السجل بالطريقة نفسها في كل خدمة

### استخدم 4 مستويات في الإنتاج

`info` لما حدث، و`warning` لما ساء لكن جرى التعامل معه، و`error` للطلبات أو المهام التي فشلت، و`critical` لما أوقف الخدمة. احتفظ بـ`debug` للتشغيل المحلي. يرسل معالج LogBrew records مستوى DEBUG كمستوى `info`.

### اضبط المستوى مرة واحدة عند بدء التشغيل

اقرأ المستوى من متغير بيئة، فيمكن لمضيف واحد أن يعمل بمستوى DEBUG بينما يبقى الباقي على INFO. في Python، يتولى `logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO"))` ذلك.

### سجّل الأرقام كأرقام

يحتفظ `amount_cents: 1999` بنوعه، لذا يعثر عليه الفلتر `amount_cents >= 1000`. أرسلنا أيضًا `amount_cents: "1999"` كنص، فتخطّى فلتر الأرقام ذلك السطر.

```bash
curl -sS -G "https://api.logbrew.co/api/logs" \
  -H "Authorization: Bearer $LOGBREW_TOKEN" \
  --data-urlencode "project_id=$LOGBREW_PROJECT" \
  --data-urlencode "since=2026-11-10T05:31:33Z" \
  --data-urlencode 'attribute_filters=[{"path":"/metadata/amount_cents","type":"number","operator":"gte","value":1000}]' \
  | jq -c '.[] | {message, amount_cents: .attributes.metadata.amount_cents}'
```

المخرجات

```text
{"message":"order paid","amount_cents":1999}
```

## ما رأيناه في الاختبار

أرسلنا طلبًا واحدًا بـ3 طرق عبر معالج Python، ثم بحثنا عنه عبر واجهة API الخاصة بـLogBrew. عثر البحث النصي `search=ord_42` على سطر واحد من 3، وهو السطر الذي يحمل المعرّف داخل الرسالة.

عثر الفلتر `order_id = ord_42` على سطر واحد، وهو المرسل عبر `extra={"order_id": "ord_42"}`. وعثر الفلتر `amount_cents >= 1000` على السطر نفسه، لأن المعالج حفظ 1999 كرقم.

لم يطابق السطر المرسل عبر `extra={"order": {...}}` أي شيء. يرسل المعالج النصوص والأرقام والقيم المنطقية و`None` من `extra` ويتخطى dict أو list.

```bash
curl -sS -G "https://api.logbrew.co/api/logs" \
  -H "Authorization: Bearer $LOGBREW_TOKEN" \
  --data-urlencode "project_id=$LOGBREW_PROJECT" \
  --data-urlencode "since=2026-11-10T05:31:33Z" \
  --data-urlencode "search=ord_42" \
  | jq -c '.[] | {message, order_id: .attributes.metadata.order_id}'
```

المخرجات

```text
{"message":"order ord_42 paid, 1999 cents","order_id":null}
```

## تنسيق السجل بالنص العادي مقابل JSON

يُسقط تنسيق `%(message)s` العادي كل قيمة من `extra`. ينتهي السطر أدناه بـ`INFO order paid` دون معرّف الطلب، فلا يستطيع أحد العثور على ذلك الطلب لاحقًا.

تحتفظ أسطر JSON بالحقول مع أنواعها، ويحلّلها جامع السجلات دون تعابير نمطية. يصعب على البشر قراءتها، لذا أبقِ الرسائل قصيرة وأسماء الحقول ثابتة.

لا يملك Python منسّق JSON مدمجًا. طلب python/cpython#77691 ذلك في 2018 وأُغلق، لأن التوثيق يوضح بالفعل كيف تكتبه. الـ`Formatter` المكوّن من 15 سطرًا أعلاه هو تلك الوصفة.

```bash
import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("taproom")

log.info("order paid", extra={"order_id": "ord_42", "amount_cents": 1999})
```

المخرجات

```text
2026-11-10 08:34:04,955 INFO order paid
```

## ما يسجّله LogBrew

يحتفظ كل سجل بمستواه واسم مسجّله وكل حقل أولي من `extra`، إضافة إلى حقول `metadata={}` في المعالج. تفلتر API حسب مسار الحقل ونوعه والمعامل. تقبل النصوص `eq` و`ne` و`contains` و`starts_with`، وتقبل الأرقام `gt` و`gte` و`lt` و`lte`.

لا يفهرس LogBrew الحقول التي يحتوي اسمها على `password` أو `secret` أو `token` أو `cookie` أو `authorization`، فلا يعثر عليها الفلتر أبدًا. ومع ذلك، أبقِ الأسرار بعيدة عن أسطر السجل.

يحمل السجل المرسل داخل تتبّع معرّف التتبّع ومعرّف Span، لذا يقودك الفلتر عبر `order_id` إلى الطلب الذي أنتج السطر.

## أسئلة شائعة

### ما التسجيل المنظّم؟

تسجيل تكون فيه كل قيمة حقلًا مسمّى له نوع، كـJSON أو كحقل في record السجل، لا كلمات داخل جملة. عندها تفلتر أدوات البحث حسب الحقل لا حسب جزء من النص.

### ما ترتيب مستويات السجل؟

DEBUG وINFO وWARNING وERROR وCRITICAL، بأرقام من 10 إلى 50 في Python. يمرّر المسجّل المضبوط على INFO مستوى INFO وما فوقه ويُسقط DEBUG.

### هل أسجّل بـJSON أم بالنص العادي؟

JSON عندما يقرأ الأسطر برنامج، مثل جامع أو ملف أو أداة بحث. النص العادي عندما لا يقرأ الطرفية سوى البشر. احتفظ بالحقول نفسها في الحالتين.

### كيف أضيف حقولًا إلى سطر سجل في Python؟

مرّر `extra={"order_id": "ord_42"}` إلى استدعاء السجل. يتجاهل المنسّق القياسي حقول extra. يحتفظ بها منسّق JSON أو معالج LogBrew.

### هل يمكنني البحث في السجلات حسب الحقل في LogBrew؟

نعم. يقبل `GET /api/logs` حتى 4 من `attribute_filters`، لكل منها مسار حقل ونوع ومعامل مثل `eq` أو `gte`.

## فلتر سجلاتك حسب أي حقل

يحتفظ LogBrew بكل حقل تسجّله ويعثر على السطر مجددًا عبر الحقل، إلى جانب التتبّع والمشكلة اللذين ينتمي إليهما. ابدأ بخطة مجانية سخية دون بطاقة.

[ابدأ مجانًا](https://logbrew.co/ar)

## المصادر

- [Python docs, Logging Cookbook](https://docs.python.org/3/howto/logging-cookbook.html)
- [Python docs, LogRecord attributes](https://docs.python.org/3/library/logging.html#logrecord-attributes)
- [python/cpython#77691](https://github.com/python/cpython/issues/77691)

## أدلة أخرى للوكلاء وPython

- [أضف خادم MCP إلى Claude Code وCodex](https://logbrew.co/ar/guides/mcp-server-claude-code-codex)
- [استخدم AGENTS.md في Claude Code وCodex](https://logbrew.co/ar/guides/agents-md)
- [أصلح خطأ Claude overloaded 529 وخطأ rate limit 429](https://logbrew.co/ar/guides/claude-overloaded-rate-limit-error)
- [إعداد التسجيل في Python بمستويات السجل الصحيحة والتقاط الاستثناءات](https://logbrew.co/ar/guides/python-logging)
- [كيف تصلح الأخطاء في Claude Code وCodex وJavaScript وPython](https://logbrew.co/ar/guides)
