package main

import (
"log"
) func init() {
log.SetPrefix("TRACE: ")
log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
} func main() {
// Println writes to the standard logger.
log.Println("message") // Fatalln is Println() followed by a call to os.Exit(1).
log.Fatalln("fatal message") // Panicln is Println() followed by a call to panic().
log.Panicln("panic message")
}

  输出

TRACE: 2019/04/09 14:24:32.868375 D:/go/TestFile/src/main/TestLog.go:15: message
TRACE: 2019/04/09 14:24:32.962329 D:/go/TestFile/src/main/TestLog.go:18: fatal message Process finished with exit code 1 log.SetPrefix("TRACE: ")设置了一个字符串,作为每个日志项的前缀。这个字符串应该是能让用户从一般的程序输出中分辨出日志的字符串。传统上这个字符串的字符会全部大写

golang.org/src/log/log.go

const (
// 将下面的位使用或运算符连接在一起,可以控制要输出的信息。没有
// 办法控制这些信息出现的顺序(下面会给出顺序)或者打印的格式
// (格式在注释里描述)。这些项后面会有一个冒号:
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
// 日期: 2009/01/23
Ldate = 1 << iota
// 时间: 01:23:23
Ltime
// 毫秒级时间: 01:23:23.123123。该设置会覆盖 Ltime 标志
Lmicroseconds
// 完整路径的文件名和行号: /a/b/c/d.go:23
Llongfile
// 最终的文件名元素和行号: d.go:23
// 覆盖 Llongfile
Lshortfile
// 标准日志记录器的初始值
LstdFlags = Ldate | Ltime
)

  

log 包有一个很方便的地方就是,这些日志记录器是多 goroutine 安全的。这意味着在多个goroutine 可以同时调用来自同一个日志记录器的这些函数,而不会有彼此间的写冲突。标准日志记录器具有这一性质,用户定制的日志记录器也应该满足这一性质。

 
.定制的日志记录器
package main

import (
"io"
"io/ioutil"
"log"
"os"
) var (
Trace *log.Logger // 记录所有日志
Info *log.Logger // 重要的信息
Warning *log.Logger // 需要注意的信息
Error *log.Logger // 非常严重的问题
) func init() {
file, err := os.OpenFile("errors.txt",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatalln("Failed to open error log file:", err)
} Trace = log.New(ioutil.Discard,
"TRACE: ",
log.Ldate|log.Ltime|log.Lshortfile) Info = log.New(os.Stdout,
"INFO: ",
log.Ldate|log.Ltime|log.Lshortfile) Warning = log.New(os.Stdout,
"WARNING: ",
log.Ldate|log.Ltime|log.Lshortfile) Error = log.New(io.MultiWriter(file, os.Stderr),
"ERROR: ",
log.Ldate|log.Ltime|log.Lshortfile)
} func main() {
Trace.Println("I have something standard to say")
Info.Println("Special Information")
Warning.Println("There is something you need to know about")
Error.Println("Something has failed")
}

  输出:

INFO: 2019/04/09 14:37:11 TestCustomLog.go:44: Special Information
ERROR: 2019/04/09 14:37:11 TestCustomLog.go:46: Something has failed
WARNING: 2019/04/09 14:37:11 TestCustomLog.go:45:
There is something you need to know about

golang.org/src/log/log.go

// New 创建一个新的 Logger。out 参数设置日志数据将被写入的目的地
// 参数 prefix 会在生成的每行日志的最开始出现
// 参数 flag 定义日志记录包含哪些属性
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}

Discard 是一个 io.Writer 接口,调用它的 Write 方法将不做任何事情并且始终成功返回。当某个等级的日志不重要时,使用 Discard 变量可以禁用这个等级的日志。

// devNull 是一个用 int 作为基础类型的类型
type devNull int
// Discard 是一个 io.Writer,所有的 Write 调用都不会有动作,但是会成功返回
var Discard io.Writer = devNull(0)
// io.Writer 接口的实现
func (devNull) Write(p []byte) (int, error) {
return len(p), nil
}

  

golang.org/src/os/file.go

// Stdin、Stdout 和 Stderr 是已经打开的文件,分别指向标准输入、标准输出和
// 标准错误的文件描述符
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
os/file_unix.go
// NewFile 用给出的文件描述符和名字返回一个新 File
func NewFile(fd uintptr, name string) *File {

可以看到 3 个变量的声明,分别表示所有操作系统里都有的 3 个标准输入/输出,即 Stdin、Stdout 和 Stderr。这 3 个变量都被声明为 File 类型的指针,这个类型实现了 io.Writer 接口。

MultiWriter 函数调用会返回一个 io.Writer接口类型值,这个值包含之前打开的文件file,以及 stderr。MultiWriter 函数是一个变参函数,可以接受任意个实现了io.Writer 接口的值。这个函数会返回一个io.Writer 值,这个值会把所有传入的 io.Writer 的值绑在一起。当对这个返回值进行写入时,会向所有绑在一起的io.Writer 值做写入。这让类似log.New 这样的函数可以同时向多个Writer 做输出。现在,当我们使用Error 记录器记录日志时,输出会同时写到文件和stderr。

每个记录器变量都包含一组方法,这组方法与 log 包里实现的那组函数完全一致

func (l *Logger) Fatal(v ...interface{})
func (l *Logger) Fatalf(format string, v ...interface{})
func (l *Logger) Fatalln(v ...interface{})
func (l *Logger) Flags() int
func (l *Logger) Output(calldepth int, s string) error
func (l *Logger) Panic(v ...interface{})
func (l *Logger) Panicf(format string, v ...interface{})
func (l *Logger) Panicln(v ...interface{})
func (l *Logger) Prefix() string
func (l *Logger) Print(v ...interface{})
func (l *Logger) Printf(format string, v ...interface{})
func (l *Logger) Println(v ...interface{})
func (l *Logger) SetFlags(flag int)
func (l *Logger) SetPrefix(prefix string)

  

二、第三方log库
1.简要汇总

参考
golang中的log库
Golang的log包哪个好用?
在Github中最受欢迎的Go日志库集合

截止2019.5.24star数统计

  • logrus 11018star 近期还在更新
  • zap 6901star 近期还在更新
  • oklog 2781star 近期还在更新
  • glog 2214star 已经很久不更新
  • seelog 1312star 已经很久不更新
  • zerolog 1981star 近期还在更新

2.选择JSON格式的日志

王健:最佳日志实践
最佳日志实践(v2.0)
达达 JSON日志文件

举个例子,在引入JSON格式的日志之后,用PHP做了一个日志查看页面,因为信息是结构化的,这个查看页面要做过滤筛选或者特殊显示就变得方便许多,不是先前做不到,而是做起来很繁琐。但是JSON日志比起二进制文件格式来说,有个明显的问题是JSON导致的文件体积膨胀。

logrus也是支持json格式日志的,可以参考Golang之使用Logrus

logrus 为什么不支持输出文件名和行号

3.性能

参考
如何开发高性能 go 组件?
Go零消耗debug log技巧
玩转高性能日志库ZAP (1)
玩转高性能日志库ZAP (2)
玩转高性能日志库ZAP(3)

看到题目,有人肯定会问,官方的log模块不好么? Debug Log一般很长,在生产环境还输出的话,也很难找。 再者,log的消耗是比较大的,特别是需要打印行号时。如果在代码中有大量的debug日志,这个损耗累积起来,那也是相当惊人的了。

在实际测试过程中发现使用的日志库 seelog 性能存在严重的瓶颈,对比结果中发现:zap 表现非常突出,单线程 Qps 也是 logrus、seelog 的数倍。

package main

import (
"github.com/logmatic/logmatic-go"
log "github.com/sirupsen/logrus"
) func main() {
// 使用 JSONFormatter
log.SetFormatter(&logmatic.JSONFormatter{})
// 使用 logrus 像往常那样记录事件
log.WithFields(log.Fields{"string": "foo", "int": 1, "float": 1.1}).Info("My first ssl event from golang")
}

  输出:

{
"date":"2019-08-19T21:44:48+08:00",
"float":1.1,
"int":1,
"level":"info",
"message":"My first ssl event from golang",
"String":"foo"
}

最常使用的简单例子就是在 Golang 日志信息前面添加日志器名称和 id。你的代码然后就会发送 “事件” 到你的标准化接口,它会继续讲它们转化为 Golang 日志消息。

// 主要部分,我们会在这里定义所有消息。
// Event 结构体很简单。为了当所有信息都被记录时能检索它们,
// 我们维护了一个 Id
var (
invalidArgMessage = Event{1, "Invalid arg: %s"}
invalidArgValueMessage = Event{2, "Invalid arg value: %s => %v"}
missingArgMessage = Event{3, "Missing arg: %s"}
) // 在我们应用程序中可以使用的所有日志事件
func (l *Logger)InvalidArg(name string) {
l.entry.Errorf(invalidArgMessage.toString(), name)
}
func (l *Logger)InvalidArgValue(name string, value interface{}) {
l.entry.WithField("arg." + name, value).Errorf(invalidArgValueMessage.toString(), name, value)
}
func (l *Logger)MissingArg(name string) {
l.entry.Errorf(missingArgMessage.toString(), name)
}

  

 
 

golang基础学习---log的更多相关文章

  1. golang基础学习及web框架

    golang的web框架 web框架百花齐放:对比 Go Web 编程 Go Web Examples Golang 适合做 Web 开发吗? beego beego简介 go-restful gol ...

  2. Golang基础学习总结

    转自:http://blog.csdn.net/yue7603835/article/details/44264925 1.不支持继承.重载 ,比如C++.Java的接口,接口的修改会影响整个实现改接 ...

  3. golang基础学习-MongoDB使用

    1.系统环境 Golang:go version go1.10.3 darwin/amd64 OS:MacOS MongoDB: version: 3.4.4 2.Golang使用MongoDB 使用 ...

  4. golang基础学习

    一.输出hello,world程序 package main; import "fmt"; func main() { fmt.Printf("hello,world&q ...

  5. Golang 基础学习笔记(2)| 如何安装Go工具

    可参考官网:http://docscn.studygolang.com/doc/install#安装 1.OS要求 gc 编译器支持以下操作系统及架构.在开始前,请确保你的系统满足这些需求. 若你的O ...

  6. golang基础学习-strings包常用函数学习

    package main import ( "fmt" "strings" ) //StrFunc 字符串说明 func main() { var testSt ...

  7. Golang 汇编asm语言基础学习

    Golang 汇编asm语言基础学习 一.CPU 基础知识 cpu 内部结构 cpu 内部主要是由寄存器.控制器.运算器和时钟四个部分组成. 寄存器:用来暂时存放指令.数据等对象.它是一个更快的内存. ...

  8. salesforce 零基础学习(五十二)Trigger使用篇(二)

    第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...

  9. Node.js系列基础学习-----回调函数,异步

    Node.js基础学习 Node.js回调函数 Node.js异步编程的直接体现就是回调,异步编程依托回调来实现,但不是异步.回调函数在完成任务后就会被调用,Node有很多的回调函数,其所有的API都 ...

随机推荐

  1. cocos: 链接错误: _lz_adler32 in liblibquickmac.a

    错误: Undefined symbols for architecture x86_64: "_adler32", referenced from: _lz_adler32 in ...

  2. Vs2017添加.NET Standard项目出现黄色未引用的SDK

    项目打开文件夹位置,按住shift键,执行dotnet restore命令

  3. Seq2Seq模型 与 Attention 策略

    Seq2Seq模型 传统的机器翻译的方法往往是基于单词与短语的统计,以及复杂的语法结构来完成的.基于序列的方式,可以看成两步,分别是 Encoder 与 Decoder,Encoder 阶段就是将输入 ...

  4. C++ 中的 inline 用法

    1.引入 inline 关键字的原因 在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数. 栈空间就是指放置程序的局部数 ...

  5. CloudFlare上线了新的Proxy Anything选项, 支持转发TCP连接

    https://www.nicho1as.wang/articles/cf-proxy-anything.html 申请地址:https://goo.gl/forms/Oc2jyyo0kXsrMyw3 ...

  6. Struts2工作原理和核心文件

    一.Struts2工作原理 如下图: 二.Struts2配置文件 1.web.xml 任何MVC框架都需要与Web应用整合,这就不得不借助于web.xml文件,只有配置了web.xml文件的Servl ...

  7. 蓝牙BLE: ATT和GATT的概念

    BLE通信由两种设备类型构成—— Client和Server. Server提供数据服务,所以一般来说设备是Server,手机是Client.Server和Client通过ATT PDU进行交互,Se ...

  8. layer.confirm 防止post重复提交

    layer.confirm("确定对该资源单的价格"+isa+value+"元吗!", function(index, layero){ $(layero).f ...

  9. [转]Vue中用props给data赋初始值遇到的问题解决

    原文地址:https://segmentfault.com/a/1190000017149162 2018-11-28更:文章发布后因为存在理解错误,经@Kim09AI同学提醒后做了调整,在此深表感谢 ...

  10. docker build提示error checking context:can't stat xxx

    现象描述 使用docker build一个镜像的时候,提示下面的错误: ➜ docker build -t image_name -f xxx.dockerfile . error checking ...