P M A J E W S K I

Please Wait For Loading

How to create simple healthcheck in 5 minutes - Software Developer's Tour

    You Are Currently Here!
  • Home
  • ProgrammingHow to create simple healthcheck in 5 minutes
heart beat

How to create simple healthcheck in 5 minutes

In the past, when I used a different hosting provider than I do now, I often encountered situations where the website was unavailable.

How can we check if a website is accessible without constantly refreshing it in the browser?

Just write your own app!

First, let’s think about what we want to do.

Application requirements

  • We want the ability to pass multiple URLs to the application
  • We haven't time, so our choice will be Console application😊
  • The application should inform us whether the request has returned a success status code or has failed.
  • To avoid being banned for DDoS, the application should incorporate a delay.between requests

You can read more about DDoS attack here.

I will paste here entire code and then we’ll talk about it closer.

class Program
{
    static async Task Main(string[] args)
    {
        const string fileName = "sites.txt";
        TimeSpan delay = TimeSpan.FromSeconds(10);
        var fileContent = await ValidateFileAsync(fileName);
        var urlsWithClients = PrepareUrls(fileContent);

        await RunInfinity(urlsWithClients, delay);
    }

    static async Task<bool> IsAliveAsync(HttpClient httpClient)
    {
        try
        {
            var result = await httpClient.GetAsync(string.Empty);

            return result.IsSuccessStatusCode;
        } catch
        {

        }

        return false;
    }

    static async Task<string[]> ValidateFileAsync(string filePath)
    {
        var doesFileExist = File.Exists(filePath);

        if (!doesFileExist)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Couldn't find file");
            Console.ResetColor();

            return [];
        }

        var fileContent = await File.ReadAllLinesAsync(filePath);

        if(!fileContent.Any())
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("There is no url in file");
            Console.ResetColor();
        }

        return fileContent;
    }

    static (string Url, HttpClient httpClient)[] PrepareUrls(IEnumerable<string> urls)
    {
        return urls.Select(x => {
            var httpClient = new HttpClient
            {
                BaseAddress = new Uri(x)
            };

            return (x, httpClient);
        }).ToArray();
    }

    static Task RunInfinity(IEnumerable<(string Url, HttpClient httpClient)> urls, TimeSpan delay)
    {
        return Task.Run(async () =>
        {
            while (true)
            {
                foreach (var (x, httpClient) in urls)
                {
                    var isAlive = await IsAliveAsync(httpClient);

                    Console.Write($"{DateTime.Now:dd-MM HH:mm:ss}\tUrl: {x}\t");
                    if (isAlive)
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine("OK");
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("FAILED");
                    }
                    Console.ResetColor();

                }

                await Task.Delay(delay);
            }
        });
    }
}

Let’s start analyzing 🙂

The main function

        const string fileName = "sites.txt";
        TimeSpan delay = TimeSpan.FromSeconds(10);
        var fileContent = await ValidateFileAsync(fileName);
        var urlsWithClients = PrepareUrls(fileContent);

        await RunInfinity(urlsWithClients, delay);

First two lines are our configuration. When creating this application, I assumed that the user will provide urls to check in file named sites.txt and this file will be next to .exe file. I aimed to keep it as straightforward as I could.

Then we have simple validation, I think we can just skip it. I added it only because I got an exception during development 😁

I didn’t want to read the file twice, so the validation function immediately passes on the read data.

    static (string Url, HttpClient httpClient)[] PrepareUrls(IEnumerable<string> urls)
    {
        return urls.Select(x => {
            var httpClient = new HttpClient
            {
                BaseAddress = new Uri(x)
            };

            return (x, httpClient);
        }).ToArray();
    }

I think this is a good place to refactor. We don’t need a tuple with a string and HttpClient here. Since HttpClient already contains BaseAddress itself, we could retrieve this information directly from it.

Function IsAliveAsync is only wrapper for GetAsync method.

And last our system’s core, our heart beating method

        return Task.Run(async () =>
        {
            while (true)
            {
                foreach (var (x, httpClient) in urls)
                {
                    var isAlive = await IsAliveAsync(httpClient);

                    Console.Write($"{DateTime.Now:dd-MM HH:mm:ss}\tUrl: {x}\t");
                    if (isAlive)
                    {
                        Console.ForegroundColor = ConsoleColor.Green;
                        Console.WriteLine("OK");
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("FAILED");
                    }
                    Console.ResetColor();

                }

                await Task.Delay(delay);
            }
        });

What are we doing here? There are just two loops, one while loop, which with Task.Delay (line 24) will be our beating heart.

Inside while loop is loop over our urls and and that’s it, no philosophy involved.

Additionally, one “if” statement to check whether the request was successful or not.

How it works?

Create file with urls

file with urls

Start application

application result

At the end

I strongly encourage you to write simple applications. Writing one can take as much time as finding the right one online.

leave a comment