Workbooks & KQL Monitoring Dashboard

Trong phần này, chúng ta sẽ tạo dashboard real-time monitoring để theo dõi toàn bộ MLOps pipeline từ data ingestion, ETL processing, đến inference endpoints. Dashboard sẽ cung cấp insights về business funnel, data quality, drift detection, và performance metrics. Đây là bước quan trọng để đảm bảo reliability và performance của hệ thống production.

1. Mục tiêu

Tạo dashboard realtime theo dõi business funnel và data quality
Monitor data drift và input errors
Track latency và error rate của ETL + inference
Phát hiện sớm bất thường cho DevOps/DataOps

TASK 6 — WORKBOOK & KQL NỀN
Mục tiêu: Tạo dashboard realtime theo dõi: Phân bố view/cart/purchase (business funnel). Data drift & input errors (dữ liệu clickstream & inference). Latency & error rate (ETL + inference endpoint). Giúp DevOps/DataOps phát hiện sớm bất thường.

2. MONITORING ARCHITECTURE

2.1 Monitoring Components

🔧 Monitoring Stack

1. Data Sources:

  • Application Insights: ETL jobs, inference endpoints
  • Azure Monitor: Infrastructure metrics
  • Log Analytics: Centralized logging
  • Custom Events: Business metrics tracking

2. Dashboard Components:

  • Business Funnel: View → Cart → Purchase conversion
  • Data Quality: Input validation, schema compliance
  • Drift Detection: Feature distribution comparison
  • Performance: Latency, error rates, throughput

3. Alerting:

  • Threshold-based: Performance degradation
  • Anomaly detection: Unusual patterns
  • Business KPIs: Conversion rate drops

2.2 Dashboard Design Strategy

📊 Dashboard Tabs

Tab 1: Business Funnel

  • Line charts: Views, carts, purchases theo thời gian
  • Stacked bar charts: Conversion rates
  • Funnel visualization: View → Cart → Purchase
  • Key metrics: Conversion rates, trends

Tab 2: Data Quality

  • Error tables: Input validation failures
  • Schema compliance: Missing fields, type mismatches
  • Data freshness: Last update timestamps
  • Quality scores: Overall data health

Tab 3: Drift Detection

  • Feature distributions: Price, category, brand
  • Baseline comparison: Historical vs current
  • Statistical tests: Kolmogorov-Smirnov, Chi-square
  • Alert thresholds: Significant drift detection

Tab 4: Latency & Errors

  • Latency distribution: P50, P95, P99 percentiles
  • Error rates: 4xx/5xx HTTP codes
  • Throughput metrics: Requests per second
  • Performance trends: Over time analysis

3. AZURE APPLICATION INSIGHTS SETUP

3.1 Prerequisites

Required Components:

  • Application Insights đã tạo (từ Task 7 - Azure ML Workspace Setup)
  • ETL Jobs đang chạy và log events (từ Task 5: ETL Aggregate)
  • Inference Endpoints deployed (từ Task 8)
  • Custom telemetry configured trong applications
  • Log Analytics Workspace connected

3.2 UI Flow (Application Insights)

Step 1: Navigate to Workbooks

Azure Portal → Application Insights → Workbooks → +New

Step 2: Workbook Configuration

  • Workbook name: Retail-Forecast-Monitoring
  • Description: Real-time monitoring dashboard for retail forecasting MLOps pipeline
  • Template: Start from blank workbook

Step 3: Add Data Sources

  • Application Insights: Primary data source
  • Log Analytics: Additional metrics
  • Custom queries: KQL scripts

Step 4: Configure Tabs

  • Add 4 tabs: Business Funnel, Data Quality, Drift Detection, Latency & Errors
  • Set refresh intervals: 1 minute for real-time monitoring
  • Configure time ranges: Last 24 hours default

4. KQL SCRIPTS IMPLEMENTATION

4.1 Event Distribution Analysis

// event_dist.kql - Event type distribution analysis
customEvents
| where timestamp >= ago(24h)
| where name in ("view_event", "cart_event", "purchase_event")
| extend event_type = case(
    name == "view_event", "view",
    name == "cart_event", "cart", 
    name == "purchase_event", "purchase",
    "unknown"
)
| summarize 
    view_count = countif(event_type == "view"),
    cart_count = countif(event_type == "cart"),
    purchase_count = countif(event_type == "purchase")
    by bin(timestamp, 1h)
| extend 
    cart_rate = cart_count / view_count,
    purchase_rate = purchase_count / view_count,
    funnel_conversion = purchase_count / view_count
