مقدمة
في عالم التطبيقات الحديثة، لا يكفي أن يعمل تطبيقك — بل يجب أن تعرف كيف يعمل. هل الاستجابات بطيئة؟ أين يقضي الخادم معظم وقته؟ ما هي نقاط الاختناق؟ هنا يأتي دور OpenTelemetry (OTel) — المعيار المفتوح للمراقبة والتتبع الموزع.
في هذا الدليل الشامل، ستتعلم كيفية:
- إعداد OpenTelemetry مع Next.js 15 من الصفر
- تتبع طلبات API والصفحات تلقائياً
- إضافة spans مخصصة لقياس أداء العمليات الحرجة
- إرسال البيانات إلى Jaeger للتتبع وPrometheus للمقاييس
- عرض كل شيء في لوحة Grafana متكاملة
- نشر الإعداد في بيئة الإنتاج مع Docker Compose
المتطلبات المسبقة
قبل البدء، تأكد من توفر التالي:
- Node.js 20+ مُثبت على جهازك
- Docker و Docker Compose للخدمات المحلية
- معرفة أساسية بـ Next.js و TypeScript
- معرفة بمفاهيم HTTP وREST APIs
- محرر أكواد (VS Code مُوصى به)
ماذا ستبني؟
ستبني تطبيق Next.js 15 مع نظام مراقبة متكامل يشمل:
- تتبع تلقائي لجميع طلبات HTTP والصفحات
- Spans مخصصة لعمليات قاعدة البيانات والخدمات الخارجية
- مقاييس أداء مثل زمن الاستجابة ومعدل الأخطاء
- لوحة مراقبة متكاملة في Grafana
- تنبيهات عند تجاوز حدود الأداء
الخطوة 1: إنشاء مشروع Next.js 15
ابدأ بإنشاء مشروع Next.js جديد:
npx create-next-app@latest otel-nextjs-demo --typescript --tailwind --app --src-dir
cd otel-nextjs-demoاختر الإعدادات الافتراضية عند السؤال. بعد انتهاء التثبيت، تأكد من عمل المشروع:
npm run devالخطوة 2: تثبيت حزم OpenTelemetry
قم بتثبيت الحزم المطلوبة لـ OpenTelemetry:
npm install @opentelemetry/api \
@opentelemetry/sdk-node \
@opentelemetry/sdk-trace-node \
@opentelemetry/sdk-metrics \
@opentelemetry/resources \
@opentelemetry/semantic-conventions \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/exporter-metrics-otlp-http \
@opentelemetry/instrumentation-http \
@opentelemetry/instrumentation-fetch \
@vercel/otelحزمة @vercel/otel توفر تكاملاً مُحسَّناً مع Next.js وتُسهّل الإعداد الأولي بشكل كبير. يمكنك استخدامها مع أي مزود OpenTelemetry وليس فقط Vercel.
الخطوة 3: إعداد OpenTelemetry في Next.js
تفعيل الميزة التجريبية
أولاً، فعّل دعم OpenTelemetry في next.config.ts:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
instrumentationHook: true,
},
};
export default nextConfig;إنشاء ملف الأداة (Instrumentation)
أنشئ ملف src/instrumentation.ts — هذا هو نقطة الدخول لـ OpenTelemetry:
// src/instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
const { NodeSDK } = await import("@opentelemetry/sdk-node");
const { OTLPTraceExporter } = await import(
"@opentelemetry/exporter-trace-otlp-http"
);
const { OTLPMetricExporter } = await import(
"@opentelemetry/exporter-metrics-otlp-http"
);
const { PeriodicExportingMetricReader } = await import(
"@opentelemetry/sdk-metrics"
);
const { Resource } = await import("@opentelemetry/resources");
const {
ATTR_SERVICE_NAME,
ATTR_SERVICE_VERSION,
ATTR_DEPLOYMENT_ENVIRONMENT_NAME,
} = await import("@opentelemetry/semantic-conventions");
const { HttpInstrumentation } = await import(
"@opentelemetry/instrumentation-http"
);
const resource = new Resource({
[ATTR_SERVICE_NAME]: "nextjs-otel-demo",
[ATTR_SERVICE_VERSION]: "1.0.0",
[ATTR_DEPLOYMENT_ENVIRONMENT_NAME]:
process.env.NODE_ENV || "development",
});
const traceExporter = new OTLPTraceExporter({
url:
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||
"http://localhost:4318/v1/traces",
});
const metricExporter = new OTLPMetricExporter({
url:
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||
"http://localhost:4318/v1/metrics",
});
const sdk = new NodeSDK({
resource,
traceExporter,
metricReader: new PeriodicExportingMetricReader({
exporter: metricExporter,
exportIntervalMillis: 10000,
}),
instrumentations: [new HttpInstrumentation()],
});
sdk.start();
process.on("SIGTERM", () => {
sdk.shutdown().then(
() => console.log("OpenTelemetry SDK shut down successfully"),
(err) => console.error("Error shutting down OpenTelemetry SDK", err)
);
});
}
}الشرط process.env.NEXT_RUNTIME === "nodejs" ضروري لأن OpenTelemetry SDK يعمل فقط في بيئة Node.js ولا يدعم Edge Runtime. بدون هذا الشرط، سيفشل التطبيق عند استخدام Middleware أو Edge Functions.
الخطوة 4: إعداد خدمات المراقبة بـ Docker Compose
أنشئ ملف docker-compose.yml في جذر المشروع لتشغيل Jaeger وPrometheus وGrafana وOpenTelemetry Collector:
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yml"]
volumes:
- ./otel-collector-config.yml:/etc/otel-collector-config.yml
ports:
- "4317:4317" # gRPC
- "4318:4318" # HTTP
- "8889:8889" # Prometheus metrics
depends_on:
- jaeger
jaeger:
image: jaegertracing/all-in-one:latest
environment:
- COLLECTOR_OTLP_ENABLED=true
ports:
- "16686:16686" # Jaeger UI
- "14268:14268" # Jaeger collector
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana-data:/var/lib/grafana
ports:
- "3001:3000"
depends_on:
- prometheus
- jaeger
volumes:
grafana-data:إعداد OpenTelemetry Collector
أنشئ ملف otel-collector-config.yml:
# otel-collector-config.yml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: "0.0.0.0:8889"
namespace: "nextjs"
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]إعداد Prometheus
أنشئ ملف prometheus.yml:
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "otel-collector"
static_configs:
- targets: ["otel-collector:8889"]
- job_name: "nextjs-app"
static_configs:
- targets: ["host.docker.internal:3000"]شغّل جميع الخدمات:
docker compose up -dالخطوة 5: إضافة Spans مخصصة
إنشاء مساعد التتبع
أنشئ ملف src/lib/tracing.ts لتوفير أدوات تتبع سهلة الاستخدام:
// src/lib/tracing.ts
import { trace, SpanStatusCode, Span, context } from "@opentelemetry/api";
const tracer = trace.getTracer("nextjs-app", "1.0.0");
export function withSpan<T>(
name: string,
fn: (span: Span) => Promise<T>,
attributes?: Record<string, string | number | boolean>
): Promise<T> {
return tracer.startActiveSpan(name, async (span) => {
try {
if (attributes) {
Object.entries(attributes).forEach(([key, value]) => {
span.setAttribute(key, value);
});
}
const result = await fn(span);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error instanceof Error ? error.message : "Unknown error",
});
span.recordException(error as Error);
throw error;
} finally {
span.end();
}
});
}
export function createDbSpan<T>(
operation: string,
table: string,
fn: (span: Span) => Promise<T>
): Promise<T> {
return withSpan(`db.${operation}`, fn, {
"db.system": "postgresql",
"db.operation": operation,
"db.sql.table": table,
});
}
export function createExternalApiSpan<T>(
service: string,
endpoint: string,
fn: (span: Span) => Promise<T>
): Promise<T> {
return withSpan(`external.${service}`, fn, {
"http.url": endpoint,
"peer.service": service,
});
}
export { tracer };استخدام Spans في API Routes
أنشئ مسار API يستخدم التتبع المخصص:
// src/app/api/users/route.ts
import { NextResponse } from "next/server";
import { withSpan, createDbSpan } from "@/lib/tracing";
// محاكاة قاعدة بيانات
async function fetchUsersFromDb() {
return createDbSpan("SELECT", "users", async (span) => {
// محاكاة تأخير قاعدة البيانات
await new Promise((resolve) => setTimeout(resolve, 50));
span.setAttribute("db.rows_affected", 10);
return [
{ id: 1, name: "أحمد", email: "ahmed@example.com" },
{ id: 2, name: "سارة", email: "sara@example.com" },
{ id: 3, name: "محمد", email: "mohamed@example.com" },
];
});
}
async function enrichUserData(users: any[]) {
return withSpan(
"enrichUserData",
async (span) => {
span.setAttribute("users.count", users.length);
// محاكاة إثراء البيانات من خدمة خارجية
await new Promise((resolve) => setTimeout(resolve, 30));
return users.map((user) => ({
...user,
avatar: `https://api.dicebear.com/7.x/initials/svg?seed=${user.name}`,
}));
},
{ "enrichment.source": "dicebear" }
);
}
export async function GET() {
return withSpan("GET /api/users", async (span) => {
span.setAttribute("http.method", "GET");
span.setAttribute("http.route", "/api/users");
const users = await fetchUsersFromDb();
const enrichedUsers = await enrichUserData(users);
span.setAttribute("response.count", enrichedUsers.length);
return NextResponse.json({
users: enrichedUsers,
total: enrichedUsers.length,
});
});
}الخطوة 6: إضافة مقاييس مخصصة
أنشئ ملف src/lib/metrics.ts لتعريف المقاييس:
// src/lib/metrics.ts
import { metrics } from "@opentelemetry/api";
const meter = metrics.getMeter("nextjs-app", "1.0.0");
// عداد الطلبات
export const requestCounter = meter.createCounter("http_requests_total", {
description: "إجمالي عدد طلبات HTTP",
unit: "requests",
});
// مقياس زمن الاستجابة
export const responseTimeHistogram = meter.createHistogram(
"http_response_duration_ms",
{
description: "مدة استجابة HTTP بالميلي ثانية",
unit: "ms",
}
);
// عداد الأخطاء
export const errorCounter = meter.createCounter("http_errors_total", {
description: "إجمالي عدد أخطاء HTTP",
unit: "errors",
});
// مقياس الاتصالات النشطة
export const activeConnections = meter.createUpDownCounter(
"active_connections",
{
description: "عدد الاتصالات النشطة حالياً",
unit: "connections",
}
);دمج المقاييس في Middleware
أنشئ middleware لتسجيل المقاييس تلقائياً:
// src/middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const start = Date.now();
const response = NextResponse.next();
// إضافة رؤوس التتبع
response.headers.set("X-Request-Start", start.toString());
response.headers.set(
"X-Request-Id",
crypto.randomUUID()
);
return response;
}
export const config = {
matcher: ["/api/:path*", "/((?!_next/static|_next/image|favicon.ico).*)"],
};إنشاء API Route مع مقاييس كاملة
// src/app/api/health/route.ts
import { NextResponse } from "next/server";
import {
requestCounter,
responseTimeHistogram,
errorCounter,
} from "@/lib/metrics";
import { withSpan } from "@/lib/tracing";
export async function GET() {
const start = Date.now();
return withSpan("GET /api/health", async (span) => {
try {
// تسجيل الطلب
requestCounter.add(1, {
method: "GET",
route: "/api/health",
});
// محاكاة فحص صحة الخدمات
const checks = {
database: "healthy",
cache: "healthy",
external_api: "healthy",
uptime: process.uptime(),
timestamp: new Date().toISOString(),
};
// تسجيل زمن الاستجابة
const duration = Date.now() - start;
responseTimeHistogram.record(duration, {
method: "GET",
route: "/api/health",
status: "200",
});
span.setAttribute("health.status", "healthy");
span.setAttribute("health.duration_ms", duration);
return NextResponse.json(checks);
} catch (error) {
errorCounter.add(1, {
method: "GET",
route: "/api/health",
error_type: "health_check_failed",
});
throw error;
}
});
}الخطوة 7: إنشاء صفحة لوحة المراقبة
أنشئ صفحة بسيطة لعرض حالة المراقبة:
// src/app/dashboard/page.tsx
"use client";
import { useEffect, useState } from "react";
interface HealthStatus {
database: string;
cache: string;
external_api: string;
uptime: number;
timestamp: string;
}
export default function DashboardPage() {
const [health, setHealth] = useState<HealthStatus | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchHealth = async () => {
try {
const res = await fetch("/api/health");
const data = await res.json();
setHealth(data);
} catch (err) {
console.error("Failed to fetch health:", err);
} finally {
setLoading(false);
}
};
fetchHealth();
const interval = setInterval(fetchHealth, 5000);
return () => clearInterval(interval);
}, []);
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500" />
</div>
);
}
return (
<div className="max-w-4xl mx-auto p-8">
<h1 className="text-3xl font-bold mb-8">
لوحة المراقبة
</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
{health && (
<>
<StatusCard
title="قاعدة البيانات"
status={health.database}
/>
<StatusCard
title="التخزين المؤقت"
status={health.cache}
/>
<StatusCard
title="API الخارجي"
status={health.external_api}
/>
</>
)}
</div>
<div className="bg-gray-900 rounded-lg p-6">
<h2 className="text-xl font-semibold mb-4">
روابط سريعة
</h2>
<div className="space-y-3">
<ExternalLink
href="http://localhost:16686"
label="Jaeger UI — عرض التتبعات"
/>
<ExternalLink
href="http://localhost:9090"
label="Prometheus — استعلام المقاييس"
/>
<ExternalLink
href="http://localhost:3001"
label="Grafana — لوحات المراقبة"
/>
</div>
</div>
</div>
);
}
function StatusCard({
title,
status,
}: {
title: string;
status: string;
}) {
const isHealthy = status === "healthy";
return (
<div className="bg-gray-800 rounded-lg p-6">
<h3 className="text-sm font-medium text-gray-400 mb-2">
{title}
</h3>
<div className="flex items-center gap-2">
<div
className={`w-3 h-3 rounded-full ${
isHealthy ? "bg-green-500" : "bg-red-500"
}`}
/>
<span
className={
isHealthy ? "text-green-400" : "text-red-400"
}
>
{isHealthy ? "سليم" : "معطل"}
</span>
</div>
</div>
);
}
function ExternalLink({
href,
label,
}: {
href: string;
label: string;
}) {
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 text-blue-400 hover:text-blue-300 transition-colors"
>
<span>←</span>
{label}
</a>
);
}الخطوة 8: إعداد Grafana Dashboard
بعد تشغيل الخدمات بـ docker compose up -d، افتح Grafana على http://localhost:3001 وسجّل الدخول بـ admin / admin123.
إضافة مصادر البيانات
- اذهب إلى Configuration ثم Data Sources
- أضف Prometheus بعنوان:
http://prometheus:9090 - أضف Jaeger بعنوان:
http://jaeger:16686
إنشاء لوحة مراقبة مخصصة
أنشئ ملف grafana-dashboard.json لاستيراده في Grafana:
{
"dashboard": {
"title": "Next.js Application Monitoring",
"panels": [
{
"title": "Request Rate",
"type": "timeseries",
"targets": [
{
"expr": "rate(nextjs_http_requests_total[5m])",
"legendFormat": "Requests/sec"
}
]
},
{
"title": "Response Time (p95)",
"type": "gauge",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(nextjs_http_response_duration_ms_bucket[5m]))",
"legendFormat": "p95 Latency"
}
]
},
{
"title": "Error Rate",
"type": "stat",
"targets": [
{
"expr": "rate(nextjs_http_errors_total[5m])",
"legendFormat": "Errors/sec"
}
]
}
]
}
}يمكنك استيراد هذا الملف من خلال Grafana UI عبر القائمة Dashboards ثم Import ثم لصق محتوى JSON. ستحصل على لوحة مراقبة جاهزة مع ثلاث لوحات فرعية.
الخطوة 9: التتبع المتقدم — ربط Frontend بـ Backend
لربط تتبعات المتصفح بتتبعات الخادم، أنشئ مكون client-side:
// src/lib/client-tracing.ts
export function createTracedFetch(
originalFetch: typeof fetch
): typeof fetch {
return async (input, init) => {
const requestId = crypto.randomUUID();
const start = performance.now();
const headers = new Headers(init?.headers);
headers.set("X-Request-Id", requestId);
headers.set("X-Client-Start", start.toString());
try {
const response = await originalFetch(input, {
...init,
headers,
});
const duration = performance.now() - start;
console.debug(
`[Trace] ${
typeof input === "string" ? input : input.url
} - ${duration.toFixed(1)}ms`
);
return response;
} catch (error) {
const duration = performance.now() - start;
console.error(
`[Trace Error] ${
typeof input === "string" ? input : input.url
} - ${duration.toFixed(1)}ms`,
error
);
throw error;
}
};
}استخدام الـ Fetch المُتتبع في المكونات
// src/hooks/useTracedFetch.ts
"use client";
import { useCallback } from "react";
import { createTracedFetch } from "@/lib/client-tracing";
const tracedFetch = createTracedFetch(fetch);
export function useTracedFetch() {
const fetchWithTrace = useCallback(
async <T>(url: string, options?: RequestInit): Promise<T> => {
const response = await tracedFetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
},
[]
);
return { fetch: fetchWithTrace };
}الخطوة 10: إعداد التنبيهات
تنبيهات Prometheus
أنشئ ملف alert-rules.yml:
# alert-rules.yml
groups:
- name: nextjs-alerts
rules:
- alert: HighErrorRate
expr: rate(nextjs_http_errors_total[5m]) > 0.1
for: 2m
labels:
severity: critical
annotations:
summary: "معدل أخطاء مرتفع في تطبيق Next.js"
description: "معدل الأخطاء تجاوز 10% خلال آخر 5 دقائق"
- alert: SlowResponseTime
expr: histogram_quantile(0.95, rate(nextjs_http_response_duration_ms_bucket[5m])) > 2000
for: 5m
labels:
severity: warning
annotations:
summary: "زمن استجابة بطيء"
description: "زمن الاستجابة p95 تجاوز 2000 ميلي ثانية"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes > 512 * 1024 * 1024
for: 10m
labels:
severity: warning
annotations:
summary: "استخدام ذاكرة مرتفع"
description: "استخدام الذاكرة تجاوز 512 ميغابايت"حدّث ملف prometheus.yml لتضمين قواعد التنبيهات:
# prometheus.yml (محدّث)
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert-rules.yml"
scrape_configs:
- job_name: "otel-collector"
static_configs:
- targets: ["otel-collector:8889"]الخطوة 11: إعداد متغيرات البيئة
أنشئ ملف .env.local للتطوير المحلي:
# .env.local
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_SERVICE_NAME=nextjs-otel-demo
OTEL_LOG_LEVEL=info
NODE_ENV=developmentولبيئة الإنتاج، أنشئ .env.production:
# .env.production
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.yourdomain.com
OTEL_SERVICE_NAME=nextjs-otel-production
OTEL_LOG_LEVEL=warn
NODE_ENV=productionالخطوة 12: اختبار النظام بالكامل
تشغيل الخدمات
# تشغيل خدمات المراقبة
docker compose up -d
# تشغيل تطبيق Next.js
npm run devتوليد بيانات تتبع
استخدم curl أو المتصفح لإرسال طلبات:
# طلب واحد
curl http://localhost:3000/api/users
# طلب صحة النظام
curl http://localhost:3000/api/health
# توليد حمل خفيف (10 طلبات)
for i in $(seq 1 10); do
curl -s http://localhost:3000/api/users > /dev/null
echo "Request $i sent"
doneالتحقق من النتائج
-
Jaeger — افتح
http://localhost:16686- اختر الخدمة
nextjs-otel-demo - سترى تتبعات لكل طلب مع التوقيت التفصيلي
- انقر على أي تتبع لرؤية شجرة الـ Spans
- اختر الخدمة
-
Prometheus — افتح
http://localhost:9090- جرّب الاستعلام:
nextjs_http_requests_total - جرّب:
histogram_quantile(0.95, rate(nextjs_http_response_duration_ms_bucket[5m]))
- جرّب الاستعلام:
-
Grafana — افتح
http://localhost:3001- استورد لوحة المراقبة من ملف JSON
- شاهد المقاييس في الوقت الفعلي
استكشاف الأخطاء وإصلاحها
لا تظهر تتبعات في Jaeger
- تأكد من تشغيل OpenTelemetry Collector:
docker compose logs otel-collector - تحقق من أن
OTEL_EXPORTER_OTLP_ENDPOINTيشير إلى العنوان الصحيح - تأكد من أن
instrumentationHook: trueمُفعّل فيnext.config.ts
المقاييس لا تظهر في Prometheus
- تحقق من أن Collector يصدّر إلى Prometheus:
curl http://localhost:8889/metrics - تأكد من إعداد
scrape_configsبشكل صحيح فيprometheus.yml
أخطاء في بيئة Edge Runtime
- OpenTelemetry SDK لا يدعم Edge Runtime
- استخدم الشرط
NEXT_RUNTIME === "nodejs"في ملف instrumentation - لا تستورد حزم OTel في Middleware أو Edge API Routes
ارتفاع استهلاك الذاكرة
- قلّل
send_batch_sizeفي إعدادات Collector - زِد
exportIntervalMillisلتقليل تكرار الإرسال - استخدم sampling لتقليل حجم البيانات في بيئة الإنتاج
أفضل الممارسات للإنتاج
1. استخدام Sampling
في بيئة الإنتاج، لا تحتاج لتتبع كل طلب. استخدم tail-based sampling:
// في instrumentation.ts
import { TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-node";
const sdk = new NodeSDK({
// تتبع 10% من الطلبات فقط
sampler: new TraceIdRatioBasedSampler(0.1),
// ... باقي الإعدادات
});2. تجنب التتبع الزائد
- لا تضف spans لعمليات سريعة جداً (أقل من 1ms)
- تجنب تسجيل بيانات حساسة في attributes
- استخدم مستوى تسجيل مناسب لكل بيئة
3. تأمين نقاط النهاية
# في الإنتاج، استخدم TLS وAuthentication
exporters:
otlp/secure:
endpoint: https://otel.yourdomain.com:4317
tls:
cert_file: /etc/ssl/certs/client.crt
key_file: /etc/ssl/private/client.key
headers:
Authorization: "Bearer YOUR_TOKEN"4. مراقبة OpenTelemetry Collector نفسه
# إضافة تتبع صحة Collector
extensions:
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
service:
extensions: [health_check, zpages]الخطوات التالية
بعد إتمام هذا الدليل، يمكنك التوسع في:
- التتبع الموزع — ربط تتبعات عبر خدمات متعددة (microservices)
- Custom Metrics — إضافة مقاييس أعمال مخصصة مثل عدد المستخدمين النشطين
- Log Correlation — ربط السجلات بالتتبعات باستخدام trace_id
- Real User Monitoring — تتبع أداء المتصفح باستخدام OpenTelemetry Browser SDK
- CI/CD Integration — إضافة تتبع لخط أنابيب النشر
خلاصة
في هذا الدليل، تعلمت كيفية:
- إعداد OpenTelemetry مع Next.js 15 باستخدام instrumentation hook
- تتبع الطلبات تلقائياً وإضافة spans مخصصة لعمليات محددة
- قياس المقاييس مثل معدل الطلبات وزمن الاستجابة ومعدل الأخطاء
- نشر بنية مراقبة متكاملة مع Jaeger وPrometheus وGrafana
- إعداد التنبيهات للكشف عن المشاكل قبل أن يلاحظها المستخدمون
المراقبة الجيدة ليست رفاهية — إنها ضرورة لأي تطبيق في بيئة الإنتاج. مع OpenTelemetry، لديك الآن رؤية كاملة لما يحدث داخل تطبيقك.