Phiên bản: 2.5.0
Cập nhật lần cuối: 26/03/2026
Hệ thống logging của Tools V2 được xây dựng hoàn toàn tùy chỉnh, không dùng thư viện bên ngoài như Winston hay Pino. Toàn bộ nằm trong src/utils/logger/ với bốn thành phần chính:
CoreLogger: Nền móng xử lý output streams, quản lý file và console.LogFormatter: Định dạng message thành chuỗi có cấu trúc.ErrorCategorizer: Phân loại lỗi tự động theo nhóm và loại.StructuredLogger: Cung cấp methods log có cấu trúc cho API calls, webhooks, GPT prompts.Entry point là src/utils/logger/index.ts, export singleton logger và class Logger.
// Dùng trong toàn bộ codebase
import { logger } from "@/utils/logger";
import { logger } from "@/utils"; // Cũng hoạt động qua barrel export
// Log đơn giản
logger.info("Server started", { port: 3005 });
logger.error("Task failed", { taskId: "abc" }, error);
// Child logger với service name
const _logger = logger.child("task-group");
_logger.info("Task started", { taskId: "abc" });
// Output: [timestamp] [LOG_LEVEL_INFO] Task started | LOG_DATA: {"taskId":"abc"}
// File: storage/log/task-group/YYYY-MM-DD/info.log
Mỗi class tạo child logger với tên service riêng:
class CronTaskGroup {
private _logger = logger.child("task-group-cron");
}
class ProxyService {
private _logger = logger.child("proxy");
}
class ReportTaskCron {
private readonly logger = logger.child("report-task-cron");
}
Child logger ghi log vào thư mục riêng: storage/log/{service-name}/YYYY-MM-DD/.
CoreLogger được khởi tạo với service name và options:
const coreLogger = new CoreLogger("task-group", {
level: "info",
writeToFile: true,
writeToConsole: true,
colorized: true,
});
Options được đọc từ biến môi trường nếu không được truyền vào:
level: Từ LOG_LEVEL, mặc định info.timestamp: Từ LOG_TIMESTAMP, mặc định false.colorized: Từ LOG_COLORIZED, mặc định false.writeToFile: Từ LOG_WRITE_TO_FILE, mặc định false.writeToConsole: Từ LOG_WRITE_TO_CONSOLE, mặc định true.logDir: Từ LOG_DIR, mặc định storage/log.Đây là điểm đặc biệt của CoreLogger: không phải tất cả log đều được in ra console. Chỉ các log khớp với whitelist mới được in:
const isCounterLog = msgLower.includes("[success]") ||
msgLower.includes("[fail]") ||
msgLower.includes("total success") ||
msgLower.includes("total fails");
const isProxyLog = msgLower.includes("assigned zingproxy") ||
msgLower.includes("proxy assigned") ||
msgLower.includes("using existing proxy") ||
msgLower.includes("browser started") ||
msgLower.includes("[proxy_check]") ||
msgLower.includes("[debug]") ||
msgLower.includes("[api_info]") ||
msgLower.includes("cron reached limit") ||
msgLower.includes("task allocation");
Tất cả log khác chỉ được ghi vào file, không in ra console. Điều này giúp console không bị ngập bởi log không quan trọng trong production.
Log được ghi vào file theo cấu trúc:
storage/log/{service}/{YYYY-MM-DD}/{level}.log
Ví dụ:
storage/log/task-group-cron/2026-03-26/info.log
storage/log/task-group-cron/2026-03-26/error.log
storage/log/proxy/2026-03-26/warn.log
Mỗi level có file riêng. Không có file combined.log — mỗi level được tách biệt.
Trong development (NODE_ENV=development), logger còn ghi thêm vào logs.txt ở thư mục gốc:
storage/log/.private isLevelEnabled(level: LogLevel): boolean {
const levels: LogLevel[] = ["error", "warn", "info", "debug"];
const configLevelIndex = levels.indexOf(this.options.level);
const logLevelIndex = levels.indexOf(level);
return logLevelIndex <= configLevelIndex;
}
Với LOG_LEVEL=info:
error → enabled (index 0 <= 2).warn → enabled (index 1 <= 2).info → enabled (index 2 <= 2).debug → disabled (index 3 > 2).Mỗi log entry có format:
[{ISO_TIMESTAMP}] [{LEVEL_MARKER}] {message} | LOG_ERROR_CATEGORY: {category} | LOG_ERROR_MESSAGE: {msg} | LOG_ERROR_CODE: {code} | LOG_DATA: {json}
Ví dụ thực tế:
[2026-03-26T09:30:00.000Z] [LOG_LEVEL_INFO] Task started | LOG_DATA: {"taskId":"abc123","type":"normalClick"}
[2026-03-26T09:30:05.000Z] [LOG_LEVEL_ERROR] Task failed | LOG_ERROR_CATEGORY: NETWORK:TIMEOUT:ETIMEDOUT | LOG_ERROR_MESSAGE: Navigation timeout | LOG_ERROR_CODE: ETIMEDOUT | LOG_DATA: {"taskId":"abc123"}
| Level | Marker |
|---|---|
error | [LOG_LEVEL_ERROR] |
warn | [LOG_LEVEL_WARN] |
info | [LOG_LEVEL_INFO] |
debug | [LOG_LEVEL_DEBUG] |
Context object được serialize thành JSON và append sau LOG_DATA::
logger.info("Proxy rotated", {
proxyId: "proxy-123",
newIp: "1.2.3.4",
region: "VN",
});
// Output: ... | LOG_DATA: {
// "proxyId": "proxy-123",
// "newIp": "1.2.3.4",
// "region": "VN"
// }
Nếu context không thể serialize (circular reference), ghi [Unable to serialize context].
ErrorCategorizer phân loại lỗi thành các nhóm:
| Group | Mô tả |
|---|---|
NETWORK | Lỗi kết nối mạng |
API | Lỗi từ API responses |
DATABASE | Lỗi MongoDB |
VALIDATION | Lỗi validation dữ liệu |
AUTHENTICATION | Lỗi xác thực |
EXTERNAL_SERVICE | Lỗi từ dịch vụ bên ngoài |
UNKNOWN | Không xác định được |
| Type | Điều kiện phát hiện | Retryable |
|---|---|---|
CONNECTION_REFUSED | ECONNREFUSED trong message hoặc code | Có |
TIMEOUT | ETIMEDOUT, ECONNRESET, "timeout" | Có |
NOT_FOUND | HTTP 404, "not found" | Không |
VALIDATION_FAILED | HTTP 400, "validation" | Không |
UNAUTHORIZED | HTTP 401, 403 | Không |
RATE_LIMIT | HTTP 429 | Có |
SERVER_ERROR | HTTP 5xx | Có |
CLIENT_ERROR | HTTP 4xx khác | Không |
PARSE_ERROR | "parse", "json" trong message | Không |
DUPLICATE_KEY | "E11000", "duplicate key" | Không |
UNKNOWN | Không khớp với bất kỳ điều kiện nào | Không |
ErrorCategorizer.formatCategory(category)
// Output: "NETWORK:TIMEOUT:ETIMEDOUT"
// Format: "{group}:{type}:{code}"
Ví dụ các category strings:
NETWORK:CONNECTION_REFUSED:ECONNREFUSEDNETWORK:TIMEOUT:ETIMEDOUTAPI:RATE_LIMIT:429API:SERVER_ERROR:500DATABASE:DUPLICATE_KEY:E11000AUTHENTICATION:UNAUTHORIZED:401retryable: true cho biết lỗi này có thể retry. Được dùng bởi các services để quyết định có retry không:
Dùng để log các API calls đến Go API Backend:
structuredLogger.logApiCall("info", "requestTasks", "REQUEST", {
url: "http://api-host/api/task/request",
method: "POST",
requestPayload: { count: 10 },
});
structuredLogger.logApiCall("info", "requestTasks", "SUCCESS", {
url: "http://api-host/api/task/request",
responsePayload: { tasks: [...] },
statusCode: 200,
});
structuredLogger.logApiCall("error", "requestTasks", "ERROR", {
url: "http://api-host/api/task/request",
error: new Error("Connection refused"),
});
Output format: API_CALL:{action}:{status} với đầy đủ context.
Dùng để log webhook calls:
structuredLogger.logWebhook("info", "updateTaskStatus", "REQUEST", {
url: "http://api-host/webhook/task/update-status",
requestPayload: { taskId: "abc", status: "completed" },
});
Output format: WEBHOOK:{action}:{status}.
Dùng để log OpenAI API calls:
structuredLogger.logGptPrompt("info", "generateReport", "SUCCESS", {
model: "gpt-4o-mini",
tokens: 1500,
cost: 0.0003,
response: "{ ... }",
});
Output format: GPT_PROMPT:{action}:{status} với tokens và cost.
SessionLogger trong src/utils/browser_manager/logger/session-logger.ts cung cấp methods log chuẩn hóa cho browser session events.
Được gọi khi một browser session mới được tạo:
SessionLogger.logBrowserStart(
session.id,
userAgent,
proxy,
geolocation,
profileInfo
);
Output ví dụ:
[2026-03-26T09:30:00.000Z] [LOG_LEVEL_INFO] Browser Started | LOG_DATA: {
"sessionId": "01JQXYZ...",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
"proxy": "http://1.2.3.4:8080",
"geolocation": "10.7769, 106.7009",
"profile": "Incognito (no profile)"
}
Log khi action bắt đầu và kết thúc:
SessionLogger.logActionStart(sessionId, taskId, "ads-flow", config);
SessionLogger.logActionResult(sessionId, taskId, "ads-flow", true, result);
SessionLogger.logActionResult(sessionId, taskId, "ads-flow", false, null, error);
storage/log/
├── app/ ← Default logger (service="app")
│ └── 2026-03-26/
│ ├── info.log
│ ├── warn.log
│ └── error.log
├── task-group-cron/ ← CronTaskGroup logger
│ └── 2026-03-26/
│ ├── info.log
│ ├── warn.log
│ └── error.log
├── task-group/ ← TaskGroupService logger
│ └── 2026-03-26/
│ └── info.log
├── report-task-cron/ ← ReportTaskCron logger
│ └── 2026-03-26/
│ ├── info.log
│ └── error.log
├── proxy/ ← ProxyService logger
│ └── 2026-03-26/
│ └── info.log
├── cron_proxy/ ← CronProxy logger
│ └── 2026-03-26/
│ ├── info.log
│ └── warn.log
├── browser_manager/ ← BrowserManager logger
│ └── 2026-03-26/
│ ├── info.log
│ └── error.log
├── monitor/ ← MonitorService logger
│ └── 2026-03-26/
│ └── info.log
├── human-behavior-service/ ← HumanBehaviorService logger
│ └── 2026-03-26/
│ └── debug.log
└── task-group-cleanup/ ← TaskGroupCleanupService logger
└── 2026-03-26/
└── info.log
info.log: Các sự kiện bình thường — task start/complete, proxy rotation, browser start.
warn.log: Cảnh báo — proxy chậm, retry attempts, stuck tasks được reset.
error.log: Lỗi nghiêm trọng — task failed, browser crash, API unreachable.
debug.log: Chi tiết kỹ thuật — cursor coordinates, network calls. Chỉ có khi LOG_LEVEL=debug.
Endpoint /logs cho phép đọc file log mà không cần SSH:
# Đọc error log hôm nay
curl "http://localhost:3005/logs?filePath=task-group-cron/2026-03-26/error.log"
# Đọc info log của proxy cron
curl "http://localhost:3005/logs?filePath=cron_proxy/2026-03-26/info.log"
# Đọc warn log của browser manager
curl "http://localhost:3005/logs?filePath=browser_manager/2026-03-26/warn.log"
Trả về 404 nếu file không tồn tại.
Endpoint quan trọng nhất để giám sát worker:
curl http://localhost:3005/
Response:
{
"name": "Traffic tool API",
"version": "2.5.0",
"environment": "production",
"browserSessionRunning": 15,
"task": {
"maxProcess": 25,
"totalProcessRunning": 12
},
"proxy": {
"maxProcess": 5,
"totalProcessRunning": 3,
"processRunning": ["proxy-id-1", "proxy-id-2", "proxy-id-3"]
}
}
Các chỉ số cần theo dõi:
browserSessionRunning: Số Chrome instances đang chạy. Nếu bằng BROWSER_LIMIT_PER_PROCESS liên tục, worker đang full capacity.task.totalProcessRunning: Số task đang thực thi. Nếu luôn bằng maxProcess, worker đang full.task.totalProcessRunning = 0: Không có task nào đang chạy — có thể là vấn đề.proxy.totalProcessRunning: Số proxy đang được xử lý.Thêm header Show-Env: true để xem toàn bộ env config (chỉ trong development):
curl -H "Show-Env: true" http://localhost:3005/
curl http://localhost:3005/health
Response:
{
"status": "ok",
"time": "2026-03-26T09:30:00.000Z",
"uptime": 3600.5
}
status: Luôn là "ok" nếu server đang chạy.time: Thời điểm hiện tại theo ISO 8601.uptime: Số giây server đã chạy.Endpoint này được Go API Backend gọi định kỳ để kiểm tra worker còn sống không.
Endpoint cung cấp thông tin chi tiết về tài nguyên hệ thống:
curl http://localhost:3005/api/monitor/
Response:
{
"ip": "192.168.1.100",
"cpu": {
"usage": 45.5,
"free": 54.5,
"cores": 8,
"threads": 8
},
"ram": {
"available": 8192,
"used": 4096,
"total": 16384,
"free": 4096,
"used_percent": 50.0,
"unit": "MB"
},
"disk": {
"total": 500,
"used": 250,
"free": 250,
"used_percent": 50.0,
"unit": "GB"
},
"browser": {
"total_session": 100,
"used_session": 15,
"browser_limit_per_session": 28
},
"cron_task_limit": 25,
"task_traffic_limit": 50,
"cron_search_limit": 1,
"search_task_limit": 1000
}
Các chỉ số quan trọng:
ram.used_percent: Nếu > 85%, cần giảm BROWSER_LIMIT_PER_PROCESS.cpu.usage: Nếu > 90% liên tục, server đang quá tải.browser.used_session: Số Chrome instances đang chạy.disk.used_percent: Nếu > 80%, cần dọn dẹp log files và screenshots.# Cú pháp
curl "http://localhost:3005/logs?filePath={service}/{date}/{level}.log"
# Ví dụ
curl "http://localhost:3005/logs?filePath=task-group-cron/2026-03-26/error.log"
curl "http://localhost:3005/logs?filePath=cron_proxy/2026-03-26/warn.log"
curl "http://localhost:3005/logs?filePath=browser_manager/2026-03-26/error.log"
Trả về nội dung file log dạng plain text. Trả về 404 nếu file không tồn tại.
Khi server khởi động thành công, các log sau xuất hiện:
[INFO] Deleted user-data folder
[INFO] Server started on port 3005
[INFO] Swagger documentation available at http://localhost:3005/swagger
[INFO] Connected to MongoDB
[INFO] All task groups set to pending successfully
Nếu thiếu bất kỳ log nào, có vấn đề với cấu hình.
Khi task được thực thi:
[INFO] Starting 5 tasks (20 pending, 10 running, 25 max). Allocation: google_ads_click=3, kill_flow=2, home_traffic=0, coccoc_ads_click=0. Running: google_ads_click=5, kill_flow=3, home_traffic=2, coccoc_ads_click=0
[INFO] [task-id-123] Task started (11/25 running)
[INFO] Browser Started | LOG_DATA: {"sessionId":"...","proxy":"http://1.2.3.4:8080",...}
[INFO] Action Started | LOG_DATA: {"taskId":"task-id-123","actionType":"ads-flow",...}
[INFO] Action Completed | LOG_DATA: {"taskId":"task-id-123","result":"Success"}
Khi proxy được rotate:
[INFO] [proxy-id-456] Proxy rotation successful | LOG_DATA: {"new_ip":"5.6.7.8","new_port":8080,"region":"VN"}
[INFO] [proxy-id-456] IP check successful | LOG_DATA: {"new_public_ip":"5.6.7.8"}
[INFO] [proxy-id-456] Calling webhook | LOG_DATA: {"webhook_url":"...","proxy_ip":"5.6.7.8"}
[INFO] [proxy-id-456] Webhook called successfully, proxy deleted
[WARN] Found 3 stuck PROCESSING tasks, resetting to PENDING
[WARN] [proxy-id-789] Proxy rotation failed | LOG_DATA: {"retry_count":2,"max_retries":15}
[WARN] [proxy-id-789] Continuing with existing proxy despite rotation failure
[WARN] Cron reached limit (25/25 running). Running types: normalClick=10, killTask=10, homeTraffic=5, coccoc=0
[ERROR] [task-id-123] Task failed | LOG_ERROR_CATEGORY: NETWORK:TIMEOUT:ETIMEDOUT | LOG_ERROR_MESSAGE: Navigation timeout of 30000 ms exceeded
[ERROR] [proxy-id-789] Proxy deleted after 15 failed rotation attempts
[ERROR] Error in cron start | LOG_ERROR_CATEGORY: DATABASE:CONNECTION_REFUSED:ECONNREFUSED
[ERROR] Report task execution failed | LOG_DATA: {"taskId":"...","platform":"GOOGLE_SAFE_BROWSING","error":"CAPTCHA not solved"}
Để theo dõi số task được xử lý theo thời gian:
# Đếm số task completed trong ngày hôm nay
grep "Action Completed" storage/log/task-group-cron/$(date +%Y-%m-%d)/info.log | wc -l
# Đếm số task failed
grep "Action Failed" storage/log/task-group-cron/$(date +%Y-%m-%d)/error.log | wc -l
# Xem 50 dòng log cuối
tail -50 storage/log/task-group-cron/$(date +%Y-%m-%d)/info.log
Hoặc qua API:
curl "http://localhost:3005/logs?filePath=task-group-cron/$(date +%Y-%m-%d)/info.log"
# Xem proxy rotation failures
grep "rotation failed" storage/log/cron_proxy/$(date +%Y-%m-%d)/warn.log
# Xem proxy deletions
grep "Proxy deleted" storage/log/cron_proxy/$(date +%Y-%m-%d)/info.log | wc -l
# Xem số browser sessions được tạo
grep "Browser Started" storage/log/browser_manager/$(date +%Y-%m-%d)/info.log | wc -l
# Xem browser crashes
grep "Error" storage/log/browser_manager/$(date +%Y-%m-%d)/error.log
# Xem report tasks completed
grep "COMPLETED" storage/log/report-task-cron/$(date +%Y-%m-%d)/info.log | wc -l
# Xem report tasks failed
grep "FAILED" storage/log/report-task-cron/$(date +%Y-%m-%d)/error.log | wc -l
# Xem CAPTCHA issues
grep "captcha" storage/log/report-task-cron/$(date +%Y-%m-%d)/error.log
Dấu hiệu: task.totalProcessRunning = 0 trong GET / response kéo dài.
Kiểm tra:
# Xem log task request
curl "http://localhost:3005/logs?filePath=task-group-cron/$(date +%Y-%m-%d)/info.log" | grep "requestTasksFromAPI"
# Kiểm tra kết nối Go API
curl $API_HOST/health
Nguyên nhân thường gặp:
API_KEY không khớp.task.totalProcessRunning).Dấu hiệu: Nhiều [ERROR] trong log, task.totalProcessRunning thấp hơn bình thường.
Kiểm tra:
# Xem error log
curl "http://localhost:3005/logs?filePath=task-group-cron/$(date +%Y-%m-%d)/error.log"
# Xem error category phổ biến nhất
curl "http://localhost:3005/logs?filePath=task-group-cron/$(date +%Y-%m-%d)/error.log" | grep "LOG_ERROR_CATEGORY"
Nguyên nhân theo error category:
NETWORK:TIMEOUT: Proxy chậm hoặc trang web không load.NETWORK:CONNECTION_REFUSED: Proxy bị block hoặc chết.API:SERVER_ERROR: Go API đang có vấn đề.UNKNOWN: Cần xem chi tiết error message.Dấu hiệu: ram.used_percent > 85% trong /api/monitor/.
Kiểm tra:
# Xem số browser sessions
curl http://localhost:3005/ | grep "browserSessionRunning"
# Xem browser manager log
curl "http://localhost:3005/logs?filePath=browser_manager/$(date +%Y-%m-%d)/error.log"
Hành động:
BROWSER_LIMIT_PER_PROCESS trong .env.Dấu hiệu: disk.used_percent > 80% trong /api/monitor/.
Kiểm tra:
# Xem kích thước thư mục log
du -sh storage/log/
# Xem kích thước screenshots
du -sh storage/screenshots/
Hành động:
# Xóa log cũ hơn 7 ngày
find storage/log/ -type f -name "*.log" -mtime +7 -delete
# Xóa thư mục log cũ hơn 30 ngày
find storage/log/ -type d -mtime +30 -empty -delete
# Xóa screenshots cũ hơn 30 ngày
find storage/screenshots/ -type f -mtime +30 -delete
Hiện tại Tools V2 không có cron job tự động xóa log files. Cần setup cron job trên server:
# Thêm vào crontab
0 2 * * * find /path/to/tools_v2/storage/log/ -type f -name "*.log" -mtime +30 -delete
0 3 * * * find /path/to/tools_v2/storage/screenshots/ -type f -mtime +90 -delete
Hiện tại không có log rotation tự động. Mỗi ngày tạo thư mục mới, nhưng file trong ngày không bị rotate. Nếu một ngày có quá nhiều log, file có thể lớn.
Để giới hạn kích thước, có thể dùng logrotate trên Linux:
/path/to/tools_v2/storage/log/*/*/*/*.log {
daily
rotate 30
compress
missingok
notifempty
}
Go API Backend định kỳ gọi GET /health của mỗi worker để kiểm tra còn sống không. Nếu worker không phản hồi trong thời gian quy định, Go API đánh dấu worker offline và ngừng phân phối tasks.
Go API cũng gọi GET /api/monitor/ để lấy thông tin tài nguyên của worker. Thông tin này được hiển thị trên Admin Dashboard trong màn hình Server Management.
Các chỉ số được hiển thị:
Admin Dashboard có màn hình Server Performance hiển thị:
Dữ liệu này được Go API thu thập từ webhook callbacks của mỗi task.
Quy trình debug khi task thất bại:
curl "http://localhost:3005/logs?filePath=task-group-cron/$(date +%Y-%m-%d)/error.log" | grep "task-id-here"
# Kiểm tra browser manager error log
curl "http://localhost:3005/logs?filePath=browser_manager/$(date +%Y-%m-%d)/error.log"
# Kiểm tra số sessions hiện tại
curl http://localhost:3005/ | grep "browserSessionRunning"
# Kiểm tra Chromium dependencies
google-chrome --version 2>&1 || chromium-browser --version 2>&1
# Xem proxy rotation log
curl "http://localhost:3005/logs?filePath=cron_proxy/$(date +%Y-%m-%d)/warn.log"
# Xem proxy deletion log
curl "http://localhost:3005/logs?filePath=cron_proxy/$(date +%Y-%m-%d)/info.log" | grep "deleted"
# Kiểm tra proxy count
curl http://localhost:3005/ | grep "proxy"
# Xem report task error log
curl "http://localhost:3005/logs?filePath=report-task-cron/$(date +%Y-%m-%d)/error.log"
# Tìm task cụ thể
curl "http://localhost:3005/logs?filePath=report-task-cron/$(date +%Y-%m-%d)/error.log" | grep "task-id-here"
Để xem log chi tiết hơn, tạm thời bật debug mode:
# Trong .env
LOG_LEVEL=debug
# Restart server
# Sau khi debug xong, đổi lại LOG_LEVEL=info
Debug mode log thêm:
| Endpoint | Mục đích | Tần suất kiểm tra |
|---|---|---|
GET /health | Kiểm tra server còn sống | Mỗi 30 giây (từ Go API) |
GET / | Trạng thái tổng quan: tasks, browsers, proxies | Khi cần kiểm tra |
GET /api/monitor/ | CPU, RAM, Disk, Browser metrics | Mỗi 5 phút |
GET /logs?filePath=... | Đọc log files từ xa | Khi debug |
Tất cả endpoints đều yêu cầu IP trong IP_ALLOWED list. Endpoint / và /health không yêu cầu authentication. Endpoint /api/monitor/ và /logs yêu cầu IP whitelist.