Skip to content

[API Proposal]: InstrumentRecorder - notification of new measurements #86783

Closed
@JamesNK

Description

Background and motivation

InstrumentRecorder is an API new in .NET 8 to make testing metrics easier:

var instrumentRecorder = new InstrumentRecorder<double>(meterRegistry, "Microsoft.AspNetCore.Hosting", "request-duration"); 

var response = await httpClient.GetAsync("/");

var measurements = instrumentRecorder.GetMeasurements();
// Assert contents of measurements

However, there are some cases where there are race conditions because measurements happen on a different thread. For example, with the request-duration counter above, the server can return the failing response to the client before the metric is recorded. GetMeasurements may or may not have an item. The test is flaky.

It would be useful if someone using InstrumentRecorder could get notifications of new measurements.

API Proposal

namespace System.Diaganostics.Metrics;

public class InstumentRecorder<T>
{
    // Wait for any number of measurements.
    public Task<IEnumerable<Measurement<T>> WaitForMeasurementsAsync(bool clear = false)
        => WaitForMeasurementsAsync(minimumCount: 1, clear);

    // Wait for measurements greater than count.
    public Task<IEnumerable<Measurement<T>> WaitForMeasurementsAsync(int minimumCount, bool clear = false);
}

WaitForMeasurementsAsync returns when one or more measurements are available. If there are already measurements, the method returns immediately.

Random thoughts:

  • It might be worth having a count overload on the method, allowing someone to wait for an expected number of measurements, e.g. WaitForMeasurementsAsync(minimumCount: 5). Edit: Added above.
  • Are parallel calls to WaitForMeasurementsAsync allowed? What happens if there are parallel calls to WaitForMeasurementsAsync, and they have clear: true?
  • If someone wants to wait multiple times, they must clear the measurements (or increase the wait count). The example below shows this.

API Usage

var instrumentRecorder = new InstrumentRecorder<double>(meterRegistry, "Microsoft.AspNetCore.Hosting", "request-duration"); 

var response1 = await httpClient.GetAsync("/");
var measurements1 = await instrumentRecorder.WaitForMeasurementsAsync(clear: true);
// Assert contents of measurements

var response2 = await httpClient.GetAsync("/");
var measurements2 = await instrumentRecorder.WaitForMeasurementsAsync(clear: true);
// Assert contents of measurements

Alternative Designs

An API to register a callback that executes for each measurement:

var tcs = new TaskCompletionSource();
var instrumentRecorder = new InstrumentRecorder<double>(meterRegistry, "Microsoft.AspNetCore.Hosting", "request-duration"); 
instrumentRecorder.Register(m => 
{ 
    tcs.TrySetResult(); 
});

// Do stuff

await tcs.Task;
var measurements = await instrumentRecorder.GetMeasurements();
// Assert contents of measurements

Risks

No response

Metadata

Assignees

Labels

api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Diagnostics.MetricenhancementProduct code improvement that does NOT require public API changes/additions

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions