mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-05 05:11:59 +00:00
Compare commits
6 Commits
issue_4071
...
rule-state
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10135d86f8 | ||
|
|
21bab3ba72 | ||
|
|
099f451cf7 | ||
|
|
9da95b231d | ||
|
|
4df40a81f2 | ||
|
|
73a76b8974 |
@@ -1763,6 +1763,140 @@ components:
|
||||
- type
|
||||
- orgId
|
||||
type: object
|
||||
RulestatehistorytypesAlertState:
|
||||
enum:
|
||||
- inactive
|
||||
- pending
|
||||
- recovering
|
||||
- firing
|
||||
- nodata
|
||||
- disabled
|
||||
type: string
|
||||
RulestatehistorytypesRuleStateHistoryContributorResponse:
|
||||
properties:
|
||||
count:
|
||||
minimum: 0
|
||||
type: integer
|
||||
fingerprint:
|
||||
minimum: 0
|
||||
type: integer
|
||||
labels:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Label'
|
||||
nullable: true
|
||||
type: array
|
||||
relatedLogsLink:
|
||||
type: string
|
||||
relatedTracesLink:
|
||||
type: string
|
||||
required:
|
||||
- fingerprint
|
||||
- labels
|
||||
- count
|
||||
type: object
|
||||
RulestatehistorytypesRuleStateHistoryResponseItem:
|
||||
properties:
|
||||
fingerprint:
|
||||
minimum: 0
|
||||
type: integer
|
||||
labels:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Label'
|
||||
nullable: true
|
||||
type: array
|
||||
overallState:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesAlertState'
|
||||
overallStateChanged:
|
||||
type: boolean
|
||||
ruleID:
|
||||
type: string
|
||||
ruleName:
|
||||
type: string
|
||||
state:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesAlertState'
|
||||
stateChanged:
|
||||
type: boolean
|
||||
unixMilli:
|
||||
format: int64
|
||||
type: integer
|
||||
value:
|
||||
format: double
|
||||
type: number
|
||||
required:
|
||||
- ruleID
|
||||
- ruleName
|
||||
- overallState
|
||||
- overallStateChanged
|
||||
- state
|
||||
- stateChanged
|
||||
- unixMilli
|
||||
- labels
|
||||
- fingerprint
|
||||
- value
|
||||
type: object
|
||||
RulestatehistorytypesRuleStateTimelineResponse:
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesRuleStateHistoryResponseItem'
|
||||
nullable: true
|
||||
type: array
|
||||
nextCursor:
|
||||
type: string
|
||||
total:
|
||||
minimum: 0
|
||||
type: integer
|
||||
required:
|
||||
- items
|
||||
- total
|
||||
type: object
|
||||
RulestatehistorytypesRuleStateWindow:
|
||||
properties:
|
||||
end:
|
||||
format: int64
|
||||
type: integer
|
||||
start:
|
||||
format: int64
|
||||
type: integer
|
||||
state:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesAlertState'
|
||||
required:
|
||||
- state
|
||||
- start
|
||||
- end
|
||||
type: object
|
||||
RulestatehistorytypesStats:
|
||||
properties:
|
||||
currentAvgResolutionTime:
|
||||
format: double
|
||||
type: number
|
||||
currentAvgResolutionTimeSeries:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5TimeSeries'
|
||||
currentTriggersSeries:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5TimeSeries'
|
||||
pastAvgResolutionTime:
|
||||
format: double
|
||||
type: number
|
||||
pastAvgResolutionTimeSeries:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5TimeSeries'
|
||||
pastTriggersSeries:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5TimeSeries'
|
||||
totalCurrentTriggers:
|
||||
minimum: 0
|
||||
type: integer
|
||||
totalPastTriggers:
|
||||
minimum: 0
|
||||
type: integer
|
||||
required:
|
||||
- totalCurrentTriggers
|
||||
- totalPastTriggers
|
||||
- currentTriggersSeries
|
||||
- pastTriggersSeries
|
||||
- currentAvgResolutionTime
|
||||
- pastAvgResolutionTime
|
||||
- currentAvgResolutionTimeSeries
|
||||
- pastAvgResolutionTimeSeries
|
||||
type: object
|
||||
ServiceaccounttypesFactorAPIKey:
|
||||
properties:
|
||||
createdAt:
|
||||
@@ -6818,6 +6952,518 @@ paths:
|
||||
summary: Update my organization
|
||||
tags:
|
||||
- orgs
|
||||
/api/v2/rules/{id}/history/filter_keys:
|
||||
get:
|
||||
deprecated: false
|
||||
description: Returns distinct label keys from rule history entries for the selected
|
||||
range.
|
||||
operationId: GetRuleHistoryFilterKeys
|
||||
parameters:
|
||||
- in: query
|
||||
name: signal
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesSignal'
|
||||
- in: query
|
||||
name: source
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesSource'
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: startUnixMilli
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: endUnixMilli
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: fieldContext
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldContext'
|
||||
- in: query
|
||||
name: fieldDataType
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
|
||||
- in: query
|
||||
name: metricName
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: searchText
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TelemetrytypesGettableFieldKeys'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: Get rule history filter keys
|
||||
tags:
|
||||
- rules
|
||||
/api/v2/rules/{id}/history/filter_values:
|
||||
get:
|
||||
deprecated: false
|
||||
description: Returns distinct label values for a given key from rule history
|
||||
entries.
|
||||
operationId: GetRuleHistoryFilterValues
|
||||
parameters:
|
||||
- in: query
|
||||
name: signal
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesSignal'
|
||||
- in: query
|
||||
name: source
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesSource'
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
type: integer
|
||||
- in: query
|
||||
name: startUnixMilli
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: endUnixMilli
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: fieldContext
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldContext'
|
||||
- in: query
|
||||
name: fieldDataType
|
||||
schema:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
|
||||
- in: query
|
||||
name: metricName
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: searchText
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: existingQuery
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TelemetrytypesGettableFieldValues'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: Get rule history filter values
|
||||
tags:
|
||||
- rules
|
||||
/api/v2/rules/{id}/history/overall_status:
|
||||
get:
|
||||
deprecated: false
|
||||
description: Returns overall firing/inactive intervals for a rule in the selected
|
||||
time range.
|
||||
operationId: GetRuleHistoryOverallStatus
|
||||
parameters:
|
||||
- in: query
|
||||
name: start
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: end
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesRuleStateWindow'
|
||||
nullable: true
|
||||
type: array
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: Get rule overall status timeline
|
||||
tags:
|
||||
- rules
|
||||
/api/v2/rules/{id}/history/stats:
|
||||
get:
|
||||
deprecated: false
|
||||
description: Returns trigger and resolution statistics for a rule in the selected
|
||||
time range.
|
||||
operationId: GetRuleHistoryStats
|
||||
parameters:
|
||||
- in: query
|
||||
name: start
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: end
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesStats'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: Get rule history stats
|
||||
tags:
|
||||
- rules
|
||||
/api/v2/rules/{id}/history/timeline:
|
||||
get:
|
||||
deprecated: false
|
||||
description: Returns paginated timeline entries for rule state transitions.
|
||||
operationId: GetRuleHistoryTimeline
|
||||
parameters:
|
||||
- in: query
|
||||
name: start
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: end
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: state
|
||||
schema:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesAlertState'
|
||||
- in: query
|
||||
name: filterExpression
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: order
|
||||
schema:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5OrderDirection'
|
||||
- in: query
|
||||
name: cursor
|
||||
schema:
|
||||
type: string
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesRuleStateTimelineResponse'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: Get rule history timeline
|
||||
tags:
|
||||
- rules
|
||||
/api/v2/rules/{id}/history/top_contributors:
|
||||
get:
|
||||
deprecated: false
|
||||
description: Returns top label combinations contributing to rule firing in the
|
||||
selected time range.
|
||||
operationId: GetRuleHistoryTopContributors
|
||||
parameters:
|
||||
- in: query
|
||||
name: start
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: end
|
||||
required: true
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: path
|
||||
name: id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
$ref: '#/components/schemas/RulestatehistorytypesRuleStateHistoryContributorResponse'
|
||||
nullable: true
|
||||
type: array
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: Get top contributors to rule firing
|
||||
tags:
|
||||
- rules
|
||||
/api/v2/sessions:
|
||||
delete:
|
||||
deprecated: false
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/cache"
|
||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/signoz"
|
||||
@@ -102,6 +103,7 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
||||
signoz.TelemetryMetadataStore,
|
||||
signoz.Prometheus,
|
||||
signoz.Modules.OrgGetter,
|
||||
signoz.Modules.RuleStateHistory,
|
||||
signoz.Querier,
|
||||
signoz.Instrumentation.ToProviderSettings(),
|
||||
signoz.QueryParser,
|
||||
@@ -349,29 +351,30 @@ func (s *Server) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeRulesManager(ch baseint.Reader, cache cache.Cache, alertmanager alertmanager.Alertmanager, sqlstore sqlstore.SQLStore, telemetryStore telemetrystore.TelemetryStore, metadataStore telemetrytypes.MetadataStore, prometheus prometheus.Prometheus, orgGetter organization.Getter, querier querier.Querier, providerSettings factory.ProviderSettings, queryParser queryparser.QueryParser) (*baserules.Manager, error) {
|
||||
func makeRulesManager(ch baseint.Reader, cache cache.Cache, alertmanager alertmanager.Alertmanager, sqlstore sqlstore.SQLStore, telemetryStore telemetrystore.TelemetryStore, metadataStore telemetrytypes.MetadataStore, prometheus prometheus.Prometheus, orgGetter organization.Getter, ruleStateHistoryModule rulestatehistory.Module, querier querier.Querier, providerSettings factory.ProviderSettings, queryParser queryparser.QueryParser) (*baserules.Manager, error) {
|
||||
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
|
||||
maintenanceStore := sqlrulestore.NewMaintenanceStore(sqlstore)
|
||||
// create manager opts
|
||||
managerOpts := &baserules.ManagerOptions{
|
||||
TelemetryStore: telemetryStore,
|
||||
MetadataStore: metadataStore,
|
||||
Prometheus: prometheus,
|
||||
Context: context.Background(),
|
||||
Logger: zap.L(),
|
||||
Reader: ch,
|
||||
Querier: querier,
|
||||
SLogger: providerSettings.Logger,
|
||||
Cache: cache,
|
||||
EvalDelay: baseconst.GetEvalDelay(),
|
||||
PrepareTaskFunc: rules.PrepareTaskFunc,
|
||||
PrepareTestRuleFunc: rules.TestNotification,
|
||||
Alertmanager: alertmanager,
|
||||
OrgGetter: orgGetter,
|
||||
RuleStore: ruleStore,
|
||||
MaintenanceStore: maintenanceStore,
|
||||
SqlStore: sqlstore,
|
||||
QueryParser: queryParser,
|
||||
TelemetryStore: telemetryStore,
|
||||
MetadataStore: metadataStore,
|
||||
Prometheus: prometheus,
|
||||
Context: context.Background(),
|
||||
Logger: zap.L(),
|
||||
Reader: ch,
|
||||
Querier: querier,
|
||||
SLogger: providerSettings.Logger,
|
||||
Cache: cache,
|
||||
EvalDelay: baseconst.GetEvalDelay(),
|
||||
PrepareTaskFunc: rules.PrepareTaskFunc,
|
||||
PrepareTestRuleFunc: rules.TestNotification,
|
||||
Alertmanager: alertmanager,
|
||||
OrgGetter: orgGetter,
|
||||
RuleStore: ruleStore,
|
||||
MaintenanceStore: maintenanceStore,
|
||||
SqlStore: sqlstore,
|
||||
QueryParser: queryParser,
|
||||
RuleStateHistoryModule: ruleStateHistoryModule,
|
||||
}
|
||||
|
||||
// create Manager
|
||||
|
||||
@@ -26,6 +26,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
||||
if err != nil {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "evaluation is invalid: %v", err)
|
||||
}
|
||||
|
||||
if opts.Rule.RuleType == ruletypes.RuleTypeThreshold {
|
||||
// create a threshold rule
|
||||
tr, err := baserules.NewThresholdRule(
|
||||
@@ -39,6 +40,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
||||
baserules.WithSQLStore(opts.SQLStore),
|
||||
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -63,6 +65,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
||||
baserules.WithSQLStore(opts.SQLStore),
|
||||
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -88,6 +91,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
|
||||
baserules.WithSQLStore(opts.SQLStore),
|
||||
baserules.WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||
baserules.WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||
baserules.WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
||||
)
|
||||
if err != nil {
|
||||
return task, err
|
||||
|
||||
744
frontend/src/api/generated/services/rules/index.ts
Normal file
744
frontend/src/api/generated/services/rules/index.ts
Normal file
@@ -0,0 +1,744 @@
|
||||
/**
|
||||
* ! Do not edit manually
|
||||
* * The file has been auto-generated using Orval for SigNoz
|
||||
* * regenerate with 'yarn generate:api'
|
||||
* SigNoz
|
||||
*/
|
||||
import type {
|
||||
InvalidateOptions,
|
||||
QueryClient,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from 'react-query';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import type { ErrorType } from '../../../generatedAPIInstance';
|
||||
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type {
|
||||
GetRuleHistoryFilterKeys200,
|
||||
GetRuleHistoryFilterKeysParams,
|
||||
GetRuleHistoryFilterKeysPathParameters,
|
||||
GetRuleHistoryFilterValues200,
|
||||
GetRuleHistoryFilterValuesParams,
|
||||
GetRuleHistoryFilterValuesPathParameters,
|
||||
GetRuleHistoryOverallStatus200,
|
||||
GetRuleHistoryOverallStatusParams,
|
||||
GetRuleHistoryOverallStatusPathParameters,
|
||||
GetRuleHistoryStats200,
|
||||
GetRuleHistoryStatsParams,
|
||||
GetRuleHistoryStatsPathParameters,
|
||||
GetRuleHistoryTimeline200,
|
||||
GetRuleHistoryTimelineParams,
|
||||
GetRuleHistoryTimelinePathParameters,
|
||||
GetRuleHistoryTopContributors200,
|
||||
GetRuleHistoryTopContributorsParams,
|
||||
GetRuleHistoryTopContributorsPathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
} from '../sigNoz.schemas';
|
||||
|
||||
/**
|
||||
* Returns distinct label keys from rule history entries for the selected range.
|
||||
* @summary Get rule history filter keys
|
||||
*/
|
||||
export const getRuleHistoryFilterKeys = (
|
||||
{ id }: GetRuleHistoryFilterKeysPathParameters,
|
||||
params?: GetRuleHistoryFilterKeysParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetRuleHistoryFilterKeys200>({
|
||||
url: `/api/v2/rules/${id}/history/filter_keys`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryFilterKeysQueryKey = (
|
||||
{ id }: GetRuleHistoryFilterKeysPathParameters,
|
||||
params?: GetRuleHistoryFilterKeysParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v2/rules/${id}/history/filter_keys`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryFilterKeysQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryFilterKeysPathParameters,
|
||||
params?: GetRuleHistoryFilterKeysParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetRuleHistoryFilterKeysQueryKey({ id }, params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>
|
||||
> = ({ signal }) => getRuleHistoryFilterKeys({ id }, params, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetRuleHistoryFilterKeysQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>
|
||||
>;
|
||||
export type GetRuleHistoryFilterKeysQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get rule history filter keys
|
||||
*/
|
||||
|
||||
export function useGetRuleHistoryFilterKeys<
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryFilterKeysPathParameters,
|
||||
params?: GetRuleHistoryFilterKeysParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterKeys>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetRuleHistoryFilterKeysQueryOptions(
|
||||
{ id },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get rule history filter keys
|
||||
*/
|
||||
export const invalidateGetRuleHistoryFilterKeys = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetRuleHistoryFilterKeysPathParameters,
|
||||
params?: GetRuleHistoryFilterKeysParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetRuleHistoryFilterKeysQueryKey({ id }, params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns distinct label values for a given key from rule history entries.
|
||||
* @summary Get rule history filter values
|
||||
*/
|
||||
export const getRuleHistoryFilterValues = (
|
||||
{ id }: GetRuleHistoryFilterValuesPathParameters,
|
||||
params?: GetRuleHistoryFilterValuesParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetRuleHistoryFilterValues200>({
|
||||
url: `/api/v2/rules/${id}/history/filter_values`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryFilterValuesQueryKey = (
|
||||
{ id }: GetRuleHistoryFilterValuesPathParameters,
|
||||
params?: GetRuleHistoryFilterValuesParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v2/rules/${id}/history/filter_values`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryFilterValuesQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryFilterValuesPathParameters,
|
||||
params?: GetRuleHistoryFilterValuesParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getGetRuleHistoryFilterValuesQueryKey({ id }, params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>
|
||||
> = ({ signal }) => getRuleHistoryFilterValues({ id }, params, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetRuleHistoryFilterValuesQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>
|
||||
>;
|
||||
export type GetRuleHistoryFilterValuesQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get rule history filter values
|
||||
*/
|
||||
|
||||
export function useGetRuleHistoryFilterValues<
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryFilterValuesPathParameters,
|
||||
params?: GetRuleHistoryFilterValuesParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryFilterValues>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetRuleHistoryFilterValuesQueryOptions(
|
||||
{ id },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get rule history filter values
|
||||
*/
|
||||
export const invalidateGetRuleHistoryFilterValues = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetRuleHistoryFilterValuesPathParameters,
|
||||
params?: GetRuleHistoryFilterValuesParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetRuleHistoryFilterValuesQueryKey({ id }, params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns overall firing/inactive intervals for a rule in the selected time range.
|
||||
* @summary Get rule overall status timeline
|
||||
*/
|
||||
export const getRuleHistoryOverallStatus = (
|
||||
{ id }: GetRuleHistoryOverallStatusPathParameters,
|
||||
params: GetRuleHistoryOverallStatusParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetRuleHistoryOverallStatus200>({
|
||||
url: `/api/v2/rules/${id}/history/overall_status`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryOverallStatusQueryKey = (
|
||||
{ id }: GetRuleHistoryOverallStatusPathParameters,
|
||||
params?: GetRuleHistoryOverallStatusParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v2/rules/${id}/history/overall_status`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryOverallStatusQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryOverallStatusPathParameters,
|
||||
params: GetRuleHistoryOverallStatusParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getGetRuleHistoryOverallStatusQueryKey({ id }, params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>
|
||||
> = ({ signal }) => getRuleHistoryOverallStatus({ id }, params, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetRuleHistoryOverallStatusQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>
|
||||
>;
|
||||
export type GetRuleHistoryOverallStatusQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get rule overall status timeline
|
||||
*/
|
||||
|
||||
export function useGetRuleHistoryOverallStatus<
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryOverallStatusPathParameters,
|
||||
params: GetRuleHistoryOverallStatusParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryOverallStatus>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetRuleHistoryOverallStatusQueryOptions(
|
||||
{ id },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get rule overall status timeline
|
||||
*/
|
||||
export const invalidateGetRuleHistoryOverallStatus = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetRuleHistoryOverallStatusPathParameters,
|
||||
params: GetRuleHistoryOverallStatusParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetRuleHistoryOverallStatusQueryKey({ id }, params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns trigger and resolution statistics for a rule in the selected time range.
|
||||
* @summary Get rule history stats
|
||||
*/
|
||||
export const getRuleHistoryStats = (
|
||||
{ id }: GetRuleHistoryStatsPathParameters,
|
||||
params: GetRuleHistoryStatsParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetRuleHistoryStats200>({
|
||||
url: `/api/v2/rules/${id}/history/stats`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryStatsQueryKey = (
|
||||
{ id }: GetRuleHistoryStatsPathParameters,
|
||||
params?: GetRuleHistoryStatsParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v2/rules/${id}/history/stats`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryStatsQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryStatsPathParameters,
|
||||
params: GetRuleHistoryStatsParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetRuleHistoryStatsQueryKey({ id }, params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getRuleHistoryStats>>
|
||||
> = ({ signal }) => getRuleHistoryStats({ id }, params, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetRuleHistoryStatsQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getRuleHistoryStats>>
|
||||
>;
|
||||
export type GetRuleHistoryStatsQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get rule history stats
|
||||
*/
|
||||
|
||||
export function useGetRuleHistoryStats<
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryStatsPathParameters,
|
||||
params: GetRuleHistoryStatsParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryStats>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetRuleHistoryStatsQueryOptions(
|
||||
{ id },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get rule history stats
|
||||
*/
|
||||
export const invalidateGetRuleHistoryStats = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetRuleHistoryStatsPathParameters,
|
||||
params: GetRuleHistoryStatsParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetRuleHistoryStatsQueryKey({ id }, params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns paginated timeline entries for rule state transitions.
|
||||
* @summary Get rule history timeline
|
||||
*/
|
||||
export const getRuleHistoryTimeline = (
|
||||
{ id }: GetRuleHistoryTimelinePathParameters,
|
||||
params: GetRuleHistoryTimelineParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetRuleHistoryTimeline200>({
|
||||
url: `/api/v2/rules/${id}/history/timeline`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryTimelineQueryKey = (
|
||||
{ id }: GetRuleHistoryTimelinePathParameters,
|
||||
params?: GetRuleHistoryTimelineParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v2/rules/${id}/history/timeline`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryTimelineQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryTimelinePathParameters,
|
||||
params: GetRuleHistoryTimelineParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetRuleHistoryTimelineQueryKey({ id }, params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>
|
||||
> = ({ signal }) => getRuleHistoryTimeline({ id }, params, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetRuleHistoryTimelineQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>
|
||||
>;
|
||||
export type GetRuleHistoryTimelineQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get rule history timeline
|
||||
*/
|
||||
|
||||
export function useGetRuleHistoryTimeline<
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryTimelinePathParameters,
|
||||
params: GetRuleHistoryTimelineParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTimeline>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetRuleHistoryTimelineQueryOptions(
|
||||
{ id },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get rule history timeline
|
||||
*/
|
||||
export const invalidateGetRuleHistoryTimeline = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetRuleHistoryTimelinePathParameters,
|
||||
params: GetRuleHistoryTimelineParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetRuleHistoryTimelineQueryKey({ id }, params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns top label combinations contributing to rule firing in the selected time range.
|
||||
* @summary Get top contributors to rule firing
|
||||
*/
|
||||
export const getRuleHistoryTopContributors = (
|
||||
{ id }: GetRuleHistoryTopContributorsPathParameters,
|
||||
params: GetRuleHistoryTopContributorsParams,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetRuleHistoryTopContributors200>({
|
||||
url: `/api/v2/rules/${id}/history/top_contributors`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryTopContributorsQueryKey = (
|
||||
{ id }: GetRuleHistoryTopContributorsPathParameters,
|
||||
params?: GetRuleHistoryTopContributorsParams,
|
||||
) => {
|
||||
return [
|
||||
`/api/v2/rules/${id}/history/top_contributors`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetRuleHistoryTopContributorsQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryTopContributorsPathParameters,
|
||||
params: GetRuleHistoryTopContributorsParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ??
|
||||
getGetRuleHistoryTopContributorsQueryKey({ id }, params);
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>
|
||||
> = ({ signal }) => getRuleHistoryTopContributors({ id }, params, signal);
|
||||
|
||||
return {
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: !!id,
|
||||
...queryOptions,
|
||||
} as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetRuleHistoryTopContributorsQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>
|
||||
>;
|
||||
export type GetRuleHistoryTopContributorsQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get top contributors to rule firing
|
||||
*/
|
||||
|
||||
export function useGetRuleHistoryTopContributors<
|
||||
TData = Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>
|
||||
>(
|
||||
{ id }: GetRuleHistoryTopContributorsPathParameters,
|
||||
params: GetRuleHistoryTopContributorsParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getRuleHistoryTopContributors>>,
|
||||
TError,
|
||||
TData
|
||||
>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetRuleHistoryTopContributorsQueryOptions(
|
||||
{ id },
|
||||
params,
|
||||
options,
|
||||
);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
query.queryKey = queryOptions.queryKey;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Get top contributors to rule firing
|
||||
*/
|
||||
export const invalidateGetRuleHistoryTopContributors = async (
|
||||
queryClient: QueryClient,
|
||||
{ id }: GetRuleHistoryTopContributorsPathParameters,
|
||||
params: GetRuleHistoryTopContributorsParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetRuleHistoryTopContributorsQueryKey({ id }, params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
@@ -2090,6 +2090,139 @@ export interface RoletypesRoleDTO {
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
export enum RulestatehistorytypesAlertStateDTO {
|
||||
inactive = 'inactive',
|
||||
pending = 'pending',
|
||||
recovering = 'recovering',
|
||||
firing = 'firing',
|
||||
nodata = 'nodata',
|
||||
disabled = 'disabled',
|
||||
}
|
||||
export interface RulestatehistorytypesRuleStateHistoryContributorResponseDTO {
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
count: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
fingerprint: number;
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
labels: Querybuildertypesv5LabelDTO[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
relatedLogsLink?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
relatedTracesLink?: string;
|
||||
}
|
||||
|
||||
export interface RulestatehistorytypesRuleStateHistoryResponseItemDTO {
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
fingerprint: number;
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
labels: Querybuildertypesv5LabelDTO[] | null;
|
||||
overallState: RulestatehistorytypesAlertStateDTO;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
overallStateChanged: boolean;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
ruleID: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
ruleName: string;
|
||||
state: RulestatehistorytypesAlertStateDTO;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
stateChanged: boolean;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
unixMilli: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface RulestatehistorytypesRuleStateTimelineResponseDTO {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
items: RulestatehistorytypesRuleStateHistoryResponseItemDTO[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
nextCursor?: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface RulestatehistorytypesRuleStateWindowDTO {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
end: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
start: number;
|
||||
state: RulestatehistorytypesAlertStateDTO;
|
||||
}
|
||||
|
||||
export interface RulestatehistorytypesStatsDTO {
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
currentAvgResolutionTime: number;
|
||||
currentAvgResolutionTimeSeries: Querybuildertypesv5TimeSeriesDTO;
|
||||
currentTriggersSeries: Querybuildertypesv5TimeSeriesDTO;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
pastAvgResolutionTime: number;
|
||||
pastAvgResolutionTimeSeries: Querybuildertypesv5TimeSeriesDTO;
|
||||
pastTriggersSeries: Querybuildertypesv5TimeSeriesDTO;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
totalCurrentTriggers: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
totalPastTriggers: number;
|
||||
}
|
||||
|
||||
export interface ServiceaccounttypesFactorAPIKeyDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -3563,6 +3696,266 @@ export type GetMyOrganization200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryFilterKeysPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetRuleHistoryFilterKeysParams = {
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
signal?: TelemetrytypesSignalDTO;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
source?: TelemetrytypesSourceDTO;
|
||||
/**
|
||||
* @type integer
|
||||
* @description undefined
|
||||
*/
|
||||
limit?: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
startUnixMilli?: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
endUnixMilli?: number;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
fieldContext?: TelemetrytypesFieldContextDTO;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
metricName?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
searchText?: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryFilterKeys200 = {
|
||||
data: TelemetrytypesGettableFieldKeysDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryFilterValuesPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetRuleHistoryFilterValuesParams = {
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
signal?: TelemetrytypesSignalDTO;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
source?: TelemetrytypesSourceDTO;
|
||||
/**
|
||||
* @type integer
|
||||
* @description undefined
|
||||
*/
|
||||
limit?: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
startUnixMilli?: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
endUnixMilli?: number;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
fieldContext?: TelemetrytypesFieldContextDTO;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
metricName?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
searchText?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
existingQuery?: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryFilterValues200 = {
|
||||
data: TelemetrytypesGettableFieldValuesDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryOverallStatusPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetRuleHistoryOverallStatusParams = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
start: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
end: number;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryOverallStatus200 = {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
data: RulestatehistorytypesRuleStateWindowDTO[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryStatsPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetRuleHistoryStatsParams = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
start: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
end: number;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryStats200 = {
|
||||
data: RulestatehistorytypesStatsDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryTimelinePathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetRuleHistoryTimelineParams = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
start: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
end: number;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
state?: RulestatehistorytypesAlertStateDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
filterExpression?: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
limit?: number;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
order?: Querybuildertypesv5OrderDirectionDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
cursor?: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryTimeline200 = {
|
||||
data: RulestatehistorytypesRuleStateTimelineResponseDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryTopContributorsPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetRuleHistoryTopContributorsParams = {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
start: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
* @description undefined
|
||||
*/
|
||||
end: number;
|
||||
};
|
||||
|
||||
export type GetRuleHistoryTopContributors200 = {
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
data: RulestatehistorytypesRuleStateHistoryContributorResponseDTO[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetSessionContext200 = {
|
||||
data: AuthtypesSessionContextDTO;
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||
@@ -29,27 +30,28 @@ import (
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
config apiserver.Config
|
||||
settings factory.ScopedProviderSettings
|
||||
router *mux.Router
|
||||
authZ *middleware.AuthZ
|
||||
orgHandler organization.Handler
|
||||
userHandler user.Handler
|
||||
sessionHandler session.Handler
|
||||
authDomainHandler authdomain.Handler
|
||||
preferenceHandler preference.Handler
|
||||
globalHandler global.Handler
|
||||
promoteHandler promote.Handler
|
||||
flaggerHandler flagger.Handler
|
||||
dashboardModule dashboard.Module
|
||||
dashboardHandler dashboard.Handler
|
||||
metricsExplorerHandler metricsexplorer.Handler
|
||||
gatewayHandler gateway.Handler
|
||||
fieldsHandler fields.Handler
|
||||
authzHandler authz.Handler
|
||||
zeusHandler zeus.Handler
|
||||
querierHandler querier.Handler
|
||||
serviceAccountHandler serviceaccount.Handler
|
||||
config apiserver.Config
|
||||
settings factory.ScopedProviderSettings
|
||||
router *mux.Router
|
||||
authZ *middleware.AuthZ
|
||||
orgHandler organization.Handler
|
||||
userHandler user.Handler
|
||||
sessionHandler session.Handler
|
||||
authDomainHandler authdomain.Handler
|
||||
preferenceHandler preference.Handler
|
||||
globalHandler global.Handler
|
||||
promoteHandler promote.Handler
|
||||
flaggerHandler flagger.Handler
|
||||
dashboardModule dashboard.Module
|
||||
dashboardHandler dashboard.Handler
|
||||
metricsExplorerHandler metricsexplorer.Handler
|
||||
gatewayHandler gateway.Handler
|
||||
fieldsHandler fields.Handler
|
||||
authzHandler authz.Handler
|
||||
zeusHandler zeus.Handler
|
||||
querierHandler querier.Handler
|
||||
serviceAccountHandler serviceaccount.Handler
|
||||
ruleStateHistoryHandler rulestatehistory.Handler
|
||||
}
|
||||
|
||||
func NewFactory(
|
||||
@@ -72,6 +74,7 @@ func NewFactory(
|
||||
zeusHandler zeus.Handler,
|
||||
querierHandler querier.Handler,
|
||||
serviceAccountHandler serviceaccount.Handler,
|
||||
ruleStateHistoryHandler rulestatehistory.Handler,
|
||||
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
|
||||
return newProvider(
|
||||
@@ -97,6 +100,7 @@ func NewFactory(
|
||||
zeusHandler,
|
||||
querierHandler,
|
||||
serviceAccountHandler,
|
||||
ruleStateHistoryHandler,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -124,31 +128,33 @@ func newProvider(
|
||||
zeusHandler zeus.Handler,
|
||||
querierHandler querier.Handler,
|
||||
serviceAccountHandler serviceaccount.Handler,
|
||||
ruleStateHistoryHandler rulestatehistory.Handler,
|
||||
) (apiserver.APIServer, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
|
||||
router := mux.NewRouter().UseEncodedPath()
|
||||
|
||||
provider := &provider{
|
||||
config: config,
|
||||
settings: settings,
|
||||
router: router,
|
||||
orgHandler: orgHandler,
|
||||
userHandler: userHandler,
|
||||
sessionHandler: sessionHandler,
|
||||
authDomainHandler: authDomainHandler,
|
||||
preferenceHandler: preferenceHandler,
|
||||
globalHandler: globalHandler,
|
||||
promoteHandler: promoteHandler,
|
||||
flaggerHandler: flaggerHandler,
|
||||
dashboardModule: dashboardModule,
|
||||
dashboardHandler: dashboardHandler,
|
||||
metricsExplorerHandler: metricsExplorerHandler,
|
||||
gatewayHandler: gatewayHandler,
|
||||
fieldsHandler: fieldsHandler,
|
||||
authzHandler: authzHandler,
|
||||
zeusHandler: zeusHandler,
|
||||
querierHandler: querierHandler,
|
||||
serviceAccountHandler: serviceAccountHandler,
|
||||
config: config,
|
||||
settings: settings,
|
||||
router: router,
|
||||
orgHandler: orgHandler,
|
||||
userHandler: userHandler,
|
||||
sessionHandler: sessionHandler,
|
||||
authDomainHandler: authDomainHandler,
|
||||
preferenceHandler: preferenceHandler,
|
||||
globalHandler: globalHandler,
|
||||
promoteHandler: promoteHandler,
|
||||
flaggerHandler: flaggerHandler,
|
||||
dashboardModule: dashboardModule,
|
||||
dashboardHandler: dashboardHandler,
|
||||
metricsExplorerHandler: metricsExplorerHandler,
|
||||
gatewayHandler: gatewayHandler,
|
||||
fieldsHandler: fieldsHandler,
|
||||
authzHandler: authzHandler,
|
||||
zeusHandler: zeusHandler,
|
||||
querierHandler: querierHandler,
|
||||
serviceAccountHandler: serviceAccountHandler,
|
||||
ruleStateHistoryHandler: ruleStateHistoryHandler,
|
||||
}
|
||||
|
||||
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
|
||||
@@ -233,6 +239,10 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := provider.addRuleStateHistoryRoutes(router); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
118
pkg/apiserver/signozapiserver/rulestatehistory.go
Normal file
118
pkg/apiserver/signozapiserver/rulestatehistory.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package signozapiserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/http/handler"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/rulestatehistorytypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (provider *provider) addRuleStateHistoryRoutes(router *mux.Router) error {
|
||||
|
||||
if err := router.Handle("/api/v2/rules/{id}/history/stats", handler.New(
|
||||
provider.authZ.ViewAccess(provider.ruleStateHistoryHandler.GetRuleHistoryStats),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetRuleHistoryStats",
|
||||
Tags: []string{"rules"},
|
||||
Summary: "Get rule history stats",
|
||||
Description: "Returns trigger and resolution statistics for a rule in the selected time range.",
|
||||
RequestQuery: new(rulestatehistorytypes.V2HistoryBaseQueryParams),
|
||||
Response: new(rulestatehistorytypes.Stats),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/rules/{id}/history/timeline", handler.New(
|
||||
provider.authZ.ViewAccess(provider.ruleStateHistoryHandler.GetRuleHistoryTimeline),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetRuleHistoryTimeline",
|
||||
Tags: []string{"rules"},
|
||||
Summary: "Get rule history timeline",
|
||||
Description: "Returns paginated timeline entries for rule state transitions.",
|
||||
RequestQuery: new(rulestatehistorytypes.V2HistoryTimelineQueryParams),
|
||||
Response: new(rulestatehistorytypes.RuleStateTimelineResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/rules/{id}/history/top_contributors", handler.New(
|
||||
provider.authZ.ViewAccess(provider.ruleStateHistoryHandler.GetRuleHistoryContributors),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetRuleHistoryTopContributors",
|
||||
Tags: []string{"rules"},
|
||||
Summary: "Get top contributors to rule firing",
|
||||
Description: "Returns top label combinations contributing to rule firing in the selected time range.",
|
||||
RequestQuery: new(rulestatehistorytypes.V2HistoryBaseQueryParams),
|
||||
Response: new([]rulestatehistorytypes.RuleStateHistoryContributorResponse),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/rules/{id}/history/filter_keys", handler.New(
|
||||
provider.authZ.ViewAccess(provider.ruleStateHistoryHandler.GetRuleHistoryFilterKeys),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetRuleHistoryFilterKeys",
|
||||
Tags: []string{"rules"},
|
||||
Summary: "Get rule history filter keys",
|
||||
Description: "Returns distinct label keys from rule history entries for the selected range.",
|
||||
RequestQuery: new(telemetrytypes.PostableFieldKeysParams),
|
||||
Response: new(telemetrytypes.GettableFieldKeys),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/rules/{id}/history/filter_values", handler.New(
|
||||
provider.authZ.ViewAccess(provider.ruleStateHistoryHandler.GetRuleHistoryFilterValues),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetRuleHistoryFilterValues",
|
||||
Tags: []string{"rules"},
|
||||
Summary: "Get rule history filter values",
|
||||
Description: "Returns distinct label values for a given key from rule history entries.",
|
||||
RequestQuery: new(telemetrytypes.PostableFieldValueParams),
|
||||
Response: new(telemetrytypes.GettableFieldValues),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/rules/{id}/history/overall_status", handler.New(
|
||||
provider.authZ.ViewAccess(provider.ruleStateHistoryHandler.GetRuleHistoryOverallStatus),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetRuleHistoryOverallStatus",
|
||||
Tags: []string{"rules"},
|
||||
Summary: "Get rule overall status timeline",
|
||||
Description: "Returns overall firing/inactive intervals for a rule in the selected time range.",
|
||||
RequestQuery: new(rulestatehistorytypes.V2HistoryBaseQueryParams),
|
||||
Response: new([]rulestatehistorytypes.RuleStateWindow),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError},
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package implrulestatehistory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
type conditionBuilder struct {
|
||||
fm qbtypes.FieldMapper
|
||||
}
|
||||
|
||||
func newConditionBuilder(fm qbtypes.FieldMapper) qbtypes.ConditionBuilder {
|
||||
return &conditionBuilder{fm: fm}
|
||||
}
|
||||
|
||||
func (c *conditionBuilder) ConditionFor(
|
||||
ctx context.Context,
|
||||
key *telemetrytypes.TelemetryFieldKey,
|
||||
operator qbtypes.FilterOperator,
|
||||
value any,
|
||||
sb *sqlbuilder.SelectBuilder,
|
||||
_ uint64,
|
||||
_ uint64,
|
||||
) (string, error) {
|
||||
if operator.IsStringSearchOperator() {
|
||||
value = querybuilder.FormatValueForContains(value)
|
||||
}
|
||||
|
||||
fieldName, err := c.fm.FieldFor(ctx, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch operator {
|
||||
case qbtypes.FilterOperatorEqual:
|
||||
return sb.E(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorNotEqual:
|
||||
return sb.NE(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorGreaterThan:
|
||||
return sb.G(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorGreaterThanOrEq:
|
||||
return sb.GE(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorLessThan:
|
||||
return sb.LT(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorLessThanOrEq:
|
||||
return sb.LE(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorLike:
|
||||
return sb.Like(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorNotLike:
|
||||
return sb.NotLike(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorILike:
|
||||
return sb.ILike(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorNotILike:
|
||||
return sb.NotILike(fieldName, value), nil
|
||||
case qbtypes.FilterOperatorContains:
|
||||
return sb.ILike(fieldName, fmt.Sprintf("%%%s%%", value)), nil
|
||||
case qbtypes.FilterOperatorNotContains:
|
||||
return sb.NotILike(fieldName, fmt.Sprintf("%%%s%%", value)), nil
|
||||
case qbtypes.FilterOperatorRegexp:
|
||||
return fmt.Sprintf(`match(%s, %s)`, sqlbuilder.Escape(fieldName), sb.Var(value)), nil
|
||||
case qbtypes.FilterOperatorNotRegexp:
|
||||
return fmt.Sprintf(`NOT match(%s, %s)`, sqlbuilder.Escape(fieldName), sb.Var(value)), nil
|
||||
case qbtypes.FilterOperatorBetween:
|
||||
values, ok := value.([]any)
|
||||
if !ok || len(values) != 2 {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
return sb.Between(fieldName, values[0], values[1]), nil
|
||||
case qbtypes.FilterOperatorNotBetween:
|
||||
values, ok := value.([]any)
|
||||
if !ok || len(values) != 2 {
|
||||
return "", qbtypes.ErrBetweenValues
|
||||
}
|
||||
return sb.NotBetween(fieldName, values[0], values[1]), nil
|
||||
case qbtypes.FilterOperatorIn:
|
||||
values, ok := value.([]any)
|
||||
if !ok {
|
||||
return "", qbtypes.ErrInValues
|
||||
}
|
||||
return sb.In(fieldName, values), nil
|
||||
case qbtypes.FilterOperatorNotIn:
|
||||
values, ok := value.([]any)
|
||||
if !ok {
|
||||
return "", qbtypes.ErrInValues
|
||||
}
|
||||
return sb.NotIn(fieldName, values), nil
|
||||
case qbtypes.FilterOperatorExists, qbtypes.FilterOperatorNotExists:
|
||||
intrinsic := []string{"rule_id", "rule_name", "overall_state", "overall_state_changed", "state", "state_changed", "unix_milli", "fingerprint", "value"}
|
||||
if slices.Contains(intrinsic, key.Name) {
|
||||
return "true", nil
|
||||
}
|
||||
if operator == qbtypes.FilterOperatorExists {
|
||||
return fmt.Sprintf("has(JSONExtractKeys(labels), %s)", sb.Var(key.Name)), nil
|
||||
}
|
||||
return fmt.Sprintf("not has(JSONExtractKeys(labels), %s)", sb.Var(key.Name)), nil
|
||||
}
|
||||
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported operator: %v", operator)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package implrulestatehistory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
var ruleStateHistoryColumns = map[string]*schema.Column{
|
||||
"rule_id": {Name: "rule_id", Type: schema.ColumnTypeString},
|
||||
"rule_name": {Name: "rule_name", Type: schema.ColumnTypeString},
|
||||
"overall_state": {Name: "overall_state", Type: schema.ColumnTypeString},
|
||||
"overall_state_changed": {Name: "overall_state_changed", Type: schema.ColumnTypeBool},
|
||||
"state": {Name: "state", Type: schema.ColumnTypeString},
|
||||
"state_changed": {Name: "state_changed", Type: schema.ColumnTypeBool},
|
||||
"unix_milli": {Name: "unix_milli", Type: schema.ColumnTypeInt64},
|
||||
"labels": {Name: "labels", Type: schema.ColumnTypeString},
|
||||
"fingerprint": {Name: "fingerprint", Type: schema.ColumnTypeUInt64},
|
||||
"value": {Name: "value", Type: schema.ColumnTypeFloat64},
|
||||
}
|
||||
|
||||
type fieldMapper struct{}
|
||||
|
||||
func newFieldMapper() qbtypes.FieldMapper {
|
||||
return &fieldMapper{}
|
||||
}
|
||||
|
||||
func (m *fieldMapper) getColumn(_ context.Context, key *telemetrytypes.TelemetryFieldKey) (*schema.Column, error) {
|
||||
name := strings.TrimSpace(key.Name)
|
||||
if col, ok := ruleStateHistoryColumns[name]; ok {
|
||||
return col, nil
|
||||
}
|
||||
return ruleStateHistoryColumns["labels"], nil
|
||||
}
|
||||
|
||||
func (m *fieldMapper) FieldFor(ctx context.Context, key *telemetrytypes.TelemetryFieldKey) (string, error) {
|
||||
col, err := m.getColumn(ctx, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if col.Name == "labels" && key.Name != "labels" {
|
||||
return fmt.Sprintf("JSONExtractString(labels, '%s')", strings.ReplaceAll(key.Name, "'", "\\'")), nil
|
||||
}
|
||||
return col.Name, nil
|
||||
}
|
||||
|
||||
func (m *fieldMapper) ColumnFor(ctx context.Context, key *telemetrytypes.TelemetryFieldKey) (*schema.Column, error) {
|
||||
return m.getColumn(ctx, key)
|
||||
}
|
||||
|
||||
func (m *fieldMapper) ColumnExpressionFor(ctx context.Context, field *telemetrytypes.TelemetryFieldKey, _ map[string][]*telemetrytypes.TelemetryFieldKey) (string, error) {
|
||||
colName, err := m.FieldFor(ctx, field)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s AS `%s`", sqlbuilder.Escape(colName), field.Name), nil
|
||||
}
|
||||
351
pkg/modules/rulestatehistory/implrulestatehistory/handler.go
Normal file
351
pkg/modules/rulestatehistory/implrulestatehistory/handler.go
Normal file
@@ -0,0 +1,351 @@
|
||||
package implrulestatehistory
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||
"github.com/SigNoz/signoz/pkg/http/render"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/rulestatehistorytypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
module rulestatehistory.Module
|
||||
}
|
||||
|
||||
type ruleHistoryRequest struct {
|
||||
Query rulestatehistorytypes.Query
|
||||
Cursor string
|
||||
}
|
||||
|
||||
type cursorToken struct {
|
||||
Offset int64 `json:"offset"`
|
||||
Limit int64 `json:"limit"`
|
||||
}
|
||||
|
||||
func NewHandler(module rulestatehistory.Module) rulestatehistory.Handler {
|
||||
return &handler{module: module}
|
||||
}
|
||||
|
||||
func (h *handler) GetRuleHistoryStats(w http.ResponseWriter, r *http.Request) {
|
||||
ruleID := mux.Vars(r)["id"]
|
||||
req, ok := h.parseV2BaseQueryRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
stats, err := h.module.GetHistoryStats(r.Context(), ruleID, req.Query)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
render.Success(w, http.StatusOK, stats)
|
||||
}
|
||||
|
||||
func (h *handler) GetRuleHistoryOverallStatus(w http.ResponseWriter, r *http.Request) {
|
||||
ruleID := mux.Vars(r)["id"]
|
||||
req, ok := h.parseV2BaseQueryRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.module.GetHistoryOverallStatus(r.Context(), ruleID, req.Query)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
render.Success(w, http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *handler) GetRuleHistoryTimeline(w http.ResponseWriter, r *http.Request) {
|
||||
ruleID := mux.Vars(r)["id"]
|
||||
req, ok := h.parseV2TimelineQueryRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if req.Cursor != "" {
|
||||
token, err := decodeCursor(req.Cursor)
|
||||
if err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid cursor"))
|
||||
return
|
||||
}
|
||||
req.Query.Offset = token.Offset
|
||||
if req.Query.Limit == 0 {
|
||||
req.Query.Limit = token.Limit
|
||||
}
|
||||
}
|
||||
if req.Query.Limit == 0 {
|
||||
req.Query.Limit = 50
|
||||
}
|
||||
|
||||
timelineItems, timelineTotal, err := h.module.GetHistoryTimeline(r.Context(), ruleID, req.Query)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := rulestatehistorytypes.RuleStateTimelineResponse{}
|
||||
resp.Items = make([]rulestatehistorytypes.RuleStateHistoryResponseItem, 0, len(timelineItems))
|
||||
for _, item := range timelineItems {
|
||||
resp.Items = append(resp.Items, rulestatehistorytypes.RuleStateHistoryResponseItem{
|
||||
RuleID: item.RuleID,
|
||||
RuleName: item.RuleName,
|
||||
OverallState: item.OverallState,
|
||||
OverallStateChanged: item.OverallStateChanged,
|
||||
State: item.State,
|
||||
StateChanged: item.StateChanged,
|
||||
UnixMilli: item.UnixMilli,
|
||||
Labels: toQBLabels(item.Labels),
|
||||
Fingerprint: item.Fingerprint,
|
||||
Value: item.Value,
|
||||
})
|
||||
}
|
||||
resp.Total = timelineTotal
|
||||
if req.Query.Limit > 0 && req.Query.Offset+int64(len(timelineItems)) < int64(timelineTotal) {
|
||||
nextOffset := req.Query.Offset + int64(len(timelineItems))
|
||||
nextCursor, err := encodeCursor(cursorToken{Offset: nextOffset, Limit: req.Query.Limit})
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
resp.NextCursor = nextCursor
|
||||
}
|
||||
render.Success(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *handler) GetRuleHistoryContributors(w http.ResponseWriter, r *http.Request) {
|
||||
ruleID := mux.Vars(r)["id"]
|
||||
req, ok := h.parseV2BaseQueryRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.module.GetHistoryContributors(r.Context(), ruleID, req.Query)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
converted := make([]rulestatehistorytypes.RuleStateHistoryContributorResponse, 0, len(res))
|
||||
for _, item := range res {
|
||||
converted = append(converted, rulestatehistorytypes.RuleStateHistoryContributorResponse{
|
||||
Fingerprint: item.Fingerprint,
|
||||
Labels: toQBLabels(item.Labels),
|
||||
Count: item.Count,
|
||||
RelatedTracesLink: item.RelatedTracesLink,
|
||||
RelatedLogsLink: item.RelatedLogsLink,
|
||||
})
|
||||
}
|
||||
render.Success(w, http.StatusOK, converted)
|
||||
}
|
||||
|
||||
func (h *handler) GetRuleHistoryFilterKeys(w http.ResponseWriter, r *http.Request) {
|
||||
ruleID := mux.Vars(r)["id"]
|
||||
query, search, limit, ok := h.parseV2FilterKeysRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.module.GetHistoryFilterKeys(r.Context(), ruleID, query, search, limit)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
render.Success(w, http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *handler) GetRuleHistoryFilterValues(w http.ResponseWriter, r *http.Request) {
|
||||
ruleID := mux.Vars(r)["id"]
|
||||
query, key, search, limit, ok := h.parseV2FilterValuesRequest(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.module.GetHistoryFilterValues(r.Context(), ruleID, key, query, search, limit)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
render.Success(w, http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (h *handler) parseV2BaseQueryRequest(w http.ResponseWriter, r *http.Request) (*ruleHistoryRequest, bool) {
|
||||
req, err := parseV2BaseQueryFromURL(r)
|
||||
if err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid query parameters"))
|
||||
return nil, false
|
||||
}
|
||||
if req.Query.Start == 0 || req.Query.End == 0 || req.Query.Start >= req.Query.End {
|
||||
render.Error(w, errors.NewInvalidInputf(errors.CodeInvalidInput, "start and end are required and start must be less than end"))
|
||||
return nil, false
|
||||
}
|
||||
return req, true
|
||||
}
|
||||
|
||||
func (h *handler) parseV2TimelineQueryRequest(w http.ResponseWriter, r *http.Request) (*ruleHistoryRequest, bool) {
|
||||
req, err := parseV2TimelineQueryFromURL(r)
|
||||
if err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid query parameters"))
|
||||
return nil, false
|
||||
}
|
||||
if err := req.Query.Validate(); err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid query parameters"))
|
||||
return nil, false
|
||||
}
|
||||
return req, true
|
||||
}
|
||||
|
||||
func (h *handler) parseV2FilterKeysRequest(w http.ResponseWriter, r *http.Request) (rulestatehistorytypes.Query, string, int64, bool) {
|
||||
raw := telemetrytypes.PostableFieldKeysParams{}
|
||||
if err := binding.Query.BindQuery(r.URL.Query(), &raw); err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid query parameters"))
|
||||
return rulestatehistorytypes.Query{}, "", 0, false
|
||||
}
|
||||
|
||||
query := rulestatehistorytypes.Query{
|
||||
Start: raw.StartUnixMilli,
|
||||
End: raw.EndUnixMilli,
|
||||
FilterExpression: qbtypes.Filter{},
|
||||
Order: qbtypes.OrderDirectionAsc,
|
||||
}
|
||||
if err := query.Validate(); err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid query parameters"))
|
||||
return rulestatehistorytypes.Query{}, "", 0, false
|
||||
}
|
||||
|
||||
limit := normalizeFilterLimit(int64(raw.Limit))
|
||||
return query, strings.TrimSpace(raw.SearchText), limit, true
|
||||
}
|
||||
|
||||
func (h *handler) parseV2FilterValuesRequest(w http.ResponseWriter, r *http.Request) (rulestatehistorytypes.Query, string, string, int64, bool) {
|
||||
raw := telemetrytypes.PostableFieldValueParams{}
|
||||
if err := binding.Query.BindQuery(r.URL.Query(), &raw); err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid query parameters"))
|
||||
return rulestatehistorytypes.Query{}, "", "", 0, false
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(raw.Name)
|
||||
if key == "" {
|
||||
render.Error(w, errors.NewInvalidInputf(errors.CodeInvalidInput, "key is required"))
|
||||
return rulestatehistorytypes.Query{}, "", "", 0, false
|
||||
}
|
||||
|
||||
query := rulestatehistorytypes.Query{
|
||||
Start: raw.StartUnixMilli,
|
||||
End: raw.EndUnixMilli,
|
||||
FilterExpression: parseFilterExpression(raw.ExistingQuery),
|
||||
Order: qbtypes.OrderDirectionAsc,
|
||||
}
|
||||
if err := query.Validate(); err != nil {
|
||||
render.Error(w, errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "invalid query parameters"))
|
||||
return rulestatehistorytypes.Query{}, "", "", 0, false
|
||||
}
|
||||
|
||||
limit := normalizeFilterLimit(int64(raw.Limit))
|
||||
return query, key, strings.TrimSpace(raw.SearchText), limit, true
|
||||
}
|
||||
|
||||
func parseV2BaseQueryFromURL(r *http.Request) (*ruleHistoryRequest, error) {
|
||||
raw := rulestatehistorytypes.V2HistoryBaseQueryParams{}
|
||||
if err := binding.Query.BindQuery(r.URL.Query(), &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &ruleHistoryRequest{}
|
||||
req.Query.Start = raw.Start
|
||||
req.Query.End = raw.End
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func parseV2TimelineQueryFromURL(r *http.Request) (*ruleHistoryRequest, error) {
|
||||
raw := rulestatehistorytypes.V2HistoryTimelineQueryParams{}
|
||||
if err := binding.Query.BindQuery(r.URL.Query(), &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &ruleHistoryRequest{}
|
||||
req.Query.Start = raw.Start
|
||||
req.Query.End = raw.End
|
||||
req.Query.State = raw.State
|
||||
req.Query.Limit = raw.Limit
|
||||
req.Query.Order = raw.Order
|
||||
req.Query.FilterExpression = parseFilterExpression(raw.FilterExpression)
|
||||
req.Cursor = raw.Cursor
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func encodeCursor(token cursorToken) (string, error) {
|
||||
data, err := json.Marshal(token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
func decodeCursor(cursor string) (*cursorToken, error) {
|
||||
data, err := base64.RawURLEncoding.DecodeString(cursor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token := &cursorToken{}
|
||||
if err := json.Unmarshal(data, token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func normalizeFilterLimit(limit int64) int64 {
|
||||
if limit <= 0 {
|
||||
return 50
|
||||
}
|
||||
if limit > 200 {
|
||||
return 200
|
||||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
func toQBLabels(raw rulestatehistorytypes.LabelsString) []*qbtypes.Label {
|
||||
if strings.TrimSpace(string(raw)) == "" {
|
||||
return []*qbtypes.Label{}
|
||||
}
|
||||
|
||||
labelsMap := map[string]any{}
|
||||
if err := json.Unmarshal([]byte(raw), &labelsMap); err != nil {
|
||||
return []*qbtypes.Label{}
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(labelsMap))
|
||||
for key := range labelsMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
labels := make([]*qbtypes.Label, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
labels = append(labels, &qbtypes.Label{
|
||||
Key: telemetrytypes.TelemetryFieldKey{
|
||||
Name: key,
|
||||
},
|
||||
Value: labelsMap[key],
|
||||
})
|
||||
}
|
||||
|
||||
return labels
|
||||
}
|
||||
|
||||
func parseFilterExpression(values ...string) qbtypes.Filter {
|
||||
for _, value := range values {
|
||||
expr := strings.TrimSpace(value)
|
||||
if expr != "" {
|
||||
return qbtypes.Filter{Expression: expr}
|
||||
}
|
||||
}
|
||||
return qbtypes.Filter{}
|
||||
}
|
||||
183
pkg/modules/rulestatehistory/implrulestatehistory/module.go
Normal file
183
pkg/modules/rulestatehistory/implrulestatehistory/module.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package implrulestatehistory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/types/rulestatehistorytypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
type module struct {
|
||||
store rulestatehistorytypes.Store
|
||||
}
|
||||
|
||||
func NewModule(store rulestatehistorytypes.Store) rulestatehistory.Module {
|
||||
return &module{store: store}
|
||||
}
|
||||
|
||||
func (m *module) AddRuleStateHistory(ctx context.Context, entries []rulestatehistorytypes.RuleStateHistory) error {
|
||||
return m.store.AddRuleStateHistory(ctx, entries)
|
||||
}
|
||||
|
||||
func (m *module) GetLastSavedRuleStateHistory(ctx context.Context, ruleID string) ([]rulestatehistorytypes.RuleStateHistory, error) {
|
||||
return m.store.GetLastSavedRuleStateHistory(ctx, ruleID)
|
||||
}
|
||||
|
||||
func (m *module) GetHistoryTimeline(ctx context.Context, ruleID string, query rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateHistory, uint64, error) {
|
||||
return m.store.ReadRuleStateHistoryByRuleID(ctx, ruleID, &query)
|
||||
}
|
||||
|
||||
func (m *module) GetHistoryFilterKeys(ctx context.Context, ruleID string, query rulestatehistorytypes.Query, search string, limit int64) (*telemetrytypes.GettableFieldKeys, error) {
|
||||
return m.store.ReadRuleStateHistoryFilterKeysByRuleID(ctx, ruleID, &query, search, limit)
|
||||
}
|
||||
|
||||
func (m *module) GetHistoryFilterValues(ctx context.Context, ruleID string, key string, query rulestatehistorytypes.Query, search string, limit int64) (*telemetrytypes.GettableFieldValues, error) {
|
||||
return m.store.ReadRuleStateHistoryFilterValuesByRuleID(ctx, ruleID, key, &query, search, limit)
|
||||
}
|
||||
|
||||
func (m *module) GetHistoryContributors(ctx context.Context, ruleID string, query rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateHistoryContributor, error) {
|
||||
return m.store.ReadRuleStateHistoryTopContributorsByRuleID(ctx, ruleID, &query)
|
||||
}
|
||||
|
||||
func (m *module) GetHistoryOverallStatus(ctx context.Context, ruleID string, query rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateWindow, error) {
|
||||
return m.store.GetOverallStateTransitions(ctx, ruleID, &query)
|
||||
}
|
||||
|
||||
func (m *module) GetHistoryStats(ctx context.Context, ruleID string, params rulestatehistorytypes.Query) (rulestatehistorytypes.Stats, error) {
|
||||
totalCurrentTriggers, err := m.store.GetTotalTriggers(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
currentTriggersSeries, err := m.store.GetTriggersByInterval(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
currentAvgResolutionTime, err := m.store.GetAvgResolutionTime(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
currentAvgResolutionTimeSeries, err := m.store.GetAvgResolutionTimeByInterval(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
|
||||
if params.End-params.Start >= 86400000 {
|
||||
days := int64(math.Ceil(float64(params.End-params.Start) / 86400000))
|
||||
params.Start -= days * 86400000
|
||||
params.End -= days * 86400000
|
||||
} else {
|
||||
params.Start -= 86400000
|
||||
params.End -= 86400000
|
||||
}
|
||||
|
||||
totalPastTriggers, err := m.store.GetTotalTriggers(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
pastTriggersSeries, err := m.store.GetTriggersByInterval(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
pastAvgResolutionTime, err := m.store.GetAvgResolutionTime(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
pastAvgResolutionTimeSeries, err := m.store.GetAvgResolutionTimeByInterval(ctx, ruleID, ¶ms)
|
||||
if err != nil {
|
||||
return rulestatehistorytypes.Stats{}, err
|
||||
}
|
||||
|
||||
if math.IsNaN(currentAvgResolutionTime) || math.IsInf(currentAvgResolutionTime, 0) {
|
||||
currentAvgResolutionTime = 0
|
||||
}
|
||||
if math.IsNaN(pastAvgResolutionTime) || math.IsInf(pastAvgResolutionTime, 0) {
|
||||
pastAvgResolutionTime = 0
|
||||
}
|
||||
|
||||
return rulestatehistorytypes.Stats{
|
||||
TotalCurrentTriggers: totalCurrentTriggers,
|
||||
TotalPastTriggers: totalPastTriggers,
|
||||
CurrentTriggersSeries: currentTriggersSeries,
|
||||
PastTriggersSeries: pastTriggersSeries,
|
||||
CurrentAvgResolutionTime: currentAvgResolutionTime,
|
||||
PastAvgResolutionTime: pastAvgResolutionTime,
|
||||
CurrentAvgResolutionTimeSeries: currentAvgResolutionTimeSeries,
|
||||
PastAvgResolutionTimeSeries: pastAvgResolutionTimeSeries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *module) RecordRuleStateHistory(ctx context.Context, ruleID string, handledRestart bool, itemsToAdd []rulestatehistorytypes.RuleStateHistory) error {
|
||||
revisedItemsToAdd := map[uint64]rulestatehistorytypes.RuleStateHistory{}
|
||||
|
||||
lastSavedState, err := m.store.GetLastSavedRuleStateHistory(ctx, ruleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !handledRestart && len(lastSavedState) > 0 {
|
||||
currentItemsByFingerprint := make(map[uint64]rulestatehistorytypes.RuleStateHistory, len(itemsToAdd))
|
||||
for _, item := range itemsToAdd {
|
||||
currentItemsByFingerprint[item.Fingerprint] = item
|
||||
}
|
||||
|
||||
shouldSkip := map[uint64]bool{}
|
||||
for _, item := range lastSavedState {
|
||||
currentState, ok := currentItemsByFingerprint[item.Fingerprint]
|
||||
if !ok {
|
||||
if item.State == rulestatehistorytypes.StateFiring || item.State == rulestatehistorytypes.StateNoData {
|
||||
item.State = rulestatehistorytypes.StateInactive
|
||||
item.StateChanged = true
|
||||
item.UnixMilli = time.Now().UnixMilli()
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
} else if item.State != currentState.State {
|
||||
item.State = currentState.State
|
||||
item.StateChanged = true
|
||||
item.UnixMilli = time.Now().UnixMilli()
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
|
||||
shouldSkip[item.Fingerprint] = true
|
||||
}
|
||||
|
||||
for _, item := range itemsToAdd {
|
||||
if _, ok := revisedItemsToAdd[item.Fingerprint]; !ok && !shouldSkip[item.Fingerprint] {
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
}
|
||||
|
||||
newState := rulestatehistorytypes.StateInactive
|
||||
for _, item := range revisedItemsToAdd {
|
||||
if item.State == rulestatehistorytypes.StateFiring || item.State == rulestatehistorytypes.StateNoData {
|
||||
newState = rulestatehistorytypes.StateFiring
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if lastSavedState[0].OverallState != newState {
|
||||
for fingerprint, item := range revisedItemsToAdd {
|
||||
item.OverallState = newState
|
||||
item.OverallStateChanged = true
|
||||
revisedItemsToAdd[fingerprint] = item
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, item := range itemsToAdd {
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
}
|
||||
|
||||
if len(revisedItemsToAdd) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries := make([]rulestatehistorytypes.RuleStateHistory, 0, len(revisedItemsToAdd))
|
||||
for _, item := range revisedItemsToAdd {
|
||||
entries = append(entries, item)
|
||||
}
|
||||
|
||||
return m.store.AddRuleStateHistory(ctx, entries)
|
||||
}
|
||||
574
pkg/modules/rulestatehistory/implrulestatehistory/store.go
Normal file
574
pkg/modules/rulestatehistory/implrulestatehistory/store.go
Normal file
@@ -0,0 +1,574 @@
|
||||
package implrulestatehistory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/rulestatehistorytypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
sqlbuilder "github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
const (
|
||||
signozHistoryDBName = "signoz_analytics"
|
||||
ruleStateHistoryTableName = "distributed_rule_state_history_v0"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
telemetryStore telemetrystore.TelemetryStore
|
||||
telemetryMetadataStore telemetrytypes.MetadataStore
|
||||
fieldMapper qbtypes.FieldMapper
|
||||
conditionBuilder qbtypes.ConditionBuilder
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewStore(telemetryStore telemetrystore.TelemetryStore, telemetryMetadataStore telemetrytypes.MetadataStore, logger *slog.Logger) rulestatehistorytypes.Store {
|
||||
fm := newFieldMapper()
|
||||
return &store{
|
||||
telemetryStore: telemetryStore,
|
||||
telemetryMetadataStore: telemetryMetadataStore,
|
||||
fieldMapper: fm,
|
||||
conditionBuilder: newConditionBuilder(fm),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) AddRuleStateHistory(ctx context.Context, entries []rulestatehistorytypes.RuleStateHistory) error {
|
||||
ib := sqlbuilder.NewInsertBuilder()
|
||||
ib.InsertInto(historyTable())
|
||||
ib.Cols(
|
||||
"rule_id",
|
||||
"rule_name",
|
||||
"overall_state",
|
||||
"overall_state_changed",
|
||||
"state",
|
||||
"state_changed",
|
||||
"unix_milli",
|
||||
"labels",
|
||||
"fingerprint",
|
||||
"value",
|
||||
)
|
||||
insertQuery, _ := ib.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
statement, err := s.telemetryStore.ClickhouseDB().PrepareBatch(
|
||||
ctx,
|
||||
insertQuery,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer statement.Abort() //nolint:errcheck
|
||||
|
||||
for _, history := range entries {
|
||||
if err = statement.Append(
|
||||
history.RuleID,
|
||||
history.RuleName,
|
||||
history.OverallState,
|
||||
history.OverallStateChanged,
|
||||
history.State,
|
||||
history.StateChanged,
|
||||
history.UnixMilli,
|
||||
history.Labels,
|
||||
history.Fingerprint,
|
||||
history.Value,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return statement.Send()
|
||||
}
|
||||
|
||||
func (s *store) GetLastSavedRuleStateHistory(ctx context.Context, ruleID string) ([]rulestatehistorytypes.RuleStateHistory, error) {
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("*")
|
||||
sb.From(historyTable())
|
||||
sb.Where(sb.E("rule_id", ruleID))
|
||||
sb.Where(sb.E("state_changed", true))
|
||||
sb.OrderBy("unix_milli DESC")
|
||||
sb.SQL("LIMIT 1 BY fingerprint")
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
history := make([]rulestatehistorytypes.RuleStateHistory, 0)
|
||||
if err := s.telemetryStore.ClickhouseDB().Select(ctx, &history, query, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return history, nil
|
||||
}
|
||||
|
||||
func (s *store) ReadRuleStateHistoryByRuleID(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateHistory, uint64, error) {
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select(
|
||||
"rule_id",
|
||||
"rule_name",
|
||||
"overall_state",
|
||||
"overall_state_changed",
|
||||
"state",
|
||||
"state_changed",
|
||||
"unix_milli",
|
||||
"labels",
|
||||
"fingerprint",
|
||||
"value",
|
||||
)
|
||||
sb.From(historyTable())
|
||||
s.applyBaseHistoryFilters(sb, ruleID, query)
|
||||
|
||||
whereClause, err := s.buildFilterClause(ctx, query.FilterExpression, query.Start, query.End)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if whereClause != nil {
|
||||
sb.AddWhereClause(sqlbuilder.CopyWhereClause(whereClause))
|
||||
}
|
||||
|
||||
sb.OrderBy(fmt.Sprintf("unix_milli %s", strings.ToUpper(query.Order.StringValue())))
|
||||
sb.Limit(int(query.Limit))
|
||||
sb.Offset(int(query.Offset))
|
||||
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
history := []rulestatehistorytypes.RuleStateHistory{}
|
||||
if err := s.telemetryStore.ClickhouseDB().Select(ctx, &history, selectQuery, args...); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
countSB := sqlbuilder.NewSelectBuilder()
|
||||
countSB.Select("count(*)")
|
||||
countSB.From(historyTable())
|
||||
s.applyBaseHistoryFilters(countSB, ruleID, query)
|
||||
if whereClause != nil {
|
||||
countSB.AddWhereClause(sqlbuilder.CopyWhereClause(whereClause))
|
||||
}
|
||||
|
||||
var total uint64
|
||||
countQuery, countArgs := countSB.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
if err := s.telemetryStore.ClickhouseDB().QueryRow(ctx, countQuery, countArgs...).Scan(&total); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return history, total, nil
|
||||
}
|
||||
|
||||
func (s *store) ReadRuleStateHistoryFilterKeysByRuleID(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query, search string, limit int64) (*telemetrytypes.GettableFieldKeys, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
keyExpr := "arrayJoin(JSONExtractKeys(labels))"
|
||||
sb.Select(fmt.Sprintf("DISTINCT %s AS key", keyExpr))
|
||||
sb.From(historyTable())
|
||||
s.applyBaseHistoryFilters(sb, ruleID, query)
|
||||
sb.Where(fmt.Sprintf("%s != ''", keyExpr))
|
||||
|
||||
search = strings.TrimSpace(search)
|
||||
if search != "" {
|
||||
sb.Where(fmt.Sprintf("positionCaseInsensitiveUTF8(%s, %s) > 0", keyExpr, sb.Var(search)))
|
||||
}
|
||||
|
||||
whereClause, err := s.buildFilterClause(ctx, query.FilterExpression, query.Start, query.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if whereClause != nil {
|
||||
sb.AddWhereClause(sqlbuilder.CopyWhereClause(whereClause))
|
||||
}
|
||||
|
||||
sb.OrderBy("key ASC")
|
||||
sb.Limit(int(limit + 1))
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
rows, err := s.telemetryStore.ClickhouseDB().Query(ctx, selectQuery, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
keys := make([]string, 0, limit+1)
|
||||
for rows.Next() {
|
||||
var key string
|
||||
if err := rows.Scan(&key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key = strings.TrimSpace(key)
|
||||
if key != "" {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
complete := true
|
||||
if int64(len(keys)) > limit {
|
||||
keys = keys[:int(limit)]
|
||||
complete = false
|
||||
}
|
||||
|
||||
keysMap := make(map[string][]*telemetrytypes.TelemetryFieldKey, len(keys))
|
||||
for _, key := range keys {
|
||||
fieldKey := &telemetrytypes.TelemetryFieldKey{
|
||||
Name: key,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
}
|
||||
keysMap[key] = []*telemetrytypes.TelemetryFieldKey{fieldKey}
|
||||
}
|
||||
|
||||
return &telemetrytypes.GettableFieldKeys{
|
||||
Keys: keysMap,
|
||||
Complete: complete,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *store) ReadRuleStateHistoryFilterValuesByRuleID(ctx context.Context, ruleID string, key string, query *rulestatehistorytypes.Query, search string, limit int64) (*telemetrytypes.GettableFieldValues, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
valExpr := fmt.Sprintf("JSONExtractString(labels, %s)", sb.Var(key))
|
||||
sb.Select(fmt.Sprintf("DISTINCT %s AS val", valExpr))
|
||||
sb.From(historyTable())
|
||||
s.applyBaseHistoryFilters(sb, ruleID, query)
|
||||
sb.Where(fmt.Sprintf("JSONHas(labels, %s)", sb.Var(key)))
|
||||
sb.Where(fmt.Sprintf("%s != ''", valExpr))
|
||||
|
||||
search = strings.TrimSpace(search)
|
||||
if search != "" {
|
||||
sb.Where(fmt.Sprintf("positionCaseInsensitiveUTF8(%s, %s) > 0", valExpr, sb.Var(search)))
|
||||
}
|
||||
|
||||
whereClause, err := s.buildFilterClause(ctx, query.FilterExpression, query.Start, query.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if whereClause != nil {
|
||||
sb.AddWhereClause(sqlbuilder.CopyWhereClause(whereClause))
|
||||
}
|
||||
|
||||
sb.OrderBy("val ASC")
|
||||
sb.Limit(int(limit + 1))
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
rows, err := s.telemetryStore.ClickhouseDB().Query(ctx, selectQuery, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
values := make([]string, 0, limit+1)
|
||||
for rows.Next() {
|
||||
var value string
|
||||
if err := rows.Scan(&value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
value = strings.TrimSpace(value)
|
||||
if value != "" {
|
||||
values = append(values, value)
|
||||
}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
complete := true
|
||||
if int64(len(values)) > limit {
|
||||
values = values[:int(limit)]
|
||||
complete = false
|
||||
}
|
||||
|
||||
return &telemetrytypes.GettableFieldValues{
|
||||
Values: &telemetrytypes.TelemetryFieldValues{
|
||||
StringValues: values,
|
||||
},
|
||||
Complete: complete,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *store) ReadRuleStateHistoryTopContributorsByRuleID(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateHistoryContributor, error) {
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select(
|
||||
"fingerprint",
|
||||
"argMax(labels, unix_milli) AS labels",
|
||||
"count(*) AS count",
|
||||
)
|
||||
sb.From(historyTable())
|
||||
sb.Where(sb.E("rule_id", ruleID))
|
||||
sb.Where(sb.E("state_changed", true))
|
||||
sb.Where(sb.E("state", rulestatehistorytypes.StateFiring.StringValue()))
|
||||
sb.Where(sb.GE("unix_milli", query.Start))
|
||||
sb.Where(sb.LT("unix_milli", query.End))
|
||||
|
||||
whereClause, err := s.buildFilterClause(ctx, query.FilterExpression, query.Start, query.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if whereClause != nil {
|
||||
sb.AddWhereClause(sqlbuilder.CopyWhereClause(whereClause))
|
||||
}
|
||||
|
||||
sb.GroupBy("fingerprint")
|
||||
sb.Having("labels != '{}'")
|
||||
sb.OrderBy("count DESC")
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
contributors := []rulestatehistorytypes.RuleStateHistoryContributor{}
|
||||
if err := s.telemetryStore.ClickhouseDB().Select(ctx, &contributors, selectQuery, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return contributors, nil
|
||||
}
|
||||
|
||||
func (s *store) GetOverallStateTransitions(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateWindow, error) {
|
||||
innerSB := sqlbuilder.NewSelectBuilder()
|
||||
|
||||
eventsSubquery := fmt.Sprintf(
|
||||
`SELECT %s AS ts, if(count(*) = 0, %s, argMax(overall_state, unix_milli)) AS state
|
||||
FROM %s
|
||||
WHERE rule_id = %s
|
||||
AND unix_milli <= %s
|
||||
UNION ALL
|
||||
SELECT unix_milli AS ts, anyLast(overall_state) AS state
|
||||
FROM %s
|
||||
WHERE rule_id = %s
|
||||
AND overall_state_changed = true
|
||||
AND unix_milli > %s
|
||||
AND unix_milli < %s
|
||||
GROUP BY unix_milli`,
|
||||
innerSB.Var(query.Start),
|
||||
innerSB.Var(rulestatehistorytypes.StateInactive.StringValue()),
|
||||
historyTable(),
|
||||
innerSB.Var(ruleID),
|
||||
innerSB.Var(query.Start),
|
||||
historyTable(),
|
||||
innerSB.Var(ruleID),
|
||||
innerSB.Var(query.Start),
|
||||
innerSB.Var(query.End),
|
||||
)
|
||||
|
||||
innerSB.Select(
|
||||
"state",
|
||||
"ts AS start",
|
||||
fmt.Sprintf(
|
||||
"ifNull(leadInFrame(toNullable(ts), 1) OVER (ORDER BY ts ASC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), %s) AS end",
|
||||
innerSB.Var(query.End),
|
||||
),
|
||||
)
|
||||
innerSB.From(fmt.Sprintf("(%s) AS events", eventsSubquery))
|
||||
innerSB.OrderBy("start ASC")
|
||||
innerQuery, args := innerSB.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
outerSB := sqlbuilder.NewSelectBuilder()
|
||||
outerSB.Select("state", "start", "end")
|
||||
outerSB.From(fmt.Sprintf("(%s) AS windows", innerQuery))
|
||||
outerSB.Where("start < end")
|
||||
selectQuery, outerArgs := outerSB.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
args = append(args, outerArgs...)
|
||||
|
||||
windows := []rulestatehistorytypes.RuleStateWindow{}
|
||||
if err := s.telemetryStore.ClickhouseDB().Select(ctx, &windows, selectQuery, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return windows, nil
|
||||
}
|
||||
|
||||
func (s *store) GetAvgResolutionTime(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query) (float64, error) {
|
||||
cte := s.buildMatchedEventsCTE(ruleID, query)
|
||||
sb := cte.Select("ifNull(toFloat64(avg(resolution_time - firing_time)) / 1000, 0) AS avg_resolution_time")
|
||||
sb.From("matched_events")
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
var avg float64
|
||||
if err := s.telemetryStore.ClickhouseDB().QueryRow(ctx, selectQuery, args...).Scan(&avg); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return avg, nil
|
||||
}
|
||||
|
||||
func (s *store) GetAvgResolutionTimeByInterval(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query) (*qbtypes.TimeSeries, error) {
|
||||
step := minStepSeconds(query.Start, query.End)
|
||||
cte := s.buildMatchedEventsCTE(ruleID, query)
|
||||
sb := cte.Select(
|
||||
fmt.Sprintf("toFloat64(avg(resolution_time - firing_time)) / 1000 AS value, toStartOfInterval(toDateTime(intDiv(firing_time, 1000)), INTERVAL %d SECOND) AS ts", step),
|
||||
)
|
||||
sb.From("matched_events")
|
||||
sb.GroupBy("ts")
|
||||
sb.OrderBy("ts ASC")
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
return s.querySeries(ctx, selectQuery, args...)
|
||||
}
|
||||
|
||||
func (s *store) GetTotalTriggers(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query) (uint64, error) {
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("count(*)")
|
||||
sb.From(historyTable())
|
||||
sb.Where(sb.E("rule_id", ruleID))
|
||||
sb.Where(sb.E("state_changed", true))
|
||||
sb.Where(sb.E("state", rulestatehistorytypes.StateFiring.StringValue()))
|
||||
sb.Where(sb.GE("unix_milli", query.Start))
|
||||
sb.Where(sb.LT("unix_milli", query.End))
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
var total uint64
|
||||
if err := s.telemetryStore.ClickhouseDB().QueryRow(ctx, selectQuery, args...).Scan(&total); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *store) GetTriggersByInterval(ctx context.Context, ruleID string, query *rulestatehistorytypes.Query) (*qbtypes.TimeSeries, error) {
|
||||
step := minStepSeconds(query.Start, query.End)
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select(
|
||||
fmt.Sprintf("toFloat64(count(*)) AS value, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL %d SECOND) AS ts", step),
|
||||
)
|
||||
sb.From(historyTable())
|
||||
sb.Where(sb.E("rule_id", ruleID))
|
||||
sb.Where(sb.E("state_changed", true))
|
||||
sb.Where(sb.E("state", rulestatehistorytypes.StateFiring.StringValue()))
|
||||
sb.Where(sb.GE("unix_milli", query.Start))
|
||||
sb.Where(sb.LT("unix_milli", query.End))
|
||||
sb.GroupBy("ts")
|
||||
sb.OrderBy("ts ASC")
|
||||
selectQuery, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
return s.querySeries(ctx, selectQuery, args...)
|
||||
}
|
||||
|
||||
func (s *store) querySeries(ctx context.Context, selectQuery string, args ...any) (*qbtypes.TimeSeries, error) {
|
||||
rows, err := s.telemetryStore.ClickhouseDB().Query(ctx, selectQuery, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
series := &qbtypes.TimeSeries{
|
||||
Labels: []*qbtypes.Label{},
|
||||
Values: []*qbtypes.TimeSeriesValue{},
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var value float64
|
||||
var ts time.Time
|
||||
if err := rows.Scan(&value, &ts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
series.Values = append(series.Values, &qbtypes.TimeSeriesValue{
|
||||
Timestamp: ts.UnixMilli(),
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return series, nil
|
||||
}
|
||||
|
||||
func (s *store) buildFilterClause(ctx context.Context, filter qbtypes.Filter, startMillis, endMillis int64) (*sqlbuilder.WhereClause, error) {
|
||||
expression := strings.TrimSpace(filter.Expression)
|
||||
if expression == "" {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
|
||||
selectors := querybuilder.QueryStringToKeysSelectors(expression)
|
||||
for i := range selectors {
|
||||
selectors[i].SelectorMatchType = telemetrytypes.FieldSelectorMatchTypeExact
|
||||
}
|
||||
|
||||
fieldKeys, _, err := s.telemetryMetadataStore.GetKeysMulti(ctx, selectors)
|
||||
if err != nil || len(fieldKeys) == 0 {
|
||||
fieldKeys = map[string][]*telemetrytypes.TelemetryFieldKey{}
|
||||
for _, sel := range selectors {
|
||||
fieldKeys[sel.Name] = []*telemetrytypes.TelemetryFieldKey{{
|
||||
Name: sel.Name,
|
||||
Signal: sel.Signal,
|
||||
FieldContext: sel.FieldContext,
|
||||
FieldDataType: sel.FieldDataType,
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
opts := querybuilder.FilterExprVisitorOpts{
|
||||
Logger: s.logger,
|
||||
FieldMapper: s.fieldMapper,
|
||||
ConditionBuilder: s.conditionBuilder,
|
||||
FieldKeys: fieldKeys,
|
||||
FullTextColumn: &telemetrytypes.TelemetryFieldKey{Name: "labels", FieldContext: telemetrytypes.FieldContextAttribute},
|
||||
}
|
||||
|
||||
startNs := querybuilder.ToNanoSecs(uint64(startMillis))
|
||||
endNs := querybuilder.ToNanoSecs(uint64(endMillis))
|
||||
prepared, err := querybuilder.PrepareWhereClause(expression, opts, startNs, endNs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if prepared == nil || prepared.WhereClause == nil {
|
||||
return nil, nil //nolint:nilnil
|
||||
}
|
||||
return prepared.WhereClause, nil
|
||||
}
|
||||
|
||||
func (s *store) applyBaseHistoryFilters(sb *sqlbuilder.SelectBuilder, ruleID string, query *rulestatehistorytypes.Query) {
|
||||
sb.Where(sb.E("rule_id", ruleID))
|
||||
sb.Where(sb.GE("unix_milli", query.Start))
|
||||
sb.Where(sb.LT("unix_milli", query.End))
|
||||
if !query.State.IsZero() {
|
||||
sb.Where(sb.E("state", query.State.StringValue()))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *store) buildMatchedEventsCTE(ruleID string, query *rulestatehistorytypes.Query) *sqlbuilder.CTEBuilder {
|
||||
firingSB := sqlbuilder.NewSelectBuilder()
|
||||
firingSB.Select("rule_id", "unix_milli AS firing_time")
|
||||
firingSB.From(historyTable())
|
||||
firingSB.Where(firingSB.E("overall_state", rulestatehistorytypes.StateFiring.StringValue()))
|
||||
firingSB.Where(firingSB.E("overall_state_changed", true))
|
||||
firingSB.Where(firingSB.E("rule_id", ruleID))
|
||||
firingSB.Where(firingSB.GE("unix_milli", query.Start))
|
||||
firingSB.Where(firingSB.LT("unix_milli", query.End))
|
||||
|
||||
resolutionSB := sqlbuilder.NewSelectBuilder()
|
||||
resolutionSB.Select("rule_id", "unix_milli AS resolution_time")
|
||||
resolutionSB.From(historyTable())
|
||||
resolutionSB.Where(resolutionSB.E("overall_state", rulestatehistorytypes.StateInactive.StringValue()))
|
||||
resolutionSB.Where(resolutionSB.E("overall_state_changed", true))
|
||||
resolutionSB.Where(resolutionSB.E("rule_id", ruleID))
|
||||
resolutionSB.Where(resolutionSB.GE("unix_milli", query.Start))
|
||||
resolutionSB.Where(resolutionSB.LT("unix_milli", query.End))
|
||||
|
||||
matchedSB := sqlbuilder.NewSelectBuilder()
|
||||
matchedSB.Select("f.rule_id", "f.firing_time", "min(r.resolution_time) AS resolution_time")
|
||||
matchedSB.From("firing_events f")
|
||||
matchedSB.JoinWithOption(sqlbuilder.LeftJoin, "resolution_events r", "f.rule_id = r.rule_id")
|
||||
matchedSB.Where("r.resolution_time > f.firing_time")
|
||||
matchedSB.GroupBy("f.rule_id", "f.firing_time")
|
||||
|
||||
return sqlbuilder.With(
|
||||
sqlbuilder.CTEQuery("firing_events").As(firingSB),
|
||||
sqlbuilder.CTEQuery("resolution_events").As(resolutionSB),
|
||||
sqlbuilder.CTEQuery("matched_events").As(matchedSB),
|
||||
)
|
||||
}
|
||||
|
||||
func historyTable() string {
|
||||
return fmt.Sprintf("%s.%s", signozHistoryDBName, ruleStateHistoryTableName)
|
||||
}
|
||||
|
||||
func minStepSeconds(start, end int64) int64 {
|
||||
if end <= start {
|
||||
return 60
|
||||
}
|
||||
rangeSeconds := (end - start) / 1000
|
||||
if rangeSeconds <= 0 {
|
||||
return 60
|
||||
}
|
||||
step := rangeSeconds / 120
|
||||
return max(step, int64(60))
|
||||
}
|
||||
30
pkg/modules/rulestatehistory/rulestatehistory.go
Normal file
30
pkg/modules/rulestatehistory/rulestatehistory.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package rulestatehistory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/rulestatehistorytypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
type Module interface {
|
||||
RecordRuleStateHistory(context.Context, string, bool, []rulestatehistorytypes.RuleStateHistory) error
|
||||
AddRuleStateHistory(context.Context, []rulestatehistorytypes.RuleStateHistory) error
|
||||
GetLastSavedRuleStateHistory(context.Context, string) ([]rulestatehistorytypes.RuleStateHistory, error)
|
||||
GetHistoryStats(context.Context, string, rulestatehistorytypes.Query) (rulestatehistorytypes.Stats, error)
|
||||
GetHistoryTimeline(context.Context, string, rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateHistory, uint64, error)
|
||||
GetHistoryFilterKeys(context.Context, string, rulestatehistorytypes.Query, string, int64) (*telemetrytypes.GettableFieldKeys, error)
|
||||
GetHistoryFilterValues(context.Context, string, string, rulestatehistorytypes.Query, string, int64) (*telemetrytypes.GettableFieldValues, error)
|
||||
GetHistoryContributors(context.Context, string, rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateHistoryContributor, error)
|
||||
GetHistoryOverallStatus(context.Context, string, rulestatehistorytypes.Query) ([]rulestatehistorytypes.RuleStateWindow, error)
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
GetRuleHistoryStats(http.ResponseWriter, *http.Request)
|
||||
GetRuleHistoryTimeline(http.ResponseWriter, *http.Request)
|
||||
GetRuleHistoryFilterKeys(http.ResponseWriter, *http.Request)
|
||||
GetRuleHistoryFilterValues(http.ResponseWriter, *http.Request)
|
||||
GetRuleHistoryContributors(http.ResponseWriter, *http.Request)
|
||||
GetRuleHistoryOverallStatus(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/http/middleware"
|
||||
"github.com/SigNoz/signoz/pkg/licensing/nooplicensing"
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/agentConf"
|
||||
@@ -106,6 +107,7 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
||||
signoz.TelemetryMetadataStore,
|
||||
signoz.Prometheus,
|
||||
signoz.Modules.OrgGetter,
|
||||
signoz.Modules.RuleStateHistory,
|
||||
signoz.Querier,
|
||||
signoz.Instrumentation.ToProviderSettings(),
|
||||
signoz.QueryParser,
|
||||
@@ -336,6 +338,7 @@ func makeRulesManager(
|
||||
metadataStore telemetrytypes.MetadataStore,
|
||||
prometheus prometheus.Prometheus,
|
||||
orgGetter organization.Getter,
|
||||
ruleStateHistoryModule rulestatehistory.Module,
|
||||
querier querier.Querier,
|
||||
providerSettings factory.ProviderSettings,
|
||||
queryParser queryparser.QueryParser,
|
||||
@@ -344,22 +347,23 @@ func makeRulesManager(
|
||||
maintenanceStore := sqlrulestore.NewMaintenanceStore(sqlstore)
|
||||
// create manager opts
|
||||
managerOpts := &rules.ManagerOptions{
|
||||
TelemetryStore: telemetryStore,
|
||||
MetadataStore: metadataStore,
|
||||
Prometheus: prometheus,
|
||||
Context: context.Background(),
|
||||
Logger: zap.L(),
|
||||
Reader: ch,
|
||||
Querier: querier,
|
||||
SLogger: providerSettings.Logger,
|
||||
Cache: cache,
|
||||
EvalDelay: constants.GetEvalDelay(),
|
||||
OrgGetter: orgGetter,
|
||||
Alertmanager: alertmanager,
|
||||
RuleStore: ruleStore,
|
||||
MaintenanceStore: maintenanceStore,
|
||||
SqlStore: sqlstore,
|
||||
QueryParser: queryParser,
|
||||
TelemetryStore: telemetryStore,
|
||||
MetadataStore: metadataStore,
|
||||
Prometheus: prometheus,
|
||||
Context: context.Background(),
|
||||
Logger: zap.L(),
|
||||
Reader: ch,
|
||||
Querier: querier,
|
||||
SLogger: providerSettings.Logger,
|
||||
Cache: cache,
|
||||
EvalDelay: constants.GetEvalDelay(),
|
||||
OrgGetter: orgGetter,
|
||||
Alertmanager: alertmanager,
|
||||
RuleStore: ruleStore,
|
||||
MaintenanceStore: maintenanceStore,
|
||||
SqlStore: sqlstore,
|
||||
QueryParser: queryParser,
|
||||
RuleStateHistoryModule: ruleStateHistoryModule,
|
||||
}
|
||||
|
||||
// create Manager
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/rulestatehistorytypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/ruletypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
@@ -96,6 +98,8 @@ type BaseRule struct {
|
||||
// newGroupEvalDelay is the grace period for new alert groups
|
||||
newGroupEvalDelay valuer.TextDuration
|
||||
|
||||
ruleStateHistoryModule rulestatehistory.Module
|
||||
|
||||
queryParser queryparser.QueryParser
|
||||
}
|
||||
|
||||
@@ -143,6 +147,12 @@ func WithMetadataStore(metadataStore telemetrytypes.MetadataStore) RuleOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithRuleStateHistoryModule(module rulestatehistory.Module) RuleOption {
|
||||
return func(r *BaseRule) {
|
||||
r.ruleStateHistoryModule = module
|
||||
}
|
||||
}
|
||||
|
||||
func NewBaseRule(id string, orgID valuer.UUID, p *ruletypes.PostableRule, reader interfaces.Reader, opts ...RuleOption) (*BaseRule, error) {
|
||||
if p.RuleCondition == nil || !p.RuleCondition.IsValid() {
|
||||
return nil, fmt.Errorf("invalid rule condition")
|
||||
@@ -400,100 +410,59 @@ func (r *BaseRule) ForEachActiveAlert(f func(*ruletypes.Alert)) {
|
||||
}
|
||||
|
||||
func (r *BaseRule) RecordRuleStateHistory(ctx context.Context, prevState, currentState model.AlertState, itemsToAdd []model.RuleStateHistory) error {
|
||||
zap.L().Debug("recording rule state history", zap.String("ruleid", r.ID()), zap.Any("prevState", prevState), zap.Any("currentState", currentState), zap.Any("itemsToAdd", itemsToAdd))
|
||||
revisedItemsToAdd := map[uint64]model.RuleStateHistory{}
|
||||
if r.ruleStateHistoryModule == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lastSavedState, err := r.reader.GetLastSavedRuleStateHistory(ctx, r.ID())
|
||||
if err != nil {
|
||||
if err := r.ruleStateHistoryModule.RecordRuleStateHistory(ctx, r.ID(), r.handledRestart, toRuleStateHistoryTypes(itemsToAdd)); err != nil {
|
||||
zap.L().Error("error while recording rule state history", zap.Error(err), zap.Any("itemsToAdd", itemsToAdd))
|
||||
return err
|
||||
}
|
||||
// if the query-service has been restarted, or the rule has been modified (which re-initializes the rule),
|
||||
// the state would reset so we need to add the corresponding state changes to previously saved states
|
||||
if !r.handledRestart && len(lastSavedState) > 0 {
|
||||
zap.L().Debug("handling restart", zap.String("ruleid", r.ID()), zap.Any("lastSavedState", lastSavedState))
|
||||
l := map[uint64]model.RuleStateHistory{}
|
||||
for _, item := range itemsToAdd {
|
||||
l[item.Fingerprint] = item
|
||||
}
|
||||
|
||||
shouldSkip := map[uint64]bool{}
|
||||
|
||||
for _, item := range lastSavedState {
|
||||
// for the last saved item with fingerprint, check if there is a corresponding entry in the current state
|
||||
currentState, ok := l[item.Fingerprint]
|
||||
if !ok {
|
||||
// there was a state change in the past, but not in the current state
|
||||
// if the state was firing, then we should add a resolved state change
|
||||
if item.State == model.StateFiring || item.State == model.StateNoData {
|
||||
item.State = model.StateInactive
|
||||
item.StateChanged = true
|
||||
item.UnixMilli = time.Now().UnixMilli()
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
// there is nothing to do if the prev state was normal
|
||||
} else {
|
||||
if item.State != currentState.State {
|
||||
item.State = currentState.State
|
||||
item.StateChanged = true
|
||||
item.UnixMilli = time.Now().UnixMilli()
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
}
|
||||
// do not add this item to revisedItemsToAdd as it is already processed
|
||||
shouldSkip[item.Fingerprint] = true
|
||||
}
|
||||
zap.L().Debug("after lastSavedState loop", zap.String("ruleid", r.ID()), zap.Any("revisedItemsToAdd", revisedItemsToAdd))
|
||||
|
||||
// if there are any new state changes that were not saved, add them to the revised items
|
||||
for _, item := range itemsToAdd {
|
||||
if _, ok := revisedItemsToAdd[item.Fingerprint]; !ok && !shouldSkip[item.Fingerprint] {
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
}
|
||||
zap.L().Debug("after itemsToAdd loop", zap.String("ruleid", r.ID()), zap.Any("revisedItemsToAdd", revisedItemsToAdd))
|
||||
|
||||
newState := model.StateInactive
|
||||
for _, item := range revisedItemsToAdd {
|
||||
if item.State == model.StateFiring || item.State == model.StateNoData {
|
||||
newState = model.StateFiring
|
||||
break
|
||||
}
|
||||
}
|
||||
zap.L().Debug("newState", zap.String("ruleid", r.ID()), zap.Any("newState", newState))
|
||||
|
||||
// if there is a change in the overall state, update the overall state
|
||||
if lastSavedState[0].OverallState != newState {
|
||||
for fingerprint, item := range revisedItemsToAdd {
|
||||
item.OverallState = newState
|
||||
item.OverallStateChanged = true
|
||||
revisedItemsToAdd[fingerprint] = item
|
||||
}
|
||||
}
|
||||
zap.L().Debug("revisedItemsToAdd after newState", zap.String("ruleid", r.ID()), zap.Any("revisedItemsToAdd", revisedItemsToAdd))
|
||||
|
||||
} else {
|
||||
for _, item := range itemsToAdd {
|
||||
revisedItemsToAdd[item.Fingerprint] = item
|
||||
}
|
||||
}
|
||||
|
||||
if len(revisedItemsToAdd) > 0 && r.reader != nil {
|
||||
zap.L().Debug("writing rule state history", zap.String("ruleid", r.ID()), zap.Any("revisedItemsToAdd", revisedItemsToAdd))
|
||||
|
||||
entries := make([]model.RuleStateHistory, 0, len(revisedItemsToAdd))
|
||||
for _, item := range revisedItemsToAdd {
|
||||
entries = append(entries, item)
|
||||
}
|
||||
err := r.reader.AddRuleStateHistory(ctx, entries)
|
||||
if err != nil {
|
||||
zap.L().Error("error while inserting rule state history", zap.Error(err), zap.Any("itemsToAdd", itemsToAdd))
|
||||
}
|
||||
}
|
||||
r.handledRestart = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(srikanthccv): remove these when v3 is cleaned up
|
||||
func toRuleStateHistoryTypes(entries []model.RuleStateHistory) []rulestatehistorytypes.RuleStateHistory {
|
||||
converted := make([]rulestatehistorytypes.RuleStateHistory, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
converted = append(converted, rulestatehistorytypes.RuleStateHistory{
|
||||
RuleID: entry.RuleID,
|
||||
RuleName: entry.RuleName,
|
||||
OverallState: toRuleStateHistoryAlertState(entry.OverallState),
|
||||
OverallStateChanged: entry.OverallStateChanged,
|
||||
State: toRuleStateHistoryAlertState(entry.State),
|
||||
StateChanged: entry.StateChanged,
|
||||
UnixMilli: entry.UnixMilli,
|
||||
Labels: rulestatehistorytypes.LabelsString(entry.Labels),
|
||||
Fingerprint: entry.Fingerprint,
|
||||
Value: entry.Value,
|
||||
})
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func toRuleStateHistoryAlertState(state model.AlertState) rulestatehistorytypes.AlertState {
|
||||
switch state {
|
||||
case model.StateInactive:
|
||||
return rulestatehistorytypes.StateInactive
|
||||
case model.StatePending:
|
||||
return rulestatehistorytypes.StatePending
|
||||
case model.StateRecovering:
|
||||
return rulestatehistorytypes.StateRecovering
|
||||
case model.StateFiring:
|
||||
return rulestatehistorytypes.StateFiring
|
||||
case model.StateNoData:
|
||||
return rulestatehistorytypes.StateNoData
|
||||
case model.StateDisabled:
|
||||
return rulestatehistorytypes.StateDisabled
|
||||
default:
|
||||
return rulestatehistorytypes.StateInactive
|
||||
}
|
||||
}
|
||||
|
||||
func (r *BaseRule) PopulateTemporality(ctx context.Context, orgID valuer.UUID, qp *v3.QueryRangeParamsV3) error {
|
||||
missingTemporality := make([]string, 0)
|
||||
metricNameToTemporality := make(map[string]map[v3.Temporality]bool)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||
"github.com/SigNoz/signoz/pkg/cache"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
querierV5 "github.com/SigNoz/signoz/pkg/querier"
|
||||
@@ -99,6 +100,8 @@ type ManagerOptions struct {
|
||||
|
||||
EvalDelay valuer.TextDuration
|
||||
|
||||
RuleStateHistoryModule rulestatehistory.Module
|
||||
|
||||
PrepareTaskFunc func(opts PrepareTaskOptions) (Task, error)
|
||||
PrepareTestRuleFunc func(opts PrepareTestRuleOptions) (int, *model.ApiError)
|
||||
Alertmanager alertmanager.Alertmanager
|
||||
@@ -174,6 +177,7 @@ func defaultPrepareTaskFunc(opts PrepareTaskOptions) (Task, error) {
|
||||
WithSQLStore(opts.SQLStore),
|
||||
WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||
WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||
WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -198,6 +202,7 @@ func defaultPrepareTaskFunc(opts PrepareTaskOptions) (Task, error) {
|
||||
WithSQLStore(opts.SQLStore),
|
||||
WithQueryParser(opts.ManagerOpts.QueryParser),
|
||||
WithMetadataStore(opts.ManagerOpts.MetadataStore),
|
||||
WithRuleStateHistoryModule(opts.ManagerOpts.RuleStateHistoryModule),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestThresholdRuleEvalBackwardCompat(t *testing.T) {
|
||||
AlertName: "Eval Backward Compatibility Test without recovery target",
|
||||
AlertType: ruletypes.AlertTypeMetric,
|
||||
RuleType: ruletypes.RuleTypeThreshold,
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{ruletypes.RollingEvaluation, ruletypes.RollingWindow{
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
||||
EvalWindow: valuer.MustParseTextDuration("5m"),
|
||||
Frequency: valuer.MustParseTextDuration("1m"),
|
||||
}},
|
||||
@@ -151,7 +151,7 @@ func TestPrepareLinksToLogs(t *testing.T) {
|
||||
AlertName: "Tricky Condition Tests",
|
||||
AlertType: ruletypes.AlertTypeLogs,
|
||||
RuleType: ruletypes.RuleTypeThreshold,
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{ruletypes.RollingEvaluation, ruletypes.RollingWindow{
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
||||
EvalWindow: valuer.MustParseTextDuration("5m"),
|
||||
Frequency: valuer.MustParseTextDuration("1m"),
|
||||
}},
|
||||
@@ -205,7 +205,7 @@ func TestPrepareLinksToLogsV5(t *testing.T) {
|
||||
AlertName: "Tricky Condition Tests",
|
||||
AlertType: ruletypes.AlertTypeLogs,
|
||||
RuleType: ruletypes.RuleTypeThreshold,
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{ruletypes.RollingEvaluation, ruletypes.RollingWindow{
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
||||
EvalWindow: valuer.MustParseTextDuration("5m"),
|
||||
Frequency: valuer.MustParseTextDuration("1m"),
|
||||
}},
|
||||
@@ -266,7 +266,7 @@ func TestPrepareLinksToTracesV5(t *testing.T) {
|
||||
AlertName: "Tricky Condition Tests",
|
||||
AlertType: ruletypes.AlertTypeTraces,
|
||||
RuleType: ruletypes.RuleTypeThreshold,
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{ruletypes.RollingEvaluation, ruletypes.RollingWindow{
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
||||
EvalWindow: valuer.MustParseTextDuration("5m"),
|
||||
Frequency: valuer.MustParseTextDuration("1m"),
|
||||
}},
|
||||
@@ -327,7 +327,7 @@ func TestPrepareLinksToTraces(t *testing.T) {
|
||||
AlertName: "Links to traces test",
|
||||
AlertType: ruletypes.AlertTypeTraces,
|
||||
RuleType: ruletypes.RuleTypeThreshold,
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{ruletypes.RollingEvaluation, ruletypes.RollingWindow{
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
||||
EvalWindow: valuer.MustParseTextDuration("5m"),
|
||||
Frequency: valuer.MustParseTextDuration("1m"),
|
||||
}},
|
||||
@@ -381,7 +381,7 @@ func TestThresholdRuleLabelNormalization(t *testing.T) {
|
||||
AlertName: "Tricky Condition Tests",
|
||||
AlertType: ruletypes.AlertTypeMetric,
|
||||
RuleType: ruletypes.RuleTypeThreshold,
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{ruletypes.RollingEvaluation, ruletypes.RollingWindow{
|
||||
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
|
||||
EvalWindow: valuer.MustParseTextDuration("5m"),
|
||||
Frequency: valuer.MustParseTextDuration("1m"),
|
||||
}},
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory/implrulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
||||
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||
@@ -44,6 +46,7 @@ type Handlers struct {
|
||||
QuickFilter quickfilter.Handler
|
||||
TraceFunnel tracefunnel.Handler
|
||||
RawDataExport rawdataexport.Handler
|
||||
RuleStateHistory rulestatehistory.Handler
|
||||
SpanPercentile spanpercentile.Handler
|
||||
Services services.Handler
|
||||
MetricsExplorer metricsexplorer.Handler
|
||||
@@ -77,6 +80,7 @@ func NewHandlers(
|
||||
QuickFilter: implquickfilter.NewHandler(modules.QuickFilter),
|
||||
TraceFunnel: impltracefunnel.NewHandler(modules.TraceFunnel),
|
||||
RawDataExport: implrawdataexport.NewHandler(modules.RawDataExport),
|
||||
RuleStateHistory: implrulestatehistory.NewHandler(modules.RuleStateHistory),
|
||||
Services: implservices.NewHandler(modules.Services),
|
||||
MetricsExplorer: implmetricsexplorer.NewHandler(modules.MetricsExplorer),
|
||||
SpanPercentile: implspanpercentile.NewHandler(modules.SpanPercentile),
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/quickfilter/implquickfilter"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rawdataexport/implrawdataexport"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory/implrulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview"
|
||||
"github.com/SigNoz/signoz/pkg/modules/savedview/implsavedview"
|
||||
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||
@@ -51,24 +53,25 @@ import (
|
||||
)
|
||||
|
||||
type Modules struct {
|
||||
OrgGetter organization.Getter
|
||||
OrgSetter organization.Setter
|
||||
Preference preference.Module
|
||||
User user.Module
|
||||
UserGetter user.Getter
|
||||
SavedView savedview.Module
|
||||
Apdex apdex.Module
|
||||
Dashboard dashboard.Module
|
||||
QuickFilter quickfilter.Module
|
||||
TraceFunnel tracefunnel.Module
|
||||
RawDataExport rawdataexport.Module
|
||||
AuthDomain authdomain.Module
|
||||
Session session.Module
|
||||
Services services.Module
|
||||
SpanPercentile spanpercentile.Module
|
||||
MetricsExplorer metricsexplorer.Module
|
||||
Promote promote.Module
|
||||
ServiceAccount serviceaccount.Module
|
||||
OrgGetter organization.Getter
|
||||
OrgSetter organization.Setter
|
||||
Preference preference.Module
|
||||
User user.Module
|
||||
UserGetter user.Getter
|
||||
SavedView savedview.Module
|
||||
Apdex apdex.Module
|
||||
Dashboard dashboard.Module
|
||||
QuickFilter quickfilter.Module
|
||||
TraceFunnel tracefunnel.Module
|
||||
RawDataExport rawdataexport.Module
|
||||
RuleStateHistory rulestatehistory.Module
|
||||
AuthDomain authdomain.Module
|
||||
Session session.Module
|
||||
Services services.Module
|
||||
SpanPercentile spanpercentile.Module
|
||||
MetricsExplorer metricsexplorer.Module
|
||||
Promote promote.Module
|
||||
ServiceAccount serviceaccount.Module
|
||||
}
|
||||
|
||||
func NewModules(
|
||||
@@ -96,23 +99,24 @@ func NewModules(
|
||||
ruleStore := sqlrulestore.NewRuleStore(sqlstore, queryParser, providerSettings)
|
||||
|
||||
return Modules{
|
||||
OrgGetter: orgGetter,
|
||||
OrgSetter: orgSetter,
|
||||
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewAvailablePreference()),
|
||||
SavedView: implsavedview.NewModule(sqlstore),
|
||||
Apdex: implapdex.NewModule(sqlstore),
|
||||
Dashboard: dashboard,
|
||||
User: user,
|
||||
UserGetter: userGetter,
|
||||
QuickFilter: quickfilter,
|
||||
TraceFunnel: impltracefunnel.NewModule(impltracefunnel.NewStore(sqlstore)),
|
||||
RawDataExport: implrawdataexport.NewModule(querier),
|
||||
AuthDomain: implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs),
|
||||
Session: implsession.NewModule(providerSettings, authNs, user, userGetter, implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs), tokenizer, orgGetter),
|
||||
SpanPercentile: implspanpercentile.NewModule(querier, providerSettings),
|
||||
Services: implservices.NewModule(querier, telemetryStore),
|
||||
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
|
||||
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
|
||||
ServiceAccount: implserviceaccount.NewModule(implserviceaccount.NewStore(sqlstore), authz, emailing, providerSettings),
|
||||
OrgGetter: orgGetter,
|
||||
OrgSetter: orgSetter,
|
||||
Preference: implpreference.NewModule(implpreference.NewStore(sqlstore), preferencetypes.NewAvailablePreference()),
|
||||
SavedView: implsavedview.NewModule(sqlstore),
|
||||
Apdex: implapdex.NewModule(sqlstore),
|
||||
Dashboard: dashboard,
|
||||
User: user,
|
||||
UserGetter: userGetter,
|
||||
QuickFilter: quickfilter,
|
||||
TraceFunnel: impltracefunnel.NewModule(impltracefunnel.NewStore(sqlstore)),
|
||||
RawDataExport: implrawdataexport.NewModule(querier),
|
||||
RuleStateHistory: implrulestatehistory.NewModule(implrulestatehistory.NewStore(telemetryStore, telemetryMetadataStore, providerSettings.Logger)),
|
||||
AuthDomain: implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs),
|
||||
Session: implsession.NewModule(providerSettings, authNs, user, userGetter, implauthdomain.NewModule(implauthdomain.NewStore(sqlstore), authNs), tokenizer, orgGetter),
|
||||
SpanPercentile: implspanpercentile.NewModule(querier, providerSettings),
|
||||
Services: implservices.NewModule(querier, telemetryStore),
|
||||
MetricsExplorer: implmetricsexplorer.NewModule(telemetryStore, telemetryMetadataStore, cache, ruleStore, dashboard, providerSettings, config.MetricsExplorer),
|
||||
Promote: implpromote.NewModule(telemetryMetadataStore, telemetryStore),
|
||||
ServiceAccount: implserviceaccount.NewModule(implserviceaccount.NewStore(sqlstore), authz, emailing, providerSettings),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/modules/preference"
|
||||
"github.com/SigNoz/signoz/pkg/modules/promote"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/serviceaccount"
|
||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||
@@ -61,6 +62,7 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
|
||||
struct{ zeus.Handler }{},
|
||||
struct{ querier.Handler }{},
|
||||
struct{ serviceaccount.Handler }{},
|
||||
struct{ rulestatehistory.Handler }{},
|
||||
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -256,6 +256,7 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
|
||||
handlers.ZeusHandler,
|
||||
handlers.QuerierHandler,
|
||||
handlers.ServiceAccountHandler,
|
||||
handlers.RuleStateHistory,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
20
pkg/types/rulestatehistorytypes/http.go
Normal file
20
pkg/types/rulestatehistorytypes/http.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package rulestatehistorytypes
|
||||
|
||||
import qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
|
||||
// V2HistoryBaseQueryParams defines URL query params common across v2 rule history APIs.
|
||||
type V2HistoryBaseQueryParams struct {
|
||||
Start int64 `query:"start" required:"true"`
|
||||
End int64 `query:"end" required:"true"`
|
||||
}
|
||||
|
||||
// V2HistoryTimelineQueryParams defines URL query params for timeline API.
|
||||
type V2HistoryTimelineQueryParams struct {
|
||||
Start int64 `query:"start" required:"true"`
|
||||
End int64 `query:"end" required:"true"`
|
||||
State AlertState `query:"state"`
|
||||
FilterExpression string `query:"filterExpression"`
|
||||
Limit int64 `query:"limit"`
|
||||
Order qbtypes.OrderDirection `query:"order"`
|
||||
Cursor string `query:"cursor"`
|
||||
}
|
||||
35
pkg/types/rulestatehistorytypes/query.go
Normal file
35
pkg/types/rulestatehistorytypes/query.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package rulestatehistorytypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
)
|
||||
|
||||
type Query struct {
|
||||
Start int64
|
||||
End int64
|
||||
State AlertState
|
||||
FilterExpression qbtypes.Filter
|
||||
Limit int64
|
||||
Offset int64
|
||||
Order qbtypes.OrderDirection
|
||||
}
|
||||
|
||||
func (q *Query) Validate() error {
|
||||
if q.Start == 0 || q.End == 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "start and end are required")
|
||||
}
|
||||
if q.Start >= q.End {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "start must be less than end")
|
||||
}
|
||||
if q.Limit < 0 || q.Offset < 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "limit and offset must be greater than or equal to 0")
|
||||
}
|
||||
if q.Order.IsZero() {
|
||||
q.Order = qbtypes.OrderDirectionDesc
|
||||
}
|
||||
if q.Order != qbtypes.OrderDirectionAsc && q.Order != qbtypes.OrderDirectionDesc {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "order must be asc or desc")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
49
pkg/types/rulestatehistorytypes/response.go
Normal file
49
pkg/types/rulestatehistorytypes/response.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package rulestatehistorytypes
|
||||
|
||||
import (
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
)
|
||||
|
||||
type RuleStateTimelineResponse struct {
|
||||
Items []RuleStateHistoryResponseItem `json:"items" required:"true"`
|
||||
Total uint64 `json:"total" required:"true"`
|
||||
NextCursor string `json:"nextCursor,omitempty"`
|
||||
}
|
||||
|
||||
type RuleStateHistoryResponseItem struct {
|
||||
RuleID string `json:"ruleID" required:"true"`
|
||||
RuleName string `json:"ruleName" required:"true"`
|
||||
OverallState AlertState `json:"overallState" required:"true"`
|
||||
OverallStateChanged bool `json:"overallStateChanged" required:"true"`
|
||||
State AlertState `json:"state" required:"true"`
|
||||
StateChanged bool `json:"stateChanged" required:"true"`
|
||||
UnixMilli int64 `json:"unixMilli" required:"true"`
|
||||
Labels []*qbtypes.Label `json:"labels" required:"true"`
|
||||
Fingerprint uint64 `json:"fingerprint" required:"true"`
|
||||
Value float64 `json:"value" required:"true"`
|
||||
}
|
||||
|
||||
type RuleStateHistoryContributorResponse struct {
|
||||
Fingerprint uint64 `json:"fingerprint" required:"true"`
|
||||
Labels []*qbtypes.Label `json:"labels" required:"true"`
|
||||
Count uint64 `json:"count" required:"true"`
|
||||
RelatedTracesLink string `json:"relatedTracesLink,omitempty"`
|
||||
RelatedLogsLink string `json:"relatedLogsLink,omitempty"`
|
||||
}
|
||||
|
||||
type RuleStateWindow struct {
|
||||
State AlertState `json:"state" ch:"state" required:"true"`
|
||||
Start int64 `json:"start" ch:"start" required:"true"`
|
||||
End int64 `json:"end" ch:"end" required:"true"`
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
TotalCurrentTriggers uint64 `json:"totalCurrentTriggers" required:"true"`
|
||||
TotalPastTriggers uint64 `json:"totalPastTriggers" required:"true"`
|
||||
CurrentTriggersSeries *qbtypes.TimeSeries `json:"currentTriggersSeries" required:"true" nullable:"true"`
|
||||
PastTriggersSeries *qbtypes.TimeSeries `json:"pastTriggersSeries" required:"true" nullable:"true"`
|
||||
CurrentAvgResolutionTime float64 `json:"currentAvgResolutionTime" required:"true"`
|
||||
PastAvgResolutionTime float64 `json:"pastAvgResolutionTime" required:"true"`
|
||||
CurrentAvgResolutionTimeSeries *qbtypes.TimeSeries `json:"currentAvgResolutionTimeSeries" required:"true" nullable:"true"`
|
||||
PastAvgResolutionTimeSeries *qbtypes.TimeSeries `json:"pastAvgResolutionTimeSeries" required:"true" nullable:"true"`
|
||||
}
|
||||
92
pkg/types/rulestatehistorytypes/store.go
Normal file
92
pkg/types/rulestatehistorytypes/store.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package rulestatehistorytypes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type AlertState struct {
|
||||
valuer.String
|
||||
}
|
||||
|
||||
var (
|
||||
StateInactive = AlertState{valuer.NewString("inactive")}
|
||||
StatePending = AlertState{valuer.NewString("pending")}
|
||||
StateRecovering = AlertState{valuer.NewString("recovering")}
|
||||
StateFiring = AlertState{valuer.NewString("firing")}
|
||||
StateNoData = AlertState{valuer.NewString("nodata")}
|
||||
StateDisabled = AlertState{valuer.NewString("disabled")}
|
||||
)
|
||||
|
||||
type LabelsString string
|
||||
|
||||
func (AlertState) Enum() []any {
|
||||
return []any{
|
||||
StateInactive,
|
||||
StatePending,
|
||||
StateRecovering,
|
||||
StateFiring,
|
||||
StateNoData,
|
||||
StateDisabled,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LabelsString) Scan(src any) error {
|
||||
switch data := src.(type) {
|
||||
case nil:
|
||||
*l = ""
|
||||
case string:
|
||||
*l = LabelsString(data)
|
||||
case []byte:
|
||||
*l = LabelsString(string(data))
|
||||
default:
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported labels type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LabelsString) Value() (driver.Value, error) {
|
||||
return string(l), nil
|
||||
}
|
||||
|
||||
type RuleStateHistory struct {
|
||||
RuleID string `ch:"rule_id"`
|
||||
RuleName string `ch:"rule_name"`
|
||||
|
||||
OverallState AlertState `ch:"overall_state"`
|
||||
OverallStateChanged bool `ch:"overall_state_changed"`
|
||||
|
||||
State AlertState `ch:"state"`
|
||||
StateChanged bool `ch:"state_changed"`
|
||||
UnixMilli int64 `ch:"unix_milli"`
|
||||
Labels LabelsString `ch:"labels"`
|
||||
Fingerprint uint64 `ch:"fingerprint"`
|
||||
Value float64 `ch:"value"`
|
||||
}
|
||||
|
||||
type RuleStateHistoryContributor struct {
|
||||
Fingerprint uint64 `ch:"fingerprint"`
|
||||
Labels LabelsString `ch:"labels"`
|
||||
Count uint64 `ch:"count"`
|
||||
RelatedTracesLink string
|
||||
RelatedLogsLink string
|
||||
}
|
||||
|
||||
type Store interface {
|
||||
AddRuleStateHistory(ctx context.Context, entries []RuleStateHistory) error
|
||||
GetLastSavedRuleStateHistory(ctx context.Context, ruleID string) ([]RuleStateHistory, error)
|
||||
ReadRuleStateHistoryByRuleID(ctx context.Context, ruleID string, query *Query) ([]RuleStateHistory, uint64, error)
|
||||
ReadRuleStateHistoryFilterKeysByRuleID(ctx context.Context, ruleID string, query *Query, search string, limit int64) (*telemetrytypes.GettableFieldKeys, error)
|
||||
ReadRuleStateHistoryFilterValuesByRuleID(ctx context.Context, ruleID string, key string, query *Query, search string, limit int64) (*telemetrytypes.GettableFieldValues, error)
|
||||
ReadRuleStateHistoryTopContributorsByRuleID(ctx context.Context, ruleID string, query *Query) ([]RuleStateHistoryContributor, error)
|
||||
GetOverallStateTransitions(ctx context.Context, ruleID string, query *Query) ([]RuleStateWindow, error)
|
||||
GetTotalTriggers(ctx context.Context, ruleID string, query *Query) (uint64, error)
|
||||
GetTriggersByInterval(ctx context.Context, ruleID string, query *Query) (*qbtypes.TimeSeries, error)
|
||||
GetAvgResolutionTime(ctx context.Context, ruleID string, query *Query) (float64, error)
|
||||
GetAvgResolutionTimeByInterval(ctx context.Context, ruleID string, query *Query) (*qbtypes.TimeSeries, error)
|
||||
}
|
||||
Reference in New Issue
Block a user