diff --git a/TrackContext.go b/TrackContext.go index cf2fdb4..fa35c66 100644 --- a/TrackContext.go +++ b/TrackContext.go @@ -1,40 +1,51 @@ package linkTrace import ( + "fmt" "github.com/farseer-go/collections" + "github.com/farseer-go/fs" + "github.com/farseer-go/fs/flog" "github.com/farseer-go/fs/parse" "github.com/farseer-go/fs/snowflake" + "github.com/farseer-go/linkTrace/eumCallType" "github.com/farseer-go/linkTrace/eumLinkType" "github.com/farseer-go/queue" + "strings" "time" ) type TraceContext struct { + TraceId int64 `es:"primaryKey"` // 上下文ID + AppId int64 // 应用ID + AppName string // 应用名称 + AppIp string // 应用IP ParentAppName string // 上游应用 - TraceId int64 // 上下文ID StartTs int64 // 调用开始时间戳 EndTs int64 // 调用结束时间戳 UseTs time.Duration // 总共使用时间毫秒 LinkType eumLinkType.Enum // 状态码 Domain string // 请求域名 - Path string // 请求地址 + Path string `es_type:"text"` // 请求地址 Method string // 请求方式 ContentType string // 请求内容类型 StatusCode int // 状态码 - Headers collections.Dictionary[string, string] // 请求头部 - RequestBody string // 请求参数 - ResponseBody string // 输出参数 + Headers collections.Dictionary[string, string] `es_type:"flattened"` // 请求头部 + RequestBody string `es_type:"text"` // 请求参数 + ResponseBody string `es_type:"text"` // 输出参数 RequestIp string // 客户端IP - List collections.List[LinkTraceDetail] // 调用的上下文 - ExceptionDetail ExceptionDetail // 是否执行异常 + List collections.List[TraceDetail] `es_type:"object"` // 调用的上下文 + ExceptionDetail ExceptionDetail `es_type:"object"` // 是否执行异常 } -func NewWebApi(domain string, path string, method string, contentType string, headerDictionary collections.ReadonlyDictionary[string, string], requestBody string, requestIp string) *TraceContext { +func TraceWebApi(domain string, path string, method string, contentType string, headerDictionary collections.ReadonlyDictionary[string, string], requestBody string, requestIp string) *TraceContext { traceId := parse.ToInt64(headerDictionary.GetValue("TraceId")) if traceId == 0 { traceId = snowflake.GenerateId() } return &TraceContext{ + AppId: fs.AppId, + AppName: fs.AppName, + AppIp: fs.AppIp, ParentAppName: headerDictionary.GetValue("AppName"), TraceId: traceId, StartTs: time.Now().UnixMicro(), @@ -46,23 +57,78 @@ func NewWebApi(domain string, path string, method string, contentType string, he Headers: headerDictionary.ToDictionary(), RequestBody: requestBody, RequestIp: requestIp, - //List: collections.List[LinkTraceDetail]{}, + List: collections.NewList[TraceDetail](), //ExceptionDetail: ExceptionDetail{}, } } +func TraceDatabase(dbName, tableName string, sql string) { + if defConfig.Enable { + trace := GetCurTrace() + if trace != nil { + trace.List.Add(TraceDetail{ + CallType: eumCallType.Database, + Data: map[string]any{ + "DbName": dbName, + "TableName": tableName, + "Sql": sql, + }, + CallStackTrace: CallStackTrace{}, + CallMethod: "", + StartTs: 0, + EndTs: 0, + IsException: false, + ExceptionMessage: "", + }) + } + } +} + // End 结束当前链路 func (receiver *TraceContext) End() { receiver.EndTs = time.Now().UnixMicro() receiver.UseTs = time.Duration(receiver.EndTs-receiver.StartTs) * time.Microsecond // 启用了链路追踪后,把数据写入到本地队列中 - if Enable { + if defConfig.Enable { queue.Push("TraceContext", receiver) } + + // 打印日志 + if defConfig.PrintLog { + lst := collections.NewList[string]() + receiver.List.For(func(index int, item *TraceDetail) { + switch item.CallType { + case eumCallType.Database: + tableName := parse.ToString(item.Data["TableName"]) + sql := flog.ReplaceBlues(parse.ToString(item.Data["Sql"]), "SELECT ", "UPDATE ", "DELETE ", " FROM ", " WHERE ", " LIMIT ", " SET ", " ORDER BY ", " and ", " or ") + sql = strings.ReplaceAll(sql, tableName, flog.Red(tableName)) + lst.Add(fmt.Sprintf("%s:[%s] %s", flog.Blue(index+1), flog.Yellow(item.CallType.ToString()), sql)) + } + }) + flog.Printf("【链路追踪】TraceId:%s,耗时:%s,%s,详情如下:\n%s\n", flog.Green(parse.ToString(receiver.TraceId)), flog.Red(receiver.UseTs.String()), receiver.Path, strings.Join(lst.ToArray(), "\n")) + fmt.Println("-----------------------------------------------------------------") + } +} + +// TraceDetail 埋点明细 +type TraceDetail struct { + CallStackTrace CallStackTrace // 调用栈 + CallMethod string // 调用方法 + CallType eumCallType.Enum // 调用类型 + Data map[string]any // 埋点数据 + StartTs int64 // 调用开始时间戳 + EndTs int64 // 调用停止时间戳 + IsException bool // 是否执行异常 + ExceptionMessage string // 异常信息 } -type LinkTraceDetail struct { +type CallStackTrace struct { + CallMethod string // 调用方法 + FileName string // 执行文件名称 + FileLineNumber int // 方法执行行数 + ReturnType string // 方法返回类型 + MethodParams map[string]string // 方法入参 } type ExceptionDetail struct { diff --git a/config.go b/config.go new file mode 100644 index 0000000..934ff3d --- /dev/null +++ b/config.go @@ -0,0 +1,6 @@ +package linkTrace + +type config struct { + Enable bool + PrintLog bool +} diff --git a/eumCallType/enum.go b/eumCallType/enum.go new file mode 100644 index 0000000..5eac83c --- /dev/null +++ b/eumCallType/enum.go @@ -0,0 +1,38 @@ +package eumCallType + +type Enum int + +const ( + HttpClient Enum = iota // HttpClient + GrpcClient // GrpcClient + Database // Database + Redis // Redis + Mq // Mq + Elasticsearch // Elasticsearch + Custom // Custom + KeyLocation // KeyLocation +) + +func (receiver Enum) ToString() string { + switch receiver { + case HttpClient: + return "HttpClient" + case GrpcClient: + return "GrpcClient" + case Database: + return "Database" + case Redis: + return "Redis" + case Mq: + return "Mq" + case Elasticsearch: + return "Elasticsearch" + return "Mq" + case Custom: + return "Custom" + return "Mq" + case KeyLocation: + return "KeyLocation" + } + return "" +} diff --git a/module.go b/module.go index 4b006b6..e6548f8 100644 --- a/module.go +++ b/module.go @@ -8,7 +8,7 @@ import ( ) // Enable 是否启用 -var Enable bool +var defConfig config type Module struct { } @@ -18,7 +18,7 @@ func (module Module) DependsModule() []modules.FarseerModule { } func (module Module) PreInitialize() { - Enable = configure.GetBool("LinkTrace.Enable") + defConfig = configure.ParseConfig[config]("LinkTrace") } func (module Module) PostInitialize() {