Files
step3/api/QuestionsApi/Services/ServiceMetrics.cs
T

95 lines
5.0 KiB
C#

using System.Text;
namespace QuestionsApi.Services;
public sealed class ServiceMetrics
{
private long _attemptsCreated;
private long _attemptDuplicates;
private long _counterFailures;
private long _ratingFailures;
private long _reconciliations;
private long _lastReconciliationUnixSeconds;
private long _httpRequests;
private long _httpDurationMicroseconds;
private long _answerStatisticsBackfillComplete;
private readonly System.Collections.Concurrent.ConcurrentDictionary<string, long> _dynamicSelections = new();
private readonly System.Collections.Concurrent.ConcurrentDictionary<string, long> _dynamicAttempts = new();
private readonly System.Collections.Concurrent.ConcurrentDictionary<string, long> _dynamicCorrect = new();
private long _dynamicTargetGapTotal;
public void AttemptCreated() => Interlocked.Increment(ref _attemptsCreated);
public void AttemptDuplicate() => Interlocked.Increment(ref _attemptDuplicates);
public void CounterFailure() => Interlocked.Increment(ref _counterFailures);
public void RatingFailure() => Interlocked.Increment(ref _ratingFailures);
public void AnswerStatisticsBackfillCompleted() => Interlocked.Exchange(ref _answerStatisticsBackfillComplete, 1);
public void Reconciled()
{
Interlocked.Increment(ref _reconciliations);
Interlocked.Exchange(ref _lastReconciliationUnixSeconds, DateTimeOffset.UtcNow.ToUnixTimeSeconds());
}
public void HttpRequest(TimeSpan duration)
{
Interlocked.Increment(ref _httpRequests);
Interlocked.Add(ref _httpDurationMicroseconds, (long)(duration.TotalMilliseconds * 1000));
}
public void DynamicSelected(string tier, int targetGap)
{
_dynamicSelections.AddOrUpdate(tier, 1, (_, value) => value + 1);
Interlocked.Add(ref _dynamicTargetGapTotal, Math.Abs(targetGap));
}
public void DynamicAttempt(string tier, bool correct)
{
_dynamicAttempts.AddOrUpdate(tier, 1, (_, value) => value + 1);
if (correct) _dynamicCorrect.AddOrUpdate(tier, 1, (_, value) => value + 1);
}
public string RenderPrometheus()
{
var output = new StringBuilder();
AppendCounter(output, "step3_question_attempts_created_total", Interlocked.Read(ref _attemptsCreated));
AppendCounter(output, "step3_question_attempt_duplicates_total", Interlocked.Read(ref _attemptDuplicates));
AppendCounter(output, "step3_answer_counter_failures_total", Interlocked.Read(ref _counterFailures));
AppendCounter(output, "step3_rating_failures_total", Interlocked.Read(ref _ratingFailures));
AppendCounter(output, "step3_answer_reconciliations_total", Interlocked.Read(ref _reconciliations));
AppendCounter(output, "step3_http_requests_total", Interlocked.Read(ref _httpRequests));
output.AppendLine("# TYPE step3_dynamic_selections_total counter");
output.AppendLine("# TYPE step3_dynamic_attempts_total counter");
output.AppendLine("# TYPE step3_dynamic_correct_total counter");
foreach (var tier in _dynamicSelections.Keys.Order())
{
AppendLabeledSample(output, "step3_dynamic_selections_total", tier, _dynamicSelections.GetValueOrDefault(tier));
AppendLabeledSample(output, "step3_dynamic_attempts_total", tier, _dynamicAttempts.GetValueOrDefault(tier));
AppendLabeledSample(output, "step3_dynamic_correct_total", tier, _dynamicCorrect.GetValueOrDefault(tier));
}
AppendCounter(output, "step3_dynamic_target_gap_points_total", Interlocked.Read(ref _dynamicTargetGapTotal));
output.AppendLine("# TYPE step3_http_request_duration_seconds summary");
output.Append("step3_http_request_duration_seconds_count ")
.Append(Interlocked.Read(ref _httpRequests))
.AppendLine();
output.Append("step3_http_request_duration_seconds_sum ")
.Append((Interlocked.Read(ref _httpDurationMicroseconds) / 1_000_000.0).ToString("0.######", System.Globalization.CultureInfo.InvariantCulture))
.AppendLine();
output.AppendLine("# TYPE step3_answer_last_reconciliation_unixtime gauge");
output.Append("step3_answer_last_reconciliation_unixtime ")
.Append(Interlocked.Read(ref _lastReconciliationUnixSeconds))
.AppendLine();
output.AppendLine("# TYPE step3_answer_statistics_v2_backfill_complete gauge");
output.Append("step3_answer_statistics_v2_backfill_complete ")
.Append(Interlocked.Read(ref _answerStatisticsBackfillComplete))
.AppendLine();
return output.ToString();
}
private static void AppendCounter(StringBuilder output, string name, long value)
{
output.Append("# TYPE ").Append(name).AppendLine(" counter");
output.Append(name).Append(' ').Append(value).AppendLine();
}
private static void AppendLabeledSample(StringBuilder output, string name, string tier, long value)
{
output.Append(name).Append("{tier=\"").Append(tier.Replace("\"", "")).Append("\"} ").Append(value).AppendLine();
}
}