Sentry Error Tracking Beispiele

Umfassende Sentry Error Tracking und Performance Monitoring Setup mit SDK-Integrationen für verschiedene Plattformen

💻 Sentry JavaScript SDK Setup javascript

🟡 intermediate ⭐⭐⭐

Vollständige JavaScript SDK Konfiguration für Browser und Node.js Anwendungen mit Error Tracking und Performance Monitoring

⏱️ 30 min 🏷️ sentry, javascript, react, node.js, monitoring, error-tracking
Prerequisites: JavaScript/Node.js, npm, Sentry account
// Sentry JavaScript SDK Configuration
// Install: npm install @sentry/browser @sentry/node @sentry/tracing

// Frontend Browser Setup
// sentry-browser.js
import * as Sentry from "@sentry/browser";
import { BrowserTracing } from "@sentry/tracing";

// Initialize Sentry for browser
Sentry.init({
  dsn: "https://[email protected]/project-id",

  // Environment
  environment: process.env.NODE_ENV || "development",

  // Release version
  release: `my-app@${process.env.APP_VERSION || "1.0.0"}`,

  // Performance monitoring
  integrations: [
    new BrowserTracing({
      // Set custom tracing options
      routingInstrumentation: Sentry.reactRouterV6Instrumentation(
        React.useEffect,
        useLocation,
        useNavigationType,
        createRoutesFromChildren,
        matchRoutes
      ),
    }),
  ],

  // Performance settings
  tracesSampleRate: 1.0, // Capture 100% of transactions for testing

  // Error sampling
  sampleRate: 1.0, // Capture 100% of errors

  // Before send callback for error filtering/enhancement
  beforeSend(event, hint) {
    // Add custom context
    event.contexts = {
      ...event.contexts,
      app: {
        name: "My Web App",
        version: process.env.APP_VERSION,
        buildNumber: process.env.BUILD_NUMBER
      },
      browser: {
        cookiesEnabled: navigator.cookieEnabled,
        language: navigator.language,
        online: navigator.onLine,
        platform: navigator.platform,
        userAgent: navigator.userAgent
      }
    };

    // Filter out certain errors
    if (event.exception) {
      const error = hint.originalException;

      // Ignore non-critical errors
      if (error.message && error.message.includes("Non-critical")) {
        return null;
      }

      // Add custom tags
      event.tags = {
        ...event.tags,
        component: "frontend",
        userId: getCurrentUserId()
      };
    }

    return event;
  },

  // Debug mode for development
  debug: process.env.NODE_ENV === "development",

  // Enable user feedback dialog
  beforeSend(event, hint) {
    // Check if it's an exception
    if (event.exception) {
      event.event_id = Sentry.showReportDialog({
        eventId: event.event_id,
        title: "Something went wrong",
        subtitle: "Our team has been notified. If you'd like to help, tell us what happened below.",
        subtitle2: "If you prefer not to share this information, you can just close this dialog.",
        labelName: "Name",
        labelEmail: "Email",
        labelComments: "What happened?",
        labelClose: "Close",
        labelSubmit: "Submit",
        errorGeneric: "An unknown error occurred while submitting your report. Please try again.",
        errorFormEntry: "Some fields were invalid. Please correct the errors and try again.",
        successMessage: "Your feedback has been sent. Thank you!"
      });
    }
    return event;
  },

  // User context (will be updated dynamically)
  initialScope: {
    tags: {
      section: "main"
    },
    user: {
      id: "anonymous"
    }
  }
});

// Set user context dynamically
function setUserContext(user) {
  Sentry.setUser({
    id: user.id,
    email: user.email,
    username: user.username,
    ip_address: "{{auto}}"
  });
}

// Clear user context
function clearUserContext() {
  Sentry.setUser(null);
}

// Add custom tags
function addTags(tags) {
  Sentry.configureScope((scope) => {
    Object.entries(tags).forEach(([key, value]) => {
      scope.setTag(key, value);
    });
  });
}

// Add breadcrumbs for user actions
function trackUserAction(action, data = {}) {
  Sentry.addBreadcrumb({
    category: "user",
    message: action,
    level: "info",
    data: data
  });
}

// Manual error capture with context
function captureError(error, context = {}) {
  Sentry.withScope((scope) => {
    // Add context data
    Object.entries(context).forEach(([key, value]) => {
      scope.setContext(key, value);
    });

    // Add tags
    if (context.tags) {
      Object.entries(context.tags).forEach(([key, value]) => {
        scope.setTag(key, value);
      });
    }

    // Capture the error
    Sentry.captureException(error);
  });
}

// Performance monitoring
function startTransaction(name, operation = "navigation") {
  return Sentry.startTransaction({
    name: name,
    operation: operation,
  });
}

// Custom performance example
function performExpensiveOperation() {
  const transaction = Sentry.startTransaction({
    name: "expensive-operation",
    operation: "task"
  });

  try {
    // Step 1: Data processing
    const dataProcessingSpan = transaction.startChild({
      op: "processing",
      description: "Processing user data"
    });

    // Simulate data processing
    const processedData = processUserData(getUserData());
    dataProcessingSpan.finish();

    // Step 2: API call
    const apiCallSpan = transaction.startChild({
      op: "http.client",
      description: "API call to external service"
    });

    return fetch('/api/process', {
      method: 'POST',
      body: JSON.stringify(processedData)
    }).then(response => {
      apiCallSpan.setData("statusCode", response.status);
      apiCallSpan.finish();

      if (!response.ok) {
        throw new Error(`API call failed: ${response.status}`);
      }

      return response.json();
    });

  } catch (error) {
    Sentry.captureException(error);
    throw error;
  } finally {
    transaction.finish();
  }
}

// React Error Boundary component
import React from "react";

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    Sentry.withScope((scope) => {
      scope.setContext("react", {
        componentStack: errorInfo.componentStack
      });

      scope.setTag("react", "error-boundary");

      Sentry.captureException(error);
    });
  }

  render() {
    if (this.state.hasError) {
      return (
        <div>
          <h1>Something went wrong.</h1>
          <p>We've been notified about this issue.</p>
          <button onClick={() => this.setState({ hasError: false })}>
            Try again
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// Backend Node.js Setup
// sentry-server.js
const Sentry = require("@sentry/node");
const { nodeprofilerIntegration } = require("@sentry/profiling-node");

// Initialize Sentry for Node.js
Sentry.init({
  dsn: "https://[email protected]/project-id",

  environment: process.env.NODE_ENV || "development",
  release: `my-api@${process.env.APP_VERSION || "1.0.0"}`,

  // Performance monitoring
  integrations: [
    // Add HTTP request tracking
    new Sentry.Integrations.Http({ tracing: true }),

    // Express.js integration
    new Sentry.Integrations.Express({ app }),

    // Profiling integration
    nodeprofilerIntegration()
  ],

  // Performance settings
  tracesSampleRate: 0.1, // Capture 10% of transactions

  // Profiling
  _experiments: {
    // The sampling rate for profiling is relative to tracesSampleRate
    profilesSampleRate: 0.1,
  },

  // Before send callback for server errors
  beforeSend(event, hint) {
    // Add server context
    event.contexts = {
      ...event.contexts,
      server: {
        name: process.env.HOSTNAME || "unknown",
        pid: process.pid,
        platform: process.platform,
        nodeVersion: process.version
      }
    };

    // Sanitize sensitive data
    if (event.request && event.request.headers) {
      // Remove sensitive headers
      delete event.request.headers.authorization;
      delete event.request.headers.cookie;
      delete event.request.headers["x-api-key"];
    }

    // Add user context if available in the request
    if (hint && hint.request && hint.request.user) {
      event.user = {
        id: hint.request.user.id,
        email: hint.request.user.email
      };
    }

    return event;
  },

  // Custom event processor
  eventProcessor(event) {
    // Filter out health check errors
    if (event.request && event.request.url && event.request.url.includes("/health")) {
      return null;
    }
    return event;
  }
});

// Express.js middleware setup
const express = require("express");
const app = express();

// Request handler
app.use(Sentry.Handlers.requestHandler());

// Tracing handler
app.use(Sentry.Handlers.tracingHandler());

// Example route with custom monitoring
app.get("/api/users/:id", async (req, res) => {
  try {
    // Add request context
    Sentry.configureScope((scope) => {
      scope.setTag("route", "/api/users/:id");
      scope.setTag("method", "GET");
      scope.setContext("request_params", {
        userId: req.params.id,
        query: req.query
      });
    });

    // Add breadcrumb
    Sentry.addBreadcrumb({
      category: "http",
      message: `Fetching user ${req.params.id}`,
      level: "info"
    });

    const user = await database.findUserById(req.params.id);

    if (!user) {
      Sentry.captureMessage(`User ${req.params.id} not found`, "warning");
      return res.status(404).json({ error: "User not found" });
    }

    // Set user context
    Sentry.setUser({ id: user.id, email: user.email });

    res.json({ user });

  } catch (error) {
    // Capture error with additional context
    Sentry.withScope((scope) => {
      scope.setContext("database_error", {
        operation: "findUserById",
        userId: req.params.id,
        error_code: error.code
      });

      Sentry.captureException(error);
    });

    res.status(500).json({ error: "Internal server error" });
  }
});

// Error handler
app.use(Sentry.Handlers.errorHandler());

// Fallback error handler
app.use(function onError(err, req, res, next) {
  res.statusCode = 500;
  res.end(res.sentry + "\n");
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);

  // Record deployment
  Sentry.captureMessage("Application started", "info");
});

💻 Sentry Python SDK Integration python

🟡 intermediate ⭐⭐⭐

Vollständige Python SDK Setup für Django, Flask und FastAPI Anwendungen mit Error Tracking und Performance Monitoring

⏱️ 35 min 🏷️ sentry, python, django, flask, fastapi, monitoring
Prerequisites: Python, Django/Flask/FastAPI, pip, Sentry account
# Sentry Python SDK Configuration
# Install: pip install sentry-sdk[django,flask,fastapi]

import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.redis import RedisIntegration
import logging

# Common configuration
SENTRY_DSN = "https://[email protected]/project-id"
ENVIRONMENT = "production"
RELEASE = "[email protected]"

# Configure sentry_sdk with common settings
def configure_sentry(integrations=None):
    """Configure Sentry SDK with common settings"""

    # Default integrations
    default_integrations = [
        SqlalchemyIntegration(),
        RedisIntegration(),
    ]

    if integrations:
        default_integrations.extend(integrations)

    sentry_sdk.init(
        dsn=SENTRY_DSN,
        environment=ENVIRONMENT,
        release=RELEASE,

        # Performance monitoring
        traces_sample_rate=0.1,

        # Error monitoring
        sample_rate=1.0,

        # Integrations
        integrations=default_integrations,

        # Before send callback for error filtering
        before_send=before_send_filter,

        # Before breadcrumb callback
        before_breadcrumb=before_breadcrumb_filter,

        # Ignore specific exceptions
        ignore_errors=[
            KeyboardInterrupt,
            SystemExit,
            "django.http.request.DisallowedHost",
        ],

        # Server name
        server_name=get_server_name(),

        # Max breadcrumbs
        max_breadcrumbs=100,

        # Attach stack traces
        attach_stacktrace=True,
    )

def before_send_filter(event, hint):
    """Filter and enhance error events"""

    # Add custom context
    event["contexts"] = {
        **event.get("contexts", {}),
        "app": {
            "version": RELEASE,
            "environment": ENVIRONMENT,
            "process_id": os.getpid(),
        },
        "system": {
            "platform": sys.platform,
            "python_version": sys.version,
        }
    }

    # Add user context if available
    if hasattr(g, 'user') and g.user:
        event["user"] = {
            "id": g.user.id,
            "email": g.user.email,
            "username": g.user.username,
        }

    # Sanitize sensitive data
    if "request" in event:
        sanitize_request_data(event["request"])

    # Filter out health checks
    if event.get("request", {}).get("url", "").endswith("/health/"):
        return None

    return event

def before_breadcrumb_filter(breadcrumb, hint):
    """Filter breadcrumb data"""

    # Sanitize breadcrumb data
    if breadcrumb.get("category") == "http":
        data = breadcrumb.get("data", {})
        if "url" in data:
            # Remove query parameters from URLs
            data["url"] = data["url"].split("?")[0]

    return breadcrumb

def sanitize_request_data(request):
    """Remove sensitive data from request"""
    sensitive_headers = ["authorization", "cookie", "x-api-key"]
    sensitive_params = ["password", "token", "secret", "key"]

    # Remove sensitive headers
    if "headers" in request:
        for header in sensitive_headers:
            request["headers"].pop(header, None)

    # Remove sensitive query parameters
    if "query_string" in request:
        query_string = request["query_string"]
        for param in sensitive_params:
            query_string = re.sub(f"{param}=[^&]*", f"{param}=[FILTERED]", query_string, flags=re.IGNORECASE)
        request["query_string"] = query_string

# Django Integration
# sentry_django.py
from django.conf import settings

# Configure Sentry for Django
configure_sentry([
    DjangoIntegration(
        transaction_style="url",
        middleware_spans=True,
        signals_spans=True,
    ),
    CeleryIntegration(
        monitor_beat_tasks=True,
        propagate_traces=True,
    ),
])

# Custom middleware for additional context
class SentryContextMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Add request context
        with sentry_sdk.configure_scope() as scope:
            scope.set_tag("request_id", get_request_id(request))
            scope.set_context("request", {
                "method": request.method,
                "path": request.path,
                "user_agent": request.META.get("HTTP_USER_AGENT"),
                "remote_addr": request.META.get("REMOTE_ADDR"),
            })

            # Add user context if authenticated
            if hasattr(request, 'user') and request.user.is_authenticated:
                scope.set_user({
                    "id": request.user.id,
                    "email": request.user.email,
                    "username": request.user.username,
                })

            # Add additional business context
            if hasattr(request, 'organization'):
                scope.set_tag("organization", request.organization.id)
                scope.set_context("organization", {
                    "id": request.organization.id,
                    "name": request.organization.name,
                })

        response = self.get_response(request)
        return response

# Add to Django MIDDLEWARE
MIDDLEWARE = [
    "path.to.SentryContextMiddleware",
    # ... other middleware
]

# Django views with custom Sentry usage
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
import sentry_sdk

@require_http_methods(["GET"])
def user_profile(request, user_id):
    """User profile view with Sentry instrumentation"""

    # Add custom breadcrumb
    sentry_sdk.add_breadcrumb(
        category="auth",
        message=f"Accessing user profile: {user_id}",
        level="info",
        data={"user_id": user_id}
    )

    try:
        # Custom transaction for profile loading
        with sentry_sdk.start_transaction(
            name="user_profile_load",
            op="view",
            data={"user_id": user_id}
        ):

            # Database operation
            with sentry_sdk.start_span(op="db", description="Fetch user from database"):
                user = User.objects.get(id=user_id)

            # Additional data fetching
            with sentry_sdk.start_span(op="db", description="Fetch user profile data"):
                profile = UserProfile.objects.get(user=user)

            # Add context
            with sentry_sdk.configure_scope() as scope:
                scope.set_context("user_profile", {
                    "user_id": user.id,
                    "profile_complete": profile.is_complete,
                    "last_login": user.last_login,
                })
                scope.set_tag("user_type", user.user_type)

            return JsonResponse({
                "user": {
                    "id": user.id,
                    "name": user.get_full_name(),
                    "email": user.email,
                    "profile": profile.to_dict()
                }
            })

    except User.DoesNotExist:
        # Capture warning for missing user
        sentry_sdk.capture_message(
            f"User {user_id} not found",
            level="warning",
            tags={"endpoint": "user_profile", "action": "not_found"}
        )
        return JsonResponse({"error": "User not found"}, status=404)

    except Exception as e:
        # Capture exception with context
        sentry_sdk.capture_exception(e)
        with sentry_sdk.configure_scope() as scope:
            scope.set_context("error_details", {
                "user_id": user_id,
                "endpoint": "user_profile",
                "error_type": type(e).__name__,
            })
        return JsonResponse({"error": "Internal server error"}, status=500)

# Flask Integration
# sentry_flask.py
from flask import Flask, request, g
import sentry_sdk

# Configure Sentry for Flask
configure_sentry([
    FlaskIntegration(
        transaction_style="url",
    ),
])

app = Flask(__name__)

@app.before_request
def add_request_context():
    """Add request context to Sentry"""
    with sentry_sdk.configure_scope() as scope:
        scope.set_tag("request_id", generate_request_id())
        scope.set_context("request", {
            "method": request.method,
            "path": request.path,
            "user_agent": request.headers.get("User-Agent"),
            "remote_addr": request.remote_addr,
        })

@app.route("/api/orders", methods=["GET", "POST"])
def handle_orders():
    """Order handling with Sentry instrumentation"""

    if request.method == "GET":
        return get_orders()
    else:
        return create_order()

def get_orders():
    """Get orders with custom monitoring"""

    # Add breadcrumb
    sentry_sdk.add_breadcrumb(
        category="orders",
        message="Fetching orders",
        level="info"
    )

    try:
        # Custom transaction
        with sentry_sdk.start_transaction(name="order_retrieval", op="http.server"):

            # Database operation
            with sentry_sdk.start_span(op="db.query", description="Fetch orders"):
                orders = Order.query.filter_by(user_id=g.user.id).all()

            # Add metrics
            with sentry_sdk.configure_scope() as scope:
                scope.set_tag("order_count", len(orders))
                scope.set_metric("orders_retrieved", len(orders))

            return jsonify({
                "orders": [order.to_dict() for order in orders]
            })

    except DatabaseError as e:
        # Capture with specific context
        sentry_sdk.capture_exception(e)
        with sentry_sdk.configure_scope() as scope:
            scope.set_context("database_error", {
                "query_type": "SELECT",
                "table": "orders",
                "user_id": getattr(g, 'user', {}).get('id'),
            })
        raise

def create_order():
    """Create order with monitoring"""

    order_data = request.get_json()

    # Validate input
    if not order_data or "items" not in order_data:
        sentry_sdk.capture_message(
            "Invalid order data received",
            level="warning",
            extra={"data": order_data}
        )
        return jsonify({"error": "Invalid order data"}), 400

    try:
        with sentry_sdk.start_transaction(name="order_creation", op="business") as transaction:

            # Add order context
            transaction.set_data("order_value", calculate_order_value(order_data))
            transaction.set_tag("payment_method", order_data.get("payment_method"))

            # Create order
            with sentry_sdk.start_span(op="db.insert", description="Create order"):
                order = Order.create(order_data)

            # Process payment
            with sentry_sdk.start_span(op="external", description="Process payment"):
                payment_result = process_payment(order)

            # Record custom event
            sentry_sdk.capture_message(
                f"Order created successfully: {order.id}",
                level="info",
                tags={
                    "order_id": order.id,
                    "user_id": order.user_id,
                    "payment_method": order_data.get("payment_method")
                }
            )

            return jsonify({
                "order_id": order.id,
                "status": "created"
            }), 201

    except PaymentError as e:
        # Capture payment error
        sentry_sdk.capture_exception(e)
        with sentry_sdk.configure_scope() as scope:
            scope.set_context("payment_error", {
                "order_data": sanitize_order_data(order_data),
                "error_code": e.code,
                "gateway": e.gateway,
            })
        return jsonify({"error": "Payment failed"}), 400

# FastAPI Integration
# sentry_fastapi.py
from fastapi import FastAPI, Request, Depends
import sentry_sdk

# Configure Sentry for FastAPI
configure_sentry([
    FastApiIntegration(
        transaction_style="url",
    ),
])

app = FastAPI()

@app.middleware("http")
async def add_sentry_context(request: Request, call_next):
    """Add request context to Sentry"""
    with sentry_sdk.configure_scope() as scope:
        scope.set_tag("request_id", generate_request_id())
        scope.set_context("request", {
            "method": request.method,
            "path": request.url.path,
            "user_agent": request.headers.get("user-agent"),
            "client_ip": request.client.host if request.client else None,
        })

    response = await call_next(request)
    return response

# Celery Integration
# sentry_celery.py
from celery import Celery
import sentry_sdk

celery_app = Celery('tasks')

@celery_app.task
def send_email_notification(user_id, message_data):
    """Send email notification with Sentry monitoring"""

    with sentry_sdk.start_transaction(name="send_email_notification", op="task"):

        try:
            # Add context
            with sentry_sdk.configure_scope() as scope:
                scope.set_tag("task", "email_notification")
                scope.set_tag("user_id", user_id)
                scope.set_tag("message_type", message_data.get("type"))

            # Fetch user data
            with sentry_sdk.start_span(op="db.query", description="Fetch user"):
                user = User.objects.get(id=user_id)

            # Send email
            with sentry_sdk.start_span(op="external", description="Send email"):
                result = email_service.send(user.email, message_data)

            # Record success
            sentry_sdk.add_breadcrumb(
                category="celery",
                message=f"Email sent successfully to {user.email}",
                level="info"
            )

            return {"status": "success", "email": user.email}

        except Exception as e:
            # Capture error with task context
            sentry_sdk.capture_exception(e)
            with sentry_sdk.configure_scope() as scope:
                scope.set_context("celery_task", {
                    "task_name": "send_email_notification",
                    "user_id": user_id,
                    "message_type": message_data.get("type"),
                    "retry_count": send_email_notification.request.retries,
                })
            raise

# Custom performance monitoring
def monitor_slow_operation(operation_name, func, *args, **kwargs):
    """Monitor slow operations with Sentry"""

    with sentry_sdk.start_transaction(name=operation_name, op="custom") as transaction:
        try:
            result = func(*args, **kwargs)

            # Record operation metrics
            duration = transaction.finish_timestamp - transaction.start_timestamp
            with sentry_sdk.configure_scope() as scope:
                scope.set_metric(f"{operation_name}_duration", duration)

            return result

        except Exception as e:
            # Capture with operation context
            sentry_sdk.capture_exception(e)
            with sentry_sdk.configure_scope() as scope:
                scope.set_context("operation", {
                    "name": operation_name,
                    "args_count": len(args),
                    "kwargs": list(kwargs.keys()),
                })
            raise

# Usage example
def process_large_dataset(dataset):
    """Example function with custom monitoring"""

    def _process_data(data):
        # Actual processing logic
        return [item.upper() for item in data]

    return monitor_slow_operation("data_processing", _process_data, dataset)

💻 Sentry Performance Monitoring javascript

🔴 complex ⭐⭐⭐⭐

Erweitertes Performance Monitoring mit benutzerdefinierten Transaktionen, Spans und Metriken für tiefgehende Application Insights

⏱️ 50 min 🏷️ sentry, performance, tracing, monitoring, profiling
Prerequisites: JavaScript, Sentry SDK, Performance concepts
// Sentry Performance Monitoring Advanced Setup
// Install: npm install @sentry/tracing @sentry/browser

import * as Sentry from "@sentry/browser";
import { BrowserTracing } from "@sentry/tracing";

// Enhanced performance configuration
Sentry.init({
  dsn: "https://[email protected]/project-id",

  // Environment and release
  environment: process.env.NODE_ENV,
  release: `my-app@${process.env.APP_VERSION}`,

  // Performance integrations
  integrations: [
    new BrowserTracing({
      // Custom routing instrumentation
      routingInstrumentation: (customRoutingInstrumentation) => ({
        // Custom implementation
        registerNavigationInstrumentation: () => {
          // Track route changes
          history.listen(({ location }) => {
            const name = location.pathname + location.search;
            Sentry.startTransaction({
              name: name,
              op: "navigation"
            }).finish();
          });
        }
      }),

      // Enable user interaction tracing
      userInteractionTracing: {
        enabled: true,
        eventTargets: [document.body],
        keypressThresholdMs: 1000,
        scrollTimeoutMs: 100
      },

      // Enable long task tracking
      enableLongTask: true,

      // Enable first paint/first contentful paint
      enableFirstPaint: true
    })
  ],

  // Performance sampling
  tracesSampleRate: calculateSampleRate(),

  // Custom transaction context
  initialScope: {
    tags: {
      section: "main",
      platform: "web"
    }
  }
});

// Dynamic sampling rate based on user
function calculateSampleRate() {
  const user = getCurrentUser();
  if (!user) return 0.1; // 10% for anonymous users

  // Higher sampling for paid users
  if (user.subscription === "premium") return 0.5; // 50%
  if (user.subscription === "pro") return 0.2; // 20%

  return 0.05; // 5% for free users
}

// Custom transaction wrapper
class PerformanceTracker {
  constructor(name, operation = "custom") {
    this.transaction = Sentry.startTransaction({
      name: name,
      operation: operation,
      data: {
        page_url: window.location.href,
        user_agent: navigator.userAgent,
        timestamp: Date.now()
      }
    });

    this.spans = new Map();
  }

  // Start a new span
  startSpan(name, operation = "custom", data = {}) {
    const span = this.transaction.startChild({
      op: operation,
      description: name,
      data: data
    });

    this.spans.set(name, span);
    return span;
  }

  // Finish a specific span
  finishSpan(name) {
    const span = this.spans.get(name);
    if (span) {
      span.finish();
      this.spans.delete(name);
      return true;
    }
    return false;
  }

  // Add data to transaction
  setData(key, value) {
    this.transaction.setData(key, value);
  }

  // Add tag to transaction
  setTag(key, value) {
    this.transaction.setTag(key, value);
  }

  // Finish the transaction
  finish() {
    // Finish any remaining spans
    this.spans.forEach(span => span.finish());

    // Add final metrics
    this.transaction.setData("total_duration",
      Date.now() - this.transaction.startTimestamp);

    this.transaction.finish();
  }
}

// Example usage: API request monitoring
class APIMonitor {
  constructor(baseURL) {
    this.baseURL = baseURL;
  }

  async request(method, endpoint, options = {}) {
    const tracker = new PerformanceTracker(
      `api-${method.toLowerCase()}-${endpoint}`,
      "http.client"
    );

    try {
      // Add request details
      tracker.setData("request_method", method);
      tracker.setData("request_endpoint", endpoint);
      tracker.setTag("api_endpoint", endpoint);
      tracker.setTag("http_method", method);

      // Network span
      const networkSpan = tracker.startSpan("network-request", "http.client", {
        url: `${this.baseURL}${endpoint}`,
        method: method
      });

      const response = await fetch(`${this.baseURL}${endpoint}`, {
        method: method,
        ...options
      });

      networkSpan.finish();

      // Response processing span
      const processingSpan = tracker.startSpan("response-processing", "http.client");

      let data;
      if (response.ok) {
        data = await response.json();
      } else {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      processingSpan.finish();

      // Add response details
      tracker.setData("response_status", response.status);
      tracker.setData("response_size", JSON.stringify(data).length);
      tracker.setTag("http_status_code", response.status.toString());

      // Track specific business metrics
      if (endpoint.includes("/orders")) {
        tracker.setData("order_count", Array.isArray(data) ? data.length : 1);
        tracker.setTag("has_orders", true);
      }

      tracker.finish();

      return data;

    } catch (error) {
      // Add error context
      tracker.setData("error_type", error.name);
      tracker.setData("error_message", error.message);
      tracker.setTag("error", true);

      // Capture error in Sentry
      Sentry.withScope((scope) => {
        scope.setContext("api_error", {
          method: method,
          endpoint: endpoint,
          error: error.message
        });
        Sentry.captureException(error);
      });

      tracker.finish();
      throw error;
    }
  }
}

// Component performance monitoring
class ComponentMonitor {
  static wrap(componentName, renderFunction) {
    return function(...args) {
      const tracker = new PerformanceTracker(
        `component-${componentName}`,
        "react.render"
      );

      try {
        // Add component context
        tracker.setTag("component_name", componentName);
        tracker.setData("props_count", args[0] ? Object.keys(args[0]).length : 0);

        // Render span
        const renderSpan = tracker.startSpan("component-render", "react.render");
        const result = renderFunction.apply(this, args);
        renderSpan.finish();

        // Post-render span (effects, etc.)
        const postRenderSpan = tracker.startSpan("component-post-render", "react.effect");

        // Use requestAnimationFrame to capture post-render work
        requestAnimationFrame(() => {
          postRenderSpan.finish();
          tracker.finish();
        });

        return result;

      } catch (error) {
        tracker.setData("render_error", error.message);
        tracker.setTag("render_error", true);
        tracker.finish();
        throw error;
      }
    };
  }
}

// User interaction monitoring
class InteractionMonitor {
  static init() {
    // Click monitoring
    document.addEventListener("click", (event) => {
      const element = event.target.closest("[data-track]");
      if (element) {
        this.trackClick(element, event);
      }
    });

    // Form submission monitoring
    document.addEventListener("submit", (event) => {
      if (event.target.tagName === "FORM") {
        this.trackFormSubmission(event.target);
      }
    });

    // Scroll monitoring (debounced)
    let scrollTimeout;
    window.addEventListener("scroll", () => {
      clearTimeout(scrollTimeout);
      scrollTimeout = setTimeout(() => {
        this.trackScrollDepth();
      }, 100);
    });
  }

  static trackClick(element, event) {
    const tracker = new PerformanceTracker("user-click", "ui.click");

    tracker.setTag("click_target", element.tagName.toLowerCase());
    tracker.setTag("click_id", element.id || "");
    tracker.setTag("click_class", element.className || "");
    tracker.setData("click_x", event.clientX);
    tracker.setData("click_y", event.clientY);

    // Track specific interactions
    if (element.matches("[data-track-cta]")) {
      tracker.setTag("cta_click", true);
      tracker.setData("cta_text", element.textContent.trim());
    }

    if (element.matches("[data-track-feature]")) {
      const feature = element.dataset.trackFeature;
      tracker.setTag("feature_interaction", true);
      tracker.setData("feature_name", feature);

      // Record custom event
      Sentry.addBreadcrumb({
        category: "ui.interaction",
        message: `Feature used: ${feature}`,
        level: "info",
        data: {
          feature: feature,
          element: element.tagName.toLowerCase()
        }
      });
    }

    tracker.finish();
  }

  static trackFormSubmission(form) {
    const tracker = new PerformanceTracker("form-submission", "ui.submit");

    tracker.setTag("form_id", form.id || "");
    tracker.setTag("form_class", form.className || "");
    tracker.setData("field_count", form.elements.length);

    // Track form type
    if (form.matches(".login-form")) {
      tracker.setTag("form_type", "login");
    } else if (form.matches(".signup-form")) {
      tracker.setTag("form_type", "signup");
    } else if (form.matches(".contact-form")) {
      tracker.setTag("form_type", "contact");
    }

    // Add breadcrumb
    Sentry.addBreadcrumb({
      category: "ui.interaction",
      message: `Form submitted: ${tracker.getTransaction().tags.form_type || 'unknown'}`,
      level: "info"
    });

    tracker.finish();
  }

  static trackScrollDepth() {
    const scrollPercentage = Math.round(
      (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
    );

    // Record significant scroll milestones
    const milestones = [25, 50, 75, 90];

    milestones.forEach(milestone => {
      if (scrollPercentage >= milestone && !this.scrollMilestones?.[milestone]) {
        this.scrollMilestones = this.scrollMilestones || {};
        this.scrollMilestones[milestone] = true;

        Sentry.addBreadcrumb({
          category: "ui.interaction",
          message: `Scroll depth reached: ${milestone}%`,
          level: "info"
        });

        // Record custom metric
        Sentry.metrics.increment(
          "ui.scroll_depth_reached",
          1,
          { tags: { milestone: milestone.toString() } }
        );
      }
    });
  }
}

// Resource loading monitoring
class ResourceMonitor {
  static init() {
    // Monitor critical resources
    this.observeCriticalResources();

    // Monitor API endpoints
    this.monitorAPIPerformance();
  }

  static observeCriticalResources() {
    const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();

      entries.forEach(entry => {
        if (entry.entryType === "resource") {
          this.trackResourceLoading(entry);
        }
      });
    });

    observer.observe({ entryTypes: ["resource"] });
  }

  static trackResourceLoading(entry) {
    // Only track certain resource types
    const trackableTypes = ["script", "link", "img", "fetch", "xmlhttprequest"];
    if (!trackableTypes.includes(entry.initiatorType)) return;

    const tracker = new PerformanceTracker(
      `resource-${entry.initiatorType}`,
      "resource"
    );

    tracker.setTag("resource_type", entry.initiatorType);
    tracker.setTag("resource_name", this.getResourceName(entry.name));
    tracker.setData("resource_size", entry.transferSize || 0);
    tracker.setData("load_time", entry.duration);

    // Flag slow resources
    if (entry.duration > 1000) { // 1 second threshold
      tracker.setTag("slow_resource", true);

      Sentry.captureMessage(
        `Slow resource detected: ${entry.name} (${entry.duration}ms)`,
        "warning"
      );
    }

    // Flag failed resources
    if (entry.transferSize === 0 && entry.duration > 0) {
      tracker.setTag("resource_failed", true);
      Sentry.captureMessage(
        `Resource failed to load: ${entry.name}`,
        "error"
      );
    }

    tracker.finish();
  }

  static getResourceName(url) {
    try {
      const urlObj = new URL(url);
      return urlObj.pathname.split('/').pop() || urlObj.pathname;
    } catch {
      return url;
    }
  }

  static monitorAPIPerformance() {
    // Track API performance metrics
    setInterval(() => {
      const apiCalls = Sentry.metrics.distribution(
        "api.request_duration",
        this.getRecentAPICallDurations(),
        { unit: "millisecond" }
      );

      const apiErrors = Sentry.metrics.increment(
        "api.error_count",
        this.getRecentAPIErrorCount(),
        { tags: { status: "error" } }
      );
    }, 30000); // Every 30 seconds
  }

  static getRecentAPICallDurations() {
    // Implementation to collect recent API call durations
    return this.apiCallDurations || [];
  }

  static getRecentAPIErrorCount() {
    // Implementation to count recent API errors
    return this.apiErrorCount || 0;
  }
}

// Initialize all monitoring
function initPerformanceMonitoring() {
  // Start interaction monitoring
  InteractionMonitor.init();

  // Start resource monitoring
  ResourceMonitor.init();

  // Record page load metrics
  window.addEventListener("load", () => {
    setTimeout(() => {
      const perfData = performance.getEntriesByType("navigation")[0];

      Sentry.addBreadcrumb({
        category: "performance",
        message: "Page load completed",
        level: "info",
        data: {
          dom_content_loaded: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
          load_complete: perfData.loadEventEnd - perfData.loadEventStart,
          first_paint: this.getFirstPaintTime(),
          first_contentful_paint: this.getFirstContentfulPaintTime()
        }
      });
    }, 0);
  });
}

// Utility functions
function getFirstPaintTime() {
  const paintEntries = performance.getEntriesByType("paint");
  const firstPaint = paintEntries.find(entry => entry.name === "first-paint");
  return firstPaint ? Math.round(firstPaint.startTime) : 0;
}

function getFirstContentfulPaintTime() {
  const paintEntries = performance.getEntriesByType("paint");
  const fcp = paintEntries.find(entry => entry.name === "first-contentful-paint");
  return fcp ? Math.round(fcp.startTime) : 0;
}

// Export monitoring classes
export {
  PerformanceTracker,
  APIMonitor,
  ComponentMonitor,
  InteractionMonitor,
  ResourceMonitor,
  initPerformanceMonitoring
};

💻 Sentry Custom Alerts und Regeln json

🔴 complex ⭐⭐⭐⭐⭐

Erweiterte Alert-Regeln, Benachrichtigungs-Integrationen und automatisierte Workflows für proaktives Fehler-Management konfigurieren

⏱️ 60 min 🏷️ sentry, alerts, monitoring, notifications, automation
Prerequisites: Sentry account, Alerting concepts, Webhook knowledge
{
  "alert_rules": [
    {
      "name": "Critical Error Spike",
      "description": "Alert when critical errors increase significantly",
      "conditions": [
        {
          "id": "error_rate_spike",
          "interval": "5m",
          "aggregation": "count()",
          "field": "level",
          "operator": "gt",
          "value": 10,
          "filters": [
            {
              "field": "level",
              "value": "error",
              "match": "exact"
            },
            {
              "field": "environment",
              "value": "production",
              "match": "exact"
            }
          ]
        }
      ],
      "actions": [
        {
          "type": "email",
          "recipients": ["[email protected]", "[email protected]"],
          "template": "critical_error_spike"
        },
        {
          "type": "slack",
          "channel": "#alerts-critical",
          "template": "error_notification",
          "mention_users": ["@devops", "@tech-lead"]
        },
        {
          "type": "pagerduty",
          "service_key": "your-pagerduty-key",
          "severity": "critical"
        },
        {
          "type": "jira",
          "project": "BUG",
          "issue_type": "Bug",
          "priority": "Highest",
          "assignee": "devops-team"
        }
      ],
      "thresholds": {
        "critical": 50,
        "warning": 20
      },
      "cooldown": "30m",
      "enabled": true,
      "tags": ["production", "critical", "error-spike"]
    },
    {
      "name": "Performance Degradation",
      "description": "Alert when application performance drops below threshold",
      "conditions": [
        {
          "id": "slow_transactions",
          "interval": "10m",
          "aggregation": "p95(transaction.duration)",
          "operator": "gt",
          "value": 2000,
          "filters": [
            {
              "field": "transaction.op",
              "value": "http.server",
              "match": "exact"
            },
            {
              "field": "environment",
              "value": "production",
              "match": "exact"
            }
          ]
        }
      ],
      "actions": [
        {
          "type": "slack",
          "channel": "#performance-alerts",
          "template": "performance_degradation"
        },
        {
          "type": "email",
          "recipients": ["[email protected]"],
          "template": "performance_alert"
        }
      ],
      "thresholds": {
        "critical": 3000,
        "warning": 2000
      },
      "cooldown": "15m",
      "enabled": true,
      "tags": ["performance", "production"]
    },
    {
      "name": "New Error Introduction",
      "description": "Alert when new error types are detected",
      "conditions": [
        {
          "id": "new_error_type",
          "interval": "1h",
          "aggregation": "count_distinct(exception.type)",
          "operator": "gt",
          "value": 1,
          "filters": [
            {
              "field": "first_seen",
              "value": ">= 1h",
              "match": "age"
            },
            {
              "field": "environment",
              "value": "production",
              "match": "exact"
            }
          ]
        }
      ],
      "actions": [
        {
          "type": "slack",
          "channel": "#development",
          "template": "new_error_alert"
        },
        {
          "type": "github",
          "repository": "company/app",
          "issue_title": "New Error Detected: {{error.type}}",
          "issue_body": "New error type detected in production:\n\n{{error.message}}\n\nStack trace:\n{{error.stacktrace}}",
          "assignees": ["development-team"]
        }
      ],
      "cooldown": "1h",
      "enabled": true,
      "tags": ["new-error", "production"]
    },
    {
      "name": "Database Connection Issues",
      "description": "Alert on database-related errors",
      "conditions": [
        {
          "id": "database_errors",
          "interval": "5m",
          "aggregation": "count()",
          "operator": "gt",
          "value": 5,
          "filters": [
            {
              "field": "exception.type",
              "value": ["DatabaseError", "ConnectionError", "TimeoutError"],
              "match": "contains"
            }
          ]
        }
      ],
      "actions": [
        {
          "type": "pagerduty",
          "service_key": "database-pagerduty-key",
          "severity": "high"
        },
        {
          "type": "email",
          "recipients": ["[email protected]", "[email protected]"],
          "template": "database_alert"
        }
      ],
      "thresholds": {
        "critical": 20,
        "warning": 5
      },
      "cooldown": "10m",
      "enabled": true,
      "tags": ["database", "infrastructure"]
    },
    {
      "name": "User Experience Issues",
      "description": "Alert on user-facing problems",
      "conditions": [
        {
          "id": "ux_errors",
          "interval": "10m",
          "aggregation": "count()",
          "operator": "gt",
          "value": 10,
          "filters": [
            {
              "field": "tags.ux_issue",
              "value": "true",
              "match": "exact"
            },
            {
              "field": "user.id",
              "value": "null",
              "match": "not"
            }
          ]
        }
      ],
      "actions": [
        {
          "type": "slack",
          "channel": "#product-alerts",
          "template": "ux_issue"
        },
        {
          "type": "linear",
          "team_id": "product-team-id",
          "priority": "medium"
        }
      ],
      "cooldown": "20m",
      "enabled": true,
      "tags": ["user-experience", "customer-facing"]
    },
    {
      "name": "Third-party Service Failures",
      "description": "Alert on external service failures",
      "conditions": [
        {
          "id": "external_service_errors",
          "interval": "5m",
          "aggregation": "count()",
          "operator": "gt",
          "value": 3,
          "filters": [
            {
              "field": "transaction.op",
              "value": "http.client",
              "match": "exact"
            },
            {
              "field": "http.response_code",
              "value": ">= 500",
              "match": "range"
            }
          ]
        }
      ],
      "actions": [
        {
          "type": "email",
          "recipients": ["[email protected]"],
          "template": "external_service_failure"
        },
        {
          "type": "statuspage",
          "component_id": "external-api",
          "status": "degraded_performance"
        }
      ],
      "cooldown": "15m",
      "enabled": true,
      "tags": ["external-services", "integrations"]
    }
  ],
  "notification_templates": {
    "critical_error_spike": {
      "subject": "🚨 Critical Error Spike Detected - {{error_count}} errors in last {{interval}}",
      "body": `<h2>Critical Error Spike Alert</h2>
        <p><strong>{{error_count}}</strong> critical errors detected in the last {{interval}}</p>

        <h3>Error Details:</h3>
        <ul>
          <li><strong>Environment:</strong> {{environment}}</li>
          <li><strong>Time Period:</strong> {{interval}}</li>
          <li><strong>Error Rate:</strong> {{error_rate}} errors/minute</li>
          <li><strong>Top Error:</strong> {{top_error.type}} - {{top_error.message}}</li>
        </ul>

        <h3>Recent Errors:</h3>
        <table border="1" style="border-collapse: collapse;">
          <tr>
            <th>Time</th>
            <th>Error Type</th>
            <th>Message</th>
            <th>User Impact</th>
          </tr>
          {{#each recent_errors}}
          <tr>
            <td>{{timestamp}}</td>
            <td>{{exception.type}}</td>
            <td>{{exception.message}}</td>
            <td>{{user_impact}}</td>
          </tr>
          {{/each}}
        </table>

        <h3>Actions Required:</h3>
        <ol>
          <li>Check Sentry dashboard: {{sentry_url}}</li>
          <li>Review recent deployments</li>
          <li>Check infrastructure health</li>
          <li>Communicate status to stakeholders</li>
        </ol>

        <p><a href="{{sentry_url}}">View in Sentry</a></p>`
    },
    "performance_degradation": {
      "subject": "⚠️ Performance Degradation Detected - P95: {{p95_duration}}ms",
      "body": `<h2>Performance Degradation Alert</h2>

        <h3>Performance Metrics:</h3>
        <ul>
          <li><strong>P95 Response Time:</strong> {{p95_duration}}ms (threshold: {{threshold}}ms)</li>
          <li><strong>P50 Response Time:</strong> {{p50_duration}}ms</li>
          <li><strong>Slowest Transaction:</strong> {{slowest_transaction.name}} ({{slowest_transaction.duration}}ms)</li>
        </ul>

        <h3>Affected Endpoints:</h3>
        <table>
          <tr><th>Endpoint</th><th>P95 Time</th><th>Error Rate</th></tr>
          {{#each slow_endpoints}}
          <tr>
            <td>{{transaction.name}}</td>
            <td>{{duration}}ms</td>
            <td>{{error_rate}}%</td>
          </tr>
          {{/each}}
        </table>

        <h3>Next Steps:</h3>
        <ol>
          <li>Review database query performance</li>
          <li>Check external service response times</li>
          <li>Analyze recent code changes</li>
          <li>Consider scaling resources if needed</li>
        </ol>`
    },
    "new_error_alert": {
      "subject": "🆕 New Error Type Detected: {{error.type}}",
      "body": `<h2>New Error Type Alert</h2>

        <p>A new error type has been detected in production:</p>

        <ul>
          <li><strong>Error Type:</strong> {{exception.type}}</li>
          <li><strong>Message:</strong> {{exception.message}}</li>
          <li><strong>First Seen:</strong> {{first_seen}}</li>
          <li><strong>Affected Users:</strong> {{affected_users}}</li>
          <li><strong>Environment:</strong> {{environment}}</li>
        </ul>

        <h3>Stack Trace Preview:</h3>
        <pre>{{exception.stacktrace | truncate: 500}}</pre>

        <h3>Investigation Steps:</h3>
        <ol>
          <li>Review recent deployments that might have introduced this error</li>
          <li>Check if this error affects critical user flows</li>
          <li>Determine if immediate action is required</li>
          <li>Create bug ticket if needed</li>
        </ol>

        <p><a href="{{sentry_url}}">View Error Details</a></p>`
    },
    "ux_issue": {
      "subject": "😟 User Experience Issue - {{issue_count}} reports",
      "body": `<h2>User Experience Issue Alert</h2>

        <p><strong>{{issue_count}}</strong> user experience issues have been reported in the last {{interval}}.</p>

        <h3>Issue Summary:</h3>
        <ul>
          <li><strong>Total Affected Users:</strong> {{affected_users}}</li>
          <li><strong>Most Common Issue:</strong> {{common_issue}}</li>
          <li><strong>User Feedback Score:</strong> {{ux_score}}/10</li>
        </ul>

        <h3>Recent User Reports:</h3>
        {{#each user_reports}}
        <div style="margin-bottom: 20px; padding: 10px; border: 1px solid #ccc;">
          <p><strong>User:</strong> {{user.email}} (ID: {{user.id}})</p>
          <p><strong>Issue:</strong> {{message}}</p>
          <p><strong>Context:</strong> {{context}}</p>
          <p><strong>Time:</strong> {{timestamp}}</p>
        </div>
        {{/each}}

        <h3>Recommended Actions:</h3>
        <ol>
          <li>Prioritize issues affecting critical user flows</li>
          <li>Reach out to affected users if needed</li>
          <li>Update product team on potential feature impact</li>
          <li>Consider temporary workarounds if issues are severe</li>
        </ol>`
    }
  },
  "integration_configs": {
    "slack": {
      "webhook_url": "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
      "default_channel": "#alerts",
      "mention_users": ["@devops", "@oncall"],
      "notification_threshold": "warning",
      "resolve_notifications": true
    },
    "pagerduty": {
      "service_key": "your-pagerduty-service-key",
      "severity_mapping": {
        "critical": "critical",
        "warning": "high",
        "info": "low"
      },
      "auto_resolve": true,
      "grouping": true
    },
    "email": {
      "smtp_server": "smtp.company.com",
      "smtp_port": 587,
      "default_recipients": ["[email protected]"],
      "use_tls": true,
      "from_address": "[email protected]"
    },
    "jira": {
      "url": "https://company.atlassian.net",
      "username": "sentry-bot",
      "project_key": "BUG",
      "default_issue_type": "Bug",
      "auto_assign": true,
      "default_priority": "Medium"
    },
    "github": {
      "token": "your-github-token",
      "repository": "company/app",
      "auto_assign": true,
      "default_labels": ["bug", "auto-generated"],
      "default_milestone": "current-sprint"
    },
    "statuspage": {
      "api_key": "your-statuspage-api-key",
      "page_id": "your-statuspage-id",
      "auto_update": true,
      "notify_subscribers": true
    },
    "linear": {
      "team_id": "your-linear-team-id",
      "api_key": "your-linear-api-key",
      "auto_create_issue": true,
      "default_state": "Backlog",
      "default_priority": "Medium"
    }
  },
  "escalation_policies": [
    {
      "name": "Critical Production Issues",
      "triggers": [
        {
          "rule_type": "error_count",
          "threshold": 50,
          "time_window": "5m",
          "environment": "production"
        },
        {
          "rule_type": "performance_degradation",
          "threshold": "5000",
          "metric": "p95_duration"
        }
      ],
      "steps": [
        {
          "wait_time": "0m",
          "actions": ["slack_critical", "pagerduty_critical"]
        },
        {
          "wait_time": "15m",
          "actions": ["phone_call", "executive_notification"]
        },
        {
          "wait_time": "30m",
          "actions": ["war_room", "all_hands_notification"]
        }
      ]
    },
    {
      "name": "Non-Critical Issues",
      "triggers": [
        {
          "rule_type": "error_count",
          "threshold": 10,
          "time_window": "30m"
        }
      ],
      "steps": [
        {
          "wait_time": "0m",
          "actions": ["slack_warning", "email_team"]
        },
        {
          "wait_time": "2h",
          "actions": ["create_ticket"]
        }
      ]
    }
  ],
  "maintenance_windows": [
    {
      "name": "Weekend Deployments",
      "description": "Silence non-critical alerts during planned maintenance",
      "schedule": {
        "type": "weekly",
        "days": ["Saturday", "Sunday"],
        "start_time": "02:00",
        "end_time": "06:00",
        "timezone": "UTC"
      },
      "affected_rules": [
        "performance_degradation",
        "database_errors"
      ],
      "critical_rules_enabled": true
    },
    {
      "name": "Major Release Window",
      "description": "Silence certain alerts during major releases",
      "schedule": {
        "type": "specific_dates",
        "dates": ["2024-01-15", "2024-02-01"],
        "start_time": "00:00",
        "end_time": "23:59"
      },
      "affected_rules": [
        "new_error_type",
        "ux_issues"
      ]
    }
  ]
}