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ırmanın beş yılında öğrendiğim en önemli ders şu: 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:

TypeScript
// Bizi sayısız kez kurtaran custom metrikler
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:

TypeScript
import AWSXRay from 'aws-xray-sdk-core';
import AWS from 'aws-sdk';

// AWS SDK'yi instrument et
const dynamoDB = AWSXRay.captureAWSClient(new AWS.DynamoDB.DocumentClient());

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:

TypeScript
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ı#

Business Metrikleri için Executive Dashboard#

Board sunumu sırasında CEO'muz sistem sağlığını sordu. Teknik metrikler göstermek yerine, bu dashboard'ı açtım:

YAML
# 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#

YAML
  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:

YAML
# 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#

TypeScript
// 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#

TypeScript
// 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#

TypeScript
// 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#

TypeScript
// 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#

TypeScript
// 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:

TypeScript
// 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#

TypeScript
// 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#

TypeScript
// 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#

YAML
# 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#

  1. Teknik değil business metrikler monitor et: Alert'lerin business etkisini yansıtması gerek
  2. Log'larınızı aranabilir şekilde yapılandırın: JSON log'lar ve consistent field'lar debugging süresinden tasarruf sağlar
  3. X-Ray'i stratejik kullan: Tam tracing her zaman gerekli değil, ama contextual tracing paha biçilmez
  4. Sisteminize debugging araçları gömün: Debug endpoint'leri ve profiling wrapper'ları kendilerini amorti eder
  5. 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.

İlerleme3/4 yazı tamamlandı
Loading...

Yorumlar (0)

Sohbete katıl

Düşüncelerini paylaşmak ve toplulukla etkileşim kurmak için giriş yap

Henüz yorum yok

Bu yazı hakkında ilk düşüncelerini paylaşan sen ol!

Related Posts