DatadogNet.Extensions.DependencyInjection 3.14.0.6

dotnet add package DatadogNet.Extensions.DependencyInjection --version 3.14.0.6
                    
NuGet\Install-Package DatadogNet.Extensions.DependencyInjection -Version 3.14.0.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DatadogNet.Extensions.DependencyInjection" Version="3.14.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DatadogNet.Extensions.DependencyInjection" Version="3.14.0.6" />
                    
Directory.Packages.props
<PackageReference Include="DatadogNet.Extensions.DependencyInjection" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DatadogNet.Extensions.DependencyInjection --version 3.14.0.6
                    
#r "nuget: DatadogNet.Extensions.DependencyInjection, 3.14.0.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DatadogNet.Extensions.DependencyInjection@3.14.0.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DatadogNet.Extensions.DependencyInjection&version=3.14.0.6
                    
Install as a Cake Addin
#tool nuget:?package=DatadogNet.Extensions.DependencyInjection&version=3.14.0.6
                    
Install as a Cake Tool

DatadogNet

NuGet release Targets: net8.0 | net9.0 | net10.0 dd-sdk-ios 3.14.0 dd-sdk-android 3.12.1 Licence: MIT AND Apache-2.0

One Datadog API for .NET MAUI. Real User Monitoring, Logs, Trace, Session Replay and crash reporting on Android, iOS and Mac Catalyst, written once in shared code.

Built on DatadogNet.iOS, DatadogNet.Android and DatadogNet.Mac, which bind the native SDKs. Those two are faithful projections of Kotlin and Objective-C, and consequently share almost no shape: Configuration.Builder(...).UseSite(...).Build() against new DDConfiguration { Site = … }, GlobalRumMonitor.Get() against DDRUMMonitor.Shared, io.opentracing against OTTracer. This package is the layer that makes a MAUI app not care.

dotnet add package DatadogNet.Maui
builder
    .UseMauiApp<App>()
    .UseDatadog(new DatadogConfiguration
    {
        ClientToken     = "<CLIENT_TOKEN>",
        Env             = "production",
        Service         = "my-app",
        Site            = DatadogSite.Us1,
        TrackingConsent = TrackingConsent.Granted,
        Rum             = new RumOptions { ApplicationId = "<RUM_APPLICATION_ID>" },
        Logs            = new LogsOptions(),
    });

That is the whole setup. Pages become RUM views, ILogger output goes to Datadog, and unhandled exceptions are reported — on both platforms.


Contents


Packages

Six packages. The version is <dd-sdk-ios version>.<binding revision>3.14.0.1 is dd-sdk-ios 3.14.0, binding revision 1, and the Android side of the same release is dd-sdk-android 3.12.1.

Why one version names one SDK. Datadog releases the iOS and Android SDKs on separate cadences and their version numbers have never matched. A façade over both has to pick one line to name itself after or invent a third numbering that maps to nothing. It picks iOS, because that is the line whose version is furthest ahead, and states the Android version everywhere it states its own.

Package What it is for Depends on
DatadogNet.Maui The one you install in a MAUI app. UseDatadog, automatic RUM views from page navigation, an ILogger provider, unhandled-exception reporting, HttpClient instrumentation. DatadogNet, DatadogNet.Extensions.DependencyInjection
DatadogNet The API itself: configuration, RUM, Logs, Trace, Session Replay, consent, user and account info, DatadogHttpMessageHandler. Usable from a plain .NET Android or .NET iOS app with no MAUI, and dependency-free on its neutral heads.
DatadogNet.Extensions.DependencyInjection The Microsoft.Extensions glue without MAUI: AddDatadog on IServiceCollection (including an IConfiguration overload for appsettings) and ILoggingBuilder, and AddDatadogTracking on IHttpClientBuilder. DatadogNet.Maui builds on it; reference it directly from a plain app, a service or a test host. DatadogNet
DatadogNet.Extensions.Diagnostics The System.Diagnostics.Activity bridge: name your ActivitySources and their activities become Datadog spans, sampled and RUM-correlated like hand-written ones. DatadogNet
DatadogNet.CrashReporting Crash reporting. Separate because it installs a signal handler. DatadogNet
DatadogNet.WebView Bridges RUM and Logs out of a web view, for hybrid apps. DatadogNet

Most apps need one or two lines:

<PackageReference Include="DatadogNet.Maui" Version="3.14.0.6" />
<PackageReference Include="DatadogNet.CrashReporting" Version="3.14.0.6" />