| order by timestamp asc
| render timechart with (title="Business Funnel Over Time")

4.2 Latency Monitoring

// latency.kql - Inference endpoint latency analysis
requests
| where timestamp >= ago(24h)
| where name contains "retail-forecast" or url contains "/predict"
| where success == true
| summarize 
    p50 = percentile(duration, 50),
    p95 = percentile(duration, 95),
    p99 = percentile(duration, 99),
    avg_latency = avg(duration),
    max_latency = max(duration),
    request_count = count()
    by bin(timestamp, 5m)
| order by timestamp asc
| render timechart with (title="Inference Latency Percentiles")

4.3 Error Analysis

// errors.kql - Error tracking and categorization
union
    requests,
    customEvents
| where timestamp >= ago(24h)
| extend error_type = case(
    resultCode >= 400 and resultCode < 500, "4xx_Client_Error",
    resultCode >= 500, "5xx_Server_Error",
    success == false, "Application_Error",
    "Success"
)
| summarize 
    total_requests = count(),
    error_count = countif(error_type != "Success"),
    error_rate = countif(error_type != "Success") * 100.0 / count(),
    client_errors = countif(error_type == "4xx_Client_Error"),
    server_errors = countif(error_type == "5xx_Server_Error"),
    app_errors = countif(error_type == "Application_Error")
    by bin(timestamp, 1h)
| order by timestamp asc
| render timechart with (title="Error Rate Analysis")

4.4 Data Drift Detection

// drift.kql - Feature distribution comparison
customEvents
| where timestamp >= ago(24h)
| where name == "data_processed"
| extend 
    price = todouble(customDimensions.price),
    category = tostring(customDimensions.category_code),
    brand = tostring(customDimensions.brand)
| where isnotnull(price) and price > 0
| summarize 
    price_mean = avg(price),
    price_std = stdev(price),
    price_p50 = percentile(price, 50),
    price_p95 = percentile(price, 95),
    category_dist = dcount(category),
    brand_dist = dcount(brand),
    record_count = count()
    by bin(timestamp, 1h)
| extend 
    price_cv = price_std / price_mean,  // Coefficient of variation
    drift_score = case(
        price_cv > 0.5, "High_Drift",
        price_cv > 0.3, "Medium_Drift",
        "Low_Drift"
    )
| order by timestamp asc
| render timechart with (title="Data Drift Indicators")

4.5 Business Funnel Analysis

// funnel.kql - Conversion funnel analysis
customEvents
| where timestamp >= ago(24h)
| where name in ("view_event", "cart_event", "purchase_event")
| extend user_id = tostring(customDimensions.user_id)
| where isnotnull(user_id)
| summarize 
    views = countif(name == "view_event"),
    carts = countif(name == "cart_event"),
    purchases = countif(name == "purchase_event")
    by user_id, bin(timestamp, 1h)
| summarize 
    total_users = dcount(user_id),
    users_with_views = countif(views > 0),
    users_with_carts = countif(carts > 0),
    users_with_purchases = countif(purchases > 0)
    by bin(timestamp, 1h)
| extend 
    view_to_cart_rate = users_with_carts * 100.0 / users_with_views,
    cart_to_purchase_rate = users_with_purchases * 100.0 / users_with_carts,
    overall_conversion_rate = users_with_purchases * 100.0 / users_with_views
| order by timestamp asc
| render timechart with (title="Conversion Funnel Rates")

5. WORKBOOK IMPLEMENTATION

5.1 Business Funnel Tab

{
  "version": "Notebook/1.0",
  "items": [
    {
      "type": 1,
      "content": {
        "json": "## Business Funnel Analysis\n\nMonitor conversion rates and user behavior patterns in real-time."
      }
    },
    {
      "type": 3,
      "content": {
        "version": "KqlItem/1.0",
        "query": "customEvents\n| where timestamp >= ago(24h)\n| where name in (\"view_event\", \"cart_event\", \"purchase_event\")\n| extend event_type = case(\n    name == \"view_event\", \"view\",\n    name == \"cart_event\", \"cart\", \n    name == \"purchase_event\", \"purchase\",\n    \"unknown\"\n)\n| summarize \n    view_count = countif(event_type == \"view\"),\n    cart_count = countif(event_type == \"cart\"),\n    purchase_count = countif(event_type == \"purchase\")\n    by bin(timestamp, 1h)\n| extend \n    cart_rate = cart_count / view_count,\n    purchase_rate = purchase_count / view_count\n| render timechart",
        "size": 0,
        "title": "Event Distribution Over Time",
        "queryType": 0,
        "resourceType": "microsoft.insights/components"
      }
    }
  ]
}

