Debugging

The {@} debug tag emits diagnostic content during template rendering without affecting the merged output. It routes through System.Diagnostics.Trace, so the destination is whichever trace listeners the host process has registered — by default, the Visual Studio / VS Code Output window.

Basic syntax

{@ expression }

The expression follows the same rules as any other tag — bind expression, literal, declared variable, $ reference, or lambda call. The resolved value is written to all registered trace listeners.

Example

Data:
var data = new
{
    FirstName = "John",
    LastName = "Doe",
    FavoriteColors = new[] { "Blue", "Red", "Green" }
};
Template:
Hello {FirstName} {LastName},

Here is a list of your favorite colors:
{@ 'starting #each block for colors' }
{#each FavoriteColors}
{@ $ }
- {$}
{/each}
{@ 'completed #each block for colors' }
Rendered output:
Hello John Doe,

Here is a list of your favorite colors:
- Blue
- Red
- Green
Output window result:
starting #each block for colors
Blue
Red
Green
completed #each block for colors

Allowed expression forms

{@} accepts the same value forms as a lambda argument:

  • String literals (single or double quoted) — {@ 'note' }
  • Numeric literals — {@ 42 }
  • Boolean literals — {@ true }
  • Bound expressions — {@ Address.City }
  • $ and scope-walk expressions — {@ $ }, {@ ..\Title }
  • Declared variables — {@ :counter }
  • Lambda return values — {@ (Items) => summary }

String literal quoting follows the same rules as lambda arguments.

Redirecting trace output

By default, System.Diagnostics.Trace.Listeners contains a DefaultTraceListener that writes to the IDE Output window during a debug session. To route debug output elsewhere — a log file, a console, a structured logging sink — register a custom TraceListener:

Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
// or
Trace.Listeners.Add(new TextWriterTraceListener("debug.log"));

See Microsoft’s TraceListeners documentation for the full list of built-in listeners and the contract for custom ones.

In production builds where TRACE is not defined, calls to Trace.WriteLine are compiled out — debug tags become no-ops without needing to be removed from templates. This makes {@} safe to leave in templates that ship to production.

When to use it

  • Render-time inspection — see exactly what value a bind expression is producing, in context, without writing temporary output to the template.
  • Loop tracing — emit the current $ on each iteration to confirm an {#each} is seeing the items you expect.
  • Variable observation — print a variable’s running value at key points in the template.
  • Lambda verification — call a lambda from inside {@} to log its return value without rendering it.

Whitespace

{@} is a non-simple tag, so it accepts - trim markers ({-@ … -}) and is affected by engine-level TrimWhitespace. See Whitespace Control.

Next steps

  • Comments{!} is the static counterpart: notes for template authors that never reach the trace stream or output
  • Exception Handling — when something throws mid-merge, the exception context tells you exactly where to insert a {@} to investigate
  • Lambda Expressions — debug tags pair well with lambdas that format or summarize complex values