diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index 87b61ea..ca56e86 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -17,22 +17,14 @@ on: schedule: - cron: '0 21 * * 5' # Runs at 21:00, only on Friday -permissions: - contents: read - jobs: codacy-security-scan: - permissions: - contents: read - security-events: write - actions: read name: Codacy Security Scan runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 - # Execute Codacy Analysis CLI and generate a SARIF output with the security - # issues identified during the analysis + uses: actions/checkout@main + - name: Run Codacy Analysis CLI uses: codacy/codacy-analysis-cli-action@master with: @@ -40,10 +32,14 @@ jobs: verbose: true output: results.sarif format: sarif + # Adjust severity of non-security issues gh-code-scanning-compat: true + # Force 0 exit code to allow SARIF file generation + # This will hand over control about PR rejection to the GitHub side max-allowed-issues: 2147483647 + # Upload the SARIF file generated in the previous step - name: Upload SARIF results file - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@main with: sarif_file: results.sarif diff --git a/Dotnet.Samples.AspNetCore.WebApi.Tests/Unit/PlayerServiceTests.cs b/Dotnet.Samples.AspNetCore.WebApi.Tests/Unit/PlayerServiceTests.cs index 53a262d..56f208c 100644 --- a/Dotnet.Samples.AspNetCore.WebApi.Tests/Unit/PlayerServiceTests.cs +++ b/Dotnet.Samples.AspNetCore.WebApi.Tests/Unit/PlayerServiceTests.cs @@ -70,6 +70,7 @@ public async Task GivenRetrieveAsync_WhenExecutedForTheSecondTime_ThenSecondExec // Arrange var players = PlayerFakes.CreateStarting11(); var (repository, logger, memoryCache) = PlayerMocks.SetupServiceMocks(cacheValue: players); + repository.Setup(repository => repository.GetAllAsync()).ReturnsAsync(players); var value = It.IsAny(); var service = new PlayerService(repository.Object, logger.Object, memoryCache.Object); diff --git a/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerMocks.cs b/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerMocks.cs index b01e594..735cb15 100644 --- a/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerMocks.cs +++ b/Dotnet.Samples.AspNetCore.WebApi.Tests/Utilities/PlayerMocks.cs @@ -36,15 +36,25 @@ public static Mock> LoggerMock() public static Mock MemoryCacheMock(object? value) { - var fromCache = false; + var cachedValue = false; var mock = new Mock(); - mock.Setup(cache => cache.TryGetValue(It.IsAny(), out value)) + + mock.Setup(cache => cache.TryGetValue(It.IsAny(), out It.Ref.IsAny!)) + .Callback( + new TryGetValueDelegate( + (object _, out object? output) => + { + output = cachedValue ? value : null; + } + ) + ) .Returns(() => { - bool hasValue = fromCache; - fromCache = true; // Subsequent invocations will return true + bool hasValue = cachedValue; + cachedValue = true; // Subsequent invocations will return true return hasValue; }); + mock.Setup(cache => cache.CreateEntry(It.IsAny())) .Returns(Mock.Of); mock.Setup(cache => cache.Remove(It.IsAny())); @@ -52,6 +62,8 @@ public static Mock MemoryCacheMock(object? value) return mock; } + private delegate void TryGetValueDelegate(object key, out object? value); + public static Mock UrlHelperMock() { var mock = new Mock(); diff --git a/Dotnet.Samples.AspNetCore.WebApi.Tests/packages.lock.json b/Dotnet.Samples.AspNetCore.WebApi.Tests/packages.lock.json index 0a12b00..8ac1704 100644 --- a/Dotnet.Samples.AspNetCore.WebApi.Tests/packages.lock.json +++ b/Dotnet.Samples.AspNetCore.WebApi.Tests/packages.lock.json @@ -850,6 +850,15 @@ "Microsoft.Extensions.Primitives": "9.0.3" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "RIEeZxWYm77+OWLwgik7DzSVSONjqkmcbuCb1koZdGAV7BgOUWnLz80VMyHZMw3onrVwFCCMHBBdruBPuQTvkg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.3", + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", "resolved": "9.0.3", @@ -858,6 +867,38 @@ "Microsoft.Extensions.Primitives": "9.0.3" } }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "RiScL99DcyngY9zJA2ROrri7Br8tn5N4hP4YNvGdTN/bvg1A3dwvDOxHnNZ3Im7x2SJ5i4LkX1uPiR/MfSFBLQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "tBNMSDJ2q7WQK2zwPhHY5I/q95t7sf6dT079mGrNm0yOZF/gM9JvR/LtCb/rwhRmh7A6XMnzv5WbpCh9KLq9EQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.3", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.3", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.3", + "Microsoft.Extensions.FileProviders.Physical": "9.0.3", + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "mjkp3ZwynNacZk4uq93I0DyCY48FZmi3yRV0xlfeDuWh44KcDunPXHwt8IWr4kL7cVM6eiFVe6YTJg97KzUAUA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.3", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.3", + "Microsoft.Extensions.Configuration.FileExtensions": "9.0.3", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.3", + "System.Text.Json": "9.0.3" + } + }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", "resolved": "9.0.3", @@ -880,6 +921,51 @@ "System.Text.Json": "9.0.3" } }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "1K8P7XzuzX8W8pmXcZjcrqS6x5eSSdvhQohmcpgiQNY/HlDAlnrhR9dvlURfFz428A+RTCJpUyB+aKTA6AgVcQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "System.Diagnostics.DiagnosticSource": "9.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "umczZ3+QPpzlrW/lkvy+IB0p52+qZ5w++aqx2lTCMOaPKzwcbVdrJgiQ3ajw5QWBp7gChLUiCYkSlWUpfjv24g==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "th2+tQBV5oWjgKhip9GjiIv2AEK3QvfAO3tZcqV3F3dEt5D6Gb411RntCj1+8GS9HaRRSxjSGx/fCrMqIjkb1Q==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.3", + "Microsoft.Extensions.FileSystemGlobbing": "9.0.3", + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "Rec77KHk4iNpFznHi5/6wF3MlUDcKqg26t8gRYbUm1PSukZ4B6mrXpZsJSNOiwyhhQVkjYbaoZxi5XJgRQ5lFg==" + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "yUKJgu81ExjvqbNWqZKshBbLntZMbMVz/P7Way2SBx7bMqA08Mfdc9O7hWDKAiSp+zPUGT6LKcSCQIPeDK+CCw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0" + } + }, "Microsoft.Extensions.Logging": { "type": "Transitive", "resolved": "9.0.3", @@ -1292,6 +1378,88 @@ "resolved": "6.11.0", "contentHash": "v/GGlIj2dd7svplFmASWEueu62veKW0MrMtBaZ7QG8aJTSGv2yE+pgUGhXRcQ4nxNOEq/wLBrz1vkth/1SND7A==" }, + "Serilog": { + "type": "Transitive", + "resolved": "4.2.0", + "contentHash": "gmoWVOvKgbME8TYR+gwMf7osROiWAURterc6Rt2dQyX7wtjZYpqFiA/pY6ztjGQKKV62GGCyOcmtP1UKMHgSmA==" + }, + "Serilog.AspNetCore": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "JslDajPlBsn3Pww1554flJFTqROvK9zz9jONNQgn0D8Lx2Trw8L0A8/n6zEQK1DAZWXrJwiVLw8cnTR3YFuYsg==", + "dependencies": { + "Serilog": "4.2.0", + "Serilog.Extensions.Hosting": "9.0.0", + "Serilog.Formatting.Compact": "3.0.0", + "Serilog.Settings.Configuration": "9.0.0", + "Serilog.Sinks.Console": "6.0.0", + "Serilog.Sinks.Debug": "3.0.0", + "Serilog.Sinks.File": "6.0.0" + } + }, + "Serilog.Extensions.Hosting": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "u2TRxuxbjvTAldQn7uaAwePkWxTHIqlgjelekBtilAGL5sYyF3+65NWctN4UrwwGLsDC7c3Vz3HnOlu+PcoxXg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", + "Serilog": "4.2.0", + "Serilog.Extensions.Logging": "9.0.0" + } + }, + "Serilog.Extensions.Logging": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", + "Serilog": "4.2.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==", + "dependencies": { + "Serilog": "4.0.0" + } + }, + "Serilog.Settings.Configuration": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "4/Et4Cqwa+F88l5SeFeNZ4c4Z6dEAIKbu3MaQb2Zz9F/g27T5a3wvfMcmCOaAiACjfUb4A6wrlTVfyYUZk3RRQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "9.0.0", + "Microsoft.Extensions.DependencyModel": "9.0.0", + "Serilog": "4.2.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==", + "dependencies": { + "Serilog": "4.0.0" + } + }, + "Serilog.Sinks.Debug": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "4BzXcdrgRX7wde9PmHuYd9U6YqycCC28hhpKonK7hx0wb19eiuRj16fPcPSVp0o/Y1ipJuNLYQ00R3q2Zs8FDA==", + "dependencies": { + "Serilog": "4.0.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "lxjg89Y8gJMmFxVkbZ+qDgjl+T4yC5F7WSLTvA+5q0R04tfKVLRL/EHpYoJ/MEQd2EeCKDuylBIVnAYMotmh2A==", + "dependencies": { + "Serilog": "4.0.0" + } + }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", "resolved": "2.1.10", @@ -1324,35 +1492,35 @@ }, "Swashbuckle.AspNetCore": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "K9FzGTxmwfD+7sVf/FTq/TZFHBTXcROgdcg7gLFwKwgvXwaqTtjGVdam27j0kYfgZZyWlOKr+abmtyd2nAd5eA==", + "resolved": "8.1.0", + "contentHash": "8NJwXskWNBhX4IntitP1VP68hzhQ9Sex4mR9ZbqCuuUsaWlfrMA+yDIYDFsbHlB2NQ6Gt27AT15BWo9jxHj1tg==", "dependencies": { "Microsoft.Extensions.ApiDescription.Server": "6.0.5", - "Swashbuckle.AspNetCore.Swagger": "8.0.0", - "Swashbuckle.AspNetCore.SwaggerGen": "8.0.0", - "Swashbuckle.AspNetCore.SwaggerUI": "8.0.0" + "Swashbuckle.AspNetCore.Swagger": "8.1.0", + "Swashbuckle.AspNetCore.SwaggerGen": "8.1.0", + "Swashbuckle.AspNetCore.SwaggerUI": "8.1.0" } }, "Swashbuckle.AspNetCore.Swagger": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "+8Y4pVTWbnzotIk6d6rcwsHGpCchPDqqrvYkyGlI3go+pFaKM+4eX30iCyI0hvr0RMtObJCFhK6aDtlQFbEF1g==", + "resolved": "8.1.0", + "contentHash": "TRpBDdWh/6IzXHOLKVEuZSuhcdc8OIwhr8dyLnWvEh6FO4f0KiATAzr8yzHspcCFzCc/CONpvUCq4DzazpP6Og==", "dependencies": { "Microsoft.OpenApi": "1.6.23" } }, "Swashbuckle.AspNetCore.SwaggerGen": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "skCeIQ93yMcUm1PQby5qitFM6KLIlLMj4/i8JHy86x2OFzxTNaaas2kUg6rNV3JvucFvYCNyImg7NMtZHErSzQ==", + "resolved": "8.1.0", + "contentHash": "sXAUNodmnzrJb1qmgdLY6HgTDsgagXCn6p+qygW2wP0BjcvdZZA6GdDgtuc8iQON9ekrp7m5G0Zc09lvuNhc/A==", "dependencies": { - "Swashbuckle.AspNetCore.Swagger": "8.0.0" + "Swashbuckle.AspNetCore.Swagger": "8.1.0" } }, "Swashbuckle.AspNetCore.SwaggerUI": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "IMqmgclFiZL2QIfopOmWYofZzckrl+SdMt1h4mKC0jc94F+uzt3IHA3YFC0CGlwBqTTSnxHqNUKomNTeAhZbYA==" + "resolved": "8.1.0", + "contentHash": "PYyTbYrEz1q8N8ZpCFzY5hOkvP5gv9oIml06wrrrjGRFvVXyCF/b58FRI/9mnRKRBAmWKZR5pqhmJBzvsVveSQ==" }, "System.ClientModel": { "type": "Transitive", @@ -1642,8 +1810,13 @@ "Microsoft.EntityFrameworkCore.InMemory": "[9.0.3, )", "Microsoft.EntityFrameworkCore.SqlServer": "[9.0.3, )", "Microsoft.EntityFrameworkCore.Sqlite": "[9.0.3, )", + "Microsoft.Extensions.Configuration.Json": "[9.0.3, )", "Microsoft.VisualStudio.Web.CodeGeneration.Design": "[9.0.0, )", - "Swashbuckle.AspNetCore": "[8.0.0, )" + "Serilog.AspNetCore": "[9.0.0, )", + "Serilog.Settings.Configuration": "[9.0.0, )", + "Serilog.Sinks.Console": "[6.0.0, )", + "Serilog.Sinks.File": "[6.0.0, )", + "Swashbuckle.AspNetCore": "[8.1.0, )" } } } diff --git a/Dotnet.Samples.AspNetCore.WebApi/Dotnet.Samples.AspNetCore.WebApi.csproj b/Dotnet.Samples.AspNetCore.WebApi/Dotnet.Samples.AspNetCore.WebApi.csproj index 0aae169..b50d47c 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/Dotnet.Samples.AspNetCore.WebApi.csproj +++ b/Dotnet.Samples.AspNetCore.WebApi/Dotnet.Samples.AspNetCore.WebApi.csproj @@ -8,6 +8,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -17,6 +18,10 @@ + + + + diff --git a/Dotnet.Samples.AspNetCore.WebApi/Program.cs b/Dotnet.Samples.AspNetCore.WebApi/Program.cs index ed46fa0..19cc234 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/Program.cs +++ b/Dotnet.Samples.AspNetCore.WebApi/Program.cs @@ -3,18 +3,24 @@ using Dotnet.Samples.AspNetCore.WebApi.Services; using Microsoft.EntityFrameworkCore; using Microsoft.OpenApi.Models; +using Serilog; var builder = WebApplication.CreateBuilder(args); +/* ----------------------------------------------------------------------------- + * Configuration + * -------------------------------------------------------------------------- */ +builder + .Configuration.SetBasePath(AppContext.BaseDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddEnvironmentVariables(); + /* ----------------------------------------------------------------------------- * Logging * -------------------------------------------------------------------------- */ +Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).CreateLogger(); -if (builder.Environment.IsDevelopment()) -{ - builder.Logging.ClearProviders(); - builder.Logging.AddSimpleConsole(options => options.SingleLine = true); -} +builder.Host.UseSerilog(); /* ----------------------------------------------------------------------------- * Services @@ -77,6 +83,8 @@ * https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware * -------------------------------------------------------------------------- */ +app.UseSerilogRequestLogging(); + if (app.Environment.IsDevelopment()) { // https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle diff --git a/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs b/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs index 17e2377..25dfd30 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs +++ b/Dotnet.Samples.AspNetCore.WebApi/Services/PlayerService.cs @@ -25,7 +25,12 @@ IMemoryCache memoryCache public async Task CreateAsync(Player player) { await _playerRepository.AddAsync(player); + _logger.LogInformation("Player added to Repository: {Player}", player); _memoryCache.Remove(MemoryCache_Key_RetrieveAsync); + _logger.LogInformation( + "Key removed from MemoryCache: {Key}", + MemoryCache_Key_RetrieveAsync + ); } /* ------------------------------------------------------------------------- @@ -36,27 +41,29 @@ public async Task> RetrieveAsync() { if (_memoryCache.TryGetValue(MemoryCache_Key_RetrieveAsync, out List? players)) { - _logger.Log(LogLevel.Information, "Players retrieved from MemoryCache."); - + _logger.LogInformation("Players retrieved from MemoryCache"); return players!; } else { - /* - Use multiple environments in ASP.NET Core - https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-8.0 - */ + // Use multiple environments in ASP.NET Core + // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-8.0 if (Environment.GetEnvironmentVariable(AspNetCore_Environment) == Development) { - // Simulates a random delay - await Task.Delay(new Random().Next(2600, 4200)); + await SimulateRepositoryDelayAsync(); } players = await _playerRepository.GetAllAsync(); - _logger.Log(LogLevel.Information, "Players retrieved from DbContext."); + _logger.LogInformation("Players retrieved from Repository"); using (var cacheEntry = _memoryCache.CreateEntry(MemoryCache_Key_RetrieveAsync)) { + _logger.LogInformation( + "{Count} players added to MemoryCache with key {Key}", + players.Count, + MemoryCache_Key_RetrieveAsync + ); + cacheEntry.SetSize(players.Count); cacheEntry.Value = players; cacheEntry.SetOptions(GetMemoryCacheEntryOptions()); } @@ -81,7 +88,12 @@ public async Task UpdateAsync(Player player) { entity.MapFrom(player); await _playerRepository.UpdateAsync(entity); + _logger.LogInformation("Player updated in Repository: {Player}", player); _memoryCache.Remove(MemoryCache_Key_RetrieveAsync); + _logger.LogInformation( + "Key removed from MemoryCache: {Key}", + MemoryCache_Key_RetrieveAsync + ); } } @@ -94,7 +106,12 @@ public async Task DeleteAsync(long id) if (await _playerRepository.FindByIdAsync(id) is not null) { await _playerRepository.RemoveAsync(id); + _logger.LogInformation("Player ID removed from Repository {Id}", id); _memoryCache.Remove(MemoryCache_Key_RetrieveAsync); + _logger.LogInformation( + "Key removed from MemoryCache: {Key}", + MemoryCache_Key_RetrieveAsync + ); } } @@ -110,4 +127,20 @@ private static MemoryCacheEntryOptions GetMemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(10)) .SetAbsoluteExpiration(TimeSpan.FromHours(1)); } + + /// + /// Simulates a delay in the repository call to mimic a long-running operation. + /// This is only used in the Development environment to simulate a delay + /// in the repository call. In production, this method should not be called. + /// + /// A Task representing the asynchronous operation. + private async Task SimulateRepositoryDelayAsync() + { + var milliseconds = new Random().Next(2600, 4200); + _logger.LogInformation( + "Simulating a random delay of {Milliseconds} milliseconds...", + milliseconds + ); + await Task.Delay(milliseconds); + } } diff --git a/Dotnet.Samples.AspNetCore.WebApi/appsettings.Development.json b/Dotnet.Samples.AspNetCore.WebApi/appsettings.Development.json index ff66ba6..429c6e0 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/appsettings.Development.json +++ b/Dotnet.Samples.AspNetCore.WebApi/appsettings.Development.json @@ -1,8 +1,24 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } + "Serilog": { + "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"], + "MinimumLevel": "Information", + "WriteTo": [ + { + "Name": "Console", + "Args": { + "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", + "outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "logs/log-.log", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message:lj}{NewLine}{Exception}" + } + } + ], + "Enrich": ["FromLogContext"] } } diff --git a/Dotnet.Samples.AspNetCore.WebApi/appsettings.json b/Dotnet.Samples.AspNetCore.WebApi/appsettings.json index 4d56694..429c6e0 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/appsettings.json +++ b/Dotnet.Samples.AspNetCore.WebApi/appsettings.json @@ -1,9 +1,24 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" + "Serilog": { + "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"], + "MinimumLevel": "Information", + "WriteTo": [ + { + "Name": "Console", + "Args": { + "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", + "outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "logs/log-.log", + "rollingInterval": "Day", + "outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message:lj}{NewLine}{Exception}" + } + } + ], + "Enrich": ["FromLogContext"] + } } diff --git a/Dotnet.Samples.AspNetCore.WebApi/packages.lock.json b/Dotnet.Samples.AspNetCore.WebApi/packages.lock.json index 38d5ad6..ac38665 100644 --- a/Dotnet.Samples.AspNetCore.WebApi/packages.lock.json +++ b/Dotnet.Samples.AspNetCore.WebApi/packages.lock.json @@ -74,6 +74,19 @@ "System.Text.Json": "9.0.3" } }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Direct", + "requested": "[9.0.3, )", + "resolved": "9.0.3", + "contentHash": "mjkp3ZwynNacZk4uq93I0DyCY48FZmi3yRV0xlfeDuWh44KcDunPXHwt8IWr4kL7cVM6eiFVe6YTJg97KzUAUA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.3", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.3", + "Microsoft.Extensions.Configuration.FileExtensions": "9.0.3", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.3", + "System.Text.Json": "9.0.3" + } + }, "Microsoft.VisualStudio.Web.CodeGeneration.Design": { "type": "Direct", "requested": "[9.0.0, )", @@ -102,6 +115,50 @@ "System.Security.Cryptography.ProtectedData": "8.0.0" } }, + "Serilog.AspNetCore": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "JslDajPlBsn3Pww1554flJFTqROvK9zz9jONNQgn0D8Lx2Trw8L0A8/n6zEQK1DAZWXrJwiVLw8cnTR3YFuYsg==", + "dependencies": { + "Serilog": "4.2.0", + "Serilog.Extensions.Hosting": "9.0.0", + "Serilog.Formatting.Compact": "3.0.0", + "Serilog.Settings.Configuration": "9.0.0", + "Serilog.Sinks.Console": "6.0.0", + "Serilog.Sinks.Debug": "3.0.0", + "Serilog.Sinks.File": "6.0.0" + } + }, + "Serilog.Settings.Configuration": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "4/Et4Cqwa+F88l5SeFeNZ4c4Z6dEAIKbu3MaQb2Zz9F/g27T5a3wvfMcmCOaAiACjfUb4A6wrlTVfyYUZk3RRQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "9.0.0", + "Microsoft.Extensions.DependencyModel": "9.0.0", + "Serilog": "4.2.0" + } + }, + "Serilog.Sinks.Console": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==", + "dependencies": { + "Serilog": "4.0.0" + } + }, + "Serilog.Sinks.File": { + "type": "Direct", + "requested": "[6.0.0, )", + "resolved": "6.0.0", + "contentHash": "lxjg89Y8gJMmFxVkbZ+qDgjl+T4yC5F7WSLTvA+5q0R04tfKVLRL/EHpYoJ/MEQd2EeCKDuylBIVnAYMotmh2A==", + "dependencies": { + "Serilog": "4.0.0" + } + }, "Swashbuckle.AspNetCore": { "type": "Direct", "requested": "[8.1.0, )", @@ -876,6 +933,15 @@ "Microsoft.Extensions.Primitives": "9.0.3" } }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "RIEeZxWYm77+OWLwgik7DzSVSONjqkmcbuCb1koZdGAV7BgOUWnLz80VMyHZMw3onrVwFCCMHBBdruBPuQTvkg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.3", + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", "resolved": "9.0.3", @@ -884,6 +950,26 @@ "Microsoft.Extensions.Primitives": "9.0.3" } }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "RiScL99DcyngY9zJA2ROrri7Br8tn5N4hP4YNvGdTN/bvg1A3dwvDOxHnNZ3Im7x2SJ5i4LkX1uPiR/MfSFBLQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "tBNMSDJ2q7WQK2zwPhHY5I/q95t7sf6dT079mGrNm0yOZF/gM9JvR/LtCb/rwhRmh7A6XMnzv5WbpCh9KLq9EQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.3", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.3", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.3", + "Microsoft.Extensions.FileProviders.Physical": "9.0.3", + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", "resolved": "9.0.3", @@ -906,6 +992,51 @@ "System.Text.Json": "9.0.3" } }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "1K8P7XzuzX8W8pmXcZjcrqS6x5eSSdvhQohmcpgiQNY/HlDAlnrhR9dvlURfFz428A+RTCJpUyB+aKTA6AgVcQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0", + "System.Diagnostics.DiagnosticSource": "9.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "umczZ3+QPpzlrW/lkvy+IB0p52+qZ5w++aqx2lTCMOaPKzwcbVdrJgiQ3ajw5QWBp7gChLUiCYkSlWUpfjv24g==", + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "th2+tQBV5oWjgKhip9GjiIv2AEK3QvfAO3tZcqV3F3dEt5D6Gb411RntCj1+8GS9HaRRSxjSGx/fCrMqIjkb1Q==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.3", + "Microsoft.Extensions.FileSystemGlobbing": "9.0.3", + "Microsoft.Extensions.Primitives": "9.0.3" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "9.0.3", + "contentHash": "Rec77KHk4iNpFznHi5/6wF3MlUDcKqg26t8gRYbUm1PSukZ4B6mrXpZsJSNOiwyhhQVkjYbaoZxi5XJgRQ5lFg==" + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "yUKJgu81ExjvqbNWqZKshBbLntZMbMVz/P7Way2SBx7bMqA08Mfdc9O7hWDKAiSp+zPUGT6LKcSCQIPeDK+CCw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0" + } + }, "Microsoft.Extensions.Logging": { "type": "Transitive", "resolved": "9.0.3", @@ -1274,6 +1405,48 @@ "resolved": "6.11.0", "contentHash": "v/GGlIj2dd7svplFmASWEueu62veKW0MrMtBaZ7QG8aJTSGv2yE+pgUGhXRcQ4nxNOEq/wLBrz1vkth/1SND7A==" }, + "Serilog": { + "type": "Transitive", + "resolved": "4.2.0", + "contentHash": "gmoWVOvKgbME8TYR+gwMf7osROiWAURterc6Rt2dQyX7wtjZYpqFiA/pY6ztjGQKKV62GGCyOcmtP1UKMHgSmA==" + }, + "Serilog.Extensions.Hosting": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "u2TRxuxbjvTAldQn7uaAwePkWxTHIqlgjelekBtilAGL5sYyF3+65NWctN4UrwwGLsDC7c3Vz3HnOlu+PcoxXg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", + "Serilog": "4.2.0", + "Serilog.Extensions.Logging": "9.0.0" + } + }, + "Serilog.Extensions.Logging": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", + "Serilog": "4.2.0" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==", + "dependencies": { + "Serilog": "4.0.0" + } + }, + "Serilog.Sinks.Debug": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "4BzXcdrgRX7wde9PmHuYd9U6YqycCC28hhpKonK7hx0wb19eiuRj16fPcPSVp0o/Y1ipJuNLYQ00R3q2Zs8FDA==", + "dependencies": { + "Serilog": "4.0.0" + } + }, "SQLitePCLRaw.bundle_e_sqlite3": { "type": "Transitive", "resolved": "2.1.10", diff --git a/codecov.yml b/codecov.yml index c14765a..24feabf 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,24 +1,35 @@ # https://docs.codecov.com/docs/codecov-yaml -ignore: - - "Dotnet.Samples.AspNetCore.WebApi/Data" - - "Dotnet.Samples.AspNetCore.WebApi/Enums" - - "Dotnet.Samples.AspNetCore.WebApi/Migrations" - - "Dotnet.Samples.AspNetCore.WebApi/Models" - - "Dotnet.Samples.AspNetCore.WebApi/Properties" - - "Dotnet.Samples.AspNetCore.WebApi/Utilities" - - "Dotnet.Samples.AspNetCore.WebApi/Program.cs" - - "Dotnet.Samples.AspNetCore.WebApi.Tests" - coverage: + + #https://docs.codecov.com/docs/commit-status status: project: + default: + target: 80% # Default target for all components + threshold: 10% # Default threshold for all components + if_not_found: success # If no coverage report is found, the status will be success + if_ci_failed: error # If the CI failed, the status will be error + + # Components inherit the default settings controllers: paths: - "Dotnet.Samples.AspNetCore.WebApi/Controllers/" services: paths: - "Dotnet.Samples.AspNetCore.WebApi/Services/" + patch: default: - informational: true + target: 80% # Default target for all components + threshold: 10% # Default threshold for all components + +ignore: + - "Dotnet.Samples.AspNetCore.WebApi/Data" + - "Dotnet.Samples.AspNetCore.WebApi/Enums" + - "Dotnet.Samples.AspNetCore.WebApi/Migrations" + - "Dotnet.Samples.AspNetCore.WebApi/Models" + - "Dotnet.Samples.AspNetCore.WebApi/Properties" + - "Dotnet.Samples.AspNetCore.WebApi/Utilities" + - "Dotnet.Samples.AspNetCore.WebApi/Program.cs" + - "Dotnet.Samples.AspNetCore.WebApi.Tests"