Skip to content

Commit 3e1bb4e

Browse files
authored
Merge pull request #6 from jongalloway/copilot/fix-3
📊 Add Production-Ready Monitoring and Observability System
2 parents 0e2bee4 + e79c40c commit 3e1bb4e

File tree

109 files changed

+1903
-32
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+1903
-32
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,6 @@ jobs:
100100
fi
101101
continue-on-error: false
102102

103-
- name: Build demo application
104-
run: dotnet build demo --configuration Release --no-restore --verbosity minimal
105-
106103
- name: Publish test results
107104
uses: dorny/test-reporter@v2
108105
if: success() || failure()
@@ -268,4 +265,4 @@ jobs:
268265
prerelease: ${{ contains(steps.version.outputs.version, '-') }}
269266
tag_name: ${{ github.ref_name }}
270267
env:
271-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
268+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

NLWebNet.sln

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet", "src\NLWebNet\NLWebNet.csproj", "{1E458E72-D542-44BB-9F84-1EDE008FBB1D}"
99
EndProject
10-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{A39C23D2-F2C0-258D-165A-CF1E7FEE6E7B}"
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{A39C23D2-F2C0-258D-165A-CF1E7FEE6E7B}"
1111
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet.Demo", "demo\NLWebNet.Demo.csproj", "{6F25FD99-AF67-4509-A46C-FCD450F6A775}"
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet.Demo", "samples\Demo\NLWebNet.Demo.csproj", "{6F25FD99-AF67-4509-A46C-FCD450F6A775}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLWebNet.AspireHost", "samples\AspireHost\NLWebNet.AspireHost.csproj", "{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}"
1315
EndProject
1416
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
1517
EndProject
@@ -61,13 +63,25 @@ Global
6163
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE}.Release|x64.Build.0 = Release|Any CPU
6264
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE}.Release|x86.ActiveCfg = Release|Any CPU
6365
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE}.Release|x86.Build.0 = Release|Any CPU
66+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
68+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x64.ActiveCfg = Debug|Any CPU
69+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x64.Build.0 = Debug|Any CPU
70+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x86.ActiveCfg = Debug|Any CPU
71+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Debug|x86.Build.0 = Debug|Any CPU
72+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
73+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x64.ActiveCfg = Release|Any CPU
75+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x64.Build.0 = Release|Any CPU
76+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x86.ActiveCfg = Release|Any CPU
77+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB}.Release|x86.Build.0 = Release|Any CPU
6478
EndGlobalSection
6579
GlobalSection(SolutionProperties) = preSolution
6680
HideSolutionNode = FALSE
6781
EndGlobalSection
68-
GlobalSection(NestedProjects) = preSolution
69-
{1E458E72-D542-44BB-9F84-1EDE008FBB1D} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
82+
GlobalSection(NestedProjects) = preSolution {1E458E72-D542-44BB-9F84-1EDE008FBB1D} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
7083
{6F25FD99-AF67-4509-A46C-FCD450F6A775} = {A39C23D2-F2C0-258D-165A-CF1E7FEE6E7B}
7184
{21F486B2-CB3A-4D61-8C1F-FBCE3CA48CFE} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
85+
{B8A5E1C0-9E2F-4A2D-8C3D-1234567890AB} = {A39C23D2-F2C0-258D-165A-CF1E7FEE6E7B}
7286
EndGlobalSection
7387
EndGlobal

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,17 @@ NLWebNet/
4848
│ ├── Middleware/ # Request processing middleware
4949
│ ├── Middleware/ # ASP.NET Core middleware
5050
│ └── Extensions/ # Dependency injection extensions
51-
├── demo/ # 🎮 .NET 9 Blazor Web App demo application
51+
├── samples/ # 🎯 Sample applications and usage examples
52+
│ ├── Demo/ # 🎮 .NET 9 Blazor Web App demo application
53+
│ └── AspireHost/ # 🏗️ .NET Aspire orchestration host
5254
│ ├── Components/ # Modern Blazor components
5355
│ │ ├── Layout/ # Layout components (MainLayout, etc.)
5456
│ │ └── Pages/ # Page components (Home, NLWebDemo, Error)
5557
│ ├── wwwroot/ # Static assets (app.css, favicon, etc.)
5658
│ └── Properties/ # Launch settings and configuration
5759
├── doc/ # 📚 Documentation
58-
└── tests/ # 🧪 Unit and integration tests (planned)
60+
└── tests/ # 🧪 Unit and integration tests
61+
└── NLWebNet.Tests/ # 📋 xUnit test project
5962
```
6063

6164
## 🔄 NLWeb Protocol Flow

copilot-setup-steps.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,38 @@ steps:
1212
if command -v dotnet &> /dev/null && dotnet --list-sdks | grep -q "9\."; then
1313
echo "✅ .NET 9 SDK is already installed"
1414
dotnet --version
15+
# Still need to install Aspire workload if not present
16+
if ! dotnet workload list | grep -q "aspire"; then
17+
echo "📦 Installing Aspire workload..."
18+
dotnet workload install aspire
19+
fi
1520
exit 0
1621
fi
17-
22+
1823
# Download and install .NET 9 SDK using official Microsoft install script
1924
echo "📦 Installing .NET 9 SDK..."
2025
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 9.0 --install-dir ~/.dotnet
21-
26+
2227
# Add .NET to PATH if not already present
2328
if [[ ":$PATH:" != *":$HOME/.dotnet:"* ]]; then
2429
echo 'export PATH="$HOME/.dotnet:$PATH"' >> ~/.bashrc
2530
export PATH="$HOME/.dotnet:$PATH"
2631
fi
27-
32+
33+
# Set environment variables for subsequent steps (GitHub Actions specific)
34+
echo "DOTNET_ROOT=$HOME/.dotnet" >> $GITHUB_ENV
35+
echo "$HOME/.dotnet" >> $GITHUB_PATH
36+
echo "$HOME/.dotnet/tools" >> $GITHUB_PATH
37+
2838
# Verify installation
2939
echo "🔍 Verifying .NET installation..."
3040
dotnet --version
3141
dotnet --info
3242
43+
# Install required workloads including Aspire
44+
echo "📦 Installing required workloads..."
45+
dotnet workload install aspire
46+
3347
- name: Restore NuGet packages
3448
description: Restore all NuGet package dependencies for the solution
3549
run: |
@@ -44,6 +58,7 @@ steps:
4458
description: Run a quick verification that everything is set up correctly
4559
run: |
4660
echo "✅ .NET 9 SDK installed successfully"
61+
echo "✅ Required workloads (including Aspire) installed"
4762
echo "✅ NuGet packages restored"
4863
echo "✅ Solution builds successfully"
4964
echo ""

doc/monitoring-demo.md

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# NLWebNet Monitoring and Observability Demo
2+
3+
This document demonstrates the production-ready monitoring and observability features implemented in NLWebNet.
4+
5+
## Features Implemented
6+
7+
### Health Checks
8+
9+
The library now includes comprehensive health checks accessible via REST endpoints:
10+
11+
#### Basic Health Check
12+
```
13+
GET /health
14+
```
15+
16+
Returns basic health status:
17+
```json
18+
{
19+
"status": "Healthy",
20+
"totalDuration": "00:00:00.0123456"
21+
}
22+
```
23+
24+
#### Detailed Health Check
25+
```
26+
GET /health/detailed
27+
```
28+
29+
Returns detailed status of all services:
30+
```json
31+
{
32+
"status": "Healthy",
33+
"totalDuration": "00:00:00.0234567",
34+
"entries": {
35+
"nlweb": {
36+
"status": "Healthy",
37+
"description": "NLWeb service is operational",
38+
"duration": "00:00:00.0012345"
39+
},
40+
"data-backend": {
41+
"status": "Healthy",
42+
"description": "Data backend (MockDataBackend) is operational",
43+
"duration": "00:00:00.0098765"
44+
},
45+
"ai-service": {
46+
"status": "Healthy",
47+
"description": "AI/MCP service is operational",
48+
"duration": "00:00:00.0087654"
49+
}
50+
}
51+
}
52+
```
53+
54+
### Metrics Collection
55+
56+
The library automatically collects comprehensive metrics using .NET 9 built-in metrics:
57+
58+
#### Request Metrics
59+
- `nlweb.requests.total` - Total number of requests processed
60+
- `nlweb.request.duration` - Duration of request processing in milliseconds
61+
- `nlweb.requests.errors` - Total number of request errors
62+
63+
#### AI Service Metrics
64+
- `nlweb.ai.calls.total` - Total number of AI service calls
65+
- `nlweb.ai.duration` - Duration of AI service calls in milliseconds
66+
- `nlweb.ai.errors` - Total number of AI service errors
67+
68+
#### Data Backend Metrics
69+
- `nlweb.data.queries.total` - Total number of data backend queries
70+
- `nlweb.data.duration` - Duration of data backend operations in milliseconds
71+
- `nlweb.data.errors` - Total number of data backend errors
72+
73+
#### Health Check Metrics
74+
- `nlweb.health.checks.total` - Total number of health check executions
75+
- `nlweb.health.failures` - Total number of health check failures
76+
77+
#### Business Metrics
78+
- `nlweb.queries.by_type` - Count of queries by type (List, Summarize, Generate)
79+
- `nlweb.queries.complexity` - Query complexity score based on length and structure
80+
81+
### Rate Limiting
82+
83+
Configurable rate limiting with multiple strategies:
84+
85+
#### Default Configuration
86+
- 100 requests per minute per client
87+
- IP-based identification by default
88+
- Optional client ID-based limiting via `X-Client-Id` header
89+
90+
#### Rate Limit Headers
91+
All responses include rate limit information:
92+
```
93+
X-RateLimit-Limit: 100
94+
X-RateLimit-Remaining: 95
95+
X-RateLimit-Reset: 45
96+
```
97+
98+
#### Rate Limit Exceeded Response
99+
When limits are exceeded, returns HTTP 429:
100+
```json
101+
{
102+
"error": "rate_limit_exceeded",
103+
"message": "Rate limit exceeded. Maximum 100 requests per 1 minute(s).",
104+
"retry_after_seconds": 45
105+
}
106+
```
107+
108+
### Structured Logging
109+
110+
Enhanced logging with correlation IDs and structured data:
111+
112+
#### Correlation ID Tracking
113+
- Automatic correlation ID generation for each request
114+
- Correlation ID included in all log entries
115+
- Exposed via `X-Correlation-ID` response header
116+
117+
#### Structured Log Data
118+
Each log entry includes:
119+
- `CorrelationId` - Unique request identifier
120+
- `RequestPath` - The request path
121+
- `RequestMethod` - HTTP method
122+
- `UserAgent` - Client user agent
123+
- `RemoteIP` - Client IP address
124+
- `Timestamp` - ISO 8601 timestamp
125+
126+
## Configuration
127+
128+
### Basic Setup
129+
130+
```csharp
131+
var builder = WebApplication.CreateBuilder(args);
132+
133+
// Add NLWebNet with monitoring
134+
builder.Services.AddNLWebNet(options =>
135+
{
136+
// Configure rate limiting
137+
options.RateLimiting.Enabled = true;
138+
options.RateLimiting.RequestsPerWindow = 100;
139+
options.RateLimiting.WindowSizeInMinutes = 1;
140+
options.RateLimiting.EnableIPBasedLimiting = true;
141+
options.RateLimiting.EnableClientBasedLimiting = false;
142+
});
143+
144+
var app = builder.Build();
145+
146+
// Add NLWebNet middleware (includes rate limiting, metrics, and correlation IDs)
147+
app.UseNLWebNet();
148+
149+
// Map NLWebNet endpoints (includes health checks)
150+
app.MapNLWebNet();
151+
152+
app.Run();
153+
```
154+
155+
### Advanced Rate Limiting Configuration
156+
157+
```csharp
158+
builder.Services.AddNLWebNet(options =>
159+
{
160+
options.RateLimiting.Enabled = true;
161+
options.RateLimiting.RequestsPerWindow = 500; // Higher limit
162+
options.RateLimiting.WindowSizeInMinutes = 5; // 5-minute window
163+
options.RateLimiting.EnableIPBasedLimiting = false; // Disable IP limiting
164+
options.RateLimiting.EnableClientBasedLimiting = true; // Enable client ID limiting
165+
options.RateLimiting.ClientIdHeader = "X-API-Key"; // Custom header
166+
});
167+
```
168+
169+
### Custom Data Backend with Health Checks
170+
171+
```csharp
172+
// Register custom data backend - health checks automatically included
173+
builder.Services.AddNLWebNet<MyCustomDataBackend>();
174+
```
175+
176+
## Monitoring Integration
177+
178+
### Prometheus/Grafana
179+
180+
The built-in .NET metrics can be exported to Prometheus:
181+
182+
```csharp
183+
builder.Services.AddOpenTelemetry()
184+
.WithMetrics(builder =>
185+
{
186+
builder.AddPrometheusExporter();
187+
builder.AddMeter("NLWebNet"); // Add NLWebNet metrics
188+
});
189+
```
190+
191+
### Azure Application Insights
192+
193+
Integrate with Azure Application Insights:
194+
195+
```csharp
196+
builder.Services.AddApplicationInsightsTelemetry();
197+
```
198+
199+
The structured logging and correlation IDs will automatically be included in Application Insights traces.
200+
201+
## Production Readiness
202+
203+
### What's Included
204+
- ✅ Comprehensive health checks for all services
205+
- ✅ Automatic metrics collection with detailed labels
206+
- ✅ Rate limiting with configurable strategies
207+
- ✅ Structured logging with correlation ID tracking
208+
- ✅ Proper HTTP status codes and error responses
209+
- ✅ CORS support for monitoring endpoints
210+
- ✅ 62 comprehensive tests (100% pass rate)
211+
212+
### Ready for Production Use
213+
The monitoring and observability features are now production-ready and provide:
214+
- Real-time health monitoring
215+
- Performance metrics collection
216+
- Request rate limiting
217+
- Distributed tracing support via correlation IDs
218+
- Integration points for external monitoring systems
219+
220+
### Next Steps for Full Production Deployment
221+
- Configure external monitoring systems (Prometheus, Application Insights)
222+
- Set up alerting rules based on health checks and metrics
223+
- Implement log aggregation and analysis
224+
- Configure distributed tracing for complex scenarios

0 commit comments

Comments
 (0)