---
title: "Logging and Tracing"
description: "Drizzle ORM provides an integrated diagnostic infrastructure combining OpenTelemetry tracing and structured query logging to observe and monitor query execution lifecycle events. The tracing subsys..."
last_updated: "2026-10-12T08:14:00.746407+00:00"
canonical_url: "https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/core-architecture/logging-and-tracing"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [drizzle-orm/src/tracing.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts)
- [drizzle-orm/src/sqlite-proxy/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-proxy/session.ts)
- [drizzle-orm/src/gel-core/dialect.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/dialect.ts)
- [drizzle-orm/src/libsql/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/libsql/session.ts)
- [drizzle-orm/src/neon-serverless/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/neon-serverless/session.ts)
- [drizzle-orm/src/pg-core/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/session.ts)
- [drizzle-orm/src/gel-core/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/session.ts)
- [drizzle-orm/src/singlestore/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore/session.ts)
- [drizzle-orm/src/pg-proxy/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-proxy/session.ts)
- [drizzle-orm/src/bun-sql/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/bun-sql/session.ts)
- [drizzle-orm/src/node-postgres/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/node-postgres/session.ts)
- [drizzle-orm/src/pg-core/query-builders/insert.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/insert.ts)
- [drizzle-orm/src/pg-core/dialect.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/dialect.ts)
- [drizzle-orm/src/neon-http/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/neon-http/session.ts)
- [drizzle-orm/src/planetscale-serverless/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/planetscale-serverless/session.ts)
- [drizzle-orm/src/tidb-serverless/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tidb-serverless/session.ts)
- [drizzle-orm/src/aws-data-api/pg/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/aws-data-api/pg/session.ts)
- [drizzle-orm/src/mysql2/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql2/session.ts)
- [drizzle-orm/src/singlestore-core/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/session.ts)
- [drizzle-orm/src/gel-core/query-builders/insert.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/gel-core/query-builders/insert.ts)
- [drizzle-orm/src/logger.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/logger.ts)
- [drizzle-orm/src/tracing-utils.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing-utils.ts)
- [drizzle-orm/src/op-sqlite/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/op-sqlite/session.ts)
- [drizzle-orm/src/singlestore-proxy/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-proxy/session.ts)
- [drizzle-orm/src/pg-core/query-builders/query.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts)
- [drizzle-orm/src/sqlite-core/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-core/session.ts)
- [drizzle-orm/src/query-promise.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/query-promise.ts)
- [drizzle-orm/src/prisma/pg/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/prisma/pg/session.ts)
- [drizzle-orm/src/singlestore-core/query-builders/delete.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/delete.ts)
- [drizzle-orm/src/mysql-core/session.ts](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/mysql-core/session.ts)
</details>

## Overview

Drizzle ORM provides an integrated diagnostic infrastructure combining OpenTelemetry tracing and structured query logging to observe and monitor query execution lifecycle events. The tracing subsystem uses the `@opentelemetry/api` package to wrap operations in active spans like `drizzle.operation`, `drizzle.prepareQuery`, `drizzle.buildSQL`, `drizzle.execute`, `drizzle.driver.execute`, and `drizzle.mapResponse`, automatically capturing error statuses and ending spans when execution completes. Concurrently, session-level logging dispatches query strings and parameters through logger implementations such as `DefaultLogger` and `ConsoleLogWriter`, formatting parameters with JSON stringification. This dual architecture allows applications to track performance metrics, inspect parameterized query statements across various driver runtimes, and handle diagnostic errors during failed database interactions.

