test
This commit is contained in:
parent
4397541d2c
commit
4ab83d1dc2
5 changed files with 89 additions and 8 deletions
|
|
@ -6,7 +6,6 @@
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": false,
|
"allowImportingTsExtensions": false,
|
||||||
"module": "ESNext",
|
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,3 @@
|
||||||
export * from './dateUtils';
|
export * from './dateUtils';
|
||||||
export * from './logger';
|
export * from './logger';
|
||||||
export * from './lokiClient';
|
export * from './lokiClient.ts';
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,14 @@
|
||||||
* Logger utility with consistent formatting and log levels
|
* Logger utility with consistent formatting and log levels
|
||||||
* Supports console and Loki logging
|
* Supports console and Loki logging
|
||||||
*/
|
*/
|
||||||
import { loggingConfig } from '@stock-bot/config';
|
import { loggingConfig, lokiConfig } from '@stock-bot/config';
|
||||||
import { LokiClient } from './lokiClient';
|
|
||||||
|
|
||||||
// Singleton Loki client
|
// Singleton Loki client
|
||||||
let lokiClient: LokiClient | null = null;
|
let lokiClient: LokiClient | null = null;
|
||||||
|
|
||||||
function getLokiClient(): LokiClient {
|
function getLokiClient(): LokiClient {
|
||||||
if (!lokiClient) {
|
if (!lokiClient) {
|
||||||
lokiClient = new LokiClient(loggingConfig);
|
lokiClient = new LokiClient();
|
||||||
}
|
}
|
||||||
return lokiClient;
|
return lokiClient;
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +44,7 @@ export class Logger {
|
||||||
const logMessage = `[${timestamp}] [${levelStr}] [${this.serviceName}] ${fullMessage}`;
|
const logMessage = `[${timestamp}] [${levelStr}] [${this.serviceName}] ${fullMessage}`;
|
||||||
|
|
||||||
// Console logging
|
// Console logging
|
||||||
if (loggingConfig.console) {
|
if (loggingConfig.LOG_CONSOLE) {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case LogLevel.ERROR:
|
case LogLevel.ERROR:
|
||||||
console.error(logMessage);
|
console.error(logMessage);
|
||||||
|
|
@ -106,3 +105,85 @@ export enum LogLevel {
|
||||||
export function createLogger(serviceName: string, level: LogLevel = LogLevel.INFO): Logger {
|
export function createLogger(serviceName: string, level: LogLevel = LogLevel.INFO): Logger {
|
||||||
return new Logger(serviceName, level);
|
return new Logger(serviceName, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class LokiClient {
|
||||||
|
private batchQueue: any[] = [];
|
||||||
|
private flushInterval: NodeJS.Timeout;
|
||||||
|
private lokiUrl: string;
|
||||||
|
private authHeader?: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const { LOKI_HOST, LOKI_PORT, LOKI_USERNAME, LOKI_PASSWORD } = lokiConfig;
|
||||||
|
|
||||||
|
this.lokiUrl = `http://${LOKI_HOST}:${LOKI_PORT}/loki/api/v1/push`;
|
||||||
|
|
||||||
|
if (LOKI_USERNAME && LOKI_PASSWORD) {
|
||||||
|
const authString = Buffer.from(`${LOKI_USERNAME}:${LOKI_PASSWORD}`).toString('base64');
|
||||||
|
this.authHeader = `Basic ${authString}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.flushInterval = setInterval(
|
||||||
|
() => this.flush(),
|
||||||
|
lokiConfig.LOKI_FLUSH_INTERVAL_MS || 1000 // Default to 1 second if not set
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async log(level: string, message: string, serviceName: string, labels: Record<string, string> = {}) {
|
||||||
|
const timestamp = Date.now() * 1000000; // Loki expects nanoseconds
|
||||||
|
|
||||||
|
this.batchQueue.push({
|
||||||
|
streams: [{
|
||||||
|
stream: {
|
||||||
|
level,
|
||||||
|
service: serviceName,
|
||||||
|
...lokiConfig.LOKI_DEFAULT_LABELS ? JSON.parse(lokiConfig.LOKI_DEFAULT_LABELS) : {},
|
||||||
|
...labels,
|
||||||
|
},
|
||||||
|
values: [[`${timestamp}`, message]],
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.batchQueue.length >= lokiConfig.LOKI_BATCH_SIZE) {
|
||||||
|
await this.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async flush() {
|
||||||
|
if (this.batchQueue.length === 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.authHeader) {
|
||||||
|
headers['Authorization'] = this.authHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(this.lokiUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: JSON.stringify({
|
||||||
|
streams: this.batchQueue.flatMap(batch => batch.streams),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`Failed to send logs to Loki: ${response.status} ${response.statusText}`);
|
||||||
|
const text = await response.text();
|
||||||
|
if (text) {
|
||||||
|
console.error(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error sending logs to Loki:', error);
|
||||||
|
} finally {
|
||||||
|
this.batchQueue = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async destroy() {
|
||||||
|
clearInterval(this.flushInterval);
|
||||||
|
return this.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"declaration": true
|
"declaration": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue