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)
}
}
实际项目中,推荐使用成熟的日志库如zap或logrus,它们已经提供了类似功能:
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语言的简洁性和明确性。现有的解决方案已经能很好地满足需求,同时保持了代码的可读性和可维护性。

