wabbajack/Wabbajack.Server/Controllers/Metrics.cs

175 lines
5.7 KiB
C#
Raw Normal View History

using System.Reflection;
2021-11-29 23:35:23 +00:00
using System.Text.Json;
using Chronic.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
2020-06-15 04:05:00 +00:00
using Nettle;
using Wabbajack.Common;
using Wabbajack.DTOs.ServerResponses;
2021-11-27 18:31:35 +00:00
using Wabbajack.Server.DataModels;
using Wabbajack.Server.DTOs;
2021-10-23 16:51:17 +00:00
namespace Wabbajack.BuildServer.Controllers;
2020-05-09 13:04:38 +00:00
2021-10-23 16:51:17 +00:00
[ApiController]
[Route("/metrics")]
public class MetricsController : ControllerBase
{
private static readonly Func<object, string> ReportTemplate = NettleEngine.GetCompiler().Compile(@"
2020-06-15 04:05:00 +00:00
<html><body>
<h2>Tar Report for {{$.key}}</h2>
<h3>Ban Status: {{$.status}}</h3>
<table>
{{each $.log }}
<tr>
<td>{{$.Timestamp}}</td>
<td>{{$.Path}}</td>
<td>{{$.Key}}</td>
</tr>
{{/each}}
</table>
</body></html>
");
2021-10-23 16:51:17 +00:00
private static Func<object, string> _totalListTemplate;
private readonly AppSettings _settings;
private ILogger<MetricsController> _logger;
2021-11-27 18:31:35 +00:00
private readonly Metrics _metricsStore;
2020-06-15 04:05:00 +00:00
2021-11-27 18:31:35 +00:00
public MetricsController(ILogger<MetricsController> logger, Metrics metricsStore,
2021-10-23 16:51:17 +00:00
AppSettings settings)
{
_logger = logger;
_settings = settings;
2021-11-27 18:31:35 +00:00
_metricsStore = metricsStore;
2021-10-23 16:51:17 +00:00
}
2021-02-17 05:46:05 +00:00
2021-10-23 16:51:17 +00:00
private static Func<object, string> TotalListTemplate
{
get
{
if (_totalListTemplate == null)
2021-02-17 05:46:05 +00:00
{
2021-10-23 16:51:17 +00:00
var resource = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("Wabbajack.Server.Controllers.Templates.TotalListTemplate.html")!
.ReadAllText();
_totalListTemplate = NettleEngine.GetCompiler().Compile(resource);
2021-02-17 05:46:05 +00:00
}
2021-10-23 16:51:17 +00:00
return _totalListTemplate;
2021-02-17 05:46:05 +00:00
}
2021-10-23 16:51:17 +00:00
}
2021-02-17 05:46:05 +00:00
2021-10-23 16:51:17 +00:00
[HttpGet]
2021-11-30 03:30:24 +00:00
[Route("{subject}/{value}")]
public async Task<Result> LogMetricAsync(string subject, string value)
2021-10-23 16:51:17 +00:00
{
var date = DateTime.UtcNow;
var metricsKey = Request.Headers[_settings.MetricsKeyHeader].FirstOrDefault();
2021-02-17 05:46:05 +00:00
2021-10-23 16:51:17 +00:00
// Used in tests
2021-11-30 03:30:24 +00:00
if (value is "Default" or "untitled" || subject == "failed_download" || Guid.TryParse(value, out _))
2021-10-23 16:51:17 +00:00
return new Result {Timestamp = date};
2021-02-18 05:44:54 +00:00
2021-11-27 18:31:35 +00:00
await _metricsStore.Ingest(new Metric
2021-02-17 05:46:05 +00:00
{
2021-11-27 18:31:35 +00:00
Timestamp = DateTime.UtcNow,
2021-11-30 03:30:24 +00:00
Action = subject,
Subject = value,
2021-11-27 18:31:35 +00:00
MetricsKey = metricsKey,
UserAgent = Request.Headers.UserAgent.FirstOrDefault() ?? "<unknown>",
2021-10-23 16:51:17 +00:00
});
2021-11-27 18:31:35 +00:00
return new Result {Timestamp = date};
2021-10-23 16:51:17 +00:00
}
2021-11-29 23:35:23 +00:00
private static byte[] EOL = {(byte)'\n'};
2021-12-01 22:53:32 +00:00
private static byte[] LBRACKET = {(byte)'['};
private static byte[] RBRACKET = {(byte)']'};
private static byte[] COMMA = {(byte) ','};
2021-11-29 23:35:23 +00:00
[HttpGet]
[Route("dump")]
2022-02-09 18:24:31 +00:00
public async Task GetMetrics([FromQuery] string action, [FromQuery] string from, [FromQuery] string? to, [FromQuery] string? subject)
2021-11-29 23:35:23 +00:00
{
var parser = new Parser();
to ??= "now";
var toDate = parser.Parse(to).Start;
var fromDate = parser.Parse(from).Start;
var records = _metricsStore.GetRecords(fromDate!.Value, toDate!.Value, action);
2022-02-09 18:24:31 +00:00
2021-11-29 23:35:23 +00:00
Response.Headers.ContentType = "application/json";
await foreach (var record in records)
{
2022-02-09 18:24:31 +00:00
if (!string.IsNullOrWhiteSpace(subject) && !record.Subject.Contains(subject))
continue;
2021-11-29 23:35:23 +00:00
await JsonSerializer.SerializeAsync(Response.Body, record);
await Response.Body.WriteAsync(EOL);
}
}
2022-02-09 18:24:31 +00:00
[HttpGet]
[Route("report")]
2021-12-01 06:06:00 +00:00
[ResponseCache(Duration = 60 * 60 * 4, VaryByQueryKeys = new [] {"action", "from", "to"})]
2021-12-02 00:12:21 +00:00
public void GetReport([FromQuery] string action, [FromQuery] string from, [FromQuery] string? to)
{
var parser = new Parser();
to ??= "now";
var toDate = parser.Parse(to).Start!.Value.TruncateToDate();
var groupFilterStart = parser.Parse("three days ago").Start!.Value.TruncateToDate();
toDate = new DateTime(toDate.Year, toDate.Month, toDate.Day);
2021-12-02 00:12:21 +00:00
var prefetch = _metricsStore.GetRecordsParallel(groupFilterStart, toDate, action)
.Where(d => d.Action != d.Subject)
.Select(d => d.GroupingSubject)
.ToHashSet();;
var fromDate = parser.Parse(from).Start!.Value.TruncateToDate();
2021-12-02 00:12:21 +00:00
var counts = _metricsStore.GetRecordsParallel(fromDate, toDate, action)
.Where(r => r.Subject != r.Action)
.Where(r => prefetch.Contains(r.GroupingSubject))
.Select(r => (r.Timestamp.TruncateToDate(), r.GroupingSubject))
.ToLookup(r => r, v => 1)
.AsParallel()
.Select(entry => KeyValuePair.Create(entry.Key, entry.Count()))
.ToDictionary(kv => kv.Key, kv => kv.Value);
Response.Headers.ContentType = "application/json";
var row = new Dictionary<string, object>();
2021-12-01 22:53:32 +00:00
2021-12-02 00:12:21 +00:00
Response.Body.Write(LBRACKET);
for (var d = fromDate; d <= toDate; d = d.AddDays(1))
{
row["_Timestamp"] = d;
foreach (var group in prefetch)
{
if (counts.TryGetValue((d, group), out var found))
row[group] = found;
else
row[group] = 0;
}
2021-12-02 00:12:21 +00:00
JsonSerializer.Serialize(Response.Body, row);
Response.Body.Write(EOL);
2021-12-01 22:53:32 +00:00
if (d != toDate)
2021-12-02 00:12:21 +00:00
Response.Body.Write(COMMA);
}
2021-12-02 00:12:21 +00:00
Response.Body.Write(RBRACKET);
2021-12-01 22:53:32 +00:00
}
2021-11-29 23:35:23 +00:00
2021-10-23 16:51:17 +00:00
public class Result
{
public DateTime Timestamp { get; set; }
}
}