Go log thread id в обработчике Gorilla


Как получить thread id или любой другой уникальный идентификатор http-запроса, обрабатываемого обработчиком в журнале внутри Gorilla Handlers?
В Java, когда Tomcat или другой контейнер обрабатывает несколько http-запросов, идентификатор потока помогает отслеживать все сообщения журнала для соответствующей обработки http-запроса.
Что такое эквивалент в Go? Учитывая REST API, разработанный с использованием библиотеки Gorilla, Как я могу отслеживать все операторы журнала конкретного http-запроса внутри обработчика?

2 2

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.

каждый раз, когда вам нужно протоколировать, требуется передать экземпляр http Request, чтобы можно было извлечь контекст, связанный с запросом, и получить уникальный идентификатор из этого контекста для запроса. Но не практично передавать экземпляр запроса всем слоям / методам.