mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
I am also using the referrer to derive the information, as it requires some development effort to have the frontend send this information.
165 lines
4.5 KiB
Go
165 lines
4.5 KiB
Go
package querier
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"regexp"
|
|
"runtime/debug"
|
|
|
|
"github.com/SigNoz/signoz/pkg/analytics"
|
|
"github.com/SigNoz/signoz/pkg/errors"
|
|
"github.com/SigNoz/signoz/pkg/factory"
|
|
"github.com/SigNoz/signoz/pkg/http/render"
|
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
|
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
|
"github.com/SigNoz/signoz/pkg/valuer"
|
|
)
|
|
|
|
type API struct {
|
|
set factory.ProviderSettings
|
|
analytics analytics.Analytics
|
|
querier Querier
|
|
}
|
|
|
|
func NewAPI(set factory.ProviderSettings, querier Querier, analytics analytics.Analytics) *API {
|
|
return &API{set: set, querier: querier, analytics: analytics}
|
|
}
|
|
|
|
func (a *API) QueryRange(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
ctx := req.Context()
|
|
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
var queryRangeRequest qbtypes.QueryRangeRequest
|
|
if err := json.NewDecoder(req.Body).Decode(&queryRangeRequest); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
stackTrace := string(debug.Stack())
|
|
|
|
queryJSON, _ := json.Marshal(queryRangeRequest)
|
|
|
|
a.set.Logger.ErrorContext(ctx, "panic in QueryRange",
|
|
"error", r,
|
|
"user", claims.UserID,
|
|
"payload", string(queryJSON),
|
|
"stacktrace", stackTrace,
|
|
)
|
|
|
|
render.Error(rw, errors.NewInternalf(
|
|
errors.CodeInternal,
|
|
"Something went wrong on our end. It's not you, it's us. Our team is notified about it. Reach out to support if issue persists.",
|
|
))
|
|
}
|
|
}()
|
|
|
|
// Validate the query request
|
|
if err := queryRangeRequest.Validate(); err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgID, err := valuer.NewUUID(claims.OrgID)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
queryRangeResponse, err := a.querier.QueryRange(ctx, orgID, &queryRangeRequest)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
a.logEvent(req.Context(), req.Header.Get("Referer"), queryRangeResponse.QBEvent)
|
|
|
|
render.Success(rw, http.StatusOK, queryRangeResponse)
|
|
}
|
|
|
|
func (a *API) logEvent(ctx context.Context, referrer string, event *qbtypes.QBEvent) {
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if !(event.LogsUsed || event.MetricsUsed || event.TracesUsed) {
|
|
return
|
|
}
|
|
|
|
properties := map[string]any{
|
|
"version": event.Version,
|
|
"logs_used": event.LogsUsed,
|
|
"traces_used": event.TracesUsed,
|
|
"metrics_used": event.MetricsUsed,
|
|
"filter_applied": event.FilterApplied,
|
|
"group_by_applied": event.GroupByApplied,
|
|
"query_type": event.QueryType,
|
|
"panel_type": event.PanelType,
|
|
"number_of_queries": event.NumberOfQueries,
|
|
}
|
|
|
|
if referrer == "" {
|
|
return
|
|
}
|
|
|
|
properties["referrer"] = referrer
|
|
|
|
logsExplorerMatched, _ := regexp.MatchString(`/logs/logs-explorer(?:\?.*)?$`, referrer)
|
|
traceExplorerMatched, _ := regexp.MatchString(`/traces-explorer(?:\?.*)?$`, referrer)
|
|
metricsExplorerMatched, _ := regexp.MatchString(`/metrics-explorer/explorer(?:\?.*)?$`, referrer)
|
|
dashboardMatched, _ := regexp.MatchString(`/dashboard/[a-zA-Z0-9\-]+/(new|edit)(?:\?.*)?$`, referrer)
|
|
alertMatched, _ := regexp.MatchString(`/alerts/(new|edit)(?:\?.*)?$`, referrer)
|
|
|
|
switch {
|
|
case dashboardMatched:
|
|
properties["module_name"] = "dashboard"
|
|
case alertMatched:
|
|
properties["module_name"] = "rule"
|
|
case metricsExplorerMatched:
|
|
properties["module_name"] = "metrics-explorer"
|
|
case logsExplorerMatched:
|
|
properties["module_name"] = "logs-explorer"
|
|
case traceExplorerMatched:
|
|
properties["module_name"] = "traces-explorer"
|
|
default:
|
|
return
|
|
}
|
|
|
|
if dashboardMatched {
|
|
if dashboardIDRegex, err := regexp.Compile(`/dashboard/([a-f0-9\-]+)/`); err == nil {
|
|
if matches := dashboardIDRegex.FindStringSubmatch(referrer); len(matches) > 1 {
|
|
properties["dashboard_id"] = matches[1]
|
|
}
|
|
}
|
|
|
|
if widgetIDRegex, err := regexp.Compile(`widgetId=([a-f0-9\-]+)`); err == nil {
|
|
if matches := widgetIDRegex.FindStringSubmatch(referrer); len(matches) > 1 {
|
|
properties["widget_id"] = matches[1]
|
|
}
|
|
}
|
|
}
|
|
|
|
if alertMatched {
|
|
if alertIDRegex, err := regexp.Compile(`ruleId=(\d+)`); err == nil {
|
|
if matches := alertIDRegex.FindStringSubmatch(referrer); len(matches) > 1 {
|
|
properties["rule_id"] = matches[1]
|
|
}
|
|
}
|
|
}
|
|
|
|
if !event.HasData {
|
|
a.analytics.TrackUser(ctx, claims.OrgID, claims.UserID, "Telemetry Query Returned Empty", properties)
|
|
return
|
|
}
|
|
a.analytics.TrackUser(ctx, claims.OrgID, claims.UserID, "Telemetry Query Returned Results", properties)
|
|
}
|