Golang Logrus 在所有应用程序日志中启用 Opentelemetry 的 Trace ID 和 Span ID。

huangapple go评论77阅读模式
英文:

Golang Logrus Enable Opentelemetry Trace ID and Span ID in all Application Logs

问题

我正在尝试为每个应用程序日志启用 Trace ID 和 Span ID。我正在使用 opentelemetry API 进行注入,并使用 logrus 库进行日志记录。以下是一个示例代码,我能够在所有日志中启用跟踪和跨度条目,但跟踪和跨度 ID 的值始终为 0。

在代码中,你可以尝试以下几点来解决问题:

  1. 确保你的代码中正确初始化了 TracerProvider,并将其设置为全局的 TracerProvider。你可以使用 sdktrace.NewTracerProvider 方法来创建 TracerProvider,并使用 sdktrace.WithSamplersdktrace.WithBatchersdktrace.WithResource 方法来配置采样器、批处理器和资源属性。

  2. 确保你正确设置了文本映射传播器。在你的代码中,你使用了 go.opentelemetry.io/contrib/propagators/b3 包中的 B3 传播器。确保你正确地设置了传播器,以便将跟踪和跨度信息注入到日志中。

  3. 确保你在日志记录器中正确提取和设置跟踪和跨度 ID。在你的代码中,你使用了自定义的日志记录器 customLogger,在其中提取了跟踪和跨度 ID,并将其添加到日志条目的数据中。确保你正确提取了跟踪和跨度 ID,并将其设置为日志条目的字段。

  4. 确保你在请求处理程序中正确使用了 OpenTelemetry 中间件。在你的代码中,你使用了 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp 包中的 otelhttp.NewHandler 方法来创建请求处理程序,并将其包装在 OpenTelemetry 中间件中。确保你正确使用了中间件,并在请求处理程序中记录日志。

如果你仍然遇到问题,可以进一步检查你的代码和配置,确保所有的依赖项和设置都正确。你还可以查阅 OpenTelemetry 和 logrus 的文档,以获取更多关于日志记录和跟踪的信息。希望这些提示能帮助你找到解决问题的方向。

英文:

I am trying to enable Trace ID and Span ID for every application log. I am using opentelemtry APIs to inject and logrus labrary for logging. Below is an example code where i am able to enable trace/span entried to all logs but the trace and span id values are always 0's.

#Server.go#

package main

import (
    log "github.com/sirupsen/logrus"
    "fmt"
    "context"
    "os"
    "net/http"
    "go.opentelemetry.io/otel/trace"
     stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
     sdktrace "go.opentelemetry.io/otel/sdk/trace"
     "go.opentelemetry.io/otel/sdk/resource"
     semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
     "go.opentelemetry.io/otel"
     "go.opentelemetry.io/contrib/propagators/b3"
     "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func init() {
  log.SetFormatter(customLogger{
                formatter:    log.JSONFormatter{FieldMap: log.FieldMap{
                "msg": "message",
                }},
        })
  log.SetOutput(os.Stdout)
  log.SetLevel(log.InfoLevel)
}


func initTracer() (*sdktrace.TracerProvider, error) {
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }

        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
                sdktrace.WithResource(resource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceNameKey.String("PreferencesService"))),
        )
        otel.SetTracerProvider(tp)
        b3 := b3.New()
        otel.SetTextMapPropagator(b3)
        return tp, err
}


func main() {
    ctx := context.Background()
    tp, err := initTracer()
    if err != nil {
           log.Error(err)
    }
    defer func() {
           if err := tp.Shutdown(ctx); err != nil {
                  log.Error("Error shutting down tracer provider: %v", err)
           }
    }()

    otelHandler := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
        log.Info("About End Point Printning...")
        fmt.Fprintln(w, "about page")
    }),"About")
    http.Handle("/about", otelHandler)
    log.Info("Listening...")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

type customLogger struct {
        formatter    log.JSONFormatter
}

func (l customLogger) Format(entry *log.Entry) ([]byte, error) {
        span := trace.SpanFromContext(entry.Context)
        entry.Data["trace_id"] = span.SpanContext().TraceID().String()
        entry.Data["span_id"] = span.SpanContext().SpanID().String()
        //Below injection is Just to understand what Context has
        entry.Data["Context"] = span.SpanContext()
        return l.formatter.Format(entry)
}

#Client.go#

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {

    resp, err := http.Get("http://localhost:8080/about")

    if err != nil {
        log.Fatal(err)
    }

    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(string(body))
}

#Output :#

{"Context":{"TraceID":"00000000000000000000000000000000","SpanID":"0000000000000000","TraceFlags":"00","TraceState":"","Remote":false},"level":"info","message":"Listening...","span_id":"0000000000000000","time":"2022-06-30T12:34:21+05:30","trace_id":"00000000000000000000000000000000"}
{"Context":
**Below Line Should have Proper Trace ID and Span ID**
{"TraceID":"00000000000000000000000000000000","SpanID":"0000000000000000","TraceFlags":"00","TraceState":"","Remote":false},"level":"info","message":"About End Point Printning...","span_id":"0000000000000000","time":"2022-06-30T12:34:28+05:30","trace_id":"00000000000000000000000000000000"}
{
"Name": "About",
"SpanContext": {
"TraceID": "cb4dc4953a633fbe9b41c19a0870e9d6",
"SpanID": "b7346b7aba15e074",
"TraceFlags": "01",
"TraceState": "",
"Remote": false
},
"Parent": {
"TraceID": "00000000000000000000000000000000",
"SpanID": "0000000000000000",
"TraceFlags": "00",
"TraceState": "",
"Remote": false
},
"SpanKind": 2,
"StartTime": "2022-06-30T12:34:28.838523497+05:30",
"EndTime": "2022-06-30T12:34:28.838858989+05:30",
"Attributes": [
{
"Key": "net.transport",
"Value": {
"Type": "STRING",
"Value": "ip_tcp"
}
},
{
"Key": "net.peer.ip",
"Value": {
"Type": "STRING",
"Value": "127.0.0.1"
}
},
{
"Key": "net.peer.port",
"Value": {
"Type": "INT64",
"Value": 41900
}
},
{
"Key": "net.host.name",
"Value": {
"Type": "STRING",
"Value": "localhost"
}
},
{
"Key": "net.host.port",
"Value": {
"Type": "INT64",
"Value": 8080
}
},
{
"Key": "http.method",
"Value": {
"Type": "STRING",
"Value": "GET"
}
},
{
"Key": "http.target",
"Value": {
"Type": "STRING",
"Value": "/about"
}
},
{
"Key": "http.server_name",
"Value": {
"Type": "STRING",
"Value": "About"
}
},
{
"Key": "http.user_agent",
"Value": {
"Type": "STRING",
"Value": "Go-http-client/1.1"
}
},
{
"Key": "http.scheme",
"Value": {
"Type": "STRING",
"Value": "http"
}
},
{
"Key": "http.host",
"Value": {
"Type": "STRING",
"Value": "localhost:8080"
}
},
{
"Key": "http.flavor",
"Value": {
"Type": "STRING",
"Value": "1.1"
}
},
{
"Key": "http.wrote_bytes",
"Value": {
"Type": "INT64",
"Value": 11
}
},
{
"Key": "http.status_code",
"Value": {
"Type": "INT64",
"Value": 200
}
}
],
"Events": null,
"Links": null,
"Status": {
"Code": "Unset",
"Description": ""
},
"DroppedAttributes": 0,
"DroppedEvents": 0,
"DroppedLinks": 0,
"ChildSpanCount": 0,
"Resource": [
{
"Key": "service.name",
"Value": {
"Type": "STRING",
"Value": "PreferencesService"
}
}
],
"InstrumentationLibrary": {
"Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp",
"Version": "semver:0.32.0",
"SchemaURL": ""
}
}

Not Sure where/what is the mistake but it will be helpful if someone route me to the right direction, how i can inject Trace ID and Span ID to all logs..

Note: I don't want to pass request context to all the functions. Looking for other options

答案1

得分: 1

问题是 span := trace.SpanFromContext(entry.Context) 无法获取到 span 上下文。

也许你应该将 req.Context() 注入到 logrus 中,然后打印日志。

otelHandler := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    //log.Info("About End Point Printning...")
    // 注入 req.Context
    log.WithContext(req.Context()).Info("About End Point Printning...")
    fmt.Fprintln(w, "about page")
}), "About")

运行 server.go,然后运行 client.go,输出结果为

{"Context":{"TraceID":"00000000000000000000000000000000","SpanID":"0000000000000000","TraceFlags":"00","TraceState":"","Remote":false},"level":"info","message":"Listening...","span_id":"0000000000000000","time":"2022-07-02T21:39:49+08:00","trace_id":"00000000000000000000000000000000"}
{"Context":{"TraceID":"7cab2739ea2c5b2d3e6a08cfb57858a9","SpanID":"ec5ba151b6823658","TraceFlags":"01","TraceState":"","Remote":false},"level":"info","message":"About End Point Printning...","span_id":"ec5ba151b6823658","time":"2022-07-02T21:39:54+08:00","trace_id":"7cab2739ea2c5b2d3e6a08cfb57858a9"}

注意:第一行没有打印 TraceId 是因为没有请求。

希望对你有帮助。

英文:

the problem is span := trace.SpanFromContext(entry.Context) can not get span context.

maybe you should inject req.Context() to logrus then print log

	otelHandler := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		//log.Info("About End Point Printning...")
		// inject req.Context
		log.WithContext(req.Context()).Info("About End Point Printning...")
		fmt.Fprintln(w, "about page")
	}), "About")

run the server.go, and then run client.go, the output is

{"Context":{"TraceID":"00000000000000000000000000000000","SpanID":"0000000000000000","TraceFlags":"00","TraceState":"","Remote":false},"level":"info","message":"Listening...","span_id":"0000000000000000","time":"2022-07-02T21:39:49+08:00","trace_id":"00000000000000000000000000000000"}
{"Context":{"TraceID":"7cab2739ea2c5b2d3e6a08cfb57858a9","SpanID":"ec5ba151b6823658","TraceFlags":"01","TraceState":"","Remote":false},"level":"info","message":"About End Point Printning...","span_id":"ec5ba151b6823658","time":"2022-07-02T21:39:54+08:00","trace_id":"7cab2739ea2c5b2d3e6a08cfb57858a9"}

notice: the first line not print traceId is because there is no request

hope it is helpful for you.

huangapple
  • 本文由 发表于 2022年6月30日 16:00:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/72812236.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定