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:
NW
2026-06-22 01:42:47 +01:00
parent ba80784ae7
commit ce1b6003cb
49 changed files with 5624 additions and 6079 deletions

View File

@@ -1,4 +1,5 @@
import db from "../config/database.js";
import logger from "../utils/logger.js";
class CategoryService {
static async getCategoriesByLocationId(locationId) {
@@ -9,7 +10,7 @@ class CategoryService {
);
return categories;
} catch (error) {
console.error('Error fetching categories by location ID:', error);
logger.error({ err: error }, 'Error fetching categories by location ID');
throw new Error('Failed to fetch categories');
}
}
@@ -29,7 +30,7 @@ class CategoryService {
);
return category;
} catch (error) {
console.error('Error fetching category by ID:', error);
logger.error({ err: error }, 'Error fetching category by ID');
throw new Error('Failed to fetch category');
}
}

View File

@@ -1,4 +1,5 @@
import db from "../config/database.js";
import logger from "../utils/logger.js";
class LocationService {
static async getCountries() {
@@ -27,7 +28,7 @@ class LocationService {
);
return location;
} catch (error) {
console.error('Error fetching location:', error);
logger.error({ err: error }, 'Error fetching location');
throw new Error('Failed to fetch location');
}
}
@@ -40,7 +41,7 @@ class LocationService {
);
return location;
} catch (error) {
console.error('Error fetching location by ID:', error);
logger.error({ err: error }, 'Error fetching location by ID');
throw new Error('Failed to fetch location');
}
}

View File

@@ -2,6 +2,7 @@
import db from "../config/database.js";
import Validators from "../utils/validators.js";
import logger from "../utils/logger.js";
class ProductService {
static async getProductById(productId) {
@@ -11,7 +12,7 @@ class ProductService {
try {
return await db.getAsync(`SELECT * FROM products WHERE id = ?`, [productId]);
} catch (error) {
console.error('Error get product:', error);
logger.error({ err: error }, 'Error get product');
throw error;
}
}
@@ -70,7 +71,7 @@ class ProductService {
[quantity, productId]
);
} catch (error) {
console.error('Error decreasing product quantity:', error);
logger.error({ err: error }, 'Error decreasing product quantity');
throw new Error('Failed to update product quantity');
}
}

View File

@@ -2,6 +2,7 @@
import db from "../config/database.js";
import crypto from "crypto";
import logger from "../utils/logger.js";
class PurchaseService {
static async getPurchasesByUserId(userId, limit, offset) {
@@ -24,7 +25,7 @@ class PurchaseService {
`, [userId, limit, offset]);
} catch (error) {
console.error('Error get purchases:', error);
logger.error({ err: error }, 'Error get purchases');
throw error;
}
}
@@ -36,7 +37,7 @@ class PurchaseService {
[purchaseId]
);
} catch (error) {
console.error('Error getting purchase by ID:', error);
logger.error({ err: error }, 'Error getting purchase by ID');
throw error;
}
}
@@ -113,7 +114,7 @@ class PurchaseService {
return result.lastID;
} catch (error) {
try { await db.runAsync('ROLLBACK'); } catch (_) {}
console.error('Error creating purchase:', error);
logger.error({ err: error }, 'Error creating purchase');
throw error;
}
}
@@ -152,7 +153,7 @@ class PurchaseService {
await db.runAsync('COMMIT');
} catch (error) {
try { await db.runAsync('ROLLBACK'); } catch (_) {}
console.error('Error updating purchase status:', error);
logger.error({ err: error }, 'Error updating purchase status');
throw new Error('Failed to update purchase status');
}
}
@@ -165,7 +166,7 @@ class PurchaseService {
);
return total.total;
} catch (error) {
console.error('Error fetching total purchases by user ID:', error);
logger.error({ err: error }, 'Error fetching total purchases by user ID');
throw new Error('Failed to fetch total purchases');
}
}

View File

@@ -3,6 +3,7 @@
import db from "../config/database.js";
import Wallet from "../models/Wallet.js";
import WalletUtils from "../utils/walletUtils.js";
import logger from "../utils/logger.js";
const ALLOWED_USER_FIELDS = new Set([
'telegram_id', 'username', 'country', 'city',
@@ -35,7 +36,6 @@ class UserService {
try {
// Нормализуем и валидируем telegram_id
const normalizedTelegramId = this.normalizeTelegramId(userData?.telegram_id);
// console.log("Normalized telegram_id:", normalizedTelegramId); // Отладочный вывод
this.validateTelegramId(normalizedTelegramId);
// Обновляем значение telegram_id в объекте userData
@@ -44,7 +44,7 @@ class UserService {
// Проверяем, существует ли пользователь с таким telegram_id
const existingUser = await this.getUserByTelegramId(normalizedTelegramId);
if (existingUser) {
console.log("User already exists with telegram_id:", normalizedTelegramId);
logger.info({ telegramId: normalizedTelegramId }, 'User already exists');
return existingUser.id;
}
@@ -70,7 +70,7 @@ class UserService {
return result.lastID;
} catch (error) {
await db.runAsync('ROLLBACK');
console.error('Error creating user:', error);
logger.error({ err: error }, 'Error creating user');
throw error;
}
}
@@ -82,7 +82,7 @@ class UserService {
[String(userId)]
);
} catch (error) {
console.error('Error getting user:', error);
logger.error({ err: error }, 'Error getting user');
throw error;
}
}
@@ -95,7 +95,7 @@ class UserService {
[normalizedTelegramId]
);
} catch (error) {
console.error('Error getting user:', error);
logger.error({ err: error }, 'Error getting user');
throw error;
}
}
@@ -120,7 +120,7 @@ class UserService {
GROUP BY u.id
`, [normalizedTelegramId]);
} catch (error) {
console.error('Error getting user stats:', error);
logger.error({ err: error }, 'Error getting user stats');
throw error;
}
}
@@ -169,9 +169,9 @@ class UserService {
[remainingBalance, user.id]
);
console.log(`[DEBUG] Updated total_balance for user ${user.id}: ${remainingBalance}`);
logger.debug({ userId: user.id, remainingBalance }, 'Updated total_balance');
} catch (error) {
console.error('Error recalculating user balance:', error);
logger.error({ err: error }, 'Error recalculating user balance');
throw error;
}
}
@@ -196,7 +196,7 @@ class UserService {
await db.runAsync('COMMIT');
} catch (e) {
await db.runAsync("ROLLBACK");
console.error('Error deleting user:', e);
logger.error({ err: e }, 'Error deleting user');
throw e;
}
}
@@ -211,7 +211,7 @@ class UserService {
// Возвращаем сумму доступного крипто-баланса и бонусного баланса
return user.total_balance + user.bonus_balance;
} catch (error) {
console.error('Error getting user balance:', error);
logger.error({ err: error }, 'Error getting user balance');
throw error;
}
}

