Creating custom Watcher

Piotr Gankiewicz edited this page Jun 26, 2016 · 4 revisions

If you're looking how to create your own Watcher that can be seamlessly used within the Warden instance, then you're in the right place. It's quite easy to do, so let me guide you through the whole process, as described in my blog post.

The one and only important thing is the IWatcher interface that provides a contract for all of the available watchers:

public interface IWatcher
{
    string Name { get; }
    Task<IWatcherCheckResult> ExecuteAsync();
}

By convention each watcher has a default name that can be changed via configuration (e.g. the WebWatcher has a name Web Watcher and so on) and the ExecuteAsync() method returns a custom implementation of the IWatcherCheckResult (so again, for the WebWatcher it would be the WebWatcherCheckResult that derives from the base WatcherCheckResult type).

public class ProcessWatcherCheckResult : WatcherCheckResult
{
    public ProcessWatcherCheckResult(IWatcher watcher, bool isValid, string description)
        : base(watcher, isValid, description)
    {
    }
}

Having that custom result type, we can go further and finally create a new class called ProcessWatcher that implements the IWatcher interface.

public class ProcessWatcher : IWatcher
{
    public string Name { get; }

    public async Task<IWatcherCheckResult> ExecuteAsync()
    {
        return await Task.FromResult(new ProcessWatcherCheckResult(this, false, string.Empty));
    }
}

Before we implement the actual functionality of the process watcher, let’s create a console application to check if it’s possible to use this new watcher (you can also create a test project for that case). Select the .NET Core Console Application, name it ProcessWatcher.App, install the Warden library using NuGet and add the reference to the ProcessWatcher class library. Once it’s done, add the following code to the console app to see if it works correctly:

public class Program
{
    public static void Main(string[] args)
    {
        var warden = ConfigureWarden();
        Task.WaitAll(warden.StartAsync());
    }

    private static IWarden ConfigureWarden()
    {
        var processWatcher = new ProcessWatcher();
        var wardenConfiguration = WardenConfiguration
            .Create()
            .AddWatcher(processWatcher, hooks =>
            {
                hooks.OnCompleted(result => Console.WriteLine("Process watcher check has completed."));
            })
            .Build();

        return Warden.Warden.Create(wardenConfiguration);
    }
}

If you run the application now, you will be able to see the following console output: Process watcher check has completed. displayed in a new line every 5 seconds – that’s a good sign, seems that everything works, and the only thing that’s left is the actual process monitoring. Before we do that, we can tweak our result type first:

public class ProcessWatcherCheckResult : WatcherCheckResult
{
    public string ProcessName { get; }

    public ProcessWatcherCheckResult(ProcessWatcher watcher, string processName, 
        bool isValid, string description) : base(watcher, isValid, description)
    {
        ProcessName = processName;
    }
}

Now the watcher type is strongly typed and the result class contains additional property that can be used e.g. for some data processing. Eventually, we can implement the process watcher logic, just make sure that you install the System.Diagnostics.Process package first:

public class ProcessWatcher : IWatcher
{
    private readonly string _processName;
    public string Name { get; } = "Process Watcher";

    public ProcessWatcher(string processName)
    {
        if(string.IsNullOrWhiteSpace(processName))
            throw new ArgumentException("Process name can not be empty", nameof(processName));

        _processName = processName;
    }

    public async Task<IWatcherCheckResult> ExecuteAsync()
    {
        var process = Process.GetProcessesByName(_processName);
        var isRunning = process.Length > 0;
        var description = $"Process '{_processName}' is {(isRunning ? string.Empty : "not ")}running.";
        var result = new ProcessWatcherCheckResult(this, _processName, isRunning, description);

        return await Task.FromResult(result);
    }
}

And the final step – fix the console application, by updating these 2 lines:

var processWatcher = new ProcessWatcher("mongod");
//Configure Warden and fix the hooks section to make it look like this:
hooks.OnCompleted(result => Console.WriteLine(result.WatcherCheckResult.Description));

Run the application now and you shall receive a much more informative description about the process monitoring status. It’s checking whether the MongoDB instance is running, but feel free to use any other process name.

And that’s how easy it is to create your own watcher – all of the official watchers, integrations and Warden configuration options can be used along with the brand new process watcher.

For more advanced stuff like creating your own extension methods so that you could e.g. invoke AddProcessWatcher() while configuring the Warden, please browse the source code, for example the following class Extensions.

You can download the source code for this example by clicking here.