Skip to content

Add DebuggerTypeProxy for InMemorySink to simplify debugging#49

Open
vfofanov wants to merge 1 commit intoserilog-contrib:mainfrom
vfofanov:feature/improve_debug
Open

Add DebuggerTypeProxy for InMemorySink to simplify debugging#49
vfofanov wants to merge 1 commit intoserilog-contrib:mainfrom
vfofanov:feature/improve_debug

Conversation

@vfofanov
Copy link
Copy Markdown

Improve debug experience for InMemorySink

Summary

This PR improves debugging of InMemorySink in IDE (and other debuggers) by adding a debugger type proxy that shows log events in a readable, expanded form. It also refines InMemorySink internals (cached read-only view of events, nullable, simplified Instance getter) and adds a predicate overload for Snapshot() to create filtered snapshots.

Changes

Debugger type proxy

  • [DebuggerTypeProxy(typeof(InMemorySinkAdvDebugProxy))] on InMemorySink so that when you inspect an InMemorySink instance in the debugger, the value is displayed via the proxy instead of the raw type.
  • InMemorySinkAdvDebugProxy – wraps the sink and exposes its log events as a List<DebugLogEvent> so the debugger shows a clear, expandable list of entries.
  • DebugLogEvent – a debug-time view of a single log event with:
    • LevelLogEventLevel
    • Message – rendered message (logEvent.RenderMessage())
    • MessageTemplate – template string
    • Properties – log event properties
    • Exception – exception if present
    • OriginalLogEvent – underlying LogEvent for deep inspection
    • ToString() – returns the rendered message for quick scanning in the Locals/Watch window

This makes it easier to inspect what was logged without expanding raw LogEvent and Serilog internals.

InMemorySink behavior and API

  • Cached read-only viewLogEvents is backed by a cached ReadOnlyCollection<LogEvent> (_logEventsSnapshot) that is invalidated whenever Emit is called, so repeated enumeration of LogEvents does not allocate and stays consistent within a “no new emit” window.
  • Snapshot(Func<LogEvent, bool> predicate) – new overload that returns an InMemorySink snapshot containing only log events that match the given predicate (e.g. filter by level, message template, or property).
  • Nullable#nullable enable and use of ReadOnlyCollection<LogEvent>? for the cached snapshot.
  • Instance getter – simplified to LocalInstance.Value ??= new InMemorySink().
  • Snapshot() – parameterless Snapshot() now builds the snapshot from GetLogEvents().ToList() (using the cached read-only view when valid).

Benefits

  • Easier debugging – In the debugger, expanding an InMemorySink shows a list of DebugLogEvent with Level, Message, MessageTemplate, Properties, and Exception, instead of raw collections and Serilog types.
  • Filtered snapshotsSnapshot(predicate) supports assertions or inspection over a subset of events (e.g. only errors, or only a given message template) without manual filtering in test code.
  • Stable enumeration – Cached read-only snapshot for LogEvents avoids repeated allocation and gives a consistent view when no new events are emitted.

Checklist

  • Solution builds with no errors or warnings.
  • All tests pass.
  • No breaking API changes for existing LogEvents or parameterless Snapshot() usage; new Snapshot(predicate) is additive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant