diff --git a/Directory.Packages.props b/Directory.Packages.props index f0caa74..f942bde 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,6 +22,8 @@ + + @@ -29,6 +31,7 @@ + diff --git a/README.md b/README.md index 55b423c..e4d7709 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,11 @@ ### Prerequisites - [.NET 9 SDK](https://dotnet.microsoft.com/download/dotnet/9.0) -- [Docker](https://www.docker.com/) (for Prometheus and Grafana) +- [Docker](https://www.docker.com/) (for Prometheus, Loki, and Grafana) -### Start Prometheus and Grafana +### Start the Observability Stack -The API exports metrics via OpenTelemetry's OTLP exporter directly to Prometheus. Before starting the API, bring up the observability stack with Docker Compose: +The API exports metrics via OpenTelemetry's OTLP exporter to Prometheus and ships structured logs via Serilog's OpenTelemetry sink to Loki. Before starting the API, bring up the observability stack with Docker Compose: ```bash docker-compose up -d @@ -58,9 +58,11 @@ This starts: | Service | URL | | --- | --- | | Prometheus | http://localhost:5431 | +| Loki | http://localhost:3100 | | Grafana | http://localhost:3000 | -Prometheus is configured with `--web.enable-otlp-receiver` so it accepts OTLP pushes from the API. The scrape interval is set to 15 s globally (10 s for the Prometheus self-scrape job). +- **Prometheus** is configured with `--web.enable-otlp-receiver` so it accepts OTLP pushes from the API. The scrape interval is set to 15 s globally (10 s for the Prometheus self-scrape job). +- **Loki** listens on port `3100` and accepts logs over the OTLP HTTP endpoint (`/otlp`). Logs are stored on the local filesystem. ### Start the API @@ -68,11 +70,16 @@ Prometheus is configured with `--web.enable-otlp-receiver` so it accepts OTLP pu dotnet run --project TournamentAPI ``` -The API will push metrics to Prometheus automatically once running. +The API will push metrics to Prometheus and logs to Loki automatically once running. ### Grafana -Open `http://localhost:3000`, log in with the default credentials (`admin` / `admin`), and add Prometheus (`http://prometheus:9090`) as a data source to build dashboards from the collected metrics. +Open `http://localhost:3000`, log in with the default credentials (`admin` / `admin`), and add the following data sources to build dashboards: + +| Data source | URL | +| --- | --- | +| Prometheus | `http://prometheus:9090` | +| Loki | `http://loki:3100` | --- diff --git a/TournamentAPI.Benchmarks/packages.lock.json b/TournamentAPI.Benchmarks/packages.lock.json index 612b164..a692237 100644 --- a/TournamentAPI.Benchmarks/packages.lock.json +++ b/TournamentAPI.Benchmarks/packages.lock.json @@ -91,6 +91,11 @@ "resolved": "2.3.0", "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.30.1", + "contentHash": "HeWXDQBabQn/sCGicbeLJ0HMunknfC4FdLrOQOsaMJHcpqx3HVIpyyJqTrqJlWnza870twhOb+rBcaTiC/TlNA==" + }, "GreenDonut": { "type": "Transitive", "resolved": "15.1.15", @@ -138,6 +143,28 @@ "resolved": "15.1.15", "contentHash": "zKYnY8NMsZvQSY3Mdwtkivu4K/uMCSSjAB9YDiXV54mDwehnYVlFzMTX9MzB34+f8menzcZ8Ko/tqzlhKSt8Sw==" }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "66UotvWcSIq41oiQhLWcQACyKPM4umxXNiht5DQTLZJfNwEswWOcS7Z0xIEHyNIBE7ZpjotH22bEjTkvhPxmVw==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "xNv0FFCVJa5S1beUtye82WFCxKThuE1jbN8DO1x1Rj8VSIWXLBUmfSID5a1fGzsU2R/EMfwPoWclJ2RMfQuGXw==", + "dependencies": { + "Grpc.Net.Common": "2.70.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "rBdEUMyCwa+iB8mqC6JKyPbj3SBHHkReJj/yy/XKJI63GcG6w9DJMMGTVcYHqq4Ci2W4m0HT4jt2pFfFscar8g==", + "dependencies": { + "Grpc.Core.Api": "2.70.0" + } + }, "HotChocolate": { "type": "Transitive", "resolved": "15.1.15", @@ -1168,6 +1195,52 @@ "resolved": "3.2.4", "contentHash": "I5qFifWw/gaTQT52MhzjZpkm/JPlfjSeO/DTZJjO7+hTKI+0aGRgOgZ3NN6D96dDuuqbIAZSeA5RimtHjqrA2A==" }, + "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.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.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" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.6.0", @@ -1361,7 +1434,9 @@ "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.2, )", "OpenTelemetry.Instrumentation.Http": "[1.15.1, )", "Serilog": "[4.3.0, )", - "Serilog.Sinks.Console": "[6.1.1, )" + "Serilog.AspNetCore": "[9.0.0, )", + "Serilog.Sinks.Console": "[6.1.1, )", + "Serilog.Sinks.OpenTelemetry": "[4.2.0, )" } }, "tournamentapi.shared": { @@ -1557,6 +1632,31 @@ "resolved": "4.3.0", "contentHash": "+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==" }, + "Serilog.AspNetCore": { + "type": "CentralTransitive", + "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.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[9.0.2, )", + "resolved": "9.0.0", + "contentHash": "NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", + "Serilog": "4.2.0" + } + }, "Serilog.Sinks.Console": { "type": "CentralTransitive", "requested": "[6.1.1, )", @@ -1565,6 +1665,17 @@ "dependencies": { "Serilog": "4.0.0" } + }, + "Serilog.Sinks.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[4.2.0, )", + "resolved": "4.2.0", + "contentHash": "PzMCyE5G19tjr5IZEi5qg+4UU5QrxBEoBEMu/hhYybTrGKXqUDiSGWKZNUDBgelaVKqLADlsmlJVyKce5SyPrg==", + "dependencies": { + "Google.Protobuf": "3.30.1", + "Grpc.Net.Client": "2.70.0", + "Serilog": "4.2.0" + } } } } diff --git a/TournamentAPI.IntegrationTests/packages.lock.json b/TournamentAPI.IntegrationTests/packages.lock.json index a1f0d9f..5758e54 100644 --- a/TournamentAPI.IntegrationTests/packages.lock.json +++ b/TournamentAPI.IntegrationTests/packages.lock.json @@ -121,6 +121,11 @@ "Docker.DotNet.Enhanced": "3.130.0" } }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.30.1", + "contentHash": "HeWXDQBabQn/sCGicbeLJ0HMunknfC4FdLrOQOsaMJHcpqx3HVIpyyJqTrqJlWnza870twhOb+rBcaTiC/TlNA==" + }, "GreenDonut": { "type": "Transitive", "resolved": "15.1.15", @@ -168,6 +173,28 @@ "resolved": "15.1.15", "contentHash": "zKYnY8NMsZvQSY3Mdwtkivu4K/uMCSSjAB9YDiXV54mDwehnYVlFzMTX9MzB34+f8menzcZ8Ko/tqzlhKSt8Sw==" }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "66UotvWcSIq41oiQhLWcQACyKPM4umxXNiht5DQTLZJfNwEswWOcS7Z0xIEHyNIBE7ZpjotH22bEjTkvhPxmVw==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "xNv0FFCVJa5S1beUtye82WFCxKThuE1jbN8DO1x1Rj8VSIWXLBUmfSID5a1fGzsU2R/EMfwPoWclJ2RMfQuGXw==", + "dependencies": { + "Grpc.Net.Common": "2.70.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "rBdEUMyCwa+iB8mqC6JKyPbj3SBHHkReJj/yy/XKJI63GcG6w9DJMMGTVcYHqq4Ci2W4m0HT4jt2pFfFscar8g==", + "dependencies": { + "Grpc.Core.Api": "2.70.0" + } + }, "HotChocolate": { "type": "Transitive", "resolved": "15.1.15", @@ -1146,6 +1173,52 @@ "OpenTelemetry.Api": "1.15.3" } }, + "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.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.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" + } + }, "SharpZipLib": { "type": "Transitive", "resolved": "1.4.2", @@ -1377,7 +1450,9 @@ "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.2, )", "OpenTelemetry.Instrumentation.Http": "[1.15.1, )", "Serilog": "[4.3.0, )", - "Serilog.Sinks.Console": "[6.1.1, )" + "Serilog.AspNetCore": "[9.0.0, )", + "Serilog.Sinks.Console": "[6.1.1, )", + "Serilog.Sinks.OpenTelemetry": "[4.2.0, )" } }, "tournamentapi.shared": { @@ -1573,6 +1648,31 @@ "resolved": "4.3.0", "contentHash": "+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==" }, + "Serilog.AspNetCore": { + "type": "CentralTransitive", + "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.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[9.0.2, )", + "resolved": "9.0.0", + "contentHash": "NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", + "Serilog": "4.2.0" + } + }, "Serilog.Sinks.Console": { "type": "CentralTransitive", "requested": "[6.1.1, )", @@ -1581,6 +1681,17 @@ "dependencies": { "Serilog": "4.0.0" } + }, + "Serilog.Sinks.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[4.2.0, )", + "resolved": "4.2.0", + "contentHash": "PzMCyE5G19tjr5IZEi5qg+4UU5QrxBEoBEMu/hhYybTrGKXqUDiSGWKZNUDBgelaVKqLADlsmlJVyKce5SyPrg==", + "dependencies": { + "Google.Protobuf": "3.30.1", + "Grpc.Net.Client": "2.70.0", + "Serilog": "4.2.0" + } } } } diff --git a/TournamentAPI.LoadTests/packages.lock.json b/TournamentAPI.LoadTests/packages.lock.json index a73bd3a..31f6307 100644 --- a/TournamentAPI.LoadTests/packages.lock.json +++ b/TournamentAPI.LoadTests/packages.lock.json @@ -241,6 +241,11 @@ "FSharp.Core": "8.0.400" } }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.30.1", + "contentHash": "HeWXDQBabQn/sCGicbeLJ0HMunknfC4FdLrOQOsaMJHcpqx3HVIpyyJqTrqJlWnza870twhOb+rBcaTiC/TlNA==" + }, "GreenDonut": { "type": "Transitive", "resolved": "15.1.15", @@ -288,6 +293,28 @@ "resolved": "15.1.15", "contentHash": "zKYnY8NMsZvQSY3Mdwtkivu4K/uMCSSjAB9YDiXV54mDwehnYVlFzMTX9MzB34+f8menzcZ8Ko/tqzlhKSt8Sw==" }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "66UotvWcSIq41oiQhLWcQACyKPM4umxXNiht5DQTLZJfNwEswWOcS7Z0xIEHyNIBE7ZpjotH22bEjTkvhPxmVw==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "xNv0FFCVJa5S1beUtye82WFCxKThuE1jbN8DO1x1Rj8VSIWXLBUmfSID5a1fGzsU2R/EMfwPoWclJ2RMfQuGXw==", + "dependencies": { + "Grpc.Net.Common": "2.70.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "rBdEUMyCwa+iB8mqC6JKyPbj3SBHHkReJj/yy/XKJI63GcG6w9DJMMGTVcYHqq4Ci2W4m0HT4jt2pFfFscar8g==", + "dependencies": { + "Grpc.Core.Api": "2.70.0" + } + }, "HdrHistogram": { "type": "Transitive", "resolved": "2.5.0", @@ -1532,6 +1559,26 @@ "Serilog": "4.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.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", @@ -1542,6 +1589,14 @@ "Serilog": "4.2.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", @@ -2504,7 +2559,9 @@ "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.2, )", "OpenTelemetry.Instrumentation.Http": "[1.15.1, )", "Serilog": "[4.3.0, )", - "Serilog.Sinks.Console": "[6.1.1, )" + "Serilog.AspNetCore": "[9.0.0, )", + "Serilog.Sinks.Console": "[6.1.1, )", + "Serilog.Sinks.OpenTelemetry": "[4.2.0, )" } }, "tournamentapi.shared": { @@ -2700,6 +2757,31 @@ "resolved": "4.3.0", "contentHash": "+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==" }, + "Serilog.AspNetCore": { + "type": "CentralTransitive", + "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.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[9.0.2, )", + "resolved": "9.0.0", + "contentHash": "NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", + "Serilog": "4.2.0" + } + }, "Serilog.Sinks.Console": { "type": "CentralTransitive", "requested": "[6.1.1, )", @@ -2708,6 +2790,17 @@ "dependencies": { "Serilog": "4.0.0" } + }, + "Serilog.Sinks.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[4.2.0, )", + "resolved": "4.2.0", + "contentHash": "PzMCyE5G19tjr5IZEi5qg+4UU5QrxBEoBEMu/hhYybTrGKXqUDiSGWKZNUDBgelaVKqLADlsmlJVyKce5SyPrg==", + "dependencies": { + "Google.Protobuf": "3.30.1", + "Grpc.Net.Client": "2.70.0", + "Serilog": "4.2.0" + } } } } diff --git a/TournamentAPI.UnitTests/packages.lock.json b/TournamentAPI.UnitTests/packages.lock.json index dcb41bd..82ecdb3 100644 --- a/TournamentAPI.UnitTests/packages.lock.json +++ b/TournamentAPI.UnitTests/packages.lock.json @@ -80,6 +80,11 @@ "Yarp.ReverseProxy": "2.3.0" } }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.30.1", + "contentHash": "HeWXDQBabQn/sCGicbeLJ0HMunknfC4FdLrOQOsaMJHcpqx3HVIpyyJqTrqJlWnza870twhOb+rBcaTiC/TlNA==" + }, "GreenDonut": { "type": "Transitive", "resolved": "15.1.15", @@ -127,6 +132,28 @@ "resolved": "15.1.15", "contentHash": "zKYnY8NMsZvQSY3Mdwtkivu4K/uMCSSjAB9YDiXV54mDwehnYVlFzMTX9MzB34+f8menzcZ8Ko/tqzlhKSt8Sw==" }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "66UotvWcSIq41oiQhLWcQACyKPM4umxXNiht5DQTLZJfNwEswWOcS7Z0xIEHyNIBE7ZpjotH22bEjTkvhPxmVw==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "xNv0FFCVJa5S1beUtye82WFCxKThuE1jbN8DO1x1Rj8VSIWXLBUmfSID5a1fGzsU2R/EMfwPoWclJ2RMfQuGXw==", + "dependencies": { + "Grpc.Net.Common": "2.70.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "rBdEUMyCwa+iB8mqC6JKyPbj3SBHHkReJj/yy/XKJI63GcG6w9DJMMGTVcYHqq4Ci2W4m0HT4jt2pFfFscar8g==", + "dependencies": { + "Grpc.Core.Api": "2.70.0" + } + }, "HotChocolate": { "type": "Transitive", "resolved": "15.1.15", @@ -664,6 +691,11 @@ "resolved": "9.0.11", "contentHash": "+ZxxZzcVU+IEzq12GItUzf/V3mEc5nSLiXijwvDc4zyhbjvSZZ043giSZqGnhakrjwRWjkerIHPrRwm9okEIpw==" }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "saxr2XzwgDU77LaQfYFXmddEDRUKHF4DaGMZkNB3qjdVSZlax3//dGJagJkKrGMIPNZs2jVFXITyCCR6UHJNdA==" + }, "Microsoft.Extensions.Diagnostics.Abstractions": { "type": "Transitive", "resolved": "9.0.11", @@ -943,6 +975,52 @@ "OpenTelemetry.Api": "1.15.3" } }, + "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.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.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" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.6.0", @@ -1148,7 +1226,9 @@ "OpenTelemetry.Instrumentation.AspNetCore": "[1.15.2, )", "OpenTelemetry.Instrumentation.Http": "[1.15.1, )", "Serilog": "[4.3.0, )", - "Serilog.Sinks.Console": "[6.1.1, )" + "Serilog.AspNetCore": "[9.0.0, )", + "Serilog.Sinks.Console": "[6.1.1, )", + "Serilog.Sinks.OpenTelemetry": "[4.2.0, )" } }, "AspNetCore.HealthChecks.SqlServer": { @@ -1341,6 +1421,31 @@ "resolved": "4.3.0", "contentHash": "+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==" }, + "Serilog.AspNetCore": { + "type": "CentralTransitive", + "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.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[9.0.2, )", + "resolved": "9.0.0", + "contentHash": "NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", + "Serilog": "4.2.0" + } + }, "Serilog.Sinks.Console": { "type": "CentralTransitive", "requested": "[6.1.1, )", @@ -1349,6 +1454,17 @@ "dependencies": { "Serilog": "4.0.0" } + }, + "Serilog.Sinks.OpenTelemetry": { + "type": "CentralTransitive", + "requested": "[4.2.0, )", + "resolved": "4.2.0", + "contentHash": "PzMCyE5G19tjr5IZEi5qg+4UU5QrxBEoBEMu/hhYybTrGKXqUDiSGWKZNUDBgelaVKqLADlsmlJVyKce5SyPrg==", + "dependencies": { + "Google.Protobuf": "3.30.1", + "Grpc.Net.Client": "2.70.0", + "Serilog": "4.2.0" + } } } } diff --git a/TournamentAPI/Program.cs b/TournamentAPI/Program.cs index df1fc92..9283731 100644 --- a/TournamentAPI/Program.cs +++ b/TournamentAPI/Program.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Identity; using Serilog; +using Serilog.Sinks.OpenTelemetry; using TournamentAPI.Configuration.Extensions; using TournamentAPI.Data; using TournamentAPI.Data.Models; @@ -11,7 +12,7 @@ Log.Logger = new LoggerConfiguration() .WriteTo.Console() - .CreateLogger(); + .CreateBootstrapLogger(); builder.Services.AddApplicationOptions(); @@ -29,6 +30,19 @@ builder.Services.AddScoped(); +builder.Services.AddSerilog((_, loggerConfiguration) => + loggerConfiguration + .WriteTo.Console() + .WriteTo.OpenTelemetry(opt => + { + opt.Endpoint = new Uri("http://localhost:3100/otlp").ToString(); + opt.Protocol = OtlpProtocol.HttpProtobuf; + opt.ResourceAttributes = new Dictionary + { + ["service.name"] = "TournamentAPI" + }; + })); + var app = builder.Build(); if (app.Environment.IsDevelopment()) diff --git a/TournamentAPI/TournamentAPI.csproj b/TournamentAPI/TournamentAPI.csproj index fff4800..493f0d0 100644 --- a/TournamentAPI/TournamentAPI.csproj +++ b/TournamentAPI/TournamentAPI.csproj @@ -29,7 +29,9 @@ + + diff --git a/TournamentAPI/packages.lock.json b/TournamentAPI/packages.lock.json index fa57a95..4b12b9e 100644 --- a/TournamentAPI/packages.lock.json +++ b/TournamentAPI/packages.lock.json @@ -213,6 +213,21 @@ "resolved": "4.3.0", "contentHash": "+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==" }, + "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.Sinks.Console": { "type": "Direct", "requested": "[6.1.1, )", @@ -222,6 +237,17 @@ "Serilog": "4.0.0" } }, + "Serilog.Sinks.OpenTelemetry": { + "type": "Direct", + "requested": "[4.2.0, )", + "resolved": "4.2.0", + "contentHash": "PzMCyE5G19tjr5IZEi5qg+4UU5QrxBEoBEMu/hhYybTrGKXqUDiSGWKZNUDBgelaVKqLADlsmlJVyKce5SyPrg==", + "dependencies": { + "Google.Protobuf": "3.30.1", + "Grpc.Net.Client": "2.70.0", + "Serilog": "4.2.0" + } + }, "AspNetCore.HealthChecks.UI.Core": { "type": "Transitive", "resolved": "9.0.0", @@ -267,6 +293,11 @@ "Yarp.ReverseProxy": "2.3.0" } }, + "Google.Protobuf": { + "type": "Transitive", + "resolved": "3.30.1", + "contentHash": "HeWXDQBabQn/sCGicbeLJ0HMunknfC4FdLrOQOsaMJHcpqx3HVIpyyJqTrqJlWnza870twhOb+rBcaTiC/TlNA==" + }, "GreenDonut": { "type": "Transitive", "resolved": "15.1.15", @@ -314,6 +345,28 @@ "resolved": "15.1.15", "contentHash": "zKYnY8NMsZvQSY3Mdwtkivu4K/uMCSSjAB9YDiXV54mDwehnYVlFzMTX9MzB34+f8menzcZ8Ko/tqzlhKSt8Sw==" }, + "Grpc.Core.Api": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "66UotvWcSIq41oiQhLWcQACyKPM4umxXNiht5DQTLZJfNwEswWOcS7Z0xIEHyNIBE7ZpjotH22bEjTkvhPxmVw==" + }, + "Grpc.Net.Client": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "xNv0FFCVJa5S1beUtye82WFCxKThuE1jbN8DO1x1Rj8VSIWXLBUmfSID5a1fGzsU2R/EMfwPoWclJ2RMfQuGXw==", + "dependencies": { + "Grpc.Net.Common": "2.70.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0" + } + }, + "Grpc.Net.Common": { + "type": "Transitive", + "resolved": "2.70.0", + "contentHash": "rBdEUMyCwa+iB8mqC6JKyPbj3SBHHkReJj/yy/XKJI63GcG6w9DJMMGTVcYHqq4Ci2W4m0HT4jt2pFfFscar8g==", + "dependencies": { + "Grpc.Core.Api": "2.70.0" + } + }, "HotChocolate": { "type": "Transitive", "resolved": "15.1.15", @@ -1190,6 +1243,52 @@ "OpenTelemetry.Api": "1.15.3" } }, + "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.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.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" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.6.0", @@ -1404,6 +1503,16 @@ "dependencies": { "System.IO.Hashing": "8.0.0" } + }, + "Serilog.Extensions.Logging": { + "type": "CentralTransitive", + "requested": "[9.0.2, )", + "resolved": "9.0.0", + "contentHash": "NwSSYqPJeKNzl5AuXVHpGbr6PkZJFlNa14CdIebVjK3k/76kYj/mz5kiTRNVSsSaxM8kAIa1kpy/qyT9E4npRQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "9.0.0", + "Serilog": "4.2.0" + } } } } diff --git a/docker-compose.yml b/docker-compose.yml index 45f5cc0..d09c9ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,17 @@ services: - ./prometheus/:/etc/prometheus/ - prometheus:/prometheus + loki: + image: grafana/loki:latest + container_name: loki + command: + - '--config.file=/etc/loki/loki.yml' + ports: + - 3100:3100 + volumes: + - ./loki/:/etc/loki/ + - loki:/loki + grafana: image: grafana/grafana:latest container_name: grafana @@ -21,4 +32,5 @@ services: volumes: prometheus: - grafana: \ No newline at end of file + grafana: + loki: \ No newline at end of file diff --git a/loki/loki.yml b/loki/loki.yml new file mode 100644 index 0000000..f9f6be8 --- /dev/null +++ b/loki/loki.yml @@ -0,0 +1,32 @@ +# This is a complete configuration to deploy Loki backed by the filesystem. +# The index will be shipped to the storage via tsdb-shipper. + +auth_enabled: false + +limits_config: + allow_structured_metadata: true + +server: + http_listen_port: 3100 + +common: + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + replication_factor: 1 + path_prefix: /tmp/loki + +schema_config: + configs: + - from: 2020-05-15 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +storage_config: + filesystem: + directory: /tmp/loki/chunks \ No newline at end of file