Skip to main content

Configuration

All options are set via AddLookout(options => { ... }) in Program.cs. Every option has a sane default — you only touch what you need.

builder.Services.AddLookout(options =>
{
options.MaxAgeHours = 24; // keep entries for 24 hours (default)
options.MaxEntryCount = 50_000; // hard cap (default)
});

General

PropertyTypeDefaultDescription
AllowInEnvironmentsIList<string>["Development"]Environments where Lookout is allowed to run. Lookout throws LookoutEnvironmentException on startup in any other environment.
AllowInProductionboolfalseWhen true, allows Lookout to run in any environment including Production. Logs a startup warning. Prefer AllowInEnvironments for named non-Production environments.
AllowNonLoopbackboolfalseSuppresses the startup warning when the app is bound to a non-loopback address.
StoragePathstring%LocalAppData%/Lookout/<EntryAssemblyName>/lookout.dbPath to the SQLite database file. Scoped by entry assembly so multiple apps on the same machine don't share a database.
MaxAgeHoursint24Entries older than this many hours are pruned by the background retention service.
MaxEntryCountint50_000Secondary retention cap. When exceeded after time-based pruning, oldest entries are removed first.
ChannelCapacityint10_000Capacity of the in-memory channel between the request path and the background flusher. When full, the oldest entry is dropped.
FlushBatchSizeint50Maximum entries written to SQLite in a single transaction per flusher cycle. Higher = lower transaction overhead; lower = lower per-write latency.
UserClaimTypestring?nullClaim type used to resolve the authenticated user name. When null, HttpContext.User.Identity?.Name is used.

HTTP request capture

