[{"data":1,"prerenderedAt":107},["ShallowReactive",2],{"qa-\u002Fdotnet\u002Fperformance-deployment\u002Flogging-monitoring":3},{"page":4,"siblings":94,"blog":104},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":84,"related":85,"seo":86,"seoDescription":87,"stem":88,"subtopic":89,"topic":90,"topicSlug":91,"updated":92,"__hash__":93},"qa\u002Fdotnet\u002Fperformance-deployment\u002Flogging-monitoring.md","Logging Monitoring",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md",".NET Core","dotnet",{},true,"\u002Fdotnet\u002Fperformance-deployment\u002Flogging-monitoring",[22,27,31,35,39,43,47,52,56,60,64,68,72,76,80],{"id":23,"difficulty":24,"q":25,"a":26},"ilogger-basics","easy","How does the built-in ILogger work in ASP.NET Core?","`ILogger\u003CT>` is the standard logging abstraction in ASP.NET Core. It is\nregistered automatically and injected like any other service. The generic\nparameter `T` becomes the **category name** that appears in every log entry,\nletting you filter by class or namespace.\n\n```csharp\npublic class OrderService\n{\n    private readonly ILogger\u003COrderService> _logger;\n\n    public OrderService(ILogger\u003COrderService> logger) => _logger = logger;\n\n    public async Task\u003COrder> PlaceOrderAsync(Order order)\n    {\n        _logger.LogInformation(\"Placing order {OrderId} for customer {CustomerId}\",\n            order.Id, order.CustomerId);\n\n        try\n        {\n            var result = await _repository.SaveAsync(order);\n            _logger.LogInformation(\"Order {OrderId} saved successfully\", result.Id);\n            return result;\n        }\n        catch (Exception ex)\n        {\n            \u002F\u002F Exception is the first parameter when logging errors:\n            _logger.LogError(ex, \"Failed to place order {OrderId}\", order.Id);\n            throw;\n        }\n    }\n}\n\n\u002F\u002F Configuration in appsettings.json — control minimum levels per namespace:\n\u002F\u002F {\n\u002F\u002F \"Logging\": {\n\u002F\u002F \"LogLevel\": {\n\u002F\u002F \"Default\":             \"Information\",\n\u002F\u002F \"Microsoft.AspNetCore\": \"Warning\",\n\u002F\u002F \"MyApp.Data\":          \"Debug\"\n\u002F\u002F }\n\u002F\u002F }\n\u002F\u002F }\n```\n\nUse **message templates** with named placeholders (`{OrderId}`) rather than\nstring interpolation (`$\"Order {order.Id}\"`). Structured logging providers\ncapture placeholders as separate fields, enabling rich querying in log aggregators.\n\n**Rule of thumb:** Always use message templates, never string interpolation.\n`LogInformation($\"Order {id}\")` stores one string; `LogInformation(\"Order {Id}\", id)`\nstores a structured document with a searchable `Id` field.\n",{"id":28,"difficulty":24,"q":29,"a":30},"log-levels","What are the log levels in .NET and when should each be used?",".NET defines six log levels in ascending severity. The configured minimum level\nfilters out everything below it, so lower-severity messages cost nothing at runtime\nwhen they are below the threshold.\n\n```csharp\n\u002F\u002F Levels in order — Trace (0) through Critical (5):\n_logger.LogTrace(\"Entering GetProductAsync(id={Id})\", id);       \u002F\u002F 0 — per-step traces\n_logger.LogDebug(\"Cache miss for product {Id}\", id);             \u002F\u002F 1 — developer diagnostics\n_logger.LogInformation(\"Order {Id} placed successfully\", id);    \u002F\u002F 2 — normal business events\n_logger.LogWarning(\"Retry {Attempt} for order {Id}\", attempt, id); \u002F\u002F 3 — unexpected but handled\n_logger.LogError(ex, \"Failed to save order {Id}\", id);           \u002F\u002F 4 — failure, action needed\n_logger.LogCritical(ex, \"Database connection lost\");             \u002F\u002F 5 — system down\n\n\u002F\u002F Guard against expensive computation when level is disabled:\nif (_logger.IsEnabled(LogLevel.Debug))\n    _logger.LogDebug(\"Full order payload: {Payload}\", JsonSerializer.Serialize(order));\n\n\u002F\u002F Common level strategy by environment:\n\u002F\u002F Development: Debug (verbose, includes framework internals)\n\u002F\u002F Staging:     Information (business events, warnings)\n\u002F\u002F Production:  Warning (only unexpected or error events)\n```\n\n| Level | Use case |\n|-------|----------|\n| Trace | Step-by-step tracing for deep debugging |\n| Debug | Developer diagnostics (loop values, cache state) |\n| Information | Normal business events (order placed, user logged in) |\n| Warning | Handled unexpected conditions (retry, degraded mode) |\n| Error | Unhandled failures that need investigation |\n| Critical | System-wide failures requiring immediate action |\n\n**Rule of thumb:** Log `Information` for every significant business event —\nthese are the facts you want in an audit trail. Log `Warning` when you handle\nan error gracefully but something unexpected happened. Log `Error` only for\nfailures that actually matter.\n",{"id":32,"difficulty":14,"q":33,"a":34},"structured-logging","What is structured logging and why is it preferred over plain text logging?","**Structured logging** captures log entries as key-value documents rather than\nflat strings. This allows log aggregators (Seq, Elasticsearch, Splunk) to query,\nfilter, and aggregate on individual fields — something impossible with plain text.\n\n```csharp\n\u002F\u002F Plain text — one unsearchable string:\n_logger.LogInformation($\"Order {order.Id} for customer {order.CustomerId} total {order.Total}\");\n\u002F\u002F Log: \"Order 42 for customer 99 total 150.00\"\n\n\u002F\u002F Structured — document with searchable fields:\n_logger.LogInformation(\n    \"Order {OrderId} placed for {CustomerId}, total {Total:C}\",\n    order.Id, order.CustomerId, order.Total);\n\u002F\u002F Log document: { \"OrderId\": 42, \"CustomerId\": 99, \"Total\": 150.00,\n\u002F\u002F \"@t\": \"...\", \"@l\": \"Information\", ... }\n\n\u002F\u002F Querying structured logs in Seq or Kibana:\n\u002F\u002F OrderId = 42\n\u002F\u002F Total > 100 AND @l = 'Error'\n\u002F\u002F CustomerId = 99 AND @t > '2026-06-01'\n\n\u002F\u002F Destructure objects — capture all properties with @:\n_logger.LogInformation(\"Processing {@Order}\", order);\n\u002F\u002F Captures: Order.Id, Order.Sku, Order.Total, Order.CustomerId as separate fields\n\n\u002F\u002F Avoid destructuring large objects — it serializes the whole graph:\n\u002F\u002F _logger.LogInformation(\"Context {@DbContext}\", dbContext); \u002F\u002F thousands of fields!\n\u002F\u002F _logger.LogInformation(\"DB query completed for {EntityType}\", typeof(Order).Name);\n```\n\nStructured logging transforms log files from write-only archives into\nqueryable datasets. You can answer \"how many orders over $100 failed last\nThursday?\" without parsing strings.\n\n**Rule of thumb:** Every significant log entry should capture the entity ID\nand the relevant business context as named fields. Never log by concatenating\nstrings — you will want to query those fields later.\n",{"id":36,"difficulty":14,"q":37,"a":38},"serilog","How do you configure Serilog in an ASP.NET Core application?","**Serilog** is the most popular third-party logging library for .NET. It\nintegrates with `ILogger\u003CT>` so existing code needs no changes, but adds\nrich sink support (files, Seq, Elasticsearch, Application Insights) and\noutput templates.\n\n```csharp\n\u002F\u002F dotnet add package Serilog.AspNetCore\n\u002F\u002F dotnet add package Serilog.Sinks.Console\n\u002F\u002F dotnet add package Serilog.Sinks.File\n\n\u002F\u002F Program.cs — configure Serilog before building the host:\nLog.Logger = new LoggerConfiguration()\n    .MinimumLevel.Information()\n    .MinimumLevel.Override(\"Microsoft.AspNetCore\", LogEventLevel.Warning)\n    .Enrich.FromLogContext()                \u002F\u002F adds scope properties to every event\n    .Enrich.WithMachineName()\n    .WriteTo.Console(new JsonFormatter())   \u002F\u002F structured JSON to stdout\n    .WriteTo.File(\n        path: \"logs\u002Fapp-.log\",\n        rollingInterval: RollingInterval.Day,\n        outputTemplate: \"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}\")\n    .WriteTo.Seq(\"http:\u002F\u002Fseq:5341\")         \u002F\u002F central log server\n    .CreateLogger();\n\nbuilder.Host.UseSerilog();                  \u002F\u002F replaces the built-in providers\n\n\u002F\u002F appsettings.json-driven config (recommended for production flexibility):\nLog.Logger = new LoggerConfiguration()\n    .ReadFrom.Configuration(builder.Configuration)\n    .CreateLogger();\n\n\u002F\u002F Request logging middleware — one log line per HTTP request with timing:\napp.UseSerilogRequestLogging(opts =>\n{\n    opts.MessageTemplate =\n        \"{RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms\";\n    opts.EnrichDiagnosticContext = (diagCtx, httpCtx) =>\n        diagCtx.Set(\"UserId\", httpCtx.User.FindFirst(\"sub\")?.Value ?? \"anon\");\n});\n```\n\n**Rule of thumb:** Use `UseSerilogRequestLogging()` to replace ASP.NET Core's\nverbose default request log (which emits two events per request) with one\nstructured event per request that includes timing, status code, and custom fields.\n",{"id":40,"difficulty":14,"q":41,"a":42},"log-scopes","What are log scopes in .NET and when do you use them?","**Log scopes** add ambient context properties to every log entry emitted within\na `using` block. This is essential for correlating all logs from one request or\noperation without passing extra parameters through every method call.\n\n```csharp\n\u002F\u002F ILogger.BeginScope — add fields to all logs within the using block:\npublic async Task\u003COrder> PlaceOrderAsync(Order order)\n{\n    using (_logger.BeginScope(new Dictionary\u003Cstring, object>\n    {\n        [\"OrderId\"]    = order.Id,\n        [\"CustomerId\"] = order.CustomerId,\n        [\"CorrelationId\"] = Activity.Current?.Id ?? Guid.NewGuid().ToString(),\n    }))\n    {\n        \u002F\u002F Every log inside this using block carries OrderId + CustomerId:\n        _logger.LogInformation(\"Validating order\");         \u002F\u002F has OrderId\n        await ValidateAsync(order);\n        _logger.LogInformation(\"Saving order to database\"); \u002F\u002F has OrderId\n        await _repository.SaveAsync(order);\n        _logger.LogInformation(\"Sending confirmation email\"); \u002F\u002F has OrderId\n        await _emailService.SendAsync(order);\n    }\n    \u002F\u002F Outside the using — scope properties are gone\n    _logger.LogInformation(\"Done\"); \u002F\u002F no OrderId here\n}\n\n\u002F\u002F Scopes nest — inner scopes add to outer:\nusing (_logger.BeginScope(\"RequestId: {RequestId}\", requestId))\nusing (_logger.BeginScope(\"TenantId: {TenantId}\", tenantId))\n{\n    _logger.LogInformation(\"Processing\"); \u002F\u002F has both RequestId and TenantId\n}\n\n\u002F\u002F In Serilog, Enrich.FromLogContext() is required to pick up scope properties.\n\u002F\u002F In the built-in provider, IncludeScopes must be true (default):\n\u002F\u002F \"Logging\": { \"Console\": { \"IncludeScopes\": true } }\n```\n\n**Rule of thumb:** Use scopes to attach a correlation ID, request ID, or\ntransaction ID at the top of an operation. This makes it trivial to filter\nall logs for one request in any log aggregator.\n",{"id":44,"difficulty":14,"q":45,"a":46},"health-checks","How do you implement health checks in ASP.NET Core?","ASP.NET Core's **health checks** expose a `\u002Fhealth` endpoint that orchestrators\n(Kubernetes, load balancers) probe to decide whether to route traffic to an\ninstance. Checks can verify databases, message brokers, and external services.\n\n```csharp\n\u002F\u002F Program.cs:\nbuilder.Services.AddHealthChecks()\n    .AddDbContextCheck\u003CAppDbContext>()       \u002F\u002F checks EF Core can query\n    .AddRedis(\"localhost:6379\")              \u002F\u002F checks Redis connectivity\n    .AddUrlGroup(new Uri(\"https:\u002F\u002Fapi.partner.com\u002Fhealth\"), \"partner-api\")\n    .AddCheck(\"custom\", () =>               \u002F\u002F arbitrary custom check\n    {\n        var queueDepth = _queue.GetDepth();\n        return queueDepth \u003C 1000\n            ? HealthCheckResult.Healthy($\"Queue depth: {queueDepth}\")\n            : HealthCheckResult.Degraded($\"Queue depth high: {queueDepth}\");\n    }, tags: [\"readiness\"]);\n\n\u002F\u002F Two endpoints — liveness (is it running?) and readiness (can it take traffic?):\napp.MapHealthChecks(\"\u002Fhealth\u002Flive\",  new HealthCheckOptions\n{\n    Predicate = _ => false,  \u002F\u002F no checks — just returns 200 if the process is up\n});\n\napp.MapHealthChecks(\"\u002Fhealth\u002Fready\", new HealthCheckOptions\n{\n    Predicate = check => check.Tags.Contains(\"readiness\"),\n    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, \u002F\u002F rich JSON\n});\n\n\u002F\u002F Response shape (healthy):\n\u002F\u002F { \"status\": \"Healthy\", \"checks\": [{ \"name\": \"...\", \"status\": \"Healthy\" }] }\n```\n\nHealth check packages:\n- `AspNetCore.HealthChecks.SqlServer` \u002F `Npgsql`\n- `AspNetCore.HealthChecks.Redis`\n- `AspNetCore.HealthChecks.UI` for a dashboard\n\n**Rule of thumb:** Always expose separate liveness and readiness probes.\nLiveness (is the process alive?) should never check external dependencies —\na failing database should not kill the process, just take it out of rotation.\n",{"id":48,"difficulty":49,"q":50,"a":51},"metrics-opentelemetry","hard","How do you emit custom metrics in .NET using System.Diagnostics.Metrics?",".NET 8+ has a built-in metrics API in `System.Diagnostics.Metrics`. Metrics\nare counters, histograms, and gauges that can be exported to Prometheus,\nOpenTelemetry, or Application Insights without changing application code.\n\n```csharp\n\u002F\u002F Define a Meter and instruments once (usually a static field):\npublic static class AppMetrics\n{\n    private static readonly Meter _meter = new(\"MyApp.Orders\", \"1.0.0\");\n\n    public static readonly Counter\u003Clong>   OrdersPlaced =\n        _meter.CreateCounter\u003Clong>(\"orders.placed.total\",\n            unit: \"{orders}\", description: \"Total orders placed\");\n\n    public static readonly Histogram\u003Cdouble> OrderTotal =\n        _meter.CreateHistogram\u003Cdouble>(\"orders.total.amount\",\n            unit: \"USD\", description: \"Distribution of order totals\");\n\n    public static readonly ObservableGauge\u003Cint> QueueDepth =\n        _meter.CreateObservableGauge(\"orders.queue.depth\",\n            () => OrderQueue.CurrentDepth, unit: \"{orders}\");\n}\n\n\u002F\u002F Emit metrics in application code:\npublic async Task\u003COrder> PlaceOrderAsync(Order order)\n{\n    var result = await _repo.SaveAsync(order);\n\n    AppMetrics.OrdersPlaced.Add(1, new TagList\n    {\n        { \"region\",  order.Region },\n        { \"channel\", order.Channel },\n    });\n    AppMetrics.OrderTotal.Record(order.Total, new TagList\n    {\n        { \"currency\", order.Currency },\n    });\n\n    return result;\n}\n\n\u002F\u002F Export to Prometheus (dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore):\nbuilder.Services.AddOpenTelemetry()\n    .WithMetrics(metrics => metrics\n        .AddMeter(\"MyApp.Orders\")\n        .AddPrometheusExporter());\n\napp.MapPrometheusScrapingEndpoint(\"\u002Fmetrics\");\n```\n\n**Rule of thumb:** Define meters and instruments as static fields — creating\nthem per-request is expensive and causes duplicate registration errors.\nUse tags (dimensions) to slice metrics by region, tenant, or error type\nrather than creating separate instruments per value.\n",{"id":53,"difficulty":49,"q":54,"a":55},"distributed-tracing","What is distributed tracing in .NET and how does OpenTelemetry enable it?","**Distributed tracing** tracks a request as it flows through multiple services.\nEach service adds a span to a shared trace, creating a full timeline that shows\nwhere time was spent and where errors occurred.\n\n```csharp\n\u002F\u002F dotnet add package OpenTelemetry.Extensions.Hosting\n\u002F\u002F dotnet add package OpenTelemetry.Instrumentation.AspNetCore\n\u002F\u002F dotnet add package OpenTelemetry.Instrumentation.Http\n\u002F\u002F dotnet add package OpenTelemetry.Instrumentation.EntityFrameworkCore\n\u002F\u002F dotnet add package OpenTelemetry.Exporter.Jaeger\n\nbuilder.Services.AddOpenTelemetry()\n    .WithTracing(tracing => tracing\n        .AddSource(\"MyApp.Orders\")            \u002F\u002F custom spans from this source\n        .AddAspNetCoreInstrumentation()        \u002F\u002F HTTP requests auto-instrumented\n        .AddHttpClientInstrumentation()        \u002F\u002F outbound HTTP calls\n        .AddEntityFrameworkCoreInstrumentation() \u002F\u002F EF Core queries\n        .AddJaegerExporter(j =>\n            j.AgentHost = \"jaeger\"));          \u002F\u002F export to Jaeger\n\n\u002F\u002F Create custom spans for significant operations:\nprivate static readonly ActivitySource _source = new(\"MyApp.Orders\");\n\npublic async Task\u003COrder> PlaceOrderAsync(Order order)\n{\n    using var activity = _source.StartActivity(\"PlaceOrder\");\n    activity?.SetTag(\"order.id\",       order.Id);\n    activity?.SetTag(\"customer.id\",    order.CustomerId);\n    activity?.SetTag(\"order.total\",    order.Total);\n\n    try\n    {\n        var result = await _repo.SaveAsync(order);\n        activity?.SetStatus(ActivityStatusCode.Ok);\n        return result;\n    }\n    catch (Exception ex)\n    {\n        activity?.SetStatus(ActivityStatusCode.Error, ex.Message);\n        activity?.RecordException(ex);\n        throw;\n    }\n}\n```\n\nThe W3C `traceparent` header propagates the trace ID across service boundaries.\nASP.NET Core reads and forwards this header automatically when\n`AddAspNetCoreInstrumentation()` is configured.\n\n**Rule of thumb:** Add custom spans (`ActivitySource.StartActivity`) for your\nbusiness operations, not just for infrastructure. A Jaeger or Zipkin trace\nthat shows \"HTTP → EF Core → HTTP\" is less useful than one that shows\n\"HTTP → PlaceOrder → ValidateInventory → ChargePayment.\"\n",{"id":57,"difficulty":14,"q":58,"a":59},"exception-middleware-logging","How do you log unhandled exceptions globally in ASP.NET Core?","ASP.NET Core provides two standard mechanisms for global exception handling:\n`UseExceptionHandler` (for non-API responses) and `IExceptionHandler`\n(introduced in .NET 8, interface-based, testable).\n\n```csharp\n\u002F\u002F .NET 8+ — IExceptionHandler (preferred):\npublic class GlobalExceptionHandler : IExceptionHandler\n{\n    private readonly ILogger\u003CGlobalExceptionHandler> _logger;\n\n    public GlobalExceptionHandler(ILogger\u003CGlobalExceptionHandler> logger)\n        => _logger = logger;\n\n    public async ValueTask\u003Cbool> TryHandleAsync(\n        HttpContext httpContext,\n        Exception   exception,\n        CancellationToken ct)\n    {\n        _logger.LogError(exception,\n            \"Unhandled exception on {Method} {Path}: {Message}\",\n            httpContext.Request.Method,\n            httpContext.Request.Path,\n            exception.Message);\n\n        httpContext.Response.StatusCode  = StatusCodes.Status500InternalServerError;\n        httpContext.Response.ContentType = \"application\u002Fproblem+json\";\n\n        await httpContext.Response.WriteAsJsonAsync(new ProblemDetails\n        {\n            Status   = 500,\n            Title    = \"An unexpected error occurred\",\n            Instance = httpContext.Request.Path,\n        }, ct);\n\n        return true; \u002F\u002F handled — stop propagation\n    }\n}\n\n\u002F\u002F Registration:\nbuilder.Services.AddExceptionHandler\u003CGlobalExceptionHandler>();\nbuilder.Services.AddProblemDetails();\napp.UseExceptionHandler();\n\n\u002F\u002F Older approach — UseExceptionHandler with a lambda:\napp.UseExceptionHandler(appError => appError.Run(async context =>\n{\n    var feature = context.Features.Get\u003CIExceptionHandlerFeature>();\n    var ex      = feature?.Error;\n    if (ex is not null)\n        logger.LogError(ex, \"Unhandled exception\");\n    context.Response.StatusCode = 500;\n    await context.Response.WriteAsync(\"An error occurred.\");\n}));\n```\n\n**Rule of thumb:** Use `IExceptionHandler` in .NET 8+ for global exception\nlogging — it's DI-friendly, testable, and can be chained (multiple handlers\ntried in registration order). Always return a `ProblemDetails` JSON response\nrather than a plain string so API clients can parse the error.\n",{"id":61,"difficulty":49,"q":62,"a":63},"logging-performance","How do you avoid performance overhead from logging in hot paths?","Logging is never truly free. Even when a message is below the minimum level,\nthe arguments may be evaluated before the logger can discard them. High-volume\npaths need additional guards.\n\n```csharp\n\u002F\u002F Problem — string interpolation always allocates:\n_logger.LogDebug($\"Processing item {item.Id} with payload {JsonSerializer.Serialize(item)}\");\n\u002F\u002F JsonSerializer.Serialize runs on every call, even if Debug is disabled!\n\n\u002F\u002F Fix 1 — IsEnabled guard:\nif (_logger.IsEnabled(LogLevel.Debug))\n    _logger.LogDebug(\"Processing item {Id} with payload {Payload}\",\n        item.Id, JsonSerializer.Serialize(item));\n\n\u002F\u002F Fix 2 — LoggerMessage.Define (zero-allocation for hot paths):\nprivate static readonly Action\u003CILogger, int, string, Exception?> _logProcessing =\n    LoggerMessage.Define\u003Cint, string>(\n        LogLevel.Debug,\n        new EventId(1001, \"ProcessingItem\"),\n        \"Processing item {Id} with payload {Payload}\");\n\n\u002F\u002F Call site — no allocation if Debug is disabled:\n_logProcessing(_logger, item.Id, item.Payload, null);\n\n\u002F\u002F Fix 3 — [LoggerMessage] source generator (.NET 6+, preferred):\npublic partial class ItemProcessor\n{\n    private readonly ILogger\u003CItemProcessor> _logger;\n\n    [LoggerMessage(Level = LogLevel.Debug,\n                   Message = \"Processing item {Id} with payload {Payload}\")]\n    partial void LogProcessing(int id, string payload);\n\n    public void Process(Item item)\n    {\n        LogProcessing(item.Id, item.Payload); \u002F\u002F zero-alloc if Debug is off\n        \u002F\u002F ...\n    }\n}\n```\n\n`[LoggerMessage]` source generators produce the same zero-allocation code as\n`LoggerMessage.Define` but with a clean, readable call site and compile-time\nvalidation of the message template.\n\n**Rule of thumb:** Use `[LoggerMessage]` source generators in any method called\nmore than ~1000 times per second. For normal business logic paths, the built-in\n`ILogger.LogXxx` with message templates is fast enough.\n",{"id":65,"difficulty":14,"q":66,"a":67},"application-insights","How do you integrate Application Insights with an ASP.NET Core application?","**Application Insights** is Azure's application performance monitoring (APM)\nservice. It collects logs, metrics, traces, and exceptions with minimal\nconfiguration and provides a rich analytics dashboard.\n\n```csharp\n\u002F\u002F dotnet add package Microsoft.ApplicationInsights.AspNetCore\n\n\u002F\u002F Program.cs:\nbuilder.Services.AddApplicationInsightsTelemetry(opts =>\n    opts.ConnectionString = builder.Configuration[\"ApplicationInsights:ConnectionString\"]);\n\n\u002F\u002F appsettings.json:\n\u002F\u002F {\n\u002F\u002F \"ApplicationInsights\": {\n\u002F\u002F \"ConnectionString\": \"InstrumentationKey=...;IngestionEndpoint=...\"\n\u002F\u002F }\n\u002F\u002F }\n\n\u002F\u002F Custom telemetry — track business events:\npublic class OrderService\n{\n    private readonly TelemetryClient _telemetry;\n\n    public async Task\u003COrder> PlaceOrderAsync(Order order)\n    {\n        var result = await _repo.SaveAsync(order);\n\n        \u002F\u002F Track a custom event with properties:\n        _telemetry.TrackEvent(\"OrderPlaced\", new Dictionary\u003Cstring, string>\n        {\n            [\"OrderId\"]    = result.Id.ToString(),\n            [\"CustomerId\"] = order.CustomerId.ToString(),\n            [\"Channel\"]    = order.Channel,\n        }, new Dictionary\u003Cstring, double>\n        {\n            [\"Total\"]    = (double)order.Total,\n            [\"ItemCount\"] = order.Items.Count,\n        });\n\n        return result;\n    }\n}\n\n\u002F\u002F Custom dependency tracking (for non-HTTP dependencies):\nusing var operation = _telemetry.StartOperation\u003CDependencyTelemetry>(\"PaymentGateway\");\noperation.Telemetry.Type   = \"HTTP\";\noperation.Telemetry.Target = \"payment.example.com\";\ntry { await _gateway.ChargeAsync(order); operation.Telemetry.Success = true; }\ncatch { operation.Telemetry.Success = false; throw; }\n```\n\n**Rule of thumb:** Let Application Insights auto-collect HTTP, SQL, and exception\ntelemetry. Add `TrackEvent` only for domain-significant events (order placed,\nsubscription cancelled) that are not captured automatically.\n",{"id":69,"difficulty":24,"q":70,"a":71},"log-filtering-overrides","How do you filter log output by namespace or provider in ASP.NET Core?","The logging framework applies a **minimum level** filter per category (namespace\nor class name) and per provider (Console, File, Application Insights). Filters\nare configured in `appsettings.json` or in code without touching log call sites.\n\n```json\n\u002F\u002F appsettings.json — granular filtering by category and provider:\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\":                        \"Information\",\n      \"Microsoft\":                      \"Warning\",\n      \"Microsoft.AspNetCore\":           \"Warning\",\n      \"Microsoft.EntityFrameworkCore\":  \"Warning\",\n      \"Microsoft.EntityFrameworkCore.Database.Command\": \"Information\",\n      \"MyApp.Services.OrderService\":    \"Debug\"\n    },\n    \"Console\": {\n      \"LogLevel\": {\n        \"Default\": \"Warning\"\n      }\n    },\n    \"ApplicationInsights\": {\n      \"LogLevel\": {\n        \"Default\": \"Information\"\n      }\n    }\n  }\n}\n```\n\n```csharp\n\u002F\u002F Equivalent in code — useful for dynamic configuration:\nbuilder.Logging.AddFilter(\"Microsoft.EntityFrameworkCore\", LogLevel.Warning);\nbuilder.Logging.AddFilter\u003CConsoleLoggerProvider>(\"Default\", LogLevel.Warning);\n\n\u002F\u002F Filter by predicate — suppress specific event IDs:\nbuilder.Logging.AddFilter((provider, category, level) =>\n{\n    \u002F\u002F Suppress EF Core command logging in Application Insights to reduce cost:\n    if (provider.Contains(\"ApplicationInsights\") &&\n        category.StartsWith(\"Microsoft.EntityFrameworkCore\"))\n        return false;\n    return level >= LogLevel.Information;\n});\n```\n\nCategory rules are matched by prefix: `Microsoft.AspNetCore` matches\n`Microsoft.AspNetCore.Routing`, `Microsoft.AspNetCore.Mvc`, etc. The most\nspecific matching rule wins.\n\n**Rule of thumb:** In production, set `Microsoft` and `System` namespaces to\n`Warning` and your own application namespaces to `Information`. This cuts log\nvolume by 80% and eliminates noise from framework internals while keeping\nall business events.\n",{"id":73,"difficulty":14,"q":74,"a":75},"enrichers-log-context","How do you enrich log entries with ambient context (user, tenant, request ID) in .NET?","**Log enrichers** add properties to every log entry automatically, without\npassing extra parameters through every method call. ASP.NET Core's `ILogger`\nscopes and Serilog's `LogContext` are the two main mechanisms.\n\n```csharp\n\u002F\u002F Middleware — push ambient context into the log scope for every request:\npublic class LogEnrichmentMiddleware\n{\n    private readonly RequestDelegate _next;\n\n    public LogEnrichmentMiddleware(RequestDelegate next) => _next = next;\n\n    public async Task InvokeAsync(HttpContext ctx, ILogger\u003CLogEnrichmentMiddleware> logger)\n    {\n        var userId   = ctx.User.FindFirst(\"sub\")?.Value   ?? \"anon\";\n        var tenantId = ctx.User.FindFirst(\"tenant\")?.Value ?? \"none\";\n        var traceId  = Activity.Current?.TraceId.ToString()\n                       ?? ctx.TraceIdentifier;\n\n        \u002F\u002F ILogger scope — carried by all loggers in this request:\n        using (logger.BeginScope(new Dictionary\u003Cstring, object>\n        {\n            [\"UserId\"]    = userId,\n            [\"TenantId\"]  = tenantId,\n            [\"TraceId\"]   = traceId,\n        }))\n        {\n            await _next(ctx);\n        }\n    }\n}\n\n\u002F\u002F Register the middleware early in the pipeline:\napp.UseMiddleware\u003CLogEnrichmentMiddleware>();\n\n\u002F\u002F Serilog LogContext — same concept, uses Serilog's enrichment mechanism:\n\u002F\u002F (requires Enrich.FromLogContext() in LoggerConfiguration)\nusing (LogContext.PushProperty(\"UserId\",   userId))\nusing (LogContext.PushProperty(\"TenantId\", tenantId))\n{\n    await _next(ctx);\n}\n\n\u002F\u002F Result — every log entry inside the request automatically includes:\n\u002F\u002F { \"UserId\": \"u-123\", \"TenantId\": \"acme\", \"TraceId\": \"4bf92f3577b34da6...\" }\n```\n\n**Rule of thumb:** Push user ID, tenant ID, and correlation\u002Ftrace ID into the\nlog scope in a single middleware. This makes filtering logs by user or tenant in\nSeq or Kibana a one-field query instead of a grep across unstructured strings.\n",{"id":77,"difficulty":14,"q":78,"a":79},"correlation-id-middleware","How do you propagate a correlation ID across service boundaries in .NET?","A **correlation ID** links all log entries for one logical operation across\nmultiple services. The originating service generates the ID and passes it in\nan HTTP header; downstream services read it, log it, and forward it to their\nown dependencies.\n\n```csharp\n\u002F\u002F Middleware — read or generate a correlation ID for every request:\npublic class CorrelationIdMiddleware\n{\n    private const string HeaderName = \"X-Correlation-Id\";\n    private readonly RequestDelegate _next;\n\n    public CorrelationIdMiddleware(RequestDelegate next) => _next = next;\n\n    public async Task InvokeAsync(HttpContext ctx, ILogger\u003CCorrelationIdMiddleware> logger)\n    {\n        \u002F\u002F Accept an inbound ID (from upstream caller) or generate a new one:\n        var correlationId = ctx.Request.Headers[HeaderName].FirstOrDefault()\n                            ?? Activity.Current?.TraceId.ToString()\n                            ?? Guid.NewGuid().ToString(\"N\");\n\n        \u002F\u002F Echo the ID back in the response header:\n        ctx.Response.Headers[HeaderName] = correlationId;\n\n        \u002F\u002F Push into log scope so every log in this request carries it:\n        using (logger.BeginScope(new Dictionary\u003Cstring, object>\n                   { [\"CorrelationId\"] = correlationId }))\n        {\n            await _next(ctx);\n        }\n    }\n}\n\n\u002F\u002F Forward the correlation ID on outbound HTTP calls (HttpClientFactory):\nbuilder.Services.AddHttpClient(\"downstream\")\n    .AddHttpMessageHandler\u003CCorrelationIdDelegatingHandler>();\n\npublic class CorrelationIdDelegatingHandler : DelegatingHandler\n{\n    private const string HeaderName = \"X-Correlation-Id\";\n\n    protected override Task\u003CHttpResponseMessage> SendAsync(\n        HttpRequestMessage request, CancellationToken ct)\n    {\n        \u002F\u002F Propagate the current trace ID as the correlation header:\n        var traceId = Activity.Current?.TraceId.ToString();\n        if (traceId is not null)\n            request.Headers.TryAddWithoutValidation(HeaderName, traceId);\n\n        return base.SendAsync(request, ct);\n    }\n}\n```\n\nNote: if OpenTelemetry is configured, the W3C `traceparent` header propagates\nthe trace ID automatically via `AddHttpClientInstrumentation()`. A manual\n`X-Correlation-Id` header is still useful for non-OpenTelemetry consumers\n(front-end apps, third-party APIs) that do not understand `traceparent`.\n\n**Rule of thumb:** Use the W3C `traceparent` header as your correlation ID\nwhen all services use OpenTelemetry. Add `X-Correlation-Id` as an alias for\nclients and logs that need a human-readable identifier in dashboards.\n",{"id":81,"difficulty":49,"q":82,"a":83},"dotnet-monitor","What is dotnet-monitor and how does it help diagnose production issues?","**dotnet-monitor** is a sidecar tool that exposes diagnostic APIs (traces,\ndumps, metrics, logs) over HTTP without modifying the application or attaching\na debugger. It is the recommended way to capture diagnostics from containerized\n.NET workloads in production.\n\n```bash\n# Run dotnet-monitor as a sidecar (Docker Compose example):\n# dotnet-monitor listens on localhost:52323 (diagnostic) and 52325 (metrics)\n# and connects to the application via the .NET diagnostic pipe.\n\n# docker-compose.yml snippet:\n# services:\n#   app:\n#     image: myapp:latest\n#   monitor:\n#     image: mcr.microsoft.com\u002Fdotnet\u002Fmonitor:8\n#     environment:\n#       - DOTNETMONITOR_DiagnosticPort__ConnectionMode=Listen\n#       - DOTNETMONITOR_Storage__DumpTempFolder=\u002Ftmp\n#     volumes:\n#       - \u002Ftmp:\u002Ftmp\n#     command: [\"collect\", \"--no-auth\"]  # Note: add auth in production\n\n# Capture a CPU trace via the REST API:\ncurl -X POST http:\u002F\u002Flocalhost:52323\u002Ftrace \\\n    -H \"Content-Type: application\u002Fjson\" \\\n    -d '{\"profile\": \"CpuSampling\", \"durationSeconds\": 30}' \\\n    --output trace.nettrace\n\n# Capture a memory dump:\ncurl -X POST http:\u002F\u002Flocalhost:52323\u002Fdump?type=Full \\\n    --output app.dmp\n\n# Live metrics stream (Prometheus-compatible scrape):\n# GET http:\u002F\u002Flocalhost:52325\u002Fmetrics\n```\n\n```json\n\u002F\u002F Trigger-based collection — automatically capture a dump when CPU exceeds 80%:\n\u002F\u002F (collectionrules.json mounted into the monitor container)\n{\n  \"CollectionRules\": {\n    \"HighCpuDump\": {\n      \"Trigger\": { \"Type\": \"EventCounter\",\n        \"Settings\": { \"ProviderName\": \"System.Runtime\",\n          \"CounterName\": \"cpu-usage\", \"GreaterThan\": 80 } },\n      \"Actions\": [\n        { \"Type\": \"CollectDump\", \"Settings\": { \"Type\": \"Heap\",\n          \"Egress\": \"AzureBlobStorage\" } }\n      ],\n      \"Limits\": { \"ActionCount\": 2, \"ActionCountSlidingWindowDuration\": \"01:00:00\" }\n    }\n  }\n}\n```\n\n**Rule of thumb:** Deploy dotnet-monitor as a sidecar in every Kubernetes pod\nrunning a .NET service. It gives you on-demand traces and dumps without\nredeploying the application or breaching the container boundary.\n",15,null,{"description":11},"Logging and monitoring interview questions — ILogger, structured logging, Serilog, log scopes, health checks, OpenTelemetry metrics, and distributed tracing.","dotnet\u002Fperformance-deployment\u002Flogging-monitoring","Logging & Monitoring","Performance & Deployment","performance-deployment","2026-06-23","EB_8n88YuCvlmWZviVj9Wokjt8SlSzSqOUpv8KJ73YQ",[95,99,100],{"subtopic":96,"path":97,"order":98},"Caching","\u002Fdotnet\u002Fperformance-deployment\u002Fcaching",1,{"subtopic":89,"path":20,"order":12},{"subtopic":101,"path":102,"order":103},"Deployment","\u002Fdotnet\u002Fperformance-deployment\u002Fdeployment",3,{"path":105,"title":106},"\u002Fblog\u002Fdotnet-logging-monitoring","Structured Logging and Monitoring in ASP.NET Core",1782244120115]