gookit / slog

📑 Lightweight, configurable, extensible logging library written in Go. Support multi level, multi outputs and built-in multi file logger, buffers, clean, rotate-file handling.一个易于使用的,轻量级、可配置、可扩展的日志库。支持多个级别,输出到多文件;内置文件日志处理、自动切割、清理、压缩等增强功能

Home Page:https://pkg.go.dev/github.com/gookit/slog

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

slog performance

yangdong-d opened this issue · comments

commented

我在参照示例写了个slog输出到文件的demo,测试性能的时候发现和benchmark的表现相差很大
即便我放在同一个项目同一台机器上,slog的性能也远不如log4go(项目想替换掉的旧日志库)
是我的使用方法不对吗,求教

demo:

func slogDemo() {
      type Obj struct {
              a int
              b int64
              c string
              d bool
       }
      str1 := "str1"
      str2 := "str222222222222"
      int1 := 1
      int2 := 2

      h1, err := handler.NewEmptyConfig().With(
                  handler.WithLogfile("./info.log"),             //路径
                  handler.WithRotateTime(handler.EveryHour),  //日志分割间隔
                  handler.WithLogLevels(slog.AllLevels),         //日志level
                  handler.WithBuffSize(4 * 1024 * 1024),                //buffer大小
                  handler.WithCompress(true),                    //是否压缩旧日志 zip
                  handler.WithBackupNum(24 * 3),                 //保留旧日志数量
                  handler.WithBuffMode(handler.BuffModeBite),
                  //handler.WithRenameFunc(),                    //RenameFunc build filename for rotate file
          ).CreateHandler()
      if err != nil {
	  fmt.Printf("Create slog handler err: %#v", err)
	  return
      }
      f := slog.AsTextFormatter(h1.Formatter())
      myTplt := "[{{datetime}}] [{{level}}] [{{caller}}] {{message}}\n"
      f.SetTemplate(myTplt)
      logs := slog.NewWithHandlers(h1)
      start = time.Now().UnixNano()
      for n := count;n > 0; n-- {
          logs.Infof("message is %d %d %s %s %#v", int1, int2, str1, str2, obj)
      }
     end = time.Now().UnixNano()
     fmt.Printf("\n slog total cost %d ns\n  avg  cost %d ns \n count %d \n", end - start, (end - start)/int64(count), count)
     logs.MustFlush()
}

result:

slog total cost 784599471 ns
  avg  cost 7845 ns
 count 100000

补充些更多的测试结果

 zap sugar no format
 total cost 213044652 ns
  avg  cost 2130 ns
 count 100000

 zap sugar format
 total cost 397701153 ns
  avg  cost 3977 ns
 count 100000

 zap no format
 total cost 215259103 ns
  avg  cost 2152 ns
 count 100000

 zap format
 total cost 430643119 ns
  avg  cost 4306 ns
 count 100000

 slog no format
 total cost 500610606 ns
  avg  cost 5006 ns
 count 100000

 slog format
 total cost 771954584 ns
  avg  cost 7719 ns
 count 100000

 log4go no format
 total cost 225615627 ns
  avg  cost 2256 ns
 count 100000

 log4go format
 total cost 452465265 ns
  avg  cost 4524 ns
 count 100000
commented

:) 跟zap、zerolog 这类的日志库比较肯定会差很多,它使用的手动指定类型来使用日志 例如 zap.String(),zap.Int()。使用上稍微有点麻烦,但是数据处理性能更好,扩展性也差了一点。

slog 使用的go fmt.SprintX 方法格式化各种类型数据(跟 logrus 类似),属于使用简单但是性能要差一些。

看你使用场景,配合 buffer 作为项目应用上的日志库 slog 或者 logrus 性能远远够用了;真实应用的性能瓶颈更多的是在 (file,net) io 上, 格式化数据这点损耗基本上可以忽略。

slog 在易于使用和性能的上,更偏向于使用方便(使用方便,易于扩展,内置好各种日志文件处理)。

commented

额理解,但是zap sugar也是print风格下测的。

commented

可以给下测试代码我本地测试一下 😄

commented

image

我本地测试相差不大

commented

可以给下测试代码我本地测试一下 😄

我是这么测的,log4go我们包装过的,不方便剥离出dome来,就没放代码里

type Obj struct {
              a int
              b int64
              c string
              d bool
       }
str1 := "str1"
str2 := "str222222222222"
int1 := 1
int2 := 2

