Skip to content

Commit

Permalink
Add query-frontend config option to log request headers in query logs (
Browse files Browse the repository at this point in the history
  • Loading branch information
pr00se authored May 18, 2023
1 parent aec9b8a commit 6439eea
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* `-blocks-storage.bucket-store.chunk-pool-max-bucket-size-bytes`
* [CHANGE] Store-gateway: remove metrics `cortex_bucket_store_chunk_pool_requested_bytes_total` and `cortex_bucket_store_chunk_pool_returned_bytes_total`. #4996
* [CHANGE] Compactor: change default of `-compactor.partial-block-deletion-delay` to `1d`. This will automatically clean up partial blocks that were a result of failed block upload or deletion. #5026
* [FEATURE] Query-frontend: add `-query-frontend.log-query-request-headers` to enable logging of request headers in query logs. #5030
* [ENHANCEMENT] Add per-tenant limit `-validation.max-native-histogram-buckets` to be able to ignore native histogram samples that have too many buckets. #4765
* [ENHANCEMENT] Store-gateway: reduce memory usage in some LabelValues calls. #4789
* [ENHANCEMENT] Store-gateway: add a `stage` label to the metric `cortex_bucket_store_series_data_touched`. This label now applies to `data_type="chunks"` and `data_type="series"`. The `stage` label has 2 values: `processed` - the number of series that parsed - and `returned` - the number of series selected from the processed bytes to satisfy the query. #4797 #4830
Expand Down
11 changes: 11 additions & 0 deletions cmd/mimir/config-descriptor.json
Original file line number Diff line number Diff line change
Expand Up @@ -3684,6 +3684,17 @@
"fieldFlag": "query-frontend.log-queries-longer-than",
"fieldType": "duration"
},
{
"kind": "field",
"name": "log_query_request_headers",
"required": false,
"desc": "Comma-separated list of request header names to include in query logs. Applies to both query stats and slow queries logs.",
"fieldValue": null,
"fieldDefaultValue": "",
"fieldFlag": "query-frontend.log-query-request-headers",
"fieldType": "string",
"fieldCategory": "advanced"
},
{
"kind": "field",
"name": "max_body_size",
Expand Down
2 changes: 2 additions & 0 deletions cmd/mimir/help-all.txt.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,8 @@ Usage of ./cmd/mimir/mimir:
Port to advertise to querier (via scheduler) (defaults to server.grpc-listen-port).
-query-frontend.log-queries-longer-than duration
Log queries that are slower than the specified duration. Set to 0 to disable. Set to < 0 to enable on all queries.
-query-frontend.log-query-request-headers comma-separated-list-of-strings
Comma-separated list of request header names to include in query logs. Applies to both query stats and slow queries logs.
-query-frontend.max-body-size int
Max body size for downstream prometheus. (default 10485760)
-query-frontend.max-cache-freshness duration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,11 @@ The `frontend` block configures the query-frontend.
# CLI flag: -query-frontend.log-queries-longer-than
[log_queries_longer_than: <duration> | default = 0s]
# (advanced) Comma-separated list of request header names to include in query
# logs. Applies to both query stats and slow queries logs.
# CLI flag: -query-frontend.log-query-request-headers
[log_query_request_headers: <string> | default = ""]
# (advanced) Max body size for downstream prometheus.
# CLI flag: -query-frontend.max-body-size
[max_body_size: <int> | default = 10485760]
Expand Down
26 changes: 23 additions & 3 deletions pkg/frontend/transport/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/weaveworks/common/httpgrpc"
"github.com/weaveworks/common/httpgrpc/server"

"github.com/grafana/dskit/flagext"
"github.com/grafana/dskit/tenant"

apierror "github.com/grafana/mimir/pkg/api/error"
Expand All @@ -49,13 +50,15 @@ var (

// Config for a Handler.
type HandlerConfig struct {
LogQueriesLongerThan time.Duration `yaml:"log_queries_longer_than"`
MaxBodySize int64 `yaml:"max_body_size" category:"advanced"`
QueryStatsEnabled bool `yaml:"query_stats_enabled" category:"advanced"`
LogQueriesLongerThan time.Duration `yaml:"log_queries_longer_than"`
LogQueryRequestHeaders flagext.StringSliceCSV `yaml:"log_query_request_headers" category:"advanced"`
MaxBodySize int64 `yaml:"max_body_size" category:"advanced"`
QueryStatsEnabled bool `yaml:"query_stats_enabled" category:"advanced"`
}

func (cfg *HandlerConfig) RegisterFlags(f *flag.FlagSet) {
f.DurationVar(&cfg.LogQueriesLongerThan, "query-frontend.log-queries-longer-than", 0, "Log queries that are slower than the specified duration. Set to 0 to disable. Set to < 0 to enable on all queries.")
f.Var(&cfg.LogQueryRequestHeaders, "query-frontend.log-query-request-headers", "Comma-separated list of request header names to include in query logs. Applies to both query stats and slow queries logs.")
f.Int64Var(&cfg.MaxBodySize, "query-frontend.max-body-size", 10*1024*1024, "Max body size for downstream prometheus.")
f.BoolVar(&cfg.QueryStatsEnabled, "query-frontend.query-stats-enabled", true, "False to disable query statistics tracking. When enabled, a message with some statistics is logged for every query.")
}
Expand Down Expand Up @@ -240,6 +243,10 @@ func (f *Handler) reportSlowQuery(r *http.Request, queryString url.Values, query
"time_taken", queryResponseTime.String(),
}, formatQueryString(queryString)...)

if len(f.cfg.LogQueryRequestHeaders) != 0 {
logMessage = append(logMessage, formatRequestHeaders(&r.Header, f.cfg.LogQueryRequestHeaders)...)
}

level.Info(util_log.WithContext(r.Context(), f.log)).Log(logMessage...)
}

