iot_server/internal/pkg/logger/logger.go

351 lines
9.4 KiB
Go

/*******************************************************************************
* Copyright 2019 Dell Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*******************************************************************************/
/*
Package logger provides a client for integration with the support-logging service. The client can also be configured
to write logs to a local file rather than sending them to a service.
*/
package logger
// Logging client for the Go implementation of edgexfoundry
import (
"fmt"
stdLog "log"
"os"
"path"
"runtime"
"strconv"
"strings"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
// Colors
const (
Reset = "\033[0m"
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Blue = "\033[34m"
Magenta = "\033[35m"
Cyan = "\033[36m"
White = "\033[37m"
BlueBold = "\033[34;1m"
MagentaBold = "\033[35;1m"
RedBold = "\033[31;1m"
YellowBold = "\033[33;1m"
)
// LoggingClient defines the interface for logging operations.
type LoggingClient interface {
// SetLogLevel sets minimum severity log level. If a logging method is called with a lower level of severity than
// what is set, it will result in no output.
SetLogLevel(logLevel string) error
// LogLevel returns the current log level setting
LogLevel() string
// Debug logs a message at the DEBUG severity level
Debug(msg string, args ...interface{})
// Error logs a message at the ERROR severity level
Error(msg string, args ...interface{})
// Info logs a message at the INFO severity level
Info(msg string, args ...interface{})
// Trace logs a message at the TRACE severity level
Trace(msg string, args ...interface{})
// Warn logs a message at the WARN severity level
Warn(msg string, args ...interface{})
// Debugf logs a formatted message at the DEBUG severity level
Debugf(msg string, args ...interface{})
// Errorf logs a formatted message at the ERROR severity level
Errorf(msg string, args ...interface{})
// Infof logs a formatted message at the INFO severity level
Infof(msg string, args ...interface{})
// Tracef logs a formatted message at the TRACE severity level
Tracef(msg string, args ...interface{})
// Warnf logs a formatted message at the WARN severity level
Warnf(msg string, args ...interface{})
}
type edgeXLogger struct {
owningServiceName string
level zap.AtomicLevel
rootLogger *zap.Logger
levelLoggers map[string]*zap.Logger
}
const (
LogPathEnvName = "LOG_PATH"
LogLevel = "LOG_LEVEL"
DefaultLogLevel = InfoLog
DefaultLogPath = ""
)
var (
zapLevels = map[string]zapcore.Level{
DebugLog: zap.DebugLevel,
InfoLog: zap.InfoLevel,
WarnLog: zap.WarnLevel,
ErrorLog: zap.ErrorLevel,
}
levels = map[zapcore.Level]string{
zap.DebugLevel: DebugLog,
zap.InfoLevel: InfoLog,
zap.WarnLevel: WarnLog,
zap.ErrorLevel: ErrorLog,
}
)
type LogMessage struct {
Time string `json:"time"`
ServiceName string `json:"service_name"`
Caller string `json:"caller"`
Message []interface{} `json:"message"`
}
func NewZapLogger(atomLevel zap.AtomicLevel, logPath string) (zapLog *zap.Logger, err error) {
stdLog.SetFlags(stdLog.LstdFlags | stdLog.Llongfile)
// 选择自定义日志样式
encoderConfig := zapcore.EncoderConfig{
MessageKey: "msg",
//StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.EpochTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
if logPath != "" {
logDir := path.Dir(logPath)
_, fileErr := os.Stat(logDir)
if fileErr != nil || !os.IsExist(fileErr) {
err = os.MkdirAll(logDir, os.ModePerm)
if err != nil {
stdLog.Fatal(fmt.Sprintf("ERROR mkdir dir %s err %+v ", logDir, err))
}
}
// 打印到文件,自动分裂
w := zapcore.AddSync(&lumberjack.Logger{
Filename: logPath,
MaxSize: 10, // megabytes
MaxBackups: 3,
MaxAge: 7, // days
})
core := zapcore.NewCore(
zapcore.NewConsoleEncoder(encoderConfig),
w,
atomLevel,
)
zapLog = zap.New(core, zap.AddCaller())
} else {
// 打印到控制台
cfg := zap.NewProductionConfig()
cfg.Level = atomLevel
cfg.Encoding = "console"
cfg.EncoderConfig = encoderConfig
zapLog, err = cfg.Build()
if err != nil {
stdLog.Fatal("ERROR ", err)
return
}
}
return
}
// NewClient creates an instance of LoggingClient
func NewClient(owningServiceName string, logLevel, logPath string) LoggingClient {
if logLevel == "" {
// 从Env环境变量获取日志等级
logLevel = os.Getenv(LogLevel)
if logLevel == "" {
logLevel = DefaultLogLevel
}
}
if logPath == "" {
logPath = os.Getenv(LogLevel)
}
if !isValidLogLevel(logLevel) {
logLevel = DebugLog
}
// Set up logging client
lc := edgeXLogger{
owningServiceName: owningServiceName,
level: zap.NewAtomicLevelAt(zapLevels[logLevel]),
}
var err error
lc.rootLogger, err = NewZapLogger(lc.level, logPath)
if err != nil {
return nil
}
lc.levelLoggers = make(map[string]*zap.Logger)
for _, level := range logLevels() {
lc.levelLoggers[level] = lc.rootLogger
}
return lc
}
// LogLevels returns an array of the possible log levels in order from most to least verbose.
func logLevels() []string {
return []string{
TraceLog,
DebugLog,
InfoLog,
WarnLog,
ErrorLog,
}
}
func isValidLogLevel(l string) bool {
for _, name := range logLevels() {
if name == l {
return true
}
}
return false
}
func (lc edgeXLogger) check(logLevel string) bool {
l := zapLevels[logLevel]
return lc.level.Enabled(l)
}
func (lc edgeXLogger) log(logLevel string, formatted bool, msg string, args ...interface{}) {
// Check minimum log level
if lc.check(logLevel) == false {
return
}
if args == nil {
args = []interface{}{msg}
} else if formatted {
args = []interface{}{fmt.Sprintf(msg, args...)}
} else {
if len(msg) > 0 {
args = append([]interface{}{msg}, args...)
}
}
argData := make([]string, 0)
for _, arg := range args {
argData = append(argData, fmt.Sprintf("%+v", arg))
}
msg = strings.Join(argData, ",")
//_, file, line, _ := runtime.Caller(2)
//idx := strings.LastIndexByte(file, '/')
//caller := file[idx+1:] + ":" + strconv.Itoa(line)
// 日志样式设置
pc, file, line, _ := runtime.Caller(2)
funcName := runtime.FuncForPC(pc).Name()
paths := strings.Split(funcName, "/")
pack := strings.Split(funcName[strings.LastIndexByte(funcName, '/')+1:], ".")[0]
paths = append(paths[:len(paths)-1], pack, strings.Split(file[strings.LastIndexByte(file, '/')+1:], ".")[0])
funcPath := strings.Join(paths, "/")
caller := funcPath + ".go:" + strconv.Itoa(line) + " "
now := time.Now().Format("2006/01/02 15:04:05")
debugStr := Yellow + "[debug] " + Reset
debugCaller := caller
infoStr := Green + "[info] " + Reset
infoCaller := caller
warnStr := Magenta + "[warn] " + Reset
warnCaller := caller
errStr := Red + "[error] " + Reset
errCaller := caller
var message = fmt.Sprintf("%v [%v] ", now, lc.owningServiceName)
// 日志输出
switch logLevel {
case DebugLog:
lc.levelLoggers[logLevel].Debug(debugStr + message + debugCaller + msg)
case InfoLog:
lc.levelLoggers[logLevel].Info(infoStr + message + infoCaller + msg)
case WarnLog:
lc.levelLoggers[logLevel].Warn(warnStr + message + warnCaller + msg)
case ErrorLog:
lc.levelLoggers[logLevel].Error(errStr + message + errCaller + msg)
}
}
func (lc edgeXLogger) SetLogLevel(logLevel string) error {
lc.level.SetLevel(zapLevels[logLevel])
return nil
}
func (lc edgeXLogger) LogLevel() string {
l := lc.level.Level()
return levels[l]
}
func (lc edgeXLogger) Info(msg string, args ...interface{}) {
lc.log(InfoLog, false, msg, args...)
}
func (lc edgeXLogger) Trace(msg string, args ...interface{}) {
lc.log(TraceLog, false, msg, args...)
}
func (lc edgeXLogger) Debug(msg string, args ...interface{}) {
lc.log(DebugLog, false, msg, args...)
}
func (lc edgeXLogger) Warn(msg string, args ...interface{}) {
lc.log(WarnLog, false, msg, args...)
}
func (lc edgeXLogger) Error(msg string, args ...interface{}) {
lc.log(ErrorLog, false, msg, args...)
}
func (lc edgeXLogger) Infof(msg string, args ...interface{}) {
lc.log(InfoLog, true, msg, args...)
}
func (lc edgeXLogger) Tracef(msg string, args ...interface{}) {
lc.log(TraceLog, true, msg, args...)
}
func (lc edgeXLogger) Debugf(msg string, args ...interface{}) {
lc.log(DebugLog, true, msg, args...)
}
func (lc edgeXLogger) Warnf(msg string, args ...interface{}) {
lc.log(WarnLog, true, msg, args...)
}
func (lc edgeXLogger) Errorf(msg string, args ...interface{}) {
lc.log(ErrorLog, true, msg, args...)
}
// Build the log entry object
func (lc edgeXLogger) buildLogEntry(logLevel string, msg string, args ...interface{}) LogEntry {
res := LogEntry{}
res.Level = logLevel
res.Message = msg
res.Args = args
res.OriginService = lc.owningServiceName
return res
}