Datadog MAUI Unified API Design
Namespace Strategy
To avoid conflicts with the existing Datadog.Trace NuGet package (APM .NET tracer), we use the Datadog.Maui namespace hierarchy:
Datadog.Maui // Main namespace - initialization and core types
Datadog.Maui.Logs // Log collection
Datadog.Maui.Rum // Real User Monitoring
Datadog.Maui.Tracing // Mobile tracing (distinct from Datadog.Trace)
Datadog.Maui.Platforms // Platform-specific implementations (internal)
Rationale:
Datadog.Mauifollows MAUI conventions (likeMicrosoft.Maui.*)- Clear distinction from
Datadog.Trace(APM tracer for backend .NET apps) - Lowercase “Maui” matches Microsoft’s convention for product names in namespaces
API Design Philosophy
- Builder Pattern: Similar to existing Datadog.Trace API
- Fluent Configuration: Chainable configuration methods
- Platform Abstraction: Hide iOS/Android differences
- MAUI Integration: Use
MauiAppBuilderextension for initialization
Core API
1. Initialization (MauiProgram.cs)
using Datadog.Maui;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
// Datadog initialization
builder.UseDatadog(config =>
{
config.ClientToken = "YOUR_CLIENT_TOKEN";
config.Environment = "production";
config.ServiceName = "com.example.myapp";
config.Site = DatadogSite.US1; // US1, US3, US5, EU1, etc.
// Optional: Override platform-specific settings
config.ConfigurePlatform(platform =>
{
#if ANDROID
platform.ApplicationId = "YOUR_ANDROID_RUM_APP_ID";
platform.TrackingConsent = TrackingConsent.Granted;
#elif IOS
platform.ApplicationId = "YOUR_IOS_RUM_APP_ID";
platform.TrackingConsent = TrackingConsent.Granted;
#endif
});
});
return builder.Build();
}
}
2. Alternative: Manual Initialization
using Datadog.Maui;
// Create configuration
var config = new DatadogConfiguration.Builder("YOUR_CLIENT_TOKEN")
.SetEnvironment("production")
.SetServiceName("com.example.myapp")
.SetSite(DatadogSite.US1)
.SetTrackingConsent(TrackingConsent.Granted)
.Build();
// Initialize Datadog
Datadog.Initialize(config);
3. Global Datadog Instance
using Datadog.Maui;
// Access global instance
var datadog = Datadog.Instance;
// Check initialization status
if (Datadog.IsInitialized)
{
// Perform operations
}
// Set user information
Datadog.SetUser(new UserInfo
{
Id = "user-123",
Name = "John Doe",
Email = "john@example.com",
ExtraInfo = new Dictionary<string, object>
{
["subscription"] = "premium",
["role"] = "admin"
}
});
// Set global tags
Datadog.SetTags(new Dictionary<string, string>
{
["version"] = "1.2.3",
["build"] = "456"
});
// Update tracking consent
Datadog.SetTrackingConsent(TrackingConsent.Granted);
Logs API
Basic Logging
using Datadog.Maui.Logs;
// Get logger instance
var logger = Logs.CreateLogger("my-logger");
// Log messages with different levels
logger.Debug("Debug message", attributes: new { userId = "123" });
logger.Info("User logged in", attributes: new { userId = "123", source = "oauth" });
logger.Warn("API rate limit approaching");
logger.Error("Failed to fetch data", error: exception);
// Log with structured attributes
logger.Info("Purchase completed", attributes: new Dictionary<string, object>
{
["transaction_id"] = "txn_123456",
["amount"] = 49.99,
["currency"] = "USD",
["items_count"] = 3
});
Logger Configuration
using Datadog.Maui.Logs;
// Configure logs during initialization
builder.UseDatadog(config =>
{
config.EnableLogs(logs =>
{
logs.SampleRate = 100; // Log 100% of events
logs.NetworkInfoEnabled = true;
logs.BundleWithRum = true; // Associate logs with RUM sessions
});
});
// Or configure after initialization
Logs.Configure(config =>
{
config.AddAttribute("app_version", "1.2.3");
config.AddAttribute("environment", "production");
});
RUM (Real User Monitoring) API
Basic RUM Tracking
using Datadog.Maui.Rum;
// Start a view
Rum.StartView("ProductDetails", attributes: new { productId = "123" });
// Add actions
Rum.AddAction("Add to Cart", attributes: new { productId = "123", quantity = 2 });
// Track errors
try
{
// Some operation
}
catch (Exception ex)
{
Rum.AddError(ex, source: RumErrorSource.Source, attributes: new { context = "checkout" });
}
// Add custom timings
Rum.AddTiming("product_loaded");
// Track resources (API calls)
Rum.StartResource("https://api.example.com/products", method: "GET", attributes: new { page = 1 });
// ... after request completes
Rum.StopResource("https://api.example.com/products", kind: RumResourceKind.Native, status: 200, size: 1024);
// Stop view
Rum.StopView("ProductDetails", attributes: new { items_viewed = 5 });
RUM Configuration
using Datadog.Maui.Rum;
builder.UseDatadog(config =>
{
config.EnableRum(rum =>
{
rum.ApplicationId = "YOUR_RUM_APP_ID";
rum.SessionSampleRate = 100; // Monitor 100% of sessions
rum.TelemetrySampleRate = 20; // Sample telemetry at 20%
rum.TrackViewsAutomatically = true; // Auto-track page navigation
rum.TrackUserInteractions = true; // Auto-track taps/clicks
rum.TrackResources = true; // Auto-track network requests
rum.TrackErrors = true; // Auto-track errors
rum.VitalsUpdateFrequency = VitalsUpdateFrequency.Average;
});
});
// Manual monitoring control
Rum.StartSession();
Rum.StopSession();
MAUI Navigation Integration
using Datadog.Maui.Rum;
// In your Shell or NavigationPage
protected override void OnNavigated(ShellNavigatedEventArgs args)
{
base.OnNavigated(args);
// Automatically track view changes
Rum.StartView(args.Current.Location.ToString(), attributes: new
{
route = args.Current.Location.OriginalString,
previous_route = args.Previous?.Location.OriginalString
});
}
Tracing API
Span Creation
using Datadog.Maui.Tracing;
// Create and start a span
using var span = Tracer.StartSpan("operation-name");
span.SetTag("user_id", "123");
span.SetTag("item_count", 5);
try
{
// Perform operation
await ProcessData();
// Add events
span.AddEvent("data_processed", attributes: new { records = 100 });
}
catch (Exception ex)
{
span.SetError(ex);
throw;
}
// Span automatically finishes when disposed
// Or manually manage span lifecycle
var span = Tracer.StartSpan("manual-operation");
// ... do work
span.Finish();
Distributed Tracing
using Datadog.Maui.Tracing;
// Inject trace context into HTTP headers
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/data");
Tracer.Inject(request.Headers);
// Extract trace context from incoming data
var context = Tracer.Extract(headers);
using var span = Tracer.StartSpan("handle-request", parent: context);
Trace Configuration
builder.UseDatadog(config =>
{
config.EnableTracing(tracing =>
{
tracing.SampleRate = 100;
tracing.TraceIdGenerationEnabled = true;
tracing.FirstPartyHosts = new[] { "api.example.com", "cdn.example.com" };
});
});
WebView Tracking
Track WebView Content
using Datadog.Maui.Rum;
// Enable WebView tracking for a specific WebView
var webView = new WebView
{
Source = "https://example.com"
};
// Track WebView RUM events
Rum.TrackWebView(webView, allowedHosts: new[] { "example.com" });
Configuration Types
DatadogSite Enum
public enum DatadogSite
{
US1, // datadoghq.com
US3, // us3.datadoghq.com
US5, // us5.datadoghq.com
EU1, // datadoghq.eu
US1_FED, // ddog-gov.com
AP1 // ap1.datadoghq.com
}
TrackingConsent Enum
public enum TrackingConsent
{
Granted, // Start tracking immediately
NotGranted, // Do not track
Pending // Store events locally, wait for consent
}
Comparison with Datadog.Trace
| Feature | Datadog.Trace (.NET APM) | Datadog.Maui (Mobile SDK) |
|---|---|---|
| Purpose | Backend .NET application tracing | Mobile app monitoring (iOS/Android) |
| Namespace | Datadog.Trace |
Datadog.Maui |
| Initialization | Tracer.Configure(settings) |
builder.UseDatadog(config) |
| Primary Use | APM, distributed tracing | RUM, logs, mobile-specific monitoring |
| Target | ASP.NET, Console apps, services | .NET MAUI mobile apps |
| Conflict | ❌ No conflict - different namespaces |
Example: Complete Integration
// MauiProgram.cs
using Datadog.Maui;
using Datadog.Maui.Logs;
using Datadog.Maui.Rum;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>();
// Initialize Datadog
builder.UseDatadog(config =>
{
config.ClientToken = Environment.GetEnvironmentVariable("DD_CLIENT_TOKEN")!;
config.Environment = "production";
config.ServiceName = "com.example.myapp";
config.Site = DatadogSite.US1;
// Enable RUM
config.EnableRum(rum =>
{
rum.ApplicationId = Environment.GetEnvironmentVariable("DD_RUM_APP_ID")!;
rum.SessionSampleRate = 100;
rum.TrackViewsAutomatically = true;
rum.TrackUserInteractions = true;
rum.TrackResources = true;
});
// Enable Logs
config.EnableLogs(logs =>
{
logs.BundleWithRum = true;
logs.NetworkInfoEnabled = true;
});
// Enable Tracing
config.EnableTracing(tracing =>
{
tracing.SampleRate = 100;
tracing.FirstPartyHosts = new[] { "api.example.com" };
});
// Set global attributes
config.GlobalTags.Add("app_version", "1.2.3");
config.GlobalTags.Add("platform", DeviceInfo.Platform.ToString());
});
return builder.Build();
}
}
// In your pages/views
public partial class ProductDetailsPage : ContentPage
{
private readonly ILogger _logger;
public ProductDetailsPage()
{
InitializeComponent();
_logger = Logs.CreateLogger("ProductDetailsPage");
}
protected override void OnAppearing()
{
base.OnAppearing();
// Track view
Rum.StartView("ProductDetails", attributes: new { productId = ProductId });
_logger.Info("Viewed product details", new { productId = ProductId });
}
protected override void OnDisappearing()
{
base.OnDisappearing();
Rum.StopView("ProductDetails");
}
private async void OnAddToCartClicked(object sender, EventArgs e)
{
using var span = Tracer.StartSpan("add_to_cart");
span.SetTag("product_id", ProductId);
try
{
// Track user action
Rum.AddAction("AddToCart", attributes: new { productId = ProductId });
await CartService.AddItem(ProductId);
_logger.Info("Added to cart", new { productId = ProductId });
}
catch (Exception ex)
{
Rum.AddError(ex, source: RumErrorSource.Source);
_logger.Error("Failed to add to cart", ex, new { productId = ProductId });
span.SetError(ex);
}
}
}
Migration Path from Native SDKs
For users migrating from native Datadog iOS/Android SDKs:
| Native iOS/Android | Datadog.Maui |
|---|---|
Datadog.initialize() |
builder.UseDatadog() or Datadog.Initialize() |
Logs.enable() |
config.EnableLogs() |
RUM.enable() |
config.EnableRum() |
Trace.enable() |
config.EnableTracing() |
RUMMonitor.shared() |
Rum static class |
Logger.create() |
Logs.CreateLogger() |
GlobalRUM.addAttribute() |
Rum.AddAttribute() |
Implementation Notes
- Platform-specific code lives in
Datadog.Maui.Platforms(internal) - Partial classes for platform-specific implementations:
// Datadog.cs public static partial class Datadog { public static partial void Initialize(DatadogConfiguration config); } // Platforms/Android/Datadog.android.cs public static partial class Datadog { public static partial void Initialize(DatadogConfiguration config) { // Android-specific implementation } } - Dependency Injection support:
builder.Services.AddSingleton<ILogger>(Logs.CreateLogger("app")); - Crash Reporting automatic on both platforms (NDK for Android, native iOS crash reporting)
Next Steps
- Implement core types (
DatadogConfiguration,Datadogstatic class) - Implement
MauiAppBuilderExtensions.UseDatadog() - Implement Logs API with platform-specific bridges
- Implement RUM API with MAUI navigation integration
- Implement Tracing API
- Add XML documentation comments
- Create sample project demonstrating all features