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.

data-collection-code-review-2
Emre KARA 8 months 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<bool> ShouldAddDeviceInfoAsync();
Task<bool> ShouldAddSolutionInformation(Guid solutionId);
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
{
private const int MaxFileRetries = 5;
private const int RetryDelayMs = 100;
private static readonly TimeSpan InfoExpirationPeriod = TimeSpan.FromDays(7);
private TelemetryActivityStorageState? _cachedState;
public async Task BufferActivityAsync(ActivityData activityData)
@ -31,19 +35,16 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
public async Task EndSessionAsync()
{
var state = await GetStateAsync();
state.SessionId = null;
await SaveAsync();
}
public async Task<DateTimeOffset?> GetLastActivitySendTimeAsync()
{
var state = await GetStateAsync();
return state.ActivitySendTime;
}
public async Task<Guid> GetOrCreateSessionInfoAsync()
{
var state = await GetStateAsync();
@ -58,7 +59,6 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
state.ActivitySendTime = DateTimeOffset.UtcNow;
state.Activities.Clear();
state.SessionId = null;
await SaveAsync();
}
@ -68,29 +68,12 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
state.Solutions[solutionId] = DateTimeOffset.UtcNow;
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();
return state.LastDeviceInfoAddTime;
state.ApplicationInfos[applicationId] = DateTimeOffset.UtcNow;
await SaveAsync();
}
public async Task MarkDeviceInfoAsAddedAsync()
@ -99,22 +82,41 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
state.LastDeviceInfoAddTime = DateTimeOffset.UtcNow;
await SaveAsync();
}
public virtual async Task<bool> ShouldAddDeviceInfoAsync()
{
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)
{
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)
{
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()
@ -129,19 +131,15 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
{
using var reader = new StreamReader(stream, Encoding.UTF8);
var json = await reader.ReadToEndAsync();
return JsonSerializer.Deserialize<TelemetryActivityStorageState?>(json,
new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }) ??
new TelemetryActivityStorageState();
});
return JsonSerializer.Deserialize<TelemetryActivityStorageState?>(json, GetJsonSerializerOptions())
?? new TelemetryActivityStorageState();
}) ?? new TelemetryActivityStorageState();
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;
const int retryDelayMs = 100;
for (int i = 0; i < maxRetries; i++)
for (int i = 0; i < MaxFileRetries; i++)
{
try
{
@ -156,27 +154,29 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
}
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()
{
var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(),
new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(), GetJsonSerializerOptions());
File.WriteAllText(TelemetryPaths.ActivityStorage, json, Encoding.UTF8);
return Task.CompletedTask;
}
private Task EnsureFileExistsAsync()
{
try
@ -190,31 +190,29 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep
if (!File.Exists(TelemetryPaths.ActivityStorage))
{
var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(),
new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true
});
var json = JsonSerializer.Serialize(_cachedState ?? new TelemetryActivityStorageState(), GetJsonSerializerOptions());
File.WriteAllText(TelemetryPaths.ActivityStorage, json, Encoding.UTF8);
}
}
catch
{
//ignored
// Ignored intentionally
}
return Task.CompletedTask;
}
private async Task<DateTimeOffset?> GetLastApplicationInfoSendTimeAsync(Guid applicationId)
private static JsonSerializerOptions GetJsonSerializerOptions()
{
var state = await GetStateAsync();
if (state.ApplicationInfos.TryGetValue(applicationId, out var lastActivitySendTime))
return new JsonSerializerOptions
{
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)
{
Check.NotNullOrEmpty(activityName, nameof(activityName));
var stopwatch = Stopwatch.StartNew();
var activityData = new ActivityData(activityName);
@ -87,6 +88,7 @@ public class TelemetryService : ITelemetryService, IScopedDependency
public async Task AddActivityAsync(string activityName, Action<ActivityData> configure)
{
Check.NotNullOrEmpty(activityName, nameof(activityName));
var activityData = new ActivityData(activityName);
configure?.Invoke(activityData);

Loading…
Cancel
Save