Skip to main content

Observability

Every statement the driver sends — reads, writes, counts, pivot loads, streams, schema statements and em.run() — passes through a single method, Neo4jConnection.executeRaw, which wraps the call in core's Connection.executeQuery. That is what makes the ORM's own logging configuration work here with no driver-specific setup.

If you already run with debug enabled

Only the streaming path used to log, so debug: ['query'] printed nothing for find or flush. Expect output you were not seeing before.

Logging​

const orm = await MikroORM.init({
debug: ['query', 'query-params'],
slowQueryThreshold: 200,
highlighter: new Neo4jHighlighter(),
});
OptionEffect
debug: trueEverything, including discovery and schema work.
debug: ['query']The Cypher only.
debug: ['query-params']Adds the parameter map to each statement.
debug: ['schema']What ensureIndexes() runs. Schema statements are reported under this namespace, not query.
slowQueryThresholdStatements at or over this many milliseconds are additionally reported under slow-query — regardless of whether query is enabled.
slowQueryLoggerFactoryWhere those go; useful for shipping them somewhere other than the console.
loggerFactoryA custom logger for everything.
highlighterColourises the Cypher. See below.

Each entry carries the elapsed time, the number of rows returned, the number of entities affected, and which connection it ran on. A statement that fails is logged at error level before the exception is converted and thrown, so a failing query is visible in the log with its timing rather than only as a stack trace.

Per query:

await em.find(Book, {}, { logging: { label: 'catalogue page' } }); // labels the entries
await em.find(Book, {}, { logging: { enabled: false } }); // silences just this one
await em.find(Book, {}, { loggerContext: { requestId } }); // reaches a custom logger

Transaction boundaries are logged as begin, commit and rollback, carrying the same context — a query log without them cannot be read back as a sequence.

A note on parameters​

Cypher parameters are named and travel to the server as a separate map, so query-params appends that map to the statement rather than inlining values into it (which is what the SQL drivers do). The values shown are the ones you passed, not their Bolt encodings — an Integer { low, high } in a log line helps nobody.

The gate reads the query-params namespace only. A per-query logging: { enabled: true } means "log this one", not "and dump its parameters".

affectedRows​

Neo4j has no rows, so "affected" counts entities created or removed — nodes and relationships. Properties set are deliberately excluded: folding them in would report a single node created with five properties as six affected rows. The full counter breakdown is available to a custom logger as counters on the log context.

Highlighting​

import { Neo4jHighlighter } from 'mikro-orm-neo4j';

MikroORM.init({ debug: ['query'], highlighter: new Neo4jHighlighter() });

Off by default, as SQL highlighting is in core. It is lossless: stripping the escape codes returns the statement byte for byte, so a query copied out of a log still runs.

Colours follow the same switches as the rest of the ORM's output — NO_COLOR, MIKRO_ORM_NO_COLOR, FORCE_COLOR, MIKRO_ORM_COLORS.

Where else to look​

  • Read replicas — which connection a statement ran on, and how to choose it.
  • Transactions — boundaries in the log, cancellation, and the options Neo4j cannot honour.
  • Exceptions — how a failing statement is reported before it is thrown.