/**
@author: xuhanlin
@date: 2022/6/28
@note: 基础线性格式日志
**/

package log

import (
	"bytes"
	"fmt"
	"github.com/sirupsen/logrus"
	"runtime"
	"strconv"
	"strings"
)

// GetGid 获取协程ID
func GetGid() uint64 {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	b = bytes.TrimPrefix(b, []byte("goroutine "))
	b = b[:bytes.IndexByte(b, ' ')]
	n, err := strconv.ParseUint(string(b), 10, 64)
	if err != nil {
		return 0
	}
	return n
}

type LineFormatter struct {
	Skip int
}

func (f *LineFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	data := ""
	// 毫秒时间
	data += entry.Time.Format("2006-01-02 15:04:05.000")
	// 日志等级
	data += "|" + strings.ToUpper(entry.Level.String())
	// 调用信息
	data += "|" + findCaller(f.Skip)
	// 线程信息
	data += "|" + strconv.FormatUint(GetGid(), 10)
	// 日志内容
	data += "|" + entry.Message
	return append([]byte(data), '\n'), nil
}

func findCaller(skip int) string {
	file, line, pc := getCaller(skip)
	fullFnName := runtime.FuncForPC(pc)

	fnName := ""
	if fullFnName != nil {
		fnNameStr := fullFnName.Name()
		parts := strings.Split(fnNameStr, ".")
		fnName = parts[len(parts)-1]
	}

	tmp := strings.Split(file, "/")
	return fmt.Sprintf("%s:%s:%d", tmp[len(tmp)-1], fnName, line)
}

func getCaller(skip int) (string, int, uintptr) {
	pc, file, line, ok := runtime.Caller(skip)
	if !ok {
		return "", 0, pc
	}
	n := 0

	for i := len(file) - 1; i > 0; i-- {
		if file[i] == '/' {
			n++
			if n >= 2 {
				file = file[i+1:]
				break
			}
		}
	}
	return file, line, pc
}
