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