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:
Run background tasks to simulate system jobs.
Periodically check application health (database and custom checks).
Send email alerts if a health check fails.
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
Run ASP.NET Core API (dotnet run)
Run Angular app (ng serve)
Open the Angular dashboard – it displays health status of SQL Server and custom checks
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.