tutorial

Monitoring Azure Event Hubs with Vigilmon: Consumer Health Checks, Partition Lag Alerts & Processor Heartbeats

Practical guide to uptime monitoring for Azure Event Hubs — HTTP health endpoints for event processors, heartbeat patterns for batch consumers, partition lag awareness, and external alerting with Vigilmon.

Azure Event Hubs ingests millions of events per second. But the streaming layer is only as healthy as the consumers reading from it. Partition lag grows silently, consumer groups stall after a config change, and checkpoint stores get corrupted — none of which triggers an obvious alarm. Azure Monitor captures metrics inside your tenant, but it's as likely to fail during an outage as the service it's watching. Vigilmon adds an external, independent health layer: HTTP monitors on your processor services and heartbeat monitors for your event-driven workers.

This tutorial shows you how to wire Event Hubs consumers into Vigilmon for meaningful uptime monitoring.

What You'll Build

  • A health endpoint on your Event Hubs processor that reports real partition status
  • A Vigilmon HTTP monitor with appropriate timeout and assertion settings
  • A heartbeat pattern for batch processors and Azure Functions event triggers
  • A partition lag probe using Azure SDK
  • An alerting strategy for consumer group failures

Prerequisites

  • Azure subscription with at least one Event Hubs namespace and consumer application
  • Azure CLI or Bicep/ARM configured locally
  • A free account at vigilmon.online

Step 1: Add a Health Endpoint to Your Event Processor

Your Event Hubs consumer is typically a long-running service (AKS pod, App Service, Azure Container Apps). Add a /health route that checks real Event Hubs connectivity.

C# (ASP.NET Core)

// Endpoints/HealthEndpoint.cs
using Azure.Messaging.EventHubs;
using Azure.Messaging.EventHubs.Consumer;

public static class HealthEndpoint
{
    public static void MapHealthEndpoint(this WebApplication app)
    {
        app.MapGet("/health", async (IConfiguration config) =>
        {
            var checks = new Dictionary<string, object>();
            var ok = true;

            try
            {
                await using var consumer = new EventHubConsumerClient(
                    EventHubConsumerClient.DefaultConsumerGroupName,
                    config["EventHubConnectionString"],
                    config["EventHubName"]
                );

                var partitions = await consumer.GetPartitionIdsAsync();
                checks["eventHub"] = "ok";
                checks["partitionCount"] = partitions.Length;
            }
            catch (Exception ex)
            {
                checks["eventHub"] = $"error: {ex.Message}";
                ok = false;
            }

            return ok
                ? Results.Ok(new { status = "ok", service = "eventhubs-processor", checks })
                : Results.Json(new { status = "degraded", service = "eventhubs-processor", checks },
                    statusCode: 503);
        });
    }
}

Python (FastAPI)

# routers/health.py
import os
from fastapi import APIRouter
from azure.eventhub import EventHubConsumerClient
from azure.eventhub.exceptions import EventHubError

router = APIRouter()

@router.get("/health")
async def health():
    checks = {}
    ok = True

    try:
        client = EventHubConsumerClient.from_connection_string(
            os.environ["EVENTHUB_CONNECTION_STRING"],
            consumer_group="$Default",
            eventhub_name=os.environ["EVENTHUB_NAME"],
        )
        async with client:
            partition_ids = await client.get_partition_ids()
            checks["eventHub"] = "ok"
            checks["partitionCount"] = len(partition_ids)
    except EventHubError as e:
        checks["eventHub"] = f"error: {e}"
        ok = False

    return {
        "status": "ok" if ok else "degraded",
        "service": "eventhubs-processor",
        "checks": checks,
    }

Node.js (Express)

// routes/health.js
import { EventHubConsumerClient } from "@azure/event-hubs";

export async function healthHandler(req, res) {
  const checks = {};
  let ok = true;

  try {
    const client = new EventHubConsumerClient(
      "$Default",
      process.env.EVENTHUB_CONNECTION_STRING,
      process.env.EVENTHUB_NAME
    );
    const partitions = await client.getPartitionIds();
    checks.eventHub = "ok";
    checks.partitionCount = partitions.length;
    await client.close();
  } catch (err) {
    checks.eventHub = `error: ${err.message}`;
    ok = false;
  }

  res.status(ok ? 200 : 503).json({
    status: ok ? "ok" : "degraded",
    service: "eventhubs-processor",
    checks,
  });
}

Step 2: Configure the Vigilmon HTTP Monitor

  1. Log in to VigilmonAdd Monitor → HTTP.
  2. URL: your processor service's health endpoint (e.g. https://processor.example.com/health).
  3. Check interval: 60 seconds.
  4. Response timeout: 15 seconds (Event Hubs client initialization can take a moment on cold start).
  5. Expected status: 200.
  6. JSON assertion: path status, expected value ok.

Tip: If your processor runs inside an Azure Virtual Network, expose the health port via an internal load balancer or Azure Application Gateway, then restrict Vigilmon's probe IPs in your NSG rules.


