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