Go标准库日志打印及同时输出到控制台与文件

作者:Go和分布式IM 时间:2024-04-26 17:22:40 

打印

在使用go写一些小程序时,我们没必要引入额外的包,直接使用fmt标准包打印即可:

import "fmt"

func main() {
  fmt.Println("line1")
  fmt.Print("line2")
  fmt.Printf("line%d \n", 3)

str1 := fmt.Sprintln("hello", 3)
  str2 := fmt.Sprint("hello ", 1, " 2")
  str3 := fmt.Sprintf("hello %d", 1)
  fmt.Print(str1, str2, str3)
}

line1
line2line3 
hello 3
hello 1 2hello 1

那么,有些场景下,我们希望能同时打印到日志文件中要怎么办呢?

log包

标准库提供了log组件,用法和fmt一致,有3种方式:

import “log"

func main() {
  log.Println("line1")
  log.Print("line2")
  log.Printf("line%d \n", 3)
}

和fmt的区别就是多了时间:

2021/08/25 17:23:47 line1
2021/08/25 17:23:47 line2
2021/08/25 17:23:47 line3 

我们通过SetFlag函数,可以设置打印的格式:

// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
const (
  Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23
  Ltime                         // the time in the local time zone: 01:23:23
  Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
  Llongfile                     // full file name and line number: /a/b/c/d.go:23
  Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
  LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone
  Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message
  LstdFlags     = Ldate | Ltime // initial values for the standard logger
)

比如,我们只需要时间和文件名:

import “log"

func main() {
  log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

log.Println("line1")
  log.Print("line2")
  log.Printf("line%d \n", 3)
}

此时,再次运行,则会打印文件和行号:

2021/08/25 17:27:56 mod_unread_redis.go:32: line1
2021/08/25 17:27:56 mod_unread_redis.go:33: line2
2021/08/25 17:27:56 mod_unread_redis.go:34: line3

如何输出日志到文件?

log包使用非常简单,默认情况下,只会输出到控制台。

我们可以使用SetOutput改变输出流,比如输出到文件。

先来看一下函数原型,其接收一个io.Writer接口:

// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
   // ...
}

那么,我们就可以创建一个文件流设置一下就行了。

// 创建、追加、读写,777,所有权限
f, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
  return
}
defer func() {
  f.Close()
}()

log.SetOutput(f)

此时,在运行,我们发现日志会输出到文件,但是控制台没有任何东西输出了。

如何同时输出到控制台和文件?

标准库io包中,有一个MultiWriter,可以把文件流和控制台标准输出流整合到一个io.Writer上,其实现上就是一个数组,在执行写操作时,遍历数组:

// MultiWriter creates a writer that duplicates its writes to all the
// provided writers, similar to the Unix tee(1) command.
//
// Each write is written to each listed writer, one at a time.
// If a listed writer returns an error, that overall write operation
// stops and returns the error; it does not continue down the list.
func MultiWriter(writers ...Writer) Writer {
  allWriters := make([]Writer, 0, len(writers))
  for _, w := range writers {
     if mw, ok := w.(*multiWriter); ok {
        allWriters = append(allWriters, mw.writers...)
     } else {
        allWriters = append(allWriters, w)
     }
  }
  return &multiWriter{allWriters}
}

// 重写io.Writer的Write函数函数,本质上就是遍历数组,比较巧妙
func (t *multiWriter) Write(p []byte) (n int, err error) {
  for _, w := range t.writers {
     n, err = w.Write(p)
     if err != nil {
        return
     }
     if n != len(p) {
        err = ErrShortWrite
        return
     }
  }
  return len(p), nil
}

使用方式如下:

func main() {
  f, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
  if err != nil {
     return
  }
  defer func() {
     f.Close()
  }()

// 组合一下即可,os.Stdout代表标准输出流
  multiWriter := io.MultiWriter(os.Stdout, f)
  log.SetOutput(multiWriter)

log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

log.Println("line1")
  log.Print("line2")
  log.Printf("line%d \n", 3)
}

此时,再运行,则会同时输出到控制台和文件中。

2021/08/25 17:38:02 mod_unread_redis.go:42: line1
2021/08/25 17:38:02 mod_unread_redis.go:43: line2
2021/08/25 17:38:02 mod_unread_redis.go:44: line3 

Go标准库日志打印及同时输出到控制台与文件

附:日志切割(按文件大小切割、按日期切割)

其实就是每次记录文件的大小,超过了就重新写一个文件。

通过Stat()函数拿到文件的一些信息

open, _:= os.Open("文件名")
   stat, _, := open.Stat()
   stat.Size()//拿到文件大小

日期切割:

拿到文件的名称或者检查下有没有当天的日志文件,没有就创建新增。

来源:https://blog.csdn.net/xmcy001122/article/details/119916227

标签:go,日志,打印
0
投稿

猜你喜欢

  • HTML5 离线存储之Web SQL

    2011-06-19 14:13:19
  • 深入了解Python中pop和remove的使用方法

    2021-02-14 00:32:43
  • Python GAE、Django导出Excel的方法

    2023-02-27 13:59:09
  • 互联网科技大佬推荐的12本必读书籍

    2022-08-23 12:56:38
  • python批量读取文件名并写入txt文件中

    2021-02-18 16:32:29
  • 老版本PHP转义Json里的特殊字符的函数

    2023-11-06 02:32:18
  • Python截取字符串的简单方法实例

    2023-02-20 04:54:01
  • django将网络中的图片,保存成model中的ImageField的实例

    2023-12-23 01:11:33
  • python读取excel进行遍历/xlrd模块操作

    2022-11-09 18:44:51
  • 关于 Flash Banner 设计的建议

    2010-02-02 18:19:00
  • JavaScript实现简单随机点名器

    2024-04-10 16:18:39
  • 在python下读取并展示raw格式的图片实例

    2022-07-02 18:47:56
  • php使用socket简单实现通信功能

    2023-05-31 13:01:23
  • Pycharm直接使用远程服务器代码并调试的解决方法

    2021-05-13 10:21:03
  • Python中使用语句导入模块或包的机制研究

    2023-02-21 15:06:40
  • mysql中关于Myisam_recover自动修复的使用方法

    2024-01-17 04:21:51
  • 用pushplus+python监控亚马逊到货动态推送微信

    2022-02-12 16:34:11
  • selenium+python截图不成功的解决方法

    2021-06-02 23:25:06
  • vue计时器的实现方法

    2024-04-30 10:24:27
  • Access数据库安全问答

    2007-08-23 15:28:00
  • asp之家 网络编程 m.aspxhome.com