Browse Source

Remove telemetry contributors and update database schema.

Removed telemetry-related contributors and session provider interfaces. Updated database schema, converting UserDeviceId and related fields to strings and adding an OS architecture field in UserDevices.
issue-#6768
Emre KARA 1 year ago
parent
commit
e088032267
  1. 35
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsContributor.cs
  2. 53
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsEnricher.cs
  3. 24
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoContributor.cs
  4. 41
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoEnricher.cs
  5. 22
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  6. 102
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityContext.cs
  7. 2
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEvent.cs
  8. 16
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEventExtensions.cs
  9. 2
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventBuilder.cs
  10. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventEnricher.cs
  11. 4
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs
  12. 15
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/EnricherNode.cs
  13. 95
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs
  14. 94
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryApplicationInfoEnricher.cs
  15. 83
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryDeviceInfoEnricher.cs
  16. 43
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryModuleInfoEnricher.cs
  17. 32
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySessionInfoEnricher.cs
  18. 126
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs
  19. 88
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs
  20. 1
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorageState.cs
  21. 137
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/ActivityPropertyNames.cs
  22. 286
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/DeviceKeyHelper.cs
  23. 6
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryConsts.cs
  24. 23
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs
  25. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryApplicationInfoContributor.cs
  26. 11
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryService.cs
  27. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetrySessionProvider.cs
  28. 29
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryModuleInfoContributor.cs
  29. 21
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryRuntimeSessionProvider.cs
  30. 123
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryService.cs
  31. 28
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoContributor.cs
  32. 42
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoEnricher.cs

35
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsContributor.cs

@ -1,35 +0,0 @@
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Constants;
using System.Linq;
namespace Volo.Abp.AspNetCore.Mvc;
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))]
public class TelemetryApplicationMetricsContributor : ITelemetryApplicationInfoContributor, ISingletonDependency
{
public Task ContributeAsync(ActivityEvent activityEvent)
{
if (activityEvent.TryGetValue(ActivityPropertyNames.Assembly, out var assemblyPath))
{
var assembly = Assembly.LoadFrom((string)assemblyPath);
var types = assembly.GetTypes();
var appServiceCount = types.Count(t =>
typeof(IApplicationService).IsAssignableFrom(t) &&
t is { IsAbstract: false, IsInterface: false });
var controllerCount = types.Count(t =>
typeof(AbpController).IsAssignableFrom(t) &&
!t.IsAbstract);
activityEvent[ActivityPropertyNames.AppServiceCount] = appServiceCount;
activityEvent[ActivityPropertyNames.ControllerCount] = controllerCount;
}
return Task.CompletedTask;
}
}

53
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsEnricher.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Constants;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Reflection;
using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Activity.Providers;
using Volo.Abp.Telemetry.Constants.Enums;
namespace Volo.Abp.AspNetCore.Mvc;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetryApplicationMetricsEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
private readonly ITypeFinder _typeFinder;
public TelemetryApplicationMetricsEnricher(ITypeFinder typeFinder)
{
_typeFinder = typeFinder;
}
public bool IsFirstRun => false;
public Type? DependsOn => typeof(TelemetryApplicationInfoEnricher);
public Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime);
}
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
var appServiceCount = _typeFinder.Types.Count(t =>
typeof(IApplicationService).IsAssignableFrom(t) &&
t is { IsAbstract: false, IsInterface: false } &&
!t.AssemblyQualifiedName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter));
var controllerCount = _typeFinder.Types.Count(t =>
typeof(ControllerBase).IsAssignableFrom(t) &&
!t.IsAbstract);
var items = new Dictionary<string, object>()
{
{ActivityPropertyNames.AppServiceCount, appServiceCount},
{ActivityPropertyNames.ControllerCount, controllerCount}
};
return Task.FromResult<Dictionary<string, object>?>(items);
}
}

24
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoContributor.cs

@ -1,24 +0,0 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Constants;
namespace Volo.Abp.Authorization.Permissions;
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))]
public class TelemetryPermissionInfoContributor : ITelemetryApplicationInfoContributor, ISingletonDependency
{
private readonly IPermissionDefinitionManager _permissionDefinitionManager;
public TelemetryPermissionInfoContributor(IPermissionDefinitionManager permissionDefinitionManager)
{
_permissionDefinitionManager = permissionDefinitionManager;
}
public async Task ContributeAsync(ActivityEvent activityEvent)
{
var permissions = await _permissionDefinitionManager.GetPermissionsAsync();
activityEvent[ActivityPropertyNames.PermissionCount] = permissions.Count;
}
}

41
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoEnricher.cs

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Activity.Providers;
using Volo.Abp.Telemetry.Constants;
namespace Volo.Abp.Authorization.Permissions;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetryPermissionInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
private readonly IPermissionDefinitionManager _permissionDefinitionManager;
public TelemetryPermissionInfoEnricher(IPermissionDefinitionManager permissionDefinitionManager)
{
_permissionDefinitionManager = permissionDefinitionManager;
}
public bool IsFirstRun => false;
public Type? DependsOn => typeof(TelemetryApplicationInfoEnricher);
public Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.ProjectId.HasValue);
}
public async Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
var permissions = await _permissionDefinitionManager.GetPermissionsAsync();
var result = new Dictionary<string, object>
{
{ ActivityPropertyNames.PermissionCount, permissions.Count }
};
return result;
}
}

22
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs

@ -372,7 +372,7 @@ public abstract class AbpApplicationBase : IAbpApplication
protected void SetupTelemetryTracking()
{
if (ShouldSendTelemetryData())
if (!ShouldSendTelemetryData())
{
return;
}
@ -387,23 +387,9 @@ public abstract class AbpApplicationBase : IAbpApplication
{
try
{
var assembly = Assembly.GetEntryAssembly()!;
var projectMetaData = AbpProjectMetadataReader.ReadProjectMetadata(assembly);
if (projectMetaData is { ProjectId: not null, AbpSlnPath: not null })
{
using var scope = ServiceProvider.CreateScope();
var telemetryService = scope.ServiceProvider.GetRequiredService<ITelemetryService>();
await telemetryService.AddActivityAsync(ActivityNameConsts.ApplicationRun, activity =>
{
activity[ActivityPropertyNames.Assembly] = assembly.Location;
activity[ActivityPropertyNames.ProjectId] = projectMetaData.ProjectId!;
activity[ActivityPropertyNames.ProjectType] = projectMetaData.Role!;
activity[ActivityPropertyNames.SolutionPath] = projectMetaData.AbpSlnPath;
});
}
using var scope = ServiceProvider.CreateScope();
var telemetryService = scope.ServiceProvider.GetRequiredService<ITelemetryService>();
await telemetryService.AddActivityAsync(ActivityNameConsts.ApplicationRun);
}
catch (Exception ex)
{

102
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityContext.cs

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Volo.Abp.Telemetry.Constants;
using Volo.Abp.Telemetry.Constants.Enums;
namespace Volo.Abp.Telemetry.Activity;
public class ActivityContext
{
private ActivityContext(ActivityEvent current)
{
Current = current;
}
public ActivityEvent Current { get; }
public Dictionary<string, object> ExtraProperties { get; } = new();
public bool IsTerminated { get; private set; }
public bool IsCancelled { get; private set; }
public CancellationToken CancellationToken { get; set; }
public static ActivityContext Create(string activityName, string? details = null,
Action<Dictionary<string, object>>? additionalProperties = null)
{
var activity = new ActivityEvent(activityName, details);
if (additionalProperties is not null)
{
activity.AdditionalProperties = new Dictionary<string, object>();
additionalProperties.Invoke(activity.AdditionalProperties);
}
return new ActivityContext(activity);
}
public Guid? ProjectId {
get {
if (!Current.TryGetValue(ActivityPropertyNames.ProjectId, out var projectId))
{
return null;
}
if (Guid.TryParse(projectId.ToString(), out var projectIdGuid))
{
return projectIdGuid;
}
return null;
}
}
public Guid? SolutionId {
get {
if (!Current.TryGetValue(ActivityPropertyNames.SolutionId, out var solutionId))
{
return null;
}
if (Guid.TryParse(solutionId!.ToString(), out var solutionIdGuid))
{
return solutionIdGuid;
}
return null;
}
}
public SessionType? SessionType {
get {
if (Current.TryGetValue(ActivityPropertyNames.SessionType, out var sessionTypeObj) &&
Enum.TryParse<SessionType>(sessionTypeObj?.ToString(), out var sessionType))
{
return sessionType;
}
return null;
}
}
public string? SolutionPath => ExtraProperties.TryGetValue(ActivityPropertyNames.SolutionPath, out var solutionPath)
? solutionPath?.ToString()
: null;
public string? DeviceId => Current.TryGetValue(ActivityPropertyNames.DeviceId, out var deviceId) ? deviceId?.ToString() : null;
public void Terminate()
{
IsTerminated = true;
}
public void Cancel()
{
IsCancelled = true;
}
public void ResetCancel()
{
IsCancelled = false;
}
}

2
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEvent.cs

@ -5,7 +5,7 @@ namespace Volo.Abp.Telemetry.Activity;
public class ActivityEvent : Dictionary<string, object>
{
private ActivityEvent()
public ActivityEvent()
{
}

16
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEventExtensions.cs

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
namespace Volo.Abp.Telemetry.Activity;
@ -11,30 +13,34 @@ public static class ActivityEventExtensions
return activityEvent;
}
public static ActivityEvent WithAdditionalProperties(this ActivityEvent activityEvent, Dictionary<string, object> properties)
public static ActivityEvent WithAdditionalProperties(this ActivityEvent activityEvent,
Dictionary<string, object> properties)
{
activityEvent.AdditionalProperties ??= new Dictionary<string, object>();
foreach (var property in properties)
{
activityEvent.AdditionalProperties[property.Key] = property.Value;
}
return activityEvent;
}
public static ActivityEvent WithAdditionalProperties(this ActivityEvent activityEvent, params (string key, object value)[] properties)
public static ActivityEvent WithAdditionalProperties(this ActivityEvent activityEvent,
params (string key, object value)[] properties)
{
activityEvent.AdditionalProperties ??= new Dictionary<string, object>();
foreach (var (key, value) in properties)
{
activityEvent.AdditionalProperties[key] = value;
}
return activityEvent;
}
public static ActivityEvent WithAdditionalProperties(this ActivityEvent activityEvent, object properties)
{
activityEvent.AdditionalProperties ??= new Dictionary<string, object>();
foreach (var property in properties.GetType().GetProperties())
{
var value = property.GetValue(properties);
@ -43,7 +49,7 @@ public static class ActivityEventExtensions
activityEvent.AdditionalProperties[property.Name] = value;
}
}
return activityEvent;
}
}
}

2
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventBuilder.cs

@ -4,5 +4,5 @@ namespace Volo.Abp.Telemetry.Activity.Contracts;
public interface ITelemetryActivityEventBuilder
{
Task BuildAsync(ActivityEvent activity);
Task<ActivityEvent?> BuildAsync(ActivityContext context);
}

10
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventEnricher.cs

@ -1,8 +1,14 @@
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Telemetry.Activity.Contracts;
public interface ITelemetryActivityEventEnricher
{
Task EnrichAsync(ActivityEvent activity);
bool IsFirstRun { get; }
Type? DependsOn { get; }
Task<bool> CanExecuteAsync(ActivityContext context);
Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context);
}

4
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs

@ -8,12 +8,8 @@ public interface ITelemetryActivityStorage
{
Task<Guid> InitializeOrGetSessionAsync();
Task MarkActivitiesAsSentAsync();
Task MarkDeviceInfoAsAddedAsync();
Task MarkSolutionInfoAsAddedAsync(Guid solutionId);
Task MarkApplicationInfoAsAddedAsync(Guid applicationInfo);
Task BufferActivityAsync(ActivityEvent activityEvent);
Task<List<ActivityEvent>> GetBufferedActivitiesAsync();
Task EndSessionAsync();
Task<bool> ShouldAddDeviceInfoAsync();
Task<bool> ShouldAddSolutionInformation(Guid solutionId);
Task<bool> ShouldAddProjectInfoAsync(Guid projectId);

15
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/EnricherNode.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
using Volo.Abp.Telemetry.Activity.Contracts;
namespace Volo.Abp.Telemetry.Activity.Providers;
public class EnricherNode
{
public ITelemetryActivityEventEnricher Enricher { get; set; }
public List<EnricherNode> Children { get; set; } = new();
public EnricherNode(ITelemetryActivityEventEnricher enricher)
{
Enricher = enricher;
}
}

95
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs

@ -1,33 +1,96 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry.Activity.Contracts;
namespace Volo.Abp.Telemetry.Activity.Providers;
public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, ISingletonDependency
public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, IScopedDependency
{
private readonly IServiceProvider _serviceProvider;
private readonly IEnumerable<ITelemetryActivityEventEnricher> _activityDataEnrichers;
public TelemetryActivityEventBuilder(IServiceProvider serviceProvider)
public TelemetryActivityEventBuilder(IEnumerable<ITelemetryActivityEventEnricher> activityDataEnrichers)
{
_serviceProvider = serviceProvider;
_activityDataEnrichers = activityDataEnrichers;
}
public virtual async Task BuildAsync(ActivityEvent activity)
public virtual async Task<ActivityEvent?> BuildAsync(ActivityContext context)
{
var sessionProvider = _serviceProvider.GetRequiredService<ITelemetrySessionProvider>();
var enricherNodes = BuildTree(_activityDataEnrichers);
await ExecuteEnrichersAsync(enricherNodes, context);
await sessionProvider.AddSessionInfoAsync(activity);
var activityDataEnrichers = _serviceProvider.GetRequiredService<IEnumerable<ITelemetryActivityEventEnricher>>();
if (context.IsTerminated)
{
return null;
}
return context.Current;
}
private async Task ExecuteEnrichersAsync(
IEnumerable<EnricherNode> nodes,
ActivityContext context)
{
async Task ExecuteEnricherChainAsync(IEnumerable<EnricherNode> currentNodes)
{
foreach (var node in currentNodes)
{
if (context.IsTerminated)
{
return;
}
var enricher = node.Enricher;
if (!await enricher.CanExecuteAsync(context))
{
continue;
}
foreach (var activityDataEnricher in activityDataEnrichers)
var enricherResult = await enricher.EnrichAsync(context);
if (enricherResult != null)
{
foreach (var kv in enricherResult)
{
context.Current[kv.Key] = kv.Value;
}
}
if (!context.IsCancelled)
{
await ExecuteEnricherChainAsync(node.Children);
}
context.ResetCancel();
}
}
await ExecuteEnricherChainAsync(nodes);
}
private static List<EnricherNode> BuildTree(IEnumerable<ITelemetryActivityEventEnricher> enrichers)
{
var enricherList = enrichers.ToList();
var nodeMap = enricherList.ToDictionary(e => e.GetType(), e => new EnricherNode(e));
foreach (var enricher in enricherList)
{
await activityDataEnricher.EnrichAsync(activity);
if (enricher.DependsOn != null)
{
if (nodeMap.TryGetValue(enricher.DependsOn, out var parentNode))
{
parentNode.Children.Add(nodeMap[enricher.GetType()]);
}
}
}
var roots = enricherList
.Where(e => e.DependsOn == null)
.OrderByDescending(e => e.IsFirstRun)
.Select(e => nodeMap[e.GetType()])
.ToList();
return roots;
}
}
}

94
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryApplicationInfoEnricher.cs

@ -1,74 +1,108 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Constants;
using Volo.Abp.Telemetry.Constants.Enums;
using Volo.Abp.Telemetry.Helpers;
namespace Volo.Abp.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetryApplicationInfoEnricher : ITelemetryActivityEventEnricher, ISingletonDependency
public class TelemetryApplicationInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
private readonly IEnumerable<ITelemetryApplicationInfoContributor> _telemetryApplicationInfoContributors;
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
public TelemetryApplicationInfoEnricher(
IEnumerable<ITelemetryApplicationInfoContributor> telemetryApplicationInfoContributors,
ITelemetryActivityStorage telemetryActivityStorage)
{
_telemetryApplicationInfoContributors = telemetryApplicationInfoContributors;
_telemetryActivityStorage = telemetryActivityStorage;
}
public async Task EnrichAsync(ActivityEvent activity)
public bool IsFirstRun => true;
public Type DependsOn => typeof(TelemetrySessionInfoEnricher);
public Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime);
}
public async Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
try
{
if (!IsValidActivity(activity))
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly is null)
{
context.Terminate();
return null;
}
var projectMetaData = AbpProjectMetadataReader.ReadProjectMetadata(entryAssembly);
if (projectMetaData?.ProjectId == null || projectMetaData.AbpSlnPath.IsNullOrEmpty())
{
return;
context.Terminate();
return null;
}
if (!TryGetProjectId(activity, out var projectId) && !await _telemetryActivityStorage.ShouldAddProjectInfoAsync(projectId))
if (!await _telemetryActivityStorage.ShouldAddProjectInfoAsync(projectMetaData.ProjectId.Value))
{
return;
context.Cancel();
return null;
}
foreach (var contributor in _telemetryApplicationInfoContributors)
context.ExtraProperties[ActivityPropertyNames.SolutionPath] = projectMetaData.AbpSlnPath;
var solutionId = ReadSolutionIdFromSolutionPath(projectMetaData.AbpSlnPath);
if (!solutionId.HasValue)
{
await contributor.ContributeAsync(activity);
context.Terminate();
return null;
}
var result = new Dictionary<string, object>
{
{ ActivityPropertyNames.SolutionId, solutionId },
{ ActivityPropertyNames.ProjectId, projectMetaData.ProjectId.Value },
{ ActivityPropertyNames.ProjectType, projectMetaData.Role ?? string.Empty },
};
activity.Remove(ActivityPropertyNames.Assembly);
activity[ActivityPropertyNames.HasProjectInfo] = true;
await _telemetryActivityStorage.MarkApplicationInfoAsAddedAsync(projectId);
result[ActivityPropertyNames.HasProjectInfo] = true;
return result;
}
catch
{
// ignored
return null;
}
}
private bool IsValidActivity(ActivityEvent activity)
{
return activity.ContainsKey(ActivityPropertyNames.Assembly)
&& activity.TryGetValue(ActivityPropertyNames.SessionType, out var sessionTypeObj)
&& Enum.TryParse<SessionType>(sessionTypeObj?.ToString(), out var parsed)
&& parsed == SessionType.ApplicationRuntime;
}
private static bool TryGetProjectId(ActivityEvent activity, out Guid projectId)
private static Guid? ReadSolutionIdFromSolutionPath(string solutionPath)
{
if (!activity.TryGetValue(ActivityPropertyNames.ProjectId, out var projectIdObj) ||
projectIdObj is not string projectIdStr ||
!Guid.TryParse(projectIdStr, out projectId))
try
{
if (solutionPath.IsNullOrEmpty())
{
return null;
}
using var fs = new FileStream(solutionPath!, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var doc = JsonDocument.Parse(fs);
if (doc.RootElement.TryGetProperty("id", out var id) &&
Guid.TryParse(id.GetString(), out var solutionId))
{
return solutionId;
}
}
catch
{
projectId = Guid.Empty;
return false;
// ignored
}
return true;
return null;
}
}

83
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryDeviceInfoEnricher.cs

@ -1,8 +1,7 @@
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
@ -10,14 +9,11 @@ using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Constants;
using Volo.Abp.Telemetry.Constants.Enums;
using Volo.Abp.Telemetry.EnvironmentInspection.Contracts;
#if WINDOWS
using System.Management;
#endif
namespace Volo.Abp.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetryDeviceInfoEnricher : ITelemetryActivityEventEnricher, ISingletonDependency
public class TelemetryDeviceInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
private readonly ISoftwareInfoProvider _softwareInfoProvider;
@ -29,64 +25,58 @@ public class TelemetryDeviceInfoEnricher : ITelemetryActivityEventEnricher, ISin
_softwareInfoProvider = softwareInfoProvider;
}
public async Task EnrichAsync(ActivityEvent activity)
public bool IsFirstRun => true;
public Type? DependsOn => null;
public Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(true);
}
public async Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
try
{
var deviceId = DeviceKeyHelper.GetUniquePhysicalKey(true);
if (!await _telemetryActivityStorage.ShouldAddDeviceInfoAsync())
{
return;
context.Cancel();
return new Dictionary<string, object>
{
{ ActivityPropertyNames.DeviceId, deviceId },
};
}
EnrichWithDeviceInfo(activity);
await EnrichWithSoftwareInfoAsync(activity);
activity[ActivityPropertyNames.HasDeviceInfo] = true;
await _telemetryActivityStorage.MarkDeviceInfoAsAddedAsync();
var result = new Dictionary<string, object>
{
[ActivityPropertyNames.DeviceId] = deviceId,
[ActivityPropertyNames.DeviceLanguage] = CultureInfo.CurrentUICulture.Name,
[ActivityPropertyNames.OperatingSystem] = GetOperatingSystem(),
[ActivityPropertyNames.CountryIsoCode] = GetCountry(),
[ActivityPropertyNames.OperatingSystemArchitecture] =RuntimeInformation.OSArchitecture.ToString()
};
await EnrichWithSoftwareInfoAsync(result);
result[ActivityPropertyNames.HasDeviceInfo] = true;
return result;
}
catch
{
context.Terminate();
return null;
//ignored
}
}
private void EnrichWithDeviceInfo(ActivityEvent activity)
{
if (TryGetDeviceId(out var deviceId))
{
activity[ActivityPropertyNames.DeviceId] = deviceId;
}
activity[ActivityPropertyNames.DeviceLanguage] = CultureInfo.CurrentUICulture.Name;
activity[ActivityPropertyNames.OperatingSystem] = GetOperatingSystem();
activity[ActivityPropertyNames.CountryIsoCode] = GetCountry();
}
private async Task EnrichWithSoftwareInfoAsync(ActivityEvent activity)
private async Task EnrichWithSoftwareInfoAsync(Dictionary<string,object> activity)
{
var softwareList = await _softwareInfoProvider.GetSoftwareInfoAsync();
activity[ActivityPropertyNames.InstalledSoftwares] = softwareList;
}
private static bool TryGetDeviceId(out Guid deviceId)
{
try
{
if (File.Exists(TelemetryPaths.ComputerId))
{
var deviceIdText = File.ReadAllText(TelemetryPaths.ComputerId);
deviceId = deviceIdText.To<Guid>();
return true;
}
}
catch
{
// ignored
}
deviceId = Guid.Empty;
return false;
}
private static OperationSystem GetOperatingSystem()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -107,6 +97,7 @@ public class TelemetryDeviceInfoEnricher : ITelemetryActivityEventEnricher, ISin
return OperationSystem.Unknown;
}
private static string GetCountry()
{

43
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryModuleInfoEnricher.cs

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Constants;
using Volo.Abp.Telemetry.Constants.Enums;
namespace Volo.Abp.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetryModuleInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
private readonly IModuleContainer _moduleContainer;
private readonly IAssemblyFinder _assemblyFinder;
public TelemetryModuleInfoEnricher(IModuleContainer moduleContainer, IAssemblyFinder assemblyFinder)
{
_moduleContainer = moduleContainer;
_assemblyFinder = assemblyFinder;
}
public bool IsFirstRun => false;
public Type? DependsOn => typeof(TelemetrySessionInfoEnricher);
public Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime);
}
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
var result = new Dictionary<string, object>
{
{ ActivityPropertyNames.ModuleCount, _moduleContainer.Modules.Count },
{ ActivityPropertyNames.ProjectCount, _assemblyFinder.Assemblies.Count(x => !x.FullName.IsNullOrEmpty() && !x.FullName.StartsWith(TelemetryConsts.VoloNameSpaceFilter)) }
};
return Task.FromResult<Dictionary<string, object>?>(result);
}
}

32
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySessionInfoEnricher.cs

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Constants;
using Volo.Abp.Telemetry.Constants.Enums;
namespace Volo.Abp.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetrySessionInfoEnricher : ITelemetryActivityEventEnricher , IScopedDependency
{
public bool IsFirstRun => true;
public Type? DependsOn => null;
public Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(true);
}
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
var result = new Dictionary<string, object>
{
{ ActivityPropertyNames.SessionType, SessionType.ApplicationRuntime },
{ ActivityPropertyNames.SessionId, Guid.NewGuid().ToString() },
{ ActivityPropertyNames.IsFirstSession, !File.Exists(TelemetryPaths.ActivityStorage) }
};
return Task.FromResult<Dictionary<string, object>?>(result);
}
}

126
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs

@ -11,7 +11,7 @@ using Volo.Abp.Telemetry.Constants;
namespace Volo.Abp.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetrySolutionInfoEnricher : ITelemetryActivityEventEnricher, ISingletonDependency
public class TelemetrySolutionInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
@ -20,122 +20,68 @@ public class TelemetrySolutionInfoEnricher : ITelemetryActivityEventEnricher, IS
_telemetryActivityStorage = telemetryActivityStorage;
}
public bool IsFirstRun => true;
public Type? DependsOn => typeof(TelemetrySessionInfoEnricher);
public async Task EnrichAsync(ActivityEvent activity)
public async Task<bool> CanExecuteAsync(ActivityContext context)
{
if (TryGetSolutionId(activity, out var solutionId))
if (context.SolutionId.HasValue && !context.SolutionPath.IsNullOrEmpty())
{
if (await _telemetryActivityStorage.ShouldAddSolutionInformation(solutionId))
{
activity[ActivityPropertyNames.SolutionId] = solutionId;
AddSolutionInfo(activity);
await _telemetryActivityStorage.MarkSolutionInfoAsAddedAsync(solutionId);
activity[ActivityPropertyNames.HasSolutionInfo] = true;
activity.Remove(ActivityPropertyNames.SolutionPath);
}
}
}
private bool TryGetSolutionId(ActivityEvent activity, out Guid solutionId)
{
if (TryGetSolutionIdFromActivity(activity, out solutionId))
{
return true;
}
if (TryGetSolutionIdFromFile(activity, out solutionId))
{
return true;
return await _telemetryActivityStorage.ShouldAddSolutionInformation(context.SolutionId.Value);
}
solutionId = Guid.Empty;
return false;
}
private void AddSolutionInfo(ActivityEvent activity)
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
try
{
var solutionPath = activity[ActivityPropertyNames.SolutionPath]!.ToString()!;
var result = new Dictionary<string, object>();
var jsonContent = File.ReadAllText(solutionPath);
var jsonContent = File.ReadAllText(context.SolutionPath!);
using var doc = JsonDocument.Parse(jsonContent);
var root = doc.RootElement;
AddSolutionCreationConfiguration(activity, root.GetProperty("creatingStudioConfiguration"));
AddSolutionCreationConfiguration(result, root.GetProperty("creatingStudioConfiguration"));
if (root.TryGetProperty("modules", out var modulesElement) &&
modulesElement.ValueKind == JsonValueKind.Object)
{
activity[ActivityPropertyNames.InstalledModules] = ExtractModules(solutionPath, modulesElement);
result[ActivityPropertyNames.InstalledModules] = ExtractModules(context.SolutionPath!, modulesElement);
}
}
catch
{
// ignored
}
}
private bool TryGetSolutionIdFromActivity(ActivityEvent activity, out Guid solutionId)
{
if (activity.TryGetValue(ActivityPropertyNames.SolutionId, out var value) &&
Guid.TryParse(value?.ToString(), out var parsedId))
{
solutionId = parsedId;
return true;
}
solutionId = Guid.Empty;
return false;
}
result[ActivityPropertyNames.HasSolutionInfo] = true;
private bool TryGetSolutionIdFromFile(ActivityEvent activity, out Guid solutionId)
{
if (activity.TryGetValue(ActivityPropertyNames.SolutionPath, out var pathValue) &&
pathValue is string &&
File.Exists(pathValue.ToString()))
return Task.FromResult<Dictionary<string, object>?>(result);
}
catch
{
try
{
var solutionPath = pathValue.ToString()!;
var json = File.ReadAllText(solutionPath);
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("id", out var idProp) &&
Guid.TryParse(idProp.GetString(), out var parsedId))
{
solutionId = parsedId;
return true;
}
}
catch
{
// ignored
}
return Task.FromResult<Dictionary<string, object>?>(null);
}
solutionId = Guid.Empty;
return false;
}
private void AddSolutionCreationConfiguration(ActivityEvent activity, JsonElement config)
private void AddSolutionCreationConfiguration(Dictionary<string, object> result, JsonElement config)
{
activity[ActivityPropertyNames.Template] = config.GetProperty("template").GetString() ?? string.Empty;
activity[ActivityPropertyNames.CreatedAbpStudioVersion] = config.GetProperty("createdAbpStudioVersion").GetString()!;
activity[ActivityPropertyNames.MultiTenancy] = ParseBool(config.GetProperty("multiTenancy"));
activity[ActivityPropertyNames.UiFramework] = config.GetProperty("uiFramework").GetString() ?? string.Empty;
activity[ActivityPropertyNames.DatabaseProvider] = config.GetProperty("databaseProvider").GetString() ?? string.Empty;
activity[ActivityPropertyNames.Theme] = config.GetProperty("theme").GetString() ?? string.Empty;
activity[ActivityPropertyNames.ThemeStyle] = config.GetProperty("themeStyle").GetString() ?? string.Empty;
activity[ActivityPropertyNames.HasPublicWebsite] = ParseBool(config.GetProperty("publicWebsite"));
activity[ActivityPropertyNames.IsTiered] = ParseBool(config.GetProperty("tiered"));
activity[ActivityPropertyNames.SocialLogins] = ParseBool(config.GetProperty("socialLogin"));
activity[ActivityPropertyNames.DatabaseManagementSystem] = config.GetProperty("databaseManagementSystem").GetString() ?? string.Empty;
activity[ActivityPropertyNames.IsSeparateTenantSchema] = ParseBool(config.GetProperty("separateTenantSchema"));
activity[ActivityPropertyNames.MobileFramework] = config.GetProperty("mobileFramework").GetString() ?? string.Empty;
activity[ActivityPropertyNames.IncludeTests] = ParseBool(config.GetProperty("includeTests"));
activity[ActivityPropertyNames.DynamicLocalization] = ParseBool(config.GetProperty("dynamicLocalization"));
activity[ActivityPropertyNames.KubernetesConfiguration] = ParseBool(config.GetProperty("kubernetesConfiguration"));
activity[ActivityPropertyNames.GrafanaDashboard] = ParseBool(config.GetProperty("grafanaDashboard"));
result[ActivityPropertyNames.Template] = config.TryGetProperty("template", out var template) ? template.GetRawText() : string.Empty;
result[ActivityPropertyNames.CreatedAbpStudioVersion] = config.TryGetProperty("createdAbpStudioVersion", out var createdAbpStudioVersion) ? createdAbpStudioVersion.GetRawText() : string.Empty;
result[ActivityPropertyNames.MultiTenancy] = config.TryGetProperty("multiTenancy", out var multiTenancy) && ParseBool(multiTenancy);
result[ActivityPropertyNames.UiFramework] = config.TryGetProperty("uiFramework", out var uiFramework) ? uiFramework.GetRawText() : string.Empty;
result[ActivityPropertyNames.DatabaseProvider] = config.TryGetProperty("databaseProvider", out var databaseProvider) ? databaseProvider.GetRawText() : string.Empty;
result[ActivityPropertyNames.Theme] = config.TryGetProperty("theme", out var theme) ? theme.GetRawText() : string.Empty;
result[ActivityPropertyNames.ThemeStyle] = config.TryGetProperty("themeStyle", out var themeStyle) ? themeStyle.GetRawText() : string.Empty;
result[ActivityPropertyNames.HasPublicWebsite] = config.TryGetProperty("publicWebsite", out var publicWebsite) && ParseBool(publicWebsite);
result[ActivityPropertyNames.IsTiered] = config.TryGetProperty("tiered", out var tiered) && ParseBool(tiered);
result[ActivityPropertyNames.SocialLogins] = config.TryGetProperty("socialLogin", out var socialLogin) && ParseBool(socialLogin);
result[ActivityPropertyNames.DatabaseManagementSystem] = config.TryGetProperty("databaseManagementSystem", out var databaseManagementSystem) ? databaseManagementSystem.GetRawText() : string.Empty;
result[ActivityPropertyNames.IsSeparateTenantSchema] = config.TryGetProperty("separateTenantSchema", out var separateTenantSchema) && ParseBool(separateTenantSchema);
result[ActivityPropertyNames.MobileFramework] = config.TryGetProperty("mobileFramework", out var mobileFramework) ? mobileFramework.GetRawText() : string.Empty;
result[ActivityPropertyNames.IncludeTests] = config.TryGetProperty("includeTests", out var includeTests) && ParseBool(includeTests);
result[ActivityPropertyNames.DynamicLocalization] = config.TryGetProperty("dynamicLocalization", out var dynamicLocalization) && ParseBool(dynamicLocalization);
result[ActivityPropertyNames.KubernetesConfiguration] = config.TryGetProperty("kubernetesConfiguration", out var kubernetesConfiguration) && ParseBool(kubernetesConfiguration);
result[ActivityPropertyNames.GrafanaDashboard] = config.TryGetProperty("grafanaDashboard", out var grafanaDashboard) && ParseBool(grafanaDashboard);
}
private List<Dictionary<string, object>> ExtractModules(string solutionPath, JsonElement modulesElement)

88
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs

@ -38,6 +38,26 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
await UpdateStateAsync(state =>
{
state.Activities.Insert(0, activityEvent);
if (activityEvent.ActivityName == ActivityNameConsts.AbpStudioClose)
{
state.SessionId = null;
}
if (activityEvent.ContainsKey(ActivityPropertyNames.HasSolutionInfo))
{
state.Solutions[Guid.Parse(activityEvent[ActivityPropertyNames.SolutionId].ToString()!)] = DateTimeOffset.UtcNow;
}
if (activityEvent.ContainsKey(ActivityPropertyNames.HasProjectInfo))
{
state.Projects[Guid.Parse(activityEvent[ActivityPropertyNames.ProjectId].ToString()!)] = DateTimeOffset.UtcNow;
}
if (activityEvent.ContainsKey(ActivityPropertyNames.HasDeviceInfo))
{
state.LastDeviceInfoAddTime = DateTimeOffset.UtcNow;
}
});
}
@ -47,14 +67,6 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
return state.Activities;
}
public async Task EndSessionAsync()
{
await UpdateStateAsync(state =>
{
state.SessionId = null;
});
}
public async Task<Guid> InitializeOrGetSessionAsync()
{
var state = await GetStateAsync();
@ -80,30 +92,6 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
});
}
public async Task MarkSolutionInfoAsAddedAsync(Guid solutionId)
{
await UpdateStateAsync(state =>
{
state.Solutions[solutionId] = DateTimeOffset.UtcNow;
});
}
public async Task MarkApplicationInfoAsAddedAsync(Guid applicationId)
{
await UpdateStateAsync(state =>
{
state.Projects[applicationId] = DateTimeOffset.UtcNow;
});
}
public async Task MarkDeviceInfoAsAddedAsync()
{
await UpdateStateAsync(state =>
{
state.LastDeviceInfoAddTime = DateTimeOffset.UtcNow;
});
}
public virtual async Task<bool> ShouldAddDeviceInfoAsync()
{
var lastSend = await GetLastDeviceInfoSendTimeAsync();
@ -130,8 +118,7 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
private static bool IsTestModeEnabled()
{
var testModeVariable =
Environment.GetEnvironmentVariable(TestModeEnvironmentVariable, EnvironmentVariableTarget.User);
var testModeVariable = Environment.GetEnvironmentVariable(TestModeEnvironmentVariable, EnvironmentVariableTarget.User);
return bool.TryParse(testModeVariable, out var isTestMode) && isTestMode;
}
@ -211,11 +198,19 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
{
try
{
CreateDirectoryIfNotExists(Path.GetDirectoryName(TelemetryPaths.ActivityStorage)!);
var storageDirectory = Path.GetDirectoryName(TelemetryPaths.ActivityStorage)!;
if (!Directory.Exists(storageDirectory))
{
Directory.CreateDirectory(storageDirectory);
}
if (!File.Exists(TelemetryPaths.ActivityStorage))
{
InitializeStorage();
var initialState = _cachedState ?? new TelemetryActivityStorageState();
var json = JsonSerializer.Serialize(initialState, JsonSerializerOptions);
var encryptedJson = Cryptography.Encrypt(json);
File.WriteAllText(TelemetryPaths.ActivityStorage, encryptedJson, Encoding.UTF8);
}
}
catch
@ -224,28 +219,11 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
}
}
private static void CreateDirectoryIfNotExists(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
}
private void InitializeStorage()
{
var initialState = _cachedState ?? new TelemetryActivityStorageState();
var json = JsonSerializer.Serialize(initialState, JsonSerializerOptions);
var encryptedJson = Cryptography.Encrypt(json);
File.WriteAllText(TelemetryPaths.ActivityStorage, encryptedJson, Encoding.UTF8);
}
private async Task<TResult?> WithExclusiveFileLockAsync<TResult>(Func<FileStream, Task<TResult>> action)
{
return await MutexExecutor.ExecuteAsync(MutexName, async () =>
{
using var stream = new FileStream(
using var stream = new FileStream(
TelemetryPaths.ActivityStorage,
FileMode.OpenOrCreate,
FileAccess.ReadWrite,

1
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorageState.cs

@ -10,7 +10,6 @@ public class TelemetryActivityStorageState
public Guid? SessionId { get; set; }
public List<ActivityEvent> Activities { get; set; } = new();
public Dictionary<Guid,DateTimeOffset> Solutions { get; set; } = new();
public Dictionary<Guid, DateTimeOffset> Projects { get; set; } = new();
}

137
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/ActivityPropertyNames.cs

@ -2,76 +2,75 @@
public static class ActivityPropertyNames
{
public const string SessionId = nameof(SessionId);
public const string ActivityName = nameof(ActivityName);
public const string Error = nameof(Error);
public const string ErrorDetail = nameof(ErrorDetail);
public const string Id = nameof(Id);
public const string UserId = nameof(UserId);
public const string OrganizationId = nameof(OrganizationId);
public const string IpAddress = nameof(IpAddress);
public const string IsFirstSession = nameof(IsFirstSession);
public const string DeviceId = nameof(DeviceId);
public const string DeviceLanguage = nameof(DeviceLanguage);
public const string OperatingSystem = nameof(OperatingSystem);
public const string CountryIsoCode = nameof(CountryIsoCode);
public const string InstalledSoftwares = nameof(InstalledSoftwares);
public const string ControllerCount = nameof(ControllerCount);
public const string EntityCount = nameof(EntityCount);
public const string ProjectCount = nameof(ProjectCount);
public const string ModuleCount = nameof(ModuleCount);
public const string PermissionCount = nameof(PermissionCount);
public const string AppServiceCount = nameof(AppServiceCount);
public const string ProjectType = nameof(ProjectType);
public const string ProjectId = nameof(ProjectId);
public const string SolutionId = nameof(SolutionId);
public const string Template = nameof(Template);
public const string CreatedAbpStudioVersion = nameof(CreatedAbpStudioVersion);
public const string IsTiered = nameof(IsTiered);
public const string UiFramework = nameof(UiFramework);
public const string DatabaseProvider = nameof(DatabaseProvider);
public const string DatabaseManagementSystem = nameof(DatabaseManagementSystem);
public const string IsSeparateTenantSchema = nameof(IsSeparateTenantSchema);
public const string Theme = nameof(Theme);
public const string ThemeStyle = nameof(ThemeStyle);
public const string MobileFramework = nameof(MobileFramework);
public const string HasPublicWebsite = nameof(HasPublicWebsite);
public const string IncludeTests = nameof(IncludeTests);
public const string MultiTenancy = nameof(MultiTenancy);
public const string DynamicLocalization = nameof(DynamicLocalization);
public const string KubernetesConfiguration = nameof(KubernetesConfiguration);
public const string GrafanaDashboard = nameof(GrafanaDashboard);
public const string SocialLogins = nameof(SocialLogins);
public const string InstalledModules = nameof(InstalledModules);
public const string SolutionPath = nameof(SolutionPath);
public const string Assembly = nameof(Assembly);
public const string LicenseType = nameof(LicenseType);
public const string SessionType = nameof(SessionType);
public const string HasError = nameof(HasError);
public const string ActivityDuration = nameof(ActivityDuration);
public const string ActivityDetails = nameof(ActivityDetails);
public const string Time = nameof(Time);
public const string SessionId = "SessionId";
public const string ActivityName = "ActivityName";
public const string Error = "Error";
public const string ErrorDetail = "ErrorDetail";
public const string Id = "Id";
public const string UserId = "UserId";
public const string OrganizationId = "OrganizationId";
public const string IpAddress = "IpAddress";
public const string IsFirstSession = "IsFirstSession";
public const string DeviceId = "DeviceId";
public const string DeviceLanguage = "DeviceLanguage";
public const string OperatingSystem = "OperatingSystem";
public const string CountryIsoCode = "CountryIsoCode";
public const string InstalledSoftwares = "InstalledSoftwares";
public const string ControllerCount = "ControllerCount";
public const string EntityCount = "EntityCount";
public const string ProjectCount = "ProjectCount";
public const string ModuleCount = "ModuleCount";
public const string PermissionCount = "PermissionCount";
public const string AppServiceCount = "AppServiceCount";
public const string ProjectType = "ProjectType";
public const string ProjectId = "ProjectId";
public const string SolutionId = "SolutionId";
public const string Template = "Template";
public const string CreatedAbpStudioVersion = "CreatedAbpStudioVersion";
public const string IsTiered = "IsTiered";
public const string UiFramework = "UiFramework";
public const string DatabaseProvider = "DatabaseProvider";
public const string DatabaseManagementSystem = "DatabaseManagementSystem";
public const string IsSeparateTenantSchema = "IsSeparateTenantSchema";
public const string Theme = "Theme";
public const string ThemeStyle = "ThemeStyle";
public const string MobileFramework = "MobileFramework";
public const string HasPublicWebsite = "HasPublicWebsite";
public const string IncludeTests = "IncludeTests";
public const string MultiTenancy = "MultiTenancy";
public const string DynamicLocalization = "DynamicLocalization";
public const string KubernetesConfiguration = "KubernetesConfiguration";
public const string GrafanaDashboard = "GrafanaDashboard";
public const string SocialLogins = "SocialLogins";
public const string InstalledModules = "InstalledModules";
public const string SolutionPath = "SolutionPath";
public const string LicenseType = "LicenseType";
public const string SessionType = "SessionType";
public const string HasError = "HasError";
public const string ActivityDuration = "ActivityDuration";
public const string ActivityDetails = "ActivityDetails";
public const string Time = "Time";
public const string SoftwareName = "Name";
public const string SoftwareVersion = "Version";
public const string SoftwareUiTheme = "UiTheme";
public const string SoftwareType = nameof(SoftwareType);
public const string WebFramework = nameof(WebFramework);
public const string Dbms = nameof(Dbms);
public const string UiTheme = nameof(UiTheme);
public const string UiThemeStyle = nameof(UiThemeStyle);
public const string MobileApp = nameof(MobileApp);
public const string SampleCrudPage = nameof(SampleCrudPage);
public const string FirstAbpVersion = nameof(FirstAbpVersion);
public const string FirstDotnetVersion = nameof(FirstDotnetVersion);
public const string CreationTool = nameof(CreationTool);
public const string ModuleName = nameof(ModuleName);
public const string ModuleVersion = nameof(ModuleVersion);
public const string ModuleInstallationTime = nameof(ModuleInstallationTime);
public const string ExtraProperties = nameof(ExtraProperties);
public const string HasSolutionInfo = nameof(HasSolutionInfo);
public const string HasDeviceInfo = nameof(HasDeviceInfo);
public const string HasProjectInfo = nameof(HasProjectInfo);
public const string ErrorMessage = nameof(ErrorMessage);
public const string FailingActivity = nameof(FailingActivity);
public const string SoftwareType = "SoftwareType";
public const string WebFramework = "WebFramework";
public const string Dbms = "Dbms";
public const string UiTheme = "UiTheme";
public const string UiThemeStyle = "UiThemeStyle";
public const string MobileApp = "MobileApp";
public const string SampleCrudPage = "SampleCrudPage";
public const string FirstAbpVersion = "FirstAbpVersion";
public const string FirstDotnetVersion = "FirstDotnetVersion";
public const string CreationTool = "CreationTool";
public const string ModuleName = "ModuleName";
public const string ModuleVersion = "ModuleVersion";
public const string ModuleInstallationTime = "ModuleInstallationTime";
public const string ExtraProperties = "ExtraProperties";
public const string HasSolutionInfo = "HasSolutionInfo";
public const string HasDeviceInfo = "HasDeviceInfo";
public const string HasProjectInfo = "HasProjectInfo";
public const string ErrorMessage = "ErrorMessage";
public const string FailingActivity = "FailingActivity";
public const string OperatingSystemArchitecture = "OperatingSystemArchitecture";
}

286
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/DeviceKeyHelper.cs

@ -0,0 +1,286 @@
namespace Volo.Abp.Telemetry.Constants;
public class DeviceKeyHelper
{
public static string GetUniquePhysicalKey(bool shouldHash)
{
char platformId = '?';
char osArchitecture = '?';
string operatingSystem = "?";
try
{
string osPrefix;
string uniqueKey;
platformId = GetPlatformIdOrDefault();
osArchitecture = GetOsArchitectureOrDefault();
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform
.Windows))
{
operatingSystem = "Windows";
uniqueKey = GetUniqueKeyForWindows();
osPrefix = "W";
}
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices
.OSPlatform.Linux))
{
operatingSystem = "Linux";
uniqueKey = GetHarddiskSerialForLinux();
osPrefix = "L";
}
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices
.OSPlatform.OSX)) //MAC
{
operatingSystem = "OSX";
uniqueKey = GetHarddiskSerialForOsX();
osPrefix = "O";
}
else
{
operatingSystem = "Other";
uniqueKey = GetNetworkAdapterSerial();
osPrefix = "X";
}
if (shouldHash)
{
uniqueKey = ConvertToMd5(uniqueKey).ToUpperInvariant();
}
return osPrefix + platformId + osArchitecture + "-" + uniqueKey;
}
catch (System.Exception ex)
{
System.Console.WriteLine("WARNING ABP-LIC-0025! Contact to license@abp.io with the below information" +
System.Environment.NewLine +
"* Architecture: " +
System.Runtime.InteropServices.RuntimeInformation.OSArchitecture +
System.Environment.NewLine +
"* Description: " +
System.Runtime.InteropServices.RuntimeInformation.OSDescription +
System.Environment.NewLine +
"* Platform Id: " + platformId + System.Environment.NewLine +
"* OS architecture: " + osArchitecture + System.Environment.NewLine +
"* Operating system: " + operatingSystem + System.Environment.NewLine +
"* Framework description: " +
System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription +
System.Environment.NewLine +
"* Error: " + ex.ToString());
return "95929008-b147-454a-8737-efed71fa2241";
}
}
private static string GetNetworkAdapterSerial()
{
string macAddress = string.Empty;
var networkInterfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
foreach (var networkInterface in networkInterfaces)
{
if (networkInterface.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Loopback)
{
continue;
}
var physicalAddress = networkInterface.GetPhysicalAddress().ToString();
if (string.IsNullOrEmpty(physicalAddress))
{
continue;
}
macAddress = physicalAddress;
break;
}
return macAddress!;
}
/// <returns>
/// 0 - Win32S
/// 1 - Win32Windows
/// 2 - Win32NT
/// 3 - WinCE
/// 4 - Unix
/// 5 - Xbox
/// 6 - MacOSX
/// </returns>
private static char GetPlatformIdOrDefault(char defaultValue = '*')
{
try
{
return ((int)System.Environment.OSVersion.Platform).ToString()[0];
}
catch
{
return defaultValue;
}
}
private static string ConvertToMd5(string text)
{
using (var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider())
{
return EncodeBase64(md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(text)));
}
}
private static string EncodeBase64(byte[] ba)
{
var hex = new System.Text.StringBuilder(ba.Length * 2);
foreach (var b in ba)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}
/// <returns>
/// 0 - X86 (An Intel-based 32-bit processor architecture)
/// 1 - X64 (A 64-bit ARM processor architecture)
/// 2 - Arm (A 32-bit ARM processor architecture)
/// 3 - Arm64 (A 64-bit ARM processor architecture)
/// </returns>
private static char GetOsArchitectureOrDefault(char defaultValue = '*')
{
try
{
return ((int)System.Runtime.InteropServices.RuntimeInformation.OSArchitecture).ToString()[0];
}
catch
{
return defaultValue;
}
}
private static string GetUniqueKeyForWindows()
{
try
{
return GetProcessorIdForWindows();
}
catch
{
//couldn't get processor id when logon user has no required permission. try other methods...
}
return GetWindowsMachineUniqueId();
}
private static string GetProcessorIdForWindows()
{
using (var managementObjectSearcher =
new System.Management.ManagementObjectSearcher("SELECT ProcessorId FROM Win32_Processor"))
{
using (var searcherObj = managementObjectSearcher.Get())
{
if (searcherObj.Count == 0)
{
throw new System.Exception("No unique computer ID found for this computer!");
}
var managementObjectEnumerator = searcherObj.GetEnumerator();
managementObjectEnumerator.MoveNext();
return managementObjectEnumerator.Current.GetPropertyValue("ProcessorId").ToString()!;
}
}
}
private static string GetWindowsMachineUniqueId()
{
return RunCommandAndGetOutput("powershell (Get-CimInstance -Class Win32_ComputerSystemProduct).UUID");
}
private static string GetHarddiskSerialForLinux()
{
return RunCommandAndGetOutput(
"udevadm info --query=all --name=/dev/sda | grep ID_SERIAL_SHORT | tr -d \"ID_SERIAL_SHORT=:\"");
}
private static string GetHarddiskSerialForOsX()
{
var command =
"ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { split($0, line, \"\\\"\"); printf(\"%s\\n\", line[4]); }'";
command = System.Text.RegularExpressions.Regex.Replace(command, @"(\\*)" + "\"", @"$1$1\" + "\"");
return RunCommandAndGetOutput(command);
}
private static string RunCommandAndGetOutput(string command)
{
var output = "";
using (var process = new System.Diagnostics.Process())
{
process.StartInfo = new System.Diagnostics.ProcessStartInfo(GetFileName())
{
Arguments = GetArguments(command),
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
process.Start();
process?.WaitForExit();
using (var stdOut = process!.StandardOutput)
{
using (var stdErr = process.StandardError)
{
output = stdOut.ReadToEnd();
output += stdErr.ReadToEnd();
}
}
}
return output.Trim();
}
private static string GetFileName()
{
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(
System.Runtime.InteropServices.OSPlatform.OSX) ||
System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform
.Linux))
{
string[] fileNames = { "/bin/bash", "/usr/bin/bash", "/bin/sh", "/usr/bin/sh" };
foreach (var fileName in fileNames)
{
try
{
if (System.IO.File.Exists(fileName))
{
return fileName;
}
}
catch
{
//ignore
}
}
return "/bin/bash";
}
//Windows default.
return "cmd.exe";
}
private static string GetArguments(string command)
{
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(
System.Runtime.InteropServices.OSPlatform.OSX) ||
System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform
.Linux))
{
return "-c \"" + command + "\"";
}
//Windows default.
return "/C \"" + command + "\"";
}
}

6
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryConsts.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.Telemetry.Constants;
public class TelemetryConsts
{
public const string VoloNameSpaceFilter = "Volo";
}

23
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs

@ -12,6 +12,7 @@ namespace Volo.Abp.Telemetry.EnvironmentInspection.Detectors;
internal class AbpStudioDetector : SoftwareDetector
{
public override string Name => "Abp Studio";
private const string AbpStudioVersionExtensionName = "Volo.Abp.Studio.Extensions.StandardSolutionTemplates";
public override Task<SoftwareInfo?> DetectAsync()
{
@ -39,9 +40,8 @@ internal class AbpStudioDetector : SoftwareDetector
{
return null;
}
var json = File.ReadAllText(ideStateJsonPath);
using var doc = JsonDocument.Parse(json);
using var fs = new FileStream(ideStateJsonPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var doc = JsonDocument.Parse(fs);
return doc.RootElement.TryGetProperty("theme", out var themeElement) ? themeElement.GetString() : null;
}
@ -55,17 +55,20 @@ internal class AbpStudioDetector : SoftwareDetector
return null;
}
var json = File.ReadAllText(extensionsFilePath);
using var doc = JsonDocument.Parse(json);
using var fs = new FileStream(extensionsFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var doc = JsonDocument.Parse(fs);
if (doc.RootElement.TryGetProperty("Extensions", out var extensionsElement) &&
extensionsElement.ValueKind == JsonValueKind.Array &&
extensionsElement.GetArrayLength() > 0)
extensionsElement.ValueKind == JsonValueKind.Array)
{
var firstExtension = extensionsElement[0];
if (firstExtension.TryGetProperty("version", out var versionElement))
foreach (var extension in extensionsElement.EnumerateArray())
{
return versionElement.GetString();
if (extension.TryGetProperty("name", out var nameProp) &&
nameProp.GetString() == AbpStudioVersionExtensionName &&
extension.TryGetProperty("version", out var versionProp))
{
return versionProp.GetString();
}
}
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryApplicationInfoContributor.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
using Volo.Abp.Telemetry.Activity;
namespace Volo.Abp.Telemetry;
public interface ITelemetryApplicationInfoContributor
{
Task ContributeAsync(ActivityEvent activityEvent);
}

11
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryService.cs

@ -1,19 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Telemetry.Activity;
namespace Volo.Abp.Telemetry;
public interface ITelemetryService
{
IAsyncDisposable TrackActivity(string activityName, Action<ActivityEvent>? configure = null);
IAsyncDisposable TrackActivity(ActivityEvent activityEvent);
Task AddActivityAsync(ActivityEvent @event);
Task AddActivityAsync(string activityName, string? details = null);
Task AddActivityAsync(string activityName, Action<ActivityEvent> configure);
Task AddErrorActivityAsync(Action<Dictionary<string, object>> configure);
IAsyncDisposable TrackActivityAsync(string activityName, Action<Dictionary<string,object>>? additionalProperties = null);
Task AddActivityAsync(string activityName, Action<Dictionary<string,object>>? additionalProperties = null);
Task AddErrorActivityAsync(Action<Dictionary<string, object>> additionalProperties);
Task AddErrorActivityAsync(string errorMessage);
Task AddErrorForActivityAsync(string failingActivity, string errorMessage);
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetrySessionProvider.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
using Volo.Abp.Telemetry.Activity;
namespace Volo.Abp.Telemetry;
public interface ITelemetrySessionProvider
{
Task AddSessionInfoAsync(ActivityEvent activity);
}

29
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryModuleInfoContributor.cs

@ -1,29 +0,0 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Constants;
namespace Volo.Abp.Telemetry;
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))]
public class TelemetryModuleInfoContributor : ITelemetryApplicationInfoContributor, ISingletonDependency
{
public Task ContributeAsync(ActivityEvent activityEvent)
{
if (activityEvent.TryGetValue(ActivityPropertyNames.Assembly, out var assemblyPath))
{
var assembly = Assembly.LoadFrom((string)assemblyPath);
var types = assembly.GetTypes();
var moduleCount = types.Count(t => typeof(AbpModule).IsAssignableFrom(t) && !t.IsAbstract);
var projectCount = types.Select(t => t.Assembly.GetName().Name).Distinct().Count();
activityEvent[ActivityPropertyNames.ModuleCount] = moduleCount;
activityEvent[ActivityPropertyNames.ProjectCount] = projectCount;
}
return Task.CompletedTask;
}
}

21
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryRuntimeSessionProvider.cs

@ -1,21 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Constants;
using Volo.Abp.Telemetry.Constants.Enums;
namespace Volo.Abp.Telemetry;
[ExposeServices(typeof(ITelemetrySessionProvider))]
public class TelemetryRuntimeSessionProvider : ITelemetrySessionProvider, ISingletonDependency
{
public Task AddSessionInfoAsync(ActivityEvent activity)
{
activity[ActivityPropertyNames.SessionType] = SessionType.ApplicationRuntime;
activity[ActivityPropertyNames.SessionId] = Guid.NewGuid();
activity[ActivityPropertyNames.IsFirstSession] = !File.Exists(TelemetryPaths.ActivityStorage);
return Task.CompletedTask;
}
}

123
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryService.cs

@ -5,11 +5,11 @@ using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Constants;
using ActivityEvent = Volo.Abp.Telemetry.Activity.ActivityEvent;
using ActivityContext = Volo.Abp.Telemetry.Activity.ActivityContext;
namespace Volo.Abp.Telemetry;
public class TelemetryService : ITelemetryService, ISingletonDependency
public class TelemetryService : ITelemetryService, IScopedDependency
{
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
private readonly ITelemetryActivitySender _telemetryActivitySender;
@ -24,115 +24,70 @@ public class TelemetryService : ITelemetryService, ISingletonDependency
_telemetryActivityEventBuilder = telemetryActivityEventBuilder;
}
public IAsyncDisposable TrackActivity(string activityName, Action<ActivityEvent>? configure = null)
{
Check.NotNullOrEmpty(activityName, nameof(activityName));
var stopwatch = Stopwatch.StartNew();
var activityData = new ActivityEvent(activityName);
configure?.Invoke(activityData);
return new AsyncDisposeFunc(async () =>
{
stopwatch.Stop();
activityData.ActivityDuration = stopwatch.ElapsedMilliseconds;
await AddActivityAsync(activityData);
});
}
public IAsyncDisposable TrackActivity(ActivityEvent activityEvent)
public IAsyncDisposable TrackActivityAsync(string activityName,
Action<Dictionary<string, object>>? additionalProperties = null)
{
Check.NotNullOrEmpty(activityName, nameof(activityName));
var stopwatch = Stopwatch.StartNew();
var context = ActivityContext.Create(activityName);
return new AsyncDisposeFunc(async () =>
{
stopwatch.Stop();
activityEvent.ActivityDuration = stopwatch.ElapsedMilliseconds;
await AddActivityAsync(activityEvent);
context.Current[ActivityPropertyNames.ActivityDuration] = stopwatch.ElapsedMilliseconds;
additionalProperties?.Invoke(context.Current);
await AddActivityAsync(context);
});
}
public Task AddActivityAsync(ActivityEvent @event)
public async Task AddActivityAsync(string activityName,
Action<Dictionary<string, object>>? additionalProperties = null)
{
_ = Task.Run(async () =>
{
try
{
await _telemetryActivityEventBuilder.BuildAsync(@event);
await _telemetryActivityStorage.BufferActivityAsync(@event);
if (@event.ActivityName == ActivityNameConsts.AbpStudioClose)
{
await _telemetryActivityStorage.EndSessionAsync();
}
if (await _telemetryActivityStorage.ShouldSendActivitiesAsync())
{
await _telemetryActivitySender.SendAsync();
}
}
catch
{
// ignored
}
});
return Task.CompletedTask;
Check.NotNullOrEmpty(activityName, nameof(activityName));
var context = ActivityContext.Create(activityName, additionalProperties: additionalProperties);
await AddActivityAsync(context);
}
public async Task AddActivityAsync(string activityName, string? details = null)
public async Task AddErrorActivityAsync(Action<Dictionary<string, object>> additionalProperties)
{
await AddActivityAsync(new ActivityEvent(activityName, details));
var context = ActivityContext.Create(ActivityNameConsts.Error, additionalProperties: additionalProperties);
await AddActivityAsync(context);
}
public async Task AddActivityAsync(string activityName, Action<ActivityEvent> configure)
public async Task AddErrorActivityAsync(string errorMessage)
{
Check.NotNullOrEmpty(activityName, nameof(activityName));
var activityData = new ActivityEvent(activityName);
configure?.Invoke(activityData);
await AddActivityAsync(activityData);
var context = ActivityContext.Create(ActivityNameConsts.Error, errorMessage);
await AddActivityAsync(context);
}
public async Task AddErrorActivityAsync(Action<Dictionary<string, object>> configure)
public async Task AddErrorForActivityAsync(string failingActivity, string errorMessage)
{
var activityData = new ActivityEvent(ActivityNameConsts.Error)
Check.NotNullOrEmpty(failingActivity, nameof(failingActivity));
var context = ActivityContext.Create(ActivityNameConsts.Error, errorMessage, configure =>
{
AdditionalProperties = new Dictionary<string, object>()
};
configure?.Invoke(activityData.AdditionalProperties);
await AddActivityAsync(activityData);
configure[ActivityPropertyNames.FailingActivity] = failingActivity;
});
await AddActivityAsync(context);
}
public async Task AddErrorActivityAsync(string errorMessage)
private async Task AddActivityAsync(ActivityContext context)
{
var activityData = new ActivityEvent(ActivityNameConsts.Error)
try
{
AdditionalProperties = new Dictionary<string, object>
var activityEvent = await _telemetryActivityEventBuilder.BuildAsync(context);
if (activityEvent is not null)
{
{ ActivityPropertyNames.ErrorMessage, errorMessage },
await _telemetryActivityStorage.BufferActivityAsync(activityEvent);
if (await _telemetryActivityStorage.ShouldSendActivitiesAsync())
{
await _telemetryActivitySender.SendAsync();
}
}
};
await AddActivityAsync(activityData);
}
public async Task AddErrorForActivityAsync(string failingActivity, string errorMessage)
{
var activityData = new ActivityEvent(ActivityNameConsts.Error)
}
catch (Exception ex)
{
AdditionalProperties = new Dictionary<string, object>
{
{ ActivityPropertyNames.ErrorMessage, errorMessage },
{ ActivityPropertyNames.FailingActivity, failingActivity },
}
};
await AddActivityAsync(activityData);
//ignored
}
}
}

28
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoContributor.cs

@ -1,28 +0,0 @@
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Telemetry;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Constants;
namespace Volo.Abp.Domain.Telemetry;
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))]
public class TelemetryDomainInfoContributor : ITelemetryApplicationInfoContributor , ISingletonDependency
{
public Task ContributeAsync(ActivityEvent activityEvent)
{
if (activityEvent.TryGetValue(ActivityPropertyNames.Assembly, out var assemblyPath))
{
var assembly = Assembly.LoadFrom((string)assemblyPath);
var entityCount = assembly.GetTypes().Count(t => typeof(IEntity).IsAssignableFrom(t) && !t.IsAbstract);
activityEvent[ActivityPropertyNames.EntityCount] = entityCount;
}
return Task.CompletedTask;
}
}

42
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoEnricher.cs

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Reflection;
using Volo.Abp.Telemetry.Activity;
using Volo.Abp.Telemetry.Activity.Contracts;
using Volo.Abp.Telemetry.Activity.Providers;
using Volo.Abp.Telemetry.Constants;
namespace Volo.Abp.Domain.Telemetry;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetryDomainInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
private readonly ITypeFinder _typeFinder;
public TelemetryDomainInfoEnricher(ITypeFinder typeFinder)
{
_typeFinder = typeFinder;
}
public bool IsFirstRun => false;
public Type? DependsOn => typeof(TelemetryApplicationInfoEnricher);
public Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.ProjectId.HasValue);
}
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context)
{
var entityCount = _typeFinder.Types.Count(t =>
typeof(IEntity).IsAssignableFrom(t) && !t.IsAbstract &&
!t.AssemblyQualifiedName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter));
var result = new Dictionary<string, object>() { { ActivityPropertyNames.EntityCount, entityCount } };
return Task.FromResult<Dictionary<string, object>?>(result);
}
}
Loading…
Cancel
Save