Skip to content

[SqlClient] Support native AoT#4062

Open
martincostello wants to merge 4 commits intoopen-telemetry:mainfrom
martincostello:sqlclient-native-aot
Open

[SqlClient] Support native AoT#4062
martincostello wants to merge 4 commits intoopen-telemetry:mainfrom
martincostello:sqlclient-native-aot

Conversation

@martincostello
Copy link
Copy Markdown
Member

@martincostello martincostello commented Apr 4, 2026

Changes

Add support for native AoT when targeting .NET 8+.

While an individual application's usage of SqlClient will determine the overall application's native AoT compatibility, this should avoid us being the blocker for an application that is otherwise able to use it.

Manually tested with a native AoT Minimal APIs application with SQL Server. Queries worked as expected, and metrics and traces are observed in the console.

Test Application and Results

WebApplication1.csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <InvariantGlobalization>false</InvariantGlobalization>
    <PublishAot>true</PublishAot>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Data.SqlClient" />
    <PackageReference Include="OpenTelemetry.Exporter.Console" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\src\OpenTelemetry.Instrumentation.SqlClient\OpenTelemetry.Instrumentation.SqlClient.csproj" />
  </ItemGroup>
</Project>

Program cs

using System.Data;
using Microsoft.Data.SqlClient;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateSlimBuilder(args);

builder.Logging.AddOpenTelemetry(builder => builder.AddConsoleExporter());

builder.Services.AddOpenTelemetry()
    .WithMetrics(builder => builder.AddConsoleExporter().AddSqlClientInstrumentation())
    .WithTracing(builder => builder.AddConsoleExporter().AddSqlClientInstrumentation().SetSampler(new AlwaysOnSampler()));

builder.Services.AddTransient(sp =>
{
    var connectionString = "Server=localhost,1433;Database=master;User=sa;Password=Password12345%%;Encrypt=False;TrustServerCertificate=True";
    return new SqlConnection(connectionString);
});

var app = builder.Build();

app.MapGet("/tables", async (SqlConnection db) =>
{
    await db.OpenAsync();

    await using var command = db.CreateCommand();

    command.CommandText = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES";

    var tables = new List<string>();

    await using (var reader = await command.ExecuteReaderAsync())
    {
        while (await reader.ReadAsync())
        {
            tables.Add(reader.GetString(0));
        }
    }

    return TypedResults.Text(string.Join(Environment.NewLine, tables));
});

app.MapGet("/server", async (SqlConnection db) =>
{
    await db.OpenAsync();

    await using var command = db.CreateCommand();

    command.CommandText = "sp_server_info";
    command.CommandType = CommandType.StoredProcedure;

    var serverInfo = new List<string>();

    await using (var reader = await command.ExecuteReaderAsync())
    {
        while (await reader.ReadAsync())
        {
            serverInfo.Add($"ID={reader.GetInt32(0)} , NAME={reader.GetString(1)} , VALUE={reader.GetString(2)}");
        }
    }

    return TypedResults.Text(string.Join(Environment.NewLine, serverInfo));
});

app.Run();

docker-compose.yml

services:
  mssql:
    image: mcr.microsoft.com/mssql/server:2025-latest@sha256:ca62ad2d75975074cbc9ac39a1329cd88af9cd3ea2ffb59d6302579a97cf32f4
    ports:
      - "1433:1433"
    environment:
    - ACCEPT_EULA=Y
    - MSSQL_SA_PASSWORD=Password12345%%

Results

❯ .\WebApplication1.exe
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
LogRecord.Timestamp:               2026-04-04T15:15:22.1379453Z
LogRecord.CategoryName:            Microsoft.Hosting.Lifetime
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.Body:                    Now listening on: {address}
LogRecord.Attributes (Key:Value):
    address: http://localhost:5000
    OriginalFormat (a.k.a Body): Now listening on: {address}
LogRecord.EventId:                 14
LogRecord.EventName:               ListeningOnAddress

Resource associated with LogRecord:
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.15.1
service.name: unknown_service:WebApplication1

info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
LogRecord.Timestamp:               2026-04-04T15:15:22.1407857Z
LogRecord.CategoryName:            Microsoft.Hosting.Lifetime
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.Body:                    Application started. Press Ctrl+C to shut down.
LogRecord.Attributes (Key:Value):
    OriginalFormat (a.k.a Body): Application started. Press Ctrl+C to shut down.

Resource associated with LogRecord:
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.15.1
service.name: unknown_service:WebApplication1

info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
LogRecord.Timestamp:               2026-04-04T15:15:22.1410227Z
LogRecord.CategoryName:            Microsoft.Hosting.Lifetime
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.Body:                    Hosting environment: {EnvName}
LogRecord.Attributes (Key:Value):
    EnvName: Production
    OriginalFormat (a.k.a Body): Hosting environment: {EnvName}

Resource associated with LogRecord:
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.15.1
service.name: unknown_service:WebApplication1

LogRecord.Timestamp:               2026-04-04T15:15:22.1412885Z
LogRecord.CategoryName:            Microsoft.Hosting.Lifetime
LogRecord.Severity:                Info
LogRecord.SeverityText:            Information
LogRecord.Body:                    Content root path: {ContentRoot}
LogRecord.Attributes (Key:Value):
    ContentRoot: D:\Coding\open-telemetry\opentelemetry-dotnet-contrib\artifacts\publish\WebApplication1\release
    OriginalFormat (a.k.a Body): Content root path: {ContentRoot}

Resource associated with LogRecord:
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.15.1
service.name: unknown_service:WebApplication1

info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Coding\open-telemetry\opentelemetry-dotnet-contrib\artifacts\publish\WebApplication1\release
Resource associated with Metrics:
        telemetry.sdk.name: opentelemetry
        telemetry.sdk.language: dotnet
        telemetry.sdk.version: 1.15.1
        service.name: unknown_service:WebApplication1
Activity.TraceId:            fcbb857ab6e34373bbaf71831f7daacb
Activity.SpanId:             79a6ca84089310aa
Activity.TraceFlags:         Recorded
Activity.ParentSpanId:       008d8ee92d32acfe
Activity.DisplayName:        EXECUTE sp_server_info
Activity.Kind:               Client
Activity.StartTime:          2026-04-04T15:15:40.7554979Z
Activity.Duration:           00:00:00.0014818
Activity.Tags:
    db.system.name: microsoft.sql_server
    db.namespace: master
    server.address: localhost
    db.operation.name: EXECUTE
    db.stored_procedure.name: sp_server_info
    db.query.summary: EXECUTE sp_server_info
Instrumentation scope (ActivitySource):
    Name: OpenTelemetry.Instrumentation.SqlClient
    Version: 1.15.2-alpha.0.116
    Schema URL: https://opentelemetry.io/schemas/1.33.0
Resource associated with Activity:
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.15.1
    service.name: unknown_service:WebApplication1

Resource associated with Metrics:
        telemetry.sdk.name: opentelemetry
        telemetry.sdk.language: dotnet
        telemetry.sdk.version: 1.15.1
        service.name: unknown_service:WebApplication1

Metric Name: db.client.operation.duration, Description: Duration of database client operations., Unit: s, Metric Type: Histogram
Instrumentation scope (Meter):
        Name: OpenTelemetry.Instrumentation.SqlClient
        Version: 1.15.2-alpha.0.116
        Schema URL: https://opentelemetry.io/schemas/1.33.0
(2026-04-04T15:15:22.1297320Z, 2026-04-04T15:15:42.1359078Z] db.namespace: master db.operation.name: EXECUTE db.query.summary: EXECUTE sp_server_info db.stored_procedure.name: sp_server_info db.system.name: microsoft.sql_server server.address: localhost
Value: Sum: 0.0014818 Count: 1 Min: 0.0014818 Max: 0.0014818
(-Infinity,0.001]:0
(0.001,0.005]:1
(0.005,0.01]:0
(0.01,0.05]:0
(0.05,0.1]:0
(0.1,0.5]:0
(0.5,1]:0
(1,5]:0
(5,10]:0
(10,+Infinity]:0

Activity.TraceId:            47513f1836975ee861f0beb8739138be
Activity.SpanId:             694f3831f6f1b15c
Activity.TraceFlags:         Recorded
Activity.ParentSpanId:       852d5dbe6923dc2e
Activity.DisplayName:        SELECT INFORMATION_SCHEMA.TABLES
Activity.Kind:               Client
Activity.StartTime:          2026-04-04T15:15:45.7076602Z
Activity.Duration:           00:00:00.0031571
Activity.Tags:
    db.system.name: microsoft.sql_server
    db.namespace: master
    server.address: localhost
    db.query.text: SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
    db.query.summary: SELECT INFORMATION_SCHEMA.TABLES
Instrumentation scope (ActivitySource):
    Name: OpenTelemetry.Instrumentation.SqlClient
    Version: 1.15.2-alpha.0.116
    Schema URL: https://opentelemetry.io/schemas/1.33.0
Resource associated with Activity:
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.15.1
    service.name: unknown_service:WebApplication1

