Логи, correlation id и трейсы

О лекции

Структурированный лог с id запроса склеивает строки между сервисами. Трейс — дерево отрезков работы со временем. Метрики, логи и трейсы дополняют друг друга. Секреты в логах — инцидент.

Наблюдаемость и секреты observability собеседование

Лог — событие для человека и для поиска. Трейс — цепочка вызовов с длительностями. Метрика — сумма без каждого запроса.

Три столпа не заменяют друг друга. По P99 не видно, какой SQL тормозил. По логу без id не склеить три сервиса. По одному span не посчитать error rate за месяц.

Один id на весь запрос

В микросервисах без общего идентификатора вы смотрите три несвязанных кучи текста. Correlation id (он же request id, trace id) заводят на входе — на gateway.

Дальше id кладут в context, в заголовок, в лог и в span. Имя поля одно на все сервисы. Иначе один пишет reqId, другой request_id, grep умирает.

Пример. Заказ 500. В логах api есть строка, в billing — другая, в payments — третья. Одно поле trace_id=a1b2 склеивает их в одну историю.

package httpx

func WithTrace(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		id := r.Header.Get("X-Request-ID")
		if id == "" {
			id = newID()
		}
		ctx := context.WithValue(r.Context(), keyTrace, id)
		slog.InfoContext(ctx, "request",
			"trace_id", id,
			"route", r.URL.Path,
			"method", r.Method,
		)
		w.Header().Set("X-Request-ID", id)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

Id с входа попадает в лог и в исходящий заголовок. Секреты не логируем.

Структурированные логи

Пишите поля, не прозу. JSON: level, msg, time, стабильные ключи вроде order_id.

Фраза «user 5 paid 10 rub» потом не фильтруется. Уровни врут, если всё info. Id лучше нести в context.Context и читать в middleware, а не в каждом Print.

Трейсы

Span — отрезок работы: HTTP-хендлер, SQL, исходящий вызов. Родитель и дети дают дерево. Трейс отвечает «где съели 800 мс». Лог — «какое сообщение». Метрика — «как часто».

На десятках тысяч запросов в секунду хранить 100% трейсов дорого. Сэмплируют. Ошибки и медленные запросы лучше оставлять. Debug в проде обычно выключают.

Не логируйте Authorization, cookie, номера карт, пароли, сырой JWT, тела «на debug в проде». Утечка в хранилище логов — тот же класс, что секрет в git. Индекс ещё и бэкапится.

Пример. Трейс показал: хендлер 50 мс, SQL 740 мс. Лог без id этого не склеит. Метрика P99 скажет «медленно», но не покажет запрос.

Типичная ошибка

«У нас ELK» без структуры и без id — склад текста, не наблюдаемость. Логировать Authorization «на пять минут». Хранить 100% трейсов на высоком RPS — или наоборот 0.01% без уклона в ошибки, так что инцидент не попал в выборку.

На собеседовании

Id на входе → context → лог и span → исходящий заголовок. Логи, метрики и трейсы дополняют друг друга. Секреты и персональные данные в логах — инцидент.