Browse Source

Refactor telemetry modules and improve resilience.

pull/23059/head
Emre KARA 1 year ago
parent
commit
7ed2bd97cc
  1. 162
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/SolutionInfoProvider.cs
  2. 10
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/EnvironmentInspection/Contracts/SolutionModuleInstallationInfo.cs
  3. 53
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryService.cs

162
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/SolutionInfoProvider.cs

@ -13,31 +13,40 @@ using Volo.Abp.Telemetry.Shared.Enums;
namespace Volo.Abp.Telemetry.Activity; namespace Volo.Abp.Telemetry.Activity;
public class SolutionInfoProvider : ISolutionInfoProvider , ISingletonDependency public class SolutionInfoProvider : ISolutionInfoProvider, ISingletonDependency
{ {
public async Task<IDictionary<string, object>> GetSolutionInfoAsync(string solutionPath) public async Task<IDictionary<string, object>> GetSolutionInfoAsync(string solutionPath)
{ {
var dictionary = new Dictionary<string, object>(); try
{
var dictionary = new Dictionary<string, object>();
var solutionJson = File.ReadAllText(solutionPath); var solutionJson = File.ReadAllText(solutionPath);
using var solutionDoc = JsonDocument.Parse(solutionJson); using var solutionDoc = JsonDocument.Parse(solutionJson);
var root = solutionDoc.RootElement; var root = solutionDoc.RootElement;
if (root.TryGetProperty("creatingStudioConfiguration", out var config)) if (root.TryGetProperty("creatingStudioConfiguration", out var config))
{ {
AddCreatingStudioConfiguration(dictionary, config); AddCreatingStudioConfiguration(dictionary, config);
} }
dictionary[ActivityPropertyName.LicenseType] = await GetLicenseTypeAsync();
dictionary[ActivityPropertyName.LicenseType] = await GetLicenseTypeAsync(); if (root.TryGetProperty("modules", out var modulesElement) &&
modulesElement.ValueKind == JsonValueKind.Object)
{
var directoryPath = Path.GetDirectoryName(solutionPath);
var modules = ExtractModules(directoryPath!, modulesElement);
dictionary[ActivityPropertyName.InstalledModules] = modules;
}
if (root.TryGetProperty("modules", out var modulesElement) && modulesElement.ValueKind == JsonValueKind.Object) return dictionary;
}
catch (Exception)
{ {
var modules = ExtractModules(modulesElement); return new Dictionary<string, object>();
dictionary[ActivityPropertyName.InstalledModules] = modules;
} }
return dictionary;
} }
public Guid? GetSolutionId(string solutionPath) public Guid? GetSolutionId(string solutionPath)
@ -48,7 +57,7 @@ public class SolutionInfoProvider : ISolutionInfoProvider , ISingletonDependency
return ReadSolutionIdFromJson(root); return ReadSolutionIdFromJson(root);
} }
private Guid? ReadSolutionIdFromJson(JsonElement root) private Guid? ReadSolutionIdFromJson(JsonElement root)
{ {
return root.TryGetProperty("id", out var idElement) && Guid.TryParse(idElement.GetString(), out var id) return root.TryGetProperty("id", out var idElement) && Guid.TryParse(idElement.GetString(), out var id)
@ -56,57 +65,52 @@ public class SolutionInfoProvider : ISolutionInfoProvider , ISingletonDependency
: null; : null;
} }
protected virtual void AddCreatingStudioConfiguration(Dictionary<string,object> dict, JsonElement config) protected virtual void AddCreatingStudioConfiguration(Dictionary<string, object> dict, JsonElement config)
{ {
var mappings = new Dictionary<string, Action<JsonElement>> var mappings = new Dictionary<string, Action<JsonElement>>
{ {
{ "template", value => dict[ActivityPropertyName.Template] = MapSolutionTemplate(value.GetString()) }, { "template", value => dict[ActivityPropertyName.Template] = MapSolutionTemplate(value.GetString()) },
{ "createdAbpStudioVersion", value => dict[ActivityPropertyName.CreatedAbpStudioVersion] = value.GetString()! }, { "createdAbpStudioVersion", value => dict[ActivityPropertyName.CreatedAbpStudioVersion] = value.GetString()! },
{ "multiTenancy", value => dict[ActivityPropertyName.MultiTenancy] = ParseBool(value) }, { "multiTenancy", value => dict[ActivityPropertyName.MultiTenancy] = ParseBool(value) },
{ "uiFramework", value => dict[ActivityPropertyName.UiFramework] = MapUiFramework(value.GetString()) }, { "uiFramework", value => dict[ActivityPropertyName.UiFramework] = MapUiFramework(value.GetString()) },
{ "databaseProvider", value => dict[ActivityPropertyName.DatabaseProvider] = MapDatabaseProvider(value.GetString()) }, { "databaseProvider", value => dict[ActivityPropertyName.DatabaseProvider] = MapDatabaseProvider(value.GetString()) },
{ "theme", value => dict[ActivityPropertyName.Theme] = MapUiTheme(value.GetString()) }, { "theme", value => dict[ActivityPropertyName.Theme] = MapUiTheme(value.GetString()) },
{ "themeStyle", value => dict[ActivityPropertyName.ThemeStyle] = MapUiThemeStyle(value.GetString()) }, { "themeStyle", value => dict[ActivityPropertyName.ThemeStyle] = MapUiThemeStyle(value.GetString()) },
{ "publicWebsite", value => dict[ActivityPropertyName.HasPublicWebsite] = ParseBool(value) }, { "publicWebsite", value => dict[ActivityPropertyName.HasPublicWebsite] = ParseBool(value) },
{ "tiered", value => dict[ActivityPropertyName.IsTiered] = ParseBool(value) }, { "tiered", value => dict[ActivityPropertyName.IsTiered] = ParseBool(value) },
{ "socialLogin", value => dict[ActivityPropertyName.SocialLogins] = ParseBool(value) }, { "socialLogin", value => dict[ActivityPropertyName.SocialLogins] = ParseBool(value) },
{ "databaseManagementSystem", value => dict[ActivityPropertyName.DatabaseManagementSystem] = MapDbms(value.GetString()) }, { "databaseManagementSystem", value => dict[ActivityPropertyName.DatabaseManagementSystem] = MapDbms(value.GetString()) },
{ "separateTenantSchema", value => dict[ActivityPropertyName.IsSeparateTenantSchema] = ParseBool(value) }, { "separateTenantSchema", value => dict[ActivityPropertyName.IsSeparateTenantSchema] = ParseBool(value) },
{ "mobileFramework", value => dict[ActivityPropertyName.MobileFramework] = MapMobileApp(value.GetString()) }, { "mobileFramework", value => dict[ActivityPropertyName.MobileFramework] = MapMobileApp(value.GetString()) },
{ "includeTests", value => dict[ActivityPropertyName.IncludeTests] = ParseBool(value) }, { "includeTests", value => dict[ActivityPropertyName.IncludeTests] = ParseBool(value) },
{ "dynamicLocalization", value => dict[ActivityPropertyName.DynamicLocalization] = ParseBool(value) }, { "dynamicLocalization", value => dict[ActivityPropertyName.DynamicLocalization] = ParseBool(value) },
{ "kubernetesConfiguration", value => dict[ActivityPropertyName.KubernetesConfiguration] = ParseBool(value) }, { "kubernetesConfiguration", value => dict[ActivityPropertyName.KubernetesConfiguration] = ParseBool(value) },
{ "grafanaDashboard", value => dict[ActivityPropertyName.GrafanaDashboard] = ParseBool(value) }, { "grafanaDashboard", value => dict[ActivityPropertyName.GrafanaDashboard] = ParseBool(value) },
}; };
foreach (var mapping in mappings) foreach (var mapping in mappings)
{
if (config.TryGetProperty(mapping.Key, out var propertyValue))
{ {
try mapping.Value(propertyValue);
{
if (config.TryGetProperty(mapping.Key, out var propertyValue))
{
mapping.Value(propertyValue);
}
}
catch
{
// ignored
}
} }
}
} }
private static bool ParseBool(JsonElement element) => private static bool ParseBool(JsonElement element)
element.ValueKind switch {
return element.ValueKind switch
{ {
JsonValueKind.True => true, JsonValueKind.True => true,
JsonValueKind.False => false, JsonValueKind.False => false,
JsonValueKind.String when bool.TryParse(element.GetString(), out var b) => b, JsonValueKind.String when bool.TryParse(element.GetString(), out var b) => b,
_ => false _ => false
}; };
}
protected virtual List<SolutionModuleInstallationInfo> ExtractModules(JsonElement modulesElement) protected virtual List<Dictionary<string,object>> ExtractModules(string directoryPath, JsonElement modulesElement)
{ {
var modules = new List<SolutionModuleInstallationInfo>(); var modules = new List<Dictionary<string,object>>();
foreach (var module in modulesElement.EnumerateObject()) foreach (var module in modulesElement.EnumerateObject())
{ {
@ -117,12 +121,18 @@ public class SolutionInfoProvider : ISolutionInfoProvider , ISingletonDependency
} }
var path = pathElement.GetString(); var path = pathElement.GetString();
if (path.IsNullOrEmpty() || !File.Exists(path)) if (path.IsNullOrEmpty())
{ {
continue; continue;
} }
var modulePath = Path.Combine(directoryPath, path);
var moduleJson = File.ReadAllText(path); if (!File.Exists(modulePath))
{
continue;
}
var moduleJson = File.ReadAllText(modulePath);
using var moduleDoc = JsonDocument.Parse(moduleJson); using var moduleDoc = JsonDocument.Parse(moduleJson);
if (!moduleDoc.RootElement.TryGetProperty("imports", out var imports) || if (!moduleDoc.RootElement.TryGetProperty("imports", out var imports) ||
@ -138,14 +148,25 @@ public class SolutionInfoProvider : ISolutionInfoProvider , ISingletonDependency
? DateTimeOffset.Parse(ct.GetString()!) ? DateTimeOffset.Parse(ct.GetString()!)
: (DateTimeOffset?)null; : (DateTimeOffset?)null;
if (modules.Any(x => x.ModuleName == name && x.Version == version)) if (modules.Any(x =>
x.ContainsKey("ModuleName") && (string)x["ModuleName"] == name &&
x.ContainsKey("Version") && (string)x["Version"] == version))
{ {
continue; continue;
} }
modules.Add(new SolutionModuleInstallationInfo if (modules.Any(x =>
x.TryGetValue("ModuleName", out var moduleNameObj) && moduleNameObj as string == name &&
x.TryGetValue("Version", out var versionObj) && versionObj as string == version))
{ {
ModuleName = name, Version = version, InstallationTime = creationTime continue;
}
modules.Add(new Dictionary<string, object>
{
{ "ModuleName", name },
{ "Version", version ?? string.Empty },
{ "InstallationTime", creationTime ?? DateTime.MinValue }
}); });
} }
} }
@ -160,14 +181,27 @@ public class SolutionInfoProvider : ISolutionInfoProvider , ISingletonDependency
return 0; return 0;
} }
using var httpClient = new HttpClient(); try
var accessToken = File.ReadAllText(AbpTelemetryPaths.AccessToken); {
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); using var httpClient = new HttpClient();
var accessToken = File.ReadAllText(AbpTelemetryPaths.AccessToken);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var httpResponse = await httpClient.GetAsync($"{AbpPlatformUrls.AbpIoUrl}api/license/api-key");
if (!httpResponse.IsSuccessStatusCode)
{
return 0;
}
var json = await httpClient.GetStringAsync($"{AbpPlatformUrls.AbpIoUrl}api/license/api-key"); var responseContent = await httpResponse.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json); using var doc = JsonDocument.Parse(responseContent);
return doc.RootElement.GetProperty("licenseType").GetInt32(); return doc.RootElement.GetProperty("licenseType").GetInt32();
}
catch
{
return 0;
}
} }
protected virtual UiTheme MapUiTheme(string? theme) => theme switch protected virtual UiTheme MapUiTheme(string? theme) => theme switch

10
framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/EnvironmentInspection/Contracts/SolutionModuleInstallationInfo.cs

@ -1,10 +0,0 @@
using System;
namespace Volo.Abp.Telemetry.EnvironmentInspection.Contracts;
public class SolutionModuleInstallationInfo
{
public string? ModuleName { get; set; }
public string? Version { get; set; }
public DateTimeOffset? InstallationTime { get; set; }
}

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

@ -7,13 +7,14 @@ using Volo.Abp.Telemetry.Activity;
namespace Volo.Abp.Telemetry; namespace Volo.Abp.Telemetry;
public class TelemetryService : ITelemetryService , IScopedDependency public class TelemetryService : ITelemetryService, IScopedDependency
{ {
private readonly IActivityStorage _activityStorage; private readonly IActivityStorage _activityStorage;
private readonly ITelemetryDataSender _telemetryDataSender; private readonly ITelemetryDataSender _telemetryDataSender;
private readonly IActivityDataProvider _activityDataProvider; private readonly IActivityDataProvider _activityDataProvider;
public TelemetryService(IActivityStorage activityStorage, ITelemetryDataSender telemetryDataSender, IActivityDataProvider activityDataProvider) public TelemetryService(IActivityStorage activityStorage, ITelemetryDataSender telemetryDataSender,
IActivityDataProvider activityDataProvider)
{ {
_activityStorage = activityStorage; _activityStorage = activityStorage;
_telemetryDataSender = telemetryDataSender; _telemetryDataSender = telemetryDataSender;
@ -23,10 +24,7 @@ public class TelemetryService : ITelemetryService , IScopedDependency
public IAsyncDisposable TrackActivity(string activityName, Action<ActivityData>? configure = null) public IAsyncDisposable TrackActivity(string activityName, Action<ActivityData>? configure = null)
{ {
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
var activityData = new ActivityData(activityName) var activityData = new ActivityData(activityName) { Time = DateTimeOffset.UtcNow };
{
Time = DateTimeOffset.UtcNow
};
configure?.Invoke(activityData); configure?.Invoke(activityData);
@ -59,36 +57,30 @@ public class TelemetryService : ITelemetryService , IScopedDependency
// { // {
// await _telemetryDataSender.SendAsync(); // await _telemetryDataSender.SendAsync();
// } // }
if (lastActivitySendTime is not null && lastActivitySendTime < DateTimeOffset.UtcNow.AddDays(-1) ) if (lastActivitySendTime is not null && lastActivitySendTime < DateTimeOffset.UtcNow.AddDays(-1))
{ {
await _telemetryDataSender.SendAsync(); await _telemetryDataSender.SendAsync();
} }
} }
public Task AddActivityAsync(ActivityData data)
{
Task.Run(async () => public async Task AddActivityAsync(ActivityData data)
{
try
{ {
try await _activityDataProvider.AddExtraInformationAsync(data);
{ await _activityStorage.BufferActivityAsync(data);
await _activityDataProvider.AddExtraInformationAsync(data); await CheckIfActivitySendTimeIsDueAsync();
await _activityStorage.BufferActivityAsync(data);
await CheckIfActivitySendTimeIsDueAsync(); if (data.ActivityName == ActivityNameConsts.AbpStudioClose)
if (data.ActivityName == ActivityNameConsts.AbpStudioClose)
{
await _activityStorage.EndSessionAsync();
}
}
catch
{ {
await _activityStorage.EndSessionAsync();
} }
}); }
catch
{
return Task.CompletedTask; // ignored
}
} }
public async Task AddActivityAsync(string activityName, string? details = null) public async Task AddActivityAsync(string activityName, string? details = null)
@ -98,10 +90,7 @@ public class TelemetryService : ITelemetryService , IScopedDependency
public async Task AddActivityAsync(string activityName, Action<ActivityData> configure) public async Task AddActivityAsync(string activityName, Action<ActivityData> configure)
{ {
var activityData = new ActivityData(activityName) var activityData = new ActivityData(activityName) { Time = DateTimeOffset.UtcNow };
{
Time = DateTimeOffset.UtcNow
};
configure?.Invoke(activityData); configure?.Invoke(activityData);

Loading…
Cancel
Save