Мониторинг Go приложений с помощью Proto Observability
На этой странице:
- Введение
- Добавление модулей
- Конфигурация трейсера
- Если приложение работает в Kubernetes
- Использование трейсера
- Использование трейсера вместе с OpenTracing
- Метрики Go runtime
Введение
Получение трейсов и метрик из Go приложений для Proto Observability Platform происходит посредством использования пакета pobtrace
и дополнительных пакетов для инструментации библиотек и фреймворков.
Общий процесс подключения вашего приложения:
- Установка ProtoOBP Агента
- добавление модуля
pobptrace
и модулей для автоматической инструментации библиотек - создание
tracer
- создание спанов (минимум 1 шаг из списка ниже обязателен)
- создание Entry спана
- создание локального спана и/или дочерних спанов
- создание Exit спана
- добавление тегов и логов к спану
- закрытие спанов
Добавление модулей
Добавление модуля pobptrace
go get -u git.proto.group/protoobp/pobp-trace-go/pobptrace
или добавьте в main.go файл
import(
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
Дополнительные модули
go get -u git.proto.group/protoobp/pobp-trace-go/contrib/net/http
Добавление модулей для автоматической инструментации библиотек и фреймворков
Для автоматической инструментации вызововов поддерживаются следующие библиотеки и фреймворки (примеры доступны в документации по ссылкам):
Пакеты для интеграции должны быть импортированы следующим образом:
import "git.proto.group/protoobp/pobp-trace-go/contrib/<PACKAGE_DIR>/<PACKAGE_NAME>"
Пример использование автоматической инструментации
Пример использования пакета для инструментации стандартных net/http
:
package main
import (
"fmt"
"net/http"
httptrace "git.proto.group/protoobp/pobp-trace-go/contrib/net/http" // дополнительный пакет
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func hello(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Hello My World!\n"))
}
func headers(w http.ResponseWriter, req *http.Request) {
for name, headers := range req.Header {
for _, h := range headers {
fmt.Fprintf(w, "%v: %v\n", name, h)
}
}
}
func main() {
tracer.Start(
tracer.WithService("test-server"),
tracer.WithRuntimeMetrics(),
tracer.WithAgentAddr("193.32.218.109:9080"),
tracer.WithDogstatsdAddress("193.32.218.109"),
)
mux := httptrace.NewServeMux()
defer tracer.Stop()
mux.HandleFunc("/hello", hello)
mux.HandleFunc("/headers", headers)
http.ListenAndServe(":8090", mux)
}
Конфигурация трейсера
Трейсер поддерживает конфигурацию через переменные окружения или через параметры инициализации в коде вашего приложения.
Конфигурация трейсера через переменные окружения
Добавления переменных окружения упрощает конфигурацию трейсера - это рекомендуемый метод конфигурации.
Переменные | Описание | Значения по умолчанию |
---|---|---|
POBP_SERVICE |
Имя сервиса для интерфейса | Не установлено |
POBP_AGENT_HOST |
Адрес по которому доступен ProtoOBP агент | Не установлено |
POBP_DOGSTATSD_NON_LOCAL_TRAFFIC |
Включение метрик Go runtime | false |
Если приложение запускается в Docker контейнере
Пример готового Dockerfile
ENV POBP_SERVICE=dispatch
ENV POBP_AGENT_HOST=protoobp-agent
ENV POBP_DOGSTATSD_NON_LOCAL_TRAFFIC=true
Если приложение работает в Kubernetes
Убедитесь, что у вас успешно установлен и настроен ProtoOBP Агент для Kubernetes.
Дополнительно необходимо передать поду переменную окружения POBP_AGENT_HOST
со значением IP адреса воркер-ноды, а также переменные окружения для связи трейсов с инфраструктурой (имя k8s кластера нужно задать вручную).
apiVersion: apps/v1
kind: Deployment
#(...)
spec:
containers:
- name: "<CONTAINER_NAME>"
image: "<CONTAINER_IMAGE>/<TAG>"
env:
- name: POBP_SERVICE
value: dispatch
- name: POBP_AGENT_HOST
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POBP_TAGS
value: "pod_name:$(POD_NAME),node:$(NODE_NAME),kube_namespace:$(POD_NAMESPACE),kube_cluster_name:<my_cluster_name>"
Конфигурация трейсера при инициализации в коде
Параметры можно задать явным образом при инициализаци трейсера. В случае добавления ранее переменных окружения, достаточно указать только tracer.WithRuntimeMetrics()
для отдачи метрик.
package main
import (
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func main() {
tracer.Start(
tracer.WithService("test-server"), // имя сервиса
tracer.WithRuntimeMetrics(), // включение передачи Go метрик
tracer.WithAgentAddr("protoobp-agent"), // адрес по которому доступен ProtoOBP агент
tracer.WithDogstatsdAddress("protoobp-agent"), // адрес по которому доступен ProtoOBP агент
)
defer tracer.Stop()
}
Использование трейсера
Создание трейсера
package main
import (
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func main() {
tracer.Start(
tracer.WithService("test-server"), // имя сервиса
tracer.WithRuntimeMetrics(), // включение передачи Go метрик
)
defer tracer.Stop()
}
Ручное добавление спанов
Если автоматической инструментации библиотек и фреймворков недостаточно, вы можете вручную добавить спаны. Для добавления спанов доступно две функции - StartSpan
и StartSpanFromContext
//Создание спана с эндпонитом /user, который дочерный к родительскому спану.
span := tracer.StartSpan("mainOp", tracer.ResourceName("/user"), tracer.ChildOf(parentSpan))
// Создание спана, который будет дочерним к спану в контексте ctx, если в контексте есть спан.
// Возвращает новый спан, и новый контекст, содержащий спан.
span, ctx := tracer.StartSpanFromContext(ctx, "mainOp", tracer.ResourceName("/user"))
Пример ручного создания спанов
package main
import (
"io/ioutil"
"log"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/ext"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func main() {
// Запускаем трейсер и не забывает про Stop.
tracer.Start(tracer.WithAgentAddr("host:port"))
defer tracer.Stop()
// Начинаем корневой спан.
span := tracer.StartSpan("get.data")
defer span.Finish()
// Создаем дочерный спан, вычисляем время, необходимое для открытия файла.
child := tracer.StartSpan("read.file", tracer.ChildOf(span.Context()))
child.SetTag(ext.ResourceName, "test.json")
// Выполняем операцию.
_, err := ioutil.ReadFile("~/test.json")
// Мы можем завершить дочерный спан используя возвращаемую ошибку. Если это
// nil, будет отброшено.
child.Finish(tracer.WithError(err))
if err != nil {
log.Fatal(err)
}
}
Добавление тегов к спану
package main
import (
"log"
"net/http"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Создание спана для web request для запросов к /posts.
// ResourceName - имя эндпоинта в Proto OBP
span := tracer.StartSpan("web.request", tracer.ResourceName("/posts"))
defer span.Finish()
// Добавляем тег
span.SetTag("http.url", r.URL.Path)
span.SetTag("<TAG_KEY>", "<TAG_VALUE>")
}
func main() {
tracer.Start(tracer.WithService("<SERVICE_NAME>"))
defer tracer.Stop()
http.HandleFunc("/posts", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Пример добавления тэга к спану со значением кастомного HTTP заголовка
Используйте функцию span.SetTag("<KEY>", "<VALUE>")
для добавления HTTP заголовка в тэг трейса, где:
KEY
- должен начинаться с http.request.headers.<ИМЯ_HTTP_ЗАГОЛОВКА>
VALUE
- значение заголовка
Пример:
package main
import (
"log"
"net/http"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Создание спана для web request для запросов к /posts.
// ResourceName - имя эндпоинта в Proto OBP
span := tracer.StartSpan("web.request", tracer.ResourceName("/posts"))
defer span.Finish()
// Добавляем тег
span.SetTag("http.url", r.URL.Path)
//Добавляем HTTP заголовк в тэг трейса
span.SetTag("http.request.headers.x-request-id", "<VALUE>")
}
func main() {
tracer.Start(tracer.WithService("<SERVICE_NAME>"))
defer tracer.Stop()
http.HandleFunc("/posts", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Распределенный трейсинг и обработка контекста
Мы используем тип Context
для связи спанов. Если нужно добавить теги связанные с Context
, вызывайте SpanFromContext
:
package main
import (
"net/http"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Спан для веб запроса связанный с Go Context.
if span, ok := tracer.SpanFromContext(r.Context()); ok {
// добавляем тег.
span.SetTag("http.url", r.URL.Path)
}
}
Создание распределенного трейса путем ручного внедрения контекста:
package main
import (
"net/http"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func handler(w http.ResponseWriter, r *http.Request) {
span, ctx := tracer.StartSpanFromContext(r.Context(), "post.process")
defer span.Finish()
req, err := http.NewRequest("GET", "http://example.com", nil)
req = req.WithContext(ctx)
// Внедрение Context хедеры Request
err = tracer.Inject(span.Context(), tracer.HTTPHeadersCarrier(req.Header))
if err != nil {
// Обработка или логгирование ошибки внедрения контекста
}
http.DefaultClient.Do(req)
}
Далее на серверной стороне для продолжения трейса создайте новый спан из извлеченного контекста:
package main
import (
"net/http"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Извлекаем контекст спана и продолжаем трейс в этом сервисе
sctx, err := tracer.Extract(tracer.HTTPHeadersCarrier(r.Header))
if err != nil {
// Обработка или логгирование ошибки извлечения контекста
}
span := tracer.StartSpan("post.filter", tracer.ChildOf(sctx))
defer span.Finish()
}
Ошибочный спан
Для добавления признака ошибки, используйте tracer.WithError
:
err := someOperation()
span.Finish(tracer.WithError(err))
Трейсинг асинхронных запросов
func main() {
span, ctx := tracer.StartSpanFromContext(context.Background(), "mainOp")
defer span.Finish()
go func() {
asyncSpan := tracer.StartSpanFromContext(ctx, "asyncOp")
defer asyncSpan.Finish()
performOp()
}()
}
Использование трейсера вместе с OpenTracing
package main
import (
opentracing "github.com/opentracing/opentracing-go"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/opentracer"
"git.proto.group/protoobp/pobp-trace-go/pobptrace/tracer"
)
func main() {
//Запускаем POBP tracer, опционально передаем опции (лучше все определить через переменные окружения),
// возвращем opentracing.Tracer который оборачивает его.
t := opentracer.New(tracer.WithAgentAddr("host:port"))
defer tracer.Stop() // не забываем останавливать, иначе трейсы не придут
// Используем с Opentracing API. Уже запущенный POBP tracer
// может быть использован параллельно с Opentracing API если нужно.
opentracing.SetGlobalTracer(t)
}
Метрики Go runtime
Включение метрик Go runtime
TBD
Список метрик Go runtime, собираемых Proto OBP
TBD
Отображение метрик Go runtime
TBD
Правила алертинга, доступные при установке Proto OBP, по метрикам Go runtime
TBD