Skip to content

Debug SQL tracing

This operation gives developers a way to see every SQL statement NHibernate actually sends to the database while debugging, without affecting production logging.

Field Value
Operation ID OP-NH-14
Trigger AddNhibernateServices(assemblies, debugOutputSql: true) was used at registration and the current unit of work uses a stateful session and a debugger is attached to the process
Inputs The prepared SqlString for each statement NHibernate sends
Outputs A timestamped line written to System.Diagnostics.Trace for every prepared statement
Primary components SqlDebugOutputInterceptor.OnPrepareStatement
Entry point src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/Configuration/SqlDebugOutputInterceptor.cs

Steps

  1. Build session factory is configured with debugOutputSql = true (via AddNhibernateServices's second parameter), and Open session opens a stateful session (options.StatelessSessionStrategy == false) (BR-NH-09):
  2. The session is opened with factory.WithOptions().Interceptor(new SqlDebugOutputInterceptor()).OpenSession() instead of a plain factory.OpenSession().
  3. Every time NHibernate prepares a statement to send to the database, it calls the interceptor's OnPrepareStatement(SqlString sql) override.
  4. OnPrepareStatement checks Debugger.IsAttached:
  5. If true: write "{yyyy-MM-dd HH:mm:ss} - NHibernate: " followed by the SQL text to Trace.
  6. If false: skip writing; no output is produced even though the interceptor is attached.
  7. Return base.OnPrepareStatement(sql) unchanged — the interceptor only observes/logs; it never rewrites the statement.

Process flow

The diagram below visualizes the stateful/debug-flag gate on whether the interceptor is attached at all, and the Debugger.IsAttached check inside OnPrepareStatement.

flowchart TD
    Start(["Statement prepared"]) --> Interceptor{"Stateful session with<br>debugOutputSql = true?"}
    Interceptor -->|No| NoTrace(["No interceptor attached — no tracing"])
    Interceptor -->|Yes| OnPrepare["OnPrepareStatement(sql) called"]
    OnPrepare --> DebuggerCheck{"Debugger.IsAttached?"}
    DebuggerCheck -->|No| Skip(["No output written"])
    DebuggerCheck -->|Yes| Write["Write timestamp + SQL to Trace"]
    Write --> PassThrough(["Return sql unchanged"])

Failure and edge-case behavior

  • Stateless sessions never receive this interceptor, so TestConnection/queries run through a stateless unit of work produce no SQL trace output regardless of debugOutputSql.
  • Even with debugOutputSql = true, no output appears unless a debugger is actually attached to the running process — this is not a general-purpose production logging mechanism.
  • UnitOfWorkOptions.ShowSql/DriverFormatSql (NHibernate's own show_sql/format_sql properties) are a separate, independent mechanism from this interceptor; both can be enabled or disabled independently.*

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

Sources

  • src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/Configuration/SqlDebugOutputInterceptor.cs
  • src/SAIF.NetCore.Nhibernate/SAIF.NetCore.NHibernate/SAIF.NetCore.NHibernate/Factories/UnitOfWorkFactory.cs