Target frameworks. DatadogNet, DatadogNet.Extensions.DependencyInjection, DatadogNet.Extensions.Diagnostics, DatadogNet.CrashReporting and DatadogNet.WebView ship twelve: net8.0, net9.0 and net10.0, each with its -android, -ios and -maccatalyst head — net8.0-android34.0, net8.0-ios18.0, net8.0-maccatalyst18.0, net9.0-android35.0, net9.0-ios18.0, net9.0-maccatalyst18.0, net10.0-android36.0, net10.0-ios26.0, net10.0-maccatalyst26.0. DatadogNet.Maui ships twelve too: the nine platform heads plus a -windows10.0.19041.0 stub head per band, and no neutral asset.

The Mac Catalyst head is real, not a shim: it compiles the same implementation the iOS head uses, over the DatadogNet.Mac bindings — dd-sdk-ios built from source for Catalyst. Datadog's own support for Catalyst is partial (macOS 12+), and Session Replay records nothing there, so SessionReplayOptions is accepted and does nothing on a Mac.

The plain net8.0/net9.0/net10.0 assets are not padding. A MAUI app routinely also has a Windows head, and NuGet resolves each head independently; without a platform-neutral asset the reference has to be guarded by target platform in every project that has one. With it, the reference is unconditional and the Windows head links an implementation whose every member is a documented no-op — so shared code that reports a RUM view compiles and runs there, and in a unit test, without a single #if.

DatadogNet.Maui cannot carry a neutral asset — Microsoft.Maui.Controls is a workload metapackage with no lib/ assets, so there is no neutral MAUI to compile a no-op against — so it ships a Windows stub head instead: UseDatadog compiles and runs on a Windows head and every call lands in the core package's documented no-ops. The upshot is the same either way — every package in this set, DatadogNet.Maui included, is referenced with no platform condition. (Releases up to 3.14.0.3 had no Windows head, so on those the DatadogNet.Maui reference needs the target-platform guard this section used to show.)

net8 has one limitation, and it is worth reading before you rely on it. The net8 assets work — the device checks run against them on both a simulator and an emulator — but a .NET MAUI app on net8 does not build, and no target-framework choice here can change that. Pointing the sample at net8.0-android34.0 fails during Java callable wrapper generation:

error XA4204: Unable to resolve interface type
'AndroidX.Navigation.NavController/IOnDestinationChangedListener'

MAUI 8 resolves as 8.0.100 — the original November 2023 release, since no serviced MAUI 8 exists in any installed workload band — and its AndroidX generation does not line up with the one the Datadog Android bindings require. It is the same class of failure DatadogNet.Android's README records for MAUI 9, one version earlier and with a different symptom. MAUI 8 also reached end of support on 14 May 2025.

So net8 is for a plain .NET Android or .NET iOS app — one with no MAUI — which is also what DatadogNet.Android's and DatadogNet.iOS's own net8 support is for. DatadogNet.Maui ships net8 for uniformity, so that a net8 MAUI app fails with the Android SDK error above rather than a restore error blaming the wrong package, but it cannot be used from one. For MAUI, target net9 or net10.

A net8 Android app needs one extra line. Two packages in the AndroidX graph disagree about SavedStateNavigation.Common 2.9.6 wants >= 1.4.0, SavedState.Ktx 1.3.2 pins [1.3.2, 1.3.3). The .NET 9 and .NET 10 SDKs resolve the higher version and warn (NU1608); the .NET 8 SDK refuses with NU1107. Add the version those other bands already land on:

<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">
  <PackageReference Include="Xamarin.AndroidX.SavedState" Version="1.4.0" />
</ItemGroup>

A net8 Android app must also be built with the .NET 8 SDK, not the .NET 9 one. The .NET 9 band has the API 34 reference packs and compiles it, then fails at packaging with NETSDK1112 because it has no API 34 runtime packs — those belong to the .NET 8 workload. iOS has no equivalent constraint: the .NET 9 band builds net8.0-ios18.0 outright.

net8 sunset. The net8 heads are already past their platform support window — the net8 mobile workloads left support with MAUI 8 on 14 May 2025 — and ship for the plain (non-MAUI) apps that still target them, verified by the device checks. So the decision does not persist by inertia: the net8 heads are dropped in the first release after .NET 8 itself leaves support on 10 November 2026, in step with all three binding repositories.

Minimum OS: Android API 23, iOS 12.2.

The dd-sdk-android .aar manifests declare API 21, and DatadogNet.Android's README records that as the floor. It is not reachable: those .aars depend on an AndroidX generation in which androidx.savedstate declares minSdkVersion 23, and the manifest merger takes the maximum across the graph — so an app declaring 21 fails to build. 23 is the real floor.

Version map

The whole stack, stated once. Everything else in this README that names a version is prose around this row; the CI readme-check compares it against Directory.Build.props, so it cannot go stale silently:

