Skip to content

Instantly share code, notes, and snippets.

@chobijaeyu
Created February 23, 2020 15:08
Show Gist options
  • Save chobijaeyu/e74f4ddf8649e9be1f38d01a13623ea6 to your computer and use it in GitHub Desktop.
Save chobijaeyu/e74f4ddf8649e9be1f38d01a13623ea6 to your computer and use it in GitHub Desktop.
package ulogger
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"strings"
"sync"
"time"
)
// 默认日志输出
var defaultLogger *LocalLogger
// 日志等级,从0-7,日优先级由高到低
const (
LevelEmergency = iota // 系统级紧急,比如磁盘出错,内存异常,网络不可用等
LevelAlert // 系统级警告,比如数据库访问异常,配置文件出错等
LevelCritical // 系统级危险,比如权限出错,访问异常等
LevelError // 用户级错误
LevelWarning // 用户级警告
LevelInformational // 用户级信息
LevelDebug // 用户级调试
LevelTrace // 用户级基本输出
)
// 日志等级和描述映射关系
var LevelMap = map[string]int{
"EMER": LevelEmergency,
"ALRT": LevelAlert,
"CRIT": LevelCritical,
"EROR": LevelError,
"WARN": LevelWarning,
"INFO": LevelInformational,
"DEBG": LevelDebug,
"TRAC": LevelTrace,
}
// 注册实现的适配器, 当前支持控制台,文件和网络输出
var adapters = make(map[string]Logger)
// 日志记录等级字段
var levelPrefix = [LevelTrace + 1]string{
"EMER",
"ALRT",
"CRIT",
"EROR",
"WARN",
"INFO",
"DEBG",
"TRAC",
}
const (
logTimeDefaultFormat = "2006-01-02 15:04:05" // 日志输出默认格式
AdapterConsole = "console" // 控制台输出配置项
AdapterFile = "file" // 文件输出配置项
AdapterConn = "conn" // 网络输出配置项
)
// log provider interface
type Logger interface {
Init(config string) error
LogWrite(when time.Time, msg interface{}, level int) error
Destroy()
}
// 日志输出适配器注册,log需要实现Init,LogWrite,Destroy方法
func Register(name string, log Logger) {
if log == nil {
panic("logs: Register provide is nil")
}
if _, ok := adapters[name]; ok {
panic("logs: Register called twice for provider " + name)
}
adapters[name] = log
}
type loginfo struct {
Time string
Level string
Path string
Name string
Content string
EvenType string
}
type nameLogger struct {
Logger
name string
config string
}
type LocalLogger struct {
lock sync.Mutex
init bool
outputs []*nameLogger
appName string
callDepth int
timeFormat string
usePath string
}
func NewLogger(depth ...int) *LocalLogger {
dep := append(depth, 2)[0]
l := new(LocalLogger)
// appName用于记录网络传输时标记的程序发送方,
// 通过环境变量APPSN进行设置,默认为NONE,此时无法通过网络日志检索区分不同服务发送方
appSn := os.Getenv("APPSN")
if appSn == "" {
appSn = "NONE"
}
l.appName = "[" + appSn + "]"
l.callDepth = dep
l.SetLogger(AdapterConsole)
l.timeFormat = logTimeDefaultFormat
return l
}
//配置文件
type logConfig struct {
TimeFormat string `json:"TimeFormat"`
File *fileLogger `json:"File,omitempty"`
}
func init() {
defaultLogger = NewLogger(3)
}
func (this *LocalLogger) SetLogger(adapterName string, configs ...string) error {
this.lock.Lock()
defer this.lock.Unlock()
if !this.init {
this.outputs = []*nameLogger{}
this.init = true
}
config := append(configs, "{}")[0]
var num int = -1
var i int
var l *nameLogger
for i, l = range this.outputs {
if l.name == adapterName {
if l.config == config {
//配置没有变动,不重新设置
return fmt.Errorf("you have set same config for this adaptername %s", adapterName)
}
l.Logger.Destroy()
num = i
break
}
}
logger, ok := adapters[adapterName]
if !ok {
return fmt.Errorf("unknown adaptername %s (forgotten Register?)", adapterName)
}
err := logger.Init(config)
if err != nil {
fmt.Fprintf(os.Stderr, "logger Init <%s> err:%v, %s output ignore!\n",
adapterName, err, adapterName)
return err
}
if num >= 0 {
this.outputs[i] = &nameLogger{name: adapterName, Logger: logger, config: config}
return nil
}
this.outputs = append(this.outputs, &nameLogger{name: adapterName, Logger: logger, config: config})
return nil
}
func (this *LocalLogger) DelLogger(adapterName string) error {
this.lock.Lock()
defer this.lock.Unlock()
outputs := []*nameLogger{}
for _, lg := range this.outputs {
if lg.name == adapterName {
lg.Destroy()
} else {
outputs = append(outputs, lg)
}
}
if len(outputs) == len(this.outputs) {
return fmt.Errorf("logs: unknown adaptername %s (forgotten Register?)", adapterName)
}
this.outputs = outputs
return nil
}
// 设置日志起始路径
func (this *LocalLogger) SetLogPathTrim(trimPath string) {
this.usePath = trimPath
}
func (this *LocalLogger) writeToLoggers(when time.Time, msg *loginfo, level int) {
for _, l := range this.outputs {
if l.name == AdapterConn {
//网络日志,使用json格式发送,此处使用结构体,用于类似ElasticSearch功能检索
err := l.LogWrite(when, msg, level)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err)
}
continue
}
msgStr := msg.EvenType + " " + when.Format(this.timeFormat) + " [" + msg.Level + "] " + "[" + msg.Path + "] " + msg.Content
err := l.LogWrite(when, msgStr, level)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err)
}
}
}
func (this *LocalLogger) writeMsg(logLevel int, msg string, v ...interface{}) error {
if !this.init {
this.SetLogger(AdapterConsole)
}
msgSt := new(loginfo)
src := ""
if len(v) > 0 {
msg = fmt.Sprintf(msg, v...)
}
when := time.Now()
_, file, lineno, ok := runtime.Caller(this.callDepth)
var strim string = "src/"
if this.usePath != "" {
strim = this.usePath
}
if ok {
src = strings.Replace(
fmt.Sprintf("%s:%d", stringTrim(file, strim), lineno), "%2e", ".", -1)
}
msgSt.Level = levelPrefix[logLevel]
msgSt.Path = src
msgSt.Content = msg
msgSt.Name = this.appName
msgSt.Time = when.Format(this.timeFormat)
this.writeToLoggers(when, msgSt, logLevel)
return nil
}
func (this *LocalLogger) writeInfoMsg(logLevel int, msg string, v ...interface{}) error {
if !this.init {
this.SetLogger(AdapterConsole)
}
msgSt := new(loginfo)
src := ""
if len(v) > 0 {
msg = fmt.Sprintf(msg, v...)
}
when := time.Now()
_, file, lineno, ok := runtime.Caller(this.callDepth)
var strim string = "src/"
if this.usePath != "" {
strim = this.usePath
}
if ok {
src = strings.Replace(
fmt.Sprintf("%s:%d", stringTrim(file, strim), lineno), "%2e", ".", -1)
}
msgSt.Level = levelPrefix[logLevel]
msgSt.Path = src
msgSt.Content = msg[2:len(msg)]
msgSt.Name = this.appName
msgSt.Time = when.Format(this.timeFormat)
msgSt.EvenType = msg[0:2]
this.writeToLoggers(when, msgSt, logLevel)
return nil
}
func (this *LocalLogger) Fatal(format string, args ...interface{}) {
this.Emer("###Exec Panic:"+format, args...)
os.Exit(1)
}
func (this *LocalLogger) Panic(format string, args ...interface{}) {
this.Emer("###Exec Panic:"+format, args...)
panic(fmt.Sprintf(format, args...))
}
// Emer Log EMERGENCY level message.
func (this *LocalLogger) Emer(format string, v ...interface{}) {
this.writeMsg(LevelEmergency, format, v...)
}
// Alert Log ALERT level message.
func (this *LocalLogger) Alert(format string, v ...interface{}) {
this.writeMsg(LevelAlert, format, v...)
}
// Crit Log CRITICAL level message.
func (this *LocalLogger) Crit(format string, v ...interface{}) {
this.writeMsg(LevelCritical, format, v...)
}
// Error Log ERROR level message.
func (this *LocalLogger) Error(format string, v ...interface{}) {
this.writeInfoMsg(LevelError, format, v...)
}
// Warn Log WARNING level message.
func (this *LocalLogger) Warn(format string, v ...interface{}) {
this.writeInfoMsg(LevelWarning, format, v...)
}
// Info Log INFO level message.
func (this *LocalLogger) Info(format string, v ...interface{}) {
this.writeInfoMsg(LevelInformational, format, v...)
}
// Debug Log DEBUG level message.
func (this *LocalLogger) Debug(format string, v ...interface{}) {
this.writeMsg(LevelDebug, format, v...)
}
// Trace Log TRAC level message.
func (this *LocalLogger) Trace(format string, v ...interface{}) {
this.writeMsg(LevelTrace, format, v...)
}
func (this *LocalLogger) Close() {
for _, l := range this.outputs {
l.Destroy()
}
this.outputs = nil
}
func (this *LocalLogger) Reset() {
for _, l := range this.outputs {
l.Destroy()
}
this.outputs = nil
}
func (this *LocalLogger) SetCallDepth(depth int) {
this.callDepth = depth
}
// GetlocalLogger returns the defaultLogger
func GetlocalLogger() *LocalLogger {
return defaultLogger
}
// Reset will remove all the adapter
func Reset() {
defaultLogger.Reset()
}
func SetLogPathTrim(trimPath string) {
defaultLogger.SetLogPathTrim(trimPath)
}
// param 可以是log配置文件名,也可以是log配置内容,默认DEBUG输出到控制台
func SetLogger(param ...string) error {
if 0 == len(param) {
//默认只输出到控制台
defaultLogger.SetLogger(AdapterConsole)
return nil
}
c := param[0]
conf := new(logConfig)
err := json.Unmarshal([]byte(c), conf)
if err != nil { //不是json,就认为是配置文件,如果都不是,打印日志,然后退出
// Open the configuration file
fd, err := os.Open(c)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not open %s for configure: %s\n", c, err)
os.Exit(1)
return err
}
contents, err := ioutil.ReadAll(fd)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not read %s: %s\n", c, err)
os.Exit(1)
return err
}
err = json.Unmarshal(contents, conf)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not Unmarshal %s: %s\n", contents, err)
os.Exit(1)
return err
}
}
if conf.TimeFormat != "" {
defaultLogger.timeFormat = conf.TimeFormat
}
if conf.File != nil {
file, _ := json.Marshal(conf.File)
defaultLogger.SetLogger(AdapterFile, string(file))
}
return nil
}
// Painc logs a message at emergency level and panic.
func Painc(f interface{}, v ...interface{}) {
defaultLogger.Panic(formatLog(f, v...))
}
// Fatal logs a message at emergency level and exit.
func Fatal(f interface{}, v ...interface{}) {
defaultLogger.Fatal(formatLog(f, v...))
}
// Emer logs a message at emergency level.
func Emer(f interface{}, v ...interface{}) {
defaultLogger.Emer(formatLog(f, v...))
}
// Alert logs a message at alert level.
func Alert(f interface{}, v ...interface{}) {
defaultLogger.Alert(formatLog(f, v...))
}
// Crit logs a message at critical level.
func Crit(f interface{}, v ...interface{}) {
defaultLogger.Crit(formatLog(f, v...))
}
// Error logs a message at error level.
func Error(f interface{}, v ...interface{}) {
defaultLogger.Error(formatLog(f, v...))
}
// Warn logs a message at warning level.
func Warn(f interface{}, v ...interface{}) {
defaultLogger.Warn(formatLog(f, v...))
}
// Info logs a message at info level.
func Info(f interface{}, v ...interface{}) {
defaultLogger.Info(formatLog(f, v...))
}
// Notice logs a message at debug level.
func Debug(f interface{}, v ...interface{}) {
defaultLogger.Debug(formatLog(f, v...))
}
// Trace logs a message at trace level.
func Trace(f interface{}, v ...interface{}) {
defaultLogger.Trace(formatLog(f, v...))
}
func formatLog(f interface{}, v ...interface{}) string {
var msg string
switch f.(type) {
case string:
msg = f.(string)
if len(v) == 0 {
return msg
}
if strings.Contains(msg, "%") && !strings.Contains(msg, "%%") {
//format string
} else {
//do not contain format char
msg += strings.Repeat(" %v", len(v))
}
default:
msg = fmt.Sprint(f)
if len(v) == 0 {
return msg
}
msg += strings.Repeat(" %v", len(v))
}
return fmt.Sprintf(msg, v...)
}
func stringTrim(s string, cut string) string {
ss := strings.SplitN(s, cut, 2)
if 1 == len(ss) {
return ss[0]
}
return ss[1]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment