ההבדל בין לדעת לבין לנחש
יש רגע מוכר בכל חברת תוכנה ישראלית. הטלפון מצלצל, לקוח על הקו, והוא כועס. "המערכת איטית". או גרוע מזה: "המערכת לא עובדת". ואז מתחיל המרוץ. מישהו נכנס לשרת, מישהו בודק לוגים, מישהו מנסה לשחזר את הבעיה. חולפת שעה, שעתיים, והלקוח עדיין ממתין.
ב-SysTech אנחנו מפתחים מערכות מאז 2015, ובשלב מוקדם מאוד הבנו שזה לא דרך לעבוד. הלקוח לא צריך להיות מערכת ההתראות שלנו. אנחנו צריכים לדעת שמשהו השתבש עוד לפני שהוא מרגיש את זה.
כאן נכנסים לתמונה שני מושגים שרבים מערבבים ביניהם: Monitoring ו-Observability. המאמר הזה מסביר את ההבדל, מראה איך מיישמים את שניהם עם כלים של Google Cloud Platform, ומפרט עלויות בשקלים כדי שתוכלו לתכנן תקציב.
אם אתם בשלב תכנון מערכת חדשה, כדאי להתחיל מהמדריך שלנו לפיתוח תוכנה ולחזור לכאן כשמגיעים לשלב התפעול.
Monitoring לעומת Observability: זה לא אותו דבר
Monitoring זה לשאול שאלות שאנחנו כבר יודעים לשאול. הגדרנו מראש מה לעקוב אחריו: CPU גבוה? נקבל התראה. דיסק מלא? נקבל התראה. זמן תגובה של API חרג מ-2 שניות? נקבל התראה. זה חשוב, זה בסיסי, וכל מערכת צריכה את זה.
Observability זה משהו אחר. זו היכולת לשאול שאלות שלא חשבנו עליהן מראש. למה דווקא משתמשים מאזור חיפה חווים איטיות? למה API מסוים מתנהג אחרת ביום שלישי? למה הזיכרון עולה בהדרגה ואחרי שבוע יש crash? כדי לענות על שאלות כאלה, צריכים נתונים עשירים שאפשר לחקור אותם בדיעבד, לא רק דשבורדים מוגדרים מראש.
בפרקטיקה, Monitoring הוא תת-קבוצה של Observability. אתם צריכים את שניהם. Monitoring כדי לתפוס בעיות ידועות מהר, ו-Observability כדי לחקור בעיות שלא ציפיתם להן.
שלושת העמודים: Logs, Metrics ו-Traces
הקהילה מדברת על "Three Pillars of Observability". שלושה סוגי נתונים שביחד נותנים תמונה מלאה של מה שקורה במערכת.
Logs: מה קרה
לוגים הם האירועים הבודדים. כל שורת לוג מתארת משהו שקרה: בקשה שהגיעה, שגיאה שזרקה, משימה שהסתיימה. הם הכי אינטואיטיביים כי כולנו רגילים ל-console.log.
הבעיה עם לוגים לא מובנים היא שאי אפשר לחפש בהם ביעילות. אם כל שורה נראית אחרת, אי אפשר לסנן, לקבץ או ליצור גרפים. לכן אנחנו עובדים עם structured logging בכל פרויקט:
"`javascript
const { createLogger, format, transports } = require('winston');
const logger = createLogger({
format: format.combine(
format.timestamp(),
format.json()
),
defaultMeta: { service: 'payment-service', version: '2.4.1' },
transports: [
new transports.Console()
]
});
// שימוש נכון – מובנה עם context
logger.info('Payment processed', {
userId: user.id,
amount: payment.amount,
currency: 'ILS',
provider: 'tranzila',
durationMs: endTime – startTime
});
// שימוש לא נכון – טקסט חופשי
logger.info(`Payment of ${payment.amount} processed for user ${user.id}`);
"`
ב-GCP, הלוגים נשלחים אוטומטית ל-Cloud Logging אם אתם רצים על Cloud Run או GKE. אם אתם כותבים JSON ל-stdout, Cloud Logging יפרסר את השדות ויאפשר חיפוש וסינון על כל שדה בנפרד.
Metrics: מה המצב
מטריקות הן מספרים שמשתנים לאורך זמן. כמה בקשות בשנייה, זמן תגובה ממוצע, אחוז שימוש בזיכרון, מספר שגיאות לדקה. בניגוד ללוגים שמתארים אירועים בודדים, מטריקות מראות מגמות.
"`javascript
const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const { MetricExporter } = require('@google-cloud/opentelemetry-cloud-monitoring-exporter');
const exporter = new MetricExporter();
const meterProvider = new MeterProvider({
readers: [
new PeriodicExportingMetricReader({
exporter,
exportIntervalMillis: 60000
})
]
});
const meter = meterProvider.getMeter('payment-service');
// מונה בקשות
const requestCounter = meter.createCounter('http_requests_total', {
description: 'Total HTTP requests'
});
// היסטוגרמה של זמני תגובה
const responseTime = meter.createHistogram('http_response_duration_ms', {
description: 'HTTP response time in milliseconds'
});
// שימוש ב-middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() – start;
requestCounter.add(1, {
method: req.method,
path: req.route?.path || req.path,
status: res.statusCode
});
responseTime.record(duration, {
method: req.method,
path: req.route?.path || req.path
});
});
next();
});
"`
Cloud Monitoring מקבל את המטריקות האלה ומאפשר ליצור דשבורדים וגרפים עליהן. הכוח של מטריקות הוא שהן זולות יחסית לאחסון (מספר קטן לכל נקודת זמן) ומאפשרות לראות דפוסים על ציר הזמן.
Traces: מה הנתיב
Trace עוקב אחרי בקשה אחת לאורך כל השירותים שהיא עוברת. בארכיטקטורה של מיקרו-שירותים, בקשה אחת של משתמש יכולה לעבור דרך gateway, שירות אותנטיקציה, שירות תשלומים, ושירות הודעות. בלי tracing, אם הבקשה איטית, אי אפשר לדעת איפה בדיוק הצוואר בקבוק.
"`javascript
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { TraceExporter } = require('@google-cloud/opentelemetry-cloud-trace-exporter');
const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const provider = new NodeTracerProvider();
provider.addSpanProcessor(
new BatchSpanProcessor(new TraceExporter())
);
provider.register();
registerInstrumentations({
instrumentations: [
new HttpInstrumentation(),
new ExpressInstrumentation()
]
});
// Trace מותאם אישית לפעולות עסקיות
const tracer = provider.getTracer('payment-service');
async function processPayment(order) {
return tracer.startActiveSpan('process-payment', async (span) => {
span.setAttribute('order.id', order.id);
span.setAttribute('order.amount', order.amount);
try {
const validation = await tracer.startActiveSpan('validate-order', async (childSpan) => {
const result = await validateOrder(order);
childSpan.end();
return result;
});
const charge = await tracer.startActiveSpan('charge-provider', async (childSpan) => {
childSpan.setAttribute('provider', 'tranzila');
const result = await chargePayment(order);
childSpan.end();
return result;
});
span.setStatus({ code: 1 }); // OK
return charge;
} catch (error) {
span.setStatus({ code: 2, message: error.message });
span.recordException(error);
throw error;
} finally {
span.end();
}
});
}
"`
Cloud Trace מציג את כל ה-spans בצורה ויזואלית, כך שאפשר לראות בדיוק כמה זמן כל שלב לקח. זה מציל שעות של debug כשצריך להבין למה בקשה ספציפית הייתה איטית.
כלי GCP למעקב: מה משתמשים ולמה
Cloud Monitoring
זה הכלי המרכזי לדשבורדים, התראות ומטריקות. Cloud Monitoring אוסף אוטומטית מטריקות מכל שירותי GCP (Cloud Run, Cloud SQL, Memorystore ועוד), ואפשר להוסיף מטריקות מותאמות אישית דרך OpenTelemetry כמו שהראינו.
היתרון הגדול של Cloud Monitoring הוא האינטגרציה הטבעית עם שאר GCP. לא צריך להגדיר agents, לא צריך לנהל שרתי Prometheus, הכל פשוט עובד. עבור רוב הפרויקטים שאנחנו מנהלים, זה מספיק ולא צריך כלים חיצוניים.
Cloud Logging
אוסף לוגים מכל מקום, מאפשר חיפוש מהיר עם שפת שאילתות חזקה, ומאפשר ליצור מטריקות מתוך לוגים. תכונה שימושית במיוחד: Log-based Metrics. אם אתם רוצים לעקוב אחרי כמות שגיאות 500 לדקה, אפשר ליצור מטריקה ישירות מהלוגים בלי לשנות קוד.
דוגמה לשאילתת חיפוש ב-Cloud Logging:
"`
resource.type="cloud_run_revision"
resource.labels.service_name="payment-service"
severity>=ERROR
jsonPayload.userId="user_12345"
timestamp>="2026-04-10T08:00:00Z"
"`
השאילתה הזו מוצאת את כל השגיאות של משתמש ספציפי בשירות ספציפי מתאריך מסוים. בלי structured logging, חיפוש כזה היה בלתי אפשרי.
Cloud Trace
מציג traces מלאים של בקשות. עובד יחד עם OpenTelemetry ומאפשר לראות latency breakdown של כל בקשה. שימושי במיוחד כשיש כמה שירותים שמדברים אחד עם השני, ואתם צריכים להבין איפה הזמן הולך.
התראות שעובדות בפועל
התראות הן הגשר בין Monitoring לבין פעולה. מערכת מעולה שאף אחד לא מסתכל עליה לא שווה כלום. אבל יש טעות נפוצה שאנחנו רואים כל הזמן: יותר מדי התראות. כשכל דבר קטן שולח SMS, הצוות מתחיל להתעלם מהתראות. זה נקרא alert fatigue, וזה מסוכן.
עקרונות להתראות נכונות
כלל ראשון: לא להתריע על סימפטומים פנימיים, להתריע על השפעה על משתמשים. אחוז CPU גבוה הוא לא בעיה בפני עצמו. אם ה-CPU גבוה אבל זמני התגובה תקינים, אין סיבה להעיר מישהו בלילה. אם זמני התגובה חורגים מה-SLO, זה כן סיבה.
כלל שני: severity levels ברורים. אנחנו מגדירים שלוש רמות:
- Critical (P1): מערכת לא זמינה או פגיעה באחוז גבוה מהמשתמשים. שולח SMS ומתקשר. מטופל מיד.
- Warning (P2): ירידה בביצועים או שגיאות שלא משפיעות על רוב המשתמשים. שולח ל-Slack. מטופל באותו יום עבודה.
- Info (P3): דברים שכדאי לדעת עליהם אבל לא דחופים. נרשם בדשבורד. מטופל ב-sprint הבא.
הגדרת התראה ב-Cloud Monitoring
"`javascript
// יצירת alerting policy דרך API
const monitoring = require('@google-cloud/monitoring');
const client = new monitoring.AlertPolicyServiceClient();
async function createLatencyAlert(projectId) {
const policy = {
displayName: 'High API Latency – Payment Service',
conditions: [
{
displayName: 'P95 latency > 2s for 5 minutes',
conditionThreshold: {
filter: 'resource.type = "cloud_run_revision" AND ' +
'resource.labels.service_name = "payment-service" AND ' +
'metric.type = "run.googleapis.com/request_latencies"',
aggregations: [
{
alignmentPeriod: { seconds: 300 },
perSeriesAligner: 'ALIGN_PERCENTILE_95'
}
],
comparison: 'COMPARISON_GT',
thresholdValue: 2000,
duration: { seconds: 300 },
trigger: { count: 1 }
}
}
],
notificationChannels: [
`projects/${projectId}/notificationChannels/slack-channel-id`,
`projects/${projectId}/notificationChannels/pagerduty-id`
],
alertStrategy: {
autoClose: { seconds: 3600 }
}
};
const [created] = await client.createAlertPolicy({
name: `projects/${projectId}`,
alertPolicy: policy
});
return created;
}
"`
SLOs ו-SLIs: לדבר בשפה של הלקוח
SLI (Service Level Indicator) זה מה שאנחנו מודדים. SLO (Service Level Objective) זה היעד שהגדרנו. ביחד, הם מתרגמים מטריקות טכניות לשפה שגם אנשי עסקים מבינים.
דוגמאות מפרויקטים שלנו:
| שירות | SLI | SLO |
|---|---|---|
| API ראשי | אחוז בקשות שחזרו תוך פחות מ-500ms | 99.5% בחלון של 30 יום |
| שירות תשלומים | אחוז טרנזקציות שהצליחו | 99.9% בחלון של 30 יום |
| דף נחיתה | זמן טעינה | P95 מתחת ל-2 שניות |
| Webhook processing | אחוז webhooks שעובדו תוך 30 שניות | 99% בחלון של 7 ימים |
המושג החשוב כאן הוא Error Budget. אם ה-SLO שלנו הוא 99.5% זמינות בחודש, זה אומר שמותר לנו 0.5% downtime, שזה כ-3.6 שעות בחודש. כל עוד לא שרפנו את התקציב הזה, אפשר לפרוס גרסאות חדשות ולקחת סיכונים. ברגע שה-error budget נגמר, עוצרים פריסות ומתמקדים ביציבות.
Cloud Monitoring תומך ב-SLO monitoring ישירות. אפשר להגדיר SLO על שירות, ולקבל דשבורד שמראה כמה error budget נשאר:
"`javascript
const { ServiceMonitoringServiceClient } = require('@google-cloud/monitoring');
const client = new ServiceMonitoringServiceClient();
async function createSLO(projectId, serviceId) {
const slo = {
displayName: 'API Latency SLO – 99.5% under 500ms',
serviceLevelIndicator: {
requestBased: {
distributionCut: {
distributionFilter:
'resource.type = "cloud_run_revision" ' +
'AND metric.type = "run.googleapis.com/request_latencies"',
range: {
min: 0,
max: 500
}
}
}
},
goal: 0.995,
rollingPeriod: { seconds: 2592000 } // 30 days
};
const [created] = await client.createServiceLevelObjective({
parent: `projects/${projectId}/services/${serviceId}`,
serviceLevelObjective: slo
});
return created;
}
"`
דשבורדים שמישהו באמת מסתכל עליהם
עוד טעות שאנחנו רואים: צוותים שבונים 15 דשבורדים ואף אחד לא מסתכל עליהם. דשבורד טוב עונה על שאלה ספציפית, ומישהו מסתכל עליו באופן קבוע.
אנחנו ממליצים על שלושה דשבורדים עיקריים:
1. דשבורד תפעולי (Ops Dashboard)
מוצג על מסך בחדר הצוות או ב-Slack channel. מראה את המצב הנוכחי:
- Request rate לכל שירות
- Error rate (אחוז שגיאות 5xx)
- P50 ו-P95 latency
- מספר instances פעילים
- Error budget שנשאר
2. דשבורד עסקי (Business Dashboard)
למנהלים ואנשי מוצר:
- כמות משתמשים פעילים בזמן אמת
- מספר טרנזקציות מוצלחות
- הכנסות (אם רלוונטי)
- Funnel rates
3. דשבורד חקירה (Investigation Dashboard)
לשימוש בזמן תקלה. מציג נתונים מפורטים שעוזרים ל-debug:
- שגיאות מפורטות לפי סוג
- Trace samples של בקשות איטיות
- Dependency health (שירותים חיצוניים, DB, cache)
- Resource utilization מפורט
Incident Response: מה עושים כשהכל בוער
Monitoring ו-Observability הם כלים. הם שווים רק אם יש תהליך ברור למה לעשות כשיש בעיה. ב-SysTech אנחנו מיישמים תהליך incident response פשוט אבל מוגדר:
שלב 1: זיהוי (Detection)
ההתראה מגיעה. מישהו מהצוות מכריז על incident ב-channel ייעודי. מכאן כל התקשורת רצה במקום אחד.
שלב 2: הערכה (Assessment)
בודקים את ה-dashboards. כמה משתמשים מושפעים? מה ה-blast radius? על סמך זה קובעים severity.
שלב 3: הפחתה (Mitigation)
עדיפות ראשונה: להחזיר את השירות. לא לתקן את הבאג, להחזיר את השירות. אם צריך, rollback לגרסה קודמת. תמיד.
שלב 4: תיקון (Fix)
אחרי שהשירות חזר, מתקנים את הבעיה בשקט ובודקים לפני שפורסים שוב.
שלב 5: Post-mortem
לא מחפשים אשמים. מחפשים מה שבור בתהליך שאפשר שהבעיה הזו לקרות. מתעדים ומפיקים לקחים.
הדבר הכי חשוב בתהליך הזה: אל תנסו לדבג ב-production תחת לחץ בלי כלי Observability. אם אין structured logs, אם אין traces, אם אין מטריקות מותאמות, אתם תבזבזו זמן יקר. ההשקעה ב-Observability משתלמת בדיוק ברגעים האלה.
כמה זה עולה: תמחור בשקלים
נפרט עלויות ל-GCP monitoring stack עבור אפליקציה טיפוסית (נניח SaaS עם 5,000 משתמשים פעילים):
Cloud Logging
- 50 GB ראשונים בחודש: חינם
- מעבר לזה: כ-1.85 שקל ל-GB (0.50 דולר)
- Retention ברירת מחדל: 30 יום (אפשר להאריך בתשלום)
- אפליקציה טיפוסית מייצרת 20-100 GB לוגים בחודש
עלות חודשית משוערת: 0-185 שקל
Cloud Monitoring
- מטריקות GCP מובנות: חינם
- מטריקות מותאמות אישית: חינם עד 500 time series
- מעבר לזה: כ-0.30 שקל ל-1,000 time series (0.10 דולר)
- ככל שיש יותר labels, יש יותר time series. צריך להיזהר עם high cardinality
עלות חודשית משוערת: 0-150 שקל
Cloud Trace
- 100,000 spans ראשונים בחודש: חינם
- מעבר לזה: כ-0.74 שקל ל-1 מיליון spans (0.20 דולר)
- עם sampling של 5-10%, רוב האפליקציות נשארות בתוך ה-free tier
עלות חודשית משוערת: 0-75 שקל
סיכום עלויות
עבור אפליקציה בינונית, ה-monitoring stack המלא של GCP עולה בין 0 ל-400 שקל בחודש. זה זול. בהשוואה ל-Datadog שמתחיל מ-60 שקל למשתמש לחודש (ועולה מהר), או New Relic שיכול להגיע לאלפי שקלים בחודש, כלי GCP הם בחירה כלכלית מצוינת לחברות ישראליות.
טיפ חשוב: הדבר הכי יקר ב-logging הוא נפח. כדי לשלוט בעלויות, אל תעשו log ברמת DEBUG ב-production. השתמשו ב-log levels נכונים, והגדירו log exclusion filters ב-Cloud Logging לסנן לוגים לא רלוונטיים.
טעויות נפוצות שאנחנו רואים בפרויקטים
1. מתחילים ב-Observability מאוחר מדי
הרבה צוותים מוסיפים monitoring רק אחרי תקלה ראשונה ב-production. עד אז כבר איבדו אמון של לקוחות. אנחנו מטמיעים את ה-stack מהיום הראשון, עוד לפני שיש משתמשים אמיתיים.
2. לוגים לא מובנים
console.log עם טקסט חופשי הוא שווה ערך לזרוק מידע לפח. אי אפשר לחפש בו, אי אפשר ליצור מטריקות ממנו, אי אפשר לקשר אותו ל-traces. Structured logging מיום ראשון.
3. יותר מדי התראות
ציינו את זה קודם אבל זה שווה הדגשה. 50 התראות ביום שווה אפס התראות, כי אף אחד לא קורא אותן. עדיף 5 התראות משמעותיות שכל אחת מחייבת פעולה.
4. אין correlation בין הנתונים
לוגים במקום אחד, מטריקות במקום אחד, traces במקום אחד. בלי קשר ביניהם. המפתח הוא trace ID משותף שמופיע בכל לוג, בכל span, ומאפשר לעבור מהתראה על מטריקה ללוג ספציפי ל-trace ספציפי. OpenTelemetry פותר את זה.
5. לא מודדים את מה שחשוב ללקוח
יודעים מה ה-CPU utilization אבל לא יודעים כמה זמן לוקח ללקוח להשלים רכישה. SLIs צריכים לשקף חוויית משתמש, לא מדדים טכניים.
סיכום ושלב הבא
Monitoring ו-Observability הם לא פרויקט חד-פעמי. זה תהליך מתמשך שמשתפר ככל שהמערכת גדלה. אבל הבסיס חייב להיות מיום ראשון: structured logs, מטריקות מותאמות, traces על פעולות קריטיות, התראות מדויקות, ו-SLOs מוגדרים.
ב-SysTech אנחנו מטמיעים את כל ה-stack הזה כחלק מכל פרויקט פיתוח. לא כתוספת, לא כפאזה נפרדת, אלא כחלק אינטגרלי מהארכיטקטורה. כי מערכת שלא ניתן לצפות בה היא מערכת שאתם לא באמת שולטים בה.
רוצים לבנות מערכת שאתם באמת יודעים מה קורה בה? דברו איתנו ונראה איך מתאימים monitoring stack לפרויקט שלכם. אם אתם בונים מוצר SaaS, כדאי גם להכיר את שירותי פיתוח SaaS שלנו.
שאלות נפוצות
מה ההבדל בין Monitoring ל-Observability?
Monitoring עוקב אחרי מדדים מוגדרים מראש ומתריע כשחורגים מסף. Observability נותן את הכלים לחקור בעיות שלא ציפיתם להן, דרך שילוב של logs, metrics ו-traces. בפרקטיקה, Monitoring הוא חלק מ-Observability.
כמה עולה monitoring stack ב-GCP?
עבור אפליקציה בינונית עם כמה אלפי משתמשים, כלי GCP (Cloud Monitoring, Cloud Logging, Cloud Trace) עולים בין 0 ל-400 שקל בחודש. הרוב נמצא ב-free tier. זה זול משמעותית בהשוואה לכלים כמו Datadog או New Relic.
האם OpenTelemetry הכרחי או שאפשר בלי?
אפשר להתחיל בלי, עם הכלים המובנים של GCP. אבל OpenTelemetry נותן סטנדרט אחיד ל-logs, metrics ו-traces, וגם אפשרות לעבור provider בעתיד בלי לשנות קוד. אנחנו ממליצים להטמיע אותו מההתחלה כי ההשקעה קטנה והתועלת גדולה.
מה הדבר הראשון שצריך להגדיר?
Structured logging עם Cloud Logging. זה הבסיס שכל השאר נבנה עליו. עוברים מ-console.log לספרייה כמו Winston עם JSON output, מוסיפים שדות קבועים (service name, version), ומוודאים שכל לוג מכיל context רלוונטי.
איך מונעים alert fatigue?
שלוש גישות: ראשית, מגדירים התראות רק על מדדים שמשפיעים על משתמשים (SLI-based alerts). שנית, משתמשים ב-error budget burning rate alerts במקום threshold פשוט. שלישית, עושים סקירה חודשית של כל ההתראות ומוחקים את אלה שאף אחד לא טיפל בהן.