DatadogNet dd-sdk-ios dd-sdk-android DatadogNet.iOS pin DatadogNet.Android pin DatadogNet.Mac pin .NET Minimum OS
3.14.0.6 3.14.0 3.12.1 3.14.0.5 3.12.1.3 3.14.0.4 net8 · net9 · net10 Android 23 · iOS 12.2 · macCatalyst 15.0 (macOS 12)

Earlier lines are on the releases page, each with the same numbers in its notes.


Installing

<ItemGroup>
  <PackageReference Include="DatadogNet.Maui" Version="3.14.0.6" />
</ItemGroup>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">23</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">12.2</SupportedOSPlatformVersion>

Without MAUI — a plain .NET Android or iOS app, a background service, or any Microsoft.Extensions host — reference DatadogNet.Extensions.DependencyInjection instead and make the same one call on your service collection:

builder.Services.AddDatadog(new DatadogConfiguration { /* as below */ });
builder.Logging.AddDatadog();          // the ILogger provider, if you want it
builder.Services
    .AddHttpClient<OrderService>()
    .AddDatadogTracking();             // same HttpClient instrumentation as MAUI

AddDatadog initialises the SDK and registers IRumMonitor, IDatadogLogs, IDatadogLogger, IDatadogTracer and ISessionReplay for constructor injection; an overload without the configuration registers only, for apps that initialise the SDK even earlier.


Usage

samples/DatadogNet.Maui.Sample is a working two-headed MAUI app that does everything below.

Initialise

Once, as early as the app can — MauiProgram.CreateMauiApp, before anything that might throw. Crash reporting only covers what happens after the SDK is up.

builder.UseDatadog(new DatadogConfiguration
{
    ClientToken     = "<CLIENT_TOKEN>",
    Env             = "production",
    Service         = "my-app",
    Site            = DatadogSite.Us1,       // Us1, Us3, Us5, Eu1, Ap1, Ap2, Us1Fed, Us2Fed
    TrackingConsent = TrackingConsent.Granted,

    Rum           = new RumOptions { ApplicationId = "<RUM_APPLICATION_ID>" },
    Logs          = new LogsOptions(),
    Trace         = new TraceOptions(),
    SessionReplay = new SessionReplayOptions(),

    FirstPartyHosts = new Dictionary<string, IReadOnlyList<TracingHeaderType>>
    {
        ["api.example.com"] = [TracingHeaderType.Datadog, TracingHeaderType.TraceContext],
    },
});

CrashReporting.Enable();   // DatadogNet.CrashReporting, after initialising

A feature is enabled by assigning its options object and left off by leaving it null. The ordering both native SDKs require — core first, RUM before Session Replay — is this call's problem rather than yours; getting it wrong natively fails silently, with the feature simply never producing data.

TrackingConsent defaults to Pending, not Granted. Pending collects and holds without uploading, so an app that has not yet asked its user loses nothing by starting there and calling Datadog.SetTrackingConsent when it has an answer. Defaulting to granted uploads data the user has not agreed to, and no later call takes that back.

RUM

Pages become views automatically. Report the rest yourself:

using (Datadog.Rum.StartView("checkout", "Checkout"))
{
    Datadog.Rum.AddAction(RumActionType.Tap, "pay", new Dictionary<string, object?>
    {
        ["cart.total"] = 42.50m,
    });

    Datadog.Rum.AddTiming("cart-loaded");

    try { /* ... */ }
    catch (Exception exception) { Datadog.Rum.AddError(exception); }
}

The view scope is worth adopting everywhere. The native API on both platforms is a startView/stopView pair matched by key, and a view left open by an early return or an exception is not an error — it goes on collecting every later action and error in the session, attributed to a screen the user has left.

Also on Datadog.Rum: StartAction/StopAction for continuous gestures, StartResource and friends for network calls, AddFeatureFlagEvaluation, AddAttribute/RemoveAttribute for session-wide attributes, StopSession, and GetCurrentSessionIdAsync — which is what turns "the app was slow" in a support ticket into a session you can watch.

Logs

Datadog.Logger.Info("user signed in");
Datadog.Logger.Error("payment failed", exception, new Dictionary<string, object?>
{
    ["order.id"] = orderId,
});

Levels are Debug, Info, Notice, Warn, Error, Critical. Datadog.Logs.CreateLogger makes a logger with a name, a service or a different sampling rate; Datadog.Logger is the one obvious logger for apps that want no ceremony.

An exception's type, message and stack go over as Datadog's reserved error.kind, error.message and error.stack, so the entry renders as an error rather than as three unrelated attributes.

Trace

using var span = Datadog.Tracer.StartSpan("checkout");
span.SetTag("cart.items", 3);

try { /* ... */ }
catch (Exception exception) { span.SetError(exception); throw; }

Disposing finishes the span, which is the point of the shape: a span that is never finished is never sent. span.Activate() makes it the parent of spans started while it is active — following the native SDKs' own thread-local scope managers, so it does not flow across an await.

Datadog.Tracer.Inject(span) returns the headers that continue the trace into your backend, in whichever of Datadog, B3, B3Multi and TraceContext you configured.

Where System.Diagnostics.Activity fits. IDatadogTracer is Datadog's manual span API; spans that libraries emit through ActivitySource — EF Core, gRPC, your own ActivitySource("MyCompany.Checkout") — do not arrive in Datadog on their own. The DatadogNet.Extensions.Diagnostics package is the join:

using var bridge = DatadogActivityBridge.Start(new DatadogActivityBridgeOptions {
    Sources = ["MyCompany.Checkout", "MyCompany.Sync"],
});

Forwarding is opt-in per source, because a modern .NET process emits activities from everywhere and every forwarded span is an event Datadog ingests. Tags, error status, DisplayName (as resource.name) and parentage carry across; sampling and RUM correlation are the tracer's own, because the bridge goes through the same IDatadogTracer as hand-written spans rather than through a parallel exporter. The native SDKs' OpenTelemetry APIs are deliberately not surfaced — one pipeline with one sampling configuration beats two that have to be reconciled.

HTTP

builder.Services
    .AddHttpClient<OrderService>(client => client.BaseAddress = new Uri("https://api.example.com"))
    .AddDatadogTracking();

Every request becomes a RUM resource, wrapped in a span, with tracing headers attached — but only on hosts listed in FirstPartyHosts. Everything else is still reported as a resource, because you want to see how slow a third-party API is, and is never given a trace id.

This is the one part of the façade with no native counterpart to delegate to, and it has to be: neither SDK's automatic network instrumentation can see an HttpClient call. Android's DatadogInterceptor is an OkHttp interceptor and HttpClient does not route through OkHttp by default; iOS's DDURLSessionInstrumentation hooks an NSURLSession delegate class and NSUrlSessionHandler owns its delegate rather than exposing it.

Without dependency injection, new HttpClient(new DatadogHttpMessageHandler(new HttpClientHandler())).

Session Replay

SessionReplay = new SessionReplayOptions
{
    SampleRate           = 100,
    TextAndInputPrivacy  = TextAndInputPrivacy.MaskAll,
    ImagePrivacy         = ImagePrivacy.MaskAll,
    TouchPrivacy         = TouchPrivacy.Hide,
},

Requires RUM. The three levels decide what is redacted on the device, before anything is uploaded, and they default to the most private setting of each — so the default is safe rather than convenient. Loosen them deliberately.

Set StartRecordingImmediately = false to enable the feature without recording, then Datadog.SessionReplay.StartRecording() once the user has agreed.

On Android the Material extension is registered for you, because MAUI's Android handlers are built on Material Components and without it a MAUI app records as a screen of blank boxes.

Datadog.SetTrackingConsent(TrackingConsent.Granted);
Datadog.SetUser("user-123", "Ada Lovelace", "ada@example.com");
Datadog.SetAccount("acme-inc", "ACME Inc.");
Datadog.ClearUser();      // on sign-out
Datadog.ClearAllData();   // for a "delete my data" request

What the MAUI package does for you

Each of these is something you would otherwise write twice, once per platform.

RUM views from page navigation. Neither native SDK can do this: a MAUI page is not a UIViewController and not an Activity. Android draws the whole app into one Activity, so ActivityViewTrackingStrategy reports a single view for the entire session; iOS reports per view controller, which for a Shell app is roughly per page but never matches the route names your code uses. DatadogNet.Maui subscribes to Application.PageAppearing/PageDisappearing — which fire for every page however it is shown, unlike Shell's Navigated or NavigationPage.Pushed, each of which sees only its own navigation.

Name a page whatever you would search for:

<ContentPage xmlns:dd="clr-namespace:DatadogNet.Maui;assembly=DatadogNet.Maui"
             dd:DatadogTracking.ViewName="Checkout">

or exclude one that is not a screen in the user's terms with dd:DatadogTracking.IsExcluded="True".

An ILogger provider. An app that already logs through ILogger<T> gets its logs into Datadog with no call-site changes, each entry tagged with its category and correlated with the RUM view it was written in.

Unhandled exceptions as RUM errors. Complements crash reporting rather than replacing it. A managed exception reaching AppDomain.UnhandledException still has its .NET type and managed frames; by the time the same failure arrives at a native crash handler it is a signal with those frames flattened away. TaskScheduler.UnobservedTaskException is picked up too, which does not terminate the process and so is otherwise invisible.

Dependency injection. IRumMonitor, IDatadogLogs, IDatadogLogger, IDatadogTracer and ISessionReplay are registered as singletons, so a view-model can depend on the piece it uses and be given a substitute in a test rather than reaching for the static. The registrations, the ILogger provider and AddDatadogTracking all live in DatadogNet.Extensions.DependencyInjection — which arrives with this package, and works alone in a host that has no MAUI.


Reaching the native SDK

The façade covers every documented feature both SDKs share, which is not all of either. Where it stops, it does not become a dead end: every options type has a ConfigureNative hook that receives the native configuration object at the one moment its setting can still be applied.

Rum = new RumOptions
{
    ApplicationId = "…",
    ConfigureNative = native =>
    {
#if ANDROID
        ((Com.Datadog.Android.Rum.RumConfiguration.Builder)native)
            .TrackNonFatalAnrs(true)
            .CollectAccessibility(true);
#elif IOS
        ((DatadogRUM.DDRUMConfiguration)native).TrackWatchdogTerminations = true;
#endif
    },
},

This matters more than it looks, because several native settings are configuration time only and cannot be reached after Datadog.Initialize returns. RUM's five event mappers — SetViewEventMapper, SetActionEventMapper, SetResourceEventMapper, SetErrorEventMapper, SetLongTaskEventMapper — are the supported way to redact or drop an event on the device before it is uploaded, and they can only be set here. They are not lifted into RumOptions because the event models they hand you are large, entirely different between the two SDKs, and generated from separate schemas; a cross-platform version would be a third schema to maintain and would still not let you touch the fields only one platform has.

docs/native-surface.md lists, per feature, what is lifted into the façade and what is deliberately left to ConfigureNative — including the settings that exist on only one platform.


Platform differences

The façade hides the API differences. It does not pretend the platforms are identical, and these are the places where they are not.

Android (dd-sdk-android 3.12.1) iOS (dd-sdk-ios 3.14.0)
Crash reporting JVM crashes are built in and on by default (CrashReportsEnabled), which covers an unhandled .NET exception. DatadogNet.CrashReporting adds native (NDK) crashes. Nothing is built in. Without DatadogNet.CrashReporting an iOS app reports no crashes at all.
ImagePrivacy.MaskContentImages Masks images larger than roughly 100×100 dp. Masks images not bundled with the app.
Automatic view tracking One view for the whole app, because MAUI uses a single Activity. Still worth having: it reports app start and ANRs. Roughly one view per page, through the default UIKit predicate.
RUM action types Also has Click and Back.
DatadogVerbosity.None A threshold above every android.util.Log priority. A real DDSDKVerbosityLevel.None.
Only there TrackNonFatalAnrs, CollectAccessibility, SetSlowFramesConfiguration, the other view-tracking strategies. TrackWatchdogTerminations, AppHangThreshold, the SwiftUI predicates.
Sites Also has STAGING, which is Datadog's own internal environment.

Reachable through ConfigureNative in every case.

Tracing is where the two SDKs have diverged most. dd-sdk-android 3.0 dropped OpenTracing for its own DatadogTracing/DatadogSpan; dd-sdk-ios kept OpenTracing in its Objective-C layer. IDatadogSpan is one interface over both anyway — the operations line up, only the type names do not — and the differences it absorbs are real:

  • Errors. DatadogSpan has SetError, SetErrorMessage and LogErrorMessage; OTSpan has SetErrorWithKind.
  • Ids. Android gives a typed 128-bit DatadogTraceId and a long span id. iOS gives neither — they are parsed back out of an injected Datadog header, so the two platforms render the same trace as a hex string and a decimal string respectively.
  • Injection. One Android call writes every configured header format; iOS needs one writer object per format.

The three Session Replay privacy levels are still the same three, with the same names, in the same order, apart from the middle image level above.

This is the 3.x line of both SDKs. Moving here from 2.x rewrote both platform implementations and left the public API alone — dd-sdk-android replaced OpenTracing with DatadogTracing/DatadogSpan, and dd-sdk-ios dissolved the DatadogObjc framework, redistributing the DD* types into one namespace per module and swapping PLCrashReporter for KSCrash. An app moving from the 2.30.2.1 façade changes a version number.

docs/upgrade-to-3x.md is the full analysis: what moved, what the façade gained, and what it deliberately still does not expose.


How this repository works

nuget.org: DatadogNet.iOS 3.14.0.5   DatadogNet.Android 3.12.1.3   DatadogNet.Mac 3.14.0.4
        │  (dd-sdk-ios 3.14.0)          │  (dd-sdk-android 3.12.1)     │  (dd-sdk-ios 3.14.0,
        │                               │                              │   built for Catalyst)
        └───────────────────────────────┼──────────────────────────────┘
                                        ▼
        src/DatadogNet/Platforms/{iOS,Android,Unsupported}/
                       │  one shared API, three implementations, one selected per target framework
                       │  (the maccatalyst heads compile Platforms/iOS over the .Mac bindings)
                       ▼
        src/DatadogNet.{Extensions.DependencyInjection,CrashReporting,WebView,Maui}/
                       │  build/BuildNugets.sh — pack twice (net9 band, net10 band), then merge
                       ▼
                artifacts/*.nupkg  ──►  nuget.org

Nothing native is bound here and nothing is downloaded: all three platform package sets come from nuget.org, each pinned to a single version. The pin is what NuGet calls a minimum — >= 3.14.0.5 in the packed nuspec, not an exact requirement — and default resolution lands exactly on it. The pin exists because this layer calls into the hand-written convenience APIs in the binding repositories — RumMonitorExtensions, DDRUMMonitor.Ergonomics and friends — which no compatibility promise covers beyond "the pinned revision has them".

Running ahead of the pins. Because the pin is a floor, an app can add a direct PackageReference to a newer binding revision and the façade will compile against it — the supported escape hatch for consuming an emergency binding patch before the façade re-pins. What that keeps working: anything on the same native SDK line (the first three version components), since binding revisions over one native release only add. What it does not: a binding that moves to a new native SDK version may rename or drop convenience members the façade calls, and the failure then appears in your build. Check this repository for a release that re-pins before jumping native lines.

Building the 2.x façade turned up a set of gaps in those two repositories, including one that made iOS tracing unusable from C# outright — docs/upstream-changes.md is that list. As of 3.14.0.2 there are no shims left here: the per-value attribute converters, the session-id and header-injection adapters over Kotlin function interfaces, and the iOS trace-id reassembly all live in the binding repositories, where every consumer of them benefits rather than only this package. That is also why the pins are exact and why 3.14.0.2 will not build against binding revision .1.

The three-repository review those changes came out of is docs/gap-analysis-3x.md — measured coverage of both bindings against their native surface, what is missing, and what is missing upstream and cannot be bound at all.

Why the two-pass build. Each .NET SDK's android/ios workloads ship reference packs for the current target framework and the previous one — the .NET 9 band builds net8 + net9, the .NET 10 band builds net9 + net10. No single SDK builds all of them, so BuildNugets.sh packs twice and merge-packages.py grafts the net10 assets into the net9 packages, carrying the dependency group across with them. Both binding repositories do the same.

Layout

Path What
src/DatadogNet/ The API. Shared sources declare it; Platforms/<name>/ supplies the bodies.
src/DatadogNet/Platforms/Unsupported/ The no-op implementation the neutral net8.0/net9.0/net10.0 assemblies link.
src/DatadogNet.Extensions.DependencyInjection/ The Microsoft.Extensions glue: AddDatadog, the ILogger provider, AddDatadogTracking. One set of shared sources — it only calls the core API, which is identical across heads.
src/DatadogNet.Extensions.Diagnostics/ The Activity bridge, shaped the same way: shared sources only, no platform directories.
src/Datadog.Facade.props Everything the six projects share, so each .csproj is its identity and its dependencies.
build/packages.tsv The package set. The build script, both workflows, the tests and the device runners all read it.
tests/DatadogNet.PackageTests/ Asserts the shape of the packed .nupkgs.
tests/DatadogNet.DeviceTests/ One app, two heads, one shared list of checks — run on an Android emulator and an iOS simulator against the packed packages.
samples/DatadogNet.Maui.Sample/ A MAUI app doing the same, the way an app would.

The tests consume the packed .nupkgs from artifacts/ rather than referencing the projects, so they exercise what actually gets published — including the dependencies on DatadogNet.iOS and DatadogNet.Android, which a ProjectReference would bypass entirely.

That the device tests are one file of checks running on both platforms is the point of the repository stated as a test: if the façade did not actually unify the two SDKs, that file could not compile for both heads.


Building locally

Requires macOS, Xcode, and the .NET 9 and .NET 10 SDKs with the android, ios and maui workloads, plus the Android SDK with platforms 34, 35 and 36. The .NET 9 band builds net8 and net9; the .NET 10 band builds net10.

./build/BuildNugets.sh
dotnet test tests/DatadogNet.PackageTests

Run the device checks against the packed packages:

./.github/scripts/run-simulator-tests.sh 3.14.0.6 net8.0-ios18.0
./.github/scripts/run-emulator-tests.sh 3.14.0.6 net8.0-android34.0

Any of the six platform target frameworks works. CI runs net8 and net10 — the oldest asset set, and the one the merge step produces — and skips net9, which shares a pack pass with net8.

Build and run the sample:

cd /tmp && dotnet new globaljson --sdk-version 10.0.301 --force
dotnet build <repo>/samples/DatadogNet.Maui.Sample/DatadogNet.Maui.Sample.csproj -f net10.0-ios26.0 -t:Run

The sample targets the net10 band only, and this repository's global.json pins .NET 9 — hence the scratch directory. That is a constraint on the MAUI version you build the sample with, not on what the packages support: MAUI 9 cannot build against the AndroidX generation the Datadog Android bindings depend on, and it is an SDK defect rather than anything the bindings do.

Anything targeting net10.0-ios26.0 needs an Xcode carrying the iOS 26 SDK; .NET for iOS refuses a mismatch outright. CI handles this with the select-xcode action; locally, prefix the command with DEVELOPER_DIR=/Applications/Xcode_26.0.1.app/Contents/Developer.


Upgrading

The two platform package sets move independently, and so does this one.

  1. Bump DatadogNetiOSVersion and/or DatadogNetAndroidVersion in Directory.Build.props, along with DatadogNativeVersion and DatadogAndroidNativeVersion beside them.
  2. Reset DatadogBindingRevision to 1 if either native version moved; increment it if neither did.
  3. ./build/BuildNugets.sh and run both test suites.
  4. Re-read docs/native-surface.md against the new SDKs. A new setting on one platform is reachable through ConfigureNative on day one; lifting it into the façade is a separate decision, and one worth taking only when the other platform has an equivalent.

A minor version of either SDK has repeatedly added members without removing any, so the usual upgrade is a two-line edit and a test run. The exception is a major: see the note on 3.x above.


Releasing

Tag it. v3.14.0.6 builds, tests, publishes all six packages to nuget.org via trusted publishing, and creates a GitHub release.

Pull requests publish a -beta.<pr>.<run> prerelease of the whole set.

Both run the same build.yml: pack, package tests, the simulator and emulator checks, and — on pull requests — a compile of the MAUI sample against the packed packages, plus the windows-head consumer check.

Curated notes in docs/release-notes/<version>.md replace the generated commit list when present.


Troubleshooting

Nothing appears in Datadog. Check Site matches your organisation's region — this is the most common cause by a wide margin. Then set Verbosity = DatadogVerbosity.Debug in the configuration and watch logcat or the Xcode console; that is where "your client token is invalid" appears.

Everything is a no-op and Datadog.IsSupported is false. You are on a Windows head, or in a unit test running against the neutral assembly. That is what it is for. (Mac Catalyst stopped being in this list when the DatadogNet.Mac bindings arrived: it is a real head now.)

RUM records one view for the whole app. Page tracking is off, or the app never reaches Application.PageAppearing — a Blazor Hybrid app, typically, where one MAUI page hosts every route. Report views yourself with Datadog.Rum.StartView from your router.

HTTP calls do not appear as RUM resources. The client has no DatadogHttpMessageHandler. Neither SDK's automatic instrumentation sees HttpClient; see HTTP.

Undefined symbols for architecture arm64: "_OBJC_CLASS_$_DD…" building for a real iPhone. The prebuilt dd-sdk-ios device slices ship without static Objective-C registration for 41 classes, so every iOS device link failed while simulator builds worked. Fixed in the DatadogNet.iOS binding packages 3.14.0.5, which is what this repository pins — so restoring DatadogNet pulls the fix in. See docs/device-arm64-missing-objc-class-symbols.md.

Traces do not continue into my backend. The host is not in FirstPartyHosts, which is an allowlist and matched by suffix on a label boundary — example.com covers api.example.com but not notexample.com.

NU1608 about Xamarin.AndroidX.SavedState or Lifecycle.Runtime. A property of the AndroidX graph the Datadog Android bindings pull in: two packages disagree about a sibling's exact range, and NuGet resolves the higher version and warns. The result works — it is what the emulator checks run against. Add <NoWarn>$(NoWarn);NU1608</NoWarn> or pin the versions yourself.

A framework fails to load, or NoClassDefFoundError. Usually a partially restored package. Clear the cached copies and restore again: rm -rf ~/.nuget/packages/datadognet*. A ClassNotFoundException only in Release on Android means R8 shrank a class reached only through JNI — DatadogNet.WebView and DatadogNet.CrashReporting ship consumer keep-rules for theirs, so update the packages first.

'Trace' is an ambiguous reference, or 'Logs' does not exist in the namespace. These are DatadogNet.Android symptoms, from calling the bindings directly. The façade puts everything on Datadog.* precisely to avoid them.


Licence

The code in this repository is MIT. It depends on DatadogNet.iOS and DatadogNet.Android, whose packages ship native binaries built and published by Datadog under Apache-2.0. Every package here declares MIT AND Apache-2.0 and carries both texts under licenses/.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-android34.0 is compatible.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-ios18.0 is compatible.  net8.0-maccatalyst was computed.  net8.0-maccatalyst18.0 is compatible.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-android35.0 is compatible.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-ios18.0 is compatible.  net9.0-maccatalyst was computed.  net9.0-maccatalyst18.0 is compatible.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-android36.0 is compatible.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst was computed.  net10.0-maccatalyst26.0 is compatible.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on DatadogNet.Extensions.DependencyInjection:

Package Downloads
DatadogNet.Maui

Wires Datadog into a .NET MAUI app: one UseDatadog call on MauiAppBuilder, automatic RUM views from page navigation, an ILogger provider, unhandled-exception reporting and HttpClient instrumentation.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.14.0.6 37 7/30/2026
3.14.0.6-beta.17.25 25 7/30/2026
3.14.0.6-beta.16.24 26 7/30/2026
3.14.0.6-beta.13.21 37 7/29/2026
3.14.0.6-beta.9.23 28 7/30/2026
3.14.0.5 88 7/26/2026
3.14.0.5-beta.10.20 34 7/29/2026
3.14.0.5-beta.8.17 33 7/29/2026
3.14.0.5-beta.7.18 30 7/29/2026
3.14.0.5-beta.6.13 44 7/26/2026
3.14.0.4-beta.5.12 44 7/24/2026
3.14.0.4-beta.4.11 50 7/24/2026
3.14.0.4-beta.4.10 49 7/24/2026

# 3.14.0.6

A re-pin release: no managed code changes, same API, same native SDKs (dd-sdk-ios 3.14.0,
dd-sdk-android 3.12.1). Both Apple binding pins move to releases that fix defects found by
running the samples on real hardware rather than simulators — one per platform, both invisible
to every simulator/emulator check that had ever run. Android is unchanged
(DatadogNet.Android `3.12.1.3`).

## iOS apps can build for a real iPhone again

DatadogNet.iOS moves `3.14.0.3` → `3.14.0.5`. Building any consuming app for a physical device
(`ios-arm64`) failed at the native link with a wall of
`Undefined symbols for architecture arm64: "_OBJC_CLASS_$_DDConfiguration" …` while the same
build ran fine on the simulator. The defect is upstream: dd-sdk-ios's prebuilt device slices are
built with a 12.0 deployment target, below which Swift withholds static Objective-C registration
for 41 of the bound classes — every prebuilt archive checked (2.30.2 through 3.14.0) has it, so
every earlier package release fails device links. The binding packages now repair it internally
(generated linker aliases to the classes' exported Swift metadata, plus a pre-`main` initializer
that realizes them before the registrar can message them), verified end-to-end on a physical
iPhone. Nothing to change in apps; the full story is
[docs/device-arm64-missing-objc-class-symbols.md](device-arm64-missing-objc-class-symbols.md)
and the binding release's own notes.

## Mac Catalyst apps no longer crash on first Datadog use

DatadogNet.Mac moves `3.14.0.2` → `3.14.0.4`. Under MAUI's default `TrimMode=partial`, the
managed-static registrar (.NET 9+'s Catalyst default) silently fails to weave its
`__Registrar__` lookup type into some binding assemblies
([dotnet/macios#21636](https://github.com/dotnet/macios/issues/21636)), and the first touch of
an affected assembly died with
`Could not find the type 'ObjCRuntime.__Registrar__' in the assembly 'DatadogNet.Logs.Mac'`.
The Mac packages now default consuming apps to the classic static registrar — only when the app
has not chosen a `Registrar` itself — which sidesteps the weaving entirely. Verified on a real
Mac.

## Documentation

The device-link investigation write-up joins `docs/`, and the README's troubleshooting section
gained entries for both crashes above, so the error texts are searchable straight to their
fixes.

## Upgrading from 3.14.0.5

```diff
-<PackageReference Include="DatadogNet" Version="3.14.0.5" />
+<PackageReference Include="DatadogNet" Version="3.14.0.6" />
```

Nothing to change beyond the version — the fixes live inside the binding packages the façade
pins. If you had added a direct reference to a newer binding package to get one of these fixes
early, it can come out again: the pins have caught up.