Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de8fc13f18 | |||
| fd37c5c301 |
@@ -239,9 +239,13 @@ fac.RedisSet(ctx, "key", "value", time.Hour)
|
||||
value, _ := fac.RedisGet(ctx, "key")
|
||||
fac.RedisDelete(ctx, "key")
|
||||
|
||||
// 数据库(GORM已经很灵活,直接返回对象)
|
||||
// 数据库(黑盒模式,获取已初始化对象)
|
||||
db, _ := fac.GetDatabase()
|
||||
db.Find(&users)
|
||||
|
||||
// Redis客户端(黑盒模式,获取已初始化对象)
|
||||
redisClient, _ := fac.GetRedisClient()
|
||||
redisClient.HGet(ctx, "key", "field").Result()
|
||||
```
|
||||
|
||||
更多示例请查看 [examples](./examples/) 目录。
|
||||
|
||||
@@ -235,6 +235,15 @@ type LoggerConfig struct {
|
||||
|
||||
// DisableTimestamp 禁用时间戳
|
||||
DisableTimestamp bool `json:"disableTimestamp"`
|
||||
|
||||
// Async 是否使用异步模式(默认false,即同步模式)
|
||||
// 异步模式:日志写入通过channel异步处理,不阻塞调用方
|
||||
// 同步模式:日志直接写入,会阻塞调用方直到写入完成
|
||||
Async bool `json:"async"`
|
||||
|
||||
// BufferSize 异步模式下的缓冲区大小(默认1000)
|
||||
// 当缓冲区满时,新的日志会阻塞直到有空间
|
||||
BufferSize int `json:"bufferSize"`
|
||||
}
|
||||
|
||||
// LoadFromFile 从文件加载配置
|
||||
|
||||
@@ -75,7 +75,9 @@
|
||||
"output": "stdout",
|
||||
"filePath": "",
|
||||
"prefix": "app",
|
||||
"disableTimestamp": false
|
||||
"disableTimestamp": false,
|
||||
"async": false,
|
||||
"bufferSize": 1000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,23 +136,52 @@ err := fac.RedisDelete(ctx, "user:123", "user:456")
|
||||
exists, err := fac.RedisExists(ctx, "user:123")
|
||||
```
|
||||
|
||||
### 7. 数据库操作
|
||||
|
||||
数据库保持返回 GORM 对象,因为 GORM 已经提供了很好的抽象:
|
||||
### 7. 数据库操作(黑盒模式)
|
||||
|
||||
```go
|
||||
// 获取数据库对象(延迟初始化)
|
||||
// 获取数据库对象(已初始化,黑盒模式)
|
||||
db, err := fac.GetDatabase()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// 直接使用GORM
|
||||
// 直接使用GORM,无需自己实现创建逻辑
|
||||
var users []User
|
||||
db.Find(&users)
|
||||
db.Create(&user)
|
||||
```
|
||||
|
||||
### 8. Redis操作(获取客户端对象)
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 获取Redis客户端对象(已初始化,黑盒模式)
|
||||
redisClient, err := fac.GetRedisClient()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// 直接使用Redis客户端,无需自己实现创建逻辑
|
||||
val, err := redisClient.Get(ctx, "key").Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
log.Printf("Redis error: %v", err)
|
||||
} else if err == redis.Nil {
|
||||
fmt.Println("Key not found")
|
||||
} else {
|
||||
fmt.Printf("Value: %s\n", val)
|
||||
}
|
||||
|
||||
// 使用高级功能(如Hash操作)
|
||||
redisClient.HSet(ctx, "user:123", "name", "John")
|
||||
name, _ := redisClient.HGet(ctx, "user:123", "name").Result()
|
||||
```
|
||||
|
||||
|
||||
## 完整示例
|
||||
|
||||
@@ -400,7 +429,26 @@ func main() {
|
||||
- 自动配置连接池参数
|
||||
- 数据库时间统一使用UTC时区
|
||||
- 延迟初始化,首次调用时创建连接
|
||||
- 黑盒模式:只需传递config对象,无需自己实现创建逻辑
|
||||
|
||||
### Redis方法
|
||||
|
||||
#### GetRedisClient() (*redis.Client, error)
|
||||
|
||||
获取Redis客户端对象(已初始化)。
|
||||
|
||||
**返回:** 已初始化的Redis客户端对象和错误信息
|
||||
|
||||
**说明:**
|
||||
- 自动处理所有配置检查和连接测试
|
||||
- 自动设置默认值(连接池大小、超时时间等)
|
||||
- 连接失败时会自动关闭客户端并返回错误
|
||||
- 返回的客户端已通过Ping测试,可直接使用
|
||||
- 黑盒模式:只需传递config对象,无需自己实现创建逻辑
|
||||
- 推荐使用 `RedisGet`、`RedisSet`、`RedisDelete` 等方法直接操作Redis
|
||||
- 如果需要使用Redis的高级功能(如Hash、List、Set等),可以使用此方法获取客户端对象
|
||||
|
||||
### 配置方法
|
||||
|
||||
#### GetConfig() *config.Config
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
- 支持日志前缀
|
||||
- 支持禁用时间戳
|
||||
- 支持带字段的日志记录
|
||||
- **支持异步/同步日志模式(默认同步)**
|
||||
- 使用配置工具统一管理配置
|
||||
|
||||
## 使用方法
|
||||
@@ -92,6 +93,40 @@ logger.Infof(fields, "User logged in")
|
||||
logger.Errorf(fields, "Failed to process request")
|
||||
```
|
||||
|
||||
### 5. 异步/同步模式
|
||||
|
||||
#### 同步模式(默认)
|
||||
|
||||
```go
|
||||
// 配置中不设置async或设置为false,使用同步模式
|
||||
// 同步模式:日志直接写入,会阻塞调用方直到写入完成
|
||||
logger.Info("This is a synchronous log")
|
||||
```
|
||||
|
||||
#### 异步模式
|
||||
|
||||
```go
|
||||
// 配置中设置async为true,使用异步模式
|
||||
// 异步模式:日志写入通过channel异步处理,不阻塞调用方
|
||||
// 配置文件示例:
|
||||
// {
|
||||
// "logger": {
|
||||
// "async": true,
|
||||
// "bufferSize": 1000
|
||||
// }
|
||||
// }
|
||||
|
||||
// 使用异步模式时,程序退出前需要调用Close()确保所有日志写入完成
|
||||
defer logger.Close()
|
||||
|
||||
logger.Info("This is an asynchronous log")
|
||||
```
|
||||
|
||||
**注意:**
|
||||
- `Fatal` 和 `Panic` 方法始终使用同步模式,确保日志写入后再退出/panic
|
||||
- 异步模式下,程序退出前应调用 `Close()` 方法,确保所有日志写入完成
|
||||
- 如果channel已满,会自动降级为同步写入,避免丢失日志
|
||||
|
||||
## API 参考
|
||||
|
||||
### NewLogger(cfg *config.LoggerConfig) (*Logger, error)
|
||||
@@ -143,6 +178,15 @@ logger.Errorf(fields, "Failed to process request")
|
||||
|
||||
记录错误日志(带字段)。
|
||||
|
||||
### (l *Logger) Close() error
|
||||
|
||||
优雅关闭logger(仅异步模式需要)。
|
||||
|
||||
**说明:**
|
||||
- 等待所有日志写入完成后再返回
|
||||
- 同步模式下调用此方法会立即返回,无需等待
|
||||
- 程序退出前应调用此方法,确保所有日志写入完成
|
||||
|
||||
## 配置说明
|
||||
|
||||
日志配置通过 `config.LoggerConfig` 提供:
|
||||
@@ -154,6 +198,8 @@ logger.Errorf(fields, "Failed to process request")
|
||||
| FilePath | string | 日志文件路径(当output为file或both时必需) | - |
|
||||
| Prefix | string | 日志前缀 | - |
|
||||
| DisableTimestamp | bool | 禁用时间戳 | false |
|
||||
| Async | bool | 是否使用异步模式 | false(同步) |
|
||||
| BufferSize | int | 异步模式下的缓冲区大小 | 1000 |
|
||||
|
||||
## 配置示例
|
||||
|
||||
@@ -196,6 +242,25 @@ logger.Errorf(fields, "Failed to process request")
|
||||
}
|
||||
```
|
||||
|
||||
### 异步模式配置
|
||||
|
||||
```json
|
||||
{
|
||||
"logger": {
|
||||
"level": "info",
|
||||
"output": "file",
|
||||
"filePath": "./logs/app.log",
|
||||
"prefix": "app",
|
||||
"async": true,
|
||||
"bufferSize": 1000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `async`: 设置为 `true` 启用异步模式,`false` 或不设置则使用同步模式(默认)
|
||||
- `bufferSize`: 异步模式下的channel缓冲区大小,默认1000。当缓冲区满时,新的日志会阻塞直到有空间,或降级为同步写入
|
||||
|
||||
## 日志级别说明
|
||||
|
||||
- **debug**: 调试信息,最详细的日志级别
|
||||
@@ -222,6 +287,13 @@ logger.Errorf(fields, "Failed to process request")
|
||||
4. **性能考虑**:
|
||||
- 使用标准库log包,性能较好
|
||||
- 文件输出使用追加模式,不会覆盖已有日志
|
||||
- 异步模式适合高并发场景,减少日志写入对业务代码的阻塞
|
||||
- 同步模式适合需要确保日志立即写入的场景(如调试)
|
||||
|
||||
5. **异步模式注意事项**:
|
||||
- 异步模式下,程序退出前必须调用 `Close()` 方法,确保所有日志写入完成
|
||||
- 如果channel缓冲区已满,会自动降级为同步写入,避免丢失日志
|
||||
- `Fatal` 和 `Panic` 方法始终使用同步模式,确保日志写入后再退出/panic
|
||||
|
||||
## 完整示例
|
||||
|
||||
@@ -257,6 +329,9 @@ func main() {
|
||||
}, "User logged in successfully")
|
||||
|
||||
logger.Error("An error occurred: %v", err)
|
||||
|
||||
// 如果使用异步模式,程序退出前需要关闭logger
|
||||
// defer logger.Close()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -265,7 +265,14 @@ type Storage interface {
|
||||
|
||||
#### 响应
|
||||
|
||||
直接返回文件内容,设置适当的Content-Type。
|
||||
- **成功**:直接返回文件内容(二进制),设置适当的Content-Type
|
||||
- **错误**:返回标准HTTP错误状态码和错误消息(文本格式)
|
||||
- `400 Bad Request`: 缺少必需参数
|
||||
- `404 Not Found`: 文件不存在
|
||||
- `405 Method Not Allowed`: 请求方法不正确
|
||||
- `500 Internal Server Error`: 系统错误
|
||||
|
||||
**注意**:`ProxyHandler` 返回的是文件内容(二进制),而不是JSON响应。错误时使用标准HTTP状态码,保持与文件响应的一致性。
|
||||
|
||||
### 辅助函数
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"git.toowon.com/jimmy/go-common/factory"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -145,12 +146,12 @@ func main() {
|
||||
fac.LogInfo("键是否存在: %v", exists)
|
||||
}
|
||||
|
||||
// ========== 数据库操作 ==========
|
||||
// ========== 数据库操作(黑盒模式,获取对象) ==========
|
||||
db, err := fac.GetDatabase()
|
||||
if err != nil {
|
||||
fac.LogError("数据库连接失败: %v", err)
|
||||
} else {
|
||||
// 直接使用GORM
|
||||
// 直接使用GORM,无需自己实现创建逻辑
|
||||
var count int64
|
||||
if err := db.Table("users").Count(&count).Error; err != nil {
|
||||
fac.LogError("查询用户数量失败: %v", err)
|
||||
@@ -159,6 +160,27 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Redis操作(获取客户端对象,黑盒模式) ==========
|
||||
redisClient, err := fac.GetRedisClient()
|
||||
if err != nil {
|
||||
fac.LogError("Redis客户端不可用: %v", err)
|
||||
} else {
|
||||
// 直接使用Redis客户端,无需自己实现创建逻辑
|
||||
val, err := redisClient.Get(ctx, "test_key").Result()
|
||||
if err != nil && err != redis.Nil {
|
||||
fac.LogError("Redis错误: %v", err)
|
||||
} else if err == redis.Nil {
|
||||
fac.LogInfo("Redis键不存在")
|
||||
} else {
|
||||
fac.LogInfo("Redis值: %s", val)
|
||||
}
|
||||
|
||||
// 使用高级功能(如Hash操作)
|
||||
redisClient.HSet(ctx, "user:123", "name", "John")
|
||||
name, _ := redisClient.HGet(ctx, "user:123", "name").Result()
|
||||
fac.LogInfo("Redis Hash值: %s", name)
|
||||
}
|
||||
|
||||
|
||||
fac.LogInfo("示例执行完成")
|
||||
}
|
||||
|
||||
@@ -14,13 +14,20 @@ func main() {
|
||||
log.Fatal("Failed to load config:", err)
|
||||
}
|
||||
|
||||
// 使用工厂创建日志记录器(推荐方式)
|
||||
// 方式1:使用工厂创建日志记录器(推荐方式)
|
||||
fac := factory.NewFactory(cfg)
|
||||
logger, err := fac.GetLogger()
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create logger:", err)
|
||||
}
|
||||
|
||||
// 如果使用异步模式,程序退出前需要关闭logger
|
||||
defer logger.Close()
|
||||
|
||||
// 方式2:直接使用工厂的日志方法(黑盒模式,更简单)
|
||||
// fac.LogInfo("Application started")
|
||||
// fac.LogError("An error occurred")
|
||||
|
||||
// 示例1:基本日志记录
|
||||
logger.Info("Application started")
|
||||
logger.Debug("Debug message: %s", "This is a debug message")
|
||||
@@ -45,6 +52,11 @@ func main() {
|
||||
logger.Warn("This is a warn log")
|
||||
logger.Error("This is an error log")
|
||||
|
||||
// 示例4:异步模式使用
|
||||
// 如果配置中设置了 "async": true,日志会异步写入
|
||||
// 程序退出前需要调用 Close() 确保所有日志写入完成
|
||||
// logger.Close()
|
||||
|
||||
// 注意:Fatal和Panic会终止程序,示例中不执行
|
||||
// logger.Fatal("This would exit the program")
|
||||
// logger.Panic("This would panic")
|
||||
|
||||
@@ -153,6 +153,14 @@ func (f *Factory) getLogger() (*logger.Logger, error) {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// GetLogger 获取日志记录器对象(已初始化)
|
||||
// 返回已初始化的日志记录器对象,可直接使用
|
||||
// 注意:推荐使用 LogDebug、LogInfo、LogWarn、LogError 等方法直接记录日志
|
||||
// 如果需要使用logger的高级功能(如Close方法),可以使用此方法获取logger对象
|
||||
func (f *Factory) GetLogger() (*logger.Logger, error) {
|
||||
return f.getLogger()
|
||||
}
|
||||
|
||||
// LogDebug 记录调试日志
|
||||
// message: 日志消息
|
||||
// args: 格式化参数(可选)
|
||||
@@ -430,6 +438,14 @@ func (f *Factory) getRedisClient() (*redis.Client, error) {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// GetRedisClient 获取Redis客户端对象(已初始化)
|
||||
// 返回已初始化的Redis客户端对象,可直接使用
|
||||
// 注意:推荐使用 RedisGet、RedisSet、RedisDelete 等方法直接操作Redis
|
||||
// 如果需要使用Redis的高级功能(如Hash、List、Set等),可以使用此方法获取客户端对象
|
||||
func (f *Factory) GetRedisClient() (*redis.Client, error) {
|
||||
return f.getRedisClient()
|
||||
}
|
||||
|
||||
// RedisGet 获取Redis值(黑盒模式)
|
||||
// key: Redis键
|
||||
func (f *Factory) RedisGet(ctx context.Context, key string) (string, error) {
|
||||
|
||||
218
logger/logger.go
218
logger/logger.go
@@ -6,10 +6,19 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"git.toowon.com/jimmy/go-common/config"
|
||||
)
|
||||
|
||||
// logMessage 异步日志消息结构
|
||||
type logMessage struct {
|
||||
level string // debug, info, warn, error
|
||||
format string
|
||||
args []interface{}
|
||||
fields map[string]interface{} // 用于带字段的日志
|
||||
}
|
||||
|
||||
// Logger 日志记录器
|
||||
type Logger struct {
|
||||
infoLog *log.Logger
|
||||
@@ -17,6 +26,14 @@ type Logger struct {
|
||||
warnLog *log.Logger
|
||||
debugLog *log.Logger
|
||||
config *config.LoggerConfig
|
||||
|
||||
// 异步相关字段
|
||||
async bool // 是否异步模式
|
||||
logChan chan *logMessage // 异步日志channel
|
||||
done chan struct{} // 用于优雅关闭
|
||||
wg sync.WaitGroup // 等待所有日志写入完成
|
||||
closed bool // 是否已关闭
|
||||
closeMux sync.RWMutex // 保护closed字段
|
||||
}
|
||||
|
||||
// NewLogger 创建日志记录器
|
||||
@@ -24,9 +41,11 @@ func NewLogger(cfg *config.LoggerConfig) (*Logger, error) {
|
||||
if cfg == nil {
|
||||
// 使用默认配置
|
||||
cfg = &config.LoggerConfig{
|
||||
Level: "info",
|
||||
Output: "stdout",
|
||||
FilePath: "",
|
||||
Level: "info",
|
||||
Output: "stdout",
|
||||
FilePath: "",
|
||||
Async: false, // 默认同步
|
||||
BufferSize: 1000, // 默认缓冲区大小
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +56,9 @@ func NewLogger(cfg *config.LoggerConfig) (*Logger, error) {
|
||||
if cfg.Output == "" {
|
||||
cfg.Output = "stdout"
|
||||
}
|
||||
if cfg.BufferSize <= 0 {
|
||||
cfg.BufferSize = 1000 // 默认缓冲区大小
|
||||
}
|
||||
|
||||
// 创建输出目标
|
||||
var writers []io.Writer
|
||||
@@ -89,6 +111,7 @@ func NewLogger(cfg *config.LoggerConfig) (*Logger, error) {
|
||||
// 创建日志记录器
|
||||
logger := &Logger{
|
||||
config: cfg,
|
||||
async: cfg.Async,
|
||||
}
|
||||
|
||||
// 根据日志级别创建不同的logger
|
||||
@@ -106,39 +129,152 @@ func NewLogger(cfg *config.LoggerConfig) (*Logger, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果启用异步模式,启动goroutine处理日志
|
||||
if cfg.Async {
|
||||
logger.logChan = make(chan *logMessage, cfg.BufferSize)
|
||||
logger.done = make(chan struct{})
|
||||
logger.wg.Add(1)
|
||||
go logger.processLogs()
|
||||
}
|
||||
|
||||
return logger, nil
|
||||
}
|
||||
|
||||
// processLogs 异步处理日志(goroutine)
|
||||
func (l *Logger) processLogs() {
|
||||
defer l.wg.Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg := <-l.logChan:
|
||||
if msg == nil {
|
||||
// channel已关闭,退出
|
||||
return
|
||||
}
|
||||
l.writeLog(msg)
|
||||
case <-l.done:
|
||||
// 收到关闭信号,处理完剩余日志后退出
|
||||
for {
|
||||
select {
|
||||
case msg := <-l.logChan:
|
||||
if msg == nil {
|
||||
return
|
||||
}
|
||||
l.writeLog(msg)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writeLog 实际写入日志(内部方法)
|
||||
func (l *Logger) writeLog(msg *logMessage) {
|
||||
var logger *log.Logger
|
||||
switch msg.level {
|
||||
case "debug":
|
||||
logger = l.debugLog
|
||||
case "info":
|
||||
logger = l.infoLog
|
||||
case "warn":
|
||||
logger = l.warnLog
|
||||
case "error":
|
||||
logger = l.errorLog
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果有字段,先格式化字段
|
||||
format := msg.format
|
||||
if len(msg.fields) > 0 {
|
||||
fieldStr := formatFields(msg.fields)
|
||||
format = fieldStr + format
|
||||
}
|
||||
|
||||
// 写入日志
|
||||
logger.Printf(format, msg.args...)
|
||||
}
|
||||
|
||||
// isClosed 检查logger是否已关闭
|
||||
func (l *Logger) isClosed() bool {
|
||||
l.closeMux.RLock()
|
||||
defer l.closeMux.RUnlock()
|
||||
return l.closed
|
||||
}
|
||||
|
||||
// setClosed 设置logger为已关闭状态
|
||||
func (l *Logger) setClosed() {
|
||||
l.closeMux.Lock()
|
||||
defer l.closeMux.Unlock()
|
||||
l.closed = true
|
||||
}
|
||||
|
||||
// log 内部日志方法,根据模式选择同步或异步
|
||||
func (l *Logger) log(level string, format string, args []interface{}, fields map[string]interface{}) {
|
||||
// 如果已关闭,直接返回
|
||||
if l.isClosed() {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是异步模式,发送到channel
|
||||
if l.async {
|
||||
// 检查channel是否已关闭
|
||||
select {
|
||||
case l.logChan <- &logMessage{
|
||||
level: level,
|
||||
format: format,
|
||||
args: args,
|
||||
fields: fields,
|
||||
}:
|
||||
// 成功发送
|
||||
default:
|
||||
// channel已满或已关闭,同步写入(降级处理)
|
||||
l.writeLog(&logMessage{
|
||||
level: level,
|
||||
format: format,
|
||||
args: args,
|
||||
fields: fields,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 同步模式,直接写入
|
||||
l.writeLog(&logMessage{
|
||||
level: level,
|
||||
format: format,
|
||||
args: args,
|
||||
fields: fields,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Debug 记录调试日志
|
||||
func (l *Logger) Debug(format string, v ...interface{}) {
|
||||
if l.debugLog != nil {
|
||||
l.debugLog.Printf(format, v...)
|
||||
}
|
||||
l.log("debug", format, v, nil)
|
||||
}
|
||||
|
||||
// Info 记录信息日志
|
||||
func (l *Logger) Info(format string, v ...interface{}) {
|
||||
if l.infoLog != nil {
|
||||
l.infoLog.Printf(format, v...)
|
||||
}
|
||||
l.log("info", format, v, nil)
|
||||
}
|
||||
|
||||
// Warn 记录警告日志
|
||||
func (l *Logger) Warn(format string, v ...interface{}) {
|
||||
if l.warnLog != nil {
|
||||
l.warnLog.Printf(format, v...)
|
||||
}
|
||||
l.log("warn", format, v, nil)
|
||||
}
|
||||
|
||||
// Error 记录错误日志
|
||||
func (l *Logger) Error(format string, v ...interface{}) {
|
||||
if l.errorLog != nil {
|
||||
l.errorLog.Printf(format, v...)
|
||||
}
|
||||
l.log("error", format, v, nil)
|
||||
}
|
||||
|
||||
// Fatal 记录致命错误日志并退出程序
|
||||
// Fatal 记录致命错误日志并退出程序(始终同步)
|
||||
func (l *Logger) Fatal(format string, v ...interface{}) {
|
||||
// Fatal必须同步执行,确保日志写入后再退出
|
||||
if l.errorLog != nil {
|
||||
l.errorLog.Fatalf(format, v...)
|
||||
} else {
|
||||
@@ -146,8 +282,9 @@ func (l *Logger) Fatal(format string, v ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// Panic 记录恐慌日志并触发panic
|
||||
// Panic 记录恐慌日志并触发panic(始终同步)
|
||||
func (l *Logger) Panic(format string, v ...interface{}) {
|
||||
// Panic必须同步执行,确保日志写入后再panic
|
||||
if l.errorLog != nil {
|
||||
l.errorLog.Panicf(format, v...)
|
||||
} else {
|
||||
@@ -175,33 +312,48 @@ func formatFields(fields map[string]interface{}) string {
|
||||
|
||||
// Debugf 记录调试日志(带字段)
|
||||
func (l *Logger) Debugf(fields map[string]interface{}, format string, v ...interface{}) {
|
||||
if l.debugLog != nil {
|
||||
fieldStr := formatFields(fields)
|
||||
l.debugLog.Printf(fieldStr+format, v...)
|
||||
}
|
||||
l.log("debug", format, v, fields)
|
||||
}
|
||||
|
||||
// Infof 记录信息日志(带字段)
|
||||
func (l *Logger) Infof(fields map[string]interface{}, format string, v ...interface{}) {
|
||||
if l.infoLog != nil {
|
||||
fieldStr := formatFields(fields)
|
||||
l.infoLog.Printf(fieldStr+format, v...)
|
||||
}
|
||||
l.log("info", format, v, fields)
|
||||
}
|
||||
|
||||
// Warnf 记录警告日志(带字段)
|
||||
func (l *Logger) Warnf(fields map[string]interface{}, format string, v ...interface{}) {
|
||||
if l.warnLog != nil {
|
||||
fieldStr := formatFields(fields)
|
||||
l.warnLog.Printf(fieldStr+format, v...)
|
||||
}
|
||||
l.log("warn", format, v, fields)
|
||||
}
|
||||
|
||||
// Errorf 记录错误日志(带字段)
|
||||
func (l *Logger) Errorf(fields map[string]interface{}, format string, v ...interface{}) {
|
||||
if l.errorLog != nil {
|
||||
fieldStr := formatFields(fields)
|
||||
l.errorLog.Printf(fieldStr+format, v...)
|
||||
}
|
||||
l.log("error", format, v, fields)
|
||||
}
|
||||
|
||||
// Close 优雅关闭logger(仅异步模式需要)
|
||||
// 等待所有日志写入完成后再返回
|
||||
func (l *Logger) Close() error {
|
||||
if !l.async {
|
||||
// 同步模式不需要关闭
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查是否已关闭
|
||||
if l.isClosed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 设置关闭状态
|
||||
l.setClosed()
|
||||
|
||||
// 发送关闭信号
|
||||
close(l.done)
|
||||
|
||||
// 关闭channel(会触发processLogs退出)
|
||||
close(l.logChan)
|
||||
|
||||
// 等待所有日志写入完成
|
||||
l.wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -155,18 +155,17 @@ func NewProxyHandler(storage Storage) *ProxyHandler {
|
||||
|
||||
// ServeHTTP 处理文件查看请求
|
||||
// URL参数: key (对象键)
|
||||
// 注意:此方法直接返回文件内容(二进制),错误时返回标准HTTP错误状态码
|
||||
func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
handler := commonhttp.NewHandler(w, r)
|
||||
|
||||
if r.Method != http.MethodGet {
|
||||
handler.Error(4001, "Method not allowed")
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取对象键
|
||||
objectKey := handler.GetQuery("key", "")
|
||||
objectKey := r.URL.Query().Get("key")
|
||||
if objectKey == "" {
|
||||
handler.Error(4004, "Missing parameter: key")
|
||||
http.Error(w, "Missing parameter: key", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -174,19 +173,19 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
exists, err := h.storage.Exists(ctx, objectKey)
|
||||
if err != nil {
|
||||
handler.SystemError(fmt.Sprintf("Failed to check file existence: %v", err))
|
||||
http.Error(w, fmt.Sprintf("Failed to check file existence: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
handler.Error(4005, "File not found")
|
||||
http.Error(w, "File not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取文件内容
|
||||
reader, err := h.storage.GetObject(ctx, objectKey)
|
||||
if err != nil {
|
||||
handler.SystemError(fmt.Sprintf("Failed to get file: %v", err))
|
||||
http.Error(w, fmt.Sprintf("Failed to get file: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
@@ -210,7 +209,8 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// 复制文件内容到响应
|
||||
_, err = io.Copy(w, reader)
|
||||
if err != nil {
|
||||
handler.SystemError(fmt.Sprintf("Failed to write response: %v", err))
|
||||
// 如果已经开始写入响应,无法再设置错误状态码
|
||||
// 这里只能记录错误,无法返回错误响应
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user