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::toDiagnostic()returns the diagnostic for the supplied exception only.DiagnosticHelper::toDocument()builds the root diagnostic followed by every preserved cause.
/// `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.
TextDocument::toString()orPlainTextRendererproduces portable text for logs, files, tests, and redirected output.TerminalDocumentRendererapplies the application’s terminal style and retains visual hierarchy for interactive output.
/// 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.