Description
Problem Description
System.Diagnostics.Metrics completely fails to register any meters when AOT/linking is enabled on .NET MAUI Android and iOS. The static field Meter.s_allMeters remains empty, causing MeterListener.InstrumentPublished callbacks to never fire.
This seems to be BCL issue, not an OpenTelemetry SDK issue. Any code relying on System.Diagnostics.Metrics will fail silently under AOT/linking on both Android and iOS.
Tested and confirmed on both platforms:
- Android Release build (AOT enabled): Metrics completely broken, no logs
- iOS Release build (Linking enabled): Metrics completely broken, no logs
- Debug builds on both platforms: Metrics work perfectly
Reproduction Steps
Code
// Create a meter
var meter = new Meter("Mobile", "1.0.0");
// Create instruments
var counter = meter.CreateCounter<long>("app.launch.total", "1", "Total app launches");
var histogram = meter.CreateHistogram<double>("photo.capture.time", "ms", "Photo capture time");
// Record values
counter.Add(1);
histogram.Record(1234.5);
// Set up listener
var listener = new MeterListener();
listener.InstrumentPublished = (instrument, listener) =>
{
Console.WriteLine($"✅ INSTRUMENT PUBLISHED: {instrument.Name}");
listener.EnableMeasurementEvents(instrument);
};
listener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
{
Console.WriteLine($"📊 MEASUREMENT: {instrument.Name} = {measurement}");
});
listener.Start();
Expected behavior
Expected Behavior (Debug build)
Diagnostic logs show successful registration:
[OTEL-DIAGNOSTICS] Registered Meters via Reflection
Static Fields in Meter: 2
Field: s_allMeters
Type: List`1
Meter Count: 5
Meter #1:
Name: System.Runtime
Instruments Count: 19
Meter #2:
Name: Mobile
Instruments Count: 10
Instrument #1: photo.taken.total
...
[OTEL-DIAGNOSTICS] Manual MeterListener Test
✅ INSTRUMENT PUBLISHED!
Meter: Mobile
Instrument: photo.taken.total
Type: Counter`1
✅ Enabled measurements for photo.taken.total
✅ INSTRUMENT PUBLISHED!
Meter: Mobile
Instrument: app.launch.total
Type: Counter`1
✅ Enabled measurements for app.launch.total
Listener started!
Instruments found: 39
[CONSOLE-EXPORTER] TestExporter Export called at 13:35:01
[CONSOLE-EXPORTER] ✅ Found 1 metrics:
Metric: app.launch.total
Description: Total number of application launches
Unit: 1
Type: LongSum
Point #1:
Tags: device.manufacturer=samsung, device.model=SM-A057G, device.os=15, device.platform=Android, system_language=pl-PL
Value (Sum): 1
Start: 12:34:53
End: 12:35:01
Total Points: 1
Actual behavior
Actual Behavior (Release + AOT)
Diagnostic logs show complete failure:
[OTEL-DIAGNOSTICS] Registered Meters via Reflection
Static Fields in Meter: 2
Field: s_allMeters
Type: List`1
Meter Count: 0
⚠️ NO METERS REGISTERED!
Field: <IsSupported>k__BackingField
Type: Boolean
[OTEL-DIAGNOSTICS] Manual MeterListener Test
Starting listener...
Listener started!
⚠️ NO INSTRUMENTS PUBLISHED TO LISTENER!
This means MeterListener.InstrumentPublished is NOT being called.
This is the root cause of the AOT issue.
Instruments found: 0
[CONSOLE-EXPORTER] TestExporter Export called at 13:28:48
[CONSOLE-EXPORTER] Batch size: 0
[CONSOLE-EXPORTER] ⚠️ EMPTY BATCH - No metrics collected!
Regression?
No response
Known Workarounds
No response
Configuration
Environment
- .NET Version: 10.0 (net10.0-android, net10.0-ios)
- Platform:
- Android (tested on Samsung SM-A057G, Android 15)
- iOS (tested on iPhone 16 pro, iOS 26.2.1)
- AOT Settings (Android Release):
<RunAOTCompilation>true</RunAOTCompilation>
<AndroidAotMode>Hybrid</AndroidAotMode>
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
- iOS Release Settings:
<MtouchLink>SdkOnly</MtouchLink>
<MtouchExtraArgs>--optimize=register-protocols</MtouchExtraArgs>
<UseLlvm>false</UseLlvm>
<Optimize>true</Optimize>
- OpenTelemetry Version: 1.14.0
- Build Configuration: Release
Other information
Root Cause Analysis
When new Meter("Mobile", "1.0.0") is called, the meter instance is created but never registered in the static Meter.s_allMeters list. This appears to be caused by AOT compilation breaking the reflection-based registration mechanism in the Meter constructor or static initializer.
Sequence of Events
Working (Debug):
new Meter("Mobile", "1.0.0") → Creates meter instance
- Meter registers itself in
Meter.s_allMeters static field
MeterListener.Start() → Iterates s_allMeters and fires InstrumentPublished callbacks
- Measurements are collected and exported
Broken (Release + AOT):
new Meter("Mobile", "1.0.0") → Creates meter instance
- ❌ Meter fails to register in
Meter.s_allMeters (remains empty)
MeterListener.Start() → Iterates empty list, no callbacks fired
- ❌ No measurements collected (empty batches exported)
Diagnostic Evidence
We created a manual console exporter that directly subscribes to MeterListener to verify the root cause:
public class ConsoleTestMetricExporter : BaseExporter<Metric>
{
public override ExportResult Export(in Batch<Metric> batch)
{
var count = 0;
foreach (var metric in batch)
{
count++;
Console.WriteLine($"Metric: {metric.Name}");
}
if (count == 0)
{
Console.WriteLine("[CONSOLE-EXPORTER] ⚠️ EMPTY BATCH - No metrics collected!");
}
return ExportResult.Success;
}
}
Result: The exporter is called correctly, but the batch is always empty in Release+AOT builds because MeterListener never receives any instruments.
Additional Information
- LinkerConfig.xml with
<assembly fullname="System.Diagnostics.DiagnosticSource" preserve="All" /> does not help
- rd.xml preservation directives do not help
- The issue occurs even with
AndroidLinkMode=None
- The issue is specific to AOT compilation; trimming alone does not cause the problem
Related Issues
Description
Problem Description
System.Diagnostics.Metricscompletely fails to register any meters when AOT/linking is enabled on .NET MAUI Android and iOS. The static fieldMeter.s_allMetersremains empty, causingMeterListener.InstrumentPublishedcallbacks to never fire.This seems to be BCL issue, not an OpenTelemetry SDK issue. Any code relying on
System.Diagnostics.Metricswill fail silently under AOT/linking on both Android and iOS.Tested and confirmed on both platforms:
Reproduction Steps
Code
Expected behavior
Expected Behavior (Debug build)
Diagnostic logs show successful registration:
Actual behavior
Actual Behavior (Release + AOT)
Diagnostic logs show complete failure:
Regression?
No response
Known Workarounds
No response
Configuration
Environment
Other information
Root Cause Analysis
When
new Meter("Mobile", "1.0.0")is called, the meter instance is created but never registered in the staticMeter.s_allMeterslist. This appears to be caused by AOT compilation breaking the reflection-based registration mechanism in theMeterconstructor or static initializer.Sequence of Events
Working (Debug):
new Meter("Mobile", "1.0.0")→ Creates meter instanceMeter.s_allMetersstatic fieldMeterListener.Start()→ Iteratess_allMetersand firesInstrumentPublishedcallbacksBroken (Release + AOT):
new Meter("Mobile", "1.0.0")→ Creates meter instanceMeter.s_allMeters(remains empty)MeterListener.Start()→ Iterates empty list, no callbacks firedDiagnostic Evidence
We created a manual console exporter that directly subscribes to
MeterListenerto verify the root cause:Result: The exporter is called correctly, but the batch is always empty in Release+AOT builds because
MeterListenernever receives any instruments.Additional Information
<assembly fullname="System.Diagnostics.DiagnosticSource" preserve="All" />does not helpAndroidLinkMode=NoneRelated Issues