本文介绍: 由上文可以知道分布式链路追踪的作用,但是距离用到项目中,我们需要项目中一些关键组件进行opentelemetry集成,例如日志库,ORM、http框架rpc框架等。

由上文分布式链路追踪入门篇-基础原理与快速应用可以知道分布式链路追踪的作用,但是距离用到项目中,我们需要项目中一些关键组件进行opentelemetry集成,例如日志库,ORM、http框架rpc框架等。

一、日志如何集成opentelemetry

其实对日志库的集成无非就是调用日志库的方法进行日志打印时,可以把日志信息也被记录下来,推送可视化的后端(eg:jaeger)。而由上文分布式链路追踪入门篇-基础原理与快速应用可以知道,通过span可以关联上相关的操作信息

二、简单demo演示

例如就对我们go原生log库进行封装

log.go

package pkg

import (
	`context`
	`log`
	`os`
	
	`go.opentelemetry.io/otel/attribute`
	`go.opentelemetry.io/otel/trace`
)

//封装一下日志
type Logger struct {
	log *log.Logger
}

var Log *Logger

func init()  {
	Log = &Logger{
		log: log.New(os.Stderr, "", log.LstdFlags),
	}
}

func (l *Logger) Debug(ctx context.Context, msg string) {
	span := trace.SpanFromContext(ctx)
	span.AddEvent("", trace.WithAttributes(attribute.String("log", msg)))
	l.log.Println(msg)
}

//todo生日志库只有Print方法没有INFO、WARNd等分级
func (l *Logger) Info(ctx context.Context, msg string) {}
//todo
func (l *Logger) Warn(ctx context.Context, msg string) {}
//todo
func (l *Logger) Error(ctx context.Context, msg string) {}
//todo
func (l *Logger) Fatal(ctx context.Context, msg string) {}

main.go

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	
	"go.opentelemetry.io/otel"
	
	"go.opentelemetry.io/otel/exporters/trace/jaeger"
	`go.opentelemetry.io/otel/sdk/resource`
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	`go.opentelemetry.io/otel/semconv`
	
	`otel/log/pkg`
)

// 初始化 OpenTelemetry
func initTracer() *sdktrace.TracerProvider {
	exporter, err := jaeger.NewRawExporter(
		jaeger.WithAgentEndpoint(func(options *jaeger.AgentEndpointOptions) {
			options.Host = "localhost"
			options.Port = "6831"
		}),
	)
	if err != nil {
		log.Fatalf("Error creating Jaeger exporter: %v", err)
	}
	
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exporter),
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithResource(resource.NewWithAttributes(
			semconv.ServiceNameKey.String("demo_service"), // 服务
		)),
	)
	otel.SetTracerProvider(tp)
	return tp
}

func main() {
	tp := initTracer()
	defer func() {
		if cerr := tp.Shutdown(context.Background()); cerr != nil {
			log.Fatalf("Error shutting down tracer provider: %v", cerr)
		}
	}()
	
	//启动http服务器
	http.HandleFunc("/log/demo", handleRequest)
	
	go func() {
		if err := http.ListenAndServe(":8080", nil); err != nil {
			log.Fatalf("Error starting Service A server: %v", err)
		}
	}()
	
	//模拟请求
	SimulateRequest()
}

func SimulateRequest()  {
	req, err := http.NewRequest("GET", "http://localhost:8080/log/demo", nil)
	if err != nil {
		log.Fatalf("Creating request fail: %v", err)
	}
	
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalf("Request failed: %v", err)
	}
	defer resp.Body.Close()
	fmt.Println("Response received from Root Service")
}

func handleRequest(w http.ResponseWriter, req *http.Request) {
	tracer := otel.Tracer("root")
	//开始创建root span
	ctx, span := tracer.Start(req.Context(), "root service")
	defer span.End()
	
	pkg.Log.Debug(ctx, "this is root service")
	
	//访问服务A
	callServiceA(ctx)
	
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "Response from Service Root")
}

// Service A
func callServiceA(ctx context.Context) {
	tracer := otel.Tracer("service A")
	ctx, span := tracer.Start(ctx, "ServiceA")
	defer span.End()
	
	pkg.Log.Debug(ctx, "this is A service")
	
	fmt.Println("Service A")
}

运行程序后,访问jeager:
在这里插入图片描述

三、总结

1. 其实日志库的集成就是对原先的日志库进行一层封装,日志打印方法传入上下文,通过上下文获取操作单元span,然后给span关联上日志信息
2. 上面demo只是一个演示,我们也可以依照这个思路封装我们自己项目中的日志库

原文地址:https://blog.csdn.net/huanyi5214/article/details/134600405

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_15707.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注