تطبيقك يعمل، لكن هل أنت متأكد أنك تعرف ما بداخله؟
\n\nتخيل: لقد قمت بتشغيل خدمة مصغرة بلغة Go في بيئة الإنتاج. إنها لا تتعطل، ولا تنتج أخطاء 500، لكن المستخدمين يشكون من أن الصفحة تتحمّل "ببطء نوعًا ما". تذهب إلى المراقبة — استخدام وحدة المعالجة المركزية 30%، الذاكرة طبيعية. ماذا تفعل؟ بدون المراقبة في Go أنت كقبطان سفينة بدون خريطة وبوصلة.
\n\nالمراقبة (Observability) ليست مجرد "سجلات". إنها ثلاث ركائز: التتبع، المقاييس والسجلات. معًا يمنحونك قوة خارقة: فهم ما يحدث في النظام، حتى لو لم تكن تتوقع مشكلة. في هذه المقالة سنشرح كل جانب من هذه الجوانب مع أمثلة على الكود بلغة Go. لن تقرأ النظرية فقط — بل ستحصل على وصفات جاهزة يمكنك تطبيقها اليوم.
\n\nلماذا Go هي اللغة المثالية للمراقبة؟
\n\nتم إنشاء Go لأنظمة الخلفية حيث الأداء والموثوقية مهمان. المكتبة القياسية (net/http، database/sql) قادرة بالفعل على تقديم المقاييس ودعم السياقات للتتبع. والنظام البيئي OpenTelemetry Go و Prometheus Go يجعل تطبيق المراقبة بسيطًا وأنيقًا.
لكن الشيء الرئيسي — في Go لا يوجد "سحر". أنت تكتب كودًا يمكن قياسه وتتبعه وتسجيله دون ألم. دعونا نرى كيف يتم ذلك.
\n\nالتتبع في Go: OpenTelemetry في العمل
\n\nالتتبع (Tracing) هو قصة طلب واحد من دخوله إلى النظام حتى خروجه. يظهر كم من الوقت استغرق كل خطوة: طلب إلى قاعدة البيانات، استدعاء API خارجي، معالجة البيانات. في Go، يُبنى التتبع على السياقات (context.Context).
إعداد OpenTelemetry SDK
\n\nلنقم بتثبيت الحزم:
\ngo get go.opentelemetry.io/otel \\\n go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp \\\n go.opentelemetry.io/otel/sdk/trace\n\nالآن لنقم بتهيئة المتتبع:
\n\npackage main\n\nimport (\n "context"\n "log"\n "go.opentelemetry.io/otel"\n "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"\n "go.opentelemetry.io/otel/sdk/trace"\n)\n\nfunc initTracer() {\n exporter, err := otlptracehttp.New(context.Background())\n if err != nil {\n log.Fatal(err)\n }\n\n tp := trace.NewTracerProvider(\n trace.WithBatcher(exporter),\n trace.WithSampler(trace.AlwaysSample()),\n )\n otel.SetTracerProvider(tp)\n}\n\nإنشاء سبان (span) في معالج HTTP
\n\nهذا ما يبدو عليه التتبع في الكود الحقيقي:
\n\nfunc userHandler(w http.ResponseWriter, r *http.Request) {\n tracer := otel.Tracer("user-service")\n ctx, span := tracer.Start(r.Context(), "getUser")\n defer span.End()\n\n // محاكاة طلب إلى قاعدة البيانات\n user, err := getUserFromDB(ctx, r.URL.Query().Get("id"))\n if err != nil {\n span.RecordError(err)\n span.SetStatus(codes.Error, err.Error())\n http.Error(w, err.Error(), 500)\n return\n }\n\n span.SetAttributes(\n attribute.String("user.id", user.ID),\n attribute.Int64("user.age", int64(user.Age)),\n )\n\n // سبان متداخل لاستدعاء خارجي\n _, childSpan := tracer.Start(ctx, "callAnalyticsAPI")\n defer childSpan.End()\n\n callAnalyticsAPI(user)\n \n w.Write([]byte("OK"))\n}\n\nكل سبان يحتوي على اسم، وقت البدء والانتهاء، سمات وحالة. في Jaeger أو Grafana Tempo سترى خريطة كاملة للطلب.
\n\nنصيحة عملية
\nلا تنسَ تمرير السياق عبر التطبيق بأكمله. في Go يتم ذلك تلقائيًا إذا كنت تستخدم context.Context في تواقيع الدوال. OpenTelemetry Go سيلتقط السياق تلقائيًا من طلب HTTP إذا كنت تستخدم middleware.
المقاييس: Prometheus والتصدير في Go
\n\nالمقاييس هي أرقام تخبر عن حالة النظام: RPS، زمن الاستجابة، عدد الأخطاء، استخدا