181 lines
4.0 KiB
Go
181 lines
4.0 KiB
Go
|
|
package log
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"net"
|
||
|
|
"os"
|
||
|
|
"path"
|
||
|
|
"runtime"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"apigo.cc/go/cast"
|
||
|
|
)
|
||
|
|
|
||
|
|
var (
|
||
|
|
dockerImageName string
|
||
|
|
dockerImageTag string
|
||
|
|
serverName string
|
||
|
|
serverIp string
|
||
|
|
)
|
||
|
|
|
||
|
|
func init() {
|
||
|
|
dockerImageName = os.Getenv("DOCKER_IMAGE_NAME")
|
||
|
|
dockerImageTag = os.Getenv("DOCKER_IMAGE_TAG")
|
||
|
|
serverName, _ = os.Hostname()
|
||
|
|
addrs, err := net.InterfaceAddrs()
|
||
|
|
if err == nil {
|
||
|
|
for _, a := range addrs {
|
||
|
|
if an, ok := a.(*net.IPNet); ok {
|
||
|
|
// 忽略 Docker 私有网段
|
||
|
|
if an.IP.IsGlobalUnicast() && !strings.HasPrefix(an.IP.To4().String(), "172.17.") {
|
||
|
|
serverIp = an.IP.To4().String()
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MakeTime 解析时间字符串
|
||
|
|
func MakeTime(logTime string) time.Time {
|
||
|
|
tm, _ := time.Parse(time.RFC3339Nano, logTime)
|
||
|
|
return tm
|
||
|
|
}
|
||
|
|
|
||
|
|
// MakeLogTime 格式化时间为 RFC3339Nano
|
||
|
|
func MakeLogTime(tm time.Time) string {
|
||
|
|
return tm.Format(time.RFC3339Nano)
|
||
|
|
}
|
||
|
|
|
||
|
|
// MakeUsedTime 计算消耗时间(毫秒)
|
||
|
|
func MakeUsedTime(startTime, endTime time.Time) float32 {
|
||
|
|
return float32(endTime.UnixNano()-startTime.UnixNano()) / 1e6
|
||
|
|
}
|
||
|
|
|
||
|
|
// ParseBaseLog 解析基础日志行
|
||
|
|
func ParseBaseLog(line string) *BaseLog {
|
||
|
|
pos := strings.IndexByte(line, '{')
|
||
|
|
if pos == -1 {
|
||
|
|
return ParseBadLog(line)
|
||
|
|
}
|
||
|
|
|
||
|
|
l := make(map[string]any)
|
||
|
|
err := json.Unmarshal([]byte(line[pos:]), &l)
|
||
|
|
if err != nil {
|
||
|
|
return ParseBadLog(line)
|
||
|
|
}
|
||
|
|
|
||
|
|
baseLog := BaseLog{Extra: make(map[string]any)}
|
||
|
|
for k, v := range l {
|
||
|
|
lk := strings.ToLower(k)
|
||
|
|
switch lk {
|
||
|
|
case "logname":
|
||
|
|
baseLog.LogName = cast.String(v)
|
||
|
|
case "logtype":
|
||
|
|
baseLog.LogType = cast.String(v)
|
||
|
|
case "logtime":
|
||
|
|
baseLog.LogTime = cast.String(v)
|
||
|
|
case "traceid":
|
||
|
|
baseLog.TraceId = cast.String(v)
|
||
|
|
case "imagename":
|
||
|
|
baseLog.ImageName = cast.String(v)
|
||
|
|
case "imagetag":
|
||
|
|
baseLog.ImageTag = cast.String(v)
|
||
|
|
case "servername":
|
||
|
|
baseLog.ServerName = cast.String(v)
|
||
|
|
case "serverip":
|
||
|
|
baseLog.ServerIp = cast.String(v)
|
||
|
|
default:
|
||
|
|
baseLog.Extra[lk] = v
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return &baseLog
|
||
|
|
}
|
||
|
|
|
||
|
|
// ParseBadLog 解析非 JSON 格式的日志
|
||
|
|
func ParseBadLog(line string) *BaseLog {
|
||
|
|
baseLog := BaseLog{Extra: make(map[string]any)}
|
||
|
|
baseLog.LogType = LogTypeUndefined
|
||
|
|
if len(line) > 19 && line[19] == ' ' {
|
||
|
|
tm, err := time.Parse("2006/01/02 15:04:05", line[0:19])
|
||
|
|
if err == nil {
|
||
|
|
baseLog.LogTime = MakeLogTime(tm)
|
||
|
|
line = line[20:]
|
||
|
|
} else {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
} else if len(line) > 26 && line[26] == ' ' {
|
||
|
|
tm, err := time.Parse("2006/01/02 15:04:05.000000", line[0:26])
|
||
|
|
if err == nil {
|
||
|
|
baseLog.LogTime = MakeLogTime(tm)
|
||
|
|
line = line[27:]
|
||
|
|
} else {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
baseLog.Extra["info"] = line
|
||
|
|
return &baseLog
|
||
|
|
}
|
||
|
|
|
||
|
|
// fixField 格式化字段名(去横线,小写)
|
||
|
|
func fixField(s string) string {
|
||
|
|
return strings.ToLower(strings.ReplaceAll(s, "-", ""))
|
||
|
|
}
|
||
|
|
|
||
|
|
// getCallStacks 获取调用栈
|
||
|
|
func getCallStacks(truncations []string) []string {
|
||
|
|
callStacks := make([]string, 0)
|
||
|
|
inLogger := true
|
||
|
|
for i := 0; i < 50; i++ {
|
||
|
|
_, file, line, ok := runtime.Caller(i)
|
||
|
|
if !ok {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
if strings.Contains(file, "/go/src/") {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if strings.Contains(file, "/log/") { // 注意这里的路径匹配,迁移后是 /log/
|
||
|
|
if inLogger {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
inLogger = false
|
||
|
|
}
|
||
|
|
if truncations != nil {
|
||
|
|
for _, truncation := range truncations {
|
||
|
|
if pos := strings.Index(file, truncation); pos != -1 {
|
||
|
|
file = file[pos+len(truncation):]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
callStacks = append(callStacks, fmt.Sprintf("%s:%d", file, line))
|
||
|
|
}
|
||
|
|
return callStacks
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetDefaultName 获取默认应用名称
|
||
|
|
func GetDefaultName() string {
|
||
|
|
name := os.Getenv("DISCOVER_APP")
|
||
|
|
if name == "" {
|
||
|
|
name = os.Getenv("discover_app")
|
||
|
|
}
|
||
|
|
if name == "" {
|
||
|
|
imageName := os.Getenv("DOCKER_IMAGE_NAME")
|
||
|
|
if imageName != "" {
|
||
|
|
parts := strings.Split(imageName, "/")
|
||
|
|
imageName = parts[len(parts)-1]
|
||
|
|
imageName = strings.SplitN(imageName, ":", 2)[0]
|
||
|
|
imageName = strings.SplitN(imageName, "#", 2)[0]
|
||
|
|
name = imageName
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if name == "" {
|
||
|
|
name = path.Base(os.Args[0])
|
||
|
|
}
|
||
|
|
return name
|
||
|
|
}
|