[Prod] More logging #4

Merged
thomas.nuyken merged 7 commits from main into production 2025-07-21 21:25:22 +02:00
8 changed files with 90 additions and 10 deletions
Showing only changes of commit 84a72a8557 - Show all commits

View File

@@ -39,19 +39,41 @@ public static class CreateCar
IEnumerable<IValidator<Request>> validators, IEnumerable<IValidator<Request>> validators,
ApplicationDbContext dbContext, ApplicationDbContext dbContext,
UserAccessor userAccessor, UserAccessor userAccessor,
ILoggerFactory loggerFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
ILogger logger = loggerFactory.CreateLogger(nameof(CreateCar));
List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken: cancellationToken); List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken: cancellationToken);
if (failedValidations.Count > 0) if (failedValidations.Count > 0)
{ {
logger.LogDebug(
"Validation failed for request {@Request} with errors {@Errors}",
request,
failedValidations
.Where(x => !x.IsValid)
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary()));
} }
bool isDuplicate = await dbContext.Cars
.AnyAsync(x => x.Name.ToUpper() == request.Name.ToUpper(), cancellationToken);
if (isDuplicate)
{
logger.LogDebug("Car with name '{CarName}' (case insensitive) already exists", request.Name);
return TypedResults.Conflict();
}
string userId = userAccessor.GetUserId(); string userId = userAccessor.GetUserId();
User? user = await dbContext.Users.FindAsync([userId], cancellationToken: cancellationToken); User? user = await dbContext.Users.FindAsync([userId], cancellationToken: cancellationToken);
if (user is null) if (user is null)
{ {
logger.LogDebug("User with ID '{UserId}' not found, creating new user", userId);
user = new User user = new User
{ {
Id = userId Id = userId
@@ -65,17 +87,11 @@ public static class CreateCar
UserId = userId UserId = userId
}; };
bool isDuplicate = await dbContext.Cars
.AnyAsync(x => x.Name.ToUpper() == request.Name.ToUpper(), cancellationToken);
if (isDuplicate)
{
return TypedResults.Conflict();
}
await dbContext.Cars.AddAsync(car, cancellationToken); await dbContext.Cars.AddAsync(car, cancellationToken);
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
logger.LogTrace("Created new car: {@Car}", car);
Response response = new(car.Id.Value, car.Name); Response response = new(car.Id.Value, car.Name);
return TypedResults.Created($"/v1/cars/{car.Id}", response); return TypedResults.Created($"/v1/cars/{car.Id}", response);
} }

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Diagnostics;
using Vegasco.Server.Api.Persistence; using Vegasco.Server.Api.Persistence;
namespace Vegasco.Server.Api.Cars; namespace Vegasco.Server.Api.Cars;
@@ -21,6 +22,9 @@ public static class DeleteCar
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
Activity? activity = Activity.Current;
activity?.SetTag("id", id);
int rows = await dbContext.Cars int rows = await dbContext.Cars
.Where(x => x.Id == new CarId(id)) .Where(x => x.Id == new CarId(id))
.ExecuteDeleteAsync(cancellationToken); .ExecuteDeleteAsync(cancellationToken);

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Diagnostics;
using Vegasco.Server.Api.Persistence; using Vegasco.Server.Api.Persistence;
namespace Vegasco.Server.Api.Cars; namespace Vegasco.Server.Api.Cars;
@@ -34,11 +35,15 @@ public static class GetCars
ApplicationDbContext dbContext, ApplicationDbContext dbContext,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
Activity? activity = Activity.Current;
List<ResponseDto> cars = await dbContext.Cars List<ResponseDto> cars = await dbContext.Cars
.Select(x => new ResponseDto(x.Id.Value, x.Name)) .Select(x => new ResponseDto(x.Id.Value, x.Name))
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
ApiResponse response = new ApiResponse activity?.SetTag("carCount", cars.Count);
ApiResponse response = new()
{ {
Cars = cars Cars = cars
}; };

View File

@@ -10,6 +10,7 @@ namespace Vegasco.Server.Api.Cars;
public static class UpdateCar public static class UpdateCar
{ {
public record Request(string Name); public record Request(string Name);
public record Response(Guid Id, string Name); public record Response(Guid Id, string Name);
public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder)
@@ -40,11 +41,22 @@ public static class UpdateCar
IEnumerable<IValidator<Request>> validators, IEnumerable<IValidator<Request>> validators,
ApplicationDbContext dbContext, ApplicationDbContext dbContext,
UserAccessor userAccessor, UserAccessor userAccessor,
ILoggerFactory loggerFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var logger = loggerFactory.CreateLogger(nameof(UpdateCar));
List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken); List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken);
if (failedValidations.Count > 0) if (failedValidations.Count > 0)
{ {
logger.LogDebug(
"Validation failed for request {@Request} with errors {@Errors}",
request,
failedValidations
.Where(x => !x.IsValid)
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary()));
} }
@@ -60,13 +72,16 @@ public static class UpdateCar
if (isDuplicate) if (isDuplicate)
{ {
logger.LogDebug("Car with name '{CarName}' (case insensitive) already exists", request.Name);
return TypedResults.Conflict(); return TypedResults.Conflict();
} }
car.Name = request.Name.Trim(); car.Name = request.Name.Trim();
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
logger.LogTrace("Updated car: {@Car}", car);
Response response = new(car.Id.Value, car.Name); Response response = new(car.Id.Value, car.Name);
return TypedResults.Ok(response); return TypedResults.Ok(response);
} }
} }

View File

@@ -1,5 +1,6 @@
using FluentValidation; using FluentValidation;
using FluentValidation.Results; using FluentValidation.Results;
using System.Diagnostics;
using Vegasco.Server.Api.Cars; using Vegasco.Server.Api.Cars;
using Vegasco.Server.Api.Common; using Vegasco.Server.Api.Common;
using Vegasco.Server.Api.Persistence; using Vegasco.Server.Api.Persistence;
@@ -49,11 +50,22 @@ public static class CreateConsumption
ApplicationDbContext dbContext, ApplicationDbContext dbContext,
Request request, Request request,
IEnumerable<IValidator<Request>> validators, IEnumerable<IValidator<Request>> validators,
ILoggerFactory loggerFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
ILogger logger = loggerFactory.CreateLogger(nameof(CreateConsumption));
List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken); List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken);
if (failedValidations.Count > 0) if (failedValidations.Count > 0)
{ {
logger.LogDebug(
"Validation failed for request {@Request} with errors {@Errors}",
request,
failedValidations
.Where(x => !x.IsValid)
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary()));
} }
@@ -74,6 +86,8 @@ public static class CreateConsumption
dbContext.Consumptions.Add(consumption); dbContext.Consumptions.Add(consumption);
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
logger.LogTrace("Created new consumption: {@Consumption}", consumption);
return TypedResults.Created($"consumptions/{consumption.Id.Value}", return TypedResults.Created($"consumptions/{consumption.Id.Value}",
new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount, new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount,
consumption.CarId.Value)); consumption.CarId.Value));

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Diagnostics;
using Vegasco.Server.Api.Persistence; using Vegasco.Server.Api.Persistence;
namespace Vegasco.Server.Api.Consumptions; namespace Vegasco.Server.Api.Consumptions;
@@ -21,6 +22,9 @@ public static class DeleteConsumption
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
Activity? activity = Activity.Current;
activity?.SetTag("id", id);
int rows = await dbContext.Consumptions int rows = await dbContext.Consumptions
.Where(x => x.Id == new ConsumptionId(id)) .Where(x => x.Id == new ConsumptionId(id))
.ExecuteDeleteAsync(cancellationToken); .ExecuteDeleteAsync(cancellationToken);

View File

@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Diagnostics;
using Vegasco.Server.Api.Cars; using Vegasco.Server.Api.Cars;
using Vegasco.Server.Api.Persistence; using Vegasco.Server.Api.Persistence;
@@ -49,8 +50,14 @@ public static class GetConsumptions
private static async Task<Ok<ApiResponse>> Endpoint( private static async Task<Ok<ApiResponse>> Endpoint(
[AsParameters] Request request, [AsParameters] Request request,
ApplicationDbContext dbContext, ApplicationDbContext dbContext,
ILoggerFactory loggerFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
ILogger logger = loggerFactory.CreateLogger(nameof(GetConsumptions));
logger.LogTrace("Received request to get consumptions with parameters: {@Request}", request);
Activity? activity = Activity.Current;
Dictionary<CarId, List<Consumption>> consumptionsByCar = await dbContext.Consumptions Dictionary<CarId, List<Consumption>> consumptionsByCar = await dbContext.Consumptions
.Include(x => x.Car) .Include(x => x.Car)
.GroupBy(x => x.CarId) .GroupBy(x => x.CarId)
@@ -83,6 +90,8 @@ public static class GetConsumptions
literPer100Km)); literPer100Km));
} }
} }
activity?.SetTag("consumptionCount", responses.Count);
ApiResponse apiResponse = new() ApiResponse apiResponse = new()
{ {

View File

@@ -48,11 +48,22 @@ public static class UpdateConsumption
Guid id, Guid id,
Request request, Request request,
IEnumerable<IValidator<Request>> validators, IEnumerable<IValidator<Request>> validators,
ILoggerFactory loggerFactory,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
ILogger logger = loggerFactory.CreateLogger(nameof(UpdateConsumption));
List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken); List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken);
if (failedValidations.Count > 0) if (failedValidations.Count > 0)
{ {
logger.LogDebug(
"Validation failed for request {@Request} with errors {@Errors}",
request,
failedValidations
.Where(x => !x.IsValid)
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary()));
} }
@@ -68,6 +79,8 @@ public static class UpdateConsumption
await dbContext.SaveChangesAsync(cancellationToken); await dbContext.SaveChangesAsync(cancellationToken);
logger.LogTrace("Updated consumption: {@Consumption}", consumption);
return TypedResults.Ok(new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount, consumption.CarId.Value)); return TypedResults.Ok(new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount, consumption.CarId.Value));
} }
} }