linxus fs fixes
This commit is contained in:
parent
ac23b70146
commit
0b7846fe67
292 changed files with 41947 additions and 41947 deletions
|
|
@ -1,18 +1,18 @@
|
|||
/**
|
||||
* @stock-bot/logger - Simplified logging library
|
||||
*
|
||||
* Main exports for the logger library
|
||||
*/
|
||||
|
||||
// Core logger classes and functions
|
||||
export {
|
||||
Logger,
|
||||
getLogger,
|
||||
shutdownLoggers
|
||||
} from './logger';
|
||||
|
||||
// Type definitions
|
||||
export type { LogLevel, LogContext, LogMetadata } from './types';
|
||||
|
||||
// Default export
|
||||
export { getLogger as default } from './logger';
|
||||
/**
|
||||
* @stock-bot/logger - Simplified logging library
|
||||
*
|
||||
* Main exports for the logger library
|
||||
*/
|
||||
|
||||
// Core logger classes and functions
|
||||
export {
|
||||
Logger,
|
||||
getLogger,
|
||||
shutdownLoggers
|
||||
} from './logger';
|
||||
|
||||
// Type definitions
|
||||
export type { LogLevel, LogContext, LogMetadata } from './types';
|
||||
|
||||
// Default export
|
||||
export { getLogger as default } from './logger';
|
||||
|
|
|
|||
|
|
@ -1,271 +1,271 @@
|
|||
/**
|
||||
* Simplified Pino-based logger for Stock Bot platform
|
||||
*
|
||||
* Features:
|
||||
* - High performance JSON logging with Pino
|
||||
* - Console, file, and Loki transports
|
||||
* - Structured logging with metadata
|
||||
* - Service-specific context
|
||||
*/
|
||||
|
||||
import pino from 'pino';
|
||||
import { loggingConfig, lokiConfig } from '@stock-bot/config';
|
||||
import type { LogLevel, LogContext, LogMetadata } from './types';
|
||||
|
||||
// Simple cache for logger instances
|
||||
const loggerCache = new Map<string, pino.Logger>();
|
||||
console.log('Logger cache initialized: ', loggingConfig.LOG_LEVEL);
|
||||
/**
|
||||
* Create transport configuration
|
||||
*/
|
||||
function createTransports(serviceName: string): any {
|
||||
const targets: any[] = [];
|
||||
// const isDev = loggingConfig.LOG_ENVIRONMENT === 'development';
|
||||
// Console transport
|
||||
if (loggingConfig.LOG_CONSOLE) {
|
||||
targets.push({
|
||||
target: 'pino-pretty',
|
||||
level: loggingConfig.LOG_LEVEL, // Only show errors on console
|
||||
options: {
|
||||
colorize: true,
|
||||
translateTime: 'yyyy-mm-dd HH:MM:ss.l',
|
||||
messageFormat: '[{service}{childName}] {msg}',
|
||||
singleLine: true,
|
||||
hideObject: false,
|
||||
ignore: 'pid,hostname,service,environment,version,childName',
|
||||
errorLikeObjectKeys: ['err', 'error'],
|
||||
errorProps: 'message,stack,name,code',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// File transport
|
||||
if (loggingConfig.LOG_FILE) {
|
||||
targets.push({
|
||||
target: 'pino/file',
|
||||
level: loggingConfig.LOG_LEVEL,
|
||||
options: {
|
||||
destination: `${loggingConfig.LOG_FILE_PATH}/${serviceName}.log`,
|
||||
mkdir: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Loki transport
|
||||
if (lokiConfig.LOKI_HOST) {
|
||||
targets.push({
|
||||
target: 'pino-loki',
|
||||
level: loggingConfig.LOG_LEVEL,
|
||||
options: {
|
||||
host: lokiConfig.LOKI_URL || `http://${lokiConfig.LOKI_HOST}:${lokiConfig.LOKI_PORT}`,
|
||||
labels: {
|
||||
service: serviceName,
|
||||
environment: lokiConfig.LOKI_ENVIRONMENT_LABEL
|
||||
},
|
||||
ignore: 'childName',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return targets.length > 0 ? { targets } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create pino logger
|
||||
*/
|
||||
function getPinoLogger(serviceName: string): pino.Logger {
|
||||
if (!loggerCache.has(serviceName)) {
|
||||
const transport = createTransports(serviceName);
|
||||
|
||||
const config: pino.LoggerOptions = {
|
||||
level: loggingConfig.LOG_LEVEL,
|
||||
base: {
|
||||
service: serviceName,
|
||||
environment: loggingConfig.LOG_ENVIRONMENT,
|
||||
version: loggingConfig.LOG_SERVICE_VERSION
|
||||
}
|
||||
};
|
||||
|
||||
if (transport) {
|
||||
config.transport = transport;
|
||||
}
|
||||
|
||||
loggerCache.set(serviceName, pino(config));
|
||||
}
|
||||
|
||||
return loggerCache.get(serviceName)!;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simplified Logger class
|
||||
*/
|
||||
export class Logger {
|
||||
private pino: pino.Logger;
|
||||
private context: LogContext;
|
||||
private serviceName: string;
|
||||
private childName?: string;
|
||||
|
||||
constructor(serviceName: string, context: LogContext = {}) {
|
||||
this.pino = getPinoLogger(serviceName);
|
||||
this.context = context;
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core log method
|
||||
*/
|
||||
private log(level: LogLevel, message: string | object, metadata?: LogMetadata): void {
|
||||
const data = { ...this.context, ...metadata };
|
||||
|
||||
if (typeof message === 'string') {
|
||||
(this.pino as any)[level](data, message);
|
||||
} else {
|
||||
(this.pino as any)[level]({ ...data, data: message }, 'Object logged');
|
||||
}
|
||||
}
|
||||
|
||||
// Simple log level methods
|
||||
debug(message: string | object, metadata?: LogMetadata): void {
|
||||
this.log('debug', message, metadata);
|
||||
}
|
||||
|
||||
info(message: string | object, metadata?: LogMetadata): void {
|
||||
this.log('info', message, metadata);
|
||||
}
|
||||
|
||||
warn(message: string | object, metadata?: LogMetadata): void {
|
||||
this.log('warn', message, metadata);
|
||||
}
|
||||
|
||||
error(message: string | object, metadata?: LogMetadata & { error?: any } | unknown): void {
|
||||
let data: any = {};
|
||||
|
||||
// Handle metadata parameter normalization
|
||||
if (metadata instanceof Error) {
|
||||
// Direct Error object as metadata
|
||||
data = { error: metadata };
|
||||
} else if (metadata !== null && typeof metadata === 'object') {
|
||||
// Object metadata (including arrays, but not null)
|
||||
data = { ...metadata };
|
||||
} else if (metadata !== undefined) {
|
||||
// Primitive values (string, number, boolean, etc.)
|
||||
data = { metadata };
|
||||
}
|
||||
|
||||
// Handle multiple error properties in metadata
|
||||
const errorKeys = ['error', 'err', 'primaryError', 'secondaryError'];
|
||||
errorKeys.forEach(key => {
|
||||
if (data[key]) {
|
||||
const normalizedKey = key === 'error' ? 'err' : `${key}_normalized`;
|
||||
data[normalizedKey] = this.normalizeError(data[key]);
|
||||
|
||||
// Only delete the original 'error' key to maintain other error properties
|
||||
if (key === 'error') {
|
||||
delete data.error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.log('error', message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize any error type to a structured format
|
||||
*/
|
||||
private normalizeError(error: any): any {
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
};
|
||||
}
|
||||
|
||||
if (error && typeof error === 'object') {
|
||||
// Handle error-like objects
|
||||
return {
|
||||
name: error.name || 'UnknownError',
|
||||
message: error.message || error.toString(),
|
||||
...(error.stack && { stack: error.stack }),
|
||||
...(error.code && { code: error.code }),
|
||||
...(error.status && { status: error.status })
|
||||
};
|
||||
}
|
||||
|
||||
// Handle primitives (string, number, etc.)
|
||||
return {
|
||||
name: 'UnknownError',
|
||||
message: String(error)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create child logger with additional context
|
||||
*/
|
||||
child(serviceName: string, context?: LogContext): Logger {
|
||||
// Create child logger that shares the same pino instance with additional context
|
||||
const childLogger = Object.create(Logger.prototype);
|
||||
childLogger.serviceName = this.serviceName;
|
||||
childLogger.childName = serviceName;
|
||||
childLogger.context = { ...this.context, ...context };
|
||||
const childBindings = {
|
||||
service: this.serviceName,
|
||||
childName: ' -> ' + serviceName,
|
||||
...(context || childLogger.context)
|
||||
};
|
||||
|
||||
childLogger.pino = this.pino.child(childBindings);
|
||||
return childLogger;
|
||||
// }
|
||||
// childLogger.pino = this.pino.child(context || childLogger.context); // Let pino handle level inheritance naturally
|
||||
// return childLogger;
|
||||
}
|
||||
|
||||
// Getters for service and context
|
||||
getServiceName(): string {
|
||||
return this.serviceName;
|
||||
}
|
||||
getChildName(): string | undefined {
|
||||
return this.childName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main factory function
|
||||
*/
|
||||
export function getLogger(serviceName: string, context?: LogContext): Logger {
|
||||
return new Logger(serviceName, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully shutdown all logger instances
|
||||
* This should be called during application shutdown to ensure all logs are flushed
|
||||
*/
|
||||
export async function shutdownLoggers(): Promise<void> {
|
||||
const flushPromises = Array.from(loggerCache.values()).map(logger => {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (typeof logger.flush === 'function') {
|
||||
logger.flush((err) => {
|
||||
if (err) {
|
||||
console.error('Logger flush error:', err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.allSettled(flushPromises);
|
||||
console.log('All loggers flushed successfully');
|
||||
} catch (error) {
|
||||
console.error('Logger flush failed:', error);
|
||||
} finally {
|
||||
loggerCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Export types for convenience
|
||||
export type { LogLevel, LogContext, LogMetadata } from './types';
|
||||
/**
|
||||
* Simplified Pino-based logger for Stock Bot platform
|
||||
*
|
||||
* Features:
|
||||
* - High performance JSON logging with Pino
|
||||
* - Console, file, and Loki transports
|
||||
* - Structured logging with metadata
|
||||
* - Service-specific context
|
||||
*/
|
||||
|
||||
import pino from 'pino';
|
||||
import { loggingConfig, lokiConfig } from '@stock-bot/config';
|
||||
import type { LogLevel, LogContext, LogMetadata } from './types';
|
||||
|
||||
// Simple cache for logger instances
|
||||
const loggerCache = new Map<string, pino.Logger>();
|
||||
console.log('Logger cache initialized: ', loggingConfig.LOG_LEVEL);
|
||||
/**
|
||||
* Create transport configuration
|
||||
*/
|
||||
function createTransports(serviceName: string): any {
|
||||
const targets: any[] = [];
|
||||
// const isDev = loggingConfig.LOG_ENVIRONMENT === 'development';
|
||||
// Console transport
|
||||
if (loggingConfig.LOG_CONSOLE) {
|
||||
targets.push({
|
||||
target: 'pino-pretty',
|
||||
level: loggingConfig.LOG_LEVEL, // Only show errors on console
|
||||
options: {
|
||||
colorize: true,
|
||||
translateTime: 'yyyy-mm-dd HH:MM:ss.l',
|
||||
messageFormat: '[{service}{childName}] {msg}',
|
||||
singleLine: true,
|
||||
hideObject: false,
|
||||
ignore: 'pid,hostname,service,environment,version,childName',
|
||||
errorLikeObjectKeys: ['err', 'error'],
|
||||
errorProps: 'message,stack,name,code',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// File transport
|
||||
if (loggingConfig.LOG_FILE) {
|
||||
targets.push({
|
||||
target: 'pino/file',
|
||||
level: loggingConfig.LOG_LEVEL,
|
||||
options: {
|
||||
destination: `${loggingConfig.LOG_FILE_PATH}/${serviceName}.log`,
|
||||
mkdir: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Loki transport
|
||||
if (lokiConfig.LOKI_HOST) {
|
||||
targets.push({
|
||||
target: 'pino-loki',
|
||||
level: loggingConfig.LOG_LEVEL,
|
||||
options: {
|
||||
host: lokiConfig.LOKI_URL || `http://${lokiConfig.LOKI_HOST}:${lokiConfig.LOKI_PORT}`,
|
||||
labels: {
|
||||
service: serviceName,
|
||||
environment: lokiConfig.LOKI_ENVIRONMENT_LABEL
|
||||
},
|
||||
ignore: 'childName',
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return targets.length > 0 ? { targets } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create pino logger
|
||||
*/
|
||||
function getPinoLogger(serviceName: string): pino.Logger {
|
||||
if (!loggerCache.has(serviceName)) {
|
||||
const transport = createTransports(serviceName);
|
||||
|
||||
const config: pino.LoggerOptions = {
|
||||
level: loggingConfig.LOG_LEVEL,
|
||||
base: {
|
||||
service: serviceName,
|
||||
environment: loggingConfig.LOG_ENVIRONMENT,
|
||||
version: loggingConfig.LOG_SERVICE_VERSION
|
||||
}
|
||||
};
|
||||
|
||||
if (transport) {
|
||||
config.transport = transport;
|
||||
}
|
||||
|
||||
loggerCache.set(serviceName, pino(config));
|
||||
}
|
||||
|
||||
return loggerCache.get(serviceName)!;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simplified Logger class
|
||||
*/
|
||||
export class Logger {
|
||||
private pino: pino.Logger;
|
||||
private context: LogContext;
|
||||
private serviceName: string;
|
||||
private childName?: string;
|
||||
|
||||
constructor(serviceName: string, context: LogContext = {}) {
|
||||
this.pino = getPinoLogger(serviceName);
|
||||
this.context = context;
|
||||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core log method
|
||||
*/
|
||||
private log(level: LogLevel, message: string | object, metadata?: LogMetadata): void {
|
||||
const data = { ...this.context, ...metadata };
|
||||
|
||||
if (typeof message === 'string') {
|
||||
(this.pino as any)[level](data, message);
|
||||
} else {
|
||||
(this.pino as any)[level]({ ...data, data: message }, 'Object logged');
|
||||
}
|
||||
}
|
||||
|
||||
// Simple log level methods
|
||||
debug(message: string | object, metadata?: LogMetadata): void {
|
||||
this.log('debug', message, metadata);
|
||||
}
|
||||
|
||||
info(message: string | object, metadata?: LogMetadata): void {
|
||||
this.log('info', message, metadata);
|
||||
}
|
||||
|
||||
warn(message: string | object, metadata?: LogMetadata): void {
|
||||
this.log('warn', message, metadata);
|
||||
}
|
||||
|
||||
error(message: string | object, metadata?: LogMetadata & { error?: any } | unknown): void {
|
||||
let data: any = {};
|
||||
|
||||
// Handle metadata parameter normalization
|
||||
if (metadata instanceof Error) {
|
||||
// Direct Error object as metadata
|
||||
data = { error: metadata };
|
||||
} else if (metadata !== null && typeof metadata === 'object') {
|
||||
// Object metadata (including arrays, but not null)
|
||||
data = { ...metadata };
|
||||
} else if (metadata !== undefined) {
|
||||
// Primitive values (string, number, boolean, etc.)
|
||||
data = { metadata };
|
||||
}
|
||||
|
||||
// Handle multiple error properties in metadata
|
||||
const errorKeys = ['error', 'err', 'primaryError', 'secondaryError'];
|
||||
errorKeys.forEach(key => {
|
||||
if (data[key]) {
|
||||
const normalizedKey = key === 'error' ? 'err' : `${key}_normalized`;
|
||||
data[normalizedKey] = this.normalizeError(data[key]);
|
||||
|
||||
// Only delete the original 'error' key to maintain other error properties
|
||||
if (key === 'error') {
|
||||
delete data.error;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.log('error', message, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize any error type to a structured format
|
||||
*/
|
||||
private normalizeError(error: any): any {
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
};
|
||||
}
|
||||
|
||||
if (error && typeof error === 'object') {
|
||||
// Handle error-like objects
|
||||
return {
|
||||
name: error.name || 'UnknownError',
|
||||
message: error.message || error.toString(),
|
||||
...(error.stack && { stack: error.stack }),
|
||||
...(error.code && { code: error.code }),
|
||||
...(error.status && { status: error.status })
|
||||
};
|
||||
}
|
||||
|
||||
// Handle primitives (string, number, etc.)
|
||||
return {
|
||||
name: 'UnknownError',
|
||||
message: String(error)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create child logger with additional context
|
||||
*/
|
||||
child(serviceName: string, context?: LogContext): Logger {
|
||||
// Create child logger that shares the same pino instance with additional context
|
||||
const childLogger = Object.create(Logger.prototype);
|
||||
childLogger.serviceName = this.serviceName;
|
||||
childLogger.childName = serviceName;
|
||||
childLogger.context = { ...this.context, ...context };
|
||||
const childBindings = {
|
||||
service: this.serviceName,
|
||||
childName: ' -> ' + serviceName,
|
||||
...(context || childLogger.context)
|
||||
};
|
||||
|
||||
childLogger.pino = this.pino.child(childBindings);
|
||||
return childLogger;
|
||||
// }
|
||||
// childLogger.pino = this.pino.child(context || childLogger.context); // Let pino handle level inheritance naturally
|
||||
// return childLogger;
|
||||
}
|
||||
|
||||
// Getters for service and context
|
||||
getServiceName(): string {
|
||||
return this.serviceName;
|
||||
}
|
||||
getChildName(): string | undefined {
|
||||
return this.childName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main factory function
|
||||
*/
|
||||
export function getLogger(serviceName: string, context?: LogContext): Logger {
|
||||
return new Logger(serviceName, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully shutdown all logger instances
|
||||
* This should be called during application shutdown to ensure all logs are flushed
|
||||
*/
|
||||
export async function shutdownLoggers(): Promise<void> {
|
||||
const flushPromises = Array.from(loggerCache.values()).map(logger => {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (typeof logger.flush === 'function') {
|
||||
logger.flush((err) => {
|
||||
if (err) {
|
||||
console.error('Logger flush error:', err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
await Promise.allSettled(flushPromises);
|
||||
console.log('All loggers flushed successfully');
|
||||
} catch (error) {
|
||||
console.error('Logger flush failed:', error);
|
||||
} finally {
|
||||
loggerCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Export types for convenience
|
||||
export type { LogLevel, LogContext, LogMetadata } from './types';
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
/**
|
||||
* Simplified type definitions for the logger library
|
||||
*/
|
||||
|
||||
// Standard log levels (simplified to pino defaults)
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
// Context that persists across log calls
|
||||
export interface LogContext {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// Metadata for individual log entries
|
||||
export interface LogMetadata {
|
||||
[key: string]: any;
|
||||
}
|
||||
/**
|
||||
* Simplified type definitions for the logger library
|
||||
*/
|
||||
|
||||
// Standard log levels (simplified to pino defaults)
|
||||
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
// Context that persists across log calls
|
||||
export interface LogContext {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// Metadata for individual log entries
|
||||
export interface LogMetadata {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue