Browse Source

Refactor telemetry system to remove ABP CLI dependencies

pull/23059/head
Emre KARA 1 year ago
parent
commit
2425b7e861
  1. 5
      framework/src/Volo.Abp.Telemetry/AbpTelemetryModule.cs
  2. 41
      framework/src/Volo.Abp.Telemetry/Activity/ActivityData.cs
  3. 53
      framework/src/Volo.Abp.Telemetry/Activity/ActivityDataProvider.cs
  4. 35
      framework/src/Volo.Abp.Telemetry/Activity/ActivityStorage.cs
  5. 41
      framework/src/Volo.Abp.Telemetry/ActivityEventHandler.cs
  6. 7
      framework/src/Volo.Abp.Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs
  7. 4
      framework/src/Volo.Abp.Telemetry/EnvironmentInspection/DeviceInfoProvider.cs
  8. 2
      framework/src/Volo.Abp.Telemetry/ITelemetryService.cs
  9. 12
      framework/src/Volo.Abp.Telemetry/Shared/AbpTelemetryPaths.cs
  10. 38
      framework/src/Volo.Abp.Telemetry/TelemetryDataSender.cs
  11. 35
      framework/src/Volo.Abp.Telemetry/TelemetryService.cs
  12. 2
      framework/src/Volo.Abp.Telemetry/Volo.Abp.Telemetry.csproj

5
framework/src/Volo.Abp.Telemetry/AbpTelemetryModule.cs

@ -5,12 +5,9 @@ using Activity;
using Helpers;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
[DependsOn(
typeof(AbpAutofacModule)
)]
public class AbpTelemetryModule : AbpModule
{

41
framework/src/Volo.Abp.Telemetry/Activity/ActivityData.cs

@ -45,3 +45,44 @@ public class ActivityData : Dictionary<string, object>
ActivityDetails = details;
}
}
public static class ActivityPropertyNameConstants
{
public const string SessionId = "SessionId";
public const string IsFirstSession = "IsFirstSession";
public const string DeviceId = "DeviceId";
public const string DeviceType = "DeviceType";
public const string DeviceLanguage = "DeviceLanguage";
public const string OperatingSystem = "OperatingSystem";
public const string CountryIsoCode = "CountryIsoCode";
public const string InstalledSoftwares = "InstalledSoftwares";
public const string ControllerCount = nameof(ApplicationInfo.ControllerCount);
public const string EntityCount = nameof(ApplicationInfo.EntityCount);
public const string AbpModuleCount = nameof(ApplicationInfo.AbpModuleCount);
public const string PermissionCount = nameof(ApplicationInfo.PermissionCount);
public const string AppServiceCount = nameof(ApplicationInfo.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 ProjectAssemblyForScan = "ProjectAssemblyForScan";
public const string SolutionPath = "SolutionPath";
}

53
framework/src/Volo.Abp.Telemetry/Activity/ActivityDataProvider.cs

@ -70,13 +70,13 @@ public class ActivityDataProvider : IActivityDataProvider, IScopedDependency
private async Task AddDeviceInformationAsync(ActivityData activityData)
{
activityData.Add("DeviceType", _deviceInfoProvider.GetDeviceType());
activityData.Add("DeviceLanguage", _deviceInfoProvider.GetLanguage());
activityData.Add("OperatingSystem", _deviceInfoProvider.GetOperatingSystem());
activityData.Add("CountryIsoCode", _deviceInfoProvider.GetCountry());
activityData.Add(ActivityPropertyNameConstants.DeviceType , _deviceInfoProvider.GetDeviceType());
activityData.Add(ActivityPropertyNameConstants.DeviceLanguage, _deviceInfoProvider.GetLanguage());
activityData.Add(ActivityPropertyNameConstants.OperatingSystem, _deviceInfoProvider.GetOperatingSystem());
activityData.Add(ActivityPropertyNameConstants.CountryIsoCode, _deviceInfoProvider.GetCountry());
var softwareList = await _softwareInfoProvider.GetSoftwareInfoAsync();
activityData.Add("InstalledSoftwares", softwareList);
activityData.Add(ActivityPropertyNameConstants.InstalledSoftwares, softwareList);
await _activityStorage.MarkDeviceInfoAsSentAsync();
}
@ -89,8 +89,8 @@ public class ActivityDataProvider : IActivityDataProvider, IScopedDependency
activityData.Add(nameof(ApplicationInfo.AbpModuleCount), info.AbpModuleCount);
activityData.Add(nameof(ApplicationInfo.PermissionCount), info.PermissionCount);
activityData.Add(nameof(ApplicationInfo.AppServiceCount), info.AppServiceCount);
activityData.Add("ProjectType", activityData["ProjectType"]);
activityData.Add("ProjectId", activityData["ProjectId"]);
activityData.Add(ActivityPropertyNameConstants.ProjectType, activityData["ProjectType"]);
activityData.Add(ActivityPropertyNameConstants.ProjectId, activityData["ProjectId"]);
activityData.Remove("ProjectAssemblyForScan");
}
@ -105,30 +105,29 @@ public class ActivityDataProvider : IActivityDataProvider, IScopedDependency
var solutionId = rootJObject["id"]!.To<Guid>();
var lastSolutionInfoSendTime =
await _activityStorage.GetLastSolutionInfoSendTimeAsync(solutionId);
activityData.Add("SolutionId", solutionId);
activityData.Add(ActivityPropertyNameConstants.SolutionId, solutionId);
if (lastSolutionInfoSendTime is null ||
DateTimeOffset.UtcNow - lastSolutionInfoSendTime > TimeSpan.FromDays(7))
{
var infoJObject = (JObject)rootJObject["creatingStudioConfiguration"]!;
activityData.Add("Template", GetSolutionTemplate(infoJObject["template"]?.ToString()));
activityData.Add("CreatedAbpStudioVersion", infoJObject["createdAbpStudioVersion"]!.ToString());
activityData.Add("IsTiered", infoJObject.Value<bool>("Tiered"));
activityData.Add("UiFramework", GetUiFramework(infoJObject["uiFramework"]?.ToString()));
activityData.Add("DatabaseProvider", GetDatabaseProvider(infoJObject["databaseProvider"]?.ToString()));
activityData.Add("DatabaseManagementSystem",GetDbms(infoJObject["databaseManagementSystem"]?.ToString()));
activityData.Add("IsSeparateTenantSchema", infoJObject.Value<bool>("separateTenantSchema"));
activityData.Add("Theme", GetUiTheme(infoJObject["theme"]?.ToString()));
activityData.Add("ThemeStyle", GetUiThemeStyle(infoJObject["themeStyle"]?.ToString()));
activityData.Add("MobileFramework", GetMobileApp(infoJObject["mobileFramework"]?.ToString()));
activityData.Add("HasPublicWebsite", infoJObject.Value<bool>("publicWebsite"));
activityData.Add("IncludeTests", infoJObject.Value<bool>("includeTests"));
activityData.Add("MultiTenancy", infoJObject.Value<bool>("multiTenancy"));
activityData.Add("DynamicLocalization", infoJObject.Value<bool>("dynamicLocalization"));
activityData.Add("KubernetesConfiguration", infoJObject.Value<bool>("kubernetesConfiguration"));
activityData.Add("GrafanaDashboard", infoJObject.Value<bool>("grafanaDashboard"));
activityData.Add("SocialLogins", infoJObject.Value<bool>("socialLogin"));
activityData.Add(ActivityPropertyNameConstants.Template, GetSolutionTemplate(infoJObject["template"]?.ToString()));
activityData.Add(ActivityPropertyNameConstants.CreatedAbpStudioVersion, infoJObject["createdAbpStudioVersion"]!.ToString());
activityData.Add(ActivityPropertyNameConstants.IsTiered, infoJObject.Value<bool>("Tiered"));
activityData.Add(ActivityPropertyNameConstants.UiFramework, GetUiFramework(infoJObject["uiFramework"]?.ToString()));
activityData.Add(ActivityPropertyNameConstants.DatabaseProvider, GetDatabaseProvider(infoJObject["databaseProvider"]?.ToString()));
activityData.Add(ActivityPropertyNameConstants.DatabaseManagementSystem, GetDbms(infoJObject["databaseManagementSystem"]?.ToString()));
activityData.Add(ActivityPropertyNameConstants.IsSeparateTenantSchema, infoJObject.Value<bool>("separateTenantSchema"));
activityData.Add(ActivityPropertyNameConstants.Theme, GetUiTheme(infoJObject["theme"]?.ToString()));
activityData.Add(ActivityPropertyNameConstants.ThemeStyle, GetUiThemeStyle(infoJObject["themeStyle"]?.ToString()));
activityData.Add(ActivityPropertyNameConstants.MobileFramework, GetMobileApp(infoJObject["mobileFramework"]?.ToString()));
activityData.Add(ActivityPropertyNameConstants.HasPublicWebsite, infoJObject.Value<bool>("publicWebsite"));
activityData.Add(ActivityPropertyNameConstants.IncludeTests, infoJObject.Value<bool>("includeTests"));
activityData.Add(ActivityPropertyNameConstants.MultiTenancy, infoJObject.Value<bool>("multiTenancy"));
activityData.Add(ActivityPropertyNameConstants.DynamicLocalization, infoJObject.Value<bool>("dynamicLocalization"));
activityData.Add(ActivityPropertyNameConstants.KubernetesConfiguration, infoJObject.Value<bool>("kubernetesConfiguration"));
activityData.Add(ActivityPropertyNameConstants.GrafanaDashboard, infoJObject.Value<bool>("grafanaDashboard"));
activityData.Add(ActivityPropertyNameConstants.SocialLogins, infoJObject.Value<bool>("socialLogin"));
var modules = new List<SolutionModuleInstallationInfo>();
foreach (var module in rootJObject["modules"]!.Children<JProperty>())
@ -168,7 +167,7 @@ public class ActivityDataProvider : IActivityDataProvider, IScopedDependency
});
}
activityData.Add("InstalledModules", modules);
activityData.Add(ActivityPropertyNameConstants.InstalledModules, modules);
}
}
}

35
framework/src/Volo.Abp.Telemetry/Activity/ActivityStorage.cs

@ -3,25 +3,20 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Cli;
using Shared;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
namespace Activity;
public class ActivityStorage : IActivityStorage, ISingletonDependency
{
private readonly IJsonSerializer _jsonSerializer;
private readonly string _filePath;
private ActivityStorageState? _cachedState;
public ActivityStorage(IJsonSerializer jsonSerializer)
{
_jsonSerializer = jsonSerializer;
_filePath = Path.Combine(CliPaths.AbpRootPath, "activity-storage.json");
}
public async Task BufferActivityAsync(ActivityData activityData, CancellationToken cancellationToken = default)
{
@ -154,7 +149,7 @@ public class ActivityStorage : IActivityStorage, ISingletonDependency
{
using var reader = new StreamReader(stream, Encoding.UTF8, leaveOpen: true);
var json = await reader.ReadToEndAsync();
return _jsonSerializer.Deserialize<ActivityStorageState?>(json) ?? new ActivityStorageState();
return JsonSerializer.Deserialize<ActivityStorageState?>(json) ?? new ActivityStorageState();
});
return _cachedState;
}
@ -169,7 +164,7 @@ public class ActivityStorage : IActivityStorage, ISingletonDependency
try
{
await using var stream = new FileStream(
_filePath,
AbpTelemetryPaths.ActivityStorage,
FileMode.OpenOrCreate,
FileAccess.ReadWrite,
FileShare.None
@ -191,8 +186,11 @@ public class ActivityStorage : IActivityStorage, ISingletonDependency
private async Task SaveAsync()
{
var json = _jsonSerializer.Serialize(_cachedState ?? new ActivityStorageState(), indented: true);
await File.WriteAllTextAsync(_filePath, json, Encoding.UTF8);
var json = JsonSerializer.Serialize(_cachedState ?? new ActivityStorageState(), new JsonSerializerOptions
{
WriteIndented = true
});
await File.WriteAllTextAsync(AbpTelemetryPaths.ActivityStorage, json, Encoding.UTF8);
}
@ -200,17 +198,20 @@ public class ActivityStorage : IActivityStorage, ISingletonDependency
{
try
{
var directory = Path.GetDirectoryName(_filePath);
var directory = Path.GetDirectoryName(AbpTelemetryPaths.ActivityStorage);
if (!Directory.Exists(directory) && ! directory.IsNullOrEmpty())
{
Directory.CreateDirectory(directory);
}
if (!File.Exists(_filePath))
if (!File.Exists(AbpTelemetryPaths.ActivityStorage))
{
var json = _jsonSerializer.Serialize(new ActivityStorageState(), indented: true);
await File.WriteAllTextAsync(_filePath, json, Encoding.UTF8);
var json = JsonSerializer.Serialize(_cachedState ?? new ActivityStorageState(), new JsonSerializerOptions
{
WriteIndented = true
});
await File.WriteAllTextAsync(AbpTelemetryPaths.ActivityStorage, json, Encoding.UTF8);
}
}
catch

41
framework/src/Volo.Abp.Telemetry/ActivityEventHandler.cs

@ -1,41 +0,0 @@
using System;
using System.Threading.Tasks;
using Activity;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
public class ActivityEventHandler : ILocalEventHandler<ActivityData>, ITransientDependency
{
private readonly IActivityStorage _activityStorage;
private readonly IActivityDataProvider _activityDataProvider;
private readonly ITelemetryDataSender _telemetryDataSender;
public ActivityEventHandler(IActivityDataProvider activityDataProvider, IActivityStorage activityStorage, ITelemetryDataSender telemetryDataSender)
{
_activityDataProvider = activityDataProvider;
_activityStorage = activityStorage;
_telemetryDataSender = telemetryDataSender;
}
public async Task HandleEventAsync(ActivityData eventData)
{
var build = await _activityDataProvider.AddExtraInformationAsync(eventData);
await _activityStorage.BufferActivityAsync(build);
await CheckIfActivitySendTimeIsDueAsync();
}
private async Task CheckIfActivitySendTimeIsDueAsync()
{
var lastActivitySendTime = await _activityStorage.GetLastActivitySendTimeAsync();
if (lastActivitySendTime is null)
{
await _telemetryDataSender.SendAsync();
}
if (lastActivitySendTime is not null && lastActivitySendTime > DateTimeOffset.UtcNow.AddDays(7) )
{
await _telemetryDataSender.SendAsync();
}
}
}

7
framework/src/Volo.Abp.Telemetry/EnvironmentInspection/Detectors/AbpStudioDetector.cs

@ -4,7 +4,6 @@ using System.Threading.Tasks;
using EnvironmentInspection.Contracts;
using EnvironmentInspection.Enums;
using Newtonsoft.Json.Linq;
using Volo.Abp.Cli;
namespace EnvironmentInspection.Detectors;
@ -30,7 +29,8 @@ internal class AbpStudioDetector : SoftwareDetector, ISoftwareDetector
public async Task<string?> GetAbpStudioUiThemeAsync()
{
var ideStateJsonPath = Path.Combine(
CliPaths.AbpRootPath,
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".abp",
"studio",
"ui",
"ide-state.json"
@ -43,7 +43,8 @@ internal class AbpStudioDetector : SoftwareDetector, ISoftwareDetector
public async Task<string?> GetAbpStudioVersionAsync()
{
var extensionsFilePath = Path.Combine(
CliPaths.AbpRootPath,
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
".abp",
"studio",
"extensions.json"
);

4
framework/src/Volo.Abp.Telemetry/EnvironmentInspection/DeviceInfoProvider.cs

@ -7,7 +7,6 @@ using System.Management;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using EnvironmentInspection.Enums;
using Volo.Abp.Cli;
using Volo.Abp.DependencyInjection;
namespace EnvironmentInspection;
@ -16,8 +15,7 @@ public class DeviceInfoProvider : IDeviceInfoProvider , ISingletonDependency
{
public async Task<Guid> GetDeviceIdAsync()
{
var deviceId =
await File.ReadAllTextAsync(Path.Combine(CliPaths.AbpRootPath, "cli", "computer-id.bin"));
var deviceId = await File.ReadAllTextAsync(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".abp", "cli", "computer-id.bin"));
return deviceId.To<Guid>();
}

2
framework/src/Volo.Abp.Telemetry/ITelemetryService.cs

@ -4,7 +4,7 @@ using System.Threading.Tasks;
using Activity;
using Volo.Abp.DependencyInjection;
public interface ITelemetryService : IScopedDependency
public interface ITelemetryService
{
IAsyncDisposable TrackActivity(string activityName, Action<ActivityData>? configure = null);
IAsyncDisposable TrackActivity(ActivityData activityData);

12
framework/src/Volo.Abp.Telemetry/Shared/AbpTelemetryPaths.cs

@ -0,0 +1,12 @@
using System;
using System.IO;
namespace Shared;
public static class AbpTelemetryPaths
{
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, "activity-storage.json");
private readonly static string AbpRootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".abp");
}

38
framework/src/Volo.Abp.Telemetry/TelemetryDataSender.cs

@ -1,30 +1,36 @@
using System.Net.Http;
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Activity;
using Volo.Abp.Cli;
using Volo.Abp.Cli.Http;
using Shared;
using Volo.Abp.DependencyInjection;
public class TelemetryDataSender : ITelemetryDataSender , IScopedDependency
{
private readonly CliHttpClientFactory _cliHttpClientFactory;
#if DEBUG
private const string ApiUrl = "https://localhost:44393/";
#else
private const string ApiUrl = "https://telemetry.abp.io/";
#endif
private readonly IActivityStorage _activityStorage;
private readonly IActivityDataProvider _activityDataProvider;
public TelemetryDataSender(CliHttpClientFactory cliHttpClientFactory, IActivityStorage activityStorage, IActivityDataProvider activityDataProvider)
public TelemetryDataSender(IActivityStorage activityStorage, IActivityDataProvider activityDataProvider)
{
_cliHttpClientFactory = cliHttpClientFactory;
_activityStorage = activityStorage;
_activityDataProvider = activityDataProvider;
}
public async Task SendAsync()
{
using var client = _cliHttpClientFactory.CreateClient();
client.AddAbpAuthenticationToken();
using var httpClient = new HttpClient();
await AddAbpAuthenticationTokenAsync(httpClient);
var activities = await _activityStorage.GetBufferedActivitiesAsync();
@ -33,7 +39,7 @@ public class TelemetryDataSender : ITelemetryDataSender , IScopedDependency
foreach (var activity in activities)
{
await _activityDataProvider.AddExtraInformationAsync(activity);
await client.PostAsync(CliUrls.TelemetryAbpIo,
await httpClient.PostAsync(ApiUrl,
new StringContent(JsonSerializer.Serialize(activity), Encoding.UTF8,
MediaTypeNames.Application.Json));
@ -42,4 +48,18 @@ public class TelemetryDataSender : ITelemetryDataSender , IScopedDependency
await _activityStorage.MarkActivitiesAsSentAsync();
}
}
private async static Task AddAbpAuthenticationTokenAsync(HttpClient httpClient)
{
if (!File.Exists(AbpTelemetryPaths.AccessToken))
{
return;
}
var accessToken = await File.ReadAllTextAsync(AbpTelemetryPaths.AccessToken, Encoding.UTF8);
if (!accessToken.IsNullOrEmpty())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
}
}
}

35
framework/src/Volo.Abp.Telemetry/TelemetryService.cs

@ -4,18 +4,21 @@ using System.Threading;
using System.Threading.Tasks;
using Activity;
using Volo.Abp;
using Volo.Abp.EventBus.Local;
using Volo.Abp.DependencyInjection;
public class TelemetryService : ITelemetryService
public class TelemetryService : ITelemetryService , IScopedDependency
{
private readonly IActivityStorage _activityStorage;
private readonly IActivityDataProvider _activityDataProvider;
private readonly ITelemetryDataSender _telemetryDataSender;
private readonly ILocalEventBus _localEventBus;
public TelemetryService( ILocalEventBus localEventBus)
public TelemetryService(IActivityStorage activityStorage, IActivityDataProvider activityDataProvider, ITelemetryDataSender telemetryDataSender)
{
_localEventBus = localEventBus;
_activityStorage = activityStorage;
_activityDataProvider = activityDataProvider;
_telemetryDataSender = telemetryDataSender;
}
public IAsyncDisposable TrackActivity(string activityName, Action<ActivityData>? configure = null)
{
var stopwatch = Stopwatch.StartNew();
@ -48,11 +51,25 @@ public class TelemetryService : ITelemetryService
});
}
private async Task CheckIfActivitySendTimeIsDueAsync()
{
var lastActivitySendTime = await _activityStorage.GetLastActivitySendTimeAsync();
if (lastActivitySendTime is null)
{
await _telemetryDataSender.SendAsync();
}
if (lastActivitySendTime is not null && lastActivitySendTime > DateTimeOffset.UtcNow.AddDays(7) )
{
await _telemetryDataSender.SendAsync();
}
}
public async Task AddActivityAsync(ActivityData data, CancellationToken cancellationToken = default)
{
await _localEventBus.PublishAsync(data);
var build = await _activityDataProvider.AddExtraInformationAsync(data, cancellationToken);
await _activityStorage.BufferActivityAsync(build, cancellationToken);
await CheckIfActivitySendTimeIsDueAsync();
}
public async Task AddActivityAsync(string activityName, string? details = null, CancellationToken cancellationToken = default)

2
framework/src/Volo.Abp.Telemetry/Volo.Abp.Telemetry.csproj

@ -16,8 +16,6 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj"/>
<ProjectReference Include="..\Volo.Abp.Cli.Core\Volo.Abp.Cli.Core.csproj"/>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj"/>
<ProjectReference Include="..\Volo.Abp.EventBus.Abstractions\Volo.Abp.EventBus.Abstractions.csproj"/>
</ItemGroup>

Loading…
Cancel
Save