@@ -36,6 +36,8 @@ type UserInfo struct {
3636 Tenant string
3737 ForwardedFor string
3838 UserAgent string
39+ Groups string
40+ Email string
3941}
4042
4143// ResponseStats holds statistics extracted from query response.
@@ -91,6 +93,10 @@ func ExtractUserInfoFromHeaders(headers []*RequestHeader) UserInfo {
9193 if userInfo .Source == "" {
9294 userInfo .Source = headerValue
9395 }
96+ case "x-auth-request-groups" :
97+ userInfo .Groups = headerValue
98+ case "x-auth-request-email" :
99+ userInfo .Email = headerValue
94100 }
95101 }
96102
@@ -102,29 +108,6 @@ func ExtractUserInfoFromHeaders(headers []*RequestHeader) UserInfo {
102108 return userInfo
103109}
104110
105- // ExtractEmailFromResponse extracts the email from response headers (works for both range and instant queries).
106- func ExtractEmailFromResponse (resp queryrange.Response ) string {
107- if resp == nil {
108- return ""
109- }
110-
111- // Check both response types using OR condition
112- var headers []* queryrange.PrometheusResponseHeader
113- if promResp , ok := resp .(* queryrange.PrometheusResponse ); ok {
114- headers = promResp .GetHeaders ()
115- } else if promResp , ok := resp .(* queryrange.PrometheusInstantQueryResponse ); ok {
116- headers = promResp .GetHeaders ()
117- }
118-
119- for _ , header := range headers {
120- if strings .ToLower (header .Name ) == "x-auth-request-email" && len (header .Values ) > 0 {
121- return header .Values [0 ]
122- }
123- }
124-
125- return ""
126- }
127-
128111// ConvertStoreMatchers converts internal store matchers to logging format.
129112func ConvertStoreMatchers (storeMatchers [][]* labels.Matcher ) []StoreMatcherSet {
130113 if len (storeMatchers ) == 0 {
@@ -174,6 +157,59 @@ func GetResponseStats(resp queryrange.Response) ResponseStats {
174157 return stats
175158}
176159
160+ // ExtractMetricNames extracts all unique __name__ labels from query response (works for both range and instant queries).
161+ func ExtractMetricNames (resp queryrange.Response ) []string {
162+ if resp == nil {
163+ return nil
164+ }
165+
166+ metricNamesMap := make (map [string ]struct {})
167+
168+ // Handle range query response (resultType: matrix)
169+ if r , ok := resp .(* queryrange.PrometheusResponse ); ok {
170+ for _ , stream := range r .Data .Result {
171+ for _ , label := range stream .Labels {
172+ if label .Name == "__name__" {
173+ metricNamesMap [label .Value ] = struct {}{}
174+ break
175+ }
176+ }
177+ }
178+ } else if r , ok := resp .(* queryrange.PrometheusInstantQueryResponse ); ok {
179+ // Handle instant query response - check all result types
180+ if vector := r .Data .Result .GetVector (); vector != nil {
181+ // resultType: vector
182+ for _ , sample := range vector .Samples {
183+ for _ , label := range sample .Labels {
184+ if label .Name == "__name__" {
185+ metricNamesMap [label .Value ] = struct {}{}
186+ break
187+ }
188+ }
189+ }
190+ } else if matrix := r .Data .Result .GetMatrix (); matrix != nil {
191+ // resultType: matrix (subqueries in instant queries)
192+ for _ , stream := range matrix .SampleStreams {
193+ for _ , label := range stream .Labels {
194+ if label .Name == "__name__" {
195+ metricNamesMap [label .Value ] = struct {}{}
196+ break
197+ }
198+ }
199+ }
200+ }
201+ // Scalar and StringSample don't have __name__ labels
202+ }
203+
204+ // Convert map to slice
205+ metricNames := make ([]string , 0 , len (metricNamesMap ))
206+ for name := range metricNamesMap {
207+ metricNames = append (metricNames , name )
208+ }
209+
210+ return metricNames
211+ }
212+
177213// WriteJSONLogToFile writes query logs to file in JSON format.
178214func WriteJSONLogToFile (logger log.Logger , writer interface {}, queryLog interface {}, queryType string ) error {
179215 if writer == nil {
0 commit comments