Browse Source

Merge pull request #23625 from abpframework/auto-merge/rel-9-3/3961

Merge branch dev with rel-9.3
pull/23626/head
Ma Liming 5 months ago
committed by GitHub
parent
commit
339512d199
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      Directory.Packages.props
  2. 47
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/TelemetryApplicationMetricsEnricher.cs
  3. 20
      framework/src/Volo.Abp.Authorization.Abstractions/Properties/AssemblyInfo.cs
  4. 2
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs
  5. 18
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs
  6. 2
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs
  7. 5
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs
  8. 45
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/TelemetryPermissionInfoEnricher.cs
  9. 1
      framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj
  10. 54
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  11. 4
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs
  12. 3
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs
  13. 50
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/ActivityContext.cs
  14. 147
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/ActivityEvent.cs
  15. 7
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Contracts/IHasParentTelemetryActivityEventEnricher.cs
  16. 8
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Contracts/ITelemetryActivityEventBuilder.cs
  17. 11
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Contracts/ITelemetryActivityEventEnricher.cs
  18. 17
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs
  19. 49
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventBuilder.cs
  20. 70
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventEnricher.cs
  21. 104
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryApplicationInfoEnricher.cs
  22. 79
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryDeviceInfoEnricher.cs
  23. 39
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryModuleInfoEnricher.cs
  24. 27
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetrySessionInfoEnricher.cs
  25. 130
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetrySolutionInfoEnricher.cs
  26. 18
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Storage/FailedActivityInfo.cs
  27. 207
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Storage/TelemetryActivityStorage.cs
  28. 16
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Storage/TelemetryActivityStorageState.cs
  29. 34
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Storage/TelemetryPeriod.cs
  30. 34
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/TelemetryJsonExtensions.cs
  31. 12
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/AbpPlatformUrls.cs
  32. 76
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/ActivityNameConsts.cs
  33. 77
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/ActivityPropertyNames.cs
  34. 260
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/DeviceManager.cs
  35. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/AbpTool.cs
  36. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/OperationSystem.cs
  37. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/SessionType.cs
  38. 11
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/SoftwareType.cs
  39. 6
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/TelemetryConsts.cs
  40. 13
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/TelemetryPaths.cs
  41. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Contracts/ISoftwareDetector.cs
  42. 9
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Contracts/ISoftwareInfoProvider.cs
  43. 11
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Contracts/SoftwareInfo.cs
  44. 78
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Core/SoftwareDetector.cs
  45. 76
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs
  46. 48
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/ChromeDetector.cs
  47. 17
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/DotnetSdkDetector.cs
  48. 45
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/FireFoxDetector.cs
  49. 47
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/MsEdgeDetector.cs
  50. 33
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/NodeJsDetector.cs
  51. 77
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/OperatingSystemDetector.cs
  52. 100
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/RiderDetector.cs
  53. 132
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/VisualStudioCodeDetector.cs
  54. 106
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/VisualStudioDetector.cs
  55. 40
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Providers/SoftwareInfoProvider.cs
  56. 135
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/AbpPackageMetadataReader.cs
  57. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/AbpProjectMetaData.cs
  58. 42
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/Cryptography.cs
  59. 46
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/MutexExecutor.cs
  60. 8
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/ITelemetryActivitySender.cs
  61. 13
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/ITelemetryService.cs
  62. 131
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryActivitySender.cs
  63. 101
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs
  64. 41
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Telemetry/TelemetryDomainInfoEnricher.cs
  65. 6
      framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/AuthorizationTestPermissionDefinitionProvider.cs
  66. 89
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Telemetry/ActivityEvent_Tests.cs
  67. 52
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Telemetry/Cryptography_Tests.cs

1
Directory.Packages.props

@ -186,5 +186,6 @@
<PackageVersion Include="coverlet.collector" Version="6.0.4" /> <PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" /> <PackageVersion Include="ConfigureAwait.Fody" Version="3.3.2" />
<PackageVersion Include="Fody" Version="6.9.2" /> <PackageVersion Include="Fody" Version="6.9.2" />
<PackageVersion Include="System.Management" Version="9.0.1"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

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

@ -0,0 +1,47 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Internal.Telemetry.Activity;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Activity.Providers;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc;
[ExposeServices(typeof(ITelemetryActivityEventEnricher), typeof(IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>))]
public sealed class TelemetryApplicationMetricsEnricher : TelemetryActivityEventEnricher, IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>
{
private readonly ITypeFinder _typeFinder;
public TelemetryApplicationMetricsEnricher(ITypeFinder typeFinder, IServiceProvider serviceProvider) : base(serviceProvider)
{
_typeFinder = typeFinder;
}
protected override Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime);
}
protected override Task ExecuteAsync(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 &&
!t.AssemblyQualifiedName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter));
context.Current[ActivityPropertyNames.AppServiceCount] = appServiceCount;
context.Current[ActivityPropertyNames.ControllerCount] = controllerCount;
return Task.CompletedTask;
}
}

20
framework/src/Volo.Abp.Authorization.Abstractions/Properties/AssemblyInfo.cs

@ -0,0 +1,20 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Volo.Abp.Authorization.Abstractions")]
[assembly: AssemblyTrademark("")]
[assembly: InternalsVisibleTo("Volo.Abp.Authorization")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("83632bec-2f60-4c2b-b964-30e8b37fa9d8")]

2
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs

@ -108,6 +108,8 @@ public class PermissionDefinition :
Parent = this Parent = this
}; };
child[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName] = this[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName];
_children.Add(child); _children.Add(child);
return child; return child;

18
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionContext.cs

@ -11,6 +11,13 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
public Dictionary<string, PermissionGroupDefinition> Groups { get; } public Dictionary<string, PermissionGroupDefinition> Groups { get; }
internal IPermissionDefinitionProvider? CurrentProvider { get; set; }
public static class KnownPropertyNames
{
public const string CurrentProviderName = "_CurrentProviderName";
}
public PermissionDefinitionContext(IServiceProvider serviceProvider) public PermissionDefinitionContext(IServiceProvider serviceProvider)
{ {
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
@ -28,7 +35,16 @@ public class PermissionDefinitionContext : IPermissionDefinitionContext
throw new AbpException($"There is already an existing permission group with name: {name}"); throw new AbpException($"There is already an existing permission group with name: {name}");
} }
return Groups[name] = new PermissionGroupDefinition(name, displayName); var group = new PermissionGroupDefinition(name, displayName);
if (CurrentProvider != null)
{
group[KnownPropertyNames.CurrentProviderName] = CurrentProvider.GetType().FullName;
}
Groups[name] = group;
return group;
} }
[NotNull] [NotNull]

2
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs

@ -61,6 +61,8 @@ public class PermissionGroupDefinition : ICanAddChildPermission
isEnabled isEnabled
); );
permission[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName] = this[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName];
_permissions.Add(permission); _permissions.Add(permission);
return permission; return permission;

5
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/StaticPermissionDefinitionStore.cs

@ -84,18 +84,23 @@ public class StaticPermissionDefinitionStore : IStaticPermissionDefinitionStore,
foreach (var provider in providers) foreach (var provider in providers)
{ {
context.CurrentProvider = provider;
provider.PreDefine(context); provider.PreDefine(context);
} }
foreach (var provider in providers) foreach (var provider in providers)
{ {
context.CurrentProvider = provider;
provider.Define(context); provider.Define(context);
} }
foreach (var provider in providers) foreach (var provider in providers)
{ {
context.CurrentProvider = provider;
provider.PostDefine(context); provider.PostDefine(context);
} }
context.CurrentProvider = null;
return context.Groups; return context.Groups;
} }

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

@ -0,0 +1,45 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Activity.Providers;
using Volo.Abp.Internal.Telemetry.Constants;
namespace Volo.Abp.Authorization.Permissions;
[ExposeServices(typeof(ITelemetryActivityEventEnricher), typeof(IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>))]
public sealed class TelemetryPermissionInfoEnricher : TelemetryActivityEventEnricher, IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>
{
private readonly IPermissionDefinitionManager _permissionDefinitionManager;
public TelemetryPermissionInfoEnricher(IPermissionDefinitionManager permissionDefinitionManager,
IServiceProvider serviceProvider) : base(serviceProvider)
{
_permissionDefinitionManager = permissionDefinitionManager;
}
protected override Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.ProjectId.HasValue);
}
protected async override Task ExecuteAsync(ActivityContext context)
{
var permissions = await _permissionDefinitionManager.GetPermissionsAsync();
var userDefinedPermissionsCount = permissions.Count(IsUserDefinedPermission);
context.Current[ActivityPropertyNames.PermissionCount] = userDefinedPermissionsCount;
}
private static bool IsUserDefinedPermission(PermissionDefinition permission)
{
return permission.Properties.TryGetValue(PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName, out var providerName) &&
providerName is string &&
!providerName.ToString()!.StartsWith(TelemetryConsts.VoloNameSpaceFilter);
}
}

1
framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj

@ -22,6 +22,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" /> <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" />
<PackageReference Include="Microsoft.Extensions.Options" /> <PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="System.Management" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<PackageReference Include="Microsoft.Extensions.Logging" /> <PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />

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

@ -4,13 +4,17 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal; using Volo.Abp.Internal;
using Volo.Abp.Internal.Telemetry;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Logging; using Volo.Abp.Logging;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Threading;
namespace Volo.Abp; namespace Volo.Abp;
@ -149,6 +153,56 @@ public abstract class AbpApplicationBase : IAbpApplication
options.PlugInSources options.PlugInSources
); );
} }
protected void SetupTelemetryTracking()
{
if (!ShouldSendTelemetryData())
{
return;
}
AsyncHelper.RunSync(InitializeTelemetryTracking);
}
protected async Task SetupTelemetryTrackingAsync()
{
if (!ShouldSendTelemetryData())
{
return;
}
await InitializeTelemetryTracking();
}
private async Task InitializeTelemetryTracking()
{
try
{
using var scope = ServiceProvider.CreateScope();
var telemetryService = scope.ServiceProvider.GetRequiredService<ITelemetryService>();
await telemetryService.AddActivityAsync(ActivityNameConsts.ApplicationRun);
}
catch (Exception ex)
{
try
{
using var scope = ServiceProvider.CreateScope();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<AbpApplicationBase>>();
logger.LogException(ex, LogLevel.Trace);
}
catch
{
/* ignored */
}
}
}
private bool ShouldSendTelemetryData()
{
using var scope = ServiceProvider.CreateScope();
var abpHostEnvironment = scope.ServiceProvider.GetRequiredService<IAbpHostEnvironment>();
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
return abpHostEnvironment.IsDevelopment() && configuration.GetValue<bool?>("Abp:Telemetry:IsEnabled") == true;
}
//TODO: We can extract a new class for this //TODO: We can extract a new class for this
public virtual async Task ConfigureServicesAsync() public virtual async Task ConfigureServicesAsync()

4
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithExternalServiceProvider.cs

@ -44,6 +44,8 @@ internal class AbpApplicationWithExternalServiceProvider : AbpApplicationBase, I
SetServiceProvider(serviceProvider); SetServiceProvider(serviceProvider);
await InitializeModulesAsync(); await InitializeModulesAsync();
await SetupTelemetryTrackingAsync();
} }
public void Initialize([NotNull] IServiceProvider serviceProvider) public void Initialize([NotNull] IServiceProvider serviceProvider)
@ -53,6 +55,8 @@ internal class AbpApplicationWithExternalServiceProvider : AbpApplicationBase, I
SetServiceProvider(serviceProvider); SetServiceProvider(serviceProvider);
InitializeModules(); InitializeModules();
SetupTelemetryTracking();
} }
public override void Dispose() public override void Dispose()

3
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationWithInternalServiceProvider.cs

@ -50,12 +50,15 @@ internal class AbpApplicationWithInternalServiceProvider : AbpApplicationBase, I
{ {
CreateServiceProvider(); CreateServiceProvider();
await InitializeModulesAsync(); await InitializeModulesAsync();
await SetupTelemetryTrackingAsync();
} }
public void Initialize() public void Initialize()
{ {
CreateServiceProvider(); CreateServiceProvider();
InitializeModules(); InitializeModules();
SetupTelemetryTracking();
} }
public override void Dispose() public override void Dispose()

50
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/ActivityContext.cs

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
namespace Volo.Abp.Internal.Telemetry.Activity;
public class ActivityContext
{
public ActivityEvent Current { get; }
public Dictionary<string, object> ExtraProperties { get; } = new();
public bool IsTerminated { get; private set; }
public Guid? ProjectId => Current.Get<Guid?>(ActivityPropertyNames.ProjectId);
public Guid? SolutionId => Current.Get<Guid?>(ActivityPropertyNames.SolutionId);
public SessionType? SessionType => Current.Get<SessionType?>(ActivityPropertyNames.SessionType);
public string? DeviceId => Current.Get<string?>(ActivityPropertyNames.DeviceId);
public string? SolutionPath => ExtraProperties.TryGetValue(ActivityPropertyNames.SolutionPath, out var solutionPath)
? solutionPath?.ToString()
: null;
private ActivityContext(ActivityEvent current)
{
Current = current;
}
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)
{
var additionalPropertiesDict = new Dictionary<string, object>();
activity[ActivityPropertyNames.AdditionalProperties] = additionalPropertiesDict;
additionalProperties.Invoke(additionalPropertiesDict);
}
return new ActivityContext(activity);
}
public void Terminate()
{
IsTerminated = true;
}
}

147
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/ActivityEvent.cs

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Volo.Abp.Internal.Telemetry.Constants;
namespace Volo.Abp.Internal.Telemetry.Activity;
public class ActivityEvent : Dictionary<string, object?>
{
public ActivityEvent()
{
this[ActivityPropertyNames.Id] = Guid.NewGuid();
this[ActivityPropertyNames.Time] = DateTimeOffset.UtcNow;
}
public ActivityEvent(string activityName, string? details = null) : this()
{
Check.NotNullOrWhiteSpace(activityName, nameof(activityName));
this[ActivityPropertyNames.ActivityName] = activityName;
this[ActivityPropertyNames.ActivityDetails] = details;
}
public bool HasSolutionInfo()
{
return this.ContainsKey(ActivityPropertyNames.HasSolutionInfo);
}
public bool HasDeviceInfo()
{
return this.ContainsKey(ActivityPropertyNames.HasDeviceInfo);
}
public bool HasProjectInfo()
{
return this.ContainsKey(ActivityPropertyNames.HasProjectInfo);
}
public T Get<T>(string key)
{
return TryConvert<T>(key, out var value) ? value : default!;
}
public bool TryGetValue<T>(string key, out T value)
{
return TryConvert(key, out value);
}
private bool TryConvert<T>(string key, out T result)
{
result = default!;
if (!this.TryGetValue(key, out var value) || value is null)
{
return false;
}
try
{
if (value is T tValue)
{
result = tValue;
return true;
}
if (value is JsonElement jsonElement)
{
value = ExtractFromJsonElement(jsonElement);
if (value is null)
{
return false;
}
}
var targetType = typeof(T);
var underlyingType = Nullable.GetUnderlyingType(targetType) ?? targetType;
if (underlyingType.IsEnum)
{
if (value is string str)
{
result = (T)Enum.Parse(underlyingType, str, ignoreCase: true);
}
else if (value is int intValue)
{
result = (T)Enum.ToObject(underlyingType, intValue);
}
return true;
}
if (underlyingType == typeof(Dictionary<string, object>[]))
{
result = (T)value;
return true;
}
if (underlyingType == typeof(Guid))
{
result = (T)(object)Guid.Parse(value.ToString()!);
return true;
}
if (underlyingType == typeof(DateTimeOffset))
{
result = (T)(object)DateTimeOffset.Parse(value.ToString()!);
return true;
}
// Nullable types
result = (T)Convert.ChangeType(value, underlyingType);
return true;
}
catch
{
return false;
}
}
private static object? ExtractFromJsonElement(JsonElement element)
{
return element.ValueKind switch
{
JsonValueKind.String => element.GetString(),
JsonValueKind.Number => element.GetInt32(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Null => null,
JsonValueKind.Array => element.EnumerateArray()
.Select(item =>
{
if (item.ValueKind == JsonValueKind.Object)
{
return item.EnumerateObject()
.ToDictionary(prop => prop.Name, prop => ExtractFromJsonElement(prop.Value));
}
return new Dictionary<string, object?> { { "value", ExtractFromJsonElement(item) } };
})
.ToArray(),
JsonValueKind.Object => element.EnumerateObject()
.ToDictionary(prop => prop.Name, prop => ExtractFromJsonElement(prop.Value)),
_ => element.ToString()
};
}
}

7
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Contracts/IHasParentTelemetryActivityEventEnricher.cs

@ -0,0 +1,7 @@
using Volo.Abp.Internal.Telemetry.Activity.Providers;
namespace Volo.Abp.Internal.Telemetry.Activity.Contracts;
public interface IHasParentTelemetryActivityEventEnricher<out TParent> where TParent: TelemetryActivityEventEnricher
{
}

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

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace Volo.Abp.Internal.Telemetry.Activity.Contracts;
public interface ITelemetryActivityEventBuilder
{
Task<ActivityEvent?> BuildAsync(ActivityContext context);
}

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

@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace Volo.Abp.Internal.Telemetry.Activity.Contracts;
public interface ITelemetryActivityEventEnricher
{
int ExecutionOrder { get; }
Task EnrichAsync(ActivityContext context);
}

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

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.Internal.Telemetry.Activity.Contracts;
public interface ITelemetryActivityStorage
{
Guid InitializeOrGetSession();
void DeleteActivities(ActivityEvent[] activities);
void SaveActivity(ActivityEvent activityEvent);
List<ActivityEvent> GetActivities();
bool ShouldAddDeviceInfo();
bool ShouldAddSolutionInformation(Guid solutionId);
bool ShouldAddProjectInfo(Guid projectId);
bool ShouldSendActivities();
void MarkActivitiesAsFailed(ActivityEvent[] activities);
}

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

@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
namespace Volo.Abp.Internal.Telemetry.Activity.Providers;
public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, ISingletonDependency
{
private readonly List<ITelemetryActivityEventEnricher> _activityEnrichers;
public TelemetryActivityEventBuilder(IEnumerable<ITelemetryActivityEventEnricher> activityDataEnrichers)
{
_activityEnrichers = activityDataEnrichers
.Where(FilterEnricher)
.OrderByDescending(x => x.ExecutionOrder)
.ToList();
}
public virtual async Task<ActivityEvent?> BuildAsync(ActivityContext context)
{
foreach (var enricher in _activityEnrichers)
{
try
{
await enricher.EnrichAsync(context);
}
catch
{
//ignored
}
if (context.IsTerminated)
{
return null;
}
}
return context.Current;
}
private static bool FilterEnricher(ITelemetryActivityEventEnricher enricher)
{
return ProxyHelper.GetUnProxiedType(enricher).Assembly.FullName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter) &&
enricher is not IHasParentTelemetryActivityEventEnricher<TelemetryActivityEventEnricher>;
}
}

70
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Providers/TelemetryActivityEventEnricher.cs

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
namespace Volo.Abp.Internal.Telemetry.Activity.Providers;
public abstract class TelemetryActivityEventEnricher : ITelemetryActivityEventEnricher, IScopedDependency
{
public virtual int ExecutionOrder { get; set; } = 0;
protected bool IgnoreChildren { get; set; }
protected virtual Type? ReplaceParentType { get; set; }
private readonly IServiceProvider _serviceProvider;
protected TelemetryActivityEventEnricher(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task EnrichAsync(ActivityContext context)
{
if (!await CanExecuteAsync(context))
{
return;
}
await ExecuteAsync(context);
await ExecuteChildrenAsync(context);
}
protected virtual Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(true);
}
protected abstract Task ExecuteAsync(ActivityContext context);
protected virtual async Task ExecuteChildrenAsync(ActivityContext context)
{
if (IgnoreChildren)
{
return;
}
using var scope = _serviceProvider.CreateScope();
foreach (var child in GetChildren(scope.ServiceProvider))
{
await child.EnrichAsync(context);
}
}
private ITelemetryActivityEventEnricher[] GetChildren(IServiceProvider serviceProvider)
{
var targetType = ReplaceParentType ?? ProxyHelper.GetUnProxiedType(this);
var genericInterfaceType = typeof(IHasParentTelemetryActivityEventEnricher<>).MakeGenericType(targetType);
var enumerableType = typeof(IEnumerable<>).MakeGenericType(genericInterfaceType);
var childServices = (IEnumerable<object>)serviceProvider.GetRequiredService(enumerableType);
return childServices
.Cast<ITelemetryActivityEventEnricher>()
.ToArray();
}
}

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

@ -0,0 +1,104 @@
using System;
using System.IO;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.Helpers;
namespace Volo.Abp.Internal.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher), typeof(IHasParentTelemetryActivityEventEnricher<TelemetrySessionInfoEnricher>))]
public sealed class TelemetryApplicationInfoEnricher : TelemetryActivityEventEnricher, IHasParentTelemetryActivityEventEnricher<TelemetrySessionInfoEnricher>
{
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
public TelemetryApplicationInfoEnricher(ITelemetryActivityStorage telemetryActivityStorage, IServiceProvider serviceProvider) : base(serviceProvider)
{
_telemetryActivityStorage = telemetryActivityStorage;
}
protected override Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime);
}
protected override Task ExecuteAsync(ActivityContext context)
{
try
{
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly is null)
{
context.Terminate();
return Task.CompletedTask;
}
var projectMetaData = AbpProjectMetadataReader.ReadProjectMetadata(entryAssembly);
if (projectMetaData?.ProjectId == null || projectMetaData.AbpSlnPath.IsNullOrEmpty())
{
context.Terminate();
return Task.CompletedTask;
}
if (!_telemetryActivityStorage.ShouldAddProjectInfo(projectMetaData.ProjectId.Value))
{
IgnoreChildren = true;
return Task.CompletedTask;
}
var solutionId = ReadSolutionIdFromSolutionPath(projectMetaData.AbpSlnPath);
if (!solutionId.HasValue)
{
IgnoreChildren = true;
context.Terminate();
return Task.CompletedTask;
}
context.ExtraProperties[ActivityPropertyNames.SolutionPath] = projectMetaData.AbpSlnPath;
context.Current[ActivityPropertyNames.ProjectType] = projectMetaData.Role ?? string.Empty;
context.Current[ActivityPropertyNames.ProjectId] = projectMetaData.ProjectId.Value;
context.Current[ActivityPropertyNames.SolutionId] = solutionId;
context.Current[ActivityPropertyNames.HasProjectInfo] = true;
}
catch
{
//ignored
}
return Task.CompletedTask;
}
private static Guid? ReadSolutionIdFromSolutionPath(string solutionPath)
{
try
{
if (solutionPath.IsNullOrEmpty())
{
return null;
}
using var fs = new FileStream(solutionPath!, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var doc = JsonDocument.Parse(fs, new JsonDocumentOptions
{
AllowTrailingCommas = true
});
if (doc.RootElement.TryGetProperty("id", out var property) && property.TryGetGuid(out var solutionId))
{
return solutionId;
}
}
catch
{
// ignored
}
return null;
}
}

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

@ -0,0 +1,79 @@
using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
namespace Volo.Abp.Internal.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
internal sealed class TelemetryDeviceInfoEnricher : TelemetryActivityEventEnricher
{
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
private readonly ISoftwareInfoProvider _softwareInfoProvider;
public TelemetryDeviceInfoEnricher(ITelemetryActivityStorage telemetryActivityStorage,
ISoftwareInfoProvider softwareInfoProvider, IServiceProvider serviceProvider) : base(serviceProvider)
{
_telemetryActivityStorage = telemetryActivityStorage;
_softwareInfoProvider = softwareInfoProvider;
}
protected async override Task ExecuteAsync(ActivityContext context)
{
try
{
var deviceId = DeviceManager.GetUniquePhysicalKey(true);
context.Current[ActivityPropertyNames.DeviceId] = deviceId;
if (!_telemetryActivityStorage.ShouldAddDeviceInfo())
{
return;
}
var softwareList = await _softwareInfoProvider.GetSoftwareInfoAsync();
context.Current[ActivityPropertyNames.InstalledSoftwares] = softwareList;
context.Current[ActivityPropertyNames.DeviceLanguage] = CultureInfo.CurrentUICulture.Name;
context.Current[ActivityPropertyNames.OperatingSystem] = GetOperatingSystem();
context.Current[ActivityPropertyNames.CountryIsoCode] = GetCountry();
context.Current[ActivityPropertyNames.HasDeviceInfo] = true;
context.Current[ActivityPropertyNames.OperatingSystemArchitecture] = RuntimeInformation.OSArchitecture.ToString();
}
catch
{
//ignored
}
}
private static OperationSystem GetOperatingSystem()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return OperationSystem.Windows;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return OperationSystem.Linux;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return OperationSystem.MacOS;
}
return OperationSystem.Unknown;
}
private static string GetCountry()
{
var region = new RegionInfo(CultureInfo.InstalledUICulture.Name);
return region.TwoLetterISORegionName;
}
}

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

@ -0,0 +1,39 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
namespace Volo.Abp.Internal.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher), typeof(IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>))]
internal sealed class TelemetryModuleInfoEnricher : TelemetryActivityEventEnricher, IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>
{
private readonly IModuleContainer _moduleContainer;
private readonly IAssemblyFinder _assemblyFinder;
public TelemetryModuleInfoEnricher(IModuleContainer moduleContainer, IAssemblyFinder assemblyFinder,
IServiceProvider serviceProvider) : base(serviceProvider)
{
_moduleContainer = moduleContainer;
_assemblyFinder = assemblyFinder;
}
protected override Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime);
}
protected override Task ExecuteAsync(ActivityContext context)
{
context.Current[ActivityPropertyNames.ModuleCount] = _moduleContainer.Modules.Count;
context.Current[ActivityPropertyNames.ProjectCount] = _assemblyFinder.Assemblies.Count(x =>
!x.FullName.IsNullOrEmpty() &&
!x.FullName.StartsWith(TelemetryConsts.VoloNameSpaceFilter));
return Task.CompletedTask;
}
}

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

@ -0,0 +1,27 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
namespace Volo.Abp.Internal.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher))]
public class TelemetrySessionInfoEnricher : TelemetryActivityEventEnricher
{
public override int ExecutionOrder { get; set; } = 10;
public TelemetrySessionInfoEnricher(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
protected override Task ExecuteAsync(ActivityContext context)
{
context.Current[ActivityPropertyNames.SessionType] = SessionType.ApplicationRuntime;
context.Current[ActivityPropertyNames.SessionId] = Guid.NewGuid().ToString();
context.Current[ActivityPropertyNames.IsFirstSession] = !File.Exists(TelemetryPaths.ActivityStorage);
return Task.CompletedTask;
}
}

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

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
namespace Volo.Abp.Internal.Telemetry.Activity.Providers;
[ExposeServices(typeof(ITelemetryActivityEventEnricher), typeof(IHasParentTelemetryActivityEventEnricher<TelemetrySessionInfoEnricher>))]
internal sealed class TelemetrySolutionInfoEnricher : TelemetryActivityEventEnricher, IHasParentTelemetryActivityEventEnricher<TelemetrySessionInfoEnricher>
{
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
public TelemetrySolutionInfoEnricher(ITelemetryActivityStorage telemetryActivityStorage, IServiceProvider serviceProvider) : base(serviceProvider)
{
_telemetryActivityStorage = telemetryActivityStorage;
}
protected override Task<bool> CanExecuteAsync(ActivityContext context)
{
if (context.SolutionId.HasValue && !context.SolutionPath.IsNullOrEmpty())
{
return Task.FromResult(_telemetryActivityStorage.ShouldAddSolutionInformation(context.SolutionId.Value));
}
return Task.FromResult(false);
}
protected override Task ExecuteAsync(ActivityContext context)
{
try
{
var jsonContent = File.ReadAllText(context.SolutionPath!);
using var doc = JsonDocument.Parse(jsonContent, new JsonDocumentOptions
{
AllowTrailingCommas = true
});
var root = doc.RootElement;
if (root.TryGetProperty("creatingStudioConfiguration", out var creatingStudioConfiguration))
{
AddSolutionCreationConfiguration(context, creatingStudioConfiguration);
}
if (root.TryGetProperty("modules", out var modulesElement))
{
AddModuleInfo(context, modulesElement);
}
context.Current[ActivityPropertyNames.HasSolutionInfo] = true;
}
catch
{
//ignored
}
return Task.CompletedTask;
}
private static void AddSolutionCreationConfiguration(ActivityContext context, JsonElement config)
{
context.Current[ActivityPropertyNames.Template] = TelemetryJsonExtensions.GetStringOrNull(config, "template");
context.Current[ActivityPropertyNames.CreatedAbpStudioVersion] = TelemetryJsonExtensions.GetStringOrNull(config, "createdAbpStudioVersion");
context.Current[ActivityPropertyNames.MultiTenancy] = TelemetryJsonExtensions.GetBooleanOrNull(config, "multiTenancy");
context.Current[ActivityPropertyNames.UiFramework] = TelemetryJsonExtensions.GetStringOrNull(config, "uiFramework");
context.Current[ActivityPropertyNames.DatabaseProvider] = TelemetryJsonExtensions.GetStringOrNull(config, "databaseProvider");
context.Current[ActivityPropertyNames.Theme] = TelemetryJsonExtensions.GetStringOrNull(config, "theme");
context.Current[ActivityPropertyNames.ThemeStyle] = TelemetryJsonExtensions.GetStringOrNull(config, "themeStyle");
context.Current[ActivityPropertyNames.HasPublicWebsite] = TelemetryJsonExtensions.GetBooleanOrNull(config, "publicWebsite");
context.Current[ActivityPropertyNames.IsTiered] = TelemetryJsonExtensions.GetBooleanOrNull(config, "tiered");
context.Current[ActivityPropertyNames.SocialLogins] = TelemetryJsonExtensions.GetBooleanOrNull(config, "socialLogin");
context.Current[ActivityPropertyNames.DatabaseManagementSystem] = TelemetryJsonExtensions.GetStringOrNull(config, "databaseManagementSystem");
context.Current[ActivityPropertyNames.IsSeparateTenantSchema] = TelemetryJsonExtensions.GetBooleanOrNull(config, "separateTenantSchema");
context.Current[ActivityPropertyNames.MobileFramework] = TelemetryJsonExtensions.GetStringOrNull(config, "mobileFramework");
context.Current[ActivityPropertyNames.IncludeTests] = TelemetryJsonExtensions.GetBooleanOrNull(config, "includeTests");
context.Current[ActivityPropertyNames.DynamicLocalization] = TelemetryJsonExtensions.GetBooleanOrNull(config, "dynamicLocalization");
context.Current[ActivityPropertyNames.KubernetesConfiguration] = TelemetryJsonExtensions.GetBooleanOrNull(config, "kubernetesConfiguration");
context.Current[ActivityPropertyNames.GrafanaDashboard] = TelemetryJsonExtensions.GetBooleanOrNull(config, "grafanaDashboard");
}
private static void AddModuleInfo(ActivityContext context, JsonElement modulesElement)
{
var modules = new List<Dictionary<string, object?>>();
foreach (var module in modulesElement.EnumerateObject())
{
var modulePath = GetModuleFilePath(context.SolutionPath!, module);
if (modulePath.IsNullOrEmpty())
{
continue;
}
var moduleJsonFileContent = File.ReadAllText(modulePath);
using var moduleDoc = JsonDocument.Parse(moduleJsonFileContent);
if (!moduleDoc.RootElement.TryGetProperty("imports", out var imports))
{
continue;
}
foreach (var import in imports.EnumerateObject())
{
modules.Add(new Dictionary<string, object?>
{
{ ActivityPropertyNames.ModuleName, import.Name },
{ ActivityPropertyNames.ModuleVersion, TelemetryJsonExtensions.GetStringOrNull(import.Value, "version") },
{ ActivityPropertyNames.ModuleInstallationTime, TelemetryJsonExtensions.GetDateTimeOffsetOrNull(import.Value, "creationTime") }
});
}
}
context.Current[ActivityPropertyNames.InstalledModules] = modules;
}
private static string? GetModuleFilePath(string solutionPath, JsonProperty module)
{
var path = TelemetryJsonExtensions.GetStringOrNull(module.Value, "path");
if (path.IsNullOrEmpty())
{
return null;
}
var fullPath = Path.Combine(Path.GetDirectoryName(solutionPath)!, path);
return File.Exists(fullPath) ? fullPath : null;
}
}

18
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Storage/FailedActivityInfo.cs

@ -0,0 +1,18 @@
using System;
namespace Volo.Abp.Internal.Telemetry.Activity.Storage;
internal class FailedActivityInfo
{
public DateTimeOffset FirstFailTime { get; set; }
public DateTimeOffset LastFailTime { get; set; }
public int RetryCount { get; set; }
public bool IsExpired()
{
var now = DateTimeOffset.UtcNow;
return RetryCount >= TelemetryPeriod.MaxActivityRetryCount ||
now - FirstFailTime > TelemetryPeriod.MaxFailedActivityAge;
}
}

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

@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Helpers;
namespace Volo.Abp.Internal.Telemetry.Activity.Storage;
public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDependency
{
private TelemetryActivityStorageState State { get; }
private readonly static JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
public TelemetryActivityStorage()
{
CreateDirectoryIfNotExist();
State = LoadState();
}
public void SaveActivity(ActivityEvent activityEvent)
{
State.Activities.Add(activityEvent);
var activityName = activityEvent.Get<string>(ActivityPropertyNames.ActivityName);
if (activityName == ActivityNameConsts.AbpStudioClose)
{
State.SessionId = null;
}
if (activityEvent.HasDeviceInfo())
{
State.LastDeviceInfoAddTime = DateTimeOffset.UtcNow;
}
if (activityEvent.HasSolutionInfo())
{
var solutionId = activityEvent.Get<Guid>(ActivityPropertyNames.SolutionId);
State.Solutions[solutionId] = DateTimeOffset.UtcNow;
}
if (activityEvent.HasProjectInfo())
{
var projectId = activityEvent.Get<Guid>(ActivityPropertyNames.ProjectId);
State.Projects[projectId] = DateTimeOffset.UtcNow;
}
SaveState();
}
public List<ActivityEvent> GetActivities()
{
return State.Activities;
}
public Guid InitializeOrGetSession()
{
if (State.SessionId.HasValue)
{
return State.SessionId.Value;
}
State.SessionId = Guid.NewGuid();
SaveState();
return State.SessionId.Value;
}
public void DeleteActivities(ActivityEvent[] activities)
{
var activityIds = new HashSet<Guid>(activities.Select(x => x.Get<Guid>(ActivityPropertyNames.Id)));
State.Activities.RemoveAll(x => activityIds.Contains(x.Get<Guid>(ActivityPropertyNames.Id)));
SaveState();
}
public void MarkActivitiesAsFailed(ActivityEvent[] activities)
{
var now = DateTimeOffset.UtcNow;
foreach (var activity in activities)
{
var activityId = activity.Get<Guid>(ActivityPropertyNames.Id);
if (State.FailedActivities.TryGetValue(activityId, out var failedActivityInfo))
{
failedActivityInfo.RetryCount++;
failedActivityInfo.LastFailTime = now;
if (!failedActivityInfo.IsExpired())
{
continue;
}
State.Activities.RemoveAll(x=> x.Get<Guid>(ActivityPropertyNames.Id) == activityId);
State.FailedActivities.Remove(activityId);
}
else
{
State.FailedActivities[activityId] = new FailedActivityInfo
{
FirstFailTime = now,
LastFailTime = now,
RetryCount = 1
};
}
}
SaveState();
}
public bool ShouldAddDeviceInfo()
{
return State.LastDeviceInfoAddTime is null ||
DateTimeOffset.UtcNow - State.LastDeviceInfoAddTime > TelemetryPeriod.InformationSendPeriod;
}
public bool ShouldAddSolutionInformation(Guid solutionId)
{
return !State.Solutions.TryGetValue(solutionId, out var lastSend) ||
DateTimeOffset.UtcNow - lastSend > TelemetryPeriod.InformationSendPeriod;
}
public bool ShouldAddProjectInfo(Guid projectId)
{
return !State.Projects.TryGetValue(projectId, out var lastSend) ||
DateTimeOffset.UtcNow - lastSend > TelemetryPeriod.InformationSendPeriod;
}
public bool ShouldSendActivities()
{
return State.ActivitySendTime is null ||
DateTimeOffset.UtcNow - State.ActivitySendTime > TelemetryPeriod.ActivitySendPeriod;
}
private void SaveState()
{
try
{
var json = JsonSerializer.Serialize(State, JsonSerializerOptions);
var encryptedJson = Cryptography.Encrypt(json);
File.WriteAllText(TelemetryPaths.ActivityStorage, encryptedJson, Encoding.UTF8);
}
catch
{
// Ignored
}
}
private static TelemetryActivityStorageState LoadState()
{
try
{
if (!File.Exists(TelemetryPaths.ActivityStorage))
{
return new TelemetryActivityStorageState();
}
var fileContent = MutexExecutor.ReadFileSafely(TelemetryPaths.ActivityStorage);
if (fileContent.IsNullOrEmpty())
{
return new TelemetryActivityStorageState();
}
var json = Cryptography.Decrypt(fileContent);
var state = JsonSerializer.Deserialize<TelemetryActivityStorageState>(json, JsonSerializerOptions)!;
state.Activities = state.Activities.Where(x => x != null).ToList();
return state;
}
catch
{
return new TelemetryActivityStorageState();
}
}
private static void CreateDirectoryIfNotExist()
{
try
{
var storageDirectory = Path.GetDirectoryName(TelemetryPaths.ActivityStorage)!;
if (!Directory.Exists(storageDirectory))
{
Directory.CreateDirectory(storageDirectory);
}
}
catch
{
// Ignored
}
}
}

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

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.Internal.Telemetry.Activity.Storage;
internal class TelemetryActivityStorageState
{
public DateTimeOffset? ActivitySendTime { get; set; }
public DateTimeOffset? LastDeviceInfoAddTime { get; set; }
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();
public Dictionary<Guid, FailedActivityInfo> FailedActivities { get; set; } = new();
}

34
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/Storage/TelemetryPeriod.cs

@ -0,0 +1,34 @@
using System;
namespace Volo.Abp.Internal.Telemetry.Activity.Storage;
static internal class TelemetryPeriod
{
private const string TestModeEnvironmentVariable = "ABP_TELEMETRY_TEST_MODE";
static TelemetryPeriod()
{
var isTestMode = IsTestModeEnabled();
InformationSendPeriod = isTestMode
? TimeSpan.FromSeconds(15)
: TimeSpan.FromDays(7);
ActivitySendPeriod = isTestMode
? TimeSpan.FromSeconds(5)
: TimeSpan.FromDays(1);
}
public static TimeSpan ActivitySendPeriod { get; }
public static TimeSpan InformationSendPeriod { get; }
public static int MaxActivityRetryCount { get; set; } = 3;
public static TimeSpan MaxFailedActivityAge { get; set; } = TimeSpan.FromDays(30);
private static bool IsTestModeEnabled()
{
var testModeVariable =
Environment.GetEnvironmentVariable(TestModeEnvironmentVariable, EnvironmentVariableTarget.User);
return bool.TryParse(testModeVariable, out var isTestMode) && isTestMode;
}
}

34
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Activity/TelemetryJsonExtensions.cs

@ -0,0 +1,34 @@
using System;
using System.Text.Json;
namespace Volo.Abp.Internal.Telemetry.Activity;
static internal class TelemetryJsonExtensions
{
static internal string? GetStringOrNull(JsonElement element, string propertyName)
{
return element.TryGetProperty(propertyName, out var property)
? property.GetString() ?? null
: null;
}
static internal bool? GetBooleanOrNull(JsonElement element, string propertyName)
{
if (element.TryGetProperty(propertyName, out var property) && bool.TryParse(property.GetString(), out var boolValue))
{
return boolValue;
}
return null;
}
static internal DateTimeOffset? GetDateTimeOffsetOrNull(JsonElement element, string propertyName)
{
if (element.TryGetProperty(propertyName, out var date) && DateTimeOffset.TryParse(date.GetString(), out var dateTimeValue))
{
return dateTimeValue;
}
return null;
}
}

12
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/AbpPlatformUrls.cs

@ -0,0 +1,12 @@
namespace Volo.Abp.Internal.Telemetry.Constants;
public static class AbpPlatformUrls
{
#if DEBUG
public const string AbpTelemetryApiUrl = "https://localhost:44393/";
#else
public const string AbpTelemetryApiUrl = "https://telemetry.abp.io/";
#endif
}

76
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/ActivityNameConsts.cs

@ -0,0 +1,76 @@
namespace Volo.Abp.Internal.Telemetry.Constants;
public static class ActivityNameConsts
{
public const string AbpStudioOpen = "AbpStudio.Open";
public const string AbpStudioOpenFirstTimeForDevice = "AbpStudio.Open.FirstTimeForDevice";
public const string AbpStudioOpenFirstTimeForUser = "AbpStudio.Open.FirstTimeForUser";
public const string AbpStudioClose = "AbpStudio.Close";
public const string AbpStudioCloseWithoutLogin = "AbpStudio.Close.WithoutLogin";
public const string AbpStudioLogin = "AbpStudio.Login";
public const string AbpStudioCampaignClick = "AbpStudio.Campaing.Click";
public const string AbpStudioCommunityPostClick = "AbpStudio.CommunityPost.Click";
public const string AbpStudioSolutionInitExisting = "AbpStudio.Solution.InitExisting";
public const string AbpStudioSolutionNew = "AbpStudio.Solution.New";
public const string AbpStudioSolutionNewMicroservice = "AbpStudio.Solution.New.Microservice";
public const string AbpStudioSolutionAddModule = "AbpStudio.Solution.Add.Module";
public const string AbpStudioSolutionAddModuleEmpty = "AbpStudio.Solution.Add.Module.Empty";
public const string AbpStudioSolutionAddModuleDdd = "AbpStudio.Solution.Add.Module.Ddd";
public const string AbpStudioSolutionAddModuleStandard = "AbpStudio.Solution.Add.Module.Standard";
public const string AbpStudioSolutionAddMicroservice = "AbpStudio.Solution.Add.Microservice";
public const string AbpStudioSolutionAddGateway = "AbpStudio.Solution.Add.Gateway";
public const string AbpStudioSolutionAddWeb = "AbpStudio.Solution.Add.Web";
public const string AbpStudioSolutionAddPackage = "AbpStudio.Solution.Add.Package";
public const string AbpStudioSolutionAddPackageHttpApiLayer = "AbpStudio.Solution.Add.Package.HttpApiLayer";
public const string AbpStudioAbpCliInstallLibs = "AbpStudio.AbpCli.InstallLibs";
public const string AbpStudioAbpCliUpgradeAbp = "AbpStudio.AbpCli.UpgradeAbp";
public const string AbpStudioAbpCliSwitchToStable = "AbpStudio.AbpCli.SwitchToStable";
public const string AbpStudioAbpCliSwitchToPreview = "AbpStudio.AbpCli.SwitchToPreview";
public const string AbpStudioAbpCliSwitchToNightly = "AbpStudio.AbpCli.SwitchToNightly";
public const string AbpStudioAbpCliClean = "AbpStudio.AbpCli.Clean";
public const string AbpStudioDotnetCliBuild = "AbpStudio.DotnetCli.Build";
public const string AbpStudioDotnetCliGraphBuild = "AbpStudio.DotnetCli.GraphBuild";
public const string AbpStudioDotnetCliClean = "AbpStudio.DotnetCli.Clean";
public const string AbpStudioDotnetCliRestore = "AbpStudio.DotnetCli.Restore";
public const string AbpStudioSolutionRunnerRunApp = "AbpStudio.SolutionRunner.RunApp";
public const string AbpStudioSolutionRunnerAddCsharpApp = "AbpStudio.SolutionRunner.Add.CsharpApp";
public const string AbpStudioSolutionRunnerAddCliApp = "AbpStudio.SolutionRunner.Add.CliApp";
public const string AbpStudioSolutionRunnerAddProfile = "AbpStudio.SolutionRunner.Add.Profile";
public const string AbpStudioSolutionRunnerAppManageMetadata = "AbpStudio.SolutionRunner.App.Manage.Metadata";
public const string AbpStudioSolutionRunnerAppManageSecrets = "AbpStudio.SolutionRunner.App.Manage.Secrets";
public const string AbpStudioSolutionOpen = "AbpStudio.Solution.Open";
public const string AbpStudioMonitoringBrowse = "AbpStudio.Monitoring.Browse";
public const string AbpStudioMonitoringHttpRequests = "AbpStudio.Monitoring.HttpRequests";
public const string AbpStudioMonitoringHttpRequestsDetail = "AbpStudio.Monitoring.HttpRequests.Detail";
public const string AbpStudioMonitoringEvents = "AbpStudio.Monitoring.Events";
public const string AbpStudioMonitoringEventsDetail = "AbpStudio.Monitoring.Events.Detail";
public const string AbpStudioMonitoringExceptions = "AbpStudio.Monitoring.Exceptions";
public const string AbpStudioMonitoringExceptionsDetail = "AbpStudio.Monitoring.Exceptions.Detail";
public const string AbpStudioMonitoringLogs = "AbpStudio.Monitoring.Logs";
public const string AbpStudioKubernetesAddProfile = "AbpStudio.Kubernetes.Add.Profile";
public const string AbpStudioKubernetesConnect = "AbpStudio.Kubernetes.Connect";
public const string AbpStudioKubernetesIntercept = "AbpStudio.Kubernetes.Intercept";
public const string AbpStudioKubernetesHelmCommandsInstall = "AbpStudio.Kubernetes.Helm.Commands.Install";
public const string AbpStudioKubernetesHelmCommandsBuildImages = "AbpStudio.Kubernetes.Helm.Commands.BuildImages";
public const string AbpStudioKubernetesHelmChartsRefreshSubCharts = "AbpStudio.Kubernetes.Helm.Charts.RefreshSubCharts";
public const string AbpStudioKubernetesHelmChartsManageMetadata = "AbpStudio.Kubernetes.Helm.Charts.Manage.Metadata";
public const string AbpStudioLogsShow = "AbpStudio.Logs.Show";
public const string AbpStudioSuiteOpen = "AbpStudio.Suite.Open";
public const string AbpStudioGlobalSecretsManage = "AbpStudio.GlobalSecrets.Manage";
public const string AbpStudioGlobalMetadataManage = "AbpStudio.GlobalMetadata.Manage";
public const string AbpCliCommandsNewSolution = "AbpCli.Comands.NewSolution";
public const string AbpCliCommandsNewModule = "AbpCli.Comands.NewModule";
public const string AbpCliCommandsNewPackage = "AbpCli.Comands.NewPackage";
public const string AbpCliCommandsUpdate = "AbpCli.Comands.Update";
public const string AbpCliCommandsClean = "AbpCli.Comands.Clean";
public const string AbpCliCommandsAddPackage = "AbpCli.Comands.AddPackage";
public const string AbpCliCommandsAddPackageRef = "AbpCli.Comands.AddPackageRef";
public const string AbpCliCommandsInstallModule = "AbpCli.Comands.InstallModule";
public const string AbpCliCommandsInstallLocalModule = "AbpCli.Comands.InstallLocalModule";
public const string AbpCliCommandsListModules = "AbpCli.Comands.ListModules";
public const string AbpCliRun = "AbpCli.Run";
public const string AbpCliExit = "AbpCli.Exit";
public const string ApplicationRun = "Application.Run";
public const string AbpStudioBrowserOpen = "AbpStudio.Browser.Open";
public const string Error = "Error";
}

77
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/ActivityPropertyNames.cs

@ -0,0 +1,77 @@
namespace Volo.Abp.Internal.Telemetry.Constants;
public static class ActivityPropertyNames
{
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 = "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";
public const string AdditionalProperties = "AdditionalProperties";
}

260
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/DeviceManager.cs

@ -0,0 +1,260 @@
using System;
namespace Volo.Abp.Internal.Telemetry.Constants;
static internal class DeviceManager
{
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
{
return Guid.NewGuid().ToString();
}
}
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!;
}
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();
}
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
{
}
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 + "\"";
}
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/AbpTool.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.Internal.Telemetry.Constants.Enums;
public enum AbpTool : byte
{
Unknown = 0,
StudioUI = 1,
StudioCli = 2,
OldCli = 3
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/OperationSystem.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.Internal.Telemetry.Constants.Enums;
public enum OperationSystem
{
Unknown = 0,
Windows = 1,
MacOS = 2,
Linux = 3,
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/SessionType.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.Internal.Telemetry.Constants.Enums;
public enum SessionType
{
Unknown = 0,
AbpStudio = 1,
AbpCli = 2,
ApplicationRuntime = 3
}

11
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/Enums/SoftwareType.cs

@ -0,0 +1,11 @@
namespace Volo.Abp.Internal.Telemetry.Constants.Enums;
public enum SoftwareType : byte
{
Others = 0,
AbpStudio = 1,
DotnetSdk = 2,
OperatingSystem = 3,
Ide = 4,
Browser = 5
}

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

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

13
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Constants/TelemetryPaths.cs

@ -0,0 +1,13 @@
using System;
using System.IO;
namespace Volo.Abp.Internal.Telemetry.Constants;
public static class TelemetryPaths
{
public static string AccessToken => Path.Combine(AbpRootPath, "cli", "access-token.bin");
public static string ComputerId => Path.Combine(AbpRootPath, "cli", "computer-id.bin");
public static string ActivityStorage => Path.Combine(AbpRootPath , "telemetry", "activity-storage.bin");
public static string Studio => Path.Combine(AbpRootPath, "studio");
private readonly static string AbpRootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".abp");
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Contracts/ISoftwareDetector.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
internal interface ISoftwareDetector
{
string Name { get; }
Task<SoftwareInfo?> DetectAsync();
}

9
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Contracts/ISoftwareInfoProvider.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
internal interface ISoftwareInfoProvider
{
Task<List<SoftwareInfo>> GetSoftwareInfoAsync();
}

11
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Contracts/SoftwareInfo.cs

@ -0,0 +1,11 @@
using Volo.Abp.Internal.Telemetry.Constants.Enums;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
internal class SoftwareInfo(string name, string? version, string? uiTheme, SoftwareType softwareType)
{
public string Name { get; set; } = name;
public string? Version { get; set; } = version;
public string? UiTheme { get; set; } = uiTheme;
public SoftwareType SoftwareType { get; set; } = softwareType;
}

78
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Core/SoftwareDetector.cs

@ -0,0 +1,78 @@
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
[ExposeServices(typeof(ISoftwareDetector))]
abstract internal class SoftwareDetector: ISoftwareDetector , ISingletonDependency
{
public abstract string Name { get; }
public abstract Task<SoftwareInfo?> DetectAsync();
protected virtual async Task<string?> ExecuteCommandAsync(string command, string? arg)
{
var outputBuilder = new StringBuilder();
var processStartInfo = new ProcessStartInfo
{
FileName = command,
Arguments = arg ?? "",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = new Process();
process.StartInfo = processStartInfo;
process.EnableRaisingEvents = true;
var tcs = new TaskCompletionSource<bool>();
process.OutputDataReceived += (sender, e) =>
{
if (e.Data != null)
{
outputBuilder.AppendLine(e.Data);
}
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data != null)
{
outputBuilder.AppendLine(e.Data);
}
};
process.Exited += (sender, e) =>
{
tcs.TrySetResult(true);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await tcs.Task;
var output = outputBuilder.ToString().Trim();
return string.IsNullOrWhiteSpace(output) ? null : output;
}
protected string? GetFileVersion(string filePath)
{
try
{
var versionInfo = FileVersionInfo.GetVersionInfo(filePath);
return versionInfo.FileVersion;
}
catch
{
return string.Empty;
}
}
}

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

@ -0,0 +1,76 @@
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class AbpStudioDetector : SoftwareDetector
{
public override string Name => "Abp Studio";
private const string AbpStudioVersionExtensionName = "Volo.Abp.Studio.Extensions.StandardSolutionTemplates";
public override Task<SoftwareInfo?> DetectAsync()
{
try
{
var uiTheme = GetAbpStudioUiTheme();
var version = GetAbpStudioVersion();
return Task.FromResult<SoftwareInfo?>(new SoftwareInfo(Name, version, uiTheme, SoftwareType.AbpStudio));
}
catch
{
return Task.FromResult<SoftwareInfo?>(null);
}
}
private string? GetAbpStudioUiTheme()
{
var ideStateJsonPath = Path.Combine(
TelemetryPaths.Studio,
"ui",
"ide-state.json"
);
if (!File.Exists(ideStateJsonPath))
{
return null;
}
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;
}
private string? GetAbpStudioVersion()
{
var extensionsFilePath = Path.Combine(TelemetryPaths.Studio, "extensions.json");
if (!File.Exists(extensionsFilePath))
{
return null;
}
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)
{
foreach (var extension in extensionsElement.EnumerateArray())
{
if (extension.TryGetProperty("name", out var nameProp) &&
nameProp.GetString() == AbpStudioVersionExtensionName &&
extension.TryGetProperty("version", out var versionProp))
{
return versionProp.GetString();
}
}
}
return null;
}
}

48
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/ChromeDetector.cs

@ -0,0 +1,48 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class ChromeDetector : SoftwareDetector
{
public override string Name => "Chrome";
public async override Task<SoftwareInfo?> DetectAsync()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var chromePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Google", "Chrome", "Application", "chrome.exe");
if (File.Exists(chromePath))
{
return new SoftwareInfo(Name, GetFileVersion(chromePath), null, SoftwareType.Browser);
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var chromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
if (File.Exists(chromePath))
{
var version = await ExecuteCommandAsync(chromePath, "--version");
return new SoftwareInfo(Name, version, null, SoftwareType.Browser);
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var chromePath = "/usr/bin/google-chrome";
if (File.Exists(chromePath))
{
var version = await ExecuteCommandAsync(chromePath, "--version");
return new SoftwareInfo(Name, version, null, SoftwareType.Browser);
}
}
return null;
}
}

17
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/DotnetSdkDetector.cs

@ -0,0 +1,17 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class DotnetSdkDetector : SoftwareDetector
{
public override string Name => "DotnetSdk";
public async override Task<SoftwareInfo?> DetectAsync()
{
return new SoftwareInfo(Name, Environment.Version.ToString(), null, SoftwareType.DotnetSdk);
}
}

45
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/FireFoxDetector.cs

@ -0,0 +1,45 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class FireFoxDetector : SoftwareDetector
{
public override string Name => "Firefox";
public async override Task<SoftwareInfo?> DetectAsync()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var firefoxPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Mozilla Firefox", "firefox.exe");
if (File.Exists(firefoxPath))
{
return new SoftwareInfo(Name, GetFileVersion(firefoxPath), null, SoftwareType.Browser);
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var firefoxPath = "/Applications/Firefox.app/Contents/MacOS/firefox";
if (File.Exists(firefoxPath))
{
var version = await ExecuteCommandAsync(firefoxPath, "--version");
return new SoftwareInfo(Name, version, null, SoftwareType.Browser);
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var firefoxPath = "/usr/bin/firefox";
if (File.Exists(firefoxPath))
{
var version = await ExecuteCommandAsync(firefoxPath, "--version");
return new SoftwareInfo(Name, version, null, SoftwareType.Browser);
}
}
return null;
}
}

47
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/MsEdgeDetector.cs

@ -0,0 +1,47 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class MsEdgeDetector : SoftwareDetector
{
public override string Name => "MsEdge";
public async override Task<SoftwareInfo?> DetectAsync()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var firefoxPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
"Microsoft", "Edge", "Application", "msedge.exe");
if (File.Exists(firefoxPath))
{
return new SoftwareInfo(Name, GetFileVersion(firefoxPath), null, SoftwareType.Browser);
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var edgePath = "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge";
if (File.Exists(edgePath))
{
var version = await ExecuteCommandAsync(edgePath, "--version");
return new SoftwareInfo(Name, version, null, SoftwareType.Browser);
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var edgePath = "/usr/bin/microsoft-edge";
if (File.Exists(edgePath))
{
var version = await ExecuteCommandAsync(edgePath, "--version");
return new SoftwareInfo(Name, version, null, SoftwareType.Browser);
}
}
return null;
}
}

33
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/NodeJsDetector.cs

@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal class NodeJsDetector : SoftwareDetector
{
public override string Name => "Node.js";
public async override Task<SoftwareInfo?> DetectAsync()
{
try
{
var output = await ExecuteCommandAsync("node", "-v");
if (output.IsNullOrWhiteSpace())
{
return null;
}
var version = output.Trim().TrimStart('v');
return new SoftwareInfo(Name, version, uiTheme: null, SoftwareType.Others);
}
catch
{
return null;
}
}
}

77
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/OperatingSystemDetector.cs

@ -0,0 +1,77 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class OperatingSystemDetector : SoftwareDetector
{
public override string Name => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Windows" :
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "macOS" : "Linux";
public async override Task<SoftwareInfo?> DetectAsync()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return new SoftwareInfo(Name, Environment.OSVersion.Version.ToString(), null, SoftwareType.OperatingSystem);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var version = await ExecuteCommandAsync("sw_vers", "-productVersion");
return new SoftwareInfo(Name, version, GetMacUiTheme(), SoftwareType.OperatingSystem);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
var version = await ExecuteCommandAsync("lsb_release", "-ds") ?? await ExecuteCommandAsync("uname", "-r");
return new SoftwareInfo(Name, version, await GetLinuxUiTheme(), SoftwareType.OperatingSystem);
}
return null;
}
private async Task<string?> GetLinuxUiTheme()
{
var output = await ExecuteCommandAsync("gsettings", "get org.gnome.desktop.interface gtk-theme");
if (!output.IsNullOrWhiteSpace() && output.ToLowerInvariant().Contains("dark"))
{
return "Dark";
}
return "Light";
}
private string? GetMacUiTheme()
{
try
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = "defaults",
Arguments = "read -g AppleInterfaceStyle",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
process.Start();
var output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit();
return output == "Dark" ? "Dark" : "Light";
}
catch
{
return "Light";
}
}
}

100
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/RiderDetector.cs

@ -0,0 +1,100 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Xml.Linq;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class RiderDetector : SoftwareDetector
{
public override string Name => "Rider";
public override Task<SoftwareInfo?> DetectAsync()
{
try
{
string baseConfigDir;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
baseConfigDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"JetBrains");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
baseConfigDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"Library", "Application Support", "JetBrains");
}
else
{
baseConfigDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
".config", "JetBrains");
}
if (!Directory.Exists(baseConfigDir))
{
return Task.FromResult<SoftwareInfo?>(null);
}
var riderDirs = Directory
.GetDirectories(baseConfigDir, "Rider*")
.Select(dir =>
{
var name = Path.GetFileName(dir);
var verStr = name.Substring("Rider".Length);
return Version.TryParse(verStr, out var v)
? (Path: dir, Version: v)
: (Path: null, Version: null);
})
.Where(x => x.Path != null)
.ToList();
if (!riderDirs.Any())
{
return Task.FromResult<SoftwareInfo?>(null);
}
var latest = riderDirs
.OrderByDescending(x => x.Version)
.First();
var theme = string.Empty;
var colorsFile = Path.Combine(latest.Path!, "options", "colors.scheme.xml");
if (File.Exists(colorsFile))
{
try
{
var doc = XDocument.Load(colorsFile);
var schemeEl = doc
.Descendants("global_color_scheme")
.FirstOrDefault();
var schemeName = schemeEl?.Attribute("name")?.Value;
if (!schemeName.IsNullOrEmpty())
{
theme = schemeName.IndexOf("dark", StringComparison.OrdinalIgnoreCase) >= 0
? "Dark"
: "Light";
}
}
catch
{
//ignored
}
}
return Task.FromResult<SoftwareInfo?>(new SoftwareInfo(Name, latest.Version?.ToString(), theme,
SoftwareType.Ide));
}
catch (Exception e)
{
return Task.FromResult<SoftwareInfo?>(null);
}
}
}

132
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/VisualStudioCodeDetector.cs

@ -0,0 +1,132 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading.Tasks;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class VisualStudioCodeDetector : SoftwareDetector
{
public override string Name => "Visual Studio Code";
public async override Task<SoftwareInfo?> DetectAsync()
{
string? installDir = null;
string? settingsPath = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var progFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
var candidates = new[]
{
Path.Combine(localAppData, "Programs", "Microsoft VS Code"),
Path.Combine(progFiles, "Microsoft VS Code")
};
installDir = candidates.FirstOrDefault(Directory.Exists);
settingsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"Code", "User", "globalStorage" ,"storage.json"
);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
var app = "/Applications/Visual Studio Code.app";
if (Directory.Exists(app))
{
installDir = app;
}
settingsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
"Library", "Application Support", "Code", "User", "globalStorage", "storage.json"
);
}
else
{
var candidate = "/usr/share/code";
if (Directory.Exists(candidate))
{
installDir = candidate;
}
settingsPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
".config", "Code", "User", "globalStorage", "storage.json"
);
}
if (installDir == null)
{
return null;
}
Version? version = null;
var productJson = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
? Path.Combine(installDir, "Contents", "Resources", "app", "product.json")
: Path.Combine(installDir, "resources", "app", "product.json");
if (File.Exists(productJson))
{
try
{
using var jsonDoc = JsonDocument.Parse(File.ReadAllText(productJson));
var root = jsonDoc.RootElement;
if (root.TryGetProperty("version", out var versionProp))
{
var versionStr = versionProp.GetString();
if (Version.TryParse(versionStr, out var v))
{
version = v;
}
}
}
catch
{
}
}
if (version == null)
{
return null;
}
var theme = "Unknown";
if (File.Exists(settingsPath))
{
try
{
using var json = JsonDocument.Parse( File.ReadAllText(settingsPath));
var root = json.RootElement;
if (root.TryGetProperty("theme", out var themeProp))
{
var themeName = themeProp.GetString() ?? "";
if (themeName.IndexOf("dark", StringComparison.OrdinalIgnoreCase) >= 0)
{
theme = "Dark";
}
else if (themeName.IndexOf("light", StringComparison.OrdinalIgnoreCase) >= 0)
{
theme = "Light";
}
}
}
catch
{
// ignored
}
}
return new SoftwareInfo(Name, version?.ToString(), theme, SoftwareType.Ide);
}
}

106
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Detectors/VisualStudioDetector.cs

@ -0,0 +1,106 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using Volo.Abp.Internal.Telemetry.Constants.Enums;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Core;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Detectors;
internal sealed class VisualStudioDetector : SoftwareDetector
{
public override string Name => "Visual Studio";
public override Task<SoftwareInfo?> DetectAsync()
{
var version = GetVisualStudioVersionViaVsWhere();
var theme = GetVisualStudioTheme();
if (version == null)
{
return Task.FromResult<SoftwareInfo?>(null);
}
return Task.FromResult<SoftwareInfo?>(new SoftwareInfo(
name: Name,
version: version,
uiTheme: theme,
softwareType: SoftwareType.Ide));
}
private string? GetVisualStudioVersionViaVsWhere()
{
var vswherePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
"Microsoft Visual Studio",
"Installer",
"vswhere.exe");
if (!File.Exists(vswherePath))
{
return null;
}
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = vswherePath,
Arguments = "-latest -property catalog_productDisplayVersion",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
var output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit();
return string.IsNullOrWhiteSpace(output) ? null : output;
}
private string? GetVisualStudioTheme()
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var vsSettingsDir = Path.Combine(localAppData, "Microsoft", "VisualStudio");
if (!Directory.Exists(vsSettingsDir))
{
return null;
}
var settingsPath = Directory.GetFiles(vsSettingsDir, "CurrentSettings*.vssettings", SearchOption.AllDirectories)
.OrderByDescending(File.GetLastWriteTime)
.FirstOrDefault();
if (string.IsNullOrEmpty(settingsPath))
{
return null;
}
try
{
var doc = XDocument.Load(settingsPath);
var themeId = doc.Descendants("Theme")
.FirstOrDefault()?.Attribute("Id")?.Value;
return themeId?.ToUpperInvariant() switch
{
"{1DED0138-47CE-435E-84EF-9EC1F439B749}" => "Dark",
"{DE3DBBCD-F642-433C-8353-8F1DF4370ABA}" => "Light",
"{2DED0138-47CE-435E-84EF-9EC1F439B749}" => "Blue",
_ => "Unknown"
};
}
catch
{
return null;
}
}
}

40
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/EnvironmentInspection/Providers/SoftwareInfoProvider.cs

@ -0,0 +1,40 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.EnvironmentInspection.Contracts;
namespace Volo.Abp.Internal.Telemetry.EnvironmentInspection.Providers;
internal class SoftwareInfoProvider : ISoftwareInfoProvider , ISingletonDependency
{
private readonly IEnumerable<ISoftwareDetector> _softwareDetectors;
public SoftwareInfoProvider(IEnumerable<ISoftwareDetector> softwareDetectors)
{
_softwareDetectors = softwareDetectors;
}
public async Task<List<SoftwareInfo>> GetSoftwareInfoAsync()
{
var result = new List<SoftwareInfo>();
foreach (var softwareDetector in _softwareDetectors)
{
try
{
var softwareInfo = await softwareDetector.DetectAsync();
if (softwareInfo is not null)
{
result.Add(softwareInfo);
}
}
catch
{
//ignored
}
}
return result;
}
}

135
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/AbpPackageMetadataReader.cs

@ -0,0 +1,135 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
namespace Volo.Abp.Internal.Telemetry.Helpers;
static internal class AbpProjectMetadataReader
{
private const string AbpPackageSearchPattern = "*.abppkg";
private const string AbpSolutionSearchPattern = "*.abpsln";
private const int MaxDepth = 5;
public static AbpProjectMetaData? ReadProjectMetadata(Assembly assembly)
{
var assemblyPath = assembly.Location;
try
{
var projectDirectory = Path.GetDirectoryName(assemblyPath);
if (projectDirectory == null)
{
return null;
}
var abpPackagePath = FindFileUpwards(projectDirectory, AbpPackageSearchPattern);
if (abpPackagePath.IsNullOrEmpty())
{
return null;
}
var projectMetaData = ReadOrCreateMetadata(abpPackagePath);
var abpSolutionPath = FindFileUpwards(projectDirectory, AbpSolutionSearchPattern);
if (!abpSolutionPath.IsNullOrEmpty())
{
projectMetaData.AbpSlnPath = abpSolutionPath;
}
return projectMetaData;
}
catch
{
return null;
}
}
private static AbpProjectMetaData ReadOrCreateMetadata(string packagePath)
{
var fileContent = File.ReadAllText(packagePath);
var metadata = new AbpProjectMetaData();
using var document = JsonDocument.Parse(fileContent);
var root = document.RootElement;
if (TryGetProjectId(root,out var projectId))
{
metadata.ProjectId = projectId;
}
else
{
metadata.ProjectId = Guid.NewGuid();
WriteProjectIdToPackageFile(root, packagePath, metadata.ProjectId.Value);
}
if (root.TryGetProperty("role", out var roleElement) &&
roleElement.ValueKind == JsonValueKind.String)
{
metadata.Role = roleElement.GetString()!;
}
return metadata;
}
private static void WriteProjectIdToPackageFile(JsonElement root, string packagePath, Guid projectId)
{
using var stream = new MemoryStream();
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }))
{
writer.WriteStartObject();
if (root.ValueKind == JsonValueKind.Object)
{
foreach (var property in root.EnumerateObject())
{
if (property.Name != "projectId")
{
property.WriteTo(writer);
}
}
}
writer.WriteString("projectId", projectId.ToString());
writer.WriteEndObject();
}
var json = Encoding.UTF8.GetString(stream.ToArray());
File.WriteAllText(packagePath, json);
}
private static string? FindFileUpwards(string startingDir, string searchPattern)
{
var currentDir = new DirectoryInfo(startingDir);
var currentDepth = 0;
while (currentDir != null && currentDepth < MaxDepth)
{
var file = currentDir.GetFiles(searchPattern).FirstOrDefault();
if (file != null)
{
return file.FullName;
}
currentDir = currentDir.Parent;
currentDepth++;
}
return null;
}
private static bool TryGetProjectId(JsonElement element, out Guid projectId)
{
if (element.TryGetProperty("projectId", out var projectIdElement) &&
projectIdElement.ValueKind == JsonValueKind.String &&
Guid.TryParse(projectIdElement.GetString(), out projectId))
{
return true;
}
projectId = Guid.Empty;
return false;
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/AbpProjectMetaData.cs

@ -0,0 +1,10 @@
using System;
namespace Volo.Abp.Internal.Telemetry.Helpers;
internal class AbpProjectMetaData
{
public Guid? ProjectId { get; set; }
public string? Role { get; set; }
public string? AbpSlnPath { get; set; }
}

42
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/Cryptography.cs

@ -0,0 +1,42 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace Volo.Abp.Internal.Telemetry.Helpers;
static internal class Cryptography
{
private const string EncryptionKey = "AbpTelemetryStorageKey";
public static string Encrypt(string plainText)
{
Check.NotNullOrEmpty(plainText, nameof(plainText));
using var aes = Aes.Create();
using var sha256 = SHA256.Create();
aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(EncryptionKey));
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
var encryptor = aes.CreateEncryptor();
var inputBytes = Encoding.UTF8.GetBytes(plainText);
var encryptedBytes = encryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
return Convert.ToBase64String(encryptedBytes);
}
public static string Decrypt(string cipherText)
{
Check.NotNullOrEmpty(cipherText, nameof(cipherText));
using var aes = Aes.Create();
using var sha256 = SHA256.Create();
aes.Key = sha256.ComputeHash(Encoding.UTF8.GetBytes(EncryptionKey));
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.PKCS7;
var decryptor = aes.CreateDecryptor();
var inputBytes = Convert.FromBase64String(cipherText);
var decryptedBytes = decryptor.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
return Encoding.UTF8.GetString(decryptedBytes);
}
}

46
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/Helpers/MutexExecutor.cs

@ -0,0 +1,46 @@
using System;
using System.IO;
using System.Threading;
namespace Volo.Abp.Internal.Telemetry.Helpers;
static internal class MutexExecutor
{
private const string MutexName = "Global\\MyFileReadMutex";
private const int TimeoutMilliseconds = 3000;
public static string? ReadFileSafely(string filePath)
{
using var mutex = new Mutex(false, MutexName);
if (!mutex.WaitOne(TimeoutMilliseconds))
{
return null;
}
try
{
if (!File.Exists(filePath))
{
return null;
}
return File.ReadAllText(filePath);
}
catch (IOException)
{
return null;
}
finally
{
try
{
mutex.ReleaseMutex();
}
catch
{
// Already released or abandoned
}
}
}
}

8
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/ITelemetryActivitySender.cs

@ -0,0 +1,8 @@
using System.Threading.Tasks;
namespace Volo.Abp.Internal.Telemetry;
public interface ITelemetryActivitySender
{
Task TrySendQueuedActivitiesAsync();
}

13
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/ITelemetryService.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.Internal.Telemetry;
public interface ITelemetryService
{
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);
}

131
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryActivitySender.cs

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
namespace Volo.Abp.Internal.Telemetry;
public class TelemetryActivitySender : ITelemetryActivitySender, IScopedDependency
{
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
private readonly ILogger<TelemetryActivitySender> _logger;
private const int ActivitySendBatchSize = 50;
private const int MaxRetryAttempts = 3;
private const int RetryDelayMilliseconds = 1000;
public TelemetryActivitySender(ITelemetryActivityStorage telemetryActivityStorage, ILogger<TelemetryActivitySender> logger)
{
_telemetryActivityStorage = telemetryActivityStorage;
_logger = logger;
}
public async Task TrySendQueuedActivitiesAsync()
{
if (!_telemetryActivityStorage.ShouldSendActivities())
{
return;
}
await SendActivitiesAsync();
}
private async Task SendActivitiesAsync()
{
try
{
var activities = _telemetryActivityStorage.GetActivities();
var batches = CreateActivityBatches(activities);
using var httpClient = new HttpClient();
ConfigureHttpClientAuthentication(httpClient);
foreach (var batch in batches)
{
var isSuccessful = await TrySendBatchWithRetriesAsync(httpClient, batch);
if (!isSuccessful)
{
break;
}
}
}
catch
{
//ignored
}
}
private async Task<bool> TrySendBatchWithRetriesAsync(HttpClient httpClient, ActivityEvent[] activities)
{
var currentAttempt = 0;
while (currentAttempt < MaxRetryAttempts)
{
try
{
var response = await httpClient.PostAsync($"{AbpPlatformUrls.AbpTelemetryApiUrl}api/telemetry/collect", new StringContent(JsonSerializer.Serialize(activities), Encoding.UTF8, "application/json"));
if (response.IsSuccessStatusCode)
{
_telemetryActivityStorage.DeleteActivities(activities);
}
else
{
_logger.LogWithLevel(LogLevel.Trace,
$"Failed to send telemetry activities. Status code: {response.StatusCode}, Reason: {response.ReasonPhrase}");
_telemetryActivityStorage.MarkActivitiesAsFailed(activities);
}
return true;
}
catch (Exception ex)
{
_logger.LogWithLevel(LogLevel.Trace, $"Error sending telemetry activities: {ex.Message}");
currentAttempt++;
await Task.Delay(currentAttempt * RetryDelayMilliseconds);
}
}
_logger.LogWithLevel(LogLevel.Trace, "Max retries reached. Failed to send telemetry activities.");
return false;
}
private static IEnumerable<ActivityEvent[]> CreateActivityBatches(List<ActivityEvent> activities)
{
return activities
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / ActivitySendBatchSize)
.Select(x => x.Select(v => v.Value).ToArray());
}
private static void ConfigureHttpClientAuthentication(HttpClient httpClient)
{
if (!File.Exists(TelemetryPaths.AccessToken))
{
return;
}
var accessToken = File.ReadAllText(TelemetryPaths.AccessToken, Encoding.UTF8);
if (accessToken.IsNullOrEmpty())
{
return;
}
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
}