Resource associated with Metrics:
        telemetry.sdk.name: opentelemetry
        telemetry.sdk.language: dotnet
        telemetry.sdk.version: 1.15.1
        service.name: unknown_service:WebApplication1

Metric Name: db.client.operation.duration, Description: Duration of database client operations., Unit: s, Metric Type: Histogram
Instrumentation scope (Meter):
        Name: OpenTelemetry.Instrumentation.SqlClient
        Version: 1.15.2-alpha.0.116
        Schema URL: https://opentelemetry.io/schemas/1.33.0
(2026-04-04T15:15:22.1297320Z, 2026-04-04T15:15:52.1373618Z] db.namespace: master db.operation.name: EXECUTE db.query.summary: EXECUTE sp_server_info db.stored_procedure.name: sp_server_info db.system.name: microsoft.sql_server server.address: localhost
Value: Sum: 0.0014818 Count: 1 Min: 0.0014818 Max: 0.0014818
(-Infinity,0.001]:0
(0.001,0.005]:1
(0.005,0.01]:0
(0.01,0.05]:0
(0.05,0.1]:0
(0.1,0.5]:0
(0.5,1]:0
(1,5]:0
(5,10]:0
(10,+Infinity]:0

Merge requirement checklist

  • CONTRIBUTING guidelines followed (license requirements, nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
  • Changes in public API reviewed (if applicable)

Add support for native AoT when targeting .NET 8+.
@github-actions github-actions bot added the comp:instrumentation.sqlclient Things related to OpenTelemetry.Instrumentation.SqlClient label Apr 4, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 4, 2026

Codecov Report

❌ Patch coverage is 94.87179% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.89%. Comparing base (d1717a8) to head (ba036d9).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ient/Implementation/SqlClientDiagnosticListener.cs 94.87% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4062      +/-   ##
==========================================
+ Coverage   72.75%   72.89%   +0.13%     
==========================================
  Files         458      448      -10     
  Lines       17876    17820      -56     
==========================================
- Hits        13006    12989      -17     
+ Misses       4870     4831      -39     
Flag Coverage Δ
unittests-Instrumentation.Cassandra ?
unittests-Instrumentation.EntityFrameworkCore 80.80% <ø> (ø)
unittests-Instrumentation.SqlClient 85.57% <94.87%> (-0.18%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...trumentation.SqlClient/SqlClientInstrumentation.cs 92.00% <ø> (ø)
...lClient/SqlClientMeterProviderBuilderExtensions.cs 100.00% <ø> (ø)
...ation.SqlClient/TracerProviderBuilderExtensions.cs 100.00% <ø> (ø)
...ient/Implementation/SqlClientDiagnosticListener.cs 87.67% <94.87%> (-0.41%) ⬇️

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Remove hallucinated justifications and comments from Copilot.
@martincostello martincostello marked this pull request as ready for review April 7, 2026 06:25
@martincostello martincostello requested a review from a team as a code owner April 7, 2026 06:25
Copilot AI review requested due to automatic review settings April 7, 2026 06:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to make OpenTelemetry.Instrumentation.SqlClient usable in native AOT scenarios on .NET 8+ by removing trimming/AOT blockers and adjusting SqlClient DiagnosticSource payload handling.

Changes:

  • Marks the SqlClient instrumentation package as AOT compatible for .NETCoreApp TFMs.
  • Removes RequiresUnreferencedCode annotations from SqlClient public extension methods and internal types.
  • Refactors SqlClientDiagnosticListener to rely more on IDbCommand/DbConnection APIs and adds trim-warning suppressions around PropertyFetcher usage.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/OpenTelemetry.Instrumentation.SqlClient/TracerProviderBuilderExtensions.cs Removes trimming warning annotations from tracing registration APIs.
src/OpenTelemetry.Instrumentation.SqlClient/SqlClientMeterProviderBuilderExtensions.cs Removes trimming warning annotations from metrics registration API.
src/OpenTelemetry.Instrumentation.SqlClient/SqlClientInstrumentation.cs Removes trimming warning annotation and trimming message constant.
src/OpenTelemetry.Instrumentation.SqlClient/OpenTelemetry.Instrumentation.SqlClient.csproj Sets <IsAotCompatible>true</IsAotCompatible> for .NETCoreApp TFMs.
src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs Reduces reflection usage for command/connection data; adds trim suppressions for remaining PropertyFetcher calls.
src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md Documents native AOT support addition.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Use local variable that's already captured the command's connection.
- Dispose of created command.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:instrumentation.sqlclient Things related to OpenTelemetry.Instrumentation.SqlClient

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants