Browse Source

Enhance telemetry functionality by adding null checks for activity names in TelemetryService and updating TelemetryActivityStorage to improve state management and info expiration logic.

pull/23059/head
Emre KARA 1 year ago
parent
commit
e600ee3f6e
  1. 1
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Contracts/ITelemetryActivityStorage.cs
  2. 112
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs
  3. 2
      framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/TelemetryService.cs

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

@ -17,6 +17,5 @@ public interface ITelemetryActivityStorage
Task EndSessionAsync(); Task EndSessionAsync();
Task<bool> ShouldAddDeviceInfoAsync(); Task<bool> ShouldAddDeviceInfoAsync();
Task<bool> ShouldAddSolutionInformation(Guid solutionId); Task<bool> ShouldAddSolutionInformation(Guid solutionId);
Task<bool> ShouldAddApplicationInfoAsync(Guid applicationId); Task<bool> ShouldAddApplicationInfoAsync(Guid applicationId);
} }

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

@ -13,6 +13,10 @@ namespace Volo.Abp.Telemetry.Activity.Storage;
public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDependency public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDependency
{ {
private const int MaxFileRetries = 5;
private const int RetryDelayMs = 100;
private static readonly TimeSpan InfoExpirationPeriod = TimeSpan.FromDays(7);
private TelemetryActivityStorageState? _cachedState; private TelemetryActivityStorageState? _cachedState;
public async Task BufferActivityAsync(ActivityData activityData) public async Task BufferActivityAsync(ActivityData activityData)
@ -31,19 +35,16 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
public async Task EndSessionAsync() public async Task EndSessionAsync()
{ {
var state = await GetStateAsync(); var state = await GetStateAsync();
state.SessionId = null; state.SessionId = null;
await SaveAsync(); await SaveAsync();
} }
public async Task<DateTimeOffset?> GetLastActivitySendTimeAsync() public async Task<DateTimeOffset?> GetLastActivitySendTimeAsync()
{ {
var state = await GetStateAsync(); var state = await GetStateAsync();
return state.ActivitySendTime; return state.ActivitySendTime;
} }
public async Task<Guid> GetOrCreateSessionInfoAsync() public async Task<Guid> GetOrCreateSessionInfoAsync()
{ {
var state = await GetStateAsync(); var state = await GetStateAsync();
@ -58,7 +59,6 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
state.ActivitySendTime = DateTimeOffset.UtcNow; state.ActivitySendTime = DateTimeOffset.UtcNow;
state.Activities.Clear(); state.Activities.Clear();
state.SessionId = null; state.SessionId = null;
await SaveAsync(); await SaveAsync();
} }
@ -68,29 +68,12 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
state.Solutions[solutionId] = DateTimeOffset.UtcNow; state.Solutions[solutionId] = DateTimeOffset.UtcNow;
await SaveAsync(); await SaveAsync();
} }
public async Task MarkApplicationInfoAsAddedAsync(Guid applicationInfo)
{
var state = await GetStateAsync();
state.Solutions[applicationInfo] = DateTimeOffset.UtcNow;
await SaveAsync();
}
private async Task<DateTimeOffset?> GetLastSolutionInfoSendTimeAsync(Guid id)
{
var state = await GetStateAsync();
if (state.Solutions.TryGetValue(id, out var date))
{
return date;
}
return null;
}
private async Task<DateTimeOffset?> GetLastDeviceInfoSendTimeAsync() public async Task MarkApplicationInfoAsAddedAsync(Guid applicationId)
{ {
var state = await GetStateAsync(); var state = await GetStateAsync();
return state.LastDeviceInfoAddTime; state.ApplicationInfos[applicationId] = DateTimeOffset.UtcNow;
await SaveAsync();
} }
public async Task MarkDeviceInfoAsAddedAsync() public async Task MarkDeviceInfoAsAddedAsync()
@ -99,22 +82,41 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
state.LastDeviceInfoAddTime = DateTimeOffset.UtcNow; state.LastDeviceInfoAddTime = DateTimeOffset.UtcNow;
await SaveAsync(); await SaveAsync();
} }
public virtual async Task<bool> ShouldAddDeviceInfoAsync() public virtual async Task<bool> ShouldAddDeviceInfoAsync()
{ {
var lastSend = await GetLastDeviceInfoSendTimeAsync(); var lastSend = await GetLastDeviceInfoSendTimeAsync();
return lastSend is null || DateTimeOffset.UtcNow - lastSend > TimeSpan.FromDays(7); return ShouldAddInfo(lastSend);
} }
public virtual async Task<bool> ShouldAddSolutionInformation(Guid solutionId) public virtual async Task<bool> ShouldAddSolutionInformation(Guid solutionId)
{ {
var lastSend = await GetLastSolutionInfoSendTimeAsync(solutionId); var lastSend = await GetLastSolutionInfoSendTimeAsync(solutionId);
return lastSend is null || DateTimeOffset.UtcNow - lastSend > TimeSpan.FromDays(7); return ShouldAddInfo(lastSend);
} }
public virtual async Task<bool> ShouldAddApplicationInfoAsync(Guid applicationId) public virtual async Task<bool> ShouldAddApplicationInfoAsync(Guid applicationId)
{ {
var lastSend = await GetLastApplicationInfoSendTimeAsync(applicationId); var lastSend = await GetLastApplicationInfoSendTimeAsync(applicationId);
return lastSend is null || DateTimeOffset.UtcNow - lastSend > TimeSpan.FromDays(7); return ShouldAddInfo(lastSend);
}
private async Task<DateTimeOffset?> GetLastSolutionInfoSendTimeAsync(Guid solutionId)
{
var state = await GetStateAsync();
return state.Solutions.TryGetValue(solutionId, out var date) ? date : null;
}
private async Task<DateTimeOffset?> GetLastApplicationInfoSendTimeAsync(Guid applicationId)
{
var state = await GetStateAsync();
return state.ApplicationInfos.TryGetValue(applicationId, out var date) ? date : null;
}
private async Task<DateTimeOffset?> GetLastDeviceInfoSendTimeAsync()
{
var state = await GetStateAsync();
return state.LastDeviceInfoAddTime;
} }
private async Task<TelemetryActivityStorageState> GetStateAsync() private async Task<TelemetryActivityStorageState> GetStateAsync()
@ -129,19 +131,15 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
{ {
using var reader = new StreamReader(stream, Encoding.UTF8); using var reader = new StreamReader(stream, Encoding.UTF8);
var json = await reader.ReadToEndAsync(); var json = await reader.ReadToEndAsync();
return JsonSerializer.Deserialize<TelemetryActivityStorageState?>(json, return JsonSerializer.Deserialize<TelemetryActivityStorageState?>(json, GetJsonSerializerOptions())
new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) ?? ?? new TelemetryActivityStorageState();
new TelemetryActivityStorageState(); }) ?? new TelemetryActivityStorageState();
});
return _cachedState; return _cachedState;
} }
private async Task<TResult> WithExclusiveFileLockAsync<TResult>(Func<FileStream, Task<TResult>> action) private async Task<TResult?> WithExclusiveFileLockAsync<TResult>(Func<FileStream, Task<TResult>> action)
{ {
const int maxRetries = 5; for (int i = 0; i < MaxFileRetries; i++)
const int retryDelayMs = 100;
for (int i = 0; i < maxRetries; i++)
{ {
try try
{ {
@ -156,27 +154,29 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
} }
catch (IOException) catch (IOException)
{ {
if (i == maxRetries - 1) if (i == MaxFileRetries - 1)
{ {
throw; return default;
} }
await Task.Delay(retryDelayMs); await Task.Delay(RetryDelayMs);
}
catch
{
return default;
} }
} }
throw new IOException("Unable to acquire file lock for ActivityStorage."); return default;
} }
private Task SaveAsync() private Task SaveAsync()
{ {
var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(), var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(), GetJsonSerializerOptions());
new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
File.WriteAllText(TelemetryPaths.ActivityStorage, json, Encoding.UTF8); File.WriteAllText(TelemetryPaths.ActivityStorage, json, Encoding.UTF8);
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task EnsureFileExistsAsync() private Task EnsureFileExistsAsync()
{ {
try try
@ -190,31 +190,29 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
if (!File.Exists(TelemetryPaths.ActivityStorage)) if (!File.Exists(TelemetryPaths.ActivityStorage))
{ {
var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(), var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(), GetJsonSerializerOptions());
new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true
});
File.WriteAllText(TelemetryPaths.ActivityStorage, json, Encoding.UTF8); File.WriteAllText(TelemetryPaths.ActivityStorage, json, Encoding.UTF8);
} }
} }
catch catch
{ {
//ignored // Ignored intentionally
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
private async Task<DateTimeOffset?> GetLastApplicationInfoSendTimeAsync(Guid applicationId) private static JsonSerializerOptions GetJsonSerializerOptions()
{ {
var state = await GetStateAsync(); return new JsonSerializerOptions
if (state.ApplicationInfos.TryGetValue(applicationId, out var lastActivitySendTime))
{ {
return lastActivitySendTime; PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
} WriteIndented = true
};
}
return null; private static bool ShouldAddInfo(DateTimeOffset? lastSend)
{
return lastSend is null || DateTimeOffset.UtcNow - lastSend > InfoExpirationPeriod;
} }
} }

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

@ -26,6 +26,7 @@ public class TelemetryService : ITelemetryService, IScopedDependency
public IAsyncDisposable TrackActivity(string activityName, Action<ActivityData>? configure = null) public IAsyncDisposable TrackActivity(string activityName, Action<ActivityData>? configure = null)
{ {
Check.NotNullOrEmpty(activityName, nameof(activityName));
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
var activityData = new ActivityData(activityName); var activityData = new ActivityData(activityName);
@ -87,6 +88,7 @@ public class TelemetryService : ITelemetryService, IScopedDependency
public async Task AddActivityAsync(string activityName, Action<ActivityData> configure) public async Task AddActivityAsync(string activityName, Action<ActivityData> configure)
{ {
Check.NotNullOrEmpty(activityName, nameof(activityName));
var activityData = new ActivityData(activityName); var activityData = new ActivityData(activityName);
configure?.Invoke(activityData); configure?.Invoke(activityData);

Loading…
Cancel
Save