Program.cs (@/Users/divyanshu/walkover/GiddhTemplate/Program.cs) is the application entry point that configures services, middleware, logging, monitoring, and error handling for the Giddh Template Service.
Execution Flow:
- Global Exception Handlers Setup
- Configuration Loading
- Structured Logging Configuration
- Web Application Builder Setup
- OpenTelemetry Configuration
- Service Registration
- Middleware Pipeline Configuration
- Application Startup
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile(
$"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"}.json",
optional: true)
.AddEnvironmentVariables()
.Build();Configuration Sources (Priority Order):
- Environment Variables (highest)
- Environment-specific JSON files
- Base appsettings.json (lowest)
var serviceVersion = Environment.GetEnvironmentVariable("APP_VERSION") ?? "1.0.0";
var environmentName = Environment.GetEnvironmentVariable("GRAFANA_APP_ENV")
?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
?? "Development";
var serviceName = Environment.GetEnvironmentVariable("SERVICE_NAME") ?? "giddh-template";
var company = Environment.GetEnvironmentVariable("GRAFANA_COMPANY") ?? "Walkover";
var product = Environment.GetEnvironmentVariable("GRAFANA_PRODUCT") ?? "GIDDH";Key Environment Variables:
APP_VERSION- Application version for telemetryGRAFANA_APP_ENV- Environment label for monitoringSERVICE_NAME- Service identifierGRAFANA_COMPANY- Company name for telemetryGRAFANA_PRODUCT- Product name for telemetrySERVER_REGION- Geographic region identifierGRAFANA_ORG_ID- Organization ID for telemetry
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
.Enrich.WithEnvironmentName()
.Enrich.WithProcessId()
.Enrich.WithThreadId()
.Enrich.WithMachineName()
.Enrich.WithProperty("Application", "GiddhTemplateService")
.Enrich.WithProperty("Version", serviceVersion)
.Enrich.WithProperty("Service", serviceName)
.Enrich.WithProperty("ServiceType", serviceType)
.Enrich.WithProperty("Environment", environmentName)
.Enrich.WithProperty("Company", company)
.Enrich.WithProperty("Product", product)
.WriteTo.Console(new JsonFormatter())
.WriteTo.File(new JsonFormatter(), logFilePath, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30)
.CreateLogger();Enrichment Properties:
- Context: Request correlation and tracing
- Environment: Machine name, process ID, thread ID
- Application: Service metadata and versioning
- Business: Company and product information
Output Destinations:
- Console: JSON formatted for container logs
- File:
/var/log/template-logs/giddh-template.logwith 30-day retention
openTelemetry.ConfigureResource(resource =>
resource.AddService(
serviceName: serviceName,
serviceVersion: serviceVersion,
serviceInstanceId: Environment.MachineName)
.AddAttributes(new KeyValuePair<string, object>[]
{
new("deployment.environment", environmentName),
new("company", company),
new("product", product),
new("service.type", serviceType),
new("service.namespace", product),
new("service.instance.id", Environment.MachineName),
new("server.region", serverRegion)
}));openTelemetry.WithTracing(tracing =>
{
tracing
.SetSampler(new AlwaysOnSampler())
.AddAspNetCoreInstrumentation(options =>
{
options.RecordException = true;
})
.AddHttpClientInstrumentation()
.AddSource("GiddhTemplate.*")
.AddOtlpExporter(options =>
{
options.Endpoint = new Uri("http://127.0.0.1:4318/v1/traces");
options.Protocol = OtlpExportProtocol.HttpProtobuf;
if (!string.IsNullOrWhiteSpace(orgId))
{
options.Headers = $"X-Scope-OrgID={orgId}";
}
});
});Tracing Features:
- 100% Sampling: All requests traced
- Exception Recording: Automatic exception capture
- HTTP Instrumentation: Outbound HTTP calls traced
- OTLP Export: OpenTelemetry Protocol export
- Organization Headers: Multi-tenant support
openTelemetry.WithMetrics(metrics =>
{
metrics
.AddAspNetCoreInstrumentation()
.AddRuntimeInstrumentation()
.AddMeter("GiddhTemplate.Metrics")
.AddPrometheusExporter();
});Metrics Sources:
- ASP.NET Core: HTTP request metrics
- Runtime: .NET runtime performance
- Custom: Application-specific metrics
- Prometheus: Metrics export endpoint
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ISlackService, SlackService>();
builder.Services.AddScoped<PdfService>();
builder.Services.AddControllers();Service Lifetimes:
- Scoped: Per-request instances for business services
- Singleton: HTTP client factory (built-in)
- Transient: HTTP context accessor (built-in)
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
Log.Fatal(e.ExceptionObject as Exception, "Unhandled (domain)");
TaskScheduler.UnobservedTaskException += (_, e) =>
{
Log.Error(e.Exception, "Unobserved task");
e.SetObserved();
};app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async ctx =>
{
var exceptionDetails = ctx.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerPathFeature>();
if (exceptionDetails?.Error is Exception ex)
{
// Rich context capture
var userAgent = ctx.Request.Headers["User-Agent"].FirstOrDefault() ?? "Unknown";
var remoteIp = ctx.Connection.RemoteIpAddress?.ToString() ?? "Unknown";
var method = ctx.Request.Method;
var route = ctx.Request.Path.Value ?? "unknown";
var queryString = ctx.Request.QueryString.Value ?? "";
// Distributed tracing context
var activity = Activity.Current;
var traceId = activity?.TraceId.ToString() ?? ctx.TraceIdentifier;
var spanId = activity?.SpanId.ToString() ?? "N/A";
}
});
});Exception Context Capture:
- HTTP Context: Method, route, query parameters
- Client Information: User agent, IP address
- Tracing: Trace ID, span ID correlation
- Timing: Request timestamp and duration
Error Response:
await ctx.Response.WriteAsJsonAsync(new
{
error = "Internal Server Error",
traceId = ctx.TraceIdentifier,
timestamp = DateTimeOffset.UtcNow
});try
{
var slackService = ctx.RequestServices.GetRequiredService<ISlackService>();
var errorContext = $"**Route:** {route} {method}\n**TraceId:** {traceId}\n**UserAgent:** {userAgent}\n**RemoteIP:** {remoteIp}";
await slackService.SendErrorAlertAsync(
route,
slackEnvironment,
$"{ex.GetType().Name}: {ex.Message}\n\n{errorContext}",
ex.StackTrace ?? "No stack trace available");
}
catch (Exception slackEx)
{
Log.Warning(slackEx, "Failed to send Slack alert for {Route} {Method}", route, method);
}app.UseSerilogRequestLogging(options =>
{
options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} → {StatusCode} ({Elapsed:0.0000}ms) [{ContentLength}b] | TraceId: {TraceId}";
options.GetLevel = (httpContext, elapsed, ex) =>
{
if (ex != null || httpContext.Response.StatusCode >= 500)
return Serilog.Events.LogEventLevel.Error;
if (httpContext.Response.StatusCode >= 400)
return Serilog.Events.LogEventLevel.Warning;
if (elapsed > 5000) // Slow requests
return Serilog.Events.LogEventLevel.Warning;
return Serilog.Events.LogEventLevel.Information;
};
});Log Level Strategy:
- Error: Exceptions or 5xx status codes
- Warning: 4xx status codes or slow requests (>5s)
- Information: Successful requests
options.EnrichDiagnosticContext = (diagCtx, httpContext) =>
{
// Request context
diagCtx.Set("RequestHost", httpContext.Request.Host.Value);
diagCtx.Set("RequestScheme", httpContext.Request.Scheme);
diagCtx.Set("QueryString", httpContext.Request.QueryString.Value);
// Client context
diagCtx.Set("UserAgent", httpContext.Request.Headers["User-Agent"].FirstOrDefault() ?? "Unknown");
diagCtx.Set("RemoteIpAddress", httpContext.Connection.RemoteIpAddress?.ToString() ?? "Unknown");
// Response context
diagCtx.Set("ContentLength", httpContext.Response.ContentLength ?? 0);
diagCtx.Set("ContentType", httpContext.Response.ContentType);
// Distributed tracing context
var activity = Activity.Current;
if (activity != null)
{
diagCtx.Set("TraceId", activity.TraceId.ToString());
diagCtx.Set("SpanId", activity.SpanId.ToString());
diagCtx.Set("ParentId", activity.ParentId);
}
};app.MapPrometheusScrapingEndpoint();
app.MapControllers();Endpoints:
/metrics- Prometheus metrics scraping- API Controllers - Business logic endpoints
try
{
Log.Information("GIDDH Template Service started successfully on port 5000");
await app.RunAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "GIDDH Template Service terminated unexpectedly");
throw;
}
finally
{
Log.Information("GIDDH Template Service is shutting down...");
await Log.CloseAndFlushAsync();
}Shutdown Process:
- Graceful Stop: Await pending requests
- Log Flush: Ensure all logs are written
- Resource Cleanup: Dispose of services
- Non-blocking I/O: All file and network operations
- Concurrent Processing: Multiple requests handled simultaneously
- Resource Pooling: HTTP client factory usage
- Scoped Services: Per-request lifecycle
- Logging Buffers: Efficient log batching
- Configuration Caching: Single configuration load
- Health Checks: Built-in ASP.NET Core health endpoints
- Metrics Collection: Real-time performance data
- Distributed Tracing: Request flow visibility
- Generic Client Responses: No sensitive information exposed
- Detailed Server Logs: Full context for debugging
- Trace Correlation: Secure trace ID generation
- Model Binding: Automatic request validation
- Content Type Validation: JSON content enforcement
- Request Size Limits: Built-in ASP.NET Core limits
var environmentName = Environment.GetEnvironmentVariable("GRAFANA_APP_ENV")
?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
?? "Development";Development Features:
- Detailed exception pages
- Swagger UI enabled
- Console logging prominent
Production Features:
- Structured JSON logging
- External monitoring integration
- Error alerting via Slack
-
Configuration Missing:
- Verify environment variables
- Check appsettings.json structure
- Validate file permissions
-
Logging Failures:
- Ensure log directory exists (
/var/log/template-logs/) - Check write permissions
- Verify Grafana Loki connectivity
- Ensure log directory exists (
-
OpenTelemetry Issues:
- Validate OTLP endpoint accessibility
- Check organization ID configuration
- Verify network connectivity
-
Startup Logging:
Log.Information("Starting GIDDH Template Service...");
-
Configuration Validation:
- Log loaded configuration values
- Verify environment variable resolution
-
Service Registration:
- Validate dependency injection setup
- Check service lifetime configurations
Author/Developer: Divyanshu Shrivastava
Last Updated: December 2025