Step 3: Partition Lag Probe

Partition lag — events sitting unread in a partition — is the single most important Event Hubs health signal. Add a lag check to your health endpoint.

// Enhanced health endpoint with lag check
app.MapGet("/health", async (IConfiguration config) =>
{
    var checks = new Dictionary<string, object>();
    var ok = true;
    long totalLag = 0;

    await using var consumer = new EventHubConsumerClient(
        config["ConsumerGroup"],
        config["EventHubConnectionString"],
        config["EventHubName"]
    );

    var partitions = await consumer.GetPartitionIdsAsync();

    foreach (var partitionId in partitions)
    {
        var props = await consumer.GetPartitionPropertiesAsync(partitionId);
        var lastEnqueued = props.LastEnqueuedSequenceNumber;

        // Compare to your checkpoint store's last processed sequence
        // (simplified: use your actual checkpoint logic here)
        var checkpoint = await GetCheckpointSequenceNumber(partitionId);
        var lag = lastEnqueued - checkpoint;
        totalLag += lag;

        if (lag > long.Parse(config["MaxPartitionLag"] ?? "10000"))
        {
            checks[$"partition_{partitionId}"] = $"lagging: {lag} events";
            ok = false;
        }
        else
        {
            checks[$"partition_{partitionId}"] = $"ok (lag: {lag})";
        }
    }

    checks["totalLag"] = totalLag;

    return ok
        ? Results.Ok(new { status = "ok", checks })
        : Results.Json(new { status = "degraded", checks }, statusCode: 503);
});

Step 4: Heartbeat Monitoring for Azure Functions Event Triggers

Azure Functions with Event Hubs triggers run on-demand — there is no persistent process to HTTP-probe. Use Vigilmon's Heartbeat monitor and ping it from inside the function after successful processing.

// Functions/EventProcessor.cs
[FunctionName("ProcessEvents")]
public async Task Run(
    [EventHubTrigger("myeventhub", Connection = "EventHubConnection")] EventData[] events,
    ILogger log)
{
    foreach (var eventData in events)
    {
        var body = Encoding.UTF8.GetString(eventData.EventBody.ToArray());
        await ProcessEvent(body, log);
    }

    // Ping Vigilmon heartbeat after successful batch
    using var http = new HttpClient();
    await http.PostAsync(
        Environment.GetEnvironmentVariable("VIGILMON_HEARTBEAT_URL"),
        null
    );

    log.LogInformation($"Processed {events.Length} events — heartbeat sent");
}

In Vigilmon, create a Heartbeat monitor:

  • Set the grace period to 2–3× the maximum expected gap between event batches.
  • If the function stops firing (trigger stalled, partition lease lost), the heartbeat goes silent and Vigilmon fires an alert.

Store the heartbeat URL as an application setting in Azure:

az functionapp config appsettings set \
  --name my-function-app \
  --resource-group my-rg \
  --settings VIGILMON_HEARTBEAT_URL="https://vigilmon.online/api/heartbeat/<your-id>"

Step 5: Multi-Namespace and Consumer Group Strategy

For production Event Hubs setups with multiple namespaces or consumer groups, create one Vigilmon monitor per consumer group / processor service pair.

| Vigilmon monitor | What it watches | |---|---| | processor-ingest-$Default | Main ingest consumer, default group | | processor-analytics-analyticsGroup | Analytics consumer, dedicated group | | processor-archive-archiveGroup | Archive consumer, dedicated group | | dlq-probe-heartbeat | Dead-letter / unprocessable events check |

Group all monitors under a single Status Page so stakeholders see one aggregate status rather than a wall of individual checks.


Step 6: Alerting Strategy

| Alert type | Recommended channel | |---|---| | Processor service 503 | Email + PagerDuty webhook | | Heartbeat missed (function stalled) | Slack webhook | | Partition lag threshold breached | Slack + email | | Multi-region namespace degraded | Status page + PagerDuty |

Set alert escalation: notify immediately on first failure, re-notify every 5 minutes if still down.


What Vigilmon Catches That Azure Monitor Misses

| Scenario | Azure Monitor | Vigilmon | |---|---|---| | Processor pod crashes (HTTP 503) | Needs container health probe + alert rule | HTTP monitor catches immediately | | Azure Function trigger stalls | Needs custom metric + alert | Heartbeat grace period fires alert | | Partition lag grows silently | Needs Consumer Lag metric + alert rule | Lag probe via health endpoint | | Azure Monitor itself is degraded | Self-monitoring blind spot | Vigilmon is external — unaffected | | Cross-region namespace failover | Needs geo-redundant alert config | One monitor per namespace, same alert channel |


Event Hubs makes high-throughput streaming look easy. What it doesn't do is tell you when your consumers fall behind or stop running. External monitoring from Vigilmon gives you the independent signal you need — before partition lag turns into data loss.

Start monitoring your Event Hubs processors in under 5 minutes — register free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →