From e088032267ddabf68b5a4a10f815fb198e67c7bb Mon Sep 17 00:00:00 2001 From: Emre KARA Date: Mon, 16 Jun 2025 22:34:10 +0300 Subject: [PATCH] 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. --- .../TelemetryApplicationMetricsContributor.cs | 35 --- .../TelemetryApplicationMetricsEnricher.cs | 53 ++++ .../TelemetryPermissionInfoContributor.cs | 24 -- .../TelemetryPermissionInfoEnricher.cs | 41 +++ .../Volo/Abp/AbpApplicationBase.cs | 22 +- .../Abp/Telemetry/Activity/ActivityContext.cs | 102 +++++++ .../Abp/Telemetry/Activity/ActivityEvent.cs | 2 +- .../Activity/ActivityEventExtensions.cs | 16 +- .../ITelemetryActivityEventBuilder.cs | 2 +- .../ITelemetryActivityEventEnricher.cs | 10 +- .../Contracts/ITelemetryActivityStorage.cs | 4 - .../Activity/Providers/EnricherNode.cs | 15 + .../TelemetryActivityEventBuilder.cs | 95 +++++- .../TelemetryApplicationInfoEnricher.cs | 94 ++++-- .../Providers/TelemetryDeviceInfoEnricher.cs | 83 +++-- .../Providers/TelemetryModuleInfoEnricher.cs | 43 +++ .../Providers/TelemetrySessionInfoEnricher.cs | 32 ++ .../TelemetrySolutionInfoEnricher.cs | 126 +++----- .../Storage/TelemetryActivityStorage.cs | 88 ++---- .../Storage/TelemetryActivityStorageState.cs | 1 - .../Constants/ActivityPropertyNames.cs | 137 +++++---- .../Telemetry/Constants/DeviceKeyHelper.cs | 286 ++++++++++++++++++ .../Telemetry/Constants/TelemetryConsts.cs | 6 + .../Detectors/AbpStudioDetector.cs | 23 +- .../ITelemetryApplicationInfoContributor.cs | 9 - .../Volo/Abp/Telemetry/ITelemetryService.cs | 11 +- .../Telemetry/ITelemetrySessionProvider.cs | 9 - .../TelemetryModuleInfoContributor.cs | 29 -- .../TelemetryRuntimeSessionProvider.cs | 21 -- .../Volo/Abp/Telemetry/TelemetryService.cs | 123 +++----- .../TelemetryDomainInfoContributor.cs | 28 -- .../Telemetry/TelemetryDomainInfoEnricher.cs | 42 +++ 32 files changed, 1017 insertions(+), 595 deletions(-) delete mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsContributor.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsEnricher.cs delete mode 100644 framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoContributor.cs create mode 100644 framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoEnricher.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityContext.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/EnricherNode.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryModuleInfoEnricher.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySessionInfoEnricher.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/DeviceKeyHelper.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryConsts.cs delete mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryApplicationInfoContributor.cs delete mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetrySessionProvider.cs delete mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryModuleInfoContributor.cs delete mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryRuntimeSessionProvider.cs delete mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoContributor.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoEnricher.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsContributor.cs deleted file mode 100644 index 234f746316..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsContributor.cs +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsEnricher.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsEnricher.cs new file mode 100644 index 0000000000..d151edcbc1 --- /dev/null +++ b/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 CanExecuteAsync(ActivityContext context) + { + return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime); + } + + public Task?> 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() + { + {ActivityPropertyNames.AppServiceCount, appServiceCount}, + {ActivityPropertyNames.ControllerCount, controllerCount} + }; + return Task.FromResult?>(items); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoContributor.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoContributor.cs deleted file mode 100644 index c86ebadbb4..0000000000 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoContributor.cs +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoEnricher.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoEnricher.cs new file mode 100644 index 0000000000..42166715c3 --- /dev/null +++ b/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 CanExecuteAsync(ActivityContext context) + { + return Task.FromResult(context.ProjectId.HasValue); + } + + public async Task?> EnrichAsync(ActivityContext context) + { + var permissions = await _permissionDefinitionManager.GetPermissionsAsync(); + + var result = new Dictionary + { + { ActivityPropertyNames.PermissionCount, permissions.Count } + }; + + return result; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs index 18d8208e51..79edf82aba 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs +++ b/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(); - - 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(); + await telemetryService.AddActivityAsync(ActivityNameConsts.ApplicationRun); } catch (Exception ex) { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityContext.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityContext.cs new file mode 100644 index 0000000000..76c89f4b42 --- /dev/null +++ b/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 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>? additionalProperties = null) + { + var activity = new ActivityEvent(activityName, details); + if (additionalProperties is not null) + { + activity.AdditionalProperties = new Dictionary(); + 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(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; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEvent.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEvent.cs index cd803272f9..42a8d0be66 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEvent.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEvent.cs @@ -5,7 +5,7 @@ namespace Volo.Abp.Telemetry.Activity; public class ActivityEvent : Dictionary { - private ActivityEvent() + public ActivityEvent() { } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEventExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEventExtensions.cs index b70487c5ff..7991e47d3f 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/ActivityEventExtensions.cs +++ b/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 properties) + public static ActivityEvent WithAdditionalProperties(this ActivityEvent activityEvent, + Dictionary properties) { activityEvent.AdditionalProperties ??= new Dictionary(); 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(); 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(); - + 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; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventBuilder.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventBuilder.cs index d9f9f2f8b6..3468767740 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventBuilder.cs +++ b/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 BuildAsync(ActivityContext context); } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventEnricher.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventEnricher.cs index 9cd7afcc42..1b863e7f84 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityEventEnricher.cs +++ b/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 CanExecuteAsync(ActivityContext context); + Task?> EnrichAsync(ActivityContext context); + } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs index b0aafefd34..be7407e490 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs @@ -8,12 +8,8 @@ public interface ITelemetryActivityStorage { Task InitializeOrGetSessionAsync(); Task MarkActivitiesAsSentAsync(); - Task MarkDeviceInfoAsAddedAsync(); - Task MarkSolutionInfoAsAddedAsync(Guid solutionId); - Task MarkApplicationInfoAsAddedAsync(Guid applicationInfo); Task BufferActivityAsync(ActivityEvent activityEvent); Task> GetBufferedActivitiesAsync(); - Task EndSessionAsync(); Task ShouldAddDeviceInfoAsync(); Task ShouldAddSolutionInformation(Guid solutionId); Task ShouldAddProjectInfoAsync(Guid projectId); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/EnricherNode.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/EnricherNode.cs new file mode 100644 index 0000000000..ca12b5d2fb --- /dev/null +++ b/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 Children { get; set; } = new(); + + public EnricherNode(ITelemetryActivityEventEnricher enricher) + { + Enricher = enricher; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs index 124c3b55db..ea35098dda 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs +++ b/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 _activityDataEnrichers; - public TelemetryActivityEventBuilder(IServiceProvider serviceProvider) + public TelemetryActivityEventBuilder(IEnumerable activityDataEnrichers) { - _serviceProvider = serviceProvider; + _activityDataEnrichers = activityDataEnrichers; } - public virtual async Task BuildAsync(ActivityEvent activity) + public virtual async Task BuildAsync(ActivityContext context) { - - var sessionProvider = _serviceProvider.GetRequiredService(); + var enricherNodes = BuildTree(_activityDataEnrichers); + await ExecuteEnrichersAsync(enricherNodes, context); - await sessionProvider.AddSessionInfoAsync(activity); - - var activityDataEnrichers = _serviceProvider.GetRequiredService>(); + if (context.IsTerminated) + { + return null; + } + return context.Current; + } + + private async Task ExecuteEnrichersAsync( + IEnumerable nodes, + ActivityContext context) + { + async Task ExecuteEnricherChainAsync(IEnumerable 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 BuildTree(IEnumerable 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; } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryApplicationInfoEnricher.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryApplicationInfoEnricher.cs index 56a6dff37b..e598050775 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryApplicationInfoEnricher.cs +++ b/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 _telemetryApplicationInfoContributors; private readonly ITelemetryActivityStorage _telemetryActivityStorage; public TelemetryApplicationInfoEnricher( - IEnumerable telemetryApplicationInfoContributors, ITelemetryActivityStorage telemetryActivityStorage) { - _telemetryApplicationInfoContributors = telemetryApplicationInfoContributors; _telemetryActivityStorage = telemetryActivityStorage; } - public async Task EnrichAsync(ActivityEvent activity) + public bool IsFirstRun => true; + public Type DependsOn => typeof(TelemetrySessionInfoEnricher); + + public Task CanExecuteAsync(ActivityContext context) + { + return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime); + } + + public async Task?> 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 + { + { 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(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; } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryDeviceInfoEnricher.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryDeviceInfoEnricher.cs index ca4fd4655c..1150f447ce 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryDeviceInfoEnricher.cs +++ b/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 CanExecuteAsync(ActivityContext context) + { + return Task.FromResult(true); + } + + + public async Task?> EnrichAsync(ActivityContext context) { try { + var deviceId = DeviceKeyHelper.GetUniquePhysicalKey(true); + if (!await _telemetryActivityStorage.ShouldAddDeviceInfoAsync()) { - return; + context.Cancel(); + return new Dictionary + { + { ActivityPropertyNames.DeviceId, deviceId }, + }; } - - EnrichWithDeviceInfo(activity); - await EnrichWithSoftwareInfoAsync(activity); - activity[ActivityPropertyNames.HasDeviceInfo] = true; - await _telemetryActivityStorage.MarkDeviceInfoAsAddedAsync(); + + var result = new Dictionary + { + [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 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(); - 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() { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryModuleInfoEnricher.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetryModuleInfoEnricher.cs new file mode 100644 index 0000000000..694f187875 --- /dev/null +++ b/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 CanExecuteAsync(ActivityContext context) + { + return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime); + } + + public Task?> EnrichAsync(ActivityContext context) + { + var result = new Dictionary + { + { ActivityPropertyNames.ModuleCount, _moduleContainer.Modules.Count }, + { ActivityPropertyNames.ProjectCount, _assemblyFinder.Assemblies.Count(x => !x.FullName.IsNullOrEmpty() && !x.FullName.StartsWith(TelemetryConsts.VoloNameSpaceFilter)) } + }; + + return Task.FromResult?>(result); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySessionInfoEnricher.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySessionInfoEnricher.cs new file mode 100644 index 0000000000..445a21c0e9 --- /dev/null +++ b/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 CanExecuteAsync(ActivityContext context) + { + return Task.FromResult(true); + } + + public Task?> EnrichAsync(ActivityContext context) + { + var result = new Dictionary + { + { ActivityPropertyNames.SessionType, SessionType.ApplicationRuntime }, + { ActivityPropertyNames.SessionId, Guid.NewGuid().ToString() }, + { ActivityPropertyNames.IsFirstSession, !File.Exists(TelemetryPaths.ActivityStorage) } + }; + + return Task.FromResult?>(result); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs index 18c5a931e4..34ec401e61 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs +++ b/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 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?> EnrichAsync(ActivityContext context) { try { - var solutionPath = activity[ActivityPropertyNames.SolutionPath]!.ToString()!; + var result = new Dictionary(); - 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?>(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?>(null); } - - solutionId = Guid.Empty; - return false; } - private void AddSolutionCreationConfiguration(ActivityEvent activity, JsonElement config) + + + private void AddSolutionCreationConfiguration(Dictionary 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> ExtractModules(string solutionPath, JsonElement modulesElement) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs index 0aef49acaf..747952154a 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs +++ b/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 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 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 WithExclusiveFileLockAsync(Func> action) { return await MutexExecutor.ExecuteAsync(MutexName, async () => { - using var stream = new FileStream( + using var stream = new FileStream( TelemetryPaths.ActivityStorage, FileMode.OpenOrCreate, FileAccess.ReadWrite, diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorageState.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorageState.cs index 8966be0c84..99ab3ff876 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorageState.cs +++ b/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 Activities { get; set; } = new(); public Dictionary Solutions { get; set; } = new(); - public Dictionary Projects { get; set; } = new(); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/ActivityPropertyNames.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/ActivityPropertyNames.cs index 5d93049f3b..37c75479ee 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/ActivityPropertyNames.cs +++ b/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"; } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/DeviceKeyHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/DeviceKeyHelper.cs new file mode 100644 index 0000000000..d49005df09 --- /dev/null +++ b/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!; + } + /// + /// 0 - Win32S + /// 1 - Win32Windows + /// 2 - Win32NT + /// 3 - WinCE + /// 4 - Unix + /// 5 - Xbox + /// 6 - MacOSX + /// + 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(); + } + + /// + /// 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) + /// + 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 + "\""; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryConsts.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryConsts.cs new file mode 100644 index 0000000000..42141929f5 --- /dev/null +++ b/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"; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs index e7a6620b2c..61a910cfe1 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs +++ b/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 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(); + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryApplicationInfoContributor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryApplicationInfoContributor.cs deleted file mode 100644 index a4b326b096..0000000000 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryApplicationInfoContributor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; -using Volo.Abp.Telemetry.Activity; - -namespace Volo.Abp.Telemetry; - -public interface ITelemetryApplicationInfoContributor -{ - Task ContributeAsync(ActivityEvent activityEvent); -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryService.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryService.cs index 63718bbc62..4ff908cc6c 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetryService.cs +++ b/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? configure = null); - IAsyncDisposable TrackActivity(ActivityEvent activityEvent); - Task AddActivityAsync(ActivityEvent @event); - Task AddActivityAsync(string activityName, string? details = null); - Task AddActivityAsync(string activityName, Action configure); - Task AddErrorActivityAsync(Action> configure); + IAsyncDisposable TrackActivityAsync(string activityName, Action>? additionalProperties = null); + Task AddActivityAsync(string activityName, Action>? additionalProperties = null); + Task AddErrorActivityAsync(Action> additionalProperties); Task AddErrorActivityAsync(string errorMessage); Task AddErrorForActivityAsync(string failingActivity, string errorMessage); } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetrySessionProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetrySessionProvider.cs deleted file mode 100644 index 7055b6bc6b..0000000000 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/ITelemetrySessionProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; -using Volo.Abp.Telemetry.Activity; - -namespace Volo.Abp.Telemetry; - -public interface ITelemetrySessionProvider -{ - Task AddSessionInfoAsync(ActivityEvent activity); -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryModuleInfoContributor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryModuleInfoContributor.cs deleted file mode 100644 index 7dc1175365..0000000000 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryModuleInfoContributor.cs +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryRuntimeSessionProvider.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryRuntimeSessionProvider.cs deleted file mode 100644 index 78c644789e..0000000000 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryRuntimeSessionProvider.cs +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryService.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryService.cs index c5c5da95ba..8dd6912374 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryService.cs +++ b/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? 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>? 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>? 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> 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 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> 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() - }; - - 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 + 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 - { - { ActivityPropertyNames.ErrorMessage, errorMessage }, - { ActivityPropertyNames.FailingActivity, failingActivity }, - } - }; - - await AddActivityAsync(activityData); + //ignored + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoContributor.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoContributor.cs deleted file mode 100644 index c863458cdf..0000000000 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoContributor.cs +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoEnricher.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoEnricher.cs new file mode 100644 index 0000000000..0b1abf0b95 --- /dev/null +++ b/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 CanExecuteAsync(ActivityContext context) + { + return Task.FromResult(context.ProjectId.HasValue); + } + + public Task?> EnrichAsync(ActivityContext context) + { + var entityCount = _typeFinder.Types.Count(t => + typeof(IEntity).IsAssignableFrom(t) && !t.IsAbstract && + !t.AssemblyQualifiedName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter)); + + + var result = new Dictionary() { { ActivityPropertyNames.EntityCount, entityCount } }; + return Task.FromResult?>(result); + } +} \ No newline at end of file