> Every UQL docs page, as Markdown: https://uql-orm.dev/llms.txt
> The same docs over MCP: https://uql-orm.dev/mcp
> Before writing UQL code, read the skill: https://uql-orm.dev/.well-known/agent-skills/uql-orm/SKILL.md

# Logging & Monitoring

> Log queries, catch slow ones, and send UQL's logs to your own logger.

Source: https://uql-orm.dev/logging

UQL can log every query with its duration, flag slow queries, and report migration and schema activity. You set it per pool, in the pool’s options.

## Configuration

`logger: true` turns on every level through the built-in `DefaultLogger`, which prints colored lines to the console:

```ts
import { PgQuerierPool } from 'uql-orm/postgres';

export const pool = new PgQuerierPool(
  { connectionString: process.env.DATABASE_URL },
  {
    logger: true,
    slowQuery: 200, // log any query taking 200ms or more as slow
  },
);
```

An array turns on only the levels you list:

```ts
import type { ExtraOptions } from 'uql-orm';

const options: ExtraOptions = {
  logger: ['error', 'warn', 'migration'],
  slowQuery: 1000,
};
```

`slowQuery` works on its own: setting it logs slow queries whatever levels `logger` lists, even with no `logger` at all. The example above is a common production setup: quiet, except for problems and queries past a second.

### Bound values

Bound values are not logged unless you set `logValues: true`, since query parameters may hold personal data. It applies to query logs and slow-query logs alike.

```ts
const options: ExtraOptions = {
  logger: true,
  logValues: true,
};
```

## Log levels

| Level | What it logs |
| - | - |
| `query` | Each statement or command, with its duration (and values if on). |
| `error` / `warn` | Errors and warnings. |
| `migration` | Each migration as it runs and finishes. |
| `skippedMigration` | Destructive schema changes that `sync` held back in safe mode. |
| `schema` / `info` | Schema sync steps and other events. |

A dropped idle connection on a driver pool is always logged through the pool’s `logger`, even when the `error` level is not listed or logging is off. See [when a connection dies while idle](https://uql-orm.dev/pool.md#when-a-connection-dies-while-idle).

## Output format

`DefaultLogger` writes lines like these (values appear only with `logValues: true`):

```text
query: SELECT * FROM "user" WHERE "id" = $1 -- [123] [2ms]
slow query: UPDATE "post" SET "title" = $1 -- ["New Title"] [1250ms]
skipped migration: [AutoSync] Skipped 1 column drops in table 'user': old_field (drop: false. Use { drop: true } to apply).
```

## Custom logger

A function receives everything. Queries arrive as `(query, values, duration)`, other messages as `(message)` or `(message, error)`:

```ts
const options: ExtraOptions = {
  logger: (message, ...args) => myLogger.debug(message, ...args),
};
```

A `Logger` object gets one method per level: `logQuery`, `logSlowQuery`, `logWarn`, `logError`, `logInfo`, `logSchema`, `logMigration` and `logSkippedMigration`. Each is optional, so you can send slow queries somewhere regular ones do not go:

```ts
import type { ExtraOptions, Logger } from 'uql-orm';

const logger: Logger = {
  logSlowQuery(query, values, duration) {
    pagerduty.alert(`Slow query (${duration}ms): ${query}`);
  },
  logError(message, error) {
    sentry.captureException(error ?? new Error(message));
  },
};

const options: ExtraOptions = { logger, slowQuery: 500 };
```

To keep the console output and add alerting, extend `DefaultLogger` and override one method:

```ts
import { DefaultLogger } from 'uql-orm';

class AlertingLogger extends DefaultLogger {
  override logSlowQuery(
    query: string,
    values?: readonly unknown[],
    duration?: number,
  ) {
    super.logSlowQuery(query, values, duration);
    pagerduty.alert(`Slow query (${duration}ms): ${query}`);
  }
}
```

[Migrations](https://uql-orm.dev/migrations.md#applying-migrations-from-code) take the same `logger`, `logValues` and `slowQuery` options: `new Migrator(pool, { logger: true })`.
