Skip to content

5.20 stacktrace — a Java stack trace as a keyed source

A stacktrace producer reads the Java stack trace in a piece of text, when a query reaches it. The key IS the text — a trace a user pasted, or a log event’s content (§5.20.1) — so a literal-seeded anchor carries it:

- { name: traceFrames, kind: stacktrace, emit: frames, jars: [jars] }
- { name: traceExceptions, kind: stacktrace, emit: exceptions, jars: [jars] }
MATCH (t:StackTrace {text: $trace})-[:HAS_FRAME]->(f:StackFrame {firstInJar: true})-[:DECLARED_AT]->(d:JavaDeclaration)
RETURN f.exception, f.className, f.method, f.line, d.text

The contract:

  • The first trace in the text is read. A header (pkg.SomeException: message, optionally after Exception in thread "…") counts only when frames follow it; text before it (a log line’s own message) is not part of the trace, and a wrapped message is kept whole. Each Caused by: and Suppressed: is a throwable of its own, in order, with its frames and the count of ... n more frames it shares with the one before (elided). Module and class-loader prefixes (java.base/, app//) are not part of the class; a logging layout’s packaging suffix (~[lib.jar:1.0]) is kept as location. Text with no trace answers nothing, with a PRODUCER_ERROR warning saying so.
  • Nesting is kept. A suppressed throwable’s own causes belong to it, not to the one thrown: depth is 0 on the chain from the thrown throwable to its root cause and one more inside each suppressed throwable, and parentIndex names the throwable each one caused or was suppressed by. isRootCause is the end of the depth-0 chain.
  • exceptions returns a row per throwable: index, relation (thrown, cause, suppressed), depth, parentIndex, exception, simpleName, message, frameCount, elided, isRootCause, topFrame, and the first frame of it in a jar — jarFrame, jarFrameLine, jarPath, sourceKey, spanKey, methodAtKey. frames returns a row per frame: exceptionIndex, relation, exception, frame (its position), isTop, firstInJar, className, packageName, method, file, line, module, native, location, text, sourcePath, and expectedName — the name its line should show (the method; the class’s simple name for a constructor; the enclosing method for a lambda body). Every row carries traceId (the same text, the same id) and anchorKey.
  • A frame finds its own jar. Each frame’s class is looked up in the jars under jars (each a jar or a folder of jars, nested folders included; -sources.jar and -javadoc.jar are not code), resolved as a jar path is: the world’s data/ tree, an operator-trusted root, the realm’s own directory. The first root, then path order, wins; jarCandidates says how many jars had the class. A frame a jar compiled carries jarPath, inJar: true, javaTypeId, sourceKey and — when it names a file and a line — spanKey and methodAtKey (the key of the jar’s by: at: the method the line was in, §5.18), spelled exactly as that jar’s own rows spell them (§5.18), so it joins to the jar’s types and the spans and declarations of its source (§5.19) with no translation. A frame no jar compiled (the JDK, a library not mounted, a generated proxy) is still a row, with inJar: false and none of those keys. A jar root that cannot be found is a PRODUCER_ERROR warning.
  • Whether the source is the jar’s version is answered by the declaration around a frame’s line (§5.19 declarations for its spanKey): matches compares that declaration’s name with the frame’s expectedName. A span’s matches looks for the name within context lines, and a frame’s line sits inside a method’s body, so it can read false on the right source.

Cost: a key is parsed in memory, and a jar’s class list is read once and kept until the jar changes. A text larger than maxTraceKb (default 256) is refused with a warning, never half-read.

5.20.1 Log events — files with unit: event

Section titled “5.20.1 Log events — files with unit: event”

A files producer with unit: event returns one row per LOG EVENT — from a line that starts one to the next — so a logged exception is one row however many lines its trace takes:

- { name: logEvents, kind: files, roots: [logs], include: ["**/*.log"], unit: event, contentCapKb: 8192 }
  • Where an event starts. At a line containing a match of eventStart, when declared. Otherwise the file decides: lines beginning with a date or time when it has any; else lines beginning with a level word; else every line that is not a trace’s continuation (indented, Caused by:, Suppressed:, ... n more).
  • What a row says. The file’s fields, plus line and endLine, content (the event’s text), timestamp, level, severity (the level on one scale: SEVERE is ERROR, WARNING is WARN, FINE… is DEBUG, CONFIG is INFO), thread, logger, message; and from the Java stack trace in it, read as §5.20 reads one, hasTrace, exception, exceptionMessage, causes, rootCause, frameCount and fingerprint — the same thrown and root exceptions at the same frames have the same fingerprint, so a failure can be counted across events. A field the layout does not show is null.
  • Layouts. eventPattern, when declared, is a regex over an event’s first line (or first two, joined by a newline) whose named groups timestamp, level, thread, logger and message fill the row; another group name is refused when the realm loads. Undeclared, the Spring Boot, Logback/Log4j, time-level-logger, timestamp-then-level, java.util.logging and level-first layouts are tried in turn.
  • Matching. Under emit: search or a content CONTAINS filter, the term is judged against the whole event. A file is read up to contentCapKb; events past it are not returned.