feat(logging): replace 207 console.log/error/warn with pino structured logger (#58)
- Add pino + pino-pretty dependencies - Create src/utils/logger.js with env-based LOG_LEVEL - Replace all 207 console.log/error/warn calls across 46 source files - Remove [DEBUG], [ERROR] string prefixes (levels convey this) - Add pino redact for sensitive fields (mnemonic, privateKey, token, etc.) - Structured logging with context objects instead of string interpolation - NODE_ENV=production disables pino-pretty transport 49 files changed, 5601 insertions, 6056 deletions
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import logger from './logger.js';
|
||||
|
||||
export default class ErrorHandler {
|
||||
static async handleError(bot, chatId, error, context) {
|
||||
console.error(`Error in ${context}:`, error);
|
||||
logger.error({ err: error, context }, 'Error in handler');
|
||||
|
||||
const errorMessage = process.env.NODE_ENV === 'development'
|
||||
? `Error: ${error.message}`
|
||||
@@ -9,16 +11,16 @@ export default class ErrorHandler {
|
||||
try {
|
||||
await bot.sendMessage(chatId, errorMessage);
|
||||
} catch (sendError) {
|
||||
console.error('Error sending error message:', sendError);
|
||||
logger.error({ err: sendError }, 'Error sending error message');
|
||||
}
|
||||
}
|
||||
|
||||
static handlePollingError(error) {
|
||||
if (error.code === 'ETELEGRAM') {
|
||||
console.error('Telegram API Error:', error.message);
|
||||
logger.error({ err: error }, 'Telegram API Error');
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.error('Polling error:', error);
|
||||
logger.error({ err: error }, 'Polling error');
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/utils/logger.js
Normal file
19
src/utils/logger.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import pino from 'pino';
|
||||
|
||||
const level = process.env.LOG_LEVEL || 'info';
|
||||
|
||||
const options = {
|
||||
level,
|
||||
redact: ['*.mnemonic', '*.privateKey', '*.secret', '*.token', '*.password', '*.ENCRYPTION_KEY', '*.BOT_TOKEN'],
|
||||
serializers: {
|
||||
err: pino.stdSerializers.err,
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
options.transport = { target: 'pino-pretty', options: { colorize: true } };
|
||||
}
|
||||
|
||||
const logger = pino(options);
|
||||
|
||||
export default logger;
|
||||
@@ -8,6 +8,7 @@ import * as bitcoin from 'bitcoinjs-lib';
|
||||
import * as ecc from 'tiny-secp256k1';
|
||||
import { ECPairFactory } from 'ecpair';
|
||||
import CryptoJS from 'crypto';
|
||||
import logger from './logger.js';
|
||||
|
||||
const ECPair = ECPairFactory(ecc);
|
||||
|
||||
@@ -16,7 +17,7 @@ export default class WalletGenerator {
|
||||
try {
|
||||
return bip39.generateMnemonic(128); // 12 слов
|
||||
} catch (error) {
|
||||
console.error('Error generating mnemonic:', error);
|
||||
logger.error({ err: error }, 'Error generating mnemonic');
|
||||
throw new Error('Failed to generate mnemonic');
|
||||
}
|
||||
}
|
||||
@@ -86,7 +87,7 @@ export default class WalletGenerator {
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error in generateWallets:', error);
|
||||
logger.error({ err: error }, 'Error in generateWallets');
|
||||
throw new Error('Failed to generate cryptocurrency wallets: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// walletUtils.js
|
||||
|
||||
import axios from 'axios';
|
||||
import db from '../config/database.js'; // Импортируем базу данных
|
||||
import db from '../config/database.js';
|
||||
import logger from './logger.js';
|
||||
|
||||
// Массив публичных RPC-узлов
|
||||
const rpcNodes = [
|
||||
@@ -94,14 +95,14 @@ export default class WalletUtils {
|
||||
static async getCryptoPrices() {
|
||||
// Если кеш актуален, возвращаем его
|
||||
if (cryptoPricesCache && Date.now() - cacheTimestamp < CACHE_TTL) {
|
||||
console.log('[DEBUG] Using cached crypto prices:', cryptoPricesCache);
|
||||
logger.debug('Using cached crypto prices');
|
||||
return cryptoPricesCache;
|
||||
}
|
||||
|
||||
// Если кеш устарел, запрашиваем новые данные
|
||||
for (const api of cryptoPriceAPIs) {
|
||||
try {
|
||||
console.log(`[DEBUG] Trying to fetch prices from ${api.name}...`);
|
||||
logger.debug({ apiName: api.name }, 'Trying to fetch prices');
|
||||
let data;
|
||||
if (api.name === 'Binance') {
|
||||
data = await api.parser(api.urls);
|
||||
@@ -109,7 +110,7 @@ export default class WalletUtils {
|
||||
const response = await axios.get(api.url);
|
||||
data = api.parser(response.data);
|
||||
}
|
||||
console.log(`[DEBUG] Successfully fetched prices from ${api.name}:`, data);
|
||||
logger.debug({ apiName: api.name }, 'Successfully fetched prices');
|
||||
|
||||
// Обновляем кеш
|
||||
cryptoPricesCache = data;
|
||||
@@ -118,17 +119,17 @@ export default class WalletUtils {
|
||||
return data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 429) {
|
||||
console.log(`[DEBUG] Rate limit exceeded on ${api.name}. Retrying after 2 seconds...`);
|
||||
logger.debug({ apiName: api.name }, 'Rate limit exceeded, retrying after 2 seconds');
|
||||
await sleep(2000);
|
||||
continue; // Пробуем снова с тем же API
|
||||
} else {
|
||||
console.error(`[DEBUG] Error fetching prices from ${api.name}:`, error.message);
|
||||
logger.error({ err: error, apiName: api.name }, 'Error fetching prices');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Если все API не сработали, используем fallback-значения
|
||||
console.error('[DEBUG] All APIs failed. Using fallback prices.');
|
||||
logger.error('All APIs failed. Using fallback prices.');
|
||||
cryptoPricesCache = {
|
||||
btc: 0, ltc: 0, eth: 0, usdt: 1, usdc: 1
|
||||
};
|
||||
@@ -139,17 +140,17 @@ export default class WalletUtils {
|
||||
|
||||
async fetchApiRequest(url) {
|
||||
try {
|
||||
console.log(`[DEBUG] Fetching data from: ${url}`); // Логируем URL запроса
|
||||
logger.debug({ url }, 'Fetching data');
|
||||
const response = await axios.get(url);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching data from ${url}:`, error);
|
||||
logger.error({ err: error, url }, 'Error fetching data');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchRpcRequest(method, params) {
|
||||
console.log(`[DEBUG] fetchRpcRequest called with method: ${method}, params: ${JSON.stringify(params)}`); // Логируем вызов метода
|
||||
logger.debug({ method, params }, 'fetchRpcRequest called');
|
||||
|
||||
const results = [];
|
||||
for (const node of rpcNodes) {
|
||||
@@ -163,12 +164,12 @@ export default class WalletUtils {
|
||||
|
||||
if (response.data && response.data.result) {
|
||||
results.push(response.data.result);
|
||||
console.log(`Запрос успешно выполнен на узле ${node}`); // Логируем успешный запрос
|
||||
logger.debug({ node }, 'Request successful');
|
||||
} else {
|
||||
console.warn(`Некорректный ответ от узла ${node}`); // Логируем некорректный ответ
|
||||
logger.warn({ node }, 'Invalid response');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Ошибка на узле ${node}: ${error.message}`); // Логируем ошибку
|
||||
logger.error({ err: error, node }, 'Node error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,68 +179,68 @@ export default class WalletUtils {
|
||||
|
||||
const uniqueResults = [...new Set(results)];
|
||||
if (uniqueResults.length === 1) {
|
||||
console.log("Баланс совпадает на всех узлах:", uniqueResults[0]); // Логируем совпадение балансов
|
||||
logger.debug('Balance matches across all nodes');
|
||||
return uniqueResults[0];
|
||||
} else {
|
||||
console.warn("Результаты отличаются на некоторых узлах. Возвращаем первый результат."); // Логируем различия
|
||||
logger.warn('Results differ across nodes. Returning first result.');
|
||||
return results[0];
|
||||
}
|
||||
}
|
||||
|
||||
async getBtcBalance() {
|
||||
if (!this.btcAddress) {
|
||||
console.log('[DEBUG] BTC address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||
logger.debug('BTC address is not provided, skipping balance check');
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
const url = `https://blockchain.info/balance?active=${this.btcAddress}`;
|
||||
console.log(`[DEBUG] Fetching BTC balance from: ${url}`); // Логируем URL запроса
|
||||
logger.debug({ url }, 'Fetching BTC balance');
|
||||
const data = await this.fetchApiRequest(url);
|
||||
return data?.[this.btcAddress]?.final_balance / 100000000 || 0;
|
||||
} catch (error) {
|
||||
console.error('Error getting BTC balance:', error);
|
||||
logger.error({ err: error }, 'Error getting BTC balance');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getLtcBalance() {
|
||||
if (!this.ltcAddress) {
|
||||
console.log('[DEBUG] LTC address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||
logger.debug('LTC address is not provided, skipping balance check');
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
const url = `https://api.blockcypher.com/v1/ltc/main/addrs/${this.ltcAddress}/balance`;
|
||||
console.log(`[DEBUG] Fetching LTC balance from: ${url}`); // Логируем URL запроса
|
||||
logger.debug({ url }, 'Fetching LTC balance');
|
||||
const data = await this.fetchApiRequest(url);
|
||||
return data?.balance / 100000000 || 0;
|
||||
} catch (error) {
|
||||
console.error('Error getting LTC balance:', error);
|
||||
logger.error({ err: error }, 'Error getting LTC balance');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getEthBalance() {
|
||||
if (!this.ethAddress) {
|
||||
console.log('[DEBUG] ETH address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||
logger.debug('ETH address is not provided, skipping balance check');
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
console.log(`[DEBUG] Fetching ETH balance for address: ${this.ethAddress}`); // Логируем адрес
|
||||
logger.debug({ addressPrefix: this.ethAddress.slice(0, 6) + '...' }, 'Fetching ETH balance');
|
||||
const balanceHex = await this.fetchRpcRequest("eth_getBalance", [this.ethAddress, "latest"]);
|
||||
return parseInt(balanceHex, 16) / 1e18;
|
||||
} catch (error) {
|
||||
console.error('Error getting ETH balance:', error);
|
||||
logger.error({ err: error }, 'Error getting ETH balance');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getUsdtErc20Balance() {
|
||||
if (!this.usdtAddress) {
|
||||
console.log('[DEBUG] USDT address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||
logger.debug('USDT address is not provided, skipping balance check');
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
console.log(`[DEBUG] Fetching USDT ERC-20 balance for address: ${this.usdtAddress}`); // Логируем адрес
|
||||
logger.debug({ addressPrefix: this.usdtAddress.slice(0, 6) + '...' }, 'Fetching USDT ERC-20 balance');
|
||||
const balanceHex = await this.fetchRpcRequest("eth_call", [
|
||||
{
|
||||
to: "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
||||
@@ -249,18 +250,18 @@ export default class WalletUtils {
|
||||
]);
|
||||
return parseInt(balanceHex, 16) / 1e6;
|
||||
} catch (error) {
|
||||
console.error('Error getting USDT ERC-20 balance:', error);
|
||||
logger.error({ err: error }, 'Error getting USDT ERC-20 balance');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async getUsdcErc20Balance() {
|
||||
if (!this.usdcAddress) {
|
||||
console.log('[DEBUG] USDC address is not provided, skipping balance check.'); // Логируем отсутствие адреса
|
||||
logger.debug('USDC address is not provided, skipping balance check');
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
console.log(`[DEBUG] Fetching USDC ERC-20 balance for address: ${this.usdcAddress}`); // Логируем адрес
|
||||
logger.debug({ addressPrefix: this.usdcAddress.slice(0, 6) + '...' }, 'Fetching USDC ERC-20 balance');
|
||||
const balanceHex = await this.fetchRpcRequest("eth_call", [
|
||||
{
|
||||
to: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||
@@ -270,7 +271,7 @@ export default class WalletUtils {
|
||||
]);
|
||||
return parseInt(balanceHex, 16) / 1e6;
|
||||
} catch (error) {
|
||||
console.error('Error getting USDC ERC-20 balance:', error);
|
||||
logger.error({ err: error }, 'Error getting USDC ERC-20 balance');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -310,7 +311,7 @@ export default class WalletUtils {
|
||||
}
|
||||
|
||||
async getAllBalancesExt() {
|
||||
console.log('[DEBUG] getAllBalancesExt called'); // Логируем вызов метода
|
||||
logger.debug('getAllBalancesExt called');
|
||||
|
||||
const [
|
||||
btcBalance,
|
||||
@@ -328,14 +329,7 @@ export default class WalletUtils {
|
||||
WalletUtils.getCryptoPrices()
|
||||
]);
|
||||
|
||||
console.log('[DEBUG] Balances fetched:', { // Логируем полученные балансы
|
||||
btcBalance,
|
||||
ltcBalance,
|
||||
ethBalance,
|
||||
usdtErc20Balance,
|
||||
usdcErc20Balance,
|
||||
prices
|
||||
});
|
||||
logger.debug({ btcBalance, ltcBalance, ethBalance, usdtErc20Balance, usdcErc20Balance }, 'Balances fetched');
|
||||
|
||||
return {
|
||||
BTC: { amount: btcBalance, usdValue: btcBalance * prices.btc },
|
||||
|
||||
Reference in New Issue
Block a user