Go Web-app Instrumentation
在本博文中,您将亲自学习如何在没有先前知识的情况下使用OpenTelemetry Go创建和可视化跟踪。
我们将首先创建一个使用Mongo和Gin框架的简单待办事项应用程序。然后,我们将把跟踪数据发送到Jaeger Tracing以进行可视化。您可以在这个GitHub存储库中找到所有相关的文件。
Hello world: OpenTelemetry Go示例
我们将从创建待办事项服务并安装两个库(Gin和Mongo)开始,以了解如何进行工具化。
第1步:为我们的待办事项应用程序创建main.go文件
-
安装Gin和Mongo-driver
go get -u github.com/gin-gonic/gin go get go.mongodb.org/mongo-driver/mongo
-
设置gin和mongo监听“/todo”
-
创建一些用于种子数据的待办事项
package main import ( "context" "net/http" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) var client *mongo.Client func main() { connectMongo() setupWebServer() } func connectMongo() { opts := options.Client() opts.ApplyURI("mongodb://localhost:27017") client, _ = mongo.Connect(context.Background(), opts) //用待办事项种子数据填充数据库 docs := []interface{}{ bson.D{ {"id", "1"}, {"title", "购买杂货"}, }, bson.D{ {"id", "2"}, {"title", "安装Aspecto.io"}, }, bson.D{ {"id", "3"}, {"title", "购买dogz.io域名"}, }, } client.Database("todo").Collection("todos").InsertMany(context.Background(), docs) } func setupWebServer() { r := gin.Default() r.GET("/todo", func(c *gin.Context) { collection := client.Database("todo").Collection("todos") //重要提示:确保将c.Request.Context()作为上下文传递,而不是c本身-TBD cur, findErr := collection.Find(c.Request.Context(), bson.D{}) if findErr != nil { c.AbortWithError(500, findErr) return } results := make([]interface{}, 0) curErr := cur.All(c, &results) if curErr != nil { c.AbortWithError(500, curErr) return } c.JSON(http.StatusOK, results) }) _ = r.Run(":8080") }
现在,我们的小型待办事项应用程序准备就绪,让我们引入OpenTelemetry。
第2步:安装OpenTelemetry Go
我们将配置OpenTelemetry以工具化我们的Go应用程序。
-
要安装OTel SDK,请运行:
go get go.opentelemetry.io/otel/ go.opentelemetry.io/otel/sdk/
-
工具化我们的Gin和Mongo库以生成跟踪。
-
Gin和Mongo工具化:安装otelgin和otelmongo
go get go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin/ go get go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo
Gin工具化:gin.Context
我们之前讨论了上下文传播的概念 - 在分布式服务之间传递元数据以关联系统中的事件的方法。
Gin框架有自己的gin.Context类型,作为参数传递给HTTP处理程序。但是,应传递到Mongo操作的上下文实际上是标准的Go库Context对象,该对象在gin.Context.Request.Context中可用。
//确保将c.Request.Context()作为上下文传递,而不是c本身
cur, findErr := collection.Find(c.Request.Context(), bson.D{})
因此,请确保将上下文传递给MongoDB操作。欲了解更多信息,请参考此问题。
现在,我们的待办事项应用程序已准备就绪并进行了工具化。现在该利用OpenTelemetry的全部潜力了。跟踪可视化是这项技术真正的故障排除能力体现之处。
为了进行可视化,我们将使用开源的Jaeger Tracing。
使用Jaeger进行可视化
OpenTelemetry Go和Jaeger Tracing:将跟踪导出到Jaeger
Jaeger Tracing是一套管理整个分布式跟踪“栈”的开源项目套件:客户端、收集器和UI。Jaeger UI是最常用的开源工具,用于可视化跟踪。
设置如下:
-
安装Jaeger导出器
go get go.opentelemetry.io/otel/exporters/jaeger
-
创建一个tracing文件夹和一个jaeger.go文件
-
将以下代码添加到文件中
package tracing import ( "go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" ) func JaegerTraceProvider() (*sdktrace.TracerProvider, error) { exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces"))) if err != nil { return nil, err } tp := sdktrace.NewTracerProvider( sdktrace.WithBatcher(exp), sdktrace.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("todo-service"), semconv.DeploymentEnvironmentKey.String("production"), )), ) return tp, nil }
-
返回到main.go文件并修改我们的代码以使用刚刚创建的JaegerTraceProvider函数
func main() { tp, tpErr := tracing.JaegerTraceProvider() if tpErr != nil { log.Fatal(tpErr) } otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) connectMongo() setupWebServer() }
接下来,我们将连接已安装的工具。
-
添加Mongo工具化。在connectMongo函数中添加以下行
opts.Monitor = otelmongo.NewMonitor()
函数应如下所示:
func connectMongo() { opts := options.Client() //Mongo OpenTelemetry工具化 opts.Monitor = otelmongo.NewMonitor() opts.ApplyURI("mongodb://localhost:27017") client, _ = mongo.Connect(context.Background(), opts) //用一些待办事项填充数据库 docs := []interface{}{ bson.D{ {"id", "1"}, {"title", "购买杂货"}, }, bson.D{ {"id", "2"}, {"title", "安装Aspecto.io"}, }, bson.D{ {"id", "3"}, {"title", "购买dogz.io域名"}, }, } client.Database("todo").Collection("todos").InsertMany(context.Background(), docs) }
现在,添加Gin工具化。
-
转到startWebServer函数并在创建gin实例后添加以下行
r.Use(otelgin.Middleware("todo-service"))
函数应如下所示
func startWebServer() { r := gin.Default() //Gin OpenTelemetry工具化 r.Use(otelgin.Middleware("todo-service")) r.GET("/todo", func(c *gin.Context) { collection := client.Database("todo").Collection("todos") //确保将c.Request.Context()作为上下文传递,而不是c本身 cur, findErr := collection.Find(c.Request.Context(), bson.D{}) if findErr != nil { c.AbortWithError(500, findErr) return } results := make([]interface{}, 0) curErr := cur.All(c, &results) if curErr != nil { c.AbortWithError(500, curErr) return } c.JSON(http.StatusOK, results) }) _ = r.Run(":8080") }
完整的
main.go
文件如下所示。现在,我们终于可以导出到Jaeger了。package main import ( "context" "log" "net/http" "github.com/aspecto-io/opentelemetry-examples/tracing" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" "go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" ) var client *mongo.Client func main() { //将跟踪导出到Jaeger tp, tpErr := tracing.JaegerTraceProvider() if tpErr != nil { log.Fatal(tpErr) } otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) connectMongo() startWebServer() } func connectMongo() { opts := options.Client() //Mongo OpenTelemetry工具化 opts.Monitor = otelmongo.NewMonitor() opts.ApplyURI("mongodb://localhost:27017") client, _ = mongo.Connect(context.Background(), opts) //用一些待办事项填充数据库 docs := []interface{}{ bson.D{ {"id", "1"}, {"title", "购买杂货"}, }, bson.D{ {"id", "2"}, {"title", "安装Aspecto.io"}, }, bson.D{ {"id", "3"}, {"title", "购买dogz.io域名"}, }, } client.Database("todo").Collection("todos").InsertMany(context.Background(), docs) } func startWebServer() { r := gin.Default() //gin OpenTelemetry工具化 r.Use(otelgin.Middleware("todo-service")) r.GET("/todo", func(c *gin.Context) { collection := client.Database("todo").Collection("todos") //确保将c.Request.Context()作为上下文传递,而不是c本身 cur, findErr := collection.Find(c.Request.Context(), bson.D{}) if findErr != nil { c.AbortWithError(500, findErr) return } results := make([]interface{}, 0) curErr := cur.All(c, &results) if curErr != nil { c.AbortWithError(500, curErr) return } c.JSON(http.StatusOK, results) }) _ = r.Run(":8080") }
将跟踪导出到Jaeger
- 运行
go run main.go
以启动todo-service。 - 为了生成一些跟踪,请向http://localhost:8080/todo发出HTTP GET请求。
- 要查看跟踪,请打开Jaeger网址http://localhost:16686/search。
现在,您可以看到Jaeger UI了。选择todo-service,然后点击“Find traces”。您应该可以在右侧看到您的跟踪:
Jaeger UI显示我们的todo-service中使用OpenTelemetry的Go跟踪 通过单击跟踪,您可以深入了解并查看更多细节,以便进一步自行调查:
Summary
就是这样!我们希望本指南易于理解和跟随。您可以在我们的GitHub 存储库中找到所有准备好使用的文件。
本文 [的版本原先发布在 ][] Aspecto博客上。
[的版本原先发布在 ][] https://www.aspecto.io/blog/opentelemetry-go-getting-started/