دليل شامل لإعداد OpenTelemetry مع Next.js 15 للمراقبة والتتبع في الإنتاج

مقدمة
في عالم التطبيقات الحديثة، لا يكفي أن يعمل تطبيقك — بل يجب أن تعرف كيف يعمل. هل الاستجابات بطيئة؟ أين يقضي الخادم معظم وقته؟ ما هي نقاط الاختناق؟ هنا يأتي دور 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، لديك الآن رؤية كاملة لما يحدث داخل تطبيقك.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

نشر تطبيق Next.js باستخدام Docker و CI/CD في بيئة الإنتاج
تعلم كيف تُحاوِل تطبيق Next.js باستخدام Docker، وتُعدّ خط أنابيب CI/CD مع GitHub Actions، وتنشر تلقائياً في بيئة الإنتاج على خادم VPS. دليل شامل من التطوير إلى النشر الآلي.

Docker Compose للمطورين: Next.js مع PostgreSQL و Redis
تعلم كيفية تغليف تطبيق Next.js كامل مع PostgreSQL و Redis باستخدام Docker Compose. يغطي هذا الدليل العملي تنسيق الخدمات المتعددة وسير عمل التطوير وإعادة التحميل الفوري وفحوصات الصحة والإعدادات الجاهزة للإنتاج.

Next.js 15 Partial Prerendering (PPR): بناء لوحة تحكم فائقة السرعة مع العرض الهجين
أتقن Partial Prerendering في Next.js 15 — ادمج العرض الثابت والديناميكي في صفحة واحدة. ابنِ لوحة تحكم تحليلية بقالب ثابت فوري ومحتوى ديناميكي متدفق.