Golang新关键字解析

Golang新关键字解析 假设 Go 语言引入一个新的关键字 __func(level),Go 编译器会将其替换为字符串 package_name.func_name: line_number。 level 可以是 F | E | W | I(分别代表 FATAL | ERROR | WARNING | INFO)。

这将为良好的日志记录节省时间。

// 一个示例
package myPackage

func myFunc() {
    ...
    fmt.Println(__func(I), "Everything is good")
    ...
    If err != nil {
        log.Fatalln(__func(F), err)
    }
    ...
}
**输出**
INFO: myPackage.myFunc: 12. Everything is good
FATAL: myPackage.myFunc: 31. The system cannot find ... 

你怎么看?


更多关于Golang新关键字解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

为什么需要 var 关键字?https://play.golang.org/p/P6a_2E_H9HO

更多关于Golang新关键字解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 Sean, 太棒了! 不需要新的关键字 🙂 也许只是为了在大量日志记录时节省执行时间。 非常感谢!

这是一个有趣的语言扩展提案,但Go语言目前不支持这种预处理器风格的关键字替换。不过,我们可以通过现有的Go特性实现类似功能。

以下是使用runtime.Caller和自定义日志函数实现的等效方案:

package myPackage

import (
    "fmt"
    "log"
    "runtime"
    "strings"
)

const (
    F = "FATAL"
    E = "ERROR"
    W = "WARNING"
    I = "INFO"
)

// 获取调用者信息
func getCallerInfo(skip int) string {
    pc, file, line, ok := runtime.Caller(skip)
    if !ok {
        return "unknown:0"
    }
    
    // 获取函数名
    fn := runtime.FuncForPC(pc)
    funcName := "unknown"
    if fn != nil {
        // 提取短函数名
        fullName := fn.Name()
        parts := strings.Split(fullName, ".")
        funcName = parts[len(parts)-1]
    }
    
    // 提取包名
    packageName := "unknown"
    if fn != nil {
        fullName := fn.Name()
        lastDot := strings.LastIndex(fullName[:strings.LastIndex(fullName, ".")], ".")
        if lastDot > 0 {
            packageName = fullName[lastDot+1 : strings.LastIndex(fullName, ".")]
        }
    }
    
    return fmt.Sprintf("%s.%s:%d", packageName, funcName, line)
}

// 日志函数
func LogFunc(level, format string, args ...interface{}) {
    callerInfo := getCallerInfo(3) // 跳过3层调用栈
    prefix := fmt.Sprintf("%s: %s. ", level, callerInfo)
    log.Printf(prefix+format, args...)
}

// 示例使用
func myFunc() {
    // 模拟INFO日志
    LogFunc(I, "Everything is good")
    
    // 模拟错误处理
    err := fmt.Errorf("The system cannot find the file specified")
    if err != nil {
        LogFunc(F, "%v", err)
        // 或者使用标准log.Fatal
        log.Fatalf("%s: %s. %v", F, getCallerInfo(2), err)
    }
}

// 更简洁的包装函数
func Info(format string, args ...interface{}) {
    LogFunc(I, format, args...)
}

func Fatal(err error) {
    log.Fatalf("%s: %s. %v", F, getCallerInfo(2), err)
}

// 使用包装函数
func anotherFunc() {
    Info("Processing started")
    
    if err := doSomething(); err != nil {
        Fatal(err)
    }
}

func doSomething() error {
    return fmt.Errorf("operation failed")
}

对于更接近你设想的语法,可以使用代码生成工具:

// 使用go:generate指令
//go:generate stringer -type=LogLevel

type LogLevel int

const (
    INFO LogLevel = iota
    WARNING
    ERROR
    FATAL
)

// 生成日志代码
var __func = func(level LogLevel) string {
    _, file, line, _ := runtime.Caller(1)
    return fmt.Sprintf("%s: %s:%d", level, filepath.Base(file), line)
}

// 使用示例
func exampleFunc() {
    fmt.Println(__func(INFO), "Everything is good")
    
    if err != nil {
        log.Println(__func(FATAL), err)
    }
}

实际项目中,推荐使用成熟的日志库如zaplogrus,它们已经提供了类似功能:

import "go.uber.org/zap"

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync()
    
    logger.Info("message",
        zap.String("func", "myPackage.myFunc"),
        zap.Int("line", 12),
    )
}

这种关键字提案虽然能简化代码,但会破坏Go语言的简洁性和明确性。现有的解决方案已经能很好地满足需求,同时保持了代码的可读性和可维护性。

回到顶部