1 程序运行日志记录
我们的程序起来之后,在后台运行的时候,我们如何拿到他在运行中的一些信息呢,我们当然可以通过日志去记录。我们就得使用 log 包
[15:14:25 root@go testlog]#go doc log
package log // import "log"
Package log implements a simple logging package. It defines a type, Logger,
with methods for formatting output. It also has a predefined 'standard'
Logger accessible through helper functions Print[f|ln], Fatal[f|ln], and
Panic[f|ln], which are easier to use than creating a Logger manually. That
logger writes to standard error and prints the date and time of each logged
message. Every log message is output on a separate line: if the message
being printed does not end in a newline, the logger will add one. The Fatal
functions call os.Exit(1) after writing the log message. The Panic functions
call panic after writing the log message.
const Ldate = 1 << iota ...
func Fatal(v ...interface{})
func Fatalf(format string, v ...interface{})
func Fatalln(v ...interface{})
func Flags() int
func Output(calldepth int, s string) error
func Panic(v ...interface{})
func Panicf(format string, v ...interface{})
func Panicln(v ...interface{})
func Prefix() string
func Print(v ...interface{})
func Printf(format string, v ...interface{})
func Println(v ...interface{})
func SetFlags(flag int)
func SetOutput(w io.Writer)
func SetPrefix(prefix string)
func Writer() io.Writer
type Logger struct{ ... }
func Default() *Logger
func New(out io.Writer, prefix string, flag int) *Logger
1.1 日志的几种记录方式
1.1.1 log.Println 记录日志输出
log.Println
用法和
package main
import "log"
func main() {
log.Println("println")
}
执行我们会发现他以一种日志加上我们打印内容输出到终端上
[15:20:55 root@go testlog]#go run main.go
2021/08/04 15:21:36 println
1.1.2 log.Fatal 记录日志输出并退出程序输出状态码
Fatal
源码如下
func Fatal(v ...interface{}) {
std.Output(2, fmt.Sprint(v...))
os.Exit(1)
}
log.Fatal
用法
package main
import "log"
func main() {
log.Fatal("fatal")
fmt.Println("11111")
}
执行发现他会在终端输出我们打印的内容,并且返回一个退出状态码,直接退出程序,而没有打印 11111
[15:21:36 root@go testlog]#go run main.go
2021/08/04 15:36:32 fatal
exit status 1
之所以有状态退出码 1,是因为在他的源码中调用了 os.Exit(1)
1.1.3 log.Panic 直接报 panic 并且退出程序
panic 源码如下
// Panic is equivalent to Print() followed by a call to panic().
func Panic(v ...interface{}) {
s := fmt.Sprint(v...)
std.Output(2, s)
panic(s)
}
package main
import (
"fmt"
"log"
)
func main() {
log.Panic("panic")
fmt.Println("11111")
}
执行后发现直接报 panic 平且不会执行 fmt.Println("11111")
[15:38:04 root@go testlog]#go run main.go
2021/08/04 16:08:41 panic
panic: panic
goroutine 1 [running]:
log.Panic(0xc000074f58, 0x1, 0x1)
/usr/local/go/src/log/log.go:354 +0xae
main.main()
/data/go/day11/testlog/main.go:9 +0x65
exit status 2
之所以有状态退出码 1,是因为在他的源码中调用了 os.Exit(2)
1.2 日志记录的几种格式
日志格式有以下几个属性:
-
前缀:我们可以给日志加上前缀,通过
log.SetPrefix()
,会在输出日志之前有我们指定的字符串前缀 -
程序文件输出日志:
log.SetFlags
-
日志输入到文件中:
log.SetOutput()
1.2.1 前缀
log.SetPrefix()
定义日志输出前缀
package main
import (
"log"
)
func main() {
log.SetPrefix("testlog ")
log.Println("testlog")
}
执行发现输出的日志前面由我们的 testlog
的前缀
[17:32:57 root@go testlog]#go run main.go
testlog 2021/08/04 17:33:08 testlog
1.2.2 程序文件输出日志
也就是说我们获取到是由那个程序提供的日志信息
package main
import (
"log"
)
func main() {
log.SetFlags(log.Flags() | log.Lshortfile)
log.Print("print")
}
并且还能获取到程序报日志的代码行数
[17:47:55 root@go testlog]#go run main.go
2021/08/04 17:48:00 print
2021/08/04 17:48:00 main.go:9: print # 第 9 行输出日志
1.2.3 日志输入到文件中
log.SetOutput()
传递一个 write 对象,然后会把调用的日志写到 write 对象中
func log.SetOutput(w io.Writer)
package main
import (
"log"
"os"
)
func main() {
// 创建 test.log 文件
logfile, _ := os.Create("test.log")
// 调用 log.SetOutput(logfile) 把日志写入到 logfile 中
log.SetOutput(logfile)
log.SetFlags(log.Flags() | log.Lshortfile)
log.Print("print")
}
执行后在当前目录有了一个 test.log 文件并且记录的就是我们输出的日志信息
1.3 在 web 服务器中添加日志
在下面案例中我们通过闭包实现,闭包一般用在一堆函数有公共特征的函数,也就说这些函数有参数和返回值都是一样的时候,想在这些函数中加一些功能我们就可以使用闭包这种形式,其实也是其他语言装饰器的一种形式
我们现在每个请求来了以后都会请求到 http 包里面,http 包然后再通过我们在请求时候的 URL 会找定义的处理器方法,但是我们会发现他的请求数据都是从 Request 中来的,响应的状态码理论上我们也能通过 ResponseWriter 去获取
该功能会将用户访问的时间,还有客户端 ip 以及访问端口,请求方式,请求路径和 user-agent 信息都记录到weblog.log
中
我们这个是在 webUser 项目中 log 模块,该函数通过闭包实现
package log
import (
"fmt"
"log"
"net/http"
"os"
)
// 通过闭包的手段
// 编写日志包装函数,等会在路由中调用该函数,因为我们的 GetUser AddUser DeleteUser Edit 等多个函数满足 http.HandlerFunc 方法
func LoggerWrapper(action http.HandlerFunc) http.HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request) {
action(rw, r)
// 判断日志文件是否存在
logFile := "weblog.log"
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
fmt.Println(err)
return
}
// 写入日志文件
log.SetOutput(file)
log.SetFlags(log.Flags() | log.Lshortfile)
log.Printf(`[%s] %s %s %s`, r.RemoteAddr, r.Method, r.URL.String(), r.Header.Get("User-Agent"))
}
}