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:
- 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)
- 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
});
}
});
- 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);
}
});
- 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
}