Logging Support

hypequery includes built-in logging support to help you track query execution, timing, and status. The logger provides detailed information about your queries, including:

  • Query SQL and parameters
  • Execution start and end times
  • Query duration
  • Number of rows processed
  • Error details (if any)
  • Query-specific logging with unique IDs

Log Levels

The logger supports different log levels to control the verbosity of output:

import { logger } from '@hypequery/clickhouse';

// Available log levels:
// - 'debug': Detailed information for debugging
// - 'info': General information about query execution
// - 'warn': Warning messages
// - 'error': Error messages

logger.configure({
  level: 'debug', // Set the desired log level
  enabled: true
});

Configuring the Logger

import { logger } from '@hypequery/clickhouse';

// Configure logging options
logger.configure({
  level: 'debug',
  enabled: true,
  onQueryLog: (log) => {
    // Custom logging handler
    console.log('Query:', log.query);
    console.log('Duration:', log.duration);
    console.log('Status:', log.status);
  }
});

Query-Specific Logging

You can subscribe to logs for specific queries using a unique query ID:

import { initServe } from '@hypequery/serve';
import { db } from './analytics/client';

const { define, queries, query } = initServe({ context: () => ({ db }) });

const api = define({
  queries: queries({
    builderSource: query.query(async ({ ctx }) =>
      ctx.db.table('users').select(['id']).limit(1).execute()
    ),
  }),
});

// In the snippets below, `builder` refers to `ctx.db` inside these query definitions.
import { logger } from '@hypequery/clickhouse';
import { initServe } from '@hypequery/serve';
import { db } from './analytics/client';

const { define, queries, query } = initServe({ context: () => ({ db }) });

export const api = define({
  queries: queries({
    users: query
      .describe('Latest active users')
      .query(async ({ ctx }) =>
        ctx.db.table('users').where('status', 'eq', 'active').limit(25).execute()
      ),
  }),
});

// Subscribe to logs for a specific query
const unsubscribe = logger.subscribeToQuery('users-demo', (log) => {
  console.log('Query log:', log);
});

await api.run('users', { request: { queryId: 'users-demo' } });

unsubscribe();

Logging Specific Queries

You can selectively enable or disable logging for specific queries by temporarily modifying the logger configuration:

// Disable logging for a specific query
logger.configure({ enabled: false });
try {
  await builder
    .table('users')
    .select(['id', 'name'])
    .execute();
} finally {
  // Re-enable logging
  logger.configure({ enabled: true });
}

// Change log level for specific queries
logger.configure({ level: 'debug' });
try {
  await builder
    .table('orders')
    .select(['id', 'total'])
    .execute();
} finally {
  // Reset log level
  logger.configure({ level: 'info' });
}

Custom Logging Handlers

You can implement custom logging handlers to integrate with your existing logging system:

import { logger } from '@hypequery/clickhouse';
import winston from 'winston';

const winstonLogger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'query-logs.log' })
  ]
});

logger.configure({
  onQueryLog: (log) => {
    winstonLogger.info('Query executed', {
      query: log.query,
      duration: log.duration,
      status: log.status,
      rowCount: log.rowCount
    });
  }
});

Example Log Output

await api.run('builderSource');

// You'll see logs like:
// [hypequery Debug] Query started: SELECT id, name FROM users WHERE age > ?
// [hypequery Info] Query completed: SELECT id, name FROM users WHERE age > ? (duration: 45ms, rows: 100)

Viewing Logs

There are several ways to view and manage your query logs:

  1. Console Output (Default):
// Logs are automatically printed to console
// [hypequery Debug] Query started: SELECT id, name FROM users WHERE age > ?
// [hypequery Info] Query completed: SELECT id, name FROM users WHERE age > ? (duration: 45ms, rows: 100)
  1. Custom Log Handler:
logger.configure({
  onQueryLog: (log) => {
    // Store logs in your preferred logging system
    myLoggingSystem.log({
      timestamp: new Date(log.startTime),
      query: log.query,
      duration: log.duration,
      status: log.status,
      rowCount: log.rowCount
    });
  }
});
  1. File Logging (using Winston):
import winston from 'winston';

const fileLogger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'query-logs.log' })
  ]
});

logger.configure({
  onQueryLog: (log) => {
    fileLogger.info('Query executed', log);
  }
});
  1. In-Memory Logging:
const logs: QueryLog[] = [];

logger.configure({
  onQueryLog: (log) => {
    logs.push(log);
  }
});

// Later, analyze the logs
logs.forEach(log => {
  if (log.duration && log.duration > 1000) {
    console.log('Slow query detected:', log.query);
  }
});

Log Analysis

You can analyze logs to:

  • Identify slow queries
  • Track query patterns
  • Monitor error rates
  • Analyze query performance
// Example: Analyze query performance
const performanceLogs: QueryLog[] = [];

logger.configure({
  onQueryLog: (log) => {
    if (log.status === 'completed') {
      performanceLogs.push(log);
    }
  }
});

// After collecting logs, analyze them
const slowQueries = performanceLogs
  .filter(log => log.duration && log.duration > 1000)
  .map(log => ({
    query: log.query,
    duration: log.duration,
    rowCount: log.rowCount
  }));

console.log('Slow queries:', slowQueries);

Log Structure

Each log entry contains the following information:

interface QueryLog {
  query: string;           // The SQL query being executed
  parameters?: any[];      // Query parameters (if any)
  startTime: number;       // Timestamp when the query started
  endTime?: number;        // Timestamp when the query completed
  duration?: number;       // Query execution duration in milliseconds
  status: 'started' | 'completed' | 'error';  // Query execution status
  error?: Error;          // Error details (if any)
  rowCount?: number;      // Number of rows processed
  queryId?: string;       // Unique identifier for query-specific logging
}

See Also