Expand Down Expand Up @@ -284,6 +291,10 @@ func (f *Handler) reportQueryStats(r *http.Request, queryString url.Values, quer
"estimated_series_count", stats.GetEstimatedSeriesCount(),
}, formatQueryString(queryString)...)

if len(f.cfg.LogQueryRequestHeaders) != 0 {
logMessage = append(logMessage, formatRequestHeaders(&r.Header, f.cfg.LogQueryRequestHeaders)...)
}

if queryErr != nil {
logStatus := "failed"
if errors.Is(queryErr, context.Canceled) {
Expand All @@ -310,6 +321,15 @@ func formatQueryString(queryString url.Values) (fields []interface{}) {
return fields
}

func formatRequestHeaders(h *http.Header, headersToLog []string) (fields []interface{}) {
for _, s := range headersToLog {
if v := h.Get(s); v != "" {
fields = append(fields, fmt.Sprintf("header_%s", strings.ReplaceAll(strings.ToLower(s), "-", "_")), v)
}
}
return fields
}

func writeError(w http.ResponseWriter, err error) {
switch {
case errors.Is(err, context.Canceled):
Expand Down
15 changes: 15 additions & 0 deletions pkg/frontend/transport/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,18 @@ func (t *testLogger) Log(keyvals ...interface{}) error {
t.logMessages = append(t.logMessages, msg)
return nil
}

func TestFormatRequestHeaders(t *testing.T) {
h := http.Header{}
h.Add("X-Header-To-Log", "i should be logged!")
h.Add("X-Header-To-Not-Log", "i shouldn't be logged!")

fields := formatRequestHeaders(&h, []string{"X-Header-To-Log", "X-Header-Not-Present"})

expected := []interface{}{
"header_x_header_to_log",
"i should be logged!",
}

assert.Equal(t, expected, fields)
}

0 comments on commit 6439eea

Please sign in to comment.