Angular  

Full-Stack Monitoring System: Hosted Services + Health Checks + Email Alerts + Angular Dashboard

Building modern web applications requires not only handling user requests but also monitoring the system, performing background tasks, and notifying admins of failures. In this tutorial, we’ll build a complete full-stack project combining:

  • Hosted Services – background task execution

  • Health Checks – monitoring application and dependencies

  • Email Alerts – notifying admins on failures

  • Angular Dashboard – frontend interface to visualize system status

We will use ASP.NET Core for the backend and Angular for the frontend.

Project Overview

The system will:

  1. Run background tasks to simulate system jobs.

  2. Periodically check application health (database and custom checks).

  3. Send email alerts if a health check fails.

  4. Display live health status and task logs on an Angular dashboard.

Backend Implementation (ASP.NET Core)

1. Create the Project

dotnet new webapi -n MonitoringApp
cd MonitoringApp

Install required packages:

dotnet add package Microsoft.AspNetCore.Diagnostics.HealthChecks
dotnet add package MailKit
dotnet add package AspNetCore.HealthChecks.SqlServer

2. Configure SMTP Settings

Add email configuration to appsettings.json:

"Email": {
  "From": "[email protected]",
  "SmtpServer": "smtp.gmail.com",
  "Port": 587,
  "Username": "[email protected]",
  "Password": "your-email-password"
}

3. Create Email Service

Services/EmailService.cs:

using MailKit.Net.Smtp;
using MimeKit;

public class EmailService
{
    private readonly IConfiguration _config;
    public EmailService(IConfiguration config) => _config = config;

    public async Task SendEmailAsync(string subject, string message)
    {
        var email = new MimeMessage();
        email.From.Add(MailboxAddress.Parse(_config["Email:From"]));
        email.To.Add(MailboxAddress.Parse(_config["Email:From"]));
        email.Subject = subject;
        email.Body = new TextPart("html") { Text = message };

        using var smtp = new SmtpClient();
        await smtp.ConnectAsync(_config["Email:SmtpServer"], int.Parse(_config["Email:Port"]), false);
        await smtp.AuthenticateAsync(_config["Email:Username"], _config["Email:Password"]);
        await smtp.SendAsync(email);
        await smtp.DisconnectAsync(true);
    }
}

4. Configure Health Checks

Program.cs:

using Microsoft.AspNetCore.Diagnostics.HealthChecks;

builder.Services.AddHealthChecks()
    .AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), name: "SQL Server")
    .AddCheck<CustomHealthCheck>("Custom Check");

app.MapHealthChecks("/health", new HealthCheckOptions
{
    Predicate = _ => true,
    ResponseWriter = async (context, report) =>
    {
        context.Response.ContentType = "application/json";
        var result = new
        {
            status = report.Status.ToString(),
            checks = report.Entries.Select(e => new { name = e.Key, status = e.Value.Status.ToString() })
        };
        await context.Response.WriteAsJsonAsync(result);
    }
});

5. Custom Health Check

Services/CustomHealthCheck.cs:

using Microsoft.Extensions.Diagnostics.HealthChecks;

public class CustomHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        bool isHealthy = DateTime.Now.Second % 2 == 0; // simulate success/failure
        return Task.FromResult(isHealthy 
            ? HealthCheckResult.Healthy("Service is running") 
            : HealthCheckResult.Unhealthy("Service failure"));
    }
}

6. Create Hosted Service for Monitoring

Services/MonitoringService.cs:

using Microsoft.Extensions.Diagnostics.HealthChecks;

public class MonitoringService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly EmailService _emailService;

    public MonitoringService(IServiceScopeFactory scopeFactory, EmailService emailService)
    {
        _scopeFactory = scopeFactory;
        _emailService = emailService;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using var scope = _scopeFactory.CreateScope();
            var healthCheckService = scope.ServiceProvider.GetRequiredService<HealthCheckService>();
            var report = await healthCheckService.CheckHealthAsync();

            foreach (var entry in report.Entries)
            {
                if (entry.Value.Status != HealthStatus.Healthy)
                {
                    await _emailService.SendEmailAsync(
                        $"Alert: {entry.Key} is unhealthy",
                        $"Health check {entry.Key} returned {entry.Value.Status}");
                }
            }

            await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
        }
    }
}

Register services in Program.cs:

builder.Services.AddSingleton<EmailService>();
builder.Services.AddHostedService<MonitoringService>();
builder.Services.AddHealthChecks();
builder.Services.AddScoped<CustomHealthCheck>();

Frontend Implementation (Angular)

1. Create Angular Component

ng generate component dashboard

2. Dashboard Service

dashboard.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({ providedIn: 'root' })
export class DashboardService {
  constructor(private http: HttpClient) {}

  getHealth() {
    return this.http.get('https://localhost:5001/health');
  }
}

3. Dashboard Component

dashboard.component.ts:

import { Component, OnInit } from '@angular/core';
import { DashboardService } from './dashboard.service';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html'
})
export class DashboardComponent implements OnInit {
  healthData: any;

  constructor(private dashboardService: DashboardService) {}

  ngOnInit() {
    this.dashboardService.getHealth().subscribe(data => this.healthData = data);
    setInterval(() => this.dashboardService.getHealth().subscribe(data => this.healthData = data), 30000);
  }
}

4. Dashboard Template

dashboard.component.html:

<h3>Application Health Dashboard</h3>
<table>
  <tr>
    <th>Service</th>
    <th>Status</th>
  </tr>
  <tr *ngFor="let check of healthData?.checks">
    <td>{{ check.name }}</td>
    <td [ngStyle]="{'color': check.status === 'Healthy' ? 'green' : 'red'}">
      {{ check.status }}
    </td>
  </tr>
</table>

Testing the System

  1. Run ASP.NET Core API (dotnet run)

  2. Run Angular app (ng serve)

  3. Open the Angular dashboard – it displays health status of SQL Server and custom checks

  4. When a health check fails, the hosted service sends an email alert automatically

Best Practices

  • Use tags to separate readiness vs liveness checks

  • Handle exceptions in hosted services to prevent crashes

  • Keep background tasks lightweight for performance

  • Secure health endpoints – only internal networks or authorized users

Conclusion

This project demonstrates a production-ready monitoring system combining:

  • Hosted Services: for background monitoring tasks

  • Health Checks: to ensure application and database availability

  • Email Alerts: notify admins immediately on failure

  • Angular Dashboard: visualize health data in real time

With this architecture, you can proactively monitor your applications, improve reliability, and provide a clean interface for admins.