351 lines
9.4 KiB
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
|
|
}
|