Skip to Content
New release 11.5 available 🎉
LicensingReportingLicense Reports

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.

  1. Open your preferred Git client or the command line.
  2. Clone the GitHub repository by executing the following command:
git clone https://github.com/babelfornet/console-licensing-report-example.git
  1. Once the repository is cloned, navigate to the project directory:
cd console-licensing-report-example
  1. 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:

  1. BabelReporting and BabelLicensing Initialization: The example code initializes both a BabelReporting object for sending reports and a BabelLicensing object 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); }
  1. 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.

  2. 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 LicenseUsageTracking is 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}"); } }
  1. Sending the License Report: The SendLicenseReportAsync method 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."); }
  1. Custom License Factory: The example includes a custom MemoryRestriction class and CustomLicenseFactory to 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 feature

Tracked 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 field

Tracked 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:

  1. Identifies existing reports for the same license ID, machine ID, and client ID
  2. Merges tracking data by component name and member
  3. Accumulates access counts
  4. Updates access time ranges
  5. 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.

Last updated on