Початок роботи

Ця сторінка покаже вам, як почати роботу з OpenTelemetry в Go.

Ви дізнаєтеся, як можна інструментувати простий застосунок вручну, таким чином, щоб трейси, метрики, і логи виводилися в консоль.

Передумови

Переконайтеся, що у вас встановлено наступне:

  • Go 1.23 або новіше

Приклад застосунку

Наступний приклад використовує базовий застосунок net/http. Якщо ви не використовуєте net/http, це не проблема — ви можете використовувати OpenTelemetry Go з іншими веб-фреймворками, такими як Gin та Echo. Для повного списку бібліотек для підтримуваних фреймворків дивіться реєстр.

Для складніших прикладів дивіться приклади.

Налаштування

Для початку налаштуйте go.mod у новій теці:

go mod init dice

Створення та запуск HTTP сервера

У тій самій теці створіть файл з назвою main.go і додайте наступний код до файлу:

package main import ( "log" "net/http" ) func main() { http.HandleFunc("/rolldice", rolldice) log.Fatal(http.ListenAndServe(":8080", nil)) }

Створіть інший файл з назвою rolldice.go і додайте наступний код до файлу:

package main import ( "io" "log" "math/rand" "net/http" "strconv" ) func rolldice(w http.ResponseWriter, r *http.Request) { roll := 1 + rand.Intn(6) resp := strconv.Itoa(roll) + "\n" if _, err := io.WriteString(w, resp); err != nil { log.Printf("Write failed: %v\n", err) } }

Зберіть і запустіть застосунок за допомогою наступної команди:

go run .

Відкрийте http://localhost:8080/rolldice у вашому вебоглядачі, щоб переконатися, що він працює.

Додавання інструментів OpenTelemetry

Тепер ми покажемо, як додати інструменти OpenTelemetry до демонстраційного застосунку. Якщо ви використовуєте свій власний застосунок, ви можете слідувати разом, просто зверніть увагу, що ваш код може трохи відрізнятися.

Додавання залежностей

Встановіть наступні пакунки:

go get "go.opentelemetry.io/otel" \ "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" \ "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" \ "go.opentelemetry.io/otel/exporters/stdout/stdoutlog" \ "go.opentelemetry.io/otel/sdk/log" \ "go.opentelemetry.io/otel/log/global" \ "go.opentelemetry.io/otel/propagation" \ "go.opentelemetry.io/otel/sdk/metric" \ "go.opentelemetry.io/otel/sdk/resource" \ "go.opentelemetry.io/otel/sdk/trace" \ "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"\ "go.opentelemetry.io/contrib/bridges/otelslog"

Це встановлює компоненти SDK OpenTelemetry та інструменти для net/http.

Якщо ви інструментуєте іншу бібліотеку для мережевих запитів, вам потрібно буде встановити відповідну бібліотеку інструментів. Дивіться бібліотеки для отримання додаткової інформації.

Ініціалізація SDK OpenTelemetry

Спочатку ми ініціалізуємо SDK OpenTelemetry. Це обовʼязково для будь-якого застосунку, який експортує телеметрію.

Створіть otel.go з кодом для завантаження SDK OpenTelemetry:

package main import ( "context" "errors" "time" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/stdout/stdoutlog" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/log" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/trace" ) // setupOTelSDK завантажує конвеєр OpenTelemetry. // Якщо він не повертає помилку, обовʼязково викличте shutdown для правильного очищення. func setupOTelSDK(ctx context.Context) (func(context.Context) error, error) { var shutdownFuncs []func(context.Context) error var err error // shutdown викликає функції очищення, зареєстровані через shutdownFuncs. // Помилки з викликів обʼєднуються. // Кожне зареєстроване очищення буде викликано один раз. shutdown := func(ctx context.Context) error { var err error for _, fn := range shutdownFuncs { err = errors.Join(err, fn(ctx)) } shutdownFuncs = nil return err } // handleErr викликає shutdown для очищення і переконується, що всі помилки повертаються. handleErr := func(inErr error) { err = errors.Join(inErr, shutdown(ctx)) } // Налаштування поширювача. prop := newPropagator() otel.SetTextMapPropagator(prop) // Налаштування провайдера трасування. tracerProvider, err := newTracerProvider() if err != nil { handleErr(err) return shutdown, err } shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown) otel.SetTracerProvider(tracerProvider) // Налаштування провайдера метрик. meterProvider, err := newMeterProvider() if err != nil { handleErr(err) return return shutdown, err } shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown) otel.SetMeterProvider(meterProvider) // Налаштування провайдера логів. loggerProvider, err := newLoggerProvider() if err != nil { handleErr(err) return return shutdown, err } shutdownFuncs = append(shutdownFuncs, loggerProvider.Shutdown) global.SetLoggerProvider(loggerProvider) return return shutdown, err } func newPropagator() propagation.TextMapPropagator { return propagation.NewCompositeTextMapPropagator( propagation.TraceContext{}, propagation.Baggage{}, ) } func newTracerProvider() (*trace.TracerProvider, error) { traceExporter, err := stdouttrace.New( stdouttrace.WithPrettyPrint()) if err != nil { return nil, err } tracerProvider := trace.NewTracerProvider( trace.WithBatcher(traceExporter, // Стандартно 5s. Встановлено на 1s для демонстраційних цілей. trace.WithBatchTimeout(time.Second)), ) return tracerProvider, nil } func newMeterProvider() (*metric.MeterProvider, error) { metricExporter, err := stdoutmetric.New() if err != nil { return nil, err } meterProvider := metric.NewMeterProvider( metric.WithReader(metric.NewPeriodicReader(metricExporter, // Стандартно 1m. Встановлено на 3s для демонстраційних цілей. metric.WithInterval(3*time.Second))), ) return meterProvider, nil } func newLoggerProvider() (*log.LoggerProvider, error) { logExporter, err := stdoutlog.New() if err != nil { return nil, err } loggerProvider := log.NewLoggerProvider( log.WithProcessor(log.NewBatchProcessor(logExporter)), ) return loggerProvider, nil }

Якщо ви використовуєте лише трасування або метрики, ви можете пропустити відповідний код ініціалізації TracerProvider або MeterProvider.

Інструментування HTTP сервера

Тепер, коли ми ініціалізували SDK OpenTelemetry, ми можемо інструментувати HTTP сервер.

Змініть main.go, щоб включити код, який налаштовує SDK OpenTelemetry та інструментує HTTP сервер за допомогою бібліотеки інструментів otelhttp:

package main import ( "context" "errors" "log" "net" "net/http" "os" "os/signal" "time" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) func main() { if err := run(); err != nil { log.Fatalln(err) } } func run() error { // Належна обробка SIGINT (CTRL+C). ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() // Налаштування OpenTelemetry. otelShutdown, err := setupOTelSDK(ctx) if err != nil { return err } // Правильне завершення роботи, щоб нічого не витікало. defer func() { err = errors.Join(err, otelShutdown(context.Background())) }() // Запуск HTTP сервера. srv := &http.Server{ Addr: ":8080", BaseContext: func(_ net.Listener) context.Context { return ctx }, ReadTimeout: time.Second, WriteTimeout: 10 * time.Second, Handler: newHTTPHandler(), } srvErr := make(chan error, 1) go func() { srvErr <- srv.ListenAndServe() }() // Очікування переривання. select { case err = <-srvErr: // Помилка при запуску HTTP сервера. return err case <-ctx.Done(): // Очікування першого CTRL+C. // Припинення отримання повідомлень про сигнали якомога швидше. stop() } // Коли викликається Shutdown, ListenAndServe негайно повертає ErrServerClosed. err = srv.Shutdown(context.Background()) return err } func newHTTPHandler() http.Handler { mux := http.NewServeMux() // handleFunc є заміною для mux.HandleFunc // який збагачує HTTP інструментування обробника зразком як http.route. handleFunc := func(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) { // Налаштування "http.route" для HTTP інструментування. handler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handlerFunc)) mux.Handle(pattern, handler) } // Реєстрація обробників. handleFunc("/rolldice/", rolldice) handleFunc("/rolldice/{player}", rolldice) // Додавання HTTP інструментування для всього сервера. handler := otelhttp.NewHandler(mux, "/") return handler }

Додавання користувацького інструментування

Бібліотеки інструментування захоплюють телеметрію на краях ваших систем, таких як вхідні та вихідні HTTP запити, але вони не захоплюють те, що відбувається у вашому застосунку. Для цього вам потрібно написати деяке власне ручне інструментування.

Змініть rolldice.go, щоб включити власне інструментування за допомогою API OpenTelemetry:

package main import ( "fmt" "io" "log" "math/rand" "net/http" "strconv" "go.opentelemetry.io/contrib/bridges/otelslog" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" ) const name = "go.opentelemetry.io/otel/example/dice" var ( tracer = otel.Tracer(name) meter = otel.Meter(name) logger = otelslog.NewLogger(name) rollCnt metric.Int64Counter ) func init() { var err error rollCnt, err = meter.Int64Counter("dice.rolls", metric.WithDescription("Кількість кидків за значенням кидка"), metric.WithUnit("{roll}")) if err != nil { panic(err) } } func rolldice(w http.ResponseWriter, r *http.Request) { ctx, span := tracer.Start(r.Context(), "roll") defer span.End() roll := 1 + rand.Intn(6) var msg string if player := r.PathValue("player"); player != "" { msg = fmt.Sprintf("%s кидає кістки", player) } else { msg = "Анонімний гравець кидає кістки" } logger.InfoContext(ctx, msg, "result", roll) rollValueAttr := attribute.Int("roll.value", roll) span.SetAttributes(rollValueAttr) rollCnt.Add(ctx, 1, metric.WithAttributes(rollValueAttr)) resp := strconv.Itoa(roll) + "\n" if _, err := io.WriteString(w, resp); err != nil { log.Printf("Write failed: %v\n", err) } }

Зверніть увагу, що якщо ви використовуєте лише трасування або метрики, ви можете пропустити відповідний код, який інструментує інший тип телеметрії.

Запуск застосунку

Зберіть і запустіть застосунок за допомогою наступної команди:

go mod tidy export OTEL_RESOURCE_ATTRIBUTES="service.name=dice,service.version=0.1.0" go run .

Відкрийте http://localhost:8080/rolldice/Alice у вашому вебоглядачі. Коли ви надішлете запит до сервера, ви побачите два відрізки у трейсі, що виводяться в консоль. Відрізок, створений бібліотекою інструментів, відстежує тривалість запиту до маршруту /rolldice/{player}. Відрізок з назвою roll створюється вручну і є дочірнім до попередньо згаданого відрізка.

Переглянути приклад виводу
{ "Name": "roll", "SpanContext": { "TraceID": "829fb7ceb787403c96eac3caf285c965", "SpanID": "8b6b408b6c1a35e5", "TraceFlags": "01", "TraceState": "", "Remote": false }, "Parent": { "TraceID": "829fb7ceb787403c96eac3caf285c965", "SpanID": "612be4bbdf450de6", "TraceFlags": "01", "TraceState": "", "Remote": false }, "SpanKind": 1, "StartTime": "2023-09-25T12:42:06.177119576+02:00", "EndTime": "2023-09-25T12:42:06.177136776+02:00", "Attributes": [ { "Key": "roll.value", "Value": { "Type": "INT64", "Value": 6 } } ], "Events": null, "Links": null, "Status": { "Code": "Unset", "Description": "" }, "DroppedAttributes": 0, "DroppedEvents": 0, "DroppedLinks": 0, "ChildSpanCount": 0, "Resource": [ { "Key": "service.name", "Value": { "Type": "STRING", "Value": "dice" } }, { "Key": "service.version", "Value": { "Type": "STRING", "Value": "0.1.0" } }, { "Key": "telemetry.sdk.language", "Value": { "Type": "STRING", "Value": "go" } }, { "Key": "telemetry.sdk.name", "Value": { "Type": "STRING", "Value": "opentelemetry" } }, { "Key": "telemetry.sdk.version", "Value": { "Type": "STRING", "Value": "1.19.0-rc.1" } } ], "InstrumentationLibrary": { "Name": "rolldice", "Version": "", "SchemaURL": "" } } { "Name": "/", "SpanContext": { "TraceID": "829fb7ceb787403c96eac3caf285c965", "SpanID": "612be4bbdf450de6", "TraceFlags": "01", "TraceState": "", "Remote": false }, "Parent": { "TraceID": "00000000000000000000000000000000", "SpanID": "0000000000000000", "TraceFlags": "00", "TraceState": "", "Remote": false }, "SpanKind": 2, "StartTime": "2023-09-25T12:42:06.177071077+02:00", "EndTime": "2023-09-25T12:42:06.177158076+02:00", "Attributes": [ { "Key": "http.method", "Value": { "Type": "STRING", "Value": "GET" } }, { "Key": "http.scheme", "Value": { "Type": "STRING", "Value": "http" } }, { "Key": "http.flavor", "Value": { "Type": "STRING", "Value": "1.1" } }, { "Key": "net.host.name", "Value": { "Type": "STRING", "Value": "localhost" } }, { "Key": "net.host.port", "Value": { "Type": "INT64", "Value": 8080 } }, { "Key": "net.sock.peer.addr", "Value": { "Type": "STRING", "Value": "::1" } }, { "Key": "net.sock.peer.port", "Value": { "Type": "INT64", "Value": 49046 } }, { "Key": "http.user_agent", "Value": { "Type": "STRING", "Value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36" } }, { "Key": "http.route", "Value": { "Type": "STRING", "Value": "/rolldice/Alice" } }, { "Key": "http.wrote_bytes", "Value": { "Type": "INT64", "Value": 2 } }, { "Key": "http.status_code", "Value": { "Type": "INT64", "Value": 200 } } ], "Events": null, "Links": null, "Status": { "Code": "Unset", "Description": "" }, "DroppedAttributes": 0, "DroppedEvents": 0, "DroppedLinks": 0, "ChildSpanCount": 1, "Resource": [ { "Key": "service.name", "Value": { "Type": "STRING", "Value": "dice" } }, { "Key": "service.version", "Value": { "Type": "STRING", "Value": "0.1.0" } }, { "Key": "telemetry.sdk.language", "Value": { "Type": "STRING", "Value": "go" } }, { "Key": "telemetry.sdk.name", "Value": { "Type": "STRING", "Value": "opentelemetry" } }, { "Key": "telemetry.sdk.version", "Value": { "Type": "STRING", "Value": "1.19.0-rc.1" } } ], "InstrumentationLibrary": { "Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp", "Version": "0.44.0", "SchemaURL": "" } }

Разом з трейсом, повідомлення журналу виводяться в консоль.

Переглянути приклад виводу
{ "Timestamp": "2023-09-25T12:42:05.177136776+02:00", "ObservedTimestamp": "2023-09-25T12:42:06.809396011+02:00", "Severity": 9, "SeverityText": "", "Body": { "Type": "String", "Value": "Alice is rolling the dice" }, "Attributes": [ { "Key": "result", "Value": { "Type": "Int64", "Value": 6 } } ], "TraceID": "829fb7ceb787403c96eac3caf285c965", "SpanID": "8b6b408b6c1a35e5", "TraceFlags": "01", "Resource": [ { "Key": "service.name", "Value": { "Type": "STRING", "Value": "dice" } }, { "Key": "service.version", "Value": { "Type": "STRING", "Value": "0.1.0" } }, { "Key": "telemetry.sdk.language", "Value": { "Type": "STRING", "Value": "go" } }, { "Key": "telemetry.sdk.name", "Value": { "Type": "STRING", "Value": "opentelemetry" } }, { "Key": "telemetry.sdk.version", "Value": { "Type": "STRING", "Value": "1.19.0-rc.1" } } ], "Scope": { "Name": "rolldice", "Version": "", "SchemaURL": "" }, "DroppedAttributes": 0 }

Оновіть сторінку http://localhost:8080/rolldice/Alice кілька разів, а потім або зачекайте трохи, або завершіть роботу застосунку, і ви побачите метрики у виводі консолі. Ви побачите метрику dice.rolls, що виводиться в консоль, з окремими підрахунками для кожного значення кидка, а також HTTP метрики, створені бібліотекою інструментів.

Переглянути приклад виводу
{ "Resource": [ { "Key": "service.name", "Value": { "Type": "STRING", "Value": "dice" } }, { "Key": "service.version", "Value": { "Type": "STRING", "Value": "0.1.0" } }, { "Key": "telemetry.sdk.language", "Value": { "Type": "STRING", "Value": "go" } }, { "Key": "telemetry.sdk.name", "Value": { "Type": "STRING", "Value": "opentelemetry" } }, { "Key": "telemetry.sdk.version", "Value": { "Type": "STRING", "Value": "1.19.0-rc.1" } } ], "ScopeMetrics": [ { "Scope": { "Name": "rolldice", "Version": "", "SchemaURL": "" }, "Metrics": [ { "Name": "dice.rolls", "Description": "Кількість кидків за значенням кидка", "Unit": "{roll}", "Data": { "DataPoints": [ { "Attributes": [ { "Key": "roll.value", "Value": { "Type": "INT64", "Value": 1 } } ], "StartTime": "2023-09-25T12:42:04.279204638+02:00", "Time": "2023-09-25T12:42:15.482694258+02:00", "Value": 4 }, { "Attributes": [ { "Key": "roll.value", "Value": { "Type": "INT64", "Value": 5 } } ], "StartTime": "2023-09-25T12:42:04.279204638+02:00", "Time": "2023-09-25T12:42:15.482694258+02:00", "Value": 3 }, { "Attributes": [ { "Key": "roll.value", "Value": { "Type": "INT64", "Value": 3 } } ], "StartTime": "2023-09-25T12:42:04.279204638+02:00", "Time": "2023-09-25T12:42:15.482694258+02:00", "Value": 4 }, { "Attributes": [ { "Key": "roll.value", "Value": { "Type": "INT64", "Value": 2 } } ], "StartTime": "2023-09-25T12:42:04.279204638+02:00", "Time": "2023-09-25T12:42:15.482694258+02:00", "Value": 2 }, { "Attributes": [ { "Key": "roll.value", "Value": { "Type": "INT64", "Value": 6 } } ], "StartTime": "2023-09-25T12:42:04.279204638+02:00", "Time": "2023-09-25T12:42:15.482694258+02:00", "Value": 5 }, { "Attributes": [ { "Key": "roll.value", "Value": { "Type": "INT64", "Value": 4 } } ], "StartTime": "2023-09-25T12:42:04.279204638+02:00", "Time": "2023-09-25T12:42:15.482694258+02:00", "Value": 9 } ], "Temporality": "CumulativeTemporality", "IsMonotonic": true } } ] }, { "Scope": { "Name": "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp", "Version": "0.44.0", "SchemaURL": "" }, "Metrics": [ { "Name": "http.server.request_content_length", "Description": "", "Unit": "", "Data": { "DataPoints": [ { "Attributes": [ { "Key": "http.flavor", "Value": { "Type": "STRING", "Value": "1.1" } }, { "Key": "http.method", "Value": { "Type": "STRING", "Value": "GET" } }, { "Key": "http.route", "Value": { "Type": "STRING", "Value": "/rolldice/Alice" } }, { "Key": "http.scheme", "Value": { "Type": "STRING", "Value": "http" } }, { "Key": "http.status_code", "Value": { "Type": "INT64", "Value": 200 } }, { "Key": "net.host.name", "Value": { "Type": "STRING", "Value": "localhost" } }, { "Key": "net.host.port", "Value": { "Type": "INT64", "Value": 8080 } } ], "StartTime": "2023-09-25T12:42:04.279212238+02:00", "Time": "2023-09-25T12:42:15.482695758+02:00", "Value": 0 } ], "Temporality": "CumulativeTemporality", "IsMonotonic": true } }, { "Name": "http.server.response_content_length", "Description": "", "Unit": "", "Data": { "DataPoints": [ { "Attributes": [ { "Key": "http.flavor", "Value": { "Type": "STRING", "Value": "1.1" } }, { "Key": "http.method", "Value": { "Type": "STRING", "Value": "GET" } }, { "Key": "http.route", "Value": { "Type": "STRING", "Value": "/rolldice/Alice" } }, { "Key": "http.scheme", "Value": { "Type": "STRING", "Value": "http" } }, { "Key": "http.status_code", "Value": { "Type": "INT64", "Value": 200 } }, { "Key": "net.host.name", "Value": { "Type": "STRING", "Value": "localhost" } }, { "Key": "net.host.port", "Value": { "Type": "INT64", "Value": 8080 } } ], "StartTime": "2023-09-25T12:42:04.279214438+02:00", "Time": "2023-09-25T12:42:15.482696158+02:00", "Value": 54 } ], "Temporality": "CumulativeTemporality", "IsMonotonic": true } }, { "Name": "http.server.duration", "Description": "", "Unit": "", "Data": { "DataPoints": [ { "Attributes": [ { "Key": "http.flavor", "Value": { "Type": "STRING", "Value": "1.1" } }, { "Key": "http.method", "Value": { "Type": "STRING", "Value": "GET" } }, { "Key": "http.route", "Value": { "Type": "STRING", "Value": "/rolldice/Alice" } }, { "Key": "http.scheme", "Value": { "Type": "STRING", "Value": "http" } }, { "Key": "http.status_code", "Value": { "Type": "INT64", "Value": 200 } }, { "Key": "net.host.name", "Value": { "Type": "STRING", "Value": "localhost" } }, { "Key": "net.host.port", "Value": { "Type": "INT64", "Value": 8080 } } ], "StartTime": "2023-09-25T12:42:04.279219438+02:00", "Time": "2023-09-25T12:42:15.482697158+02:00", "Count": 27, "Bounds": [ 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000 ], "BucketCounts": [ 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], "Min": {}, "Max": {}, "Sum": 2.1752759999999993 } ], "Temporality": "CumulativeTemporality" } } ] } ] }

Наступні кроки

Для отримання додаткової інформації про інструментування вашого коду зверніться до документації ручного інструментування.

Вам також потрібно буде налаштувати відповідний експортер для експорту ваших телеметричних даних до одного або декількох бекендів телеметрії.

Якщо ви хочете дослідити складніший приклад, подивіться на Демо OpenTelemetry, яке включає заснований на Go Сервіс оформлення замовлень, Сервіс каталогу продуктів, та Сервіс бухгалтерії


Востаннє змінено December 26, 2024: [uk] Ukrainian documentation for OpenTelemetry (bd70b52d)