5.2 Data Quality Tab

{
  "type": 3,
  "content": {
    "version": "KqlItem/1.0",
    "query": "customEvents\n| where timestamp >= ago(24h)\n| where name == \"data_validation\"\n| extend \n    validation_error = tostring(customDimensions.error_type),\n    error_count = toint(customDimensions.error_count)\n| summarize \n    total_errors = sum(error_count),\n    error_types = dcount(validation_error)\n    by validation_error, bin(timestamp, 1h)\n| render barchart",
    "size": 0,
    "title": "Data Quality Issues",
    "queryType": 0,
    "resourceType": "microsoft.insights/components"
  }
}

5.3 Drift Detection Tab

{
  "type": 3,
  "content": {
    "version": "KqlItem/1.0",
    "query": "customEvents\n| where timestamp >= ago(24h)\n| where name == \"feature_distribution\"\n| extend \n    price = todouble(customDimensions.price),\n    category = tostring(customDimensions.category),\n    brand = tostring(customDimensions.brand)\n| summarize \n    price_mean = avg(price),\n    price_std = stdev(price),\n    category_diversity = dcount(category),\n    brand_diversity = dcount(brand)\n    by bin(timestamp, 1h)\n| render timechart",
    "size": 0,
    "title": "Feature Distribution Drift",
    "queryType": 0,
    "resourceType": "microsoft.insights/components"
  }
}

5.4 Latency & Errors Tab

{
  "type": 3,
  "content": {
    "version": "KqlItem/1.0",
    "query": "requests\n| where timestamp >= ago(24h)\n| where name contains \"retail-forecast\"\n| summarize \n    p50 = percentile(duration, 50),\n    p95 = percentile(duration, 95),\n    p99 = percentile(duration, 99),\n    error_rate = countif(success == false) * 100.0 / count()\n    by bin(timestamp, 5m)\n| render timechart",
    "size": 0,
    "title": "Latency and Error Rates",
    "queryType": 0,
    "resourceType": "microsoft.insights/components"
  }
}

6. CUSTOM TELEMETRY IMPLEMENTATION

6.1 ETL Job Telemetry

# etl_telemetry.py - Add to ETL pipeline
from opencensus.ext.azure.log_exporter import AzureLogHandler
from opencensus.ext.azure.trace_exporter import AzureExporter
from opencensus.trace import config_integration
from opencensus.trace.samplers import ProbabilitySampler
from opencensus.trace.tracer import Tracer
import logging

def setup_telemetry():
    """Setup Application Insights telemetry"""
    # Configure logging
    logger = logging.getLogger(__name__)
    logger.addHandler(AzureLogHandler(connection_string="your-connection-string"))
    
    # Configure tracing
    config_integration.trace_integrations(['requests'])
    tracer = Tracer(
        exporter=AzureExporter(connection_string="your-connection-string"),
        sampler=ProbabilitySampler(rate=1.0)
    )
    
    return logger, tracer

def log_business_event(event_type, properties):
    """Log business events for funnel analysis"""
    logger, tracer = setup_telemetry()
    
    with tracer.span(name=f"{event_type}_event"):
        logger.info(f"Business event: {event_type}", extra={
            "custom_dimensions": {
                "event_type": event_type,
                "user_id": properties.get("user_id"),
                "product_id": properties.get("product_id"),
                "category_code": properties.get("category_code"),
                "brand": properties.get("brand"),
                "price": properties.get("price")
            }
        })

def log_data_quality_metrics(metrics):
    """Log data quality metrics"""
    logger, _ = setup_telemetry()
    
    logger.info("Data quality metrics", extra={
        "custom_dimensions": {
            "total_records": metrics["total_records"],
            "invalid_records": metrics["invalid_records"],
            "missing_values": metrics["missing_values"],
            "schema_errors": metrics["schema_errors"],
            "quality_score": metrics["quality_score"]
        }
    })

def log_feature_distribution(features):
    """Log feature distribution for drift detection"""
    logger, _ = setup_telemetry()
    
    logger.info("Feature distribution", extra={
        "custom_dimensions": {
            "price_mean": features["price_mean"],
            "price_std": features["price_std"],
            "category_distribution": features["category_dist"],
            "brand_distribution": features["brand_dist"]
        }
    })