func testZapSugar() {
	w := zapcore.AddSync(&lumberjack.Logger{
		Filename:   "./foosugar.log",
		MaxSize:    500, // megabytes
		MaxBackups: 3,
		MaxAge:     28, // days
	  })
	core := zapcore.NewCore(
	zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),
		w,
		zap.InfoLevel,
	)
	logger := zap.New(core)

	sugar := logger.Sugar()
	sugar.Info("message is msg")

	count := 100000
	start := time.Now().UnixNano()
    for n := count;n > 0;n-- {
        sugar.Info("message is msg")
    }
    end := time.Now().UnixNano()
	fmt.Printf("\n zap sugar no format\n total cost %d ns\n  avg  cost %d ns \n count %d \n", end - start, (end - start)/int64(count), count)

    start = time.Now().UnixNano()
    for n := count;n > 0;n-- {
        sugar.Infof("message is %d %d %s %s %#v", int1, int2, str1, str2, obj)
    }
    end = time.Now().UnixNano()
    fmt.Printf("\n zap sugar format\n total cost %d ns\n  avg  cost %d ns \n count %d \n", end - start, (end - start)/int64(count), count)
    sugar.Sync()
}

func testZap() {
    w := zapcore.AddSync(&lumberjack.Logger{
      Filename:   "./foo.log",
      MaxSize:    500, // megabytes
      MaxBackups: 3,
      MaxAge:     28, // days
    })

    core := zapcore.NewCore(
      zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),
      w,
      zap.InfoLevel,
    )
    logger := zap.New(core)

    count := 100000
    start := time.Now().UnixNano()
    for n := count;n > 0;n-- {
        logger.Info("message is msg")
    }
    end := time.Now().UnixNano()
	fmt.Printf("\n zap no format\n total cost %d ns\n  avg  cost %d ns \n count %d \n", end - start, (end - start)/int64(count), count)

	start = time.Now().UnixNano()
    for n := count;n > 0;n-- {
        logger.Info("failed to fetch URL",
            // Structured context as strongly typed Field values.
            zap.Int("int1", int1),
            zap.Int("int2", int2),
			zap.String("str", str1),
			zap.String("str2", str2),
            zap.Any("backoff", obj),
        )
    }
    end = time.Now().UnixNano()
    fmt.Printf("\n zap format\n total cost %d ns\n  avg  cost %d ns \n count %d \n", end - start, (end - start)/int64(count), count)
    logger.Sync()
}

func testSlog() {	
	h1, err := handler.NewEmptyConfig().With(
		handler.WithLogfile("./info.log"),             //路径
		handler.WithRotateTime(handler.EveryHour),  //日志分割间隔
		handler.WithLogLevels(slog.AllLevels),         //日志level
		handler.WithBuffSize(4 * 1024 * 1024),                //buffer大小
		handler.WithCompress(true),                    //是否压缩旧日志 zip
		handler.WithBackupNum(24 * 3),                 //保留旧日志数量
		handler.WithBuffMode(handler.BuffModeBite),
		//handler.WithRenameFunc(),                    //RenameFunc build filename for rotate file
		).CreateHandler()
	if err != nil {
		fmt.Printf("Create slog handler err: %#v", err)
		return
	}
	f := slog.AsTextFormatter(h1.Formatter())
	myTplt := "[{{datetime}}] [{{level}}] [{{caller}}] {{message}}\n"
	f.SetTemplate(myTplt)
	logs := slog.NewWithHandlers(h1)
    
	count := 100000
	start := time.Now().UnixNano()
	for i := 0;i < count;i++ {
		logs.Info("message is msg")
	}
	end := time.Now().UnixNano()
	fmt.Printf("\n slog no format \n total cost %d ns\n  avg  cost %d ns \n count %d \n", end - start, (end - start)/int64(count), count)

	start = time.Now().UnixNano()
	for n := count;n > 0; n-- {
		logs.Infof("message is %d %d %s %s %#v", int1, int2, str1, str2, obj)
	}
	end = time.Now().UnixNano()
	fmt.Printf("\n slog format \n total cost %d ns\n  avg  cost %d ns \n count %d \n", end - start, (end - start)/int64(count), count)
	logs.MustFlush()
}
commented

我用你的测试代码跑的。可以拉下slog 的最新版 v0.5.1 再试试。

测试代码在 _example/issue100/issue100_test.go

=== RUN   TestZapSugar

 zap sugar no format
 total cost 883854000 ns
  avg  cost 8838 ns 
 count 100000 

 zap sugar format
 total cost 1058184000 ns
  avg  cost 10581 ns 
 count 100000 
--- PASS: TestZapSugar (1.94s)
=== RUN   TestZapLog

 zap no format
 total cost 793171000 ns
  avg  cost 7931 ns 
 count 100000 

 zap format
 total cost 1070121000 ns
  avg  cost 10701 ns 
 count 100000 
--- PASS: TestZapLog (1.86s)
=== RUN   TestSlog

 slog no format 
 total cost 486753000 ns
  avg  cost 4867 ns 
 count 100000 

 slog format 
 total cost 794033000 ns
  avg  cost 7940 ns 
 count 100000 
--- PASS: TestSlog (1.28s)
PASS