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 { logger } from '@hypequery/clickhouse';

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

// Execute the query with the ID
await builder
  .table('users')
  .select(['id', 'name'])
  .where('age', 'gt', 18)
  .execute({ queryId }); // Pass the queryId in options

// Unsubscribe when done
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

// When executing a query
const results = await builder
  .table('users')
  .select(['id', 'name'])
  .where('age', 'gt', 18)
  .execute();

// 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)

Logging with Streaming

When using streaming, the logger will track the total number of rows processed:

const stream = builder
  .table('events')
  .select(['timestamp', 'event_type'])
  .stream();

// The logger will show:
// [hypequery Debug] Query started: SELECT timestamp, event_type FROM events
// [hypequery Info] Query completed: SELECT timestamp, event_type FROM events (duration: 120ms, rows: 1000)

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