484 lines
12 KiB
Go
484 lines
12 KiB
Go
package logger
|
||
|
||
import (
|
||
"compress/gzip"
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
"path/filepath"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
const (
|
||
MaxFileSize = 1 * 1024 * 1024 // 1MB
|
||
LogDir = "logs"
|
||
)
|
||
|
||
// TransactionLogger 交易日志记录器
|
||
type TransactionLogger struct {
|
||
mu sync.Mutex
|
||
files map[string]*logFile // address -> logFile
|
||
logDir string
|
||
}
|
||
|
||
// logFile 单个日志文件
|
||
type logFile struct {
|
||
file *os.File
|
||
size int64
|
||
address string
|
||
logDir string
|
||
mu sync.Mutex
|
||
}
|
||
|
||
var (
|
||
txLogger *TransactionLogger
|
||
once sync.Once
|
||
)
|
||
|
||
// InitTransactionLogger 初始化交易日志系统
|
||
func InitTransactionLogger(logDir string) error {
|
||
var err error
|
||
once.Do(func() {
|
||
if logDir == "" {
|
||
logDir = LogDir
|
||
}
|
||
|
||
// 创建日志目录
|
||
err = os.MkdirAll(logDir, 0755)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
txLogger = &TransactionLogger{
|
||
files: make(map[string]*logFile),
|
||
logDir: logDir,
|
||
}
|
||
})
|
||
|
||
return err
|
||
}
|
||
|
||
// getOrCreateLogFile 获取或创建日志文件
|
||
func (tl *TransactionLogger) getOrCreateLogFile(address string) (*logFile, error) {
|
||
tl.mu.Lock()
|
||
defer tl.mu.Unlock()
|
||
|
||
// 如果已存在,返回现有的
|
||
if lf, exists := tl.files[address]; exists {
|
||
return lf, nil
|
||
}
|
||
|
||
// 创建新的日志文件
|
||
lf := &logFile{
|
||
address: address,
|
||
logDir: tl.logDir,
|
||
}
|
||
|
||
// 打开或创建文件
|
||
filePath := filepath.Join(tl.logDir, fmt.Sprintf("%s.log", address))
|
||
file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建日志文件失败: %w", err)
|
||
}
|
||
|
||
// 获取当前文件大小
|
||
info, err := file.Stat()
|
||
if err != nil {
|
||
file.Close()
|
||
return nil, fmt.Errorf("获取文件信息失败: %w", err)
|
||
}
|
||
|
||
lf.file = file
|
||
lf.size = info.Size()
|
||
tl.files[address] = lf
|
||
|
||
return lf, nil
|
||
}
|
||
|
||
// write 写入日志
|
||
func (lf *logFile) write(content string) error {
|
||
lf.mu.Lock()
|
||
defer lf.mu.Unlock()
|
||
|
||
// 检查是否需要轮转
|
||
if lf.size >= MaxFileSize {
|
||
if err := lf.rotate(); err != nil {
|
||
return fmt.Errorf("日志轮转失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 写入内容
|
||
n, err := lf.file.WriteString(content + "\n")
|
||
if err != nil {
|
||
return fmt.Errorf("写入日志失败: %w", err)
|
||
}
|
||
|
||
lf.size += int64(n)
|
||
|
||
// 立即刷新到磁盘
|
||
lf.file.Sync()
|
||
|
||
return nil
|
||
}
|
||
|
||
// rotate 日志轮转:压缩当前文件,创建新文件
|
||
func (lf *logFile) rotate() error {
|
||
// 关闭当前文件
|
||
if lf.file != nil {
|
||
lf.file.Close()
|
||
}
|
||
|
||
// 生成备份文件名(带时间戳)
|
||
timestamp := time.Now().Format("20060102_150405")
|
||
oldPath := filepath.Join(lf.logDir, fmt.Sprintf("%s.log", lf.address))
|
||
backupPath := filepath.Join(lf.logDir, fmt.Sprintf("%s_%s.log", lf.address, timestamp))
|
||
|
||
// 重命名当前文件
|
||
if err := os.Rename(oldPath, backupPath); err != nil {
|
||
return fmt.Errorf("重命名日志文件失败: %w", err)
|
||
}
|
||
|
||
// 压缩备份文件
|
||
go func() {
|
||
if err := compressFile(backupPath); err != nil {
|
||
fmt.Printf("⚠️ 压缩日志文件失败 %s: %v\n", backupPath, err)
|
||
} else {
|
||
// 删除原文件
|
||
os.Remove(backupPath)
|
||
}
|
||
}()
|
||
|
||
// 创建新文件
|
||
file, err := os.OpenFile(oldPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||
if err != nil {
|
||
return fmt.Errorf("创建新日志文件失败: %w", err)
|
||
}
|
||
|
||
lf.file = file
|
||
lf.size = 0
|
||
|
||
return nil
|
||
}
|
||
|
||
// compressFile 压缩文件
|
||
func compressFile(filePath string) error {
|
||
// 打开原文件
|
||
srcFile, err := os.Open(filePath)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer srcFile.Close()
|
||
|
||
// 创建压缩文件
|
||
gzPath := filePath + ".gz"
|
||
gzFile, err := os.Create(gzPath)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer gzFile.Close()
|
||
|
||
// 创建 gzip writer
|
||
gzWriter := gzip.NewWriter(gzFile)
|
||
defer gzWriter.Close()
|
||
|
||
// 复制数据
|
||
_, err = io.Copy(gzWriter, srcFile)
|
||
return err
|
||
}
|
||
|
||
// LogTopup 记录充值消息
|
||
func LogTopup(toAddress string, status string, amount float64, txHash string, blockHeight uint64) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(toAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||
content := fmt.Sprintf("%s [topup]-[%s] | 金额: %.6f | 交易哈希: %s | 区块高度: %d | ToAddress: %s",
|
||
timestamp, status, amount, txHash, blockHeight, toAddress)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogWithdraw 记录提现消息
|
||
func LogWithdraw(fromAddress string, status string, amount float64, toAddress string, txHash string, blockHeight uint64) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
// 使用 toAddress 作为文件名
|
||
lf, err := txLogger.getOrCreateLogFile(fromAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||
content := fmt.Sprintf("%s [提现]-[%s] | 金额: %.6f | FromAddress: %s | ToAddress: %s | 交易哈希: %s | 区块高度: %d",
|
||
timestamp, status, amount, fromAddress, toAddress, txHash, blockHeight)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogPay 记录支付消息
|
||
func LogPay(status string, fromAddress string, queueId string) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
// 使用 toAddress 作为文件名
|
||
lf, err := txLogger.getOrCreateLogFile(fromAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
// t, err := json.Marshal(transactions)
|
||
// if err != nil {
|
||
// fmt.Println("Error marshalling to JSON:", err)
|
||
// return
|
||
// }
|
||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||
content := fmt.Sprintf("%s [pay]-[%s] | FromAddress: %s | QueueId: %s",
|
||
timestamp, status, fromAddress, queueId)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// 记录当前监听的钱包和所有消息
|
||
func LogETHNode(msg string) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile("ethnode")
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||
content := fmt.Sprintf("[ETH-NODE:%s]: %s", timestamp, msg)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// =============================== RMQ <-> Listen 通信日志 ===============================
|
||
|
||
// LogRmqToListenTopupReq 记录 RMQ -> Listen 充值请求
|
||
// 使用 address 作为文件名
|
||
func LogRmqToListenTopupReq(queueId, chain, symbol, address string, timestamp uint64) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(address)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
// TopupReq 没有 fromAddress,使用 "-" 代替,toAddress 是 address,amount 为 0
|
||
content := fmt.Sprintf("[TopupReq]: %s--%s-%s-%s-0",
|
||
t, address, chain, symbol)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogRmqToListenWithdrawReq 记录 RMQ -> Listen 提现请求
|
||
// 使用 fromAddress 作为文件名
|
||
func LogRmqToListenWithdrawReq(queueId, chain, symbol, fromAddress, toAddress string, amount, fee float64, timestamp uint64) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(fromAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
content := fmt.Sprintf("[WithdrawReq]: %s-%s-%s-%s-%s-%.6f",
|
||
t, fromAddress, toAddress, chain, symbol, amount)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogRmqToListenPayReq 记录 RMQ -> Listen 支付请求
|
||
// 使用 fromAddress 作为文件名
|
||
func LogRmqToListenPayReq(queueId, chain, symbol, fromAddress, toAddress string, amount, fee float64, timestamp uint64) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(fromAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
content := fmt.Sprintf("[PayReq]: %s-%s-%s-%s-%s-%.6f",
|
||
t, fromAddress, toAddress, chain, symbol, amount)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogRmqToListenRemoveReq 记录 RMQ -> Listen 移除监听请求
|
||
// 使用 address 作为文件名
|
||
func LogRmqToListenRemoveReq(queueId string, msgType int, chain, symbol, address string, timestamp uint64) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(address)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
// RemoveReq 没有 fromAddress,toAddress 是 address,amount 为 0
|
||
content := fmt.Sprintf("[RemoveReq]: %s--%s-%s-%s-0",
|
||
t, address, chain, symbol)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogListenToRmqTopupResp 记录 Listen -> RMQ 充值响应
|
||
// 使用 address 作为文件名
|
||
func LogListenToRmqTopupResp(queueId, chain, symbol, address, fromAddress, txHash string, amount float64, blockHeight uint64, status int) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(address)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
// TopupResp 中 address 是目标地址(toAddress),fromAddress 是来源地址
|
||
content := fmt.Sprintf("[TopupResp]: %s-%s-%s-%s-%s-%.6f",
|
||
t, fromAddress, address, chain, symbol, amount)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogListenToRmqWithdrawResp 记录 Listen -> RMQ 提现响应
|
||
// 使用 fromAddress 作为文件名
|
||
func LogListenToRmqWithdrawResp(queueId, chain, symbol, fromAddress, toAddress, txHash string, amount, fee float64, blockHeight uint64, status int) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(fromAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
content := fmt.Sprintf("[WithdrawResp]: %s-%s-%s-%s-%s-%.6f",
|
||
t, fromAddress, toAddress, chain, symbol, amount)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogListenToRmqPayResp 记录 Listen -> RMQ 支付响应
|
||
// 使用 fromAddress 作为文件名
|
||
func LogListenToRmqPayResp(queueId, chain, symbol, fromAddress, toAddress, txHash string, amount, fee float64, blockHeight uint64, status int) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(fromAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
content := fmt.Sprintf("[PayResp]: %s-%s-%s-%s-%s-%.6f",
|
||
t, fromAddress, toAddress, chain, symbol, amount)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// LogListenToRmqRemoveResp 记录 Listen -> RMQ 移除监听响应
|
||
// 使用 address 作为文件名
|
||
func LogListenToRmqRemoveResp(queueId string, msgType int, chain, symbol, address string, status int) {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
lf, err := txLogger.getOrCreateLogFile(address)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ 获取日志文件失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
t := time.Now().Format("2006-01-02 15:04:05")
|
||
// 格式:[msg-Type]: time-fromaddress-toaddress-chain-symbol-amount
|
||
// RemoveResp 没有 fromAddress,toAddress 是 address,amount 为 0
|
||
content := fmt.Sprintf("[RemoveResp]: %s--%s-%s-%s-0",
|
||
t, address, chain, symbol)
|
||
|
||
if err := lf.write(content); err != nil {
|
||
fmt.Printf("⚠️ 写入日志失败: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// Close 关闭所有日志文件
|
||
func CloseTransactionLogger() {
|
||
if txLogger == nil {
|
||
return
|
||
}
|
||
|
||
txLogger.mu.Lock()
|
||
defer txLogger.mu.Unlock()
|
||
|
||
for _, lf := range txLogger.files {
|
||
if lf.file != nil {
|
||
lf.file.Close()
|
||
}
|
||
}
|
||
}
|