101
framework/src/Volo.Abp.Core/Volo/Abp/Internal/Telemetry/TelemetryService.cs

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Constants;
using ActivityContext = Volo.Abp.Internal.Telemetry.Activity.ActivityContext;
namespace Volo.Abp.Internal.Telemetry;
public class TelemetryService : ITelemetryService, IScopedDependency
{
private readonly ITelemetryActivitySender _telemetryActivitySender;
private readonly ITelemetryActivityEventBuilder _telemetryActivityEventBuilder;
private readonly ITelemetryActivityStorage _telemetryActivityStorage;
public TelemetryService(ITelemetryActivitySender telemetryActivitySender,
ITelemetryActivityEventBuilder telemetryActivityEventBuilder,
ITelemetryActivityStorage telemetryActivityStorage)
{
_telemetryActivitySender = telemetryActivitySender;
_telemetryActivityEventBuilder = telemetryActivityEventBuilder;
_telemetryActivityStorage = telemetryActivityStorage;
}
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, additionalProperties: additionalProperties);
return new AsyncDisposeFunc(async () =>
{
stopwatch.Stop();
context.Current[ActivityPropertyNames.ActivityDuration] = stopwatch.ElapsedMilliseconds;
await AddActivityAsync(context);
});
}
public async Task AddActivityAsync(string activityName,
Action<Dictionary<string, object>>? additionalProperties = null)
{
Check.NotNullOrEmpty(activityName, nameof(activityName));
var context = ActivityContext.Create(activityName, additionalProperties: additionalProperties);
await AddActivityAsync(context);
}
public async Task AddErrorActivityAsync(Action<Dictionary<string, object>> additionalProperties)
{
var context = ActivityContext.Create(ActivityNameConsts.Error, additionalProperties: additionalProperties);
await AddActivityAsync(context);
}
public async Task AddErrorActivityAsync(string errorMessage)
{
var context = ActivityContext.Create(ActivityNameConsts.Error, errorMessage);
await AddActivityAsync(context);
}
public async Task AddErrorForActivityAsync(string failingActivity, string errorMessage)
{
Check.NotNullOrEmpty(failingActivity, nameof(failingActivity));
var context = ActivityContext.Create(ActivityNameConsts.Error, errorMessage, configure =>
{
configure[ActivityPropertyNames.FailingActivity] = failingActivity;
});
await AddActivityAsync(context);
}
private Task AddActivityAsync(ActivityContext context)
{
_ = Task.Run(async () =>
{
await BuildAndSendActivityAsync(context);
});
return Task.CompletedTask;
}
private async Task BuildAndSendActivityAsync(ActivityContext context)
{
try
{
var activityEvent = await _telemetryActivityEventBuilder.BuildAsync(context);
if (activityEvent is null)
{
return;
}
_telemetryActivityStorage.SaveActivity(activityEvent);
await _telemetryActivitySender.TrySendQueuedActivitiesAsync();
}
catch
{
//ignored
}
}
}

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

@ -0,0 +1,41 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Internal.Telemetry.Activity;
using Volo.Abp.Internal.Telemetry.Activity.Contracts;
using Volo.Abp.Internal.Telemetry.Activity.Providers;
using Volo.Abp.Internal.Telemetry.Constants;
using Volo.Abp.Reflection;
namespace Volo.Abp.Domain.Telemetry;
[ExposeServices(typeof(ITelemetryActivityEventEnricher), typeof(IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>))]
public class TelemetryDomainInfoEnricher : TelemetryActivityEventEnricher, IHasParentTelemetryActivityEventEnricher<TelemetryApplicationInfoEnricher>
{
private readonly ITypeFinder _typeFinder;
public TelemetryDomainInfoEnricher(ITypeFinder typeFinder, IServiceProvider serviceProvider)
: base(serviceProvider)
{
_typeFinder = typeFinder;
}
protected override Task<bool> CanExecuteAsync(ActivityContext context)
{
return Task.FromResult(context.ProjectId.HasValue);
}
protected override Task ExecuteAsync(ActivityContext context)
{
var entityCount = _typeFinder.Types.Count(t =>
typeof(IEntity).IsAssignableFrom(t) && !t.IsAbstract &&
!t.AssemblyQualifiedName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter));
context.Current[ActivityPropertyNames.EntityCount] = entityCount;
return Task.CompletedTask;
}
}

6
framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/AuthorizationTestPermissionDefinitionProvider.cs

@ -15,8 +15,12 @@ public class AuthorizationTestPermissionDefinitionProvider : PermissionDefinitio
var group = context.AddGroup("TestGroup"); var group = context.AddGroup("TestGroup");
group.AddPermission("MyAuthorizedService1"); group[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName].ShouldBe(typeof(AuthorizationTestPermissionDefinitionProvider).FullName);
var permission1 = group.AddPermission("MyAuthorizedService1");
permission1[PermissionDefinitionContext.KnownPropertyNames.CurrentProviderName].ShouldBe(typeof(AuthorizationTestPermissionDefinitionProvider).FullName);
group.AddPermission("MyPermission1").StateCheckers.Add(new TestRequireEditionPermissionSimpleStateChecker()); group.AddPermission("MyPermission1").StateCheckers.Add(new TestRequireEditionPermissionSimpleStateChecker());
group.AddPermission("MyPermission2"); group.AddPermission("MyPermission2");
group.AddPermission("MyPermission3"); group.AddPermission("MyPermission3");

89
framework/test/Volo.Abp.Core.Tests/Volo/Abp/Telemetry/ActivityEvent_Tests.cs

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using Shouldly;
using Volo.Abp.Internal.Telemetry.Activity;
using Volo.Abp.Internal.Telemetry.Constants;
using Xunit;
namespace Volo.Abp.Telemetry;
public class ActivityEvent_Tests
{
[Fact]
public void Should_Create_ActivityEvent_With_Required_Parameters()
{
// Arrange
var activityName = "TestActivity";
var details = "Test Details";
// Act
var activityEvent = new ActivityEvent(activityName, details);
// Assert
activityEvent[ActivityPropertyNames.ActivityName].ShouldBe(activityName);
activityEvent[ActivityPropertyNames.ActivityDetails].ShouldBe(details);
activityEvent[ActivityPropertyNames.Id].ShouldNotBe(Guid.Empty);
activityEvent[ActivityPropertyNames.Time].ShouldNotBe(default);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void Should_Throw_Exception_When_ActivityName_Is_Invalid(string invalidName)
{
// Act & Assert
Should.Throw<ArgumentException>(() =>
new ActivityEvent(invalidName));
}
[Fact]
public void Should_Set_And_Get_AdditionalProperties()
{
// Arrange
var activityEvent = new ActivityEvent("TestActivity");
var additionalProps = new Dictionary<string, object>
{
{ "key1", "value1" },
{ "key2", 42 }
};
// Act
activityEvent[ActivityPropertyNames.AdditionalProperties] = additionalProps;
// Assert
var activityAdditionalProps = activityEvent[ActivityPropertyNames.AdditionalProperties] as Dictionary<string, object>;
activityAdditionalProps.ShouldNotBeNull();
activityAdditionalProps.Count.ShouldBe(2);
activityAdditionalProps["key1"].ShouldBe("value1");
activityAdditionalProps["key2"].ShouldBe(42);
}
[Fact]
public void Should_Return_Default_Values_When_Properties_Not_Set()
{
// Arrange
var activityEvent = new ActivityEvent("TestActivity");
// Assert
activityEvent[ActivityPropertyNames.ActivityDetails].ShouldBeNull();
}
[Fact]
public void Should_Behave_Like_Dictionary()
{
// Arrange
var activityEvent = new ActivityEvent("TestActivity");
// Act
activityEvent["CustomKey"] = "CustomValue";
// Assert
activityEvent["CustomKey"].ShouldBe("CustomValue");
activityEvent.ContainsKey("CustomKey").ShouldBeTrue();
}
}

52
framework/test/Volo.Abp.Core.Tests/Volo/Abp/Telemetry/Cryptography_Tests.cs

@ -0,0 +1,52 @@
using System;
using Shouldly;
using Volo.Abp.Internal.Telemetry.Helpers;
using Xunit;
namespace Volo.Abp.Telemetry;
public class Cryptography_Tests
{
[Fact]
public void Should_Encrypt_And_Decrypt_Text_Successfully()
{
// Arrange
const string plainText = "Test message 123!";
// Act
var encryptedText = Cryptography.Encrypt(plainText);
var decryptedText = Cryptography.Decrypt(encryptedText);
// Assert
decryptedText.ShouldBe(plainText);
}
[Fact]
public void Should_Generate_Different_Encrypted_Values_For_Different_Inputs()
{
// Arrange
const string text1 = "Message 1";
const string text2 = "Message 2";
// Act
var encrypted1 = Cryptography.Encrypt(text1);
var encrypted2 = Cryptography.Encrypt(text2);
// Assert
encrypted1.ShouldNotBe(encrypted2);
}
[Fact]
public void Encrypt_Should_Throw_ArgumentException_When_Input_Is_Null()
{
// Act & Assert
Should.Throw<ArgumentException>(() => Cryptography.Encrypt(null));
}
[Fact]
public void Decrypt_Should_Throw_ArgumentException_When_Input_Is_Null()
{
// Act & Assert
Should.Throw<ArgumentException>(() => Cryptography.Decrypt(null));
}
}
Loading…
Cancel
Save