View File

@@ -5,6 +5,7 @@ import config from "../config/config.js";
import WalletUtils from "../utils/walletUtils.js";
import WalletGenerator from "../utils/walletGenerator.js";
import { encrypt, decrypt } from '../utils/encryption.js';
import logger from '../utils/logger.js';
class WalletService {
static async getArchivedWalletsCount(user) {
@@ -17,7 +18,7 @@ class WalletService {
);
return archivedWallets.total;
} catch (error) {
console.error('Error fetching archived wallets count:', error);
logger.error({ err: error }, 'Error fetching archived wallets count');
throw new Error('Failed to fetch archived wallets count');
}
}
@@ -41,7 +42,7 @@ class WalletService {
return totalBalance;
} catch (error) {
console.error('Error fetching active wallets balance:', error);
logger.error({ err: error }, 'Error fetching active wallets balance');
throw new Error('Failed to fetch active wallets balance');
}
}
@@ -65,7 +66,7 @@ class WalletService {
return totalBalance;
} catch (error) {
console.error('Error fetching archived wallets balance:', error);
logger.error({ err: error }, 'Error fetching archived wallets balance');
throw new Error('Failed to fetch archived wallets balance');
}
}
@@ -82,7 +83,7 @@ class WalletService {
return wallets;
} catch (error) {
console.error('Error fetching wallets by type:', error);
logger.error({ err: error }, 'Error fetching wallets by type');
throw new Error('Failed to fetch wallets by type');
}
}
@@ -128,11 +129,7 @@ class WalletService {
// Получаем адрес для базового типа
const walletData = wallets[baseType.toUpperCase()];
if (!walletData || !walletData.address) {
console.error('Wallet generation failed:', {
baseType,
wallets: Object.keys(wallets),
walletData
});
logger.error({ baseType, walletKeys: Object.keys(wallets) }, 'Wallet generation failed');
throw new Error('Failed to generate wallet address');
}
const address = walletData.address;
@@ -159,7 +156,7 @@ class WalletService {
// Проверяем целостность записанной мнемоники
const decryptedMnemonic = decrypt(insertedWallet.mnemonic, userId);
if (decryptedMnemonic !== mnemonic) {
console.error('Mnemonic verification failed for wallet:', walletType);
logger.error({ walletType }, 'Mnemonic verification failed');
await db.runAsync(
`DELETE FROM crypto_wallets
WHERE user_id = ? AND wallet_type = ?`,
@@ -168,12 +165,7 @@ class WalletService {
throw new Error('Mnemonic verification failed');
}
console.log(`Successfully created and verified wallet: ${walletType}`, {
address,
derivationPath,
userId,
walletType
});
logger.info({ walletType, address, derivationPath, userId }, 'Successfully created and verified wallet');
return {
address,
derivationPath,
@@ -182,7 +174,7 @@ class WalletService {
};
} catch (error) {
console.error('Error creating wallet:', error);
logger.error({ err: error }, 'Error creating wallet');
throw new Error('Failed to create wallet: ' + error.message);
}
}
@@ -194,7 +186,7 @@ class WalletService {
}
return decrypt(encryptedMnemonic, userId);
} catch (error) {
console.error('Error decrypting mnemonic:', error);
logger.error({ err: error }, 'Error decrypting mnemonic');
throw new Error('Failed to decrypt mnemonic: ' + error.message);
}
}