ApmMetricsService
using Polly;
using Relativity.Transfer.Dto;
using Relativity.Transfer.Resources;
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.Transfer
{
internal class ApmMetricsService : RestServiceBase, IApmMetricsService
{
private const string DefaultEmptyValue = "N/A";
private const int MaxTotalsPerAttemptToLog = 3;
public ApmMetricsService(RelativityConnectionInfo connectionInfo, ClientConfiguration configuration, ITransferLog log)
: base(connectionInfo, configuration, log)
{
}
[AsyncStateMachine(typeof(<SubmitTransferResultMetricsAsync>d__3))]
public Task SubmitTransferResultMetricsAsync(ITransferResult result, CancellationToken token)
{
<SubmitTransferResultMetricsAsync>d__3 stateMachine = default(<SubmitTransferResultMetricsAsync>d__3);
stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
stateMachine.<>4__this = this;
stateMachine.result = result;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
private static AsperaCredential CreateAsperaCredential(ITransferResult result)
{
AsperaCredential result2 = null;
if (result.Configuration.Credential != null)
result2 = new AsperaCredential(result.Configuration.Credential);
return result2;
}
private static string CreateApplication(ITransferResult result)
{
string text = result.Request.Application;
if (string.IsNullOrEmpty(text))
text = result.Request.Name;
if (string.IsNullOrEmpty(text))
text = GlobalSettings.Instance.ApplicationName;
if (string.IsNullOrEmpty(text))
text = "transfer-api";
return text;
}
private static ITransferStatistics CreateTransferStatistics(ITransferResult result, ITransferLog log)
{
if (result.Statistics.Count == 0) {
log?.LogWarning("Statistics List in submitted transfer result is empty", Array.Empty<object>());
return new TransferStatistics(result.Request) {
JobErrorMessage = "Statistics List in submitted transfer result is empty",
JobErrorCode = 0,
TotalRequestBytes = 0,
TotalRequestFiles = 0,
TransferRateMbps = 0
};
}
return result.Statistics.Last();
}
public static ApmMetric CreateApmMetric(ITransferResult result, ITransferStatistics lastStatistic, string application, AsperaCredential asperaCredential, Guid workspaceGuid)
{
ApmMetric apmMetric = new ApmMetric {
Name = "Relativity.DataTransfer.TransferJobResult"
};
apmMetric.CustomData.Add("Application", application);
apmMetric.CustomData.Add("AverageTransferRateMbps", result.TransferRateMbps);
apmMetric.CustomData.Add("BatchNumber", result.Request.BatchNumber);
apmMetric.CustomData.Add("Client", result.Configuration.Client.ToString());
apmMetric.CustomData.Add("ClientRequestId", result.Request.ClientRequestId);
apmMetric.CustomData.Add("ConfigurationBadPathErrorsRetry", result.Configuration.BadPathErrorsRetry);
apmMetric.CustomData.Add("ConfigurationCredential", GetAsperaCredentialName(asperaCredential));
apmMetric.CustomData.Add("AsperaHost", GetAsperaCredentialHost(asperaCredential));
apmMetric.CustomData.Add("ConfigurationFileNotFoundErrorsRetry", result.Configuration.FileNotFoundErrorsRetry);
apmMetric.CustomData.Add("ConfigurationFileTransferHint", result.Configuration.FileTransferHint.ToString());
apmMetric.CustomData.Add("ConfigurationMinDataRateMbps", result.Configuration.MinDataRateMbps);
apmMetric.CustomData.Add("ConfigurationMaxJobParallelism", result.Configuration.MaxJobParallelism);
apmMetric.CustomData.Add("ConfigurationMaxJobRetryAttempts", result.Configuration.MaxJobRetryAttempts);
apmMetric.CustomData.Add("ConfigurationOverwriteFiles", result.Configuration.OverwriteFiles);
apmMetric.CustomData.Add("ConfigurationOverwritePolicy", result.Configuration.ContainsKey("aspera-overwrite-policy") ? result.Configuration["aspera-overwrite-policy"] : "Not configured");
apmMetric.CustomData.Add("ConfigurationPermissionErrorsRetry", result.Configuration.PermissionErrorsRetry);
apmMetric.CustomData.Add("ConfigurationPreserveDates", result.Configuration.PreserveDates);
apmMetric.CustomData.Add("ConfigurationSkipTooLongPaths", GlobalSettings.Instance.SkipTooLongPaths);
apmMetric.CustomData.Add("ConfigurationTargetDataRateMbps", result.Configuration.TargetDataRateMbps);
apmMetric.CustomData.Add("ConfigurationTransferEmptyDirectories", result.Configuration.TransferEmptyDirectories);
apmMetric.CustomData.Add("ConfigurationFileNotFoundErrorsDisabled", result.Configuration.FileNotFoundErrorsDisabled);
apmMetric.CustomData.Add("ConfigurationUseWebApiToggle", result.Configuration.UseLegacyWebApi);
apmMetric.CustomData.Add("Direction", result.Request.Direction.ToString());
apmMetric.CustomData.Add("Elapsed", result.Elapsed);
apmMetric.CustomData.Add("ElapsedSeconds", result.Elapsed.TotalSeconds);
apmMetric.CustomData.Add("EndTime", result.EndTime);
apmMetric.CustomData.Add("JobId", result.Request.JobId);
apmMetric.CustomData.Add("JobErrorCode", lastStatistic.JobErrorCode);
apmMetric.CustomData.Add("JobErrorMessage", lastStatistic.JobErrorMessage);
apmMetric.CustomData.Add("JobLatestIssues", TransferReportBuilder.GetLatestTransferIssues(result, 1));
apmMetric.CustomData.Add("JobUniqueIssues", TransferReportBuilder.GetUniqueTransferIssues(result, 1));
apmMetric.CustomData.Add("JobMinDataRateMbps", result.MinDataRateMbps);
apmMetric.CustomData.Add("JobTargetDataRateMbps", result.TargetDataRateMbps);
apmMetric.CustomData.Add("OperatingSystem", Environment.OSVersion.ToString());
apmMetric.CustomData.Add("OperatingSystemVersion", Environment.OSVersion.Version.ToString());
apmMetric.CustomData.Add("OperatingSystem64Bit", Environment.Is64BitOperatingSystem);
apmMetric.CustomData.Add("Process64Bit", Environment.Is64BitProcess);
apmMetric.CustomData.Add("ProcessorCount", Environment.ProcessorCount);
apmMetric.CustomData.Add("RetryAttempts", result.RetryCount);
apmMetric.CustomData.Add("StartTime", result.StartTime);
apmMetric.CustomData.Add("Status", result.Status.ToString());
apmMetric.CustomData.Add("TargetPath", result.Request.TargetPath);
apmMetric.CustomData.Add("TotalBadPathErrors", result.TotalBadPathErrors);
apmMetric.CustomData.Add("TotalBatchCount", result.Request.TotalBatchCount);
apmMetric.CustomData.Add("TotalSkippedFiles", result.TotalSkippedFiles);
apmMetric.CustomData.Add("TotalFailedFiles", result.TotalFailedFiles);
apmMetric.CustomData.Add("TotalFatalErrors", result.TotalFatalErrors);
apmMetric.CustomData.Add("TotalFilePermissionErrors", result.TotalFilePermissionErrors);
apmMetric.CustomData.Add("TotalFilesNotFound", result.TotalFilesNotFound);
apmMetric.CustomData.Add("TotalRequestBytes", lastStatistic.TotalRequestBytes);
apmMetric.CustomData.Add("TotalRequestFiles", lastStatistic.TotalRequestFiles);
apmMetric.CustomData.Add("TotalTransferredBytes", result.TotalTransferredBytes);
apmMetric.CustomData.Add("TotalTransferredFiles", result.TotalTransferredFiles);
apmMetric.CustomData.Add("WeightedTransferRateMbps", lastStatistic.TransferRateMbps);
apmMetric.CustomData.Add("WorkspaceGuid", workspaceGuid);
apmMetric.CustomData.Add("Attempts", result.Attempts);
for (int i = 0; i < Math.Min(result.TotalRequestedFilesPerAttempt.Count, 3); i++) {
apmMetric.CustomData.Add($"""{i}", result.TotalRequestedFilesPerAttempt[i]);
}
for (int j = 0; j < Math.Min(result.TotalRequestedBytesPerAttempt.Count, 3); j++) {
apmMetric.CustomData.Add($"""{j}", result.TotalRequestedBytesPerAttempt[j]);
}
return apmMetric;
}
private static string GetAsperaCredentialHost(AsperaCredential asperaCredential)
{
if (!(asperaCredential != (AsperaCredential)null))
return "N/A";
return asperaCredential.Host.ToString();
}
private static string GetAsperaCredentialName(AsperaCredential asperaCredential)
{
if (!(asperaCredential != (AsperaCredential)null))
return "N/A";
return asperaCredential.Name;
}
public Task SubmitCountAsync(ApmMetric metric, long count)
{
if (metric == null)
throw new ArgumentNullException("metric");
base.Log.LogDebug("Preparing to submit the '{Name}' metric - Count = {Count} to the '{Host}' Relativity instance.", metric.Name, count, base.ConnectionInfo.Host);
RestClient restClient = new RestClient(new HttpConnectionInfo(base.ConnectionInfo), base.Log, base.TimeoutSeconds, base.MaxRetryAttempts);
string content = SerializationHelper.SerializeToJson(new {
metric = new ApmMetricDto(metric.Name, metric.CustomData),
count = count
});
Task<string> result = restClient.RequestJsonPostAsync("/relativity.rest/api/Relativity.Telemetry.Services.Metrics.IMetricsModule/APM%20Manager/LogCountAsync", content, (int retryAttempt) => TimeSpan.FromSeconds(Math.Pow(2, (double)retryAttempt)), delegate(Exception exception, TimeSpan timespan, Context context) {
base.Log.LogError(exception, "Retry - {Timespan} - Failed to submit the metric information to the '{Host}' Relativity instance.", timespan, base.ConnectionInfo.Host);
}, (HttpStatusCode code) => "APM log count", (HttpStatusCode code) => string.Format(CultureInfo.CurrentCulture, CoreStrings.LogCountHttpPostExceptionMessage, base.ConnectionInfo.Host), CancellationToken.None);
base.Log.LogDebug("Successfully submitted the '{Name}' metric - Count = {Count} to the '{Host}' Relativity instance.", metric.Name, count, base.ConnectionInfo.Host);
return result;
}
public Task SubmitTimerAsync(ApmMetric metric, double milliseconds)
{
if (metric == null)
throw new ArgumentNullException("metric");
base.Log.LogDebug("Preparing to submit the '{Name}' timer metric (Milliseconds = {Milliseconds}) to the '{Host}' Relativity instance.", metric.Name, milliseconds, base.ConnectionInfo.Host);
RestClient restClient = new RestClient(new HttpConnectionInfo(base.ConnectionInfo), base.Log, base.TimeoutSeconds, base.MaxRetryAttempts);
string content = SerializationHelper.SerializeToJson(new {
metric,
milliseconds
});
Task<string> result = restClient.RequestJsonPostAsync("/relativity.rest/api/Relativity.Telemetry.Services.Metrics.IMetricsModule/APM%20Manager/LogTimerAsync", content, (int retryAttempt) => TimeSpan.FromSeconds(Math.Pow(2, (double)retryAttempt)), delegate(Exception exception, TimeSpan timespan, Context context) {
base.Log.LogError(exception, "Retry - {Timespan} - Failed to submit the timer metric information to the '{Host}' Relativity instance.", timespan, base.ConnectionInfo.Host);
}, (HttpStatusCode code) => "APM log timer count", (HttpStatusCode code) => string.Format(CultureInfo.CurrentCulture, CoreStrings.LogCountHttpPostExceptionMessage, base.ConnectionInfo.Host), CancellationToken.None);
base.Log.LogDebug("Successfully submitted the '{Name}' timer metric (Milliseconds = {Milliseconds}) to the '{Host}' Relativity instance.", metric.Name, milliseconds, base.ConnectionInfo.Host);
return result;
}
[AsyncStateMachine(typeof(<GetWorkspaceGuidAsync>d__12))]
private Task<Guid> GetWorkspaceGuidAsync(CancellationToken token)
{
<GetWorkspaceGuidAsync>d__12 stateMachine = default(<GetWorkspaceGuidAsync>d__12);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<Guid>.Create();
stateMachine.<>4__this = this;
stateMachine.token = token;
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
}
}