Как получить thread id
или любой другой уникальный идентификатор http-запроса, обрабатываемого обработчиком при регистрации внутри Gorilla Handlers
?
В Java, когда Tomcat или другой контейнер обрабатывает несколько HTTP-запросов, идентификатор потока помогает отслеживать все сообщения журнала для соответствующей обработки http-запросов.
Чему соответствует Go
? Учитывая Rest API, разработанный с использованием библиотеки Gorilla
, как я могу отслеживать все операторы журнала конкретного http-запроса внутри обработки обработчика?
Идентификатор потока журнала Go в Gorilla Handler
Ответы (2)
Библиотека gorilla/handlers не предоставляет возможности сделать это по умолчанию: функции ведения журнала входят в систему. Форматы Apache, которые этого не предусматривают.
Также имейте в виду, что «идентификатор потока» здесь не имеет смысла — вам нужен идентификатор запроса, связанный с *http.Request
.
Вы можете написать собственное промежуточное ПО RequestID, которое создает идентификатор и сохраняет его в контексте запроса, чтобы другие промежуточное ПО/обработчики могли извлекать его по мере необходимости:
package main
import (
"crypto/rand"
"encoding/base64"
"net/http"
"github.com/gorilla/context"
)
const ReqID string = "gorilla.RequestID"
// RequestID wraps handlers and makes a unique (32-byte) request ID available in
// the request context.
// Example:
// http.Handle("/", RequestID(LoggingHandler(YourHandler)))
//
// func LoggingHandler(h http.Handler) http.Handler {
// fn := func(w http.ResponseWriter, r *http.Request) {
// h.ServeHTTP(w, r)
//
// id := GetRequestID(r)
// log.Printf("%s | %s", id, r.RemoteAddr)
// }
//
// return http.HandlerFunc(fn)
// }
func RequestID(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
b := make([]byte, 8)
_, err = rand.Read(&b)
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
base64ID := base64.URLEncoding.EncodeToString(b)
context.Set(r, ReqID, base64ID)
h.ServeHTTP(w, r)
// Clear the context at the end of the request lifetime
context.Clear(r)
}
return http.HandlerFunc(fn)
}
func GetRequestID(r *http.Request) string {
if v, ok := context.GetOK(r, ReqID); ok {
if id, ok := v.(string); ok {
return id
}
}
return ""
}
Имейте в виду, что приведенный выше код не тестировался. Написано с головы в Playground, поэтому, пожалуйста, дайте мне знать, если есть ошибка.
Улучшения, которые вы могли бы рассмотреть помимо этого базового примера:
- Добавьте к идентификатору префикс имени хоста — полезно, если вы собираете журналы из нескольких процессов/компьютеров)
- Укажите отметку времени или увеличивающееся целое число как часть окончательного идентификатора, чтобы помочь отслеживать запросы с течением времени.
- Сравните это.
Обратите внимание, что при чрезвычайно высоких нагрузках (например, десятки тысяч запросов в секунду — десятки миллионов обращений в день) это может быть неэффективно, но вряд ли станет узким местом для > 99 % пользователей.
PS: я могу посмотреть на реализацию handlers.RequestID
в библиотеке gorilla/handlers в какой-то момент - если вы хотите ее увидеть, поднимите вопрос в репозитории, и я посмотрю, смогу ли я найти время для реализации более полной взяться за вышеперечисленное.
На основе https://groups.google.com/forum/#!searchin/golang-nuts/Logging$20http$20thread/golang-nuts/vDNEH3_vMXQ/uyqGEwdchzgJ, ThreadLocal
концепция невозможна с Go.
Везде, где вам нужно ведение журнала, требуется передать экземпляр http Request
, чтобы можно было получить контекст, связанный с запросом, и можно было получить уникальный идентификатор из этого контекста для запроса. Но нецелесообразно передавать экземпляр запроса всем слоям/методам.