Advanced Configuration
This guide covers advanced configuration options for fine-tuning Datadog RUM in your .NET MAUI application.
Table of Contents
- Configuration Methods
- RUM Configuration
- Logs Configuration
- Tracing Configuration
- Session Replay Configuration
- Privacy and Data Scrubbing
- Tracking Consent
- Performance Tuning
- Multi-Environment Setup
Configuration Methods
Datadog MAUI SDK supports three configuration approaches:
1. Configuration File (Recommended)
Use appsettings.json for declarative configuration:
{
"Datadog": {
"Environment": "production",
"ServiceName": "my-app"
}
}
builder.UseDatadogFromConfiguration();
Pros: Clean separation, easy to manage per environment, no code changes Use when: Standard configuration needs, multiple environments
2. Programmatic Configuration
Configure entirely in code:
builder.UseDatadog(datadog =>
{
datadog.SetClientToken(
android: "pub_android_token",
ios: "pub_ios_token"
);
datadog.Environment = "production";
datadog.ServiceName = "my-app";
datadog.EnableRum(rum =>
{
rum.SetApplicationId(
android: "android-app-id",
ios: "ios-app-id"
);
});
});
Pros: Type-safe, compile-time validation, dynamic configuration Use when: Configuration needs to be determined at runtime
3. Hybrid Approach
Combine configuration file with programmatic overrides:
builder.UseDatadogFromConfiguration(configure: datadog =>
{
// Base config from appsettings.json, override specific values
datadog.EnableRum(rum =>
{
rum.Variant = BuildInfo.Variant;
rum.BuildId = BuildInfo.BuildId;
});
});
Pros: Best of both worlds, flexible for build-specific values Use when: Most production scenarios (recommended)
RUM Configuration
Basic RUM Setup
datadog.EnableRum(rum =>
{
rum.SetApplicationId(
android: "YOUR_ANDROID_APP_ID",
ios: "YOUR_IOS_APP_ID"
);
rum.SetSessionSampleRate(100); // Track 100% of sessions
});
Session Sampling
Control what percentage of user sessions are tracked:
rum.SetSessionSampleRate(20); // Track 20% of sessions
Sampling Strategy:
- Development: 100% - Track all sessions for debugging
- Staging: 50-100% - High visibility for testing
- Production: 10-30% - Balance cost with visibility
- High Traffic: 1-10% - Cost-effective for large scale
Crash Reporting with Symbolication
Enable full crash symbolication:
datadog.EnableRum(rum =>
{
// These values are auto-generated by Datadog.MAUI.Symbols package
rum.Variant = Datadog.MAUI.Symbols.DatadogBuildInfo.Variant;
rum.BuildId = Datadog.MAUI.Symbols.DatadogBuildInfo.BuildId;
});
See Crash Reporting Guide for complete symbolication setup.
Custom View Names
Control how views appear in Datadog:
// Manual view tracking
Rum.StartView("checkout", "Checkout Flow", new Dictionary<string, object>
{
{ "step", "payment" },
{ "cart_value", 99.99 }
});
// Stop view when done
Rum.StopView("checkout");
Global Attributes
Add attributes to all RUM events:
// Add global attribute
Rum.AddAttribute("app_version", "2.1.0");
Rum.AddAttribute("build_number", 142);
// Remove when no longer needed
Rum.RemoveAttribute("build_number");
Logs Configuration
Enable Logging
{
"Datadog": {
"Logs": {
"Enabled": true
}
}
}
Or programmatically:
datadog.EnableLogs();
Using Loggers
Create feature-specific loggers:
using Datadog.Maui.Logs;
public class CheckoutService
{
private readonly ILogger _logger;
public CheckoutService()
{
_logger = Logs.CreateLogger("Checkout");
}
public void ProcessOrder(string orderId)
{
_logger.Info("Processing order", attributes: new Dictionary<string, object>
{
{ "order_id", orderId },
{ "timestamp", DateTime.UtcNow }
});
try
{
// Process order
}
catch (Exception ex)
{
_logger.Error("Order processing failed", ex, attributes: new Dictionary<string, object>
{
{ "order_id", orderId }
});
}
}
}
Log Levels
All standard log levels are supported:
_logger.Debug("Debug information");
_logger.Info("Informational message");
_logger.Notice("Notable event");
_logger.Warn("Warning condition");
_logger.Error("Error occurred", exception);
_logger.Critical("Critical failure", exception);
Logger Attributes
Add persistent attributes to a specific logger:
_logger.AddAttribute("feature", "checkout");
_logger.AddAttribute("version", "2.0");
// These attributes will be included in all logs from this logger
_logger.Info("Order placed"); // Will include feature and version attributes
Tracing Configuration
Enable Distributed Tracing
{
"Datadog": {
"Tracing": {
"Enabled": true,
"SampleRate": 100
},
"FirstPartyHosts": [
"api.example.com",
"cdn.example.com"
]
}
}
Or programmatically:
datadog.EnableTracing(tracing =>
{
tracing.SetFirstPartyHosts(new[]
{
"api.example.com",
"cdn.example.com"
});
tracing.SetSampleRate(100); // Trace 100% of requests
});
Creating Spans
Track custom operations:
using Datadog.Maui.Tracing;
public async Task<User> GetUserAsync(string userId)
{
using (var span = Tracer.StartSpan("database.query"))
{
span.SetTag("db.operation", "SELECT");
span.SetTag("db.table", "users");
span.SetTag("user.id", userId);
try
{
var user = await _database.GetUserAsync(userId);
span.SetTag("result.found", user != null);
return user;
}
catch (Exception ex)
{
span.SetError(ex);
throw;
}
}
}
Nested Spans
Track parent-child relationships:
using (var parentSpan = Tracer.StartSpan("checkout.process"))
{
parentSpan.SetTag("order.id", orderId);
// Child operation 1
using (var childSpan = Tracer.StartSpan("payment.validate", parent: parentSpan))
{
childSpan.SetTag("payment.method", "credit_card");
await ValidatePayment();
}
// Child operation 2
using (var childSpan = Tracer.StartSpan("inventory.reserve", parent: parentSpan))
{
childSpan.SetTag("items.count", 3);
await ReserveInventory();
}
}
HTTP Tracing
HTTP requests to first-party hosts are automatically traced. Use DatadogHttpMessageHandler for explicit control:
using Datadog.Maui.Tracing;
var httpClient = new HttpClient(new DatadogHttpMessageHandler(
firstPartyHosts: new[] { "api.example.com" },
innerHandler: new HttpClientHandler()
));
// Requests will automatically create spans
var response = await httpClient.GetAsync("https://api.example.com/users");
Session Replay Configuration
Enable Session Replay
{
"Datadog": {
"SessionReplay": {
"Enabled": true,
"SampleRate": 20,
"TextAndInputPrivacy": "MaskSensitiveInputs",
"ImagePrivacy": "MaskNonBundledOnly",
"TouchPrivacy": "Show"
}
}
}
Privacy Levels
TextAndInputPrivacy Options:
sessionReplay.SetTextAndInputPrivacy(TextAndInputPrivacy.Allow);
// Records all text exactly as displayed
sessionReplay.SetTextAndInputPrivacy(TextAndInputPrivacy.Mask);
// Masks all text with "X" characters
sessionReplay.SetTextAndInputPrivacy(TextAndInputPrivacy.MaskSensitiveInputs);
// Masks only password, email, phone fields (RECOMMENDED)
ImagePrivacy Options:
sessionReplay.SetImagePrivacy(ImagePrivacy.MaskAll);
// All images shown as placeholders
sessionReplay.SetImagePrivacy(ImagePrivacy.MaskNonBundledOnly);
// Only user-uploaded images masked (RECOMMENDED)
sessionReplay.SetImagePrivacy(ImagePrivacy.MaskNone);
// All images shown
TouchPrivacy Options:
sessionReplay.SetTouchPrivacy(TouchPrivacy.Show);
// Show all touch interactions (RECOMMENDED)
sessionReplay.SetTouchPrivacy(TouchPrivacy.Hide);
// Hide all touch interactions
Conditional Session Replay
Enable Session Replay only for specific conditions:
datadog.EnableSessionReplay(sessionReplay =>
{
sessionReplay.SetSampleRate(20);
// Only on physical devices (not simulators)
if (DeviceInfo.DeviceType == DeviceType.Physical)
{
sessionReplay.SetTextAndInputPrivacy(TextAndInputPrivacy.MaskSensitiveInputs);
}
});
Privacy and Data Scrubbing
User Information
Set user information while respecting privacy:
Datadog.SetUser(new UserInfo
{
Id = "user-12345", // Use internal ID, not email
Name = "John D.", // Optionally mask surname
Email = "j***@example.com", // Mask email if needed
ExtraInfo = new Dictionary<string, object>
{
{ "plan", "premium" },
{ "signup_date", "2024-01-15" }
// Don't include sensitive data (SSN, credit card, etc.)
}
});
Clear User Data
Clear user information on logout:
Datadog.ClearUser();
Scrubbing Sensitive Data
Remove sensitive information from logs and RUM:
// Bad - Logs credit card
_logger.Info("Payment processed", attributes: new Dictionary<string, object>
{
{ "card_number", "4111111111111111" } // ❌ Don't do this
});
// Good - Logs only last 4 digits
_logger.Info("Payment processed", attributes: new Dictionary<string, object>
{
{ "card_last4", "1111" } // ✅ Safe to log
});
Tracking Consent
Set Initial Consent
{
"Datadog": {
"TrackingConsent": "Granted"
}
}
Options:
Granted: Start tracking immediately (default)NotGranted: Wait for user consentPending: Buffer events until consent is granted
Update Consent at Runtime
using Datadog.Maui;
// User accepts tracking
Datadog.SetTrackingConsent(TrackingConsent.Granted);
// User declines tracking
Datadog.SetTrackingConsent(TrackingConsent.NotGranted);
GDPR Compliance Pattern
public class ConsentManager
{
public async Task InitializeAsync()
{
// Start with pending consent
Datadog.SetTrackingConsent(TrackingConsent.Pending);
// Show consent dialog
var consent = await ShowConsentDialog();
if (consent)
{
// Grant tracking - buffered events will be sent
Datadog.SetTrackingConsent(TrackingConsent.Granted);
}
else
{
// Deny tracking - buffered events will be discarded
Datadog.SetTrackingConsent(TrackingConsent.NotGranted);
}
}
}
Performance Tuning
Optimize Sampling Rates
Balance visibility with performance and cost:
builder.UseDatadogFromConfiguration(configure: datadog =>
{
#if DEBUG
// Development: Maximum visibility
datadog.EnableRum(rum => rum.SetSessionSampleRate(100));
datadog.EnableTracing(tracing => tracing.SetSampleRate(100));
datadog.EnableSessionReplay(sr => sr.SetSampleRate(100));
#else
// Production: Cost-effective sampling
datadog.EnableRum(rum => rum.SetSessionSampleRate(20));
datadog.EnableTracing(tracing => tracing.SetSampleRate(30));
datadog.EnableSessionReplay(sr => sr.SetSampleRate(5));
#endif
});
Batching Configuration
The SDK automatically batches events for network efficiency. No configuration needed.
Memory Management
The SDK uses bounded buffers to prevent memory issues. Events are automatically dropped if limits are reached.
Multi-Environment Setup
Using Multiple Configuration Files
appsettings.json (base):
{
"Datadog": {
"ServiceName": "my-app",
"Logs": { "Enabled": true }
}
}
appsettings.Development.json:
{
"Datadog": {
"Environment": "development",
"Android": {
"ClientToken": "dev_android_token",
"RumApplicationId": "dev_android_app_id"
},
"iOS": {
"ClientToken": "dev_ios_token",
"RumApplicationId": "dev_ios_app_id"
},
"Rum": { "SessionSampleRate": 100 }
}
}
appsettings.Production.json:
{
"Datadog": {
"Environment": "production",
"Android": {
"ClientToken": "prod_android_token",
"RumApplicationId": "prod_android_app_id"
},
"iOS": {
"ClientToken": "prod_ios_token",
"RumApplicationId": "prod_ios_app_id"
},
"Rum": { "SessionSampleRate": 20 }
}
}
Load environment-specific config:
private static void LoadAppSettings(MauiAppBuilder builder)
{
var configBuilder = new ConfigurationBuilder();
// Load base config
using var baseStream = FileSystem.OpenAppPackageFileAsync("appsettings.json")
.GetAwaiter().GetResult();
var baseMemoryStream = new MemoryStream();
baseStream.CopyTo(baseMemoryStream);
baseMemoryStream.Position = 0;
configBuilder.AddJsonStream(baseMemoryStream);
// Load environment-specific override
#if DEBUG
var envFile = "appsettings.Development.json";
#else
var envFile = "appsettings.Production.json";
#endif
try
{
using var envStream = FileSystem.OpenAppPackageFileAsync(envFile)
.GetAwaiter().GetResult();
var envMemoryStream = new MemoryStream();
envStream.CopyTo(envMemoryStream);
envMemoryStream.Position = 0;
configBuilder.AddJsonStream(envMemoryStream);
}
catch (FileNotFoundException)
{
// Environment file optional
}
var config = configBuilder.Build();
builder.Configuration.AddConfiguration(config);
}
Build Configuration-Specific Settings
Use conditional compilation for environment-specific logic:
builder.UseDatadogFromConfiguration(configure: datadog =>
{
#if DEBUG
datadog.Environment = "development";
#elif STAGING
datadog.Environment = "staging";
#elif RELEASE
datadog.Environment = "production";
#endif
});
Next Steps
- Crash Reporting: Set up crash symbolication
- Mobile Vitals: Monitor performance metrics
- Web View Tracking: Track embedded web content
- Troubleshooting: Debug common issues