الأمان والأداء
المراقبة والرصد
المراقبة والرصد
تساعدك المراقبة والرصد على فهم سلوك النظام واكتشاف المشكلات مبكراً والحفاظ على صحة التطبيق في الإنتاج.
أساسيات مراقبة التطبيق
مفاهيم المراقبة الرئيسية:
- المقاييس: قياسات رقمية بمرور الوقت (CPU، الذاكرة، الطلبات/ثانية)
- السجلات: سجلات الأحداث مع الطوابع الزمنية والسياق
- التتبعات: رحلة الطلب عبر الأنظمة الموزعة
- التنبيهات: إشعارات عندما تتجاوز المقاييس العتبات
- لوحات المعلومات: تمثيل مرئي لصحة النظام
إعداد Prometheus و Grafana
يجمع Prometheus المقاييس، ويصورها Grafana:
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
node_exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
prometheus_data:
grafana_data:
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
node_exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
prometheus_data:
grafana_data:
تكوين Prometheus
تحديد أهداف الكشط والفواصل الزمنية:
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node_exporter'
static_configs:
- targets: ['node_exporter:9100']
- job_name: 'laravel_app'
static_configs:
- targets: ['app:9091']
metrics_path: '/metrics'
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node_exporter'
static_configs:
- targets: ['node_exporter:9100']
- job_name: 'laravel_app'
static_configs:
- targets: ['app:9091']
metrics_path: '/metrics'
كشف المقاييس في Laravel
إنشاء نقطة نهاية للمقاييس:
// routes/web.php
Route::get('/metrics', [MetricsController::class, 'index'])
->middleware('metrics.auth'); // الأمان باستخدام قائمة بيضاء بعناوين IP
// app/Http/Controllers/MetricsController.php
use Illuminate\Support\Facades\DB;
class MetricsController extends Controller
{
public function index()
{
$metrics = [];
// عدد طلبات HTTP
$metrics[] = '# HELP http_requests_total Total HTTP requests';
$metrics[] = '# TYPE http_requests_total counter';
$metrics[] = 'http_requests_total ' . Cache::get('http_requests', 0);
// اتصالات قاعدة البيانات
$metrics[] = '# HELP db_connections Active database connections';
$metrics[] = '# TYPE db_connections gauge';
$metrics[] = 'db_connections ' . DB::connection()->select('SHOW STATUS LIKE "Threads_connected"')[0]->Value;
// استخدام الذاكرة
$metrics[] = '# HELP memory_usage_bytes Memory usage in bytes';
$metrics[] = '# TYPE memory_usage_bytes gauge';
$metrics[] = 'memory_usage_bytes ' . memory_get_usage();
return response(implode("\n", $metrics), 200)
->header('Content-Type', 'text/plain');
}
}
Route::get('/metrics', [MetricsController::class, 'index'])
->middleware('metrics.auth'); // الأمان باستخدام قائمة بيضاء بعناوين IP
// app/Http/Controllers/MetricsController.php
use Illuminate\Support\Facades\DB;
class MetricsController extends Controller
{
public function index()
{
$metrics = [];
// عدد طلبات HTTP
$metrics[] = '# HELP http_requests_total Total HTTP requests';
$metrics[] = '# TYPE http_requests_total counter';
$metrics[] = 'http_requests_total ' . Cache::get('http_requests', 0);
// اتصالات قاعدة البيانات
$metrics[] = '# HELP db_connections Active database connections';
$metrics[] = '# TYPE db_connections gauge';
$metrics[] = 'db_connections ' . DB::connection()->select('SHOW STATUS LIKE "Threads_connected"')[0]->Value;
// استخدام الذاكرة
$metrics[] = '# HELP memory_usage_bytes Memory usage in bytes';
$metrics[] = '# TYPE memory_usage_bytes gauge';
$metrics[] = 'memory_usage_bytes ' . memory_get_usage();
return response(implode("\n", $metrics), 200)
->header('Content-Type', 'text/plain');
}
}
تتبع الأخطاء مع Sentry
يلتقط Sentry الأخطاء وينظمها:
# تثبيت Sentry SDK
composer require sentry/sentry-laravel
# نشر التكوين
php artisan sentry:publish --dsn=your-dsn-here
# .env
SENTRY_LARAVEL_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
SENTRY_TRACES_SAMPLE_RATE=1.0 # 100% من المعاملات
SENTRY_ENVIRONMENT=production
composer require sentry/sentry-laravel
# نشر التكوين
php artisan sentry:publish --dsn=your-dsn-here
# .env
SENTRY_LARAVEL_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
SENTRY_TRACES_SAMPLE_RATE=1.0 # 100% من المعاملات
SENTRY_ENVIRONMENT=production
تكوين معالجة الأخطاء:
// app/Exceptions/Handler.php
use Sentry\Laravel\Integration;
public function register()
{
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
}
// التقاط الأخطاء يدوياً
try {
$this->processPayment();
} catch (\Exception $e) {
\Sentry\captureException($e);
\Sentry\captureMessage('فشلت معالجة الدفع', [
'user_id' => auth()->id(),
'amount' => $amount,
]);
}
use Sentry\Laravel\Integration;
public function register()
{
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
}
// التقاط الأخطاء يدوياً
try {
$this->processPayment();
} catch (\Exception $e) {
\Sentry\captureException($e);
\Sentry\captureMessage('فشلت معالجة الدفع', [
'user_id' => auth()->id(),
'amount' => $amount,
]);
}
مراقبة وقت التشغيل
مراقبة توفر التطبيق:
// استخدام Laravel Pulse (المراقبة المدمجة)
composer require laravel/pulse
php artisan pulse:install
php artisan migrate
// config/pulse.php
return [
'recorders' => [
Recorders\Servers::class => [
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
],
Recorders\Requests::class => [
'enabled' => env('PULSE_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_REQUESTS_SAMPLE_RATE', 1),
],
Recorders\SlowQueries::class => [
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
],
],
];
// الوصول إلى لوحة المعلومات على /pulse
Route::get('/pulse', [PulseController::class, 'index']);
composer require laravel/pulse
php artisan pulse:install
php artisan migrate
// config/pulse.php
return [
'recorders' => [
Recorders\Servers::class => [
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
],
Recorders\Requests::class => [
'enabled' => env('PULSE_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_REQUESTS_SAMPLE_RATE', 1),
],
Recorders\SlowQueries::class => [
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
],
],
];
// الوصول إلى لوحة المعلومات على /pulse
Route::get('/pulse', [PulseController::class, 'index']);
لوحات معلومات الأداء
إنشاء لوحات معلومات Grafana:
// أمثلة على استعلامات Grafana PromQL
// معدل الطلبات (طلبات في الثانية)
rate(http_requests_total[5m])
// نسبة معدل الخطأ
(rate(http_requests_total{status=~"5.."}[5m]) /
rate(http_requests_total[5m])) * 100
// متوسط وقت الاستجابة
rate(http_request_duration_seconds_sum[5m]) /
rate(http_request_duration_seconds_count[5m])
// استخدام مجموعة اتصالات قاعدة البيانات
(db_connections / db_max_connections) * 100
// نسبة استخدام الذاكرة
(memory_usage_bytes / memory_limit_bytes) * 100
// معدل الطلبات (طلبات في الثانية)
rate(http_requests_total[5m])
// نسبة معدل الخطأ
(rate(http_requests_total{status=~"5.."}[5m]) /
rate(http_requests_total[5m])) * 100
// متوسط وقت الاستجابة
rate(http_request_duration_seconds_sum[5m]) /
rate(http_request_duration_seconds_count[5m])
// استخدام مجموعة اتصالات قاعدة البيانات
(db_connections / db_max_connections) * 100
// نسبة استخدام الذاكرة
(memory_usage_bytes / memory_limit_bytes) * 100
استراتيجيات التنبيه
تكوين التنبيهات في Prometheus:
# alert.rules.yml
groups:
- name: application_alerts
interval: 30s
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "تم اكتشاف معدل خطأ مرتفع"
description: "معدل الخطأ هو {{ $value }} أخطاء/ثانية"
- alert: HighResponseTime
expr: http_request_duration_seconds > 2
for: 10m
labels:
severity: warning
annotations:
summary: "وقت استجابة مرتفع"
description: "وقت الاستجابة هو {{ $value }}ث"
- alert: DatabaseConnectionsHigh
expr: (db_connections / db_max_connections) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "مجموعة اتصالات قاعدة البيانات ممتلئة بنسبة 80%"
groups:
- name: application_alerts
interval: 30s
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "تم اكتشاف معدل خطأ مرتفع"
description: "معدل الخطأ هو {{ $value }} أخطاء/ثانية"
- alert: HighResponseTime
expr: http_request_duration_seconds > 2
for: 10m
labels:
severity: warning
annotations:
summary: "وقت استجابة مرتفع"
description: "وقت الاستجابة هو {{ $value }}ث"
- alert: DatabaseConnectionsHigh
expr: (db_connections / db_max_connections) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "مجموعة اتصالات قاعدة البيانات ممتلئة بنسبة 80%"
تجميع السجلات
التسجيل المركزي مع مجموعة ELK:
# filebeat.yml (شحن السجلات إلى Elasticsearch)
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/www/storage/logs/*.log
json.keys_under_root: true
json.add_error_key: true
output.elasticsearch:
hosts: ["localhost:9200"]
index: "laravel-%{+yyyy.MM.dd}"
# تنسيق سجل Laravel للتسجيل المنظم
// config/logging.php
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'sentry'],
'formatter' => \Monolog\Formatter\JsonFormatter::class,
],
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/www/storage/logs/*.log
json.keys_under_root: true
json.add_error_key: true
output.elasticsearch:
hosts: ["localhost:9200"]
index: "laravel-%{+yyyy.MM.dd}"
# تنسيق سجل Laravel للتسجيل المنظم
// config/logging.php
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'sentry'],
'formatter' => \Monolog\Formatter\JsonFormatter::class,
],
أفضل الممارسات: راقب الإشارات الذهبية الأربع: الكمون، حركة المرور، الأخطاء، والتشبع. قم بإعداد تنبيهات للمشكلات القابلة للتنفيذ فقط لتجنب إرهاق التنبيهات.
تمرين: قم بإعداد مراقبة أساسية لتطبيق Laravel الخاص بك. قم بتثبيت Laravel Pulse أو إنشاء نقطة نهاية مقاييس مخصصة. قم بتكوين تنبيه حرج واحد (مثل عتبة معدل الخطأ). اختبر التنبيه عن طريق تشغيل الشرط.