2019-02-02 16:18:25 +08:00
|
|
|
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2018-04-19 16:24:48 +08:00
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-04-19 16:24:48 +08:00
|
|
|
|
|
|
|
package glog
|
|
|
|
|
|
|
|
import (
|
2019-05-21 21:52:17 +08:00
|
|
|
"bytes"
|
2019-05-15 23:53:48 +08:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/gogf/gf/g/os/gfile"
|
|
|
|
"github.com/gogf/gf/g/os/gfpool"
|
|
|
|
"github.com/gogf/gf/g/os/gtime"
|
|
|
|
"github.com/gogf/gf/g/text/gregex"
|
2019-06-01 11:01:57 +08:00
|
|
|
"github.com/gogf/gf/g/util/gconv"
|
2019-05-15 23:53:48 +08:00
|
|
|
"io"
|
|
|
|
"os"
|
2019-06-18 19:19:43 +08:00
|
|
|
"regexp"
|
2019-05-15 23:53:48 +08:00
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2018-04-19 16:24:48 +08:00
|
|
|
)
|
|
|
|
|
2018-10-09 21:41:07 +08:00
|
|
|
type Logger struct {
|
2019-06-19 09:06:52 +08:00
|
|
|
parent *Logger // Parent logger.
|
|
|
|
writer io.Writer // Customized io.Writer.
|
|
|
|
flags int // Extra flags for logging output features.
|
|
|
|
path string // Logging directory path.
|
|
|
|
file string // Format for logging file.
|
|
|
|
level int // Output level.
|
|
|
|
prefix string // Prefix string for every logging content.
|
|
|
|
btSkip int // Skip count for backtrace.
|
|
|
|
btStatus int // Backtrace status(1: enabled - default; 0: disabled)
|
|
|
|
headerPrint bool // Print header or not(true in default).
|
|
|
|
stdoutPrint bool // Output to stdout or not(true in default).
|
2018-10-09 21:41:07 +08:00
|
|
|
}
|
|
|
|
|
2018-04-19 16:24:48 +08:00
|
|
|
const (
|
2019-06-19 09:06:52 +08:00
|
|
|
gDEFAULT_FILE_FORMAT = `{Y-m-d}.log`
|
|
|
|
gDEFAULT_FILE_POOL_FLAGS = os.O_CREATE | os.O_WRONLY | os.O_APPEND
|
|
|
|
gDEFAULT_FPOOL_PERM = os.FileMode(0666)
|
|
|
|
gDEFAULT_FPOOL_EXPIRE = 60000
|
2018-04-19 16:24:48 +08:00
|
|
|
)
|
|
|
|
|
2019-05-21 21:52:17 +08:00
|
|
|
const (
|
2019-06-01 19:34:03 +08:00
|
|
|
F_ASYNC = 1 << iota // Print logging content asynchronously。
|
|
|
|
F_FILE_LONG // Print full file name and line number: /a/b/c/d.go:23.
|
2019-05-21 23:57:03 +08:00
|
|
|
F_FILE_SHORT // Print final file name element and line number: d.go:23. overrides F_FILE_LONG.
|
|
|
|
F_TIME_DATE // Print the date in the local time zone: 2009-01-23.
|
|
|
|
F_TIME_TIME // Print the time in the local time zone: 01:23:23.
|
|
|
|
F_TIME_MILLI // Print the time with milliseconds in the local time zone: 01:23:23.675.
|
2019-06-19 09:06:52 +08:00
|
|
|
F_TIME_STD = F_TIME_DATE | F_TIME_MILLI
|
2019-05-21 21:52:17 +08:00
|
|
|
)
|
|
|
|
|
2018-10-18 13:43:00 +08:00
|
|
|
var (
|
2019-06-19 09:06:52 +08:00
|
|
|
// Default line break.
|
|
|
|
ln = "\n"
|
2018-10-18 13:43:00 +08:00
|
|
|
)
|
2018-05-23 16:41:21 +08:00
|
|
|
|
|
|
|
func init() {
|
2019-04-17 23:50:37 +08:00
|
|
|
// Initialize log line breaks depending on underlying os.
|
2019-06-19 09:06:52 +08:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
ln = "\r\n"
|
|
|
|
}
|
2018-05-23 16:41:21 +08:00
|
|
|
}
|
|
|
|
|
2019-04-17 23:50:37 +08:00
|
|
|
// New creates and returns a custom logger.
|
2018-08-28 17:06:49 +08:00
|
|
|
func New() *Logger {
|
2019-06-19 09:06:52 +08:00
|
|
|
logger := &Logger{
|
|
|
|
file: gDEFAULT_FILE_FORMAT,
|
|
|
|
flags: F_TIME_STD,
|
|
|
|
level: LEVEL_ALL,
|
|
|
|
btStatus: 1,
|
|
|
|
headerPrint: true,
|
|
|
|
stdoutPrint: true,
|
|
|
|
}
|
|
|
|
return logger
|
2018-08-24 23:41:58 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// Clone returns a new logger, which is the clone the current logger.
|
2018-08-28 17:06:49 +08:00
|
|
|
func (l *Logger) Clone() *Logger {
|
2019-06-01 11:01:57 +08:00
|
|
|
logger := Logger{}
|
2019-06-19 09:06:52 +08:00
|
|
|
logger = *l
|
2019-06-01 11:01:57 +08:00
|
|
|
logger.parent = l
|
|
|
|
return &logger
|
2018-08-24 23:41:58 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// SetLevel sets the logging level.
|
2018-08-30 13:00:49 +08:00
|
|
|
func (l *Logger) SetLevel(level int) {
|
2019-06-19 09:06:52 +08:00
|
|
|
l.level = level
|
2018-08-30 13:00:49 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// GetLevel returns the logging level value.
|
2018-08-30 13:54:45 +08:00
|
|
|
func (l *Logger) GetLevel() int {
|
2019-06-19 09:06:52 +08:00
|
|
|
return l.level
|
2018-08-30 13:54:45 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// SetDebug enables/disables the debug level for logger.
|
2019-04-17 23:50:37 +08:00
|
|
|
// The debug level is enabled in default.
|
2018-08-30 13:00:49 +08:00
|
|
|
func (l *Logger) SetDebug(debug bool) {
|
2019-06-19 09:06:52 +08:00
|
|
|
if debug {
|
|
|
|
l.level = l.level | LEVEL_DEBU
|
|
|
|
} else {
|
|
|
|
l.level = l.level & ^LEVEL_DEBU
|
|
|
|
}
|
2018-08-30 13:00:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// SetAsync enables/disables async logging output feature.
|
|
|
|
func (l *Logger) SetAsync(enabled bool) {
|
|
|
|
if enabled {
|
|
|
|
l.flags = l.flags | F_ASYNC
|
|
|
|
} else {
|
|
|
|
l.flags = l.flags & ^F_ASYNC
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 23:57:03 +08:00
|
|
|
// SetFlags sets extra flags for logging output features.
|
2019-05-21 21:52:17 +08:00
|
|
|
func (l *Logger) SetFlags(flags int) {
|
|
|
|
l.flags = flags
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFlags returns the flags of logger.
|
|
|
|
func (l *Logger) GetFlags() int {
|
|
|
|
return l.flags
|
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
|
2018-08-30 13:00:49 +08:00
|
|
|
func (l *Logger) SetBacktrace(enabled bool) {
|
2019-06-19 09:06:52 +08:00
|
|
|
if enabled {
|
|
|
|
l.btStatus = 1
|
|
|
|
} else {
|
|
|
|
l.btStatus = 0
|
|
|
|
}
|
2018-08-30 13:00:49 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// SetBacktraceSkip sets the backtrace offset from the end point.
|
2018-10-10 18:39:57 +08:00
|
|
|
func (l *Logger) SetBacktraceSkip(skip int) {
|
2019-06-19 09:06:52 +08:00
|
|
|
l.btSkip = skip
|
2018-10-10 18:39:57 +08:00
|
|
|
}
|
|
|
|
|
2019-04-17 23:50:37 +08:00
|
|
|
// SetWriter sets the customized logging <writer> for logging.
|
2019-02-20 11:16:10 +08:00
|
|
|
// The <writer> object should implements the io.Writer interface.
|
2019-04-17 23:50:37 +08:00
|
|
|
// Developer can use customized logging <writer> to redirect logging output to another service,
|
2019-02-20 11:16:10 +08:00
|
|
|
// eg: kafka, mysql, mongodb, etc.
|
2018-11-06 18:53:25 +08:00
|
|
|
func (l *Logger) SetWriter(writer io.Writer) {
|
2019-06-19 09:06:52 +08:00
|
|
|
l.writer = writer
|
2018-04-19 16:24:48 +08:00
|
|
|
}
|
|
|
|
|
2019-04-17 23:50:37 +08:00
|
|
|
// GetWriter returns the customized writer object, which implements the io.Writer interface.
|
2019-05-23 20:34:06 +08:00
|
|
|
// It returns nil if no writer previously set.
|
2018-10-18 13:43:00 +08:00
|
|
|
func (l *Logger) GetWriter() io.Writer {
|
2019-06-19 09:06:52 +08:00
|
|
|
return l.writer
|
2018-04-19 16:24:48 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// getFilePointer returns the file pinter for file logging.
|
2019-04-17 23:50:37 +08:00
|
|
|
// It returns nil if file logging is disabled, or file opening fails.
|
2018-11-11 14:21:22 +08:00
|
|
|
func (l *Logger) getFilePointer() *gfpool.File {
|
2019-06-19 09:06:52 +08:00
|
|
|
if path := l.path; path != "" {
|
|
|
|
// Content containing "{}" in the file name is formatted using gtime
|
|
|
|
file, _ := gregex.ReplaceStringFunc(`{.+?}`, l.file, func(s string) string {
|
|
|
|
return gtime.Now().Format(strings.Trim(s, "{}"))
|
|
|
|
})
|
|
|
|
// Create path if it does not exist。
|
|
|
|
if !gfile.Exists(path) {
|
|
|
|
if err := gfile.Mkdir(path); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if fp, err := gfpool.Open(
|
|
|
|
path+gfile.Separator+file,
|
|
|
|
gDEFAULT_FILE_POOL_FLAGS,
|
|
|
|
gDEFAULT_FPOOL_PERM,
|
|
|
|
gDEFAULT_FPOOL_EXPIRE); err == nil {
|
|
|
|
return fp
|
|
|
|
} else {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2018-04-19 16:24:48 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// SetPath sets the directory path for file logging.
|
2018-04-19 16:24:48 +08:00
|
|
|
func (l *Logger) SetPath(path string) error {
|
2019-06-19 09:06:52 +08:00
|
|
|
if path == "" {
|
|
|
|
return errors.New("path is empty")
|
|
|
|
}
|
|
|
|
if !gfile.Exists(path) {
|
|
|
|
if err := gfile.Mkdir(path); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, fmt.Sprintf(`[glog] mkdir "%s" failed: %s`, path, err.Error()))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
l.path = strings.TrimRight(path, gfile.Separator)
|
|
|
|
return nil
|
2018-04-19 16:24:48 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// GetPath returns the logging directory path for file logging.
|
|
|
|
// It returns empty string if no directory path set.
|
2018-10-10 18:39:57 +08:00
|
|
|
func (l *Logger) GetPath() string {
|
2019-06-19 09:06:52 +08:00
|
|
|
return l.path
|
2018-10-10 18:39:57 +08:00
|
|
|
}
|
|
|
|
|
2019-02-20 11:16:10 +08:00
|
|
|
// SetFile sets the file name <pattern> for file logging.
|
|
|
|
// Datetime pattern can be used in <pattern>, eg: access-{Ymd}.log.
|
|
|
|
// The default file name pattern is: Y-m-d.log, eg: 2018-01-01.log
|
|
|
|
func (l *Logger) SetFile(pattern string) {
|
2019-06-19 09:06:52 +08:00
|
|
|
l.file = pattern
|
2019-05-21 21:52:17 +08:00
|
|
|
}
|
|
|
|
|
2019-05-23 11:49:33 +08:00
|
|
|
// SetStdoutPrint sets whether output the logging contents to stdout, which is true in default.
|
2019-05-21 21:52:17 +08:00
|
|
|
func (l *Logger) SetStdoutPrint(enabled bool) {
|
2019-06-19 09:06:52 +08:00
|
|
|
l.stdoutPrint = enabled
|
2019-05-21 21:52:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetHeaderPrint sets whether output header of the logging contents, which is true in default.
|
|
|
|
func (l *Logger) SetHeaderPrint(enabled bool) {
|
|
|
|
l.headerPrint = enabled
|
2018-10-09 21:41:07 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 21:52:17 +08:00
|
|
|
// SetPrefix sets prefix string for every logging content.
|
|
|
|
// Prefix is part of header, which means if header output is shut, no prefix will be output.
|
|
|
|
func (l *Logger) SetPrefix(prefix string) {
|
|
|
|
l.prefix = prefix
|
2018-05-24 12:37:55 +08:00
|
|
|
}
|
|
|
|
|
2019-04-17 23:50:37 +08:00
|
|
|
// print prints <s> to defined writer, logging file or passed <std>.
|
2019-06-19 09:06:52 +08:00
|
|
|
func (l *Logger) print(std io.Writer, lead string, value ...interface{}) {
|
2019-06-01 11:01:57 +08:00
|
|
|
buffer := bytes.NewBuffer(nil)
|
2019-06-19 09:06:52 +08:00
|
|
|
if l.headerPrint {
|
|
|
|
// Time.
|
|
|
|
timeFormat := ""
|
|
|
|
if l.flags&F_TIME_DATE > 0 {
|
|
|
|
timeFormat += "2006-01-02 "
|
|
|
|
}
|
|
|
|
if l.flags&F_TIME_TIME > 0 {
|
|
|
|
timeFormat += "15:04:05 "
|
|
|
|
}
|
|
|
|
if l.flags&F_TIME_MILLI > 0 {
|
|
|
|
timeFormat += "15:04:05.000 "
|
|
|
|
}
|
|
|
|
if len(timeFormat) > 0 {
|
|
|
|
buffer.WriteString(time.Now().Format(timeFormat))
|
|
|
|
}
|
|
|
|
// Lead string.
|
|
|
|
if len(lead) > 0 {
|
|
|
|
buffer.WriteString(lead)
|
|
|
|
if len(value) > 0 {
|
|
|
|
buffer.WriteByte(' ')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Caller path.
|
|
|
|
callerPath := ""
|
|
|
|
if l.flags&F_FILE_LONG > 0 {
|
|
|
|
callerPath = l.getLongFile() + ": "
|
|
|
|
}
|
|
|
|
if l.flags&F_FILE_SHORT > 0 {
|
|
|
|
callerPath = gfile.Basename(l.getLongFile()) + ": "
|
|
|
|
}
|
|
|
|
if len(callerPath) > 0 {
|
|
|
|
buffer.WriteString(callerPath)
|
|
|
|
}
|
|
|
|
// Prefix.
|
|
|
|
if len(l.prefix) > 0 {
|
|
|
|
buffer.WriteString(l.prefix + " ")
|
|
|
|
}
|
|
|
|
}
|
2019-06-01 22:36:12 +08:00
|
|
|
for k, v := range value {
|
|
|
|
if k > 0 {
|
|
|
|
buffer.WriteByte(' ')
|
2019-06-01 11:01:57 +08:00
|
|
|
}
|
2019-06-01 22:36:12 +08:00
|
|
|
buffer.WriteString(gconv.String(v))
|
2019-06-01 11:01:57 +08:00
|
|
|
}
|
|
|
|
buffer.WriteString(ln)
|
2019-06-19 09:06:52 +08:00
|
|
|
if l.flags&F_ASYNC > 0 {
|
2019-06-01 19:34:03 +08:00
|
|
|
asyncPool.Add(func() {
|
|
|
|
l.printToWriter(std, buffer)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
l.printToWriter(std, buffer)
|
|
|
|
}
|
|
|
|
}
|
2019-06-01 11:01:57 +08:00
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// printToWriter writes buffer to writer.
|
|
|
|
func (l *Logger) printToWriter(std io.Writer, buffer *bytes.Buffer) {
|
|
|
|
if l.writer == nil {
|
|
|
|
if f := l.getFilePointer(); f != nil {
|
|
|
|
defer f.Close()
|
|
|
|
if _, err := io.WriteString(f, buffer.String()); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Allow output to stdout?
|
|
|
|
if l.stdoutPrint {
|
|
|
|
if _, err := std.Write(buffer.Bytes()); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if _, err := l.writer.Write(buffer.Bytes()); err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err.Error())
|
|
|
|
}
|
|
|
|
}
|
2018-04-19 16:24:48 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 21:52:17 +08:00
|
|
|
// printStd prints content <s> without backtrace.
|
2019-06-19 09:06:52 +08:00
|
|
|
func (l *Logger) printStd(lead string, value ...interface{}) {
|
|
|
|
l.print(os.Stdout, lead, value...)
|
2018-04-19 16:24:48 +08:00
|
|
|
}
|
|
|
|
|
2019-05-21 21:52:17 +08:00
|
|
|
// printStd prints content <s> with backtrace check.
|
2019-06-19 09:06:52 +08:00
|
|
|
func (l *Logger) printErr(lead string, value ...interface{}) {
|
|
|
|
if l.btStatus == 1 {
|
|
|
|
if s := l.GetBacktrace(); s != "" {
|
|
|
|
value = append(value, ln+"Backtrace:"+ln+s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// In matter of sequence, do not use stderr here, but use the same stdout.
|
|
|
|
l.print(os.Stdout, lead, value...)
|
2019-06-01 22:36:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// format formats <values> using fmt.Sprintf.
|
2019-06-19 09:06:52 +08:00
|
|
|
func (l *Logger) format(format string, value ...interface{}) string {
|
2019-06-01 22:36:12 +08:00
|
|
|
return fmt.Sprintf(format, value...)
|
2018-12-15 15:50:39 +08:00
|
|
|
}
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
// PrintBacktrace prints the caller backtrace,
|
2019-04-17 23:50:37 +08:00
|
|
|
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
2019-06-19 09:06:52 +08:00
|
|
|
func (l *Logger) PrintBacktrace(skip ...int) {
|
2019-06-01 11:01:57 +08:00
|
|
|
if s := l.GetBacktrace(skip...); s != "" {
|
|
|
|
l.Println("Backtrace:" + ln + s)
|
|
|
|
} else {
|
|
|
|
l.Println()
|
|
|
|
}
|
2018-08-27 23:58:32 +08:00
|
|
|
}
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
// GetBacktrace returns the caller backtrace content,
|
2019-04-17 23:50:37 +08:00
|
|
|
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
|
2019-06-19 09:06:52 +08:00
|
|
|
func (l *Logger) GetBacktrace(skip ...int) string {
|
|
|
|
customSkip := 0
|
|
|
|
if len(skip) > 0 {
|
|
|
|
customSkip = skip[0]
|
|
|
|
}
|
|
|
|
backtrace := ""
|
|
|
|
from := 0
|
|
|
|
// Find the caller position exclusive of the glog file.
|
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
if _, file, _, ok := runtime.Caller(i); ok {
|
|
|
|
if !gregex.IsMatchString("/g/os/glog/glog.+$", file) {
|
|
|
|
from = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Find the true caller file path using custom skip.
|
|
|
|
index := 1
|
|
|
|
goRoot := runtime.GOROOT()
|
2019-06-18 19:19:43 +08:00
|
|
|
if goRoot != "" {
|
2019-06-18 19:44:28 +08:00
|
|
|
goRoot = strings.Replace(goRoot, "\\", "/", -1)
|
2019-06-18 19:19:43 +08:00
|
|
|
goRoot = regexp.QuoteMeta(goRoot)
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
for i := from + customSkip + l.btSkip; i < 1000; i++ {
|
|
|
|
if _, file, cline, ok := runtime.Caller(i); ok && len(file) > 2 {
|
|
|
|
if (goRoot == "" || !gregex.IsMatchString("^"+goRoot, file)) && !gregex.IsMatchString(`<autogenerated>`, file) {
|
|
|
|
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, ln)
|
|
|
|
index++
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return backtrace
|
2018-04-19 16:24:48 +08:00
|
|
|
}
|
|
|
|
|
2019-05-23 19:14:16 +08:00
|
|
|
// getLongFile returns the absolute file path along with its line number of the caller.
|
|
|
|
func (l *Logger) getLongFile() string {
|
|
|
|
from := 0
|
|
|
|
// Find the caller position exclusive of the glog file.
|
2019-05-29 15:18:12 +08:00
|
|
|
for i := 0; i < 1000; i++ {
|
|
|
|
if _, file, _, ok := runtime.Caller(i); ok {
|
2019-05-23 19:14:16 +08:00
|
|
|
if !gregex.IsMatchString("/g/os/glog/glog.+$", file) {
|
2019-05-23 20:34:06 +08:00
|
|
|
from = i
|
2019-05-23 19:14:16 +08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Find the true caller file path using custom skip.
|
|
|
|
goRoot := runtime.GOROOT()
|
2019-05-29 15:18:12 +08:00
|
|
|
for i := from + l.btSkip; i < 1000; i++ {
|
2019-06-01 19:34:03 +08:00
|
|
|
if _, file, line, ok := runtime.Caller(i); ok && len(file) > 2 {
|
2019-06-19 09:06:52 +08:00
|
|
|
if (goRoot == "" || !gregex.IsMatchString("^"+goRoot, file)) && !gregex.IsMatchString(`<autogenerated>`, file) {
|
2019-05-23 19:14:16 +08:00
|
|
|
return fmt.Sprintf(`%s:%d`, file, line)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|