2025-09-04
AWS Lambda Production Monitoring ve Debugging: Savaşta Test Edilmiş Stratejiler
Gerçek dünya incident response deneyimine dayalı AWS Lambda için kapsamlı production monitoring ve debugging stratejileri. CloudWatch metrikleri, X-Ray tracing, structured logging ve etkili alerting pattern'leri.
Lambda function’larını ölçekte çalıştırmak bana en önemli dersi öğretti: gerçek test, function’larınızın development’da çalışıp çalışmaması değil - production’da fail olduklarında debug edebilmek. En büyük ürün lansmanımız sırasında, tüm engineering ekibinin izlediği bir anda, bir Lambda sessizce fail olmaya başladı. CloudWatch alert’i yok, açık hata yok, sadece kafası karışmış müşteriler ve hızla düşen conversion rate.
Bu olay bana Lambda monitoring’in sadece temel CloudWatch metrikleri kurmakla olmadığını öğretti - iş problemleri haline gelmeden önce sorunları debug etmenizi sağlayan kapsamlı bir observability stratejisi inşa etmekle ilgili.
Lambda Observability’nin Üç Direği
1. Metrikler: Erken Uyarı Sistemi
Mutlaka Monitor Etmeniz Gereken Metrikler:
// Bizi sayısız kez kurtaran custom metrikler
// Node.js 20.x ve 22.x runtime'ları ile uyumlu
import { CloudWatch } from '@aws-sdk/client-cloudwatch';
const cloudwatch = new CloudWatch({});
export const publishCustomMetrics = async (
functionName: string,
duration: number,
success: boolean,
businessContext?: { userId?: string, feature?: string }
) => {
const metrics = [
{
MetricName: 'FunctionDuration',
Value: duration,
Unit: 'Milliseconds',
Dimensions: [
{ Name: 'FunctionName', Value: functionName },
{ Name: 'Feature', Value: businessContext?.feature || 'unknown' }
]
},
{
MetricName: success ? 'FunctionSuccess' : 'FunctionFailure',
Value: 1,
Unit: 'Count',
Dimensions: [
{ Name: 'FunctionName', Value: functionName }
]
}
];
// Business-specific metrikler
if (businessContext?.userId) {
metrics.push({
MetricName: 'UserAction',
Value: 1,
Unit: 'Count',
Dimensions: [
{ Name: 'UserId', Value: businessContext.userId },
{ Name: 'ActionType', Value: success ? 'completed' : 'failed' }
]
});
}
await cloudwatch.putMetricData({
Namespace: 'Lambda/Business',
MetricData: metrics
});
};
2. Trace’ler: Dedektif Çalışması
X-Ray tracing tam request flow’unu anlamak için paha biçilmez oldu:
import AWSXRay from 'aws-xray-sdk-core';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
// AWS SDK v3'u instrument et
const dynamoClient = AWSXRay.captureAWSv3Client(new DynamoDBClient({}));
const dynamoDB = DynamoDBDocumentClient.from(dynamoClient);
export const handler = AWSXRay.captureAsyncFunc('payment-processor', async (event) => {
// Filtreleme için custom annotation'lar ekle
const segment = AWSXRay.getSegment();
segment?.addAnnotation('userId', event.userId);
segment?.addAnnotation('paymentMethod', event.paymentMethod);
segment?.addAnnotation('environment', process.env.STAGE);
try {
// External API call'ları trace et
const subsegment = segment?.addNewSubsegment('payment-provider-api');
const paymentResult = await processPayment(event);
subsegment?.close();
// Business metadata ekle
segment?.addMetadata('payment', {
amount: event.amount,
currency: event.currency,
processingTime: Date.now() - event.timestamp
});
return { success: true, paymentId: paymentResult.id };
} catch (error) {
// Error context'i yakala
segment?.addError(error as Error);
segment?.addMetadata('errorContext', {
userId: event.userId,
errorType: error.name,
requestId: event.requestId
});
throw error;
}
});
3. Log’lar: Tarihsel Kayıt
İşe Yarar Structured Logging Pattern’i:
import { createLogger, format, transports } from 'winston';
const logger = createLogger({
level: process.env.LOG_LEVEL || 'info',
format: format.combine(
format.timestamp(),
format.errors({ stack: true }),
format.json()
),
transports: [
new transports.Console()
]
});
// Lambda context-aware logging
export const createContextLogger = (context: any, event: any) => {
const requestId = context.awsRequestId;
const functionName = context.functionName;
return {
info: (message: string, meta?: any) => logger.info({
message,
requestId,
functionName,
stage: process.env.STAGE,
...meta
}),
error: (message: string, error?: Error, meta?: any) => logger.error({
message,
error: error?.stack || error?.message,
requestId,
functionName,
stage: process.env.STAGE,
...meta
}),
// Business event logging
business: (event: string, data: any) => logger.info({
message: `Business Event: ${event}`,
businessEvent: event,
data,
requestId,
functionName,
timestamp: new Date().toISOString()
})
};
};
// Handler'da kullanım
export const handler = async (event: any, context: any) => {
const log = createContextLogger(context, event);
log.info('Function invoked', { eventType: event.Records?.[0]?.eventName });
try {
const result = await processEvent(event);
log.business('order-processed', { orderId: result.orderId, amount: result.amount });
return result;
} catch (error) {
log.error('Processing failed', error as Error, { eventData: event });
throw error;
}
};
Gerçekten Yardımcı Olan CloudWatch Dashboard’ları
Stakeholder İletişimi için Business Dashboard
Stakeholder’lar sistem sağlığı konusunda görünürlük istediğinde, teknik detaylardan çok business odaklı metrikleri göstermek daha değerli oluyor:
# Business odaklı dashboard için CloudFormation template
Resources:
BusinessDashboard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardName: "Lambda-Business-Health"
DashboardBody: !Sub |
{
"widgets": [
{
"type": "metric",
"properties": {
"metrics": [
["Lambda/Business", "OrdersProcessed", "FunctionName", "order-processor"],
["Lambda/Business", "PaymentsCompleted", "FunctionName", "payment-processor"],
["Lambda/Business", "UserRegistrations", "FunctionName", "user-registration"]
],
"period": 300,
"stat": "Sum",
"region": "${AWS::Region}",
"title": "Business Transactions (Last 24h)"
}
},
{
"type": "metric",
"properties": {
"metrics": [
["AWS/Lambda", "Errors", "FunctionName", "order-processor"],
["AWS/Lambda", "Throttles", "FunctionName", "payment-processor"]
],
"period": 300,
"stat": "Sum",
"region": "${AWS::Region}",
"title": "System Health Issues"
}
}
]
}
Debugging için Teknik Dashboard
TechnicalDashboard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardName: "Lambda-Technical-Deep-Dive"
DashboardBody: !Sub |
{
"widgets": [
{
"type": "metric",
"properties": {
"metrics": [
["AWS/Lambda", "Duration", "FunctionName", "payment-processor", { "stat": "Average" }],
["AWS/Lambda", "Duration", "FunctionName", "payment-processor", { "stat": "p99" }]
],
"period": 60,
"region": "${AWS::Region}",
"title": "Function Duration (Average vs P99)"
}
},
{
"type": "log",
"properties": {
"query": "SOURCE '/aws/lambda/payment-processor'\n| fields @timestamp, @message, @requestId\n| filter @message like /ERROR/\n| sort @timestamp desc\n| limit 100",
"region": "${AWS::Region}",
"title": "Recent Errors (Last 1 Hour)"
}
}
]
}
Kurt Diye Bağırmayan Alerting Stratejileri
Business Etkisine Dayalı Alert’ler
Her şey için değil - business etkisi için alert ver:
# CloudFormation alert konfigürasyonu
Resources:
# Kritik: Ödeme işleme hataları
PaymentFailureAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: "Lambda-PaymentProcessor-CriticalFailures"
AlarmDescription: "Payment processing failures above threshold"
MetricName: Errors
Namespace: AWS/Lambda
Statistic: Sum
Period: 300
EvaluationPeriods: 2
Threshold: 5 # 10 dakikada 5'ten fazla hata
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: FunctionName
Value: !Ref PaymentProcessorFunction
AlarmActions:
- !Ref CriticalAlertTopic
TreatMissingData: notBreaching
# Uyarı: Normalden yavaş işlem
PaymentLatencyAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: "Lambda-PaymentProcessor-HighLatency"
MetricName: Duration
Namespace: AWS/Lambda
Statistic: Average
Period: 300
EvaluationPeriods: 3
Threshold: 5000 # 5 saniye ortalama
ComparisonOperator: GreaterThanThreshold
AlarmActions:
- !Ref WarningAlertTopic
# Genel sistem sağlığı için composite alarm
SystemHealthAlarm:
Type: AWS::CloudWatch::CompositeAlarm
Properties:
AlarmName: "Lambda-SystemHealth-Critical"
AlarmRule: !Sub |
ALARM("${PaymentFailureAlarm}") OR
ALARM("${OrderProcessingAlarm}") OR
ALARM("${DatabaseConnectionAlarm}")
AlarmActions:
- !Ref EmergencyAlertTopic
Akıllı Throttling Detection
// Akıllı throttling detection için custom metric
export const detectThrottling = async (functionName: string, context: any) => {
const remainingTime = context.getRemainingTimeInMillis();
const duration = context.logStreamName; // Execution environment info içerir
// Throttle edilmiş ortamda çalışıp çalışmadığını tespit et
if (remainingTime < 1000) {
await cloudwatch.putMetricData({
Namespace: 'Lambda/Performance',
MetricData: [{
MetricName: 'NearTimeout',
Value: 1,
Unit: 'Count',
Dimensions: [
{ Name: 'FunctionName', Value: functionName },
{ Name: 'RemainingTime', Value: remainingTime.toString() }
]
}]
});
}
};
Error Handling ve Dead Letter Queue’lar
Stratejik Error Handling
// Daha iyi debugging için error kategorilendirmesi
export enum ErrorCategory {
TRANSIENT = 'TRANSIENT', // Retry mantıklı
CLIENT_ERROR = 'CLIENT_ERROR', // Kullanıcı input sorunu
SYSTEM_ERROR = 'SYSTEM_ERROR', // Altyapı problemi
BUSINESS_ERROR = 'BUSINESS_ERROR' // Business logic ihlali
}
export class CategorizedError extends Error {
constructor(
message: string,
public category: ErrorCategory,
public retryable: boolean = false,
public context?: any
) {
super(message);
this.name = 'CategorizedError';
}
}
export const handleError = async (error: Error, event: any, context: any) => {
const log = createContextLogger(context, event);
if (error instanceof CategorizedError) {
// Kategorilere göre error handle et
switch (error.category) {
case ErrorCategory.TRANSIENT:
log.info('Transient error - will retry', {
error: error.message,
retryable: error.retryable
});
throw error; // Lambda retry mekanizmasının handle etmesini sağla
case ErrorCategory.CLIENT_ERROR:
log.info('Client error - no retry needed', { error: error.message });
return {
statusCode: 400,
body: JSON.stringify({ error: 'Invalid request' })
};
case ErrorCategory.SYSTEM_ERROR:
log.error('System error detected', error, {
requiresInvestigation: true
});
// İnceleme için DLQ'ya gönder
throw error;
case ErrorCategory.BUSINESS_ERROR:
log.business('business-rule-violation', {
rule: error.message,
context: error.context
});
return {
statusCode: 422,
body: JSON.stringify({ error: error.message })
};
}
} else {
// Bilinmeyen error - system error olarak ele al
log.error('Uncategorized error', error);
throw new CategorizedError(
error.message,
ErrorCategory.SYSTEM_ERROR,
false,
{ originalError: error.stack }
);
}
};
Dead Letter Queue Analizi
// Error pattern analizi için DLQ processor
export const dlqProcessor = async (event: any, context: any) => {
const log = createContextLogger(context, event);
for (const record of event.Records) {
try {
const failedEvent = JSON.parse(record.body);
const errorInfo = {
functionName: record.eventSourceARN?.split(':')[6],
errorCount: record.attributes?.ApproximateReceiveCount || '1',
failureReason: record.attributes?.DeadLetterReason || 'unknown',
originalTimestamp: failedEvent.timestamp,
retryCount: parseInt(record.attributes?.ApproximateReceiveCount || '0')
};
// Pattern detection
if (errorInfo.retryCount > 3) {
log.business('recurring-failure-pattern', {
pattern: 'high-retry-count',
functionName: errorInfo.functionName,
suggestion: 'investigate-configuration'
});
}
// Analiz için sakla
await storeErrorPattern(errorInfo, failedEvent);
} catch (processingError) {
log.error('Failed to process DLQ record', processingError as Error);
}
}
};
İleri Seviye Debugging Teknikleri
Lambda Function URL Debugging
// Production troubleshooting için debug endpoint
export const debugHandler = async (event: any, context: any) => {
// Sadece non-production'da veya özel header ile izin ver
const allowDebug = process.env.STAGE !== 'prod' ||
event.headers?.['x-debug-token'] === process.env.DEBUG_TOKEN;
if (!allowDebug) {
return { statusCode: 403, body: 'Debug access denied' };
}
const debugInfo = {
environment: {
stage: process.env.STAGE,
region: context.invokedFunctionArn.split(':')[3],
memorySize: context.memoryLimitInMB,
timeout: context.remainingTimeInMillis
},
runtime: {
nodeVersion: process.version,
platform: process.platform,
uptime: process.uptime()
},
lastErrors: await getRecentErrors(context.functionName),
healthChecks: {
database: await checkDatabaseConnection(),
externalAPI: await checkExternalServices(),
memory: process.memoryUsage()
}
};
return {
statusCode: 200,
body: JSON.stringify(debugInfo, null, 2)
};
};
Production’da Güvenli Performance Profiling
// Güvenli production profiling
export const profileHandler = (originalHandler: Function) => {
return async (event: any, context: any) => {
const shouldProfile = Math.random() < 0.01; // Request'lerin %1'ini profile et
if (!shouldProfile) {
return originalHandler(event, context);
}
const startTime = Date.now();
const startMemory = process.memoryUsage();
try {
const result = await originalHandler(event, context);
const endTime = Date.now();
const endMemory = process.memoryUsage();
// Profiling datasını gönder
await cloudwatch.putMetricData({
Namespace: 'Lambda/Profiling',
MetricData: [
{
MetricName: 'ExecutionDuration',
Value: endTime - startTime,
Unit: 'Milliseconds'
},
{
MetricName: 'MemoryUsed',
Value: endMemory.heapUsed - startMemory.heapUsed,
Unit: 'Bytes'
}
]
});
return result;
} catch (error) {
// Error senaryolarını da profile et
const errorTime = Date.now();
await cloudwatch.putMetricData({
Namespace: 'Lambda/Profiling',
MetricData: [{
MetricName: 'ErrorDuration',
Value: errorTime - startTime,
Unit: 'Milliseconds'
}]
});
throw error;
}
};
};
Troubleshooting Workflow’ları
5 Dakikalık Debug Protokolü
Peak trafik sırasında işler ters gittiğinde, sistematik bir yaklaşıma ihtiyacın var:
// Acil durum debug checklist'i
export const emergencyDebugChecklist = {
step1_quickHealth: async (functionName: string) => {
const metrics = await cloudwatch.getMetricStatistics({
Namespace: 'AWS/Lambda',
MetricName: 'Errors',
Dimensions: [{ Name: 'FunctionName', Value: functionName }],
StartTime: new Date(Date.now() - 10 * 60 * 1000), // Son 10 dakika
EndTime: new Date(),
Period: 300,
Statistics: ['Sum']
});
return {
recentErrors: metrics.Datapoints?.reduce((sum, dp) => sum + (dp.Sum || 0), 0),
timeframe: 'last-10-minutes'
};
},
step2_checkDependencies: async () => {
return {
database: await checkDatabaseConnection(),
externalAPIs: await checkExternalServices(),
downstream: await checkDownstreamServices()
};
},
step3_analyzeLogs: async (functionName: string) => {
// Son hatalar için CloudWatch Logs Insights query
const query = `
fields @timestamp, @message, @requestId
| filter @message like /ERROR/ or @message like /TIMEOUT/
| sort @timestamp desc
| limit 20
`;
// Implementation CloudWatch Logs API kullanacak
return { recentErrorPatterns: 'implementation-needed' };
}
};
Memory Leak Detection
// Uzun çalışan Lambda container'larında memory leak tespiti
let requestCount = 0;
const memorySnapshots: Array<{ count: number; memory: NodeJS.MemoryUsage }> = [];
export const memoryTrackingWrapper = (handler: Function) => {
return async (event: any, context: any) => {
requestCount++;
const beforeMemory = process.memoryUsage();
const result = await handler(event, context);
const afterMemory = process.memoryUsage();
// Request'ler boyunca memory artışını takip et
if (requestCount % 10 === 0) {
memorySnapshots.push({ count: requestCount, memory: afterMemory });
if (memorySnapshots.length > 10) {
const oldSnapshot = memorySnapshots[memorySnapshots.length - 10];
const currentSnapshot = memorySnapshots[memorySnapshots.length - 1];
const heapGrowth = currentSnapshot.memory.heapUsed - oldSnapshot.memory.heapUsed;
if (heapGrowth > 50 * 1024 * 1024) { // 50MB artış
await cloudwatch.putMetricData({
Namespace: 'Lambda/MemoryLeak',
MetricData: [{
MetricName: 'SuspectedMemoryLeak',
Value: heapGrowth,
Unit: 'Bytes',
Dimensions: [
{ Name: 'FunctionName', Value: context.functionName }
]
}]
});
}
}
}
return result;
};
};
Maliyete Duyarlı Monitoring
Yüksek Hacimli Function’lar için Sampling Stratejisi
// Business değerine dayalı akıllı sampling
export const createSampler = (baseSampleRate: number = 0.01) => {
return (event: any): boolean => {
// Her zaman error'ları sample et
if (event.errorType) return true;
// Her zaman yüksek değerli transaction'ları sample et
if (event.transactionValue > 1000) return true;
// Yeni kullanıcıları daha sık sample et
if (event.userType === 'new') return Math.random() < baseSampleRate * 5;
// Normal sampling
return Math.random() < baseSampleRate;
};
};
const sampler = createSampler(0.005); // %0.5 base rate
export const handler = async (event: any, context: any) => {
const shouldMonitor = sampler(event);
if (shouldMonitor) {
// Tam monitoring ve tracing
return AWSXRay.captureAsyncFunc('handler', async () => {
return processWithFullLogging(event, context);
});
} else {
// Minimal monitoring
return processWithBasicLogging(event, context);
}
};
Log Retention Stratejisi
# Log önemine göre farklı retention süresi
Resources:
BusinessLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${BusinessProcessorFunction}"
RetentionInDays: 90 # Business log'ları daha uzun tut
DebugLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${UtilityFunction}"
RetentionInDays: 7 # Debug log'ları daha kısa olabilir
Sırada: Advanced Pattern’ler ve Maliyet Optimizasyonu
Bu serinin son bölümünde, hem karmaşıklığı hem de maliyetleri azaltan advanced Lambda pattern’leri keşfedeceğiz:
- Multi-tenant mimari pattern’leri
- Event-driven maliyet optimizasyonu
- Advanced deployment stratejileri
- Performance vs maliyet trade-off’ları
Önemli Çıkarımlar
- Teknik değil business metrikler monitor et: Alert’lerin business etkisini yansıtması gerek
- Log’larınızı aranabilir şekilde yapılandırın: JSON log’lar ve consistent field’lar debugging süresinden tasarruf sağlar
- X-Ray’i stratejik kullan: Tam tracing her zaman gerekli değil, ama contextual tracing paha biçilmez
- Sisteminize debugging araçları gömün: Debug endpoint’leri ve profiling wrapper’ları kendilerini amorti eder
- Alert’lerinizi development’da test edin: False positive’ler ekibin monitoring’e güvenini sarsıyor
En iyi monitoring sistemi, müşterilerden önce problemleri size söyleyen sistem. Observability’ye erken yatırım yapın - alternatifinden çok daha ucuz.
AWS Lambda Production Rehberi: 5 Yıllık Gerçek Dünya Deneyimi
5+ yıllık production deneyimine dayalı kapsamlı AWS Lambda rehberi. Cold start optimizasyonu, performans ayarlama, monitoring ve maliyet optimizasyonu ile gerçek savaş hikayeleri ve pratik çözümler.
Serideki tüm yazılar
İlgili yazılar
Yeşil ışıklarla dolu dashboard'lardan, dağıtık izleme ve AI destekli analiz ile sistem davranışı, kullanıcı yolculukları ve iş etkisi hakkında etkileyici hikayeler anlatan observability sistemlerine geçiş
Yüksek riskli üretim ortamlarında bildirim sistemi hatalarından edinilen gerçek dünya debugging teknikleri, izleme stratejileri ve dersler
Production ortamlarından öğrenilen AWS Lambda cold start optimizasyon stratejileri. Runtime seçimi, provisioned concurrency ve pratik optimizasyon teknikleri.
Lambda Layers, VPC konfigürasyonu, cross-account execution ve kapsamlı maliyet optimizasyon stratejileri dahil advanced AWS Lambda pattern'lerinde ustalaşın. Production Lambda kullanımından gerçek dünya migration deneyimleri ve mimari kararlar.
DI container'lar, monolitik SDK'lar, god-handler'lar, modül üstü secret çağrıları ve ağır ORM'ler - soğuk başlatmada bedeli ve yerine geçen fonksiyonel yapı.