- vừa được xem lúc

Sử dụng uber zap thay thế cho logging mặc định của golang

0 0 7

Người đăng: Nguyen Tran Nhat Duc

Theo Viblo Asia

Nếu là một lập trình viên golang thì không thể không biết đến package log mặc định. Với ưu điểm hàm dễ sử dụng, log là ưu tiên hàng đầu đối với những lập trình viên mới vào nghề. Tuy nhiên có nhiều nhược điểm không thể không kể đến:

  • Log căn bản chỉ có Print, không hỗ trợ log nhiều tầng như Info, Debug.
  • Đối với error, chỉ có panic với Fatal giúp thoát chương trình khi gặp lỗi, không có log Error để chỉ hiển thị mà không thoát chương trình.
  • Thiếu khả năng Format, Custom logging, ví dụ như Format datetime hiển thị log.

1. Giới thiệu uber-go zap

Uber zap là một thư viện logging của golang có cấu trúc, nhiều tầng (bao gồm Print, Debug, Error,...), tốc độ nhanh.

Uber zap có 2 loại: Logger và Sugared Logger. Sugared Logger tốc độ chậm hơn Logger nhưng hỗ trợ nhiều kiểu log hơn, như hỗ trợ log kiểu printf. Nếu bạn làm một dự án lớn nơi từng microsecond hay object allocate bớt đi đều đáng giá thì logger là một lựa chọn hoàn hảo. Còn nếu muốn nhiều chức năng, không quá quan trọng tốc độ chênh lệch trên thì nên dùng Sugared Logger.

c3ofqa451co54dnk9km0So sánh tốc độ và objects allocate của các package logging trong golang

Ở đây op: số lượng iteration chương trình chạy qua trước khi kết thúc. Ví dụ for i:=0;i<n;i++ thì n đây chính là số iteration. ns: nanosecond, 1 nanosecond = 10-9 second (tức = 1/1 tỷ giây).

Có thể thấy dễ dàng tốc độ xử lý với object allocate của uber zap vượt trội hơn hẳn so với các package logging khác của golang.

2. Cài đặt zap

Đầu tiên ta cài đặt package:

go get -u go.uber.org/zap

2.1 Logger

func main() { logger,_ := zap.NewProduction() //Khởi tạo zap defer logger.Sync() //Xả hết buffer ra _,err := strconv.Atoi("ntf") logger.Error( "Error convert string to int..", zap.String("string", "ntf"), zap.Error(err))	//Log error khi convert lỗi n,err := strconv.Atoi("12") logger.Info("Success..", zap.String("convertSuccess", "12"), zap.Int("Result", n))	//Log thông tin convert thành công
}

Kết quả trả ra

Ở đây ta đã có đầy đủ thông tin với cấu trúc rõ ràng: log level error, thời điểm log (ở đây đang bị mã hóa), dòng code bắt đầu gọi đến (caller), message (msg), thông tin hiển thị ("string":"ntf","error":lỗi trả về).

2.2 Sugared Logger

Giờ thử thay thế đoạn Logger phía trên bằng Sugared Logger

func main() { logger,_ := zap.NewProduction() //Khởi tạo zap sugar := logger.Sugar() defer logger.Sync() //Xả hết buffer ra _,err := strconv.Atoi("ntf") sugar.Errorw( "Error convert string to int..", "string", "ntf", "error",err)	//Log error khi convert lỗi n,err := strconv.Atoi("12") sugar.Infow("Success..", "convertSuccess", "12", "Result", n)	//Log thông tin convert thành công
}

Kết quả cũng cho ra tương tự

Một chức năng có ở Sugared Logger mà Logger không có là printf style

logger,_ := zap.NewProduction() //Khởi tạo zap
sugar := logger.Sugar()
defer logger.Sync() //Xả hết buffer ra _,err := strconv.Atoi("ntf")
sugar.Errorf( "Error convert string: %s to int.. This error is: %s","ntf",err) _,err = strconv.Atoi("12")
sugar.Infof("Convert success %s to int", "12") 

