Files
raven/server/AyaNova/Controllers/ServerMetricsController.cs
2020-05-27 19:51:18 +00:00

171 lines
8.2 KiB
C#

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using AyaNova.Models;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Biz;
namespace AyaNova.Api.Controllers
{
/// <summary>
/// Server metrics
/// </summary>
[ApiController]
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/server-metric")]
[Authorize]
public class ServerMetricsController : ControllerBase
{
private readonly AyContext ct;
private readonly ILogger<LogFilesController> log;
private readonly ApiServerState serverState;
private const int MAX_RECORDS_BEFORE_DOWNSAMPLING = 400;
/// <summary>
/// ctor
/// </summary>
/// <param name="dbcontext"></param>
/// <param name="logger"></param>
/// <param name="apiServerState"></param>
public ServerMetricsController(AyContext dbcontext, ILogger<LogFilesController> logger, ApiServerState apiServerState)
{
ct = dbcontext;
log = logger;
serverState = apiServerState;
}
/// <summary>
/// Get all server metrics for time period specified
/// </summary>
/// <param name="tsStart">Start timestamp UTC</param>
/// <param name="tsEnd">End timestamp UTC</param>
/// <param name="maxRecords">Optional maximum records to return. If there are more records for the time period selected than this value the result will be downsampled. There is a 400 record maximum fixed default</param>
/// <returns>Snapshot of metrics</returns>
[HttpGet]
public async Task<IActionResult> GetMetrics([FromQuery] DateTime tsStart, [FromQuery] DateTime tsEnd, [FromQuery] int? maxRecords)
{
if (serverState.IsClosed)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!Authorized.HasReadFullRole(HttpContext.Items, AyaType.Metrics))
{
return StatusCode(403, new ApiNotAuthorizedResponse());
}
//use specified values or just return all
maxRecords ??= MAX_RECORDS_BEFORE_DOWNSAMPLING;
List<MetricMM> MinuteMetrics = new List<MetricMM>();
//touniversal is because the parameters are converted to local time here, but then sent to the query as local time as well and not universal time which is what it should be
MinuteMetrics = await ct.MetricMM.AsNoTracking().Where(z => z.t >= tsStart.ToUniversalTime() && z.t <= tsEnd.ToUniversalTime()).OrderBy(z => z.t).ToListAsync();
//var v=await ct.MetricMM.AsNoTracking().Where(z => z.t >= tsStart.ToUniversalTime() && z.t <= tsEnd.ToUniversalTime()).OrderByDescending(z => z.t).ToListAsync();
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserIdFromContext.Id(HttpContext.Items), 0, AyaType.Metrics, AyaEvent.Retrieved), ct);
//Downsample? This should work for longer time period metrics because the minute ones are the greatest quantity
if (maxRecords < MinuteMetrics.Count)
{
//yes, so need to return individual labels and downsampled data as they wont' sync anymore
var dsCPU = MinuteMetrics.Select(z => new Tuple<double, double>(new DateTimeOffset(z.t).ToUnixTimeSeconds(), z.CPU)).ToList();
dsCPU = Util.DataUtil.LargestTriangleThreeBuckets(dsCPU, (int)maxRecords) as List<Tuple<double, double>>;
var dsAllocated = MinuteMetrics.Select(z => new Tuple<double, double>(new DateTimeOffset(z.t).ToUnixTimeSeconds(), z.Allocated)).ToList();
dsAllocated = Util.DataUtil.LargestTriangleThreeBuckets(dsAllocated, (int)maxRecords) as List<Tuple<double, double>>;
var dsWorkingSet = MinuteMetrics.Select(z => new Tuple<double, double>(new DateTimeOffset(z.t).ToUnixTimeSeconds(), z.WorkingSet)).ToList();
dsWorkingSet = Util.DataUtil.LargestTriangleThreeBuckets(dsWorkingSet, (int)maxRecords) as List<Tuple<double, double>>;
var dsPrivateBytes = MinuteMetrics.Select(z => new Tuple<double, double>(new DateTimeOffset(z.t).ToUnixTimeSeconds(), z.PrivateBytes)).ToList();
dsPrivateBytes = Util.DataUtil.LargestTriangleThreeBuckets(dsPrivateBytes, (int)maxRecords) as List<Tuple<double, double>>;
var dsGen0 = MinuteMetrics.Select(z => new Tuple<double, double>(new DateTimeOffset(z.t).ToUnixTimeSeconds(), z.Gen0)).ToList();
dsGen0 = Util.DataUtil.LargestTriangleThreeBuckets(dsGen0, (int)maxRecords) as List<Tuple<double, double>>;
var dsGen1 = MinuteMetrics.Select(z => new Tuple<double, double>(new DateTimeOffset(z.t).ToUnixTimeSeconds(), z.Gen1)).ToList();
dsGen1 = Util.DataUtil.LargestTriangleThreeBuckets(dsGen1, (int)maxRecords) as List<Tuple<double, double>>;
var dsGen2 = MinuteMetrics.Select(z => new Tuple<double, double>(new DateTimeOffset(z.t).ToUnixTimeSeconds(), z.Gen2)).ToList();
dsGen2 = Util.DataUtil.LargestTriangleThreeBuckets(dsGen2, (int)maxRecords) as List<Tuple<double, double>>;
var ret = new
{
DownSampled = true,
MetricMM = new
{
cpuLabels = dsCPU.Select(z => DateTimeOffset.FromUnixTimeSeconds((long)z.Item1)).ToArray(),
cpu = dsCPU.Select(z => z.Item2).ToArray(),
get0Labels = dsGen0.Select(z => DateTimeOffset.FromUnixTimeSeconds((long)z.Item1)).ToArray(),
gen0 = dsGen0.Select(z => (int)z.Item2).ToArray(),
get1Labels = dsGen1.Select(z => DateTimeOffset.FromUnixTimeSeconds((long)z.Item1)).ToArray(),
gen1 = dsGen1.Select(z => (int)z.Item2).ToArray(),
get2Labels = dsGen2.Select(z => DateTimeOffset.FromUnixTimeSeconds((long)z.Item1)).ToArray(),
gen2 = dsGen2.Select(z => (int)z.Item2).ToArray(),
allocatedLabels = dsAllocated.Select(z => DateTimeOffset.FromUnixTimeSeconds((long)z.Item1)).ToArray(),
allocated = dsAllocated.Select(z => (long)z.Item2).ToArray(),
workingSetLabels = dsWorkingSet.Select(z => DateTimeOffset.FromUnixTimeSeconds((long)z.Item1)).ToArray(),
workingSet = dsWorkingSet.Select(z => (long)z.Item2).ToArray(),
privateBytesLabels = dsPrivateBytes.Select(z => DateTimeOffset.FromUnixTimeSeconds((long)z.Item1)).ToArray(),
privateBytes = dsPrivateBytes.Select(z => (long)z.Item2).ToArray()
}
};
return Ok(ApiOkResponse.Response(ret));
}
else
{
var tsOffset = new TimeSpan(0);
var ret = new
{
DownSampled = false,
MetricMM = new
{
labels = MinuteMetrics.Select(z => z.t.ToUniversalTime()).ToArray(),//db is in UTC, ef core retrieves as if it was local
cpu = MinuteMetrics.Select(z => z.CPU).ToArray(),
gen0 = MinuteMetrics.Select(z => z.Gen0).ToArray(),
gen1 = MinuteMetrics.Select(z => z.Gen1).ToArray(),
gen2 = MinuteMetrics.Select(z => z.Gen2).ToArray(),
allocated = MinuteMetrics.Select(z => z.Allocated).ToArray(),
workingSet = MinuteMetrics.Select(z => z.WorkingSet).ToArray(),
privateBytes = MinuteMetrics.Select(z => z.PrivateBytes).ToArray()
}
};
return Ok(ApiOkResponse.Response(ret));
}
}
//------------
}
}