Diagnostics

Tokenizer's diagnostics system records every matching decision, generates contextual hints for failures, and renders visual diffs between templates and input. Use it to understand why a pattern isn't matching.

Enabling Diagnostics

Diagnostics are disabled by default for performance. Enable them via options:

C#
var tokenizer = new Tokenizer(new TokenizerOptions
{
    EnableDiagnostics = true
});

var result = tokenizer.Tokenize(template, input);

if (result.Diagnostics is not null)
{
    Console.WriteLine(result.Diagnostics.Verdict);
    Console.WriteLine(result.Diagnostics.RenderAlignment());

    foreach (var token in result.Diagnostics.Tokens)
    {
        Console.WriteLine($"{token.TokenName}: {token.Outcome}");
        foreach (var issue in token.Issues)
        {
            Console.WriteLine($"  {issue.Type}: {issue.Description}");
            if (issue.Hint is not null)
                Console.WriteLine($"    Hint: {issue.Hint}");
        }
    }
}

Or via DI:

C#
services.AddTokenizer(new TokenizerOptions { EnableDiagnostics = true });

TokenizationDiagnostics

When diagnostics are enabled, result.Diagnostics contains:

PropertyTypeDescription
TokensIReadOnlyList<TokenDiagnostic>Per-token diagnostic narratives
VerdictstringHuman-readable summary, e.g. "Matched 3 of 5 tokens (2 missed)."
MatchedCountintNumber of matched tokens
MissedCountintNumber of missed tokens
TotalCountintTotal token count
RawEventsIReadOnlyList<TokenizationEvent>Full event trace of every matching decision
RenderAlignment()stringVisual alignment diff between template and input (cached)
RenderProcessingOrder()stringChronological engine decision walkthrough (cached)

TokenDiagnostic

Each token in the template gets a TokenDiagnostic record describing what happened during matching:

PropertyTypeDescription
TokenNamestringToken name from the template
TokenIdintInternal token identifier
OutcomeTokenOutcomeMatched, Rejected, NeverFound, or Blocked
AttemptsIReadOnlyList<TokenAttempt>Each match attempt with outcome and reason
AssignedValuestring?Final assigned value (if matched)
AssignedLocationFileLocation?Location in input where value was found
BlockedBystring?Name of the required token that blocked this one
IssuesIReadOnlyList<DiagnosticIssue>Issues with contextual hints

TokenAttempt

Each attempt to assign a value to a token is recorded:

PropertyTypeDescription
LocationFileLocation?Position in the input
Valuestring?Attempted value
OutcomeAttemptOutcomeAssigned, ValidatorRejected, TransformerFailed, or Backtracked
DecoratorNamestring?Decorator that caused rejection or failure
Reasonstring?Human-readable explanation

Alignment Rendering

The alignment renderer produces a visual diff showing exactly how the template mapped (or failed to map) to the input. Call result.Diagnostics.RenderAlignment():

C#
var tokenizer = new Tokenizer(new TokenizerOptions { EnableDiagnostics = true });
var compiled = tokenizer.Compile("Name: {Name}, Email: {Email}");
var result = tokenizer.Tokenize(compiled.Template, "Name: Alice, Email: alice@example.com");

Console.WriteLine(result.Diagnostics?.RenderAlignment());

Try the playground with a broken pattern to see alignment in action:

Processing Order

The processing order renderer produces a chronological walkthrough of every engine decision — which tokens were attempted, in what order, and why each succeeded or failed. Call result.Diagnostics.RenderProcessingOrder():

C#
var tokenizer = new Tokenizer(new TokenizerOptions { EnableDiagnostics = true });
var compiled = tokenizer.Compile("Name: {Name}, Email: {Email}");
var result = tokenizer.Tokenize(compiled.Template, "Name: Alice, Email: alice@example.com");

Console.WriteLine(result.Diagnostics?.RenderProcessingOrder());

Hint Generators

The diagnostics system includes nine hint generators that provide contextual suggestions when tokens fail to match:

GeneratorDetectsExample Hint
PreambleNearMiss Preamble text that almost matches (wrong case, substring match) "Preamble 'Email:' not found — did you mean 'email:'?"
DateFormat DateTime parsing failures with format suggestions "Value '25/12/2025' doesn't match format 'yyyy-MM-dd'. Try 'dd/MM/yyyy'."
ValidatorValue Why a specific validator rejected a value "IsInRange(1,100) failed: value '200' is outside range."
RepeatingToken Repeating token behavior that may be unexpected "Token 'Items' matched 3 times. Use {Items*} for repeating tokens."
BlockedToken Token blocked because a prior required token failed "Token 'Email' was blocked because required token 'Name' failed."
ChainedDecorator Failure partway through a decorator chain "Transformer 'ToDate' failed after 'Trim' succeeded — check input format."
MultipleRejection Value rejected by multiple validators "Value rejected by 2 validators: IsEmail, HasLength."
OptionalToken Optional token not found (informational, not an error) "Optional token 'Title' was not found — this is not an error."
ValueMismatch Greedy capture consumed too much input "Token 'Name' captured 'Alice, Age: 30' — the next preamble may be missing."

Using Diagnostics in Your Code

A practical pattern for debugging tokenization in your application:

C#
var result = tokenizer.Tokenize(template, input);

if (!result.Success)
{
    // Log the alignment for visual debugging
    logger.LogWarning("Tokenization failed:\n{Alignment}",
        result.Diagnostics?.RenderAlignment());

    // Log per-token diagnostics
    if (result.Diagnostics is not null)
    {
        foreach (var token in result.Diagnostics.Tokens)
        {
            if (token.Issues.Count == 0) continue;

            foreach (var issue in token.Issues)
            {
                logger.LogWarning("[{Token}] {Type}: {Description}. {Hint}",
                    token.TokenName,
                    issue.Type,
                    issue.Description,
                    issue.Hint ?? "");
            }
        }
    }
}

For production, consider enabling diagnostics only when a tokenization fails, by re-running with diagnostics on:

C#
var result = tokenizer.Tokenize(template, input);

if (!result.Success)
{
    // Re-run with diagnostics for the log
    var diagTokenizer = new Tokenizer(new TokenizerOptions { EnableDiagnostics = true });
    var diagResult = diagTokenizer.Tokenize(template, input);
    logger.LogWarning("Diagnostics:\n{Alignment}", diagResult.Diagnostics?.RenderAlignment());
}