11package log
22
33import (
4+ "bytes"
45 "fmt"
5- "os"
66 "path/filepath"
77 "runtime"
8+ "strconv"
89 "strings"
10+ "sync"
11+ "time"
912
10- "github.com/rs/zerolog "
13+ log "github.com/sirupsen/logrus "
1114)
1215
13- var Log zerolog.Logger
16+ var (
17+ // Used for caller information initialisation
18+ callerInitOnce sync.Once
19+ logrusPackage string
20+ minimumCallerDepth = 1
21+ )
22+
23+ const (
24+ maximumCallerDepth int = 25
25+ knownLogrusFrames int = 4
26+ )
1427
1528const timeFormat = "2006-01-02 15:04:05.000"
1629
@@ -19,71 +32,128 @@ type Config struct {
1932 Level string `mapstructure:"level"`
2033}
2134
22- // Init initializes the package logger.
23- // Supported levels are: ["debug", "info", "warn", "error"]
24- func Init (level string , fixByFile , fixByFunc []string ) {
25- l := zerolog .GlobalLevel ()
26- switch level {
27- case "trace" :
28- l = zerolog .TraceLevel
29- case "debug" :
30- l = zerolog .DebugLevel
31- case "info" :
32- l = zerolog .InfoLevel
33- case "warn" :
34- l = zerolog .WarnLevel
35- case "error" :
36- l = zerolog .ErrorLevel
37- }
38- zerolog .TimeFieldFormat = timeFormat
39- output := zerolog.ConsoleWriter {Out : os .Stdout , NoColor : false , TimeFormat : timeFormat }
40- output .FormatTimestamp = func (i interface {}) string {
41- return "[" + i .(string ) + "]"
42- }
43- output .FormatLevel = func (i interface {}) string {
44- return strings .ToUpper (fmt .Sprintf ("[%-3s]" , i ))
45- }
46- output .FormatMessage = func (i interface {}) string {
47- caller , file , line , _ := runtime .Caller (9 )
48- fileName := filepath .Base (file )
49- funcName := strings .TrimPrefix (filepath .Ext ((runtime .FuncForPC (caller ).Name ())), "." )
50- var needfix bool
51- for _ , b := range fixByFile {
52- if strings .Contains (fileName , b ) {
53- needfix = true
54- }
55- }
56- for _ , b := range fixByFunc {
57- if strings .Contains (funcName , b ) {
58- needfix = true
59- }
60- }
61- if needfix {
62- caller , file , line , _ = runtime .Caller (8 )
63- fileName = filepath .Base (file )
64- funcName = strings .TrimPrefix (filepath .Ext ((runtime .FuncForPC (caller ).Name ())), "." )
65- }
66- return fmt .Sprintf ("[%d][%s][%s] => %s" , line , fileName , funcName , i )
67- }
68-
69- Log = zerolog .New (output ).Level (l ).With ().Timestamp ().Logger ()
70- }
71-
7235// Infof logs a formatted info level log to the console
73- func Infof (format string , v ... interface {}) { Log . Info (). Msgf (format , v ... ) }
36+ func Infof (format string , v ... interface {}) { log . Infof (format , v ... ) }
7437
7538// Tracef logs a formatted debug level log to the console
76- func Tracef (format string , v ... interface {}) { Log . Trace (). Msgf (format , v ... ) }
39+ func Tracef (format string , v ... interface {}) { log . Tracef (format , v ... ) }
7740
7841// Debugf logs a formatted debug level log to the console
79- func Debugf (format string , v ... interface {}) { Log . Debug (). Msgf (format , v ... ) }
42+ func Debugf (format string , v ... interface {}) { log . Debugf (format , v ... ) }
8043
8144// Warnf logs a formatted warn level log to the console
82- func Warnf (format string , v ... interface {}) { Log . Warn (). Msgf (format , v ... ) }
45+ func Warnf (format string , v ... interface {}) { log . Warnf (format , v ... ) }
8346
8447// Errorf logs a formatted error level log to the console
85- func Errorf (format string , v ... interface {}) { Log . Error (). Msgf (format , v ... ) }
48+ func Errorf (format string , v ... interface {}) { log . Errorf (format , v ... ) }
8649
8750// Panicf logs a formatted panic level log to the console.
8851// The panic() function is called, which stops the ordinary flow of a goroutine.
89- func Panicf (format string , v ... interface {}) { Log .Panic ().Msgf (format , v ... ) }
52+ func Panicf (format string , v ... interface {}) { log .Panicf (format , v ... ) }
53+
54+ type LogFormatter struct {}
55+
56+ func getPackageName (f string ) string {
57+ for {
58+ lastPeriod := strings .LastIndex (f , "." )
59+ lastSlash := strings .LastIndex (f , "/" )
60+ if lastPeriod > lastSlash {
61+ f = f [:lastPeriod ]
62+ } else {
63+ break
64+ }
65+ }
66+
67+ return f
68+ }
69+
70+ func getFuncName (f string ) string {
71+ n := strings .LastIndex (f , "/" )
72+ if n == - 1 {
73+ return f
74+ }
75+ return f [n + 1 :]
76+ }
77+
78+ // getCaller retrieves the name of the first non-logrus calling function
79+ func getCaller () * runtime.Frame {
80+ // cache this package's fully-qualified name
81+ callerInitOnce .Do (func () {
82+ pcs := make ([]uintptr , maximumCallerDepth )
83+ _ = runtime .Callers (0 , pcs )
84+
85+ // dynamic get the package name and the minimum caller depth
86+ for i := 0 ; i < maximumCallerDepth ; i ++ {
87+ funcName := runtime .FuncForPC (pcs [i ]).Name ()
88+ if strings .Contains (funcName , "getCaller" ) {
89+ logrusPackage = getPackageName (funcName )
90+ break
91+ }
92+ }
93+
94+ minimumCallerDepth = knownLogrusFrames
95+ })
96+
97+ // Restrict the lookback frames to avoid runaway lookups
98+ pcs := make ([]uintptr , maximumCallerDepth )
99+ depth := runtime .Callers (minimumCallerDepth , pcs )
100+ frames := runtime .CallersFrames (pcs [:depth ])
101+
102+ for f , again := frames .Next (); again ; f , again = frames .Next () {
103+ pkg := getPackageName (f .Function )
104+ // find the function which is not logrus and ion-log
105+ if ! strings .Contains (pkg , "logrus" ) && pkg != logrusPackage {
106+ return & f //nolint:scopelint
107+ }
108+ }
109+
110+ // if we got here, we failed to find the caller's context
111+ return nil
112+ }
113+
114+ func (s * LogFormatter ) Format (entry * log.Entry ) ([]byte , error ) {
115+ timestamp := time .Now ().Local ().Format ("2006-01-02 15:04:05" )
116+ var file string
117+ var len int
118+ // use custom getCaller because default getCaller worked bad after we wrapper it
119+ entry .Caller = getCaller ()
120+ if entry .Caller != nil {
121+ file = filepath .Base (entry .Caller .File )
122+ len = entry .Caller .Line
123+ }
124+
125+ msg := fmt .Sprintf ("[%s][%s:%d][%s][%s] %s\n " , timestamp , file , len , strings .ToUpper (entry .Level .String ()), getFuncName (entry .Caller .Function ), entry .Message )
126+ return []byte (msg ), nil
127+ }
128+
129+ // get goroutine id
130+ func getGID () uint64 {
131+ b := make ([]byte , 64 )
132+ b = b [:runtime .Stack (b , false )]
133+ b = bytes .TrimPrefix (b , []byte ("goroutine " ))
134+ b = b [:bytes .IndexByte (b , ' ' )]
135+ n , _ := strconv .ParseUint (string (b ), 10 , 64 )
136+ return n
137+ }
138+
139+ // func Init(level string) *log.Logger {
140+ func Init (level string ) {
141+ l := log .DebugLevel
142+ switch level {
143+ case "trace" :
144+ l = log .TraceLevel
145+ case "debug" :
146+ l = log .DebugLevel
147+ case "info" :
148+ l = log .InfoLevel
149+ case "warn" :
150+ l = log .WarnLevel
151+ case "error" :
152+ l = log .ErrorLevel
153+ }
154+ // log := log.New()
155+ log .SetLevel (l )
156+ log .SetReportCaller (true )
157+ log .SetFormatter (new (LogFormatter ))
158+ // return logger
159+ }
0 commit comments