واجهات GraphQL

المراقبة والقابلية للملاحظة

16 دقيقة الدرس 28 من 35

المراقبة والقابلية للملاحظة

تعلم كيفية مراقبة وتصحيح وتحسين واجهة برمجة تطبيقات GraphQL الخاصة بك باستخدام الأدوات والممارسات المعيارية في الصناعة.

استوديو Apollo

يوفر استوديو Apollo مراقبة وتحليلات شاملة لواجهات برمجة تطبيقات GraphQL:

const { ApolloServer } = require('apollo-server'); const server = new ApolloServer({ typeDefs, resolvers, // تفعيل تكامل استوديو Apollo apollo: { key: process.env.APOLLO_KEY, graphRef: process.env.APOLLO_GRAPH_REF, // مثل "my-graph@production" }, // تفعيل تقارير الاستخدام plugins: [ require('apollo-server-core').ApolloServerPluginUsageReporting({ sendVariableValues: { all: true }, sendHeaders: { all: true }, }), ], }); server.listen().then(({ url }) => { console.log(`الخادم جاهز على ${url}`); console.log(`استوديو Apollo: https://studio.apollographql.com/`); });
ملاحظة: احصل على مفتاح API الخاص باستوديو Apollo من studio.apollographql.com. لا تلتزم أبداً بمفاتيح API في التحكم في الإصدار.

سجل المخطط

سجّل مخططك مع استوديو Apollo لتتبع الإصدار والتحقق:

# تثبيت Rover CLI npm install -g @apollo/rover # المصادقة مع استوديو Apollo rover config auth # نشر المخطط إلى السجل rover graph publish my-graph@production \ --schema ./schema.graphql \ --key $APOLLO_KEY # التحقق من تغييرات المخطط قبل النشر rover graph check my-graph@production \ --schema ./schema.graphql \ --key $APOLLO_KEY # تنزيل المخطط من السجل rover graph fetch my-graph@production \ --key $APOLLO_KEY > schema.graphql

مراقبة العمليات

تتبع أداء الاستعلام والأخطاء وأنماط الاستخدام:

const { ApolloServer } = require('apollo-server'); const { ApolloServerPluginUsageReporting } = require('apollo-server-core'); const server = new ApolloServer({ typeDefs, resolvers, plugins: [ ApolloServerPluginUsageReporting({ // الإبلاغ عن جميع العمليات sendVariableValues: { all: true }, sendHeaders: { all: true }, // اسم عملية مخصص generateClientInfo: ({ request }) => { const headers = request.http?.headers; return { clientName: headers.get('apollographql-client-name'), clientVersion: headers.get('apollographql-client-version'), }; }, // استبعاد العمليات الحساسة sendReportsImmediately: true, }), ], }); // تسمية العملية من جانب العميل import { ApolloClient, InMemoryCache } from '@apollo/client'; const client = new ApolloClient({ uri: 'http://localhost:4000/graphql', cache: new InMemoryCache(), name: 'web-app', version: '1.0.0', headers: { 'apollographql-client-name': 'web-app', 'apollographql-client-version': '1.0.0', }, });

تتبع الأخطاء

ادمج خدمات تتبع الأخطاء مثل Sentry لمراقبة شاملة للأخطاء:

const { ApolloServer } = require('apollo-server'); const Sentry = require('@sentry/node'); // تهيئة Sentry Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 1.0, }); // إضافة تنسيق خطأ مخصص const sentryPlugin = { async requestDidStart() { return { async didEncounterErrors({ errors, context }) { for (const error of errors) { // تجاهل أخطاء العميل if (error.extensions?.code === 'BAD_USER_INPUT') { continue; } // الإبلاغ إلى Sentry Sentry.withScope((scope) => { scope.setTag('kind', 'graphql'); scope.setContext('query', { query: context.request.query, variables: context.request.variables, }); scope.setUser({ id: context.user?.id, email: context.user?.email, }); Sentry.captureException(error); }); } }, }; }, }; const server = new ApolloServer({ typeDefs, resolvers, plugins: [sentryPlugin], formatError: (error) => { // لا تكشف عن أخطاء داخلية للعملاء if (process.env.NODE_ENV === 'production') { return new Error('خطأ في الخادم الداخلي'); } return error; }, });

مقاييس الأداء

تتبع أوقات تنفيذ المحللات وحدد عقبات الأداء:

const { ApolloServer } = require('apollo-server'); const responseTime = require('response-time'); // إضافة تتبع أداء مخصصة const performancePlugin = { async requestDidStart() { const start = Date.now(); const resolverTimings = new Map(); return { async executionDidStart() { return { willResolveField({ info }) { const fieldStart = Date.now(); return () => { const duration = Date.now() - fieldStart; const path = info.path.key; if (!resolverTimings.has(path)) { resolverTimings.set(path, []); } resolverTimings.get(path).push(duration); }; }, }; }, async willSendResponse({ response }) { const totalDuration = Date.now() - start; // تسجيل الاستعلامات البطيئة if (totalDuration > 1000) { console.warn('تم اكتشاف استعلام بطيء:', { duration: totalDuration, resolvers: Array.from(resolverTimings.entries()) .map(([name, timings]) => ({ name, avg: timings.reduce((a, b) => a + b) / timings.length, max: Math.max(...timings), })) .sort((a, b) => b.avg - a.avg), }); } // إضافة رأس التوقيت response.http.headers.set('X-Response-Time', `${totalDuration}ms`); }, }; }, }; const server = new ApolloServer({ typeDefs, resolvers, plugins: [performancePlugin], });
نصيحة: قم بإعداد تنبيهات في استوديو Apollo للاستعلامات البطيئة (> 2 ثانية)، ومعدلات الخطأ العالية (> 5%)، أو أنماط حركة المرور غير العادية.

التسجيل

نفذ تسجيلاً منظماً لتصحيح وتحليل أفضل:

const winston = require('winston'); // تكوين مسجل Winston const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), ], }); if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.simple(), })); } // إضافة تسجيل const loggingPlugin = { async requestDidStart({ request, context }) { const start = Date.now(); logger.info('بدأ طلب GraphQL', { operationName: request.operationName, userId: context.user?.id, }); return { async didEncounterErrors({ errors }) { errors.forEach((error) => { logger.error('خطأ GraphQL', { message: error.message, path: error.path, extensions: error.extensions, }); }); }, async willSendResponse() { const duration = Date.now() - start; logger.info('اكتمل طلب GraphQL', { operationName: request.operationName, duration, }); }, }; }, };

فحوصات الصحة

نفذ نقاط نهاية فحص الصحة للمراقبة وموازنات التحميل:

const express = require('express'); const { ApolloServer } = require('apollo-server-express'); const app = express(); // نقطة نهاية فحص الصحة app.get('/health', async (req, res) => { const health = { uptime: process.uptime(), timestamp: Date.now(), status: 'ok', checks: {}, }; try { // التحقق من اتصال قاعدة البيانات await db.raw('SELECT 1'); health.checks.database = 'ok'; } catch (error) { health.status = 'error'; health.checks.database = 'error'; } try { // التحقق من اتصال Redis await redis.ping(); health.checks.redis = 'ok'; } catch (error) { health.status = 'error'; health.checks.redis = 'error'; } const statusCode = health.status === 'ok' ? 200 : 503; res.status(statusCode).json(health); }); // فحص الجاهزية app.get('/ready', (req, res) => { res.status(200).json({ ready: true }); }); // فحص الحياة app.get('/live', (req, res) => { res.status(200).json({ alive: true }); }); const server = new ApolloServer({ typeDefs, resolvers, }); server.applyMiddleware({ app }); app.listen(4000, () => { console.log('الخادم يعمل على http://localhost:4000'); });
تحذير: لا تكشف أبداً عن معلومات حساسة (مفاتيح API، بيانات اعتماد قاعدة البيانات، أخطاء داخلية) في نقاط نهاية فحص الصحة أو السجلات.
تمرين:
  1. قم بإعداد تكامل استوديو Apollo مع خادم GraphQL الخاص بك
  2. نفذ إضافة تتبع أداء مخصصة تسجل المحللات البطيئة
  3. أضف تتبع أخطاء Sentry لالتقاط وإبلاغ أخطاء GraphQL
  4. أنشئ تسجيلاً منظماً مع Winston لجميع عمليات GraphQL
  5. نفذ نقاط نهاية فحص الصحة لاتصالات قاعدة البيانات والذاكرة المؤقتة