<PackageReference Include="Relativity.Server.Transfer.SDK" Version="7.7.0" />

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; } } }