Go log thread id в обработчике Gorilla
Как получить thread id
или любой другой уникальный идентификатор http-запроса, обрабатываемого обработчиком в журнале внутри Gorilla Handlers
?
В Java, когда Tomcat или другой контейнер обрабатывает несколько http-запросов, идентификатор потока помогает отслеживать все сообщения журнала для соответствующей обработки http-запроса.
Что такое эквивалент в Go
? Учитывая REST API, разработанный с использованием библиотеки Gorilla
, Как я могу отслеживать все операторы журнала конкретного http-запроса внутри обработчика?
2 ответа:
Библиотекаgorilla/handlers не предоставляет способа сделать это по умолчанию: функции ведения журнала там регистрируются в форматах Apache, которые этого не предусматривают.
Также имейте в виду, что" идентификатор потока " здесь не имеет смысла - вам нужен запрос ID, связанный с
*http.Request
.Вы можете написать свой собственный RequestID middleware, который создает идентификатор и сохраняет в контексте запроса для других middleware/обработчиков для извлечения по мере необходимости:
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 "" }
Держать имейте в виду, что приведенный выше код не тестируется. Списан с моей головы на детской площадке, так что, пожалуйста, дайте мне знать, если есть ошибка.
Улучшения, которые вы могли бы рассмотреть помимо этого основного примера:
- префикс ID с именем хоста-полезно, если вы агрегируете журналы из нескольких процессов / машин)
- укажите метку времени или увеличивающееся целое число в качестве части конечного идентификатора, чтобы помочь отслеживать запросы с течением времени
- проверьте его.
Обратите внимание, что под чрезвычайно высокие нагрузки (например, десятки тысяч req/s - десятки миллионов просмотров в день), это может не быть эффективным, но вряд ли будет узким местом для > 99% пользователей.
PS: я могу посмотреть на предоставление реализацииhandlers.RequestID
в библиотеке gorilla/handlers в какой - то момент-если вы хотите ее увидеть, поднимите вопрос о РЕПО, и я посмотрю, смогу ли я найти время для реализации более полного подхода к вышесказанному.
На основе https://groups.google.com/forum/#!searchin/golang-nuts/Logging$20http$20thread/golang-nuts/vDNEH3_vMXQ/uyqGEwdchzgJ,
ThreadLocal
концепция невозможна с Go.
каждый раз, когда вам нужно протоколировать, требуется передать экземпляр httpRequest
, чтобы можно было извлечь контекст, связанный с запросом, и получить уникальный идентификатор из этого контекста для запроса. Но не практично передавать экземпляр запроса всем слоям / методам.