Sources: [drizzle-orm/src/tracing.ts:1-55](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L1-L55), [drizzle-orm/src/logger.ts:1-39](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/logger.ts#L1-L39), [drizzle-orm/src/pg-core/session.ts:195-207](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/session.ts#L195-L207), [drizzle-orm/src/pg-proxy/session.ts:114-154](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-proxy/session.ts#L114-L154)

## OpenTelemetry tracing subsystem architecture

### Tracer Configuration and Module Integration

The OpenTelemetry tracing integration centers around the internal `tracer` object exported from `drizzle-orm/src/tracing.ts`. The module declares optional references to the `@opentelemetry/api` package and a raw tracer instance. When active spans are requested, the subsystem lazy-loads or references the tracer via `otel.trace.getTracer('drizzle-orm', npmVersion)`, utilizing the package version from `~/version.ts`. If `@opentelemetry/api` is not present in the runtime environment, span wrapping primitives bypass OpenTelemetry entirely and execute the provided callback directly.

```typescript
import type { Span, Tracer } from '@opentelemetry/api';
import { iife } from '~/tracing-utils.ts';
import { npmVersion } from '~/version.ts';

let otel: typeof import('@opentelemetry/api') | undefined;
let rawTracer: Tracer | undefined;
// try {
// 	otel = await import('@opentelemetry/api');
// } catch (err: any) {
// 	if (err.code !== 'MODULE_NOT_FOUND' && err.code !== 'ERR_MODULE_NOT_FOUND') {
// 		throw err;
// 	}
// }

type SpanName =
	| 'drizzle.operation'
	| 'drizzle.prepareQuery'
	| 'drizzle.buildSQL'
	| 'drizzle.execute'
	| 'drizzle.driver.execute'
	| 'drizzle.mapResponse';

/** @internal */
export const tracer = {
	startActiveSpan<F extends (span?: Span) => unknown>(name: SpanName, fn: F): ReturnType<F> {
		if (!otel) {
			return fn() as ReturnType<F>;
		}

		if (!rawTracer) {
			rawTracer = otel.trace.getTracer('drizzle-orm', npmVersion);
		}
```
Sources: [drizzle-orm/src/tracing.ts:1-32](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L1-L32)

> [!NOTE]
> If `@opentelemetry/api` is missing from the environment, `tracer.startActiveSpan` skips span creation and transparently executes the workload function without throwing module resolution errors.

### Span Names and Wrapping Primitives

The tracing subsystem defines a strict set of permitted operation names managed by the `SpanName` type. These identifiers cover the end-to-end query lifecycle from high-level operations down to driver-specific execution and response mapping.

| Span Name | Description |
| :--- | :--- |
| `drizzle.operation` | Top-level query builder or transactional operation wrapper |
| `drizzle.prepareQuery` | Statement preparation and parameter binding compilation |
| `drizzle.buildSQL` | SQL string generation and dialect operator construction |
| `drizzle.execute` | Query execution boundary across database sessions |
| `drizzle.driver.execute` | Low-level driver communication and transport layer invocation |
| `drizzle.mapResponse` | Raw database response parsing and entity mapping |

```typescript
type SpanName =
	| 'drizzle.operation'
	| 'drizzle.prepareQuery'
	| 'drizzle.buildSQL'
	| 'drizzle.execute'
	| 'drizzle.driver.execute'
	| 'drizzle.mapResponse';
```
Sources: [drizzle-orm/src/tracing.ts:15-21](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L15-L21)

Span wrapping is executed via the `startActiveSpan` method, which delegates to an immediately invoked function expression helper (`iife`) defined in `drizzle-orm/src/tracing-utils.ts`. 

```typescript
export function iife<T extends unknown[], U>(fn: (...args: T) => U, ...args: T): U {
	return fn(...args);
}
```
Sources: [drizzle-orm/src/tracing-utils.ts:1-3](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing-utils.ts#L1-L3-3)

The span wrapper wraps the callback execution inside a `try...catch...finally` block. If an exception occurs, the span status is explicitly set to `otel.SpanStatusCode.ERROR` with the error message extracted from the thrown instance, before rethrowing the error and ending the span.

```typescript
	return iife(
		(otel, rawTracer) =>
			rawTracer.startActiveSpan(
				name,
				((span: Span) => {
					try {
						return fn(span);
					} catch (e) {
						span.setStatus({
							code: otel.SpanStatusCode.ERROR,
							message: e instanceof Error ? e.message : 'Unknown error', // eslint-disable-line no-instanceof/no-instanceof
						});
						throw e;
					} finally {
						span.end();
					}
				}) as F,
			),
		otel,
		rawTracer,
	);
},
```
Sources: [drizzle-orm/src/tracing.ts:34-55](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L34-L55)

## Query builder span instrumentation

### Relational and Core Query Builder Spans

Query builder methods integrate directly with the tracing subsystem by wrapping statement preparation and query execution inside active spans. In PostgreSQL relational query builders (`PgRelationalQuery`), the `_prepare` method wraps statement construction and relational mapping within a `drizzle.prepareQuery` span. 

```typescript
_prepare(name?: string): PgPreparedQuery<PreparedQueryConfig & { execute: TResult }> {
	return tracer.startActiveSpan('drizzle.prepareQuery', () => {
		const { query, builtQuery } = this._toSQL();

		return this.session.prepareQuery<PreparedQueryConfig & { execute: TResult }>(
			builtQuery,
			undefined,
			name,
			true,
			(rawRows, mapColumnValue) => {
				const rows = rawRows.map((row) =>
					mapRelationalRow(this.schema, this.tableConfig, row, query.selection, mapColumnValue)
				);
				if (this.mode === 'first') {
					return rows[0] as TResult;
				}
				return rows as TResult;
			},
		);
	});
}
```
Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:90-110](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L90-L110)

When a relational query is executed via `execute()`, the operation is wrapped inside a `drizzle.operation` span before invoking the underlying prepared statement.

```typescript
override execute(): Promise<TResult> {
	return tracer.startActiveSpan('drizzle.operation', () => {
		return this._prepare().execute(undefined, this.authToken);
	});
}
```
Sources: [drizzle-orm/src/pg-core/query-builders/query.ts:152-156](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/pg-core/query-builders/query.ts#L152-L156)

> [!NOTE]
> Relational execution relies on `_prepare()` internally, causing a nested trace structure where `drizzle.operation` invokes `drizzle.prepareQuery` during statement compilation.

### Dialect Compilation and Execution Flow

Query building across different dialects separates syntax compilation from session execution. For instance, SingleStore delete query builders (`SingleStoreDeleteBase`) invoke dialect builders like `buildDeleteQuery` and `sqlToQuery` during `getSQL` and `prepare` calls, passing table metadata and returning clauses directly to the session.

```typescript
getSQL(): SQL {
	return this.dialect.buildDeleteQuery(this.config);
}

toSQL(): Query {
	const { typings: _typings, ...rest } = this.dialect.sqlToQuery(this.getSQL());
	return rest;
}

prepare(): SingleStoreDeletePrepare<this> {
	return this.session.prepareQuery(
		this.dialect.sqlToQuery(this.getSQL()),
		this.config.returning,
		undefined,
		undefined,
		undefined,
		{
			type: 'delete',
			tables: extractUsedTable(this.config.table),
		},
	) as SingleStoreDeletePrepare<this>;
}
```
Sources: [drizzle-orm/src/singlestore-core/query-builders/delete.ts:176-197](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-core/query-builders/delete.ts#L176-L197)

## Logger interface and default writers

### Logger Contracts and Writers

The logging subsystem defines two core abstraction contracts: `Logger`, which exposes a `logQuery` method accepting a SQL query string and an array of unknown parameters, and `LogWriter`, which defines a simple `write` method for delivering text messages.

```typescript
export interface Logger {
	logQuery(query: string, params: unknown[]): void;
}

export interface LogWriter {
	write(message: string): void;
}
```
Sources: [drizzle-orm/src/logger.ts:3-9](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/logger.ts#L3-L9)

The built-in output implementation is `ConsoleLogWriter`, which implements `LogWriter` by forwarding messages directly to `console.log`. Both `ConsoleLogWriter` and `DefaultLogger` define static `entityKind` metadata identifiers for runtime type inspection.

```typescript
export class ConsoleLogWriter implements LogWriter {
	static readonly [entityKind]: string = 'ConsoleLogWriter';

	write(message: string) {
		console.log(message);
	}
}
```
Sources: [drizzle-orm/src/logger.ts:11-17](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/logger.ts#L11-L17)

### DefaultLogger and Formatting Logic

`DefaultLogger` implements the `Logger` interface and accepts an optional configuration object containing a custom `LogWriter`. If no writer is supplied during instantiation, it defaults to a new instance of `ConsoleLogWriter`.

```typescript
export class DefaultLogger implements Logger {
	static readonly [entityKind]: string = 'DefaultLogger';

	readonly writer: LogWriter;

	constructor(config?: { writer: LogWriter }) {
		this.writer = config?.writer ?? new ConsoleLogWriter();
	}
```
Sources: [drizzle-orm/src/logger.ts:19-26](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/logger.ts#L19-L26)

When `logQuery` is invoked, `DefaultLogger` serializes each query parameter into a JSON string using `JSON.stringify`. If serialization fails due to circular references or unsupported types, it falls back to standard `String(p)` coercion. The formatted parameter array is appended to the query string under the suffix `-- params: [...]` only when parameters are present, and the resulting payload is dispatched to the configured writer.

```typescript
	logQuery(query: string, params: unknown[]): void {
		const stringifiedParams = params.map((p) => {
			try {
				return JSON.stringify(p);
			} catch {
				return String(p);
			}
		});
		const paramsStr = stringifiedParams.length ? ` -- params: [${stringifiedParams.join(', ')}]` : '';
		this.writer.write(`Query: ${query}${paramsStr}`);
	}
}
```
Sources: [drizzle-orm/src/logger.ts:28-39](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/logger.ts#L28-L39)

> [!TIP]
> If `JSON.stringify` throws an error on complex parameter structures inside `DefaultLogger.logQuery`, the parameter falls back to `String(p)` rather than aborting query logging.

Additionally, `NoopLogger` provides a null-pattern implementation of `Logger` where `logQuery` performs no operations.

```typescript
export class NoopLogger implements Logger {
	static readonly [entityKind]: string = 'NoopLogger';

	logQuery(): void {
		// noop
	}
}
```
Sources: [drizzle-orm/src/logger.ts:41-47](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/logger.ts#L41-L47)

## Session level query logging integration

### Logger Dispatch Across Drivers

Database sessions and prepared query implementations across drivers coordinate with the logging subsystem by invoking `logQuery` on the configured `Logger` instance right before executing any statement. Each driver binds placeholders via `fillPlaceholders` and dispatches the interpolated query text and parameter array. For example, `RemotePreparedQuery` in the SQLite proxy driver invokes `this.logger.logQuery(this.query.sql, params)` across its `run`, `all`, `get`, and `values` execution methods prior to invoking the remote client callback.

```typescript
async run(placeholderValues?: Record<string, unknown>): Promise<SqliteRemoteResult> {
	const params = fillPlaceholders(this.query.params, placeholderValues ?? {});
	this.logger.logQuery(this.query.sql, params);
	return await this.queryWithCache(this.query.sql, params, async () => {
		return await (this.client as AsyncRemoteCallback)(this.query.sql, params, 'run');
	});
}
```
Sources: [drizzle-orm/src/sqlite-proxy/session.ts:177-183](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-proxy/session.ts#L177-L183)

Similarly, `LibSQLPreparedQuery` dispatches `logger.logQuery(query.sql, params)` across execution branches before executing statements through the underlying LibSQL client or active transaction.

```typescript
async run(placeholderValues?: Record<string, unknown>): Promise<ResultSet> {
	const params = fillPlaceholders(this.query.params, placeholderValues ?? {});
	this.logger.logQuery(this.query.sql, params);
	return await this.queryWithCache(this.query.sql, params, async () => {
		const stmt: InStatement = { sql: this.query.sql, args: params as InArgs };
		return this.tx ? this.tx.execute(stmt) : this.client.execute(stmt);
	});
}
```
Sources: [drizzle-orm/src/libsql/session.ts:194-201](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/libsql/session.ts#L194-L201)

PostgreSQL and MySQL drivers follow the same dispatch contract. In `NeonPreparedQuery`, `BunSQLPreparedQuery`, `NeonHttpPreparedQuery`, `TiDBServerlessPreparedQuery`, and `AwsDataApiPreparedQuery`, parameter binding precedes `logger.logQuery` invocation. `AwsDataApiPreparedQuery` extracts parameters from the raw query configuration input parameters before logging.

```typescript
async values(placeholderValues: Record<string, unknown> = {}): Promise<T['values']> {
	const params = fillPlaceholders(this.params, placeholderValues ?? {});

	this.rawQuery.input.parameters = params.map((param, index) => ({
		name: `${index + 1}`,
		...toValueParam(param, this.typings[index]),
	}));

	this.options.logger?.logQuery(this.rawQuery.input.sql!, this.rawQuery.input.parameters);

```
Sources: [drizzle-orm/src/aws-data-api/pg/session.ts:110-119](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/aws-data-api/pg/session.ts#L110-L119)

Session-level fallback helpers also integrate logging directly. In `TiDBServerlessSession`, direct `all` queries compute the query structure via the dialect, invoke `this.logger.logQuery(querySql.sql, querySql.params)`, and dispatch to the client.

```typescript
override all<T = unknown>(query: SQL): Promise<T[]> {
	const querySql = this.dialect.sqlToQuery(query);
	this.logger.logQuery(querySql.sql, querySql.params);
	return this.client.execute(querySql.sql, querySql.params) as Promise<T[]>;
}
```
Sources: [drizzle-orm/src/tidb-serverless/session.ts:159-163](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tidb-serverless/session.ts#L159-L163)

### Driver Logging Integration Matrix

| Driver Module | Prepared Query Class | Session Class | Default Logger Fallback |
| :--- | :--- | :--- | :--- |
| `sqlite-proxy` | `RemotePreparedQuery` | `SQLiteProxySession` | `NoopLogger` |
| `libsql` | `LibSQLPreparedQuery` | `LibSQLSession` | `NoopLogger` |
| `neon-serverless` | `NeonPreparedQuery` | `NeonSession` | `NoopLogger` |
| `bun-sql` | `BunSQLPreparedQuery` | `BunSQLSession` | `NoopLogger` |
| `neon-http` | `NeonHttpPreparedQuery` | `NeonHttpSession` | `NoopLogger` |
| `tidb-serverless` | `TiDBServerlessPreparedQuery` | `TiDBServerlessSession` | `NoopLogger` |
| `aws-data-api/pg` | `AwsDataApiPreparedQuery` | `AwsDataApiSession` | `NoopLogger` |
| `op-sqlite` | `OPSQLitePreparedQuery` | `OPSQLiteSession` | `NoopLogger` |
| `singlestore-proxy` | `PreparedQuery` | `SingleStoreRemoteSession` | `NoopLogger` |
| `prisma/pg` | `PrismaPgPreparedQuery` | `PrismaPgSession` | `NoopLogger` |

Sources: [drizzle-orm/src/sqlite-proxy/session.ts:143-153](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/sqlite-proxy/session.ts#L143-L153), [drizzle-orm/src/libsql/session.ts:165-174](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/libsql/session.ts#L165-L174), [drizzle-orm/src/neon-serverless/session.ts:27-38](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/neon-serverless/session.ts#L27-L38), [drizzle-orm/src/bun-sql/session.ts:19-28](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/bun-sql/session.ts#L19-L28), [drizzle-orm/src/neon-http/session.ts:29-37](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/neon-http/session.ts#L29-L37), [drizzle-orm/src/tidb-serverless/session.ts:26-34](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tidb-serverless/session.ts#L26-L34), [drizzle-orm/src/aws-data-api/pg/session.ts:30-42](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/aws-data-api/pg/session.ts#L30-L42), [drizzle-orm/src/op-sqlite/session.ts:28-46](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/op-sqlite/session.ts#L28-L46), [drizzle-orm/src/singlestore-proxy/session.ts:29-45](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/singlestore-proxy/session.ts#L29-L45), [drizzle-orm/src/prisma/pg/session.ts:16-25](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/prisma/pg/session.ts#L16-L25)

> [!IMPORTANT]
> When options omit a logger during session initialization (such as in `BunSQLSession`, `TiDBServerlessSession`, and `OPSQLiteSession`), sessions instantiate a `NoopLogger` to ensure safe method invocation without requiring explicit null checks across prepared queries.

Sources: [drizzle-orm/src/bun-sql/session.ts:128-129](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/bun-sql/session.ts#L128-L129), [drizzle-orm/src/tidb-serverless/session.ts:128-129](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tidb-serverless/session.ts#L128-L129), [drizzle-orm/src/op-sqlite/session.ts:44-45](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/op-sqlite/session.ts#L44-L45)

## Diagnostics and tracing error handling

### Active Span Exception Capture

```typescript
startActiveSpan<F extends (span?: Span) => unknown>(name: SpanName, fn: F): ReturnType<F> {
	if (!otel) {
		return fn() as ReturnType<F>;
	}

	if (!rawTracer) {
		rawTracer = otel.trace.getTracer('drizzle-orm', npmVersion);
	}

	return iife(
		(otel, rawTracer) =>
			rawTracer.startActiveSpan(
				name,
				((span: Span) => {
					try {
						return fn(span);
					} catch (e) {
						span.setStatus({
							code: otel.SpanStatusCode.ERROR,
							message: e instanceof Error ? e.message : 'Unknown error', // eslint-disable-line no-instanceof/no-instanceof
						});
						throw e;
					} finally {
						span.end();
					}
				}) as F,
			),
		otel,
		rawTracer,
	);
},
```
Sources: [drizzle-orm/src/tracing.ts:25-55](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L25-L55)

The tracing subsystem intercepts exceptions during active span execution via the `startActiveSpan` wrapper. When an error is thrown within the callback function `fn(span)`, the execution catches the exception in a `try...catch` block, marks the OpenTelemetry span status as an error, and rethrows the original error.

> [!WARNING]
> If OpenTelemetry (`otel`) is not imported or available in the runtime environment, `startActiveSpan` bypasses span creation entirely and directly executes the callback function without error capture or status reporting.

Sources: [drizzle-orm/src/tracing.ts:26-28](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L26-L28)

### Span Status and Error Properties

| Condition | Span Status Code | Error Message Extraction Strategy |
| :--- | :--- | :--- |
| Caught value is an `Error` instance | `otel.SpanStatusCode.ERROR` | `e.message` |
| Caught value is not an `Error` instance | `otel.SpanStatusCode.ERROR` | `'Unknown error'` |

Sources: [drizzle-orm/src/tracing.ts:42-45](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L42-L45)

> [!IMPORTANT]
> The span lifecycle guarantees that `span.end()` is always invoked within a `finally` block, ensuring spans are closed even when query execution or response mapping throws an exception.

Sources: [drizzle-orm/src/tracing.ts:47-49](https://github.com/blade47/drizzle-orm/blob/48e5406027103a9fca6eb66417187c4a8b5c6aa3/drizzle-orm/src/tracing.ts#L47-L49)

## Related

- [Sessions and Transactions](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/technical/core-architecture/sessions-and-transactions)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e1b68fed-3c4e-4c95-b2ba-ebf050f78025/llms.txt) for all pages in this wiki.
