Working with Diagnostic Data

Diagnostics turn exceptions into structured, user-facing information. This page explains how to inspect a diagnostic, build a complete document from an exception and its causes, and render that document for logs or an interactive terminal.

One Exception, One Diagnostic

Diagnostic describes one error. It can expose a source name, source path, and CodeLocation, and it converts its domain-specific details into a neutral TextDocument.

Call Exception::diagnostic() when you need the diagnostic for the current exception only. The base implementation creates a compact diagnostic from toString(); richer domain errors override it to preserve structured fields.

/// `diagnostic()` returns the structured description for one exception.
/// Its source accessors let handlers inspect optional metadata without knowing the concrete exception type.
void inspectDiagnostic() {
    auto context = el::ApplicationErrorContext{"The score could not be read."_el};
    context.setSourceName("夜の演奏"_el)
        .setSourcePath("scores/夜の演奏.music"_el)
        .setCodeLocation(el::CodeLocation{el::LineIndex{3}, el::ColumnIndex{5}});
    const auto diagnostic = el::ApplicationError{std::move(context), {}}.diagnostic();
    const auto location = diagnostic->location();

    el::io::printLine("Source: "_el, diagnostic->sourceName());
    el::io::printLine("Path: "_el, diagnostic->sourcePath());
    el::io::printLine(
        "Location: "_el, location.line().toSizeT() + 1, ":"_el, location.column().toSizeT() + 1);
}
Source: 夜の演奏
Path: scores/夜の演奏.music
Location: 4:6

All source metadata is optional. Check the returned values before displaying them in custom interfaces, and remember that line and column indices are zero-based values even though the standard diagnostic renderers display them as one-based positions.

Build a Complete Diagnostic Document

Most reporting boundaries need more than one diagnostic. DiagnosticHelper normalizes a std::exception and provides two conversions:

/// `DiagnosticHelper` converts an exception into one diagnostic or a complete document containing every cause.
/// Use `toDocument()` at a reporting boundary so valuable information from translated failures is not lost.
void buildDiagnosticDocument() {
    try {
        try {
            throw std::runtime_error{"MIDI input stopped responding"};
        } catch (...) {
            throw el::RuntimeError{"The digital-piano input could not be read."_el, std::current_exception()};
        }
    } catch (const el::Exception &error) {
        auto document = el::DiagnosticHelper{error}.toDocument();
        el::io::printLine(document.toString());
    }
}
Error: The digital-piano input could not be read.
Caused By:
  MIDI input stopped responding

Use the static DiagnosticHelper::documentFromError() when you already have a std::exception_ptr instead of a caught reference. Foreign standard exceptions become escaped fallback diagnostics, and unknown exceptions receive a neutral message.

Render the Document for Its Destination

A diagnostic document contains semantic nodes rather than terminal control sequences or preformatted log text. Choose the renderer at the final destination:

When you use Application::run(), this reporting pipeline is already in place: Application builds the complete diagnostic document, including causes, and renders it through its system output. Render a document manually only when you need a custom reporting boundary, another destination, a preview, or code that does not use Application.

/// Diagnostic documents are renderer-neutral `TextDocument` trees.
/// The same document can become plain text for logs or styled terminal output for an interactive application.
void renderDiagnosticDocument() {
    auto context = el::ApplicationErrorContext{
        "The score could not be read."_el,
        "The tempo-marking value is out of range."_el,
    };
    context.setSourcePath("scores/朝の合奏.music"_el)
        .setCodeLocation(el::CodeLocation{el::LineIndex{6}, el::ColumnIndex{14}});
    const auto error = el::ApplicationError{std::move(context), {}};
    const auto document = el::DiagnosticHelper{error}.toDocument();

    el::io::printLine("--- Plain text ---"_el);
    el::io::printLine(document.toString());
    el::io::printLine("--- Terminal document ---"_el);
    const auto renderer = el::cterm::TerminalDocumentRenderer{el::application().systemOutputStyle()};
    renderer.renderTo(*el::application().terminal(), document);
}
--- Plain text ---
Error: The score could not be read.
  The tempo-marking value is out of range.
Error Source:
  Path:   scores/朝の合奏.music
  Line:   7
  Column: 15
--- Terminal document ---

  The score could not be read.

  The tempo-marking value is out of range.

Error Source:
  Path:   scores/朝の合奏.music
  Line:   7
  Column: 15

Diagnostic::toString() is a convenient plain-text rendering of one diagnostic. It is not a substitute for DiagnosticHelper::toDocument() when an exception may have causes.

Display Text and Localization

Diagnostic conversion accepts an optional DisplayTextMap. It supplies structural wording such as section labels and cause headings. A null pointer selects the library’s English defaults; Application passes its configured display-text map automatically.

Domain-authored titles and descriptions are kept separate from this structural wording. Translate application text in the application, and use a display-text map to adapt reusable diagnostic labels. This keeps domain context intact while allowing the same semantic document to be rendered for different audiences.

Read Writing Custom Exceptions to create diagnostics for your own domain without coupling the exception itself to a particular renderer.