donnie4w / go-logger

go高性能日志库, 支持日志格式化, 文件切割,压缩等特性
https://tlnet.top/logdoc
BSD 3-Clause "New" or "Revised" License
279 stars 103 forks source link

添加自定义函数执行逻辑 #37

Closed Bronya0 closed 1 month ago

donnie4w commented 1 month ago

@Bronya0 好的。可能有些地方需要修改一下 我之前已经把Set函数注释为废弃函数了,新增的属性j建议添加在Option结构体中。这样,不需要添加两个Set函数 另外 func(level _LEVEL, v ...interface{}) 这个函数扩展性不太好,外部引入其他函数行为,类似添加外部组件,需要一定扩展性,后续修改增加字段时,兼容前版本,使用者不需要修改代码。建议添加一个结构体,可以在option.go文件中添加

type Option struct {
    Level         _LEVEL
    Console       bool
    Format        _FORMAT
    Formatter     string
    FileOption    FileOption
    CustomHandler func(lc *LogContext) bool
}

type LogContext struct {
    Level   _LEVEL
    Args     []any
}

Logging 结构体中 customHandler func(level _LEVEL, v ...interface{}) 修改为 customHandler func(lc *LogContext) bool

    if t.customHandler != nil {
        go func() {
            defer catchError()
            t.customHandler(_level, v)
        }()
    }
        if t._level > _level {
        return
    }

修改为

        if t._level > _level {
        return
    }
    if t.customHandler != nil && !t.customHandler(&LogContext{Level: _level, Args: v}) {
        return
    }
  1. 使用同步,是否使用异步的权限留给开发者,需要异步时,开发者可以在customHandler函数中封装异步调用。
  2. 将执行流程控制权交给customHandler函数。customHandler返回false时,println函数返回,不再执行后续的打印,返回true时,继续执行后续打印。
Bronya0 commented 1 month ago

@donnie4w 我改了下代码,写了个单元测试,你看下

donnie4w commented 1 month ago

@Bronya0 好的,我看了代码,没有什么问题

if t.customHandler != nil {
    defer catchError()
    if isContinue := t.customHandler(&LogContext{Level: _level, Args: v}); !isContinue {
        return
    }
}

我看到这里捕获了println函数的异常,你的本意应该是要捕获customHandler函数的异常,实际上,customHandler异常不需要捕获。应该让更外层的函数捕获。 主要是因为 customHandler是外部函数,流程上,不应该由中间框架捕获它的panic,而应该让panic往上抛,在业务层捕获处理它的panic。如果在这里捕获了,可能一些customHandler逻辑问题,会被框架掩盖掉,这些逻辑问题其实应该被开发者知晓。 如果上层函数没捕获,进程可能会停止,这个流程的正常的。开发者必须控制这个流程,才能消除隐藏问题。

比如读取系统配置失败,连接其他服务失败,读取数据失败等异常,抛出panic,这种异常不应该被中间框架捕获掉, 而应该抛出去。如果业务上不应该恢复异常,比如系统配置错误,那么应该让进程停止。如果可以恢复异常,比如连接其他服务失败,开发者在外部函数捕获异常后,进行异常处理,重连什么的。

Bronya0 commented 1 month ago

@donnie4w 上抛指的是logger打印的时候返回吗?那每次logger.info都要返回err是不是变得太繁琐了

donnie4w commented 1 month ago

@Bronya0 上抛是指panic继续向上传播,直到被更高级别的处理程序捕获,或者最终导致整个程序崩溃。 不是返回error,而是不调用recover()函数恢复异常。说简单点就是这里不处理它。

返回error是指传递错误信息,这不被称为异常。

logger.info一般不跟业务流程相关,因此也不需要传递错误对象。logger把自己框架内部的异常捕获就可以,外部函数的异常就让它向上传播,即不主动的捕获它。

Bronya0 commented 1 month ago

我有个疑惑,如果customHandler内部跑了个子协程,然后panic了,那么主协程的recover是捕获不到的吧?这样会不会导致整个进程崩溃了,风险是不是太大了(当然可以责任都丢给子协程开发者)

donnie4w commented 1 month ago

我有个疑惑,如果customHandler内部跑了个子协程,然后panic了,那么主协程的recover是捕获不到的吧?这样会不会导致整个进程崩溃了,风险是不是太大了

是的,go目前的机制是这样的。实际上,你说的情况是必然导致进程崩溃的。

具体在这段程序代码中

if t.customHandler != nil {
    defer catchError()
    if isContinue := t.customHandler(&LogContext{Level: _level, Args: v}); !isContinue {
        return
    }
}

catchError 一样是无法捕获 customHandler函数里面 启动的子协程抛出的异常的。 假如你希望通过catchError保证customHandler函数不会导致程序崩溃,理论上是无法办到的。只要customHandler启动的另一个协程panic没有恢复,程序就会崩溃了。

Bronya0 commented 1 month ago

嗯,那这个责任就丢给开发者好了……代码我去掉defer了