forked from ddrilling/AsbCloudServer
Add request tracker. it also track users activity.
This commit is contained in:
parent
170693f445
commit
bfb76b9dc0
19
AsbCloudApp/Data/RequestLogDto.cs
Normal file
19
AsbCloudApp/Data/RequestLogDto.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Data
|
||||||
|
{
|
||||||
|
public class RequestLogDto
|
||||||
|
{
|
||||||
|
public string UserLogin { get; set; }
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public string UserIp { get; set; }
|
||||||
|
public string RequestMethod { get; set; }
|
||||||
|
public string RequestPath { get; set; }
|
||||||
|
public string Referer { get; set; }
|
||||||
|
public long ElapsedMilliseconds { get; set; }
|
||||||
|
public int Status { get; set; }
|
||||||
|
public string ExceptionMessage { get; set; }
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
public string ExceptionStack { get; set; }
|
||||||
|
}
|
||||||
|
}
|
19
AsbCloudApp/Data/RequestLogUserDto.cs
Normal file
19
AsbCloudApp/Data/RequestLogUserDto.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Data
|
||||||
|
{
|
||||||
|
public class RequestLogUserDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Login { get; set; }
|
||||||
|
public string Ip { get; set; }
|
||||||
|
public long ElapsedMs { get; set; }
|
||||||
|
public DateTime LastDate { get; set; }
|
||||||
|
public long Requests { get; set; }
|
||||||
|
public long Errors { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
AsbCloudApp/Services/IRequestTracker.cs
Normal file
17
AsbCloudApp/Services/IRequestTracker.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using AsbCloudApp.Data;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace AsbCloudApp.Services
|
||||||
|
{
|
||||||
|
public interface IRequerstTrackerService
|
||||||
|
{
|
||||||
|
void RegisterRequest(RequestLogDto requestLog);
|
||||||
|
void RegisterRequestError(RequestLogDto requestLog, Exception ex);
|
||||||
|
IEnumerable<RequestLogDto> GetAll(int take = -1);
|
||||||
|
IEnumerable<RequestLogDto> GetFast(int take = -1);
|
||||||
|
IEnumerable<RequestLogDto> GetSlow(int take = -1);
|
||||||
|
IEnumerable<RequestLogDto> GetError(int take = -1);
|
||||||
|
IEnumerable<RequestLogUserDto> GetUsersStat(int take = -1);
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ dotnet ef migrations remvoe <MigrationName> --project AsbCloudDb
|
|||||||
|
|
||||||
#backup
|
#backup
|
||||||
```
|
```
|
||||||
sudo -u postgres pg_dump -U postgres postgres -W | gzip > 2021-09-27_dump.sql.gz
|
sudo -u postgres pg_dump -U postgres postgres -W | gzip > 2021-09-27_dump.sql.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
#restore
|
#restore
|
||||||
|
@ -19,6 +19,7 @@ namespace AsbCloudInfrastructure
|
|||||||
options.UseNpgsql(configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Scoped);
|
options.UseNpgsql(configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Scoped);
|
||||||
|
|
||||||
services.AddScoped<IAsbCloudDbContext>(provider => provider.GetService<AsbCloudDbContext>());
|
services.AddScoped<IAsbCloudDbContext>(provider => provider.GetService<AsbCloudDbContext>());
|
||||||
|
services.AddScoped<IFileShareService, GoogleDriveService>();
|
||||||
|
|
||||||
services.AddHostedService<ReportsBackgroundService>();
|
services.AddHostedService<ReportsBackgroundService>();
|
||||||
services.AddHostedService<TelemetryAnalyticsBackgroundService>();
|
services.AddHostedService<TelemetryAnalyticsBackgroundService>();
|
||||||
@ -26,6 +27,7 @@ namespace AsbCloudInfrastructure
|
|||||||
services.AddSingleton(new CacheDb());
|
services.AddSingleton(new CacheDb());
|
||||||
services.AddSingleton<ITelemetryTracker, TelemetryTracker>();
|
services.AddSingleton<ITelemetryTracker, TelemetryTracker>();
|
||||||
services.AddSingleton<IReportsBackgroundQueue, ReportsBackgroundQueue>();
|
services.AddSingleton<IReportsBackgroundQueue, ReportsBackgroundQueue>();
|
||||||
|
services.AddSingleton<IRequerstTrackerService, RequerstTrackerService>();
|
||||||
|
|
||||||
services.AddTransient<IAuthService, AuthService>();
|
services.AddTransient<IAuthService, AuthService>();
|
||||||
services.AddTransient<IWellService, WellService>();
|
services.AddTransient<IWellService, WellService>();
|
||||||
@ -45,7 +47,6 @@ namespace AsbCloudInfrastructure
|
|||||||
services.AddTransient<IDrillingProgramService, DrillingProgramService>();
|
services.AddTransient<IDrillingProgramService, DrillingProgramService>();
|
||||||
services.AddTransient<IDrillParamsService, DrillParamsService>();
|
services.AddTransient<IDrillParamsService, DrillParamsService>();
|
||||||
services.AddTransient<IDrillFlowChartService, DrillFlowChartService>();
|
services.AddTransient<IDrillFlowChartService, DrillFlowChartService>();
|
||||||
services.AddTransient<IFileShareService, GoogleDriveService>();
|
|
||||||
|
|
||||||
// admin crud services:
|
// admin crud services:
|
||||||
services.AddTransient<ICrudService<DepositDto>, CrudServiceBase<DepositDto, Deposit>>();
|
services.AddTransient<ICrudService<DepositDto>, CrudServiceBase<DepositDto, Deposit>>();
|
||||||
|
133
AsbCloudInfrastructure/Services/RequerstTrackerService.cs
Normal file
133
AsbCloudInfrastructure/Services/RequerstTrackerService.cs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
using AsbCloudApp.Data;
|
||||||
|
using AsbCloudApp.Services;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AsbCloudInfrastructure.Services
|
||||||
|
{
|
||||||
|
public class RequerstTrackerService : IRequerstTrackerService
|
||||||
|
{
|
||||||
|
const int fastRequestsCount = 1000;
|
||||||
|
const int slowRequestsCount = 1000;
|
||||||
|
const int errorRequestsCount = 1000;
|
||||||
|
const int span = 100;
|
||||||
|
|
||||||
|
const int fastLimitMs = 500;
|
||||||
|
static readonly char[] stackTraceSeparators = "\r\n".ToCharArray();
|
||||||
|
|
||||||
|
ConcurrentQueue<RequestLogDto> fastRequests = new ConcurrentQueue<RequestLogDto>();
|
||||||
|
ConcurrentQueue<RequestLogDto> slowRequests = new ConcurrentQueue<RequestLogDto>();
|
||||||
|
ConcurrentQueue<RequestLogDto> errorRequests = new ConcurrentQueue<RequestLogDto>();
|
||||||
|
ConcurrentDictionary<string, RequestLogUserDto> users = new ConcurrentDictionary<string, RequestLogUserDto>();
|
||||||
|
|
||||||
|
private IEnumerable<RequestLogDto> Get(IEnumerable<RequestLogDto> list, int take = -1)
|
||||||
|
{
|
||||||
|
IEnumerable<RequestLogDto> orderedlist = list.OrderByDescending(r => r.Date);
|
||||||
|
if (take > 0)
|
||||||
|
orderedlist = orderedlist.Take(take);
|
||||||
|
return orderedlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<RequestLogUserDto> GetUsersStat(int take = -1)
|
||||||
|
{
|
||||||
|
IEnumerable<RequestLogUserDto> result = users.Values.OrderByDescending(u => u.LastDate);
|
||||||
|
if (take > 0)
|
||||||
|
result = result.Take(take);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<RequestLogDto> GetAll(int take = -1)
|
||||||
|
{
|
||||||
|
var result = fastRequests
|
||||||
|
.Union(slowRequests)
|
||||||
|
.Union(errorRequests);
|
||||||
|
|
||||||
|
return Get(result, take);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<RequestLogDto> GetFast(int take = -1)
|
||||||
|
=> Get(fastRequests, take);
|
||||||
|
|
||||||
|
public IEnumerable<RequestLogDto> GetSlow(int take = -1)
|
||||||
|
=> Get(slowRequests, take);
|
||||||
|
|
||||||
|
public IEnumerable<RequestLogDto> GetError(int take = -1)
|
||||||
|
=> Get(errorRequests, take);
|
||||||
|
|
||||||
|
public void RegisterRequest(RequestLogDto requestLog)
|
||||||
|
{
|
||||||
|
if (requestLog.Status < 200)
|
||||||
|
return;
|
||||||
|
requestLog.Date = DateTime.Now;
|
||||||
|
if (requestLog.ElapsedMilliseconds > fastLimitMs)
|
||||||
|
RegisterSlowRequest(requestLog);
|
||||||
|
else
|
||||||
|
RegisterFastRequest(requestLog);
|
||||||
|
|
||||||
|
UpdateUserStat(requestLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterFastRequest(RequestLogDto requestLog)
|
||||||
|
{
|
||||||
|
fastRequests.Enqueue(requestLog);
|
||||||
|
if (fastRequests.Count > fastRequestsCount + span)
|
||||||
|
while (fastRequests.Count > fastRequestsCount)
|
||||||
|
fastRequests.TryDequeue(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterSlowRequest(RequestLogDto requestLog)
|
||||||
|
{
|
||||||
|
slowRequests.Enqueue(requestLog);
|
||||||
|
if (slowRequests.Count > slowRequestsCount + span)
|
||||||
|
while (slowRequests.Count > slowRequestsCount)
|
||||||
|
slowRequests.TryDequeue(out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterRequestError(RequestLogDto requestLog, Exception ex)
|
||||||
|
{
|
||||||
|
requestLog.Date = DateTime.Now;
|
||||||
|
requestLog.ExceptionMessage = ex.InnerException?.InnerException?.Message
|
||||||
|
?? ex.InnerException?.Message
|
||||||
|
?? ex.Message;
|
||||||
|
requestLog.ExceptionStack = ex.StackTrace?.Split(stackTraceSeparators)[0];
|
||||||
|
errorRequests.Enqueue(requestLog);
|
||||||
|
if (errorRequests.Count > errorRequestsCount + span)
|
||||||
|
while (errorRequests.Count > errorRequestsCount)
|
||||||
|
errorRequests.TryDequeue(out _);
|
||||||
|
|
||||||
|
UpdateUserStat(requestLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUserStat(RequestLogDto requestLog)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(requestLog.UserLogin))
|
||||||
|
{
|
||||||
|
var key = $"{requestLog?.UserId}>{requestLog?.UserIp}";
|
||||||
|
if (!users.ContainsKey(key))
|
||||||
|
users[key] = new RequestLogUserDto
|
||||||
|
{
|
||||||
|
Id = requestLog.UserId,
|
||||||
|
Ip = requestLog.UserIp,
|
||||||
|
Login = requestLog.UserLogin,
|
||||||
|
};
|
||||||
|
users[key].ElapsedMs += requestLog.ElapsedMilliseconds;
|
||||||
|
users[key].LastDate = requestLog.Date;
|
||||||
|
users[key].Requests++;
|
||||||
|
if(!string.IsNullOrEmpty(requestLog.ExceptionMessage))
|
||||||
|
users[key].Errors++;
|
||||||
|
|
||||||
|
if(users.Count > 1000)
|
||||||
|
{
|
||||||
|
var count = 900 - users.Count;
|
||||||
|
var toRemove = users.OrderBy(kv => kv.Value.LastDate).Take(count);
|
||||||
|
foreach (var kv in toRemove)
|
||||||
|
users.TryRemove(kv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
AsbCloudWebApi/Controllers/RequerstTrackerController.cs
Normal file
54
AsbCloudWebApi/Controllers/RequerstTrackerController.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using AsbCloudApp.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace AsbCloudWebApi.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
public class RequerstTrackerController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly IRequerstTrackerService service;
|
||||||
|
|
||||||
|
public RequerstTrackerController(IRequerstTrackerService service)
|
||||||
|
{
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult GetAll(int take = 512)
|
||||||
|
{
|
||||||
|
var result = service.GetAll(take);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("fast")]
|
||||||
|
public IActionResult GetFast(int take = 512)
|
||||||
|
{
|
||||||
|
var result = service.GetFast(take);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("slow")]
|
||||||
|
public IActionResult GetSlow(int take = 512)
|
||||||
|
{
|
||||||
|
var result = service.GetSlow(take);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("error")]
|
||||||
|
public IActionResult GetError(int take = 512)
|
||||||
|
{
|
||||||
|
var result = service.GetError(take);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("users")]
|
||||||
|
public IActionResult GetUsersStat(int take = 512)
|
||||||
|
{
|
||||||
|
var result = service.GetUsersStat(take);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -73,9 +73,48 @@ namespace AsbCloudWebApi
|
|||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseCors("ClientPermission");
|
app.UseCors("ClientPermission");
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseResponseCaching();
|
||||||
|
//app.UseResponseCompression();
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.Use(async (context, next) => {
|
||||||
|
|
||||||
|
var service = context.RequestServices.GetRequiredService<AsbCloudApp.Services.IRequerstTrackerService>();
|
||||||
|
var requestLog = new AsbCloudApp.Data.RequestLogDto
|
||||||
|
{
|
||||||
|
UserLogin = context.User?.Identity.Name,
|
||||||
|
UserIp = context.Connection.RemoteIpAddress.ToString(),
|
||||||
|
RequestMethod = context.Request.Method,
|
||||||
|
RequestPath = context.Request.Path.Value,
|
||||||
|
Referer = context.Request.Headers["Referer"].ToString(),
|
||||||
|
};
|
||||||
|
{
|
||||||
|
var userIdString = context.User?.FindFirst("id")?.Value;
|
||||||
|
if (!string.IsNullOrEmpty(userIdString) && int.TryParse(userIdString, out int userId))
|
||||||
|
requestLog.UserId = userId;
|
||||||
|
}
|
||||||
|
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await next?.Invoke();
|
||||||
|
sw.Stop();
|
||||||
|
requestLog.ElapsedMilliseconds = sw.ElapsedMilliseconds;
|
||||||
|
requestLog.Status = context.Response.StatusCode;
|
||||||
|
service.RegisterRequest(requestLog);
|
||||||
|
}
|
||||||
|
catch(System.Exception ex)
|
||||||
|
{
|
||||||
|
sw.Stop();
|
||||||
|
requestLog.ElapsedMilliseconds = sw.ElapsedMilliseconds;
|
||||||
|
requestLog.Status = context.Response.StatusCode;
|
||||||
|
service.RegisterRequestError(requestLog, ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.UseEndpoints(endpoints =>
|
app.UseEndpoints(endpoints =>
|
||||||
{
|
{
|
||||||
endpoints.MapControllers();
|
endpoints.MapControllers();
|
||||||
|
Loading…
Reference in New Issue
Block a user