package flog import ( "fmt" "runtime" "strings" "sync" "time" ) // All log functions var ( Debug = logFunc(LevelDebug) Info = logFunc(LevelInfo) Warning = logFunc(LevelWarning) Error = logFunc(LevelError) Critical = logFunc(LevelCritical) ) func logFunc(level LevelType) func(...interface{}) { return func(message ...interface{}) { writeMessage(level, message) } } var mu sync.Mutex func writeMessage(level LevelType, message []interface{}) { if MinLevel > level { return } entry := LogEntry{ Level: level, Time: time.Now(), Message: fmt.Sprint(message...), } if MinStackLevel <= level { entry.StackTrace = getStackTrace() } mu.Lock() defer mu.Unlock() Format.FormatMessage(getOutput(), entry) } func getStackTrace() (stackTrace []StackTraceEntry) { _, filename, _, _ := runtime.Caller(1) pc := make([]uintptr, 50) entries := runtime.Callers(2, pc) frames := runtime.CallersFrames(pc[:entries]) more, firstLine := true, false for more { var frame runtime.Frame frame, more = frames.Next() if !firstLine && frame.File == filename { // Skip frames from the flog cal continue } firstLine = true if frame.Function == `runtime.gopanic` { // If a panic occurred, start at the frame that called panic stackTrace = nil continue } stackTrace = append(stackTrace, StackTraceEntry{ Function: cleanFunction(frame.Function), File: cleanFilename(frame.File), Line: frame.Line, }) } return } func cleanFunction(f string) string { parts := strings.Split(f, `/vendor/`) return parts[len(parts)-1] } func cleanFilename(file string) string { parts := strings.Split(file, `/src/`) if len(parts) < 2 { return file } return strings.Join(parts[1:], `/`) }