Kết quả

Có thể thấy Logger chỉ hỗ trợ hiển thị log có cấu trúc trong khi Sugared Log hỗ trợ cả log cấu trúc lẫn printf style.

3. Custom logging

Log phía trên có nhược điểm lớn đó là thời điểm log đã bị mã hóa, cách viết liền mạch khó đọc. Ta cần format lại cho dễ đọc, có thể bớt đi field không cần thiết như stacktrace.

Để có thể custom được ta cần import package:

import( "time" "go.uber.org/zap" "go.uber.org/zap/zapcore"
) 
func ConfigZap() *zap.SugaredLogger{ cfg := zap.Config{ Encoding: "json", //encode kiểu json hoặc console Level: zap.NewAtomicLevelAt(zap.InfoLevel),	//chọn InfoLevel có thể log ở cả 3 level OutputPaths: []string{"stderr"}, EncoderConfig: zapcore.EncoderConfig{	//Cấu hình logging, sẽ không có stacktracekey MessageKey: "message", TimeKey: "time", LevelKey: "level", CallerKey: "caller", EncodeCaller: zapcore.FullCallerEncoder,	//Lấy dòng code bắt đầu log EncodeLevel: CustomLevelEncoder,	//Format cách hiển thị level log EncodeTime: SyslogTimeEncoder,	//Format hiển thị thời điểm log }, } logger, _ := cfg.Build()	//Build ra Logger return logger.Sugar()	//Trả về logger hoặc Sugaredlogger, ở đây ta chọn trả về Logger
} func SyslogTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05"))
} func CustomLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString("[" + level.CapitalString() + "]")
} 
func main() { sugarLogger := ConfigZap() sugarLogger.Infow("Get the time now with format","time",time.Now().Format("2006-January-02")) sugarLogger.Infof("Today is :%s",time.Now().Format("2006-January-02"))
}

Kết quả trả ra

Ta có thể chuyển sang hiển thị kiểu console

4. Viết vào trong file thay thế cho console

Để viết vào trong file ta sử dụng zap.New(zapcore.Core,options…)

zapcore.Core bao gồm 3 thành phần zapcore.Encoder, zapcore.WriteSyncer, zapcore.LogLevel

4.1 Khởi động

Giờ ta bắt đầu thử viết đoạn log cơ bản (chưa format gì) vào file

func SugarLog() *zap.SugaredLogger{ writerSyncer := getLogWriter() encoder := getEncoder() core := zapcore.NewCore(encoder, writerSyncer, zapcore.DebugLevel) logger := zap.New(core) return logger.Sugar()
} func getEncoder() zapcore.Encoder { return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
} func getLogWriter() zapcore.WriteSyncer { file, _ := os.Create("./test.log") return zapcore.AddSync(file)
}
func main() { sugarLogger := SugarLog() sugarLogger.Infof("Today is :%s",time.Now().Format("2006-January-02"))
}

Kết quả hiển thị: file test.log đã được tạo ra và chương trình viết đoạn log vào trong file

4.2 Custom log trong file

Giữ nguyên đoạn code trên, ta có thể custom log bằng cách thay đổi zapcore.Encoder

func getEncoder() zapcore.Encoder { return zapcore.NewJSONEncoder(zapcore.EncoderConfig{ MessageKey: "message", TimeKey: "time", LevelKey: "level", CallerKey:"caller", EncodeLevel: CustomLevelEncoder,	//Format cách hiển thị level log EncodeTime: SyslogTimeEncoder,	//Format hiển thị thời điểm log EncodeCaller: zapcore.ShortCallerEncoder,	//Format caller })
} func SyslogTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05"))
} func CustomLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString("[" + level.CapitalString() + "]")
}

Kết quả trả ra:

{"level":"[INFO]","time":"2021-07-17 14:43:15","message":"Today is :2021-July-17"}

Khi thay đổi NewJSONEncoder thành NewConsoleEncoder kết quả như sau

2021-07-17 14:44:53	[INFO]	Today is :2021-July-17

Tuy nhiên có một vấn đề: zapcore.Encoder mặc dù đã được config CallerKey không hiển thị "caller". Để khắc phục, ta cần bổ sung thêm option zap.AddCaller() vào trong zap.New():

func SugarLog() *zap.SugaredLogger{ writerSyncer := getLogWriter() encoder := getEncoder() core := zapcore.NewCore(encoder, writerSyncer, zapcore.DebugLevel) logger := zap.New(core,zap.AddCaller()) return logger.Sugar()
}

Kết quả ConsoleEncoder

2021-07-17 14:52:35	[INFO]	golangforteaching/main.go:10	Today is :2021-July-17

Kết quả JSONEncoder

{"level":"[INFO]","time":"2021-07-17 14:54:05","caller":"golangforteaching/main.go:10","message":"Today is :2021-July-17"}

5. Convert qua lại giữa Logger và Sugared Logger

Một điều cuối trước khi kết thúc bài viết là việc chuyển đổi từ logger sang SugaredLogger hay ngược lại rất đơn giản và không hề tốn công. Hầu hết các ví dụ trong bài này ta đều dùng SugaredLogger, mà được chuyển đổi từ Logger sang chỉ với một hàm .Sugar(). Thử với ví dụ trên ta thay đổi SugaredLogger thành Logger

func main() { sugarLogger := SugarLog() sugarLogger.Desugar().Info("Today is :"+time.Now().Format("2006-January-02"))
}

Vậy chỉ 2 hàm .Sugar().Desugar() là ta đã có thể convert thành công 2 kiểu logging của uber zap rồi.

6. Kết

Mong rằng những ví dụ trên giúp bạn hiểu tổng quan về uber-go zap và đem áp dụng vào trong các dự án để có một chương trình với performance tốt. Tuy khó hơn thư viện log mặc định của golang nhưng chỉ với những bước đầu custom hơi mất chút thời gian, cùng với làm quen nhiều thì mọi thứ sẽ trở thành bản năng.

Nguồn tham khảo:

https://programmersought.com/article/29732540594/

https://sunitc.dev/2019/05/27/adding-uber-go-zap-logger-to-golang-project/

https://pkg.go.dev/go.uber.org/zap

Bình luận

Bài viết tương tự

- vừa được xem lúc

gRPC - Nó là gì và có nên sử dụng hay không?

Nhân một ngày rảnh rỗi, mình ngồi đọc lại RPC cũng như gRPC viết lại để nhớ lâu hơn. Vấn đề là gì và tại sao cần nó .

0 0 131

- vừa được xem lúc

Embedded Template in Go

Getting Start. Part of developing a web application usually revolves around working with HTML as user interface.

0 0 56

- vừa được xem lúc

Tạo Resful API đơn giản với Echo framework và MySQL

1. Giới thiệu.

0 0 60

- vừa được xem lúc

Sử dụng goquery trong golang để crawler thông tin các website Việt Nam bị deface trên mirror-h.org

. Trong bài viết này, mình sẽ cùng mọi người khám phá một package thu thập dữ liệu có tên là goquery của golang. Mục tiêu chính của chương trình crawler này sẽ là lấy thông tin các website Việt Nam bị deface (là tấn công, phá hoại website, làm thay đổi giao diện hiển thị của một trang web, khi người

0 0 237

- vừa được xem lúc

Tạo ứng dụng craw dữ liệu bing với Golang, Mysql driver

Chào mọi người . Lâu lâu ta lại gặp nhau 1 lần, để tiếp tục series chia sẻ kiến thức về tech, hôm nay mình sẽ tìm hiểu và chia sẻ về 1 ngôn ngữ đang khá hot trong cộng đồng IT đó là Golang.

0 0 75

- vừa được xem lúc

Golang: Rest api and routing using MUX

Routing with MUX. Let's create a simple CRUD api for a blog site. # All . GET articles/ .

0 0 54