Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MeterListener can't listen to RuntimeMetrics #110252

Closed
pfriesch opened this issue Nov 28, 2024 · 3 comments
Closed

MeterListener can't listen to RuntimeMetrics #110252

pfriesch opened this issue Nov 28, 2024 · 3 comments

Comments

@pfriesch
Copy link

Description

The constructor of MeterListener already publishes runtime metrics like dotnet.gc.collections. So runtime metrics can never be listened to by MeterListener.

Reproduction Steps

_listener = new MeterListener()
{
    InstrumentPublished = (instrument, listener) =>
    {
        // runtime metrics instruments like dotnet.gc.collections never get published like this
    }
};

Expected behavior

We would like a way to listen to runtime metrics like dotnet.gc.collections.

Actual behavior

Runtime metrics are published before MeterListener can listen to them.

Regression?

No response

Known Workarounds

No response

Configuration

tested on dotnet 9.0 on mac-arm64

Other information

No response

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Nov 28, 2024
@KalleOlaviNiemitalo
Copy link

Seems to be working alright on .NET 9:

using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Text;
using System.Threading;

namespace Runtime110252
{
    class Program
    {
        static void Main()
        {
            using (var listener = new MeterListener())
            {
                listener.SetMeasurementEventCallback<int>(Measure);
                listener.SetMeasurementEventCallback<long>(Measure);
                listener.SetMeasurementEventCallback<float>(Measure);
                listener.SetMeasurementEventCallback<double>(Measure);

                static void Measure<T>(Instrument instrument, T measurement, ReadOnlySpan<KeyValuePair<string, object?>> tags, object? state)
                {
                    var sb = new StringBuilder();
                    sb.Append($"Measured {typeof(T).Name}: {measurement}");

                    foreach (var tag in tags)
                    {
                        sb.Append($" [{tag.Key}={tag.Value}]");
                    }

                    Console.WriteLine(sb);
                }

                listener.InstrumentPublished = (instrument, listener) =>
                {
                    Console.WriteLine($"InstrumentPublished: {instrument.Name}");
                    if (instrument.Name == "dotnet.gc.collections")
                    {
                        listener.EnableMeasurementEvents(instrument);
                    }
                };

                listener.Start();

                while (true)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                    GC.Collect();
                    Console.WriteLine("Recording observable instruments.");
                    listener.RecordObservableInstruments();
                }
            }
        }
    }
}
InstrumentPublished: dotnet.gc.collections
InstrumentPublished: dotnet.process.memory.working_set
InstrumentPublished: dotnet.gc.heap.total_allocated
InstrumentPublished: dotnet.gc.last_collection.memory.committed_size
InstrumentPublished: dotnet.gc.last_collection.heap.size
InstrumentPublished: dotnet.gc.last_collection.heap.fragmentation.size
InstrumentPublished: dotnet.gc.pause.time
InstrumentPublished: dotnet.jit.compiled_il.size
InstrumentPublished: dotnet.jit.compiled_methods
InstrumentPublished: dotnet.jit.compilation.time
InstrumentPublished: dotnet.monitor.lock_contentions
InstrumentPublished: dotnet.thread_pool.thread.count
InstrumentPublished: dotnet.thread_pool.work_item.count
InstrumentPublished: dotnet.thread_pool.queue.length
InstrumentPublished: dotnet.timer.count
InstrumentPublished: dotnet.assembly.count
InstrumentPublished: dotnet.exceptions
InstrumentPublished: dotnet.process.cpu.count
InstrumentPublished: dotnet.process.cpu.time
Recording observable instruments.
Measured Int64: 1 [gc.heap.generation=gen2]
Measured Int64: 0 [gc.heap.generation=gen1]
Measured Int64: 0 [gc.heap.generation=gen0]
Recording observable instruments.
Measured Int64: 2 [gc.heap.generation=gen2]
Measured Int64: 0 [gc.heap.generation=gen1]
Measured Int64: 0 [gc.heap.generation=gen0]

@KalleOlaviNiemitalo
Copy link

"dotnet.gc.collections" is set up as an observable counter here:

s_meter.CreateObservableCounter(
"dotnet.gc.collections",
GetGarbageCollectionCounts,
unit: "{collection}",
description: "The number of garbage collections that have occurred since the process has started.");

So, verify that your code calls MeterListener.RecordObservableInstruments.

The InstrumentPublished delegate is called during MeterListener.Start.

@pfriesch
Copy link
Author

pfriesch commented Dec 3, 2024

Thanks for the help!

The issue was that MeterListener.RecordObservableInstruments was called in a background thread and MeterListener.Start was called in another thread. With MeterListener.Start being called in the background thread it works now.

@pfriesch pfriesch closed this as completed Dec 3, 2024
@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants