OpenTelemetry-go的SDK使用方法詳解
2019年5月,OpenCensus 和 OpenTracing形成了 OpenTelemetry(簡稱 OTel)
也就是說,我們在使用鏈路追蹤SDK的時候就需要使用OpenTelemetry的新規(guī)范.OpenTelemetry幫我們實現(xiàn)了相應(yīng)語言的SDK,所以我們只需要進(jìn)行調(diào)用即可.
接下來,我們開始對go所對應(yīng)的SDK進(jìn)行使用.
本文主要根據(jù)官方文檔實例進(jìn)行講解.
例子
本文簡化了官方的例子,每個地方都已注釋完畢! 例子使用jaeger作為鏈路追蹤服務(wù)器.
首先我們先引入它的包.
# 安裝核心模塊 go get go.opentelemetry.io/otel \ go.opentelemetry.io/otel/trace # 安裝導(dǎo)出器,這里導(dǎo)出器使用的是stdout go get go.opentelemetry.io/otel/sdk \ go.opentelemetry.io/otel/exporters/stdout/stdouttrace # 因為我們需要使用jaeger所以要導(dǎo)入jaeger導(dǎo)出器 go get -u go.opentelemetry.io/otel/exporters/jaeger
當(dāng)我們導(dǎo)入上面的包之后,就可以運行例子了.
package main import ( "context" "fmt" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/jaeger" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.12.0" ) // 計算斐波那契隊列 func Fibonacci(n uint) (uint64, error) { if n <= 1 { return uint64(n), nil } var n2, n1 uint64 = 0, 1 for i := uint(2); i < n; i++ { n2, n1 = n1, n1+n2 } return n2 + n1, nil } // 這里模擬服務(wù)之間的掉用的效果. func Run(ctx context.Context) { _, span := otel.Tracer("nihao").Start(ctx, "two") defer span.End() fibonacci, err := Fibonacci(20) if err != nil { return } fmt.Println(fibonacci) } // newExporter returns a console exporter. // 創(chuàng)建導(dǎo)出器,對于鏈路追蹤服務(wù)器的選擇就是在這里配置 // 下面是基于jaeger的導(dǎo)出器. // 這里需要注意,在使用jaeger導(dǎo)出器的時候傳輸格式是jaeger.thrift over HTTP // 所以在配置url時請使用14268端口 與/api/traces func newExporter(url string) (*jaeger.Exporter, error) { return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) } // 資源是一種特殊類型的屬性,適用于進(jìn)程生成的所有跨度。這些應(yīng)該用于表示有關(guān)非臨時進(jìn)程的底層元數(shù)據(jù) // 例如,進(jìn)程的主機(jī)名或其實例 ID func newResource() *resource.Resource { r, _ := resource.Merge( resource.Default(), resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("fib"), semconv.ServiceVersionKey.String("v0.1.0"), attribute.String("environment", "demo"), ), ) return r } // 主函數(shù) func main() { url := "http://collector.tl.com:31498/api/traces" // 創(chuàng)建導(dǎo)出器 exp, _ := newExporter(url) // 創(chuàng)建鏈路生成器,這里將導(dǎo)出器與資源信息配置進(jìn)去. tp := trace.NewTracerProvider( trace.WithBatcher(exp), trace.WithResource(newResource()), ) // 結(jié)束時關(guān)閉鏈路生成器 defer func() { if err := tp.Shutdown(context.Background()); err != nil { fmt.Println(err) } }() // 將創(chuàng)建的生成器設(shè)置為全局變量. // 這樣直接使用otel.Tracer就可以創(chuàng)建鏈路. // 否則 就要使用 tp.Tracer的形式創(chuàng)建鏈路. otel.SetTracerProvider(tp) newCtx, span := otel.Tracer("nihao").Start(context.Background(), "one") Run(newCtx) span.End() }
原理
運行成功了,那么它的底層原理是怎么實現(xiàn)的,每個組件是怎么組織的?
這里我將根據(jù)上面的例子畫一張流程圖來進(jìn)行說明.
通過上面的圖我們可以知道,跨度之間的傳輸使用的是Context 來實現(xiàn)的.
使用span.end()就是將當(dāng)前跨度信息發(fā)送給鏈路追蹤服務(wù)器.
方法使用
接下來讓我們根據(jù)上面的例子講解一下他們每個方法的使用方式與配置吧!
newExporter
它的作用是創(chuàng)建一個導(dǎo)出器
在里面有兩種配置
// 配置jaeger服務(wù)器 jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)) // 參數(shù)是一個options ...CollectorEndpointOption所以讓我們看一下都有哪些配置 jaeger.WithEndpoint(endpoint string) // 配置服務(wù)器地址 jaeger.WithUsername(username string) // 配置用戶名 jaeger.WithPassword(password string) // 配置密碼 jaeger.WithHTTPClient(client *http.Client) //設(shè)置用于發(fā)送給jeager服務(wù)器所用到的client,有默認(rèn).這里可以設(shè)置 // 配置jaeger-agent 代理地址 jaeger.WithAgentEndpoint() // 參數(shù)是options ...AgentEndpointOption jaeger.WithAgentHost(host string) // 設(shè)置代理host jaeger.WithAgentPort(port string) // 設(shè)置代理端口 jaeger.WithLogger(logger *log.Logger) // 設(shè)置日志系統(tǒng) jaeger.WithDisableAttemptReconnecting() // 設(shè)置禁止重新連接UDP jaeger.WithAttemptReconnectingInterval(interval time.Duration) // 設(shè)置代理解析jaeger服務(wù)器之間的時間間隔 jaeger.WithMaxPacketSize(size int) // 設(shè)置UDP傳輸?shù)淖畲蟀?/pre>
無論是使用agent還是Collector,導(dǎo)出器都 使用batchUploader,也就是批量上傳策略.
newResource
存在于每個span中,在jaegerUI上顯示的是tag下面的Process
它有默認(rèn)設(shè)置,也可以自定義,下面讓我們看一下具體使用
// merge 只是合并下面的兩組資源. r, _ := resource.Merge( // 設(shè)置默認(rèn)的資源. 進(jìn)程id等 resource.Default(), // 設(shè)置自定義屬性 resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("fib"), semconv.ServiceVersionKey.String("v0.1.0"), // 我們可以使用該方法,來進(jìn)行添加自定義屬性 attribute.String("environment", "demo"), ), )
這里并不強(qiáng)制使用resource.NewWithAttributes,只要返回值是 resource.Resource 即可
trace.NewTracerProvider
創(chuàng)建一個鏈路生成器,我們主要使用它來進(jìn)行鏈路追蹤
// 創(chuàng)建生成器,這里配置了資源與 導(dǎo)出器. tp := trace.NewTracerProvider( trace.WithBatcher(exp), trace.WithResource(newResource()), ) // 下面讓我們看一下它都有哪些配置 // 這里創(chuàng)建的是批量處理導(dǎo)出器,也就是我們常用的 WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) // 這里可以添加別的導(dǎo)出器,也就是說可以同時發(fā)送給jaeger與zipkin WithSpanProcessor(sp SpanProcessor) // 配置資源 WithResource(r *resource.Resource) // Tracers id與span id生成器,默認(rèn)為隨機(jī)數(shù) WithIDGenerator(g IDGenerator) // 設(shè)置采樣數(shù) WithSampler(s Sampler) // 設(shè)置屬性,資源的數(shù)量 WithSpanLimits(sl SpanLimits) WithRawSpanLimits(limits SpanLimits)
這里重點講解一下SpanProcessor.
SpanProcessor是一個導(dǎo)出器策略,存儲著如何發(fā)送span到鏈路服務(wù)器上,有兩種方式批處理和立即發(fā)送。
我們上面說的WithCollectorEndpoint 與 WithAgentEndpoint 都是創(chuàng)建的BatchSpanProcessor 也就是批處理導(dǎo)出器策略.還存在一個SpanProcessor為simpleSpanProcessor,這個就是立刻發(fā)送,沒有批處理操作.
我們添加其他導(dǎo)出器以及策略的方式:
如果使用BatchSpanProcessor 那么在創(chuàng)建TracerProvider時的傳參使用trace.WithBatcher即可
如果想自定義,則使用WithSpanProcessor
比如使用simpleSpanProcessor,則設(shè)置為 WithSpanProcessor(jaeger.NewSimpleSpanProcessor(exporter SpanExporter))
沒錯,想要添加一個導(dǎo)出器就需要配置導(dǎo)出策略。所以導(dǎo)出器與導(dǎo)出策略時綁定的。
otel
otel是一個全局配置組件,我們可以將生成器設(shè)置為全局屬性,方便調(diào)用。
otel.SetTracerProvider(tp)
注意
具體的span使用請訪問
下文都是拷貝官網(wǎng).
獲取當(dāng)前跨度
通過上面我們知道,對當(dāng)前跨度的操作是使用span就可以了,但是在日常過程中我們不能傳輸兩個值一個Context和span(通常只傳輸Context),所以可以使用Context逆向解析出當(dāng)前的span.
ctx := context.TODO() span := trace.SpanFromContext(ctx)
:::success
下面所有設(shè)置都會在每個span的log中展示出來,只是為了更能標(biāo)識信息,沒有實質(zhì)性的用處.(比如設(shè)置even,并不是運行這個事件,只是輸出一個key為even value 為msg的信息)
:::
設(shè)置span狀態(tài)
span.SetStatus(codes.Error, "operationThatCouldFail failed")
設(shè)置span屬性
span.SetAttributes(attribute.Bool("isTrue", true), attribute.String("stringAttr", "hi!"))
記錄錯誤
span.RecordError(err)
設(shè)置活動
span.AddEvent("Acquiring lock")
tp.Shutdown
注意在使用完后已經(jīng)要調(diào)用Shutdown,它的功能是關(guān)閉客戶端與鏈路服務(wù)器之間的連接,也就是我們?yōu)閮蓚€服務(wù)建立連接后為了防止資源浪費所以會調(diào)用 con.close() 即使關(guān)閉.
defer func() { if err := tp.Shutdown(context.Background()); err != nil { fmt.Println(err) } }()
到此這篇關(guān)于OpenTelemetry-go的SDK使用方法的文章就介紹到這了,更多相關(guān)OpenTelemetry-go的sdk內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go使用協(xié)程批量獲取數(shù)據(jù)加快接口返回速度
這篇文章主要介紹了Go使用協(xié)程批量獲取數(shù)據(jù)加快接口返回速度,使用Go語言后,可以并發(fā)獲取,極大提升效率,需要的朋友可以參考下2023-02-02golang?pprof?監(jiān)控goroutine?thread統(tǒng)計原理詳解
這篇文章主要為大家介紹了golang?pprof?監(jiān)控goroutine?thread統(tǒng)計原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04