Query Log: Add source IP from console queries (#6593)

* Query Log: Add source IP from console queries

Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>
This commit is contained in:
Julien Pivotto 2020-01-10 13:56:36 +01:00 committed by Brian Brazil
parent 1e64d757f7
commit e7f7b6a06f
3 changed files with 87 additions and 15 deletions

50
util/httputil/context.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2020 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httputil
import (
"context"
"net"
"net/http"
"github.com/prometheus/prometheus/promql"
)
type ctxParam int
var pathParam ctxParam
// ContextWithPath returns a new context with the given path to be used later
// when logging the query.
func ContextWithPath(ctx context.Context, path string) context.Context {
return context.WithValue(ctx, pathParam, path)
}
// ContextFromRequest returns a new context from a requests with identifiers of
// the request to be used later when logging the query.
func ContextFromRequest(ctx context.Context, r *http.Request) (context.Context, error) {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return ctx, err
}
var path string
if v := ctx.Value(pathParam); v != nil {
path = v.(string)
}
return promql.NewOriginContext(ctx, map[string]string{
"clientIP": ip,
"method": r.Method,
"path": path,
}), nil
}

View File

@ -18,7 +18,6 @@ import (
"fmt" "fmt"
"math" "math"
"math/rand" "math/rand"
"net"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -342,7 +341,7 @@ func (api *API) query(r *http.Request) apiFuncResult {
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil}
} }
ctx, err = contextFromRequest(ctx, r) ctx, err = httputil.ContextFromRequest(ctx, r)
if err != nil { if err != nil {
return apiFuncResult{nil, returnAPIError(err), nil, nil} return apiFuncResult{nil, returnAPIError(err), nil, nil}
} }
@ -417,7 +416,7 @@ func (api *API) queryRange(r *http.Request) apiFuncResult {
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil} return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil}
} }
ctx, err = contextFromRequest(ctx, r) ctx, err = httputil.ContextFromRequest(ctx, r)
if err != nil { if err != nil {
return apiFuncResult{nil, returnAPIError(err), nil, nil} return apiFuncResult{nil, returnAPIError(err), nil, nil}
} }
@ -1480,11 +1479,3 @@ func marshalPointJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
func marshalPointJSONIsEmpty(ptr unsafe.Pointer) bool { func marshalPointJSONIsEmpty(ptr unsafe.Pointer) bool {
return false return false
} }
func contextFromRequest(ctx context.Context, r *http.Request) (context.Context, error) {
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
return ctx, err
}
return promql.NewOriginContext(ctx, map[string]string{"clientIP": ip}), nil
}

View File

@ -253,7 +253,11 @@ func New(logger log.Logger, o *Options) *Handler {
} }
m := newMetrics(o.Registerer) m := newMetrics(o.Registerer)
router := route.New().WithInstrumentation(m.instrumentHandler) router := route.New().
WithInstrumentation(combineInstrumentations(
m.instrumentHandler,
setPathWithPrefix(""),
))
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
@ -542,13 +546,17 @@ func (h *Handler) Run(ctx context.Context) error {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/", h.router) mux.Handle("/", h.router)
av1 := route.New().WithInstrumentation(h.metrics.instrumentHandlerWithPrefix("/api/v1"))
h.apiV1.Register(av1)
apiPath := "/api" apiPath := "/api"
if h.options.RoutePrefix != "/" { if h.options.RoutePrefix != "/" {
apiPath = h.options.RoutePrefix + apiPath apiPath = h.options.RoutePrefix + apiPath
level.Info(h.logger).Log("msg", "router prefix", "prefix", h.options.RoutePrefix) level.Info(h.logger).Log("msg", "router prefix", "prefix", h.options.RoutePrefix)
} }
av1 := route.New().
WithInstrumentation(combineInstrumentations(
h.metrics.instrumentHandlerWithPrefix("/api/v1"),
setPathWithPrefix(apiPath+"/v1"),
))
h.apiV1.Register(av1)
mux.Handle(apiPath+"/v1/", http.StripPrefix(apiPath+"/v1", av1)) mux.Handle(apiPath+"/v1/", http.StripPrefix(apiPath+"/v1", av1))
@ -643,6 +651,12 @@ func (h *Handler) consoles(w http.ResponseWriter, r *http.Request) {
return return
} }
ctx, err = httputil.ContextFromRequest(ctx, r)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Provide URL parameters as a map for easy use. Advanced users may have need for // Provide URL parameters as a map for easy use. Advanced users may have need for
// parameters beyond the first, so provide RawParams. // parameters beyond the first, so provide RawParams.
rawParams, err := url.ParseQuery(r.URL.RawQuery) rawParams, err := url.ParseQuery(r.URL.RawQuery)
@ -685,7 +699,7 @@ func (h *Handler) consoles(w http.ResponseWriter, r *http.Request) {
} }
tmpl := template.NewTemplateExpander( tmpl := template.NewTemplateExpander(
h.context, ctx,
strings.Join(append(defs, string(text)), ""), strings.Join(append(defs, string(text)), ""),
"__console_"+name, "__console_"+name,
data, data,
@ -1103,3 +1117,20 @@ type AlertByStateCount struct {
Pending int32 Pending int32
Firing int32 Firing int32
} }
func combineInstrumentations(fs ...func(handlerName string, handler http.HandlerFunc) http.HandlerFunc) func(string, http.HandlerFunc) http.HandlerFunc {
return func(handlerName string, handler http.HandlerFunc) http.HandlerFunc {
for _, f := range fs {
handler = f(handlerName, handler)
}
return handler
}
}
func setPathWithPrefix(prefix string) func(handlerName string, handler http.HandlerFunc) http.HandlerFunc {
return func(handlerName string, handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
handler(w, r.WithContext(httputil.ContextWithPath(r.Context(), prefix+r.URL.Path)))
}
}
}