6.2 Inference Endpoint Telemetry

# inference_telemetry.py - Add to inference service
import time
from functools import wraps

def track_inference_metrics(func):
    """Decorator to track inference metrics"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        
        try:
            result = func(*args, **kwargs)
            duration = time.time() - start_time
            
            # Log successful inference
            logger.info("Inference completed", extra={
                "custom_dimensions": {
                    "duration_ms": duration * 1000,
                    "success": True,
                    "model_version": kwargs.get("model_version", "unknown"),
                    "input_size": len(str(kwargs.get("input_data", "")))
                }
            })
            
            return result
            
        except Exception as e:
            duration = time.time() - start_time
            
            # Log failed inference
            logger.error("Inference failed", extra={
                "custom_dimensions": {
                    "duration_ms": duration * 1000,
                    "success": False,
                    "error_type": type(e).__name__,
                    "error_message": str(e)
                }
            })
            
            raise
    
    return wrapper

# Usage in FastAPI endpoint
from fastapi import FastAPI
import logging

app = FastAPI()
logger = logging.getLogger(__name__)

@app.post("/predict")
@track_inference_metrics
async def predict(input_data: dict):
    # Your inference logic here
    return {"prediction": "result"}

7. BẰNG CHỨNG HOÀN THÀNH

7.1 Ảnh 1: Workbook Creation

📋 Yêu cầu screenshot:
  • ✅ Application Insights → Workbooks → +New
  • ✅ Workbook name: Retail-Forecast-Monitoring
  • ✅ 4 tabs configured: Business Funnel, Data Quality, Drift Detection, Latency & Errors
  • ✅ Real-time refresh enabled

7.2 Ảnh 2: Business Funnel Dashboard

📊 Yêu cầu screenshot:
  • ✅ Business Funnel tab active
  • ✅ Line charts showing view/cart/purchase trends
  • ✅ Conversion rates visible
  • ✅ Real-time data updating

7.3 Ảnh 3: Data Quality Monitoring

📁 Yêu cầu screenshot:
  • ✅ Data Quality tab showing error tables
  • ✅ Input validation failures displayed
  • ✅ Schema compliance metrics
  • ✅ Quality scores visible

7.4 Ảnh 4: Drift Detection Dashboard

📊 Yêu cầu screenshot:
  • ✅ Drift Detection tab active
  • ✅ Feature distribution comparisons
  • ✅ Baseline vs current distributions
  • ✅ Drift scores and alerts

7.5 Ảnh 5: Latency & Error Monitoring

⚡ Yêu cầu screenshot:
  • ✅ Latency & Errors tab showing performance metrics
  • ✅ P50, P95, P99 latency percentiles
  • ✅ Error rates (4xx/5xx) displayed
  • ✅ Throughput metrics visible

7.6 Ảnh 6: KQL Scripts Repository

📝 Yêu cầu screenshot:
  • ✅ Repository showing KQL files in azure/monitor/kql/
  • ✅ Files: event_dist.kql, latency.kql, errors.kql, drift.kql, funnel.kql
  • ✅ Commit log showing "Task 6 — add KQL scripts"
  • ✅ File contents visible

8. TIÊU CHÍ HOÀN THÀNH

8.1 Functional Requirements

  • Workbook created: Retail-Forecast-Monitoring in Application Insights
  • Dashboard tabs: 4 tabs configured with proper visualizations
  • Real-time monitoring: Dashboard updating every minute
  • KQL scripts: 5 KQL files committed to repository
  • Telemetry: Custom events logged from applications

8.2 Monitoring Requirements

  • Business funnel: View/cart/purchase tracking working
  • Data quality: Error detection and validation metrics
  • Drift detection: Feature distribution monitoring
  • Performance: Latency and error rate tracking
  • Alerting: Thresholds configured for anomaly detection

8.3 Documentation Requirements

  • KQL scripts: Well-documented and version-controlled
  • Workbook JSON: Exported and stored in repository
  • Telemetry code: Examples provided for integration
  • Troubleshooting: Common issues and solutions documented

9. AUTOMATION SCRIPTS

9.1 Workbook Deployment Script

#!/bin/bash
# deploy-workbook.sh

# Configuration
SUBSCRIPTION_ID="your-subscription-id"
RESOURCE_GROUP="retail-dev-rg"
APP_INSIGHTS_NAME="retail-ml-insights"
WORKBOOK_FILE="retail-forecast-monitoring.json"

echo "🚀 Deploying Monitoring Workbook..."

# Login to Azure
az login

# Set subscription
az account set --subscription $SUBSCRIPTION_ID

# Get Application Insights resource ID
APP_INSIGHTS_ID=$(az monitor app-insights component show \
  --app retail-ml-insights \
  --resource-group $RESOURCE_GROUP \
  --query id -o tsv)

# Deploy workbook
az monitor workbook create \
  --resource-group $RESOURCE_GROUP \
  --name "Retail-Forecast-Monitoring" \
  --display-name "Retail Forecast Monitoring" \
  --source-id $APP_INSIGHTS_ID \
  --file $WORKBOOK_FILE

echo "✅ Workbook deployed successfully!"

9.2 KQL Script Validation

#!/bin/bash
# validate-kql-scripts.sh

KQL_DIR="azure/monitor/kql"
SCRIPTS=("event_dist.kql" "latency.kql" "errors.kql" "drift.kql" "funnel.kql")

echo "🔍 Validating KQL Scripts..."

for script in "${SCRIPTS[@]}"; do
    if [ -f "$KQL_DIR/$script" ]; then
        echo "✅ $script exists"
        
        # Basic syntax validation
        if grep -q "render" "$KQL_DIR/$script"; then
            echo "✅ $script contains render statement"
        else
            echo "❌ $script missing render statement"
        fi
        
        if grep -q "where timestamp" "$KQL_DIR/$script"; then
            echo "✅ $script contains time filtering"
        else
            echo "❌ $script missing time filtering"
        fi
        
    else
        echo "❌ $script not found"
    fi
done

echo "📊 KQL Script validation completed!"

9.3 Telemetry Setup Script

# setup_telemetry.py
from azure.monitor.opentelemetry import configure_azure_monitor
from opentelemetry import trace
from opentelemetry.exporter.azure_monitor import AzureMonitorTraceExporter
import logging

def configure_monitoring():
    """Configure Azure Monitor telemetry"""
    
    # Configure Azure Monitor
    configure_azure_monitor(
        connection_string="your-connection-string",
        enable_live_metrics=True
    )
    
    # Setup logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)
    
    # Setup tracing
    tracer = trace.get_tracer(__name__)
    
    return logger, tracer

def create_custom_events():
    """Create custom events for monitoring"""
    logger, tracer = configure_monitoring()
    
    # Business events
    with tracer.start_as_current_span("business_event"):
        logger.info("Business event logged", extra={
            "custom_dimensions": {
                "event_type": "view",
                "user_id": "user123",
                "product_id": "prod456"
            }
        })
    
    # Data quality events
    with tracer.start_as_current_span("data_quality"):
        logger.info("Data quality check", extra={
            "custom_dimensions": {
                "quality_score": 0.95,
                "invalid_records": 5,
                "total_records": 1000
            }
        })
    
    print("✅ Telemetry configured successfully!")

if __name__ == "__main__":
    create_custom_events()

10. LƯU Ý QUAN TRỌNG

10.1 Monitoring Best Practices

⚠️ Important Considerations
  • Performance Impact: Minimize telemetry overhead in production
  • Data Retention: Configure appropriate retention policies
  • Alert Fatigue: Set meaningful thresholds to avoid noise
  • Cost Management: Monitor Application Insights usage and costs
  • Security: Ensure sensitive data is not logged in custom dimensions

10.2 Troubleshooting Common Issues

Issue 1: “No data in dashboard”

# Check Application Insights connection
# Verify custom events are being logged
# Check time range settings
# Validate KQL query syntax

Issue 2: “High latency in queries”

# Optimize KQL queries with proper indexing
# Use appropriate time ranges
# Consider data sampling for large datasets
# Check Application Insights performance

Issue 3: “Missing custom events”

# Verify telemetry configuration
# Check connection string
# Validate custom event logging code
# Monitor Application Insights ingestion

10.3 Next Steps

  1. Monitoring Dashboard completed ← Current step
  2. 🔄 Alert configuration ← Next step (Task 15)
  3. 🔄 Automated remediation
  4. 🔄 Performance optimization
  5. 🔄 Cost monitoring and optimization

Best Practice: Regularly review and update monitoring dashboards based on operational insights. Use the KQL scripts as a foundation for custom alerts and automated responses.

Cost Note: Application Insights can generate significant costs with high-volume telemetry. Monitor usage and implement sampling strategies for cost optimization.

Monitoring dashboard hoàn tất! 🎉 Real-time monitoring đã sẵn sàng cho Task 7: Alert Configuration.