License Reports
License reporting provides detailed insights into how your application uses licensed features, fields, and restrictions. By enabling license usage tracking, you can monitor feature adoption, validate compliance, and make data-driven decisions about licensing strategy.
Overview
The Babel Licensing library includes a powerful tracking system that automatically monitors access to license components:
- Feature Usage: Track which features are accessed, how often, and by which members
- Field Access: Monitor which license fields are read and their values
- Restriction Validation: Record restriction checks and validation results
- Automatic Collection: Usage data is captured transparently without code changes
- Smart Merging: Server-side intelligence combines reports for comprehensive analysis
Console Application Example
To get started with a practical example, clone the console-licensing-report-example repository which demonstrates how to track license usage and send license reports using Babel Reporting functionality.
- Open your preferred Git client or the command line.
- Clone the GitHub repository by executing the following command:
git clone https://github.com/babelfornet/console-licensing-report-example.git- Once the repository is cloned, navigate to the project directory:
cd console-licensing-report-example- You are now ready to explore and run the example code.
The console-licensing-report-example demonstrates how to integrate the Babel Licensing client library into your application to track license usage and send license reports to the Babel Licensing Service. Here’s a breakdown of the code:
- BabelReporting and BabelLicensing Initialization: The example code initializes both a
BabelReportingobject for sending reports and aBabelLicensingobject for license validation.
private BabelReporting CreateReportingClient()
{
var reporting = new BabelReporting();
reporting.Configuration.ClientId = ClientId;
reporting.Configuration.ServiceUrl = ServiceUrl;
reporting.Configuration.UseHttp(http => {
http.Timeout = TimeSpan.FromSeconds(3);
http.Handler = new HttpClientHandler() {
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
});
reporting.BeforeSendReport += (s, e) => {
e.Report.Properties.Add("cmdline", Environment.CommandLine);
e.Report.Properties.Add("username", Environment.UserName);
};
return reporting;
}
private BabelLicensing CreateLicensingClient()
{
var config = new BabelLicensingConfiguration()
{
ServiceUrl = ServiceUrl,
SignatureProvider = RSASignature.FromKeys(PublicKey),
ClientId = ClientId
};
config.UseHttp(http => {
http.Timeout = TimeSpan.FromSeconds(10);
http.Handler = new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
});
// Register custom license factory to handle MemoryRestriction
config.LicenseFactory = new CustomLicenseFactory();
return new BabelLicensing(config);
}-
User Key and Public Key: The example code uses a user key (
UserKey) to authorize the client and a public key (PublicKey) for license signature verification. Make sure to replace these with valid keys. To generate a user key please refer to the Floating License or License Activation paragraphs. -
License Validation with Tracking: The example demonstrates how to validate a floating license and access its features, fields, and restrictions. All access is automatically tracked when
LicenseUsageTrackingis enabled:
private async Task ValidateLicenseAsync()
{
try
{
// Acquire floating license
var requestResult = await _licensing.RequestFloatingLicenseAsync(UserKey, typeof(Program));
Console.WriteLine($"Floating license {requestResult.License.Id} acquired.");
try
{
// Validate the license (this will be tracked)
var result = await _licensing.ValidateLicenseAsync(UserKey, typeof(Program));
var license = result.License;
Console.WriteLine($"License validated: {license.Id}");
foreach (var feature in license.Features)
{
Console.WriteLine($" Feature: {feature.Name} - {feature.Description}");
}
foreach (var field in license.Fields)
{
Console.WriteLine($" Field: {field.Name} = {field.Value}");
}
foreach (var restriction in license.Restrictions)
{
Console.WriteLine($" Restriction: {restriction.Name}");
}
// Simulate multiple accesses to demonstrate tracking
var reportingFeature = license.Features.FirstOrDefault(f => f.Name == "Reporting");
var field2Field = license.Fields.FirstOrDefault(f => f.Name == "field2");
for (int i = 0; i < 5; i++)
{
var _ = reportingFeature?.Data;
var __ = field2Field?.Value;
}
Console.WriteLine($"\nLicense validation completed.");
}
finally
{
// Always release the floating license when done
await _licensing.ReleaseFloatingLicenseAsync(UserKey);
Console.WriteLine("Floating license released.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error validating license: {ex.Message}");
}
}- Sending the License Report: The
SendLicenseReportAsyncmethod sends the accumulated tracking data to the Babel Licensing Service:
private async Task SendLicenseReportAsync()
{
var result = await _reporting.SendLicenseReportAsync(UserKey);
if (result.IsError)
{
Console.WriteLine($"Error sending LicenseReport: {result.Error}");
return;
}
Console.WriteLine("LicenseReport sent.");
}- Custom License Factory: The example includes a custom
MemoryRestrictionclass andCustomLicenseFactoryto demonstrate how to handle custom restriction types in your licenses:
class MemoryRestriction : Restriction, ILicenseSerializable
{
public override string Name => "Memory";
public long TotalMemory { get; set; }
public override ValidationResult Validate(ILicenseContext context, Type type, object instance)
{
ISystemInformation sys = (ISystemInformation)context.GetService(typeof(ISystemInformation));
long totalMem = ToMegabytes(sys.TotalPhysicalMemory);
if (totalMem < TotalMemory)
return ValidationResult.Invalid;
return base.Validate(context, type, instance);
}
private static long ToMegabytes(long bytes)
{
return bytes / 1024 / 1024;
}
// ILicenseSerializable implementation...
}
class CustomLicenseFactory : ILicenseFactory
{
public Restriction CreateRestriction(string type)
{
string[] tokens = type.Split(':');
string restriction = tokens[0];
switch (restriction)
{
case "Memory":
var memory = new MemoryRestriction();
if (tokens.Length > 1)
memory.TotalMemory = long.Parse(tokens[1]);
return memory;
default:
return null!;
}
}
// Other ILicenseFactory methods...
}
License Report Example
The console-licensing-report-example provides a practical demonstration of how to track license usage and send reports to the Babel Licensing Service. By integrating license tracking into your applications, you can monitor feature adoption, validate compliance, and make data-driven decisions about your licensing strategy.
Enabling License Tracking
To start tracking license usage, enable tracking before validating your license:
using Babel.Licensing.Reports;
// Enable tracking (must be called before license validation)
LicenseUsageTracking.Enable();
// Validate your license as usual
var license = licenseManager.Validate(key, type);
// Use licensed features - access is automatically tracked
var reportingFeature = license.Features["Reporting"];
var data = reportingFeature.Data;
var versionField = license.Fields["Version"];
var version = versionField.Value;Sending License Reports
Once you’ve enabled tracking, you can send reports to your Babel Licensing Service:
using Babel.Licensing.Services;
// Initialize the reporting service
var reporting = new BabelReporting();
reporting.Configuration.ServiceUrl = "https://licensing.example.com";
reporting.Configuration.ClientId = "MyApplication";
// Send the license report
var result = await reporting.SendLicenseReportAsync(userKey);
if (result.Success)
{
Console.WriteLine($"Report sent successfully: {result.ReportUid}");
}
else
{
Console.WriteLine($"Failed to send report: {result.Error?.Message}");
}What Gets Tracked
Feature Access Tracking
For each feature accessed in your license:
// This access is automatically tracked
var feature = license.Features["Reporting"];
var data = feature.Data; // Tracked: get_Data on Reporting featureTracked Information:
- Feature name and ID
- Member accessed (e.g.,
get_Data,get_IsEnabled) - Number of accesses
- First access timestamp
- Last access timestamp
- Total access duration (time between first and last access)
Field Access Tracking
For each field read from your license:
// This access is automatically tracked
var field = license.Fields["Version"];
var value = field.Value; // Tracked: get_Value on Version fieldTracked Information:
- Field name
- Field value (truncated to 12 characters for privacy)
- Member accessed (e.g.,
get_Value) - Access counts and timestamps
Restriction Validation Tracking
For each restriction checked:
// This validation is automatically tracked
var validation = license.Restrictions.Validate();Tracked Information:
- Restriction name and type
- Validation result (Pass/Fail)
- Member accessed
- Access counts and timestamps
Report Content
Each license report includes three main sections:
1. License Metadata
Basic information about the license being tracked:
{
"license_id": "L-ABC-123",
"license_type": "Standard",
"issue_date": "2025-01-15",
"expire_date": "2026-01-15",
"support_expire_date": "2025-07-15",
"licensee_name": "John Smith",
"licensee_company": "ACME Corporation",
"product_name": "EquiTrack",
"product_version": "2.0"
}2. Usage Tracking Data
Detailed access statistics for all tracked components:
{
"features": [
{
"id": "F-001",
"name": "Reporting",
"members": [
{
"member": "get_Data",
"access_count": 15,
"first_access": "2025-01-20T10:30:00Z",
"last_access": "2025-01-20T14:45:00Z",
"access_time": "04:15:00"
}
]
}
],
"fields": [
{
"name": "Version",
"value": "2.0.1",
"members": [
{
"member": "get_Value",
"access_count": 3,
"first_access": "2025-01-20T10:00:00Z"
}
]
}
],
"restrictions": [
{
"name": "MachineLock",
"type": "Hardware",
"members": [
{
"member": "Validate",
"access_count": 1,
"validation_result": "Pass",
"first_access": "2025-01-20T10:00:00Z"
}
]
}
]
}3. Environment and System Information
Context about the application and machine (configurable):
- Application name, version, and loaded assemblies
- Operating system details
- Hardware information (CPU, memory, disk)
- Network adapters
- Environment variables (filtered)
- Running processes (optional)
Configuration Options
Customize what data gets included in your license reports:
var reporting = new BabelReporting();
// Configure license report options
reporting.Configuration.LicenseReportOptions = new LicenseReportOptions
{
// What to track
CollectFeaturesAccess = true,
CollectFieldsAccess = true,
CollectRestrictionsAccess = true,
// Filter specific members (optional)
CollectMembers = new List<string> { "get_Data", "get_IsEnabled" },
// Environment information
Environment = new EnvironmentReportOptions
{
CollectApplicationInformation = true,
CollectLoadedAssemblies = true,
CollectEnvironmentVariables = false,
CollectProcessInformation = false
},
// System information
System = new SystemReportOptions
{
CollectSystemInformation = true,
CollectOsInformation = true,
CollectProcessorInformation = true,
CollectMemoryInformation = true,
CollectDiskInformation = false,
CollectDisplayInformation = false,
CollectNetworkInformation = false
},
// Output options
Formatted = true, // Pretty-print JSON
EncryptionKey = "my-secret-key" // Optional encryption
};Managing Tracked Data
Check Tracking Status
if (LicenseUsageTracking.IsEnabled)
{
Console.WriteLine("License tracking is active");
}Access Tracked Information
// Get all tracked licenses
foreach (var info in LicenseUsageTracking.TrackedLicenses)
{
Console.WriteLine($"License: {info.LicenseId}");
Console.WriteLine($"Product: {info.ProductName} v{info.ProductVersion}");
// Feature access details
foreach (var feature in info.TrackedFeatures)
{
Console.WriteLine($" Feature {feature.Name}:");
Console.WriteLine($" Accesses: {feature.AccessCount}");
Console.WriteLine($" First: {feature.FirstAccessTime}");
Console.WriteLine($" Last: {feature.LastAccessTime}");
}
// Field access details
foreach (var field in info.TrackedFields)
{
Console.WriteLine($" Field {field.Name}: {field.AccessCount} accesses");
}
// Restriction validation details
foreach (var restriction in info.TrackedRestrictions)
{
Console.WriteLine($" Restriction {restriction.Name}: {restriction.AccessCount} checks");
}
}Get Specific License Tracking
var trackingInfo = LicenseUsageTracking.GetTrackingInfo("L-ABC-123");
if (trackingInfo != null)
{
Console.WriteLine($"Tracking {trackingInfo.TrackedFeatures.Count()} features");
}Clear Tracking Data
// Clear all tracked data without disabling tracking
LicenseUsageTracking.Clear();Disable Tracking
// Disable tracking and clear all data
LicenseUsageTracking.Disable();Customizing Reports
Add custom properties to your license reports using the BeforeSendReport event:
reporting.BeforeSendReport += (s, e) =>
{
// Add custom metadata
e.Report.Properties.Add("application_mode", "production");
e.Report.Properties.Add("user_count", activeUsers);
e.Report.Properties.Add("deployment_id", deploymentId);
// Cancel sending if needed
if (shouldSkipReport)
{
e.Cancel = true;
}
};Report Merging
The Babel Licensing Service automatically merges license reports from the same client and machine. This provides:
- Cumulative Statistics: Access counts are added together
- Timeline Preservation: Earliest first access and latest last access are maintained
- Historical Analysis: Complete usage patterns over time
- Storage Efficiency: Single comprehensive report per license/machine/client
When a new report arrives, the server:
- Identifies existing reports for the same license ID, machine ID, and client ID
- Merges tracking data by component name and member
- Accumulates access counts
- Updates access time ranges
- Refreshes metadata with latest values
Error Handling
Handle report sending failures gracefully:
reporting.AfterSendReport += (s, e) =>
{
if (e.Error != null)
{
// Log the error
logger.LogError(e.Error, "Failed to send license report");
// Store report locally for retry
SaveReportForRetry(e.Report);
}
else
{
logger.LogInformation($"Report sent: {e.Result.ReportUid}");
}
};
var result = await reporting.SendLicenseReportAsync(userKey);Best Practices
1. Enable Early
Enable tracking before validating your license to capture all access from the start:
// GOOD: Enable before validation
LicenseUsageTracking.Enable();
var license = licenseManager.Validate(key, type);
// BAD: Enable after validation (misses initial access)
var license = licenseManager.Validate(key, type);
LicenseUsageTracking.Enable();2. Send Periodically
Send reports at regular intervals or application shutdown:
// Option 1: Periodic reporting
var timer = new Timer(async _ =>
{
if (LicenseUsageTracking.IsEnabled)
{
await reporting.SendLicenseReportAsync(userKey);
LicenseUsageTracking.Clear(); // Reset for next period
}
}, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
// Option 2: On application shutdown
AppDomain.CurrentDomain.ProcessExit += async (s, e) =>
{
await reporting.SendLicenseReportAsync(userKey);
};3. Minimize Data Collection
Only collect data you need to reduce report size and network usage:
reporting.Configuration.LicenseReportOptions = new LicenseReportOptions
{
CollectFeaturesAccess = true, // Essential
CollectFieldsAccess = false, // Skip if not needed
CollectRestrictionsAccess = true, // For compliance
System = new SystemReportOptions
{
CollectSystemInformation = true,
CollectDiskInformation = false, // Skip to reduce size
CollectDisplayInformation = false
}
};4. Handle Offline Scenarios
Store reports locally when the server is unreachable:
var result = await reporting.SendLicenseReportAsync(userKey);
if (!result.Success)
{
// Serialize and store report
var reportJson = JsonSerializer.Serialize(result.Report);
await File.WriteAllTextAsync("pending-reports/report-" + Guid.NewGuid() + ".json", reportJson);
}5. Protect Sensitive Data
Use encryption for reports containing sensitive information:
reporting.Configuration.EncryptionKey = Configuration["Reporting:EncryptionKey"];
reporting.Configuration.LicenseReportOptions.EncryptionKey = reporting.Configuration.EncryptionKey;Example: Complete License Tracking
Here’s a comprehensive example demonstrating license tracking in a real application:
using Babel.Licensing;
using Babel.Licensing.Reports;
using Babel.Licensing.Services;
public class LicenseManager
{
private readonly BabelReporting _reporting;
private readonly Timer _reportTimer;
public LicenseManager(IConfiguration configuration)
{
// Initialize reporting service
_reporting = new BabelReporting();
_reporting.Configuration.ServiceUrl = configuration["Licensing:ServiceUrl"];
_reporting.Configuration.ClientId = "EquiTrack v2.0";
_reporting.Configuration.EncryptionKey = configuration["Licensing:EncryptionKey"];
// Configure what to track
_reporting.Configuration.LicenseReportOptions = new LicenseReportOptions
{
CollectFeaturesAccess = true,
CollectFieldsAccess = true,
CollectRestrictionsAccess = true,
Environment = new EnvironmentReportOptions
{
CollectApplicationInformation = true,
CollectLoadedAssemblies = true
},
System = new SystemReportOptions
{
CollectSystemInformation = true,
CollectOsInformation = true,
CollectProcessorInformation = true,
CollectMemoryInformation = true
},
Formatted = false // Compact JSON for network efficiency
};
// Add custom properties before sending
_reporting.BeforeSendReport += (s, e) =>
{
e.Report.Properties.Add("environment", configuration["Environment"]);
e.Report.Properties.Add("version", Assembly.GetExecutingAssembly().GetName().Version.ToString());
};
// Handle send results
_reporting.AfterSendReport += (s, e) =>
{
if (e.Error != null)
{
Logger.Error($"Failed to send license report: {e.Error.Message}");
}
else
{
Logger.Info($"License report sent: {e.Result.ReportUid}");
}
};
// Send reports every 24 hours
_reportTimer = new Timer(
async _ => await SendLicenseReportAsync(),
null,
TimeSpan.FromHours(24),
TimeSpan.FromHours(24)
);
}
public ILicense ValidateLicense(string userKey, LicenseKeyTypes keyType)
{
// Enable tracking before validation
LicenseUsageTracking.Enable();
// Validate license
var licenseManager = new BabelLicensing();
var license = licenseManager.Validate(userKey, keyType);
return license;
}
public async Task SendLicenseReportAsync()
{
if (!LicenseUsageTracking.IsEnabled)
return;
try
{
var result = await _reporting.SendLicenseReportAsync(_userKey);
if (result.Success)
{
// Clear tracking data after successful send
LicenseUsageTracking.Clear();
}
}
catch (Exception ex)
{
Logger.Error($"Exception sending license report: {ex.Message}");
}
}
public void Shutdown()
{
// Send final report on shutdown
SendLicenseReportAsync().Wait(TimeSpan.FromSeconds(10));
// Cleanup
_reportTimer?.Dispose();
LicenseUsageTracking.Disable();
}
}Server-Side Analysis
Once reports reach your Babel Licensing Service, you can:
- Monitor Feature Adoption: See which features are used most frequently
- Identify Unused Features: Find features that are never accessed
- Track Usage Patterns: Analyze access times and frequencies
- Validate Compliance: Ensure restrictions are being checked
- Plan Licensing Strategy: Make data-driven decisions about feature packaging
Reports are stored with full metadata and can be queried through the Babel Licensing Service API or viewed in the management interface.
Related Topics
- Exception Reports - Send crash and error reports
- Custom Reports - Create your own report types
- Floating License - Generate user keys for reporting
- License Activation - Activate licenses for tracking