mirror of https://github.com/abpframework/abp.git
Browse Source
Removed telemetry-related contributors and session provider interfaces. Updated database schema, converting UserDeviceId and related fields to strings and adding an OS architecture field in UserDevices.issue-#6768
32 changed files with 1017 additions and 595 deletions
@ -1,35 +0,0 @@ |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
using System.Linq; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))] |
|||
public class TelemetryApplicationMetricsContributor : ITelemetryApplicationInfoContributor, ISingletonDependency |
|||
{ |
|||
public Task ContributeAsync(ActivityEvent activityEvent) |
|||
{ |
|||
if (activityEvent.TryGetValue(ActivityPropertyNames.Assembly, out var assemblyPath)) |
|||
{ |
|||
var assembly = Assembly.LoadFrom((string)assemblyPath); |
|||
var types = assembly.GetTypes(); |
|||
|
|||
var appServiceCount = types.Count(t => |
|||
typeof(IApplicationService).IsAssignableFrom(t) && |
|||
t is { IsAbstract: false, IsInterface: false }); |
|||
|
|||
var controllerCount = types.Count(t => |
|||
typeof(AbpController).IsAssignableFrom(t) && |
|||
!t.IsAbstract); |
|||
|
|||
activityEvent[ActivityPropertyNames.AppServiceCount] = appServiceCount; |
|||
activityEvent[ActivityPropertyNames.ControllerCount] = controllerCount; |
|||
} |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
using System.Linq; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
using Volo.Abp.Telemetry.Activity.Providers; |
|||
using Volo.Abp.Telemetry.Constants.Enums; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
[ExposeServices(typeof(ITelemetryActivityEventEnricher))] |
|||
public class TelemetryApplicationMetricsEnricher : ITelemetryActivityEventEnricher, IScopedDependency |
|||
{ |
|||
private readonly ITypeFinder _typeFinder; |
|||
|
|||
public TelemetryApplicationMetricsEnricher(ITypeFinder typeFinder) |
|||
{ |
|||
_typeFinder = typeFinder; |
|||
} |
|||
|
|||
public bool IsFirstRun => false; |
|||
public Type? DependsOn => typeof(TelemetryApplicationInfoEnricher); |
|||
public Task<bool> CanExecuteAsync(ActivityContext context) |
|||
{ |
|||
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime); |
|||
} |
|||
|
|||
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context) |
|||
{ |
|||
var appServiceCount = _typeFinder.Types.Count(t => |
|||
typeof(IApplicationService).IsAssignableFrom(t) && |
|||
t is { IsAbstract: false, IsInterface: false } && |
|||
!t.AssemblyQualifiedName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter)); |
|||
|
|||
var controllerCount = _typeFinder.Types.Count(t => |
|||
typeof(ControllerBase).IsAssignableFrom(t) && |
|||
!t.IsAbstract); |
|||
|
|||
|
|||
var items = new Dictionary<string, object>() |
|||
{ |
|||
{ActivityPropertyNames.AppServiceCount, appServiceCount}, |
|||
{ActivityPropertyNames.ControllerCount, controllerCount} |
|||
}; |
|||
return Task.FromResult<Dictionary<string, object>?>(items); |
|||
} |
|||
} |
|||
@ -1,24 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
|
|||
namespace Volo.Abp.Authorization.Permissions; |
|||
|
|||
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))] |
|||
public class TelemetryPermissionInfoContributor : ITelemetryApplicationInfoContributor, ISingletonDependency |
|||
{ |
|||
private readonly IPermissionDefinitionManager _permissionDefinitionManager; |
|||
|
|||
public TelemetryPermissionInfoContributor(IPermissionDefinitionManager permissionDefinitionManager) |
|||
{ |
|||
_permissionDefinitionManager = permissionDefinitionManager; |
|||
} |
|||
|
|||
public async Task ContributeAsync(ActivityEvent activityEvent) |
|||
{ |
|||
var permissions = await _permissionDefinitionManager.GetPermissionsAsync(); |
|||
activityEvent[ActivityPropertyNames.PermissionCount] = permissions.Count; |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
using Volo.Abp.Telemetry.Activity.Providers; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
|
|||
namespace Volo.Abp.Authorization.Permissions; |
|||
|
|||
[ExposeServices(typeof(ITelemetryActivityEventEnricher))] |
|||
public class TelemetryPermissionInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency |
|||
{ |
|||
private readonly IPermissionDefinitionManager _permissionDefinitionManager; |
|||
|
|||
public TelemetryPermissionInfoEnricher(IPermissionDefinitionManager permissionDefinitionManager) |
|||
{ |
|||
_permissionDefinitionManager = permissionDefinitionManager; |
|||
} |
|||
|
|||
public bool IsFirstRun => false; |
|||
public Type? DependsOn => typeof(TelemetryApplicationInfoEnricher); |
|||
|
|||
public Task<bool> CanExecuteAsync(ActivityContext context) |
|||
{ |
|||
return Task.FromResult(context.ProjectId.HasValue); |
|||
} |
|||
|
|||
public async Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context) |
|||
{ |
|||
var permissions = await _permissionDefinitionManager.GetPermissionsAsync(); |
|||
|
|||
var result = new Dictionary<string, object> |
|||
{ |
|||
{ ActivityPropertyNames.PermissionCount, permissions.Count } |
|||
}; |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
using Volo.Abp.Telemetry.Constants.Enums; |
|||
|
|||
namespace Volo.Abp.Telemetry.Activity; |
|||
|
|||
public class ActivityContext |
|||
{ |
|||
private ActivityContext(ActivityEvent current) |
|||
{ |
|||
Current = current; |
|||
} |
|||
|
|||
public ActivityEvent Current { get; } |
|||
public Dictionary<string, object> ExtraProperties { get; } = new(); |
|||
public bool IsTerminated { get; private set; } |
|||
public bool IsCancelled { get; private set; } |
|||
|
|||
public CancellationToken CancellationToken { get; set; } |
|||
|
|||
|
|||
public static ActivityContext Create(string activityName, string? details = null, |
|||
Action<Dictionary<string, object>>? additionalProperties = null) |
|||
{ |
|||
var activity = new ActivityEvent(activityName, details); |
|||
if (additionalProperties is not null) |
|||
{ |
|||
activity.AdditionalProperties = new Dictionary<string, object>(); |
|||
additionalProperties.Invoke(activity.AdditionalProperties); |
|||
} |
|||
|
|||
return new ActivityContext(activity); |
|||
} |
|||
|
|||
public Guid? ProjectId { |
|||
get { |
|||
if (!Current.TryGetValue(ActivityPropertyNames.ProjectId, out var projectId)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
if (Guid.TryParse(projectId.ToString(), out var projectIdGuid)) |
|||
{ |
|||
return projectIdGuid; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public Guid? SolutionId { |
|||
get { |
|||
if (!Current.TryGetValue(ActivityPropertyNames.SolutionId, out var solutionId)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
if (Guid.TryParse(solutionId!.ToString(), out var solutionIdGuid)) |
|||
{ |
|||
return solutionIdGuid; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public SessionType? SessionType { |
|||
get { |
|||
if (Current.TryGetValue(ActivityPropertyNames.SessionType, out var sessionTypeObj) && |
|||
Enum.TryParse<SessionType>(sessionTypeObj?.ToString(), out var sessionType)) |
|||
{ |
|||
return sessionType; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public string? SolutionPath => ExtraProperties.TryGetValue(ActivityPropertyNames.SolutionPath, out var solutionPath) |
|||
? solutionPath?.ToString() |
|||
: null; |
|||
|
|||
|
|||
public string? DeviceId => Current.TryGetValue(ActivityPropertyNames.DeviceId, out var deviceId) ? deviceId?.ToString() : null; |
|||
|
|||
public void Terminate() |
|||
{ |
|||
IsTerminated = true; |
|||
} |
|||
|
|||
public void Cancel() |
|||
{ |
|||
IsCancelled = true; |
|||
} |
|||
|
|||
public void ResetCancel() |
|||
{ |
|||
IsCancelled = false; |
|||
} |
|||
} |
|||
@ -1,8 +1,14 @@ |
|||
using System.Threading.Tasks; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Telemetry.Activity.Contracts; |
|||
|
|||
public interface ITelemetryActivityEventEnricher |
|||
{ |
|||
Task EnrichAsync(ActivityEvent activity); |
|||
bool IsFirstRun { get; } |
|||
Type? DependsOn { get; } |
|||
Task<bool> CanExecuteAsync(ActivityContext context); |
|||
Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context); |
|||
|
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
|
|||
namespace Volo.Abp.Telemetry.Activity.Providers; |
|||
|
|||
public class EnricherNode |
|||
{ |
|||
public ITelemetryActivityEventEnricher Enricher { get; set; } |
|||
public List<EnricherNode> Children { get; set; } = new(); |
|||
|
|||
public EnricherNode(ITelemetryActivityEventEnricher enricher) |
|||
{ |
|||
Enricher = enricher; |
|||
} |
|||
} |
|||
@ -1,33 +1,96 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
|
|||
namespace Volo.Abp.Telemetry.Activity.Providers; |
|||
|
|||
public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, ISingletonDependency |
|||
public class TelemetryActivityEventBuilder : ITelemetryActivityEventBuilder, IScopedDependency |
|||
{ |
|||
private readonly IServiceProvider _serviceProvider; |
|||
private readonly IEnumerable<ITelemetryActivityEventEnricher> _activityDataEnrichers; |
|||
|
|||
public TelemetryActivityEventBuilder(IServiceProvider serviceProvider) |
|||
public TelemetryActivityEventBuilder(IEnumerable<ITelemetryActivityEventEnricher> activityDataEnrichers) |
|||
{ |
|||
_serviceProvider = serviceProvider; |
|||
_activityDataEnrichers = activityDataEnrichers; |
|||
} |
|||
|
|||
public virtual async Task BuildAsync(ActivityEvent activity) |
|||
public virtual async Task<ActivityEvent?> BuildAsync(ActivityContext context) |
|||
{ |
|||
|
|||
var sessionProvider = _serviceProvider.GetRequiredService<ITelemetrySessionProvider>(); |
|||
var enricherNodes = BuildTree(_activityDataEnrichers); |
|||
await ExecuteEnrichersAsync(enricherNodes, context); |
|||
|
|||
await sessionProvider.AddSessionInfoAsync(activity); |
|||
|
|||
var activityDataEnrichers = _serviceProvider.GetRequiredService<IEnumerable<ITelemetryActivityEventEnricher>>(); |
|||
if (context.IsTerminated) |
|||
{ |
|||
return null; |
|||
} |
|||
return context.Current; |
|||
} |
|||
|
|||
private async Task ExecuteEnrichersAsync( |
|||
IEnumerable<EnricherNode> nodes, |
|||
ActivityContext context) |
|||
{ |
|||
async Task ExecuteEnricherChainAsync(IEnumerable<EnricherNode> currentNodes) |
|||
{ |
|||
foreach (var node in currentNodes) |
|||
{ |
|||
if (context.IsTerminated) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var enricher = node.Enricher; |
|||
|
|||
if (!await enricher.CanExecuteAsync(context)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
foreach (var activityDataEnricher in activityDataEnrichers) |
|||
var enricherResult = await enricher.EnrichAsync(context); |
|||
|
|||
if (enricherResult != null) |
|||
{ |
|||
foreach (var kv in enricherResult) |
|||
{ |
|||
context.Current[kv.Key] = kv.Value; |
|||
} |
|||
} |
|||
|
|||
if (!context.IsCancelled) |
|||
{ |
|||
await ExecuteEnricherChainAsync(node.Children); |
|||
} |
|||
|
|||
context.ResetCancel(); |
|||
} |
|||
} |
|||
|
|||
await ExecuteEnricherChainAsync(nodes); |
|||
} |
|||
|
|||
private static List<EnricherNode> BuildTree(IEnumerable<ITelemetryActivityEventEnricher> enrichers) |
|||
{ |
|||
var enricherList = enrichers.ToList(); |
|||
var nodeMap = enricherList.ToDictionary(e => e.GetType(), e => new EnricherNode(e)); |
|||
|
|||
foreach (var enricher in enricherList) |
|||
{ |
|||
await activityDataEnricher.EnrichAsync(activity); |
|||
if (enricher.DependsOn != null) |
|||
{ |
|||
if (nodeMap.TryGetValue(enricher.DependsOn, out var parentNode)) |
|||
{ |
|||
parentNode.Children.Add(nodeMap[enricher.GetType()]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
var roots = enricherList |
|||
.Where(e => e.DependsOn == null) |
|||
.OrderByDescending(e => e.IsFirstRun) |
|||
.Select(e => nodeMap[e.GetType()]) |
|||
.ToList(); |
|||
|
|||
return roots; |
|||
} |
|||
} |
|||
} |
|||
@ -1,74 +1,108 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Reflection; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
using Volo.Abp.Telemetry.Constants.Enums; |
|||
using Volo.Abp.Telemetry.Helpers; |
|||
|
|||
namespace Volo.Abp.Telemetry.Activity.Providers; |
|||
|
|||
[ExposeServices(typeof(ITelemetryActivityEventEnricher))] |
|||
public class TelemetryApplicationInfoEnricher : ITelemetryActivityEventEnricher, ISingletonDependency |
|||
public class TelemetryApplicationInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency |
|||
{ |
|||
private readonly IEnumerable<ITelemetryApplicationInfoContributor> _telemetryApplicationInfoContributors; |
|||
private readonly ITelemetryActivityStorage _telemetryActivityStorage; |
|||
|
|||
public TelemetryApplicationInfoEnricher( |
|||
IEnumerable<ITelemetryApplicationInfoContributor> telemetryApplicationInfoContributors, |
|||
ITelemetryActivityStorage telemetryActivityStorage) |
|||
{ |
|||
_telemetryApplicationInfoContributors = telemetryApplicationInfoContributors; |
|||
_telemetryActivityStorage = telemetryActivityStorage; |
|||
} |
|||
|
|||
public async Task EnrichAsync(ActivityEvent activity) |
|||
public bool IsFirstRun => true; |
|||
public Type DependsOn => typeof(TelemetrySessionInfoEnricher); |
|||
|
|||
public Task<bool> CanExecuteAsync(ActivityContext context) |
|||
{ |
|||
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime); |
|||
} |
|||
|
|||
public async Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context) |
|||
{ |
|||
try |
|||
{ |
|||
if (!IsValidActivity(activity)) |
|||
var entryAssembly = Assembly.GetEntryAssembly(); |
|||
if (entryAssembly is null) |
|||
{ |
|||
context.Terminate(); |
|||
return null; |
|||
} |
|||
|
|||
var projectMetaData = AbpProjectMetadataReader.ReadProjectMetadata(entryAssembly); |
|||
if (projectMetaData?.ProjectId == null || projectMetaData.AbpSlnPath.IsNullOrEmpty()) |
|||
{ |
|||
return; |
|||
context.Terminate(); |
|||
return null; |
|||
} |
|||
|
|||
if (!TryGetProjectId(activity, out var projectId) && !await _telemetryActivityStorage.ShouldAddProjectInfoAsync(projectId)) |
|||
if (!await _telemetryActivityStorage.ShouldAddProjectInfoAsync(projectMetaData.ProjectId.Value)) |
|||
{ |
|||
return; |
|||
context.Cancel(); |
|||
return null; |
|||
} |
|||
|
|||
foreach (var contributor in _telemetryApplicationInfoContributors) |
|||
context.ExtraProperties[ActivityPropertyNames.SolutionPath] = projectMetaData.AbpSlnPath; |
|||
|
|||
var solutionId = ReadSolutionIdFromSolutionPath(projectMetaData.AbpSlnPath); |
|||
|
|||
if (!solutionId.HasValue) |
|||
{ |
|||
await contributor.ContributeAsync(activity); |
|||
context.Terminate(); |
|||
return null; |
|||
} |
|||
|
|||
var result = new Dictionary<string, object> |
|||
{ |
|||
{ ActivityPropertyNames.SolutionId, solutionId }, |
|||
{ ActivityPropertyNames.ProjectId, projectMetaData.ProjectId.Value }, |
|||
{ ActivityPropertyNames.ProjectType, projectMetaData.Role ?? string.Empty }, |
|||
}; |
|||
|
|||
activity.Remove(ActivityPropertyNames.Assembly); |
|||
activity[ActivityPropertyNames.HasProjectInfo] = true; |
|||
await _telemetryActivityStorage.MarkApplicationInfoAsAddedAsync(projectId); |
|||
result[ActivityPropertyNames.HasProjectInfo] = true; |
|||
return result; |
|||
|
|||
} |
|||
catch |
|||
{ |
|||
// ignored
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
private bool IsValidActivity(ActivityEvent activity) |
|||
{ |
|||
return activity.ContainsKey(ActivityPropertyNames.Assembly) |
|||
&& activity.TryGetValue(ActivityPropertyNames.SessionType, out var sessionTypeObj) |
|||
&& Enum.TryParse<SessionType>(sessionTypeObj?.ToString(), out var parsed) |
|||
&& parsed == SessionType.ApplicationRuntime; |
|||
} |
|||
|
|||
private static bool TryGetProjectId(ActivityEvent activity, out Guid projectId) |
|||
private static Guid? ReadSolutionIdFromSolutionPath(string solutionPath) |
|||
{ |
|||
if (!activity.TryGetValue(ActivityPropertyNames.ProjectId, out var projectIdObj) || |
|||
projectIdObj is not string projectIdStr || |
|||
!Guid.TryParse(projectIdStr, out projectId)) |
|||
try |
|||
{ |
|||
if (solutionPath.IsNullOrEmpty()) |
|||
{ |
|||
return null; |
|||
} |
|||
using var fs = new FileStream(solutionPath!, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); |
|||
using var doc = JsonDocument.Parse(fs); |
|||
if (doc.RootElement.TryGetProperty("id", out var id) && |
|||
Guid.TryParse(id.GetString(), out var solutionId)) |
|||
{ |
|||
return solutionId; |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
projectId = Guid.Empty; |
|||
return false; |
|||
// ignored
|
|||
} |
|||
|
|||
return true; |
|||
return null; |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
using Volo.Abp.Telemetry.Constants.Enums; |
|||
|
|||
namespace Volo.Abp.Telemetry.Activity.Providers; |
|||
|
|||
[ExposeServices(typeof(ITelemetryActivityEventEnricher))] |
|||
public class TelemetryModuleInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency |
|||
{ |
|||
|
|||
private readonly IModuleContainer _moduleContainer; |
|||
private readonly IAssemblyFinder _assemblyFinder; |
|||
public TelemetryModuleInfoEnricher(IModuleContainer moduleContainer, IAssemblyFinder assemblyFinder) |
|||
{ |
|||
_moduleContainer = moduleContainer; |
|||
_assemblyFinder = assemblyFinder; |
|||
} |
|||
|
|||
public bool IsFirstRun => false; |
|||
public Type? DependsOn => typeof(TelemetrySessionInfoEnricher); |
|||
public Task<bool> CanExecuteAsync(ActivityContext context) |
|||
{ |
|||
return Task.FromResult(context.SessionType == SessionType.ApplicationRuntime); |
|||
} |
|||
|
|||
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context) |
|||
{ |
|||
var result = new Dictionary<string, object> |
|||
{ |
|||
{ ActivityPropertyNames.ModuleCount, _moduleContainer.Modules.Count }, |
|||
{ ActivityPropertyNames.ProjectCount, _assemblyFinder.Assemblies.Count(x => !x.FullName.IsNullOrEmpty() && !x.FullName.StartsWith(TelemetryConsts.VoloNameSpaceFilter)) } |
|||
}; |
|||
|
|||
return Task.FromResult<Dictionary<string, object>?>(result); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
using Volo.Abp.Telemetry.Constants.Enums; |
|||
|
|||
namespace Volo.Abp.Telemetry.Activity.Providers; |
|||
[ExposeServices(typeof(ITelemetryActivityEventEnricher))] |
|||
public class TelemetrySessionInfoEnricher : ITelemetryActivityEventEnricher , IScopedDependency |
|||
{ |
|||
public bool IsFirstRun => true; |
|||
public Type? DependsOn => null; |
|||
public Task<bool> CanExecuteAsync(ActivityContext context) |
|||
{ |
|||
return Task.FromResult(true); |
|||
} |
|||
|
|||
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context) |
|||
{ |
|||
var result = new Dictionary<string, object> |
|||
{ |
|||
{ ActivityPropertyNames.SessionType, SessionType.ApplicationRuntime }, |
|||
{ ActivityPropertyNames.SessionId, Guid.NewGuid().ToString() }, |
|||
{ ActivityPropertyNames.IsFirstSession, !File.Exists(TelemetryPaths.ActivityStorage) } |
|||
}; |
|||
|
|||
return Task.FromResult<Dictionary<string, object>?>(result); |
|||
} |
|||
} |
|||
@ -0,0 +1,286 @@ |
|||
namespace Volo.Abp.Telemetry.Constants; |
|||
|
|||
public class DeviceKeyHelper |
|||
{ |
|||
public static string GetUniquePhysicalKey(bool shouldHash) |
|||
{ |
|||
char platformId = '?'; |
|||
char osArchitecture = '?'; |
|||
string operatingSystem = "?"; |
|||
|
|||
try |
|||
{ |
|||
string osPrefix; |
|||
string uniqueKey; |
|||
|
|||
platformId = GetPlatformIdOrDefault(); |
|||
osArchitecture = GetOsArchitectureOrDefault(); |
|||
|
|||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform |
|||
.Windows)) |
|||
{ |
|||
operatingSystem = "Windows"; |
|||
uniqueKey = GetUniqueKeyForWindows(); |
|||
osPrefix = "W"; |
|||
} |
|||
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices |
|||
.OSPlatform.Linux)) |
|||
{ |
|||
operatingSystem = "Linux"; |
|||
uniqueKey = GetHarddiskSerialForLinux(); |
|||
osPrefix = "L"; |
|||
} |
|||
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices |
|||
.OSPlatform.OSX)) //MAC
|
|||
{ |
|||
operatingSystem = "OSX"; |
|||
uniqueKey = GetHarddiskSerialForOsX(); |
|||
osPrefix = "O"; |
|||
} |
|||
else |
|||
{ |
|||
operatingSystem = "Other"; |
|||
uniqueKey = GetNetworkAdapterSerial(); |
|||
osPrefix = "X"; |
|||
} |
|||
|
|||
if (shouldHash) |
|||
{ |
|||
uniqueKey = ConvertToMd5(uniqueKey).ToUpperInvariant(); |
|||
} |
|||
|
|||
return osPrefix + platformId + osArchitecture + "-" + uniqueKey; |
|||
} |
|||
catch (System.Exception ex) |
|||
{ |
|||
System.Console.WriteLine("WARNING ABP-LIC-0025! Contact to license@abp.io with the below information" + |
|||
System.Environment.NewLine + |
|||
"* Architecture: " + |
|||
System.Runtime.InteropServices.RuntimeInformation.OSArchitecture + |
|||
System.Environment.NewLine + |
|||
"* Description: " + |
|||
System.Runtime.InteropServices.RuntimeInformation.OSDescription + |
|||
System.Environment.NewLine + |
|||
"* Platform Id: " + platformId + System.Environment.NewLine + |
|||
"* OS architecture: " + osArchitecture + System.Environment.NewLine + |
|||
"* Operating system: " + operatingSystem + System.Environment.NewLine + |
|||
"* Framework description: " + |
|||
System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription + |
|||
System.Environment.NewLine + |
|||
"* Error: " + ex.ToString()); |
|||
|
|||
return "95929008-b147-454a-8737-efed71fa2241"; |
|||
} |
|||
} |
|||
private static string GetNetworkAdapterSerial() |
|||
{ |
|||
string macAddress = string.Empty; |
|||
|
|||
var networkInterfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); |
|||
foreach (var networkInterface in networkInterfaces) |
|||
{ |
|||
if (networkInterface.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Loopback) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var physicalAddress = networkInterface.GetPhysicalAddress().ToString(); |
|||
if (string.IsNullOrEmpty(physicalAddress)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
macAddress = physicalAddress; |
|||
break; |
|||
} |
|||
|
|||
return macAddress!; |
|||
} |
|||
/// <returns>
|
|||
/// 0 - Win32S
|
|||
/// 1 - Win32Windows
|
|||
/// 2 - Win32NT
|
|||
/// 3 - WinCE
|
|||
/// 4 - Unix
|
|||
/// 5 - Xbox
|
|||
/// 6 - MacOSX
|
|||
/// </returns>
|
|||
private static char GetPlatformIdOrDefault(char defaultValue = '*') |
|||
{ |
|||
try |
|||
{ |
|||
return ((int)System.Environment.OSVersion.Platform).ToString()[0]; |
|||
} |
|||
catch |
|||
{ |
|||
return defaultValue; |
|||
} |
|||
} |
|||
private static string ConvertToMd5(string text) |
|||
{ |
|||
using (var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider()) |
|||
{ |
|||
return EncodeBase64(md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(text))); |
|||
} |
|||
} |
|||
|
|||
private static string EncodeBase64(byte[] ba) |
|||
{ |
|||
var hex = new System.Text.StringBuilder(ba.Length * 2); |
|||
|
|||
foreach (var b in ba) |
|||
{ |
|||
hex.AppendFormat("{0:x2}", b); |
|||
} |
|||
|
|||
return hex.ToString(); |
|||
} |
|||
|
|||
/// <returns>
|
|||
/// 0 - X86 (An Intel-based 32-bit processor architecture)
|
|||
/// 1 - X64 (A 64-bit ARM processor architecture)
|
|||
/// 2 - Arm (A 32-bit ARM processor architecture)
|
|||
/// 3 - Arm64 (A 64-bit ARM processor architecture)
|
|||
/// </returns>
|
|||
private static char GetOsArchitectureOrDefault(char defaultValue = '*') |
|||
{ |
|||
try |
|||
{ |
|||
return ((int)System.Runtime.InteropServices.RuntimeInformation.OSArchitecture).ToString()[0]; |
|||
} |
|||
catch |
|||
{ |
|||
return defaultValue; |
|||
} |
|||
} |
|||
|
|||
private static string GetUniqueKeyForWindows() |
|||
{ |
|||
try |
|||
{ |
|||
return GetProcessorIdForWindows(); |
|||
} |
|||
catch |
|||
{ |
|||
//couldn't get processor id when logon user has no required permission. try other methods...
|
|||
} |
|||
|
|||
return GetWindowsMachineUniqueId(); |
|||
} |
|||
|
|||
private static string GetProcessorIdForWindows() |
|||
{ |
|||
using (var managementObjectSearcher = |
|||
new System.Management.ManagementObjectSearcher("SELECT ProcessorId FROM Win32_Processor")) |
|||
{ |
|||
using (var searcherObj = managementObjectSearcher.Get()) |
|||
{ |
|||
if (searcherObj.Count == 0) |
|||
{ |
|||
throw new System.Exception("No unique computer ID found for this computer!"); |
|||
} |
|||
|
|||
var managementObjectEnumerator = searcherObj.GetEnumerator(); |
|||
managementObjectEnumerator.MoveNext(); |
|||
return managementObjectEnumerator.Current.GetPropertyValue("ProcessorId").ToString()!; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static string GetWindowsMachineUniqueId() |
|||
{ |
|||
return RunCommandAndGetOutput("powershell (Get-CimInstance -Class Win32_ComputerSystemProduct).UUID"); |
|||
} |
|||
|
|||
|
|||
private static string GetHarddiskSerialForLinux() |
|||
{ |
|||
return RunCommandAndGetOutput( |
|||
"udevadm info --query=all --name=/dev/sda | grep ID_SERIAL_SHORT | tr -d \"ID_SERIAL_SHORT=:\""); |
|||
} |
|||
|
|||
private static string GetHarddiskSerialForOsX() |
|||
{ |
|||
var command = |
|||
"ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { split($0, line, \"\\\"\"); printf(\"%s\\n\", line[4]); }'"; |
|||
|
|||
command = System.Text.RegularExpressions.Regex.Replace(command, @"(\\*)" + "\"", @"$1$1\" + "\""); |
|||
|
|||
return RunCommandAndGetOutput(command); |
|||
} |
|||
|
|||
private static string RunCommandAndGetOutput(string command) |
|||
{ |
|||
var output = ""; |
|||
|
|||
using (var process = new System.Diagnostics.Process()) |
|||
{ |
|||
process.StartInfo = new System.Diagnostics.ProcessStartInfo(GetFileName()) |
|||
{ |
|||
Arguments = GetArguments(command), |
|||
UseShellExecute = false, |
|||
CreateNoWindow = true, |
|||
RedirectStandardOutput = true, |
|||
RedirectStandardError = true |
|||
}; |
|||
|
|||
process.Start(); |
|||
process?.WaitForExit(); |
|||
|
|||
using (var stdOut = process!.StandardOutput) |
|||
{ |
|||
using (var stdErr = process.StandardError) |
|||
{ |
|||
output = stdOut.ReadToEnd(); |
|||
output += stdErr.ReadToEnd(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return output.Trim(); |
|||
} |
|||
|
|||
private static string GetFileName() |
|||
{ |
|||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( |
|||
System.Runtime.InteropServices.OSPlatform.OSX) || |
|||
System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform |
|||
.Linux)) |
|||
{ |
|||
string[] fileNames = { "/bin/bash", "/usr/bin/bash", "/bin/sh", "/usr/bin/sh" }; |
|||
foreach (var fileName in fileNames) |
|||
{ |
|||
try |
|||
{ |
|||
if (System.IO.File.Exists(fileName)) |
|||
{ |
|||
return fileName; |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
//ignore
|
|||
} |
|||
} |
|||
|
|||
return "/bin/bash"; |
|||
} |
|||
|
|||
//Windows default.
|
|||
return "cmd.exe"; |
|||
} |
|||
|
|||
private static string GetArguments(string command) |
|||
{ |
|||
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( |
|||
System.Runtime.InteropServices.OSPlatform.OSX) || |
|||
System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform |
|||
.Linux)) |
|||
{ |
|||
return "-c \"" + command + "\""; |
|||
} |
|||
|
|||
//Windows default.
|
|||
return "/C \"" + command + "\""; |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
namespace Volo.Abp.Telemetry.Constants; |
|||
|
|||
public class TelemetryConsts |
|||
{ |
|||
public const string VoloNameSpaceFilter = "Volo"; |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
|
|||
namespace Volo.Abp.Telemetry; |
|||
|
|||
public interface ITelemetryApplicationInfoContributor |
|||
{ |
|||
Task ContributeAsync(ActivityEvent activityEvent); |
|||
} |
|||
@ -1,19 +1,14 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
|
|||
namespace Volo.Abp.Telemetry; |
|||
|
|||
public interface ITelemetryService |
|||
{ |
|||
IAsyncDisposable TrackActivity(string activityName, Action<ActivityEvent>? configure = null); |
|||
IAsyncDisposable TrackActivity(ActivityEvent activityEvent); |
|||
Task AddActivityAsync(ActivityEvent @event); |
|||
Task AddActivityAsync(string activityName, string? details = null); |
|||
Task AddActivityAsync(string activityName, Action<ActivityEvent> configure); |
|||
Task AddErrorActivityAsync(Action<Dictionary<string, object>> configure); |
|||
IAsyncDisposable TrackActivityAsync(string activityName, Action<Dictionary<string,object>>? additionalProperties = null); |
|||
Task AddActivityAsync(string activityName, Action<Dictionary<string,object>>? additionalProperties = null); |
|||
Task AddErrorActivityAsync(Action<Dictionary<string, object>> additionalProperties); |
|||
Task AddErrorActivityAsync(string errorMessage); |
|||
Task AddErrorForActivityAsync(string failingActivity, string errorMessage); |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
|
|||
namespace Volo.Abp.Telemetry; |
|||
|
|||
public interface ITelemetrySessionProvider |
|||
{ |
|||
Task AddSessionInfoAsync(ActivityEvent activity); |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
|
|||
namespace Volo.Abp.Telemetry; |
|||
|
|||
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))] |
|||
public class TelemetryModuleInfoContributor : ITelemetryApplicationInfoContributor, ISingletonDependency |
|||
{ |
|||
public Task ContributeAsync(ActivityEvent activityEvent) |
|||
{ |
|||
if (activityEvent.TryGetValue(ActivityPropertyNames.Assembly, out var assemblyPath)) |
|||
{ |
|||
var assembly = Assembly.LoadFrom((string)assemblyPath); |
|||
var types = assembly.GetTypes(); |
|||
var moduleCount = types.Count(t => typeof(AbpModule).IsAssignableFrom(t) && !t.IsAbstract); |
|||
var projectCount = types.Select(t => t.Assembly.GetName().Name).Distinct().Count(); |
|||
|
|||
activityEvent[ActivityPropertyNames.ModuleCount] = moduleCount; |
|||
activityEvent[ActivityPropertyNames.ProjectCount] = projectCount; |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
using Volo.Abp.Telemetry.Constants.Enums; |
|||
|
|||
namespace Volo.Abp.Telemetry; |
|||
|
|||
[ExposeServices(typeof(ITelemetrySessionProvider))] |
|||
public class TelemetryRuntimeSessionProvider : ITelemetrySessionProvider, ISingletonDependency |
|||
{ |
|||
public Task AddSessionInfoAsync(ActivityEvent activity) |
|||
{ |
|||
activity[ActivityPropertyNames.SessionType] = SessionType.ApplicationRuntime; |
|||
activity[ActivityPropertyNames.SessionId] = Guid.NewGuid(); |
|||
activity[ActivityPropertyNames.IsFirstSession] = !File.Exists(TelemetryPaths.ActivityStorage); |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.Telemetry; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
|
|||
namespace Volo.Abp.Domain.Telemetry; |
|||
|
|||
[ExposeServices(typeof(ITelemetryApplicationInfoContributor))] |
|||
public class TelemetryDomainInfoContributor : ITelemetryApplicationInfoContributor , ISingletonDependency |
|||
{ |
|||
public Task ContributeAsync(ActivityEvent activityEvent) |
|||
{ |
|||
if (activityEvent.TryGetValue(ActivityPropertyNames.Assembly, out var assemblyPath)) |
|||
{ |
|||
var assembly = Assembly.LoadFrom((string)assemblyPath); |
|||
|
|||
var entityCount = assembly.GetTypes().Count(t => typeof(IEntity).IsAssignableFrom(t) && !t.IsAbstract); |
|||
|
|||
activityEvent[ActivityPropertyNames.EntityCount] = entityCount; |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Entities; |
|||
using Volo.Abp.Reflection; |
|||
using Volo.Abp.Telemetry.Activity; |
|||
using Volo.Abp.Telemetry.Activity.Contracts; |
|||
using Volo.Abp.Telemetry.Activity.Providers; |
|||
using Volo.Abp.Telemetry.Constants; |
|||
|
|||
namespace Volo.Abp.Domain.Telemetry; |
|||
|
|||
[ExposeServices(typeof(ITelemetryActivityEventEnricher))] |
|||
public class TelemetryDomainInfoEnricher : ITelemetryActivityEventEnricher, IScopedDependency |
|||
{ |
|||
private readonly ITypeFinder _typeFinder; |
|||
|
|||
public TelemetryDomainInfoEnricher(ITypeFinder typeFinder) |
|||
{ |
|||
_typeFinder = typeFinder; |
|||
} |
|||
public bool IsFirstRun => false; |
|||
public Type? DependsOn => typeof(TelemetryApplicationInfoEnricher); |
|||
|
|||
public Task<bool> CanExecuteAsync(ActivityContext context) |
|||
{ |
|||
return Task.FromResult(context.ProjectId.HasValue); |
|||
} |
|||
|
|||
public Task<Dictionary<string, object>?> EnrichAsync(ActivityContext context) |
|||
{ |
|||
var entityCount = _typeFinder.Types.Count(t => |
|||
typeof(IEntity).IsAssignableFrom(t) && !t.IsAbstract && |
|||
!t.AssemblyQualifiedName!.StartsWith(TelemetryConsts.VoloNameSpaceFilter)); |
|||
|
|||
|
|||
var result = new Dictionary<string, object>() { { ActivityPropertyNames.EntityCount, entityCount } }; |
|||
return Task.FromResult<Dictionary<string, object>?>(result); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue