Vaughan Reid's blog

Measuring .Net Core: 3. dotnet-counters with a custom provider

This is the third in a series of posts covering measuring a .Net Core application. If you want to try follow the code then you can have a look at this repo: Blog-Diagnostics.

This is the plan for the series. I will update this with links as it evolves.

  1. Setting up local diagnostics tools
  2. Using dotnet-counters
  3. dotnet-counters with a custom provider
  4. Using dotnet-gcdump to investigate memory leaks
  5. Creating a dump file when a ASP.NET Core application crashes

Now that we have tried using the built in providers for the dotnet-counters tool, lets try create our own custom one.

You can achieve this by using a custom EventSource. You just need to create a class that implements EventSource and populate different types of counters depending on what you are trying to achieve.

The event source that I created will report as Diagnostics.Person and does a silly measure of the amount of gets of people with steve in their name. Way too many steves around ;)


[EventSource(Name = PersonEventSource.SourceName)]
public class PersonEventSource : EventSource
{
	readonly EventCounter steveCreatedCounter;

	int steveCreatedCount = 0;

	readonly EventCounter notSteveCreatedCounter;

	int notSteveCreatedCount = 0;

	public PersonEventSource()
	{
		steveCreatedCounter = new EventCounter("Steve created", this);
		notSteveCreatedCounter = new EventCounter("Not Steve created", this);
	}

	const string SourceName = "Diagnostics.Person";
	[Event(1, Level= EventLevel.Informational)]
	public void Created(string name)
	{
		if (name.Contains("Steve", StringComparison.InvariantCultureIgnoreCase))
		{
			steveCreatedCounter.WriteMetric(Interlocked.Increment(ref steveCreatedCount));
		}
		else
		{
			notSteveCreatedCounter.WriteMetric(Interlocked.Increment(ref notSteveCreatedCount));
		}
	}
}

I then register it as a singleton in startup ConfigureServices.


public void ConfigureServices(IServiceCollection services)
{
	services.AddControllers();

	services.AddSingleton<PersonEventSource>();

	services.AddScoped<IPersonService, PersonService>();
}

Then injected it into my service.


public class PersonService : IPersonService
{
	private readonly PersonEventSource personEventSource;

	public PersonService(PersonEventSource personEventSource)
	{
		this.personEventSource = personEventSource;
	}
	public async Task<Person> GetAsync(string name)
	{
		await Task.Delay(TimeSpan.FromSeconds(1));
		personEventSource.Created(name);
		return new Person { Name = name, Age = 18 };
	}
}

After restarting the application I can run the counters command as:

dotnet counters monitor -p 24 --providers Diagnostics.Person

This will give the following result after a little prodding.

Obviously not a real example but hopefully you can see the power. An example I could use at work is for an application processing realtime trades. I could add metrics on the average process time per asset class.