Structured logging interface with debug, info, and error levels for monitoring and debugging Algolia client operations.
Creates a no-operation logger that discards all log messages. Useful for production environments where logging is handled externally or disabled.
/**
* Creates a null logger that discards all log messages
* @returns Logger instance that performs no operations
*/
function createNullLogger(): Logger;Usage Examples:
import { createNullLogger } from "@algolia/client-common";
const logger = createNullLogger();
// All log calls are no-ops
await logger.debug("Debug message", { data: "value" });
await logger.info("Info message", { data: "value" });
await logger.error("Error message", { data: "value" });
// No output is producedStandard logging interface with async methods for debug, info, and error levels.
interface Logger {
/**
* Logs debug-level messages for detailed troubleshooting
* @param message - Log message string
* @param args - Optional additional data to log
* @returns Promise resolving when log operation completes
*/
debug: (message: string, args?: any) => Promise<void>;
/**
* Logs info-level messages for general information
* @param message - Log message string
* @param args - Optional additional data to log
* @returns Promise resolving when log operation completes
*/
info: (message: string, args?: any) => Promise<void>;
/**
* Logs error-level messages for errors and failures
* @param message - Log message string
* @param args - Optional additional data to log
* @returns Promise resolving when log operation completes
*/
error: (message: string, args?: any) => Promise<void>;
}Enumeration and type definitions for log levels with numeric values.
/**
* Log level enumeration with numeric values
*/
const LogLevelEnum: Readonly<Record<string, LogLevelType>> = {
Debug: 1,
Info: 2,
Error: 3,
};
/**
* Log level type definition
*/
type LogLevelType = 1 | 2 | 3;Usage Examples:
import { LogLevelEnum, LogLevelType } from "@algolia/client-common";
// Use log level constants
const currentLevel: LogLevelType = LogLevelEnum.Info;
// Level comparison
function shouldLog(messageLevel: LogLevelType, currentLevel: LogLevelType): boolean {
return messageLevel >= currentLevel;
}
console.log(shouldLog(LogLevelEnum.Debug, LogLevelEnum.Info)); // false
console.log(shouldLog(LogLevelEnum.Error, LogLevelEnum.Info)); // trueThe logging interface allows for custom implementations based on your requirements:
Console Logger Example:
import { Logger } from "@algolia/client-common";
class ConsoleLogger implements Logger {
private minLevel: LogLevelType;
constructor(minLevel: LogLevelType = LogLevelEnum.Info) {
this.minLevel = minLevel;
}
async debug(message: string, args?: any): Promise<void> {
if (this.shouldLog(LogLevelEnum.Debug)) {
console.debug(`[DEBUG] ${message}`, args || '');
}
}
async info(message: string, args?: any): Promise<void> {
if (this.shouldLog(LogLevelEnum.Info)) {
console.info(`[INFO] ${message}`, args || '');
}
}
async error(message: string, args?: any): Promise<void> {
if (this.shouldLog(LogLevelEnum.Error)) {
console.error(`[ERROR] ${message}`, args || '');
}
}
private shouldLog(level: LogLevelType): boolean {
return level >= this.minLevel;
}
}
// Usage
const logger = new ConsoleLogger(LogLevelEnum.Debug);
await logger.debug("Debug message", { requestId: "123" });Structured Logger Example:
import { Logger } from "@algolia/client-common";
interface LogEntry {
timestamp: string;
level: string;
message: string;
data?: any;
source: string;
}
class StructuredLogger implements Logger {
private logEntries: LogEntry[] = [];
async debug(message: string, args?: any): Promise<void> {
this.addEntry('DEBUG', message, args);
}
async info(message: string, args?: any): Promise<void> {
this.addEntry('INFO', message, args);
}
async error(message: string, args?: any): Promise<void> {
this.addEntry('ERROR', message, args);
}
private addEntry(level: string, message: string, data?: any): void {
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level,
message,
data,
source: 'algolia-client-common'
};
this.logEntries.push(entry);
// Also output to console in development
if (process.env.NODE_ENV === 'development') {
console.log(JSON.stringify(entry, null, 2));
}
}
// Additional methods for log management
getLogEntries(): LogEntry[] {
return [...this.logEntries];
}
clearLogs(): void {
this.logEntries = [];
}
async flush(): Promise<void> {
// Send logs to external service
if (this.logEntries.length > 0) {
await this.sendToLoggingService(this.logEntries);
this.clearLogs();
}
}
private async sendToLoggingService(entries: LogEntry[]): Promise<void> {
// Implementation for external logging service
// e.g., send to Elasticsearch, CloudWatch, etc.
}
}Remote Logger Example:
import { Logger } from "@algolia/client-common";
class RemoteLogger implements Logger {
private endpoint: string;
private apiKey: string;
private buffer: any[] = [];
private batchSize: number = 10;
constructor(endpoint: string, apiKey: string) {
this.endpoint = endpoint;
this.apiKey = apiKey;
}
async debug(message: string, args?: any): Promise<void> {
await this.log('debug', message, args);
}
async info(message: string, args?: any): Promise<void> {
await this.log('info', message, args);
}
async error(message: string, args?: any): Promise<void> {
await this.log('error', message, args);
}
private async log(level: string, message: string, args?: any): Promise<void> {
const logEntry = {
timestamp: Date.now(),
level,
message,
data: args,
sessionId: this.getSessionId()
};
this.buffer.push(logEntry);
if (this.buffer.length >= this.batchSize) {
await this.flush();
}
}
async flush(): Promise<void> {
if (this.buffer.length === 0) return;
const logs = [...this.buffer];
this.buffer = [];
try {
await fetch(this.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({ logs })
});
} catch (error) {
// Fall back to console in case of network issues
console.warn('Failed to send logs to remote service:', error);
logs.forEach(log => console.log(log));
}
}
private getSessionId(): string {
// Generate or retrieve session ID
return 'session-' + Math.random().toString(36).substr(2, 9);
}
}Logging is integrated throughout the transport layer for monitoring and debugging:
import { createTransporter, createNullLogger, Logger } from "@algolia/client-common";
// Using with transporter
const logger: Logger = createNullLogger(); // or your custom logger
const transporter = createTransporter({
// ... other options
logger,
});
// The transporter will automatically log:
// - Retryable failures with stack traces
// - Host state changes
// - Request/response details (in debug mode)Common Log Messages from Transport Layer:
// Info level - retryable failures
await logger.info('Retryable failure', {
request: sanitizedRequest,
response: sanitizedResponse,
host: hostInfo,
triesLeft: 2
});
// Debug level - request details
await logger.debug('Making request', {
method: 'POST',
path: '/1/indexes/my-index/query',
headers: sanitizedHeaders
});
// Error level - critical failures
await logger.error('All hosts unreachable', {
lastError: errorDetails,
hostsAttempted: hostList
});Log Level Usage:
Data Sanitization:
Always sanitize sensitive data before logging:
function sanitizeForLogging(data: any): any {
if (!data) return data;
const sanitized = { ...data };
// Remove sensitive fields
if ('x-algolia-api-key' in sanitized) {
sanitized['x-algolia-api-key'] = '*****';
}
if ('apiKey' in sanitized) {
sanitized['apiKey'] = '*****';
}
return sanitized;
}
// Usage in custom logger
await logger.info('Request completed', sanitizeForLogging(requestData));Performance Considerations:
Error Handling in Loggers:
class SafeLogger implements Logger {
private fallbackLogger: Logger;
constructor(private primaryLogger: Logger, fallbackLogger?: Logger) {
this.fallbackLogger = fallbackLogger || createNullLogger();
}
async debug(message: string, args?: any): Promise<void> {
try {
await this.primaryLogger.debug(message, args);
} catch (error) {
await this.fallbackLogger.debug(`Logging failed: ${message}`, { originalArgs: args, error });
}
}
async info(message: string, args?: any): Promise<void> {
try {
await this.primaryLogger.info(message, args);
} catch (error) {
await this.fallbackLogger.info(`Logging failed: ${message}`, { originalArgs: args, error });
}
}
async error(message: string, args?: any): Promise<void> {
try {
await this.primaryLogger.error(message, args);
} catch (error) {
await this.fallbackLogger.error(`Logging failed: ${message}`, { originalArgs: args, error });
}
}
}