PropertyTypeDefaultDescription
CaptureRequestBodybooltrueCaptures inbound request bodies (content-type gated, size-capped). Set to false to skip buffering.
CaptureResponseBodybooltrueCaptures HTTP response bodies. Same gating as request body.
MaxBodyBytesint65_536 (64 KiB)Maximum bytes captured per request or response body. Larger bodies are truncated with a marker.
SkipPathsHashSet<string>/healthz, /health, /ready, /favicon.icoPaths skipped entirely by the HTTP capture middleware. Case-insensitive. /lookout is always skipped.
SkipStaticAssetExtensionsHashSet<string>.js, .css, .map, .woff, .woff2, .ttf, .otf, .eot, .svg, .png, .jpg, .jpeg, .gif, .webp, .icoFile extensions skipped entirely by the HTTP capture middleware. Prevents UseStaticFiles() traffic from drowning real API requests. Clear the set to capture asset requests.
CapturedContentTypesHashSet<string>application/json, application/x-www-form-urlencoded, text/*Content types eligible for body capture. Supports text/* wildcard.

EF Core (options.Ef)

PropertyTypeDefaultDescription
CaptureParameterValuesbooltrueCaptures actual SQL parameter values (after redaction).
CaptureParameterTypesOnlyboolfalseCaptures only parameter type names — never values. Overrides CaptureParameterValues.
MaxStackFramesint20Maximum user-code frames captured per query.
N1DetectionMinOccurrencesint3Minimum identical-shaped queries within one request before flagging as N+1.
N1IgnorePatternsIList<Regex>[]SQL shape keys matching any of these patterns are excluded from N+1 detection.
options.Ef.N1DetectionMinOccurrences = 5;  // only flag 5+ repeated queries
options.Ef.N1IgnorePatterns.Add(new Regex("SELECT.*__EFMigrationsHistory"));

Outbound HTTP (options.Http)

PropertyTypeDefaultDescription
CaptureOutboundbooltrueMaster switch for outbound HttpClient capture.
CaptureOutboundRequestBodybooltrueCaptures outbound request bodies (content-type gated).
CaptureOutboundResponseBodybooltrueCaptures outbound response bodies (content-type gated).
OutboundBodyMaxBytesint65_536 (64 KiB)Maximum bytes captured per outbound body.

Cache (options.Cache)

Registration order

AddLookout() decorates IMemoryCache and IDistributedCache by wrapping whichever implementations are already registered at call time. Call AddMemoryCache() / AddDistributedMemoryCache() (or equivalent) before AddLookout(), otherwise the decorator finds nothing to wrap and cache capture is silently skipped.

If you use a framework that registers caches inside modules (ABP, Orchard Core, etc.), move AddLookout() to after those module registrations complete. See Troubleshooting for details.

PropertyTypeDefaultDescription
CaptureMemoryCachebooltrueCaptures IMemoryCache get/set/remove operations.
CaptureDistributedCachebooltrueCaptures IDistributedCache get/set/refresh/remove operations.
SensitiveKeyPatternsIList<Regex>[]Cache keys matching these patterns are replaced with *** before storage.

Logging (options.Logging)

PropertyTypeDefaultDescription
CapturebooltrueMaster switch for log capture.
MinimumLevelLogLevelInformationMinimum log level captured. Raise to Warning to reduce noise.
IgnoreCategoriesIList<string>Microsoft.*, System.*Logger category patterns dropped before recording. Trailing * is a prefix wildcard.
MaxScopeFramesint5Maximum active scope frames captured per log entry.
options.Logging.MinimumLevel = LogLevel.Warning;
options.Logging.IgnoreCategories.Add("Hangfire.*");

Exceptions (options.Exceptions)

PropertyTypeDefaultDescription
CapturebooltrueMaster switch for exception capture.
IgnoreExceptionTypesIList<string>TaskCanceledException, OperationCanceledExceptionFull CLR type names dropped before recording. Hot-reload cancellation is excluded by default.
MaxStackFramesint20Maximum user-code frames captured per exception.

Dump (options.Dump)

PropertyTypeDefaultDescription
CapturebooltrueMaster switch for Lookout.Dump() entries.
MaxBytesint65_536Maximum characters serialised per dump entry before truncation.

Hangfire (options.Hangfire)

Requires the Lookout.Hangfire package.

PropertyTypeDefaultDescription
CapturebooltrueMaster switch for Hangfire job capture.
CaptureArgumentsbooltrueSerialises job argument values before recording.
ArgumentMaxBytesint8_192Maximum bytes per job argument value before truncation.
IgnoreJobTypesIList<string>[]Full CLR type names of job classes to skip (e.g. chatty heartbeat jobs).
options.Hangfire.IgnoreJobTypes.Add("MyApp.Jobs.HeartbeatJob");

Redaction (options.Redaction)

Lookout redacts sensitive values before writing to storage. Matched values are replaced with ***.

PropertyTypeDefault members
HeadersHashSet<string>Authorization, Cookie, Set-Cookie, X-Api-Key
QueryParamsHashSet<string>password, token, access_token, refresh_token, secret, api_key, apikey
FormFieldsHashSet<string>same as QueryParams
JsonFieldsHashSet<string>same as QueryParams — applied recursively to captured JSON bodies
SqlParametersHashSet<string>same as QueryParams

Add your own:

options.Redaction.Headers.Add("X-Internal-Token");
options.Redaction.JsonFields.Add("creditCardNumber");

For custom redaction logic, use the Redact callback — it runs after built-in redactors:

options.Redact = entry =>
{
// Scrub a custom field from JSON body captures
if (entry.Type == "http" && entry.Content.Contains("loyaltyCardNumber"))
{
var scrubbed = Regex.Replace(entry.Content, @"""loyaltyCardNumber""\s*:\s*""[^""]*""", @"""loyaltyCardNumber"":""***""");
return entry with { Content = scrubbed };
}
return entry;
};

Filtering and tagging

Drop entries before enqueue

// Per-entry filter: runs synchronously on the request path — keep it fast
options.Filter = entry => entry.Type != "log"; // drop all log entries

Drop entries in the background flusher

// Batch filter: runs off the request path, may be slower
options.FilterBatch = entries =>
entries.Where(e => e.DurationMs > 5).ToList();

Attach custom tags

options.Tag = (entry, tags) =>
{
tags["tenant"] = GetCurrentTenantId();
};

Tags appear as filterable chips in the dashboard.