mirror of
https://github.com/SigNoz/signoz.git
synced 2026-10-21 01:20:40 +01:00
Compare commits
2 Commits
fix/dynami
...
feat/semco
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdefce8150 | ||
|
|
04021842f0 |
2
.github/workflows/goci.yaml
vendored
2
.github/workflows/goci.yaml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: check-semconv-generated-files
|
||||
- name: check-semconv-generated-files-and-product-literals
|
||||
run: make semconv-check
|
||||
build:
|
||||
if: |
|
||||
|
||||
4
Makefile
4
Makefile
@@ -263,8 +263,8 @@ semconv-generate: ## Regenerate semantic-convention families for Go and TypeScri
|
||||
@go run ./scripts/semconv
|
||||
|
||||
.PHONY: semconv-check
|
||||
semconv-check: ## Fail if the generated semantic-convention files are stale
|
||||
@go run ./scripts/semconv -check
|
||||
semconv-check: ## Verify generated semantic-convention files and reject old-name product literals
|
||||
@go run ./scripts/semconv -check -lint
|
||||
|
||||
.PHONY: gen-mocks
|
||||
gen-mocks:
|
||||
|
||||
@@ -7792,6 +7792,8 @@ components:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldContext'
|
||||
fieldDataType:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
|
||||
fieldResolution:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
|
||||
meta:
|
||||
properties:
|
||||
unit:
|
||||
@@ -7836,6 +7838,10 @@ components:
|
||||
rowsScanned:
|
||||
minimum: 0
|
||||
type: integer
|
||||
semconvResolutions:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5SemconvResolution'
|
||||
type: array
|
||||
stepIntervals:
|
||||
additionalProperties:
|
||||
minimum: 0
|
||||
@@ -7902,6 +7908,8 @@ components:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldContext'
|
||||
fieldDataType:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
|
||||
fieldResolution:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
|
||||
name:
|
||||
type: string
|
||||
signal:
|
||||
@@ -7989,6 +7997,8 @@ components:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldContext'
|
||||
fieldDataType:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
|
||||
fieldResolution:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
|
||||
name:
|
||||
type: string
|
||||
signal:
|
||||
@@ -8565,6 +8575,20 @@ components:
|
||||
stepInterval:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Step'
|
||||
type: object
|
||||
Querybuildertypesv5SemconvResolution:
|
||||
properties:
|
||||
current:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
members:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
requested:
|
||||
type: string
|
||||
type: object
|
||||
Querybuildertypesv5Step:
|
||||
description: Step interval. Accepts a Go duration string (e.g., "60s", "1m",
|
||||
"1h") or a number representing seconds (e.g., 60).
|
||||
@@ -10347,6 +10371,11 @@ components:
|
||||
- number
|
||||
- ""
|
||||
type: string
|
||||
TelemetrytypesFieldResolution:
|
||||
enum:
|
||||
- exact
|
||||
- ""
|
||||
type: string
|
||||
TelemetrytypesGettableFieldKeys:
|
||||
properties:
|
||||
complete:
|
||||
@@ -10372,6 +10401,42 @@ components:
|
||||
- values
|
||||
- complete
|
||||
type: object
|
||||
TelemetrytypesGettableSemconvMigrationReport:
|
||||
properties:
|
||||
endUnixMilli:
|
||||
format: int64
|
||||
type: integer
|
||||
entries:
|
||||
items:
|
||||
$ref: '#/components/schemas/TelemetrytypesSemconvMigrationReportEntry'
|
||||
nullable: true
|
||||
type: array
|
||||
startUnixMilli:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- entries
|
||||
type: object
|
||||
TelemetrytypesSemconvMigrationReportEntry:
|
||||
properties:
|
||||
current:
|
||||
type: string
|
||||
lastSeenUnixMilli:
|
||||
format: int64
|
||||
type: integer
|
||||
old:
|
||||
type: string
|
||||
resourceSets:
|
||||
minimum: 0
|
||||
type: integer
|
||||
services:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
signal:
|
||||
type: string
|
||||
type: object
|
||||
TelemetrytypesSignal:
|
||||
enum:
|
||||
- traces
|
||||
@@ -10392,6 +10457,8 @@ components:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldContext'
|
||||
fieldDataType:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
|
||||
fieldResolution:
|
||||
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
|
||||
name:
|
||||
type: string
|
||||
signal:
|
||||
@@ -12739,6 +12806,65 @@ paths:
|
||||
tags:
|
||||
- fields
|
||||
x-signoz-stability: alpha
|
||||
/api/v1/fields/semconv-migration:
|
||||
get:
|
||||
deprecated: false
|
||||
description: Returns services that still emit old semantic-convention names
|
||||
without the current family name
|
||||
operationId: GetSemconvMigrationReport
|
||||
parameters:
|
||||
- in: query
|
||||
name: startUnixMilli
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
- in: query
|
||||
name: endUnixMilli
|
||||
schema:
|
||||
format: int64
|
||||
type: integer
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/TelemetrytypesGettableSemconvMigrationReport'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"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 semantic-convention migration report
|
||||
tags:
|
||||
- fields
|
||||
x-signoz-stability: alpha
|
||||
/api/v1/fields/values:
|
||||
get:
|
||||
deprecated: false
|
||||
@@ -21261,8 +21387,8 @@ paths:
|
||||
alert: Payments-api error log rate above 1%
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: Error log rate in {{$deployment.environment}} is
|
||||
{{$value}}%
|
||||
description: Error log rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%
|
||||
summary: Payments-api error rate above {{$threshold}}%
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -21278,7 +21404,7 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -21292,14 +21418,14 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{deployment.environment}}'
|
||||
legend: '{{deployment.environment.name}}'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -21325,7 +21451,7 @@ paths:
|
||||
team: payments
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -21344,7 +21470,7 @@ paths:
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
|
||||
in {{$deployment.environment}}.'
|
||||
in {{$deployment.environment.name}}.'
|
||||
summary: Payments service panic
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -21362,8 +21488,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -21392,7 +21518,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -21462,8 +21588,9 @@ paths:
|
||||
version: v5
|
||||
metric_promql:
|
||||
description: PromQL expression instead of the builder. Dotted OTEL
|
||||
resource attributes are quoted ("deployment.environment"). Useful
|
||||
for queries that combine series with group_right or other Prom operators.
|
||||
resource attributes are quoted ("deployment.environment.name").
|
||||
Useful for queries that combine series with group_right or other
|
||||
Prom operators.
|
||||
summary: Metric threshold PromQL rule
|
||||
value:
|
||||
alert: Kafka consumer group lag above 1000
|
||||
@@ -21479,9 +21606,9 @@ paths:
|
||||
- spec:
|
||||
legend: '{{topic}}/{{partition}} ({{group}})'
|
||||
name: A
|
||||
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment") group_right
|
||||
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
|
||||
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment.name")
|
||||
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
|
||||
> 0
|
||||
type: promql
|
||||
queryType: promql
|
||||
@@ -21617,7 +21744,7 @@ paths:
|
||||
alertType: METRIC_BASED_ALERT
|
||||
annotations:
|
||||
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
|
||||
in {{$deployment.environment}}.
|
||||
in {{$deployment.environment.name}}.
|
||||
summary: Pod CPU above {{$threshold}} of request
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -21636,8 +21763,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60
|
||||
@@ -21668,7 +21795,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -21692,7 +21819,7 @@ paths:
|
||||
alert: API 5xx error rate above 1%
|
||||
alertType: TRACES_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%.'
|
||||
summary: API service error rate elevated
|
||||
condition:
|
||||
@@ -21704,7 +21831,7 @@ paths:
|
||||
- expression: count()
|
||||
disabled: true
|
||||
filter:
|
||||
expression: service.name CONTAINS 'api' AND http.status_code
|
||||
expression: service.name CONTAINS 'api' AND http.response.status_code
|
||||
>= 500
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
@@ -21712,7 +21839,7 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
@@ -21729,14 +21856,14 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{service.name}} ({{deployment.environment}})'
|
||||
legend: '{{service.name}} ({{deployment.environment.name}})'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -21764,7 +21891,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- service.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
newGroupEvalDelay: 2m
|
||||
renotify:
|
||||
alertStates:
|
||||
@@ -22213,8 +22340,8 @@ paths:
|
||||
alert: Payments-api error log rate above 1%
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: Error log rate in {{$deployment.environment}} is
|
||||
{{$value}}%
|
||||
description: Error log rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%
|
||||
summary: Payments-api error rate above {{$threshold}}%
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -22230,7 +22357,7 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -22244,14 +22371,14 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{deployment.environment}}'
|
||||
legend: '{{deployment.environment.name}}'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -22277,7 +22404,7 @@ paths:
|
||||
team: payments
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -22296,7 +22423,7 @@ paths:
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
|
||||
in {{$deployment.environment}}.'
|
||||
in {{$deployment.environment.name}}.'
|
||||
summary: Payments service panic
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -22314,8 +22441,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -22344,7 +22471,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -22414,8 +22541,9 @@ paths:
|
||||
version: v5
|
||||
metric_promql:
|
||||
description: PromQL expression instead of the builder. Dotted OTEL
|
||||
resource attributes are quoted ("deployment.environment"). Useful
|
||||
for queries that combine series with group_right or other Prom operators.
|
||||
resource attributes are quoted ("deployment.environment.name").
|
||||
Useful for queries that combine series with group_right or other
|
||||
Prom operators.
|
||||
summary: Metric threshold PromQL rule
|
||||
value:
|
||||
alert: Kafka consumer group lag above 1000
|
||||
@@ -22431,9 +22559,9 @@ paths:
|
||||
- spec:
|
||||
legend: '{{topic}}/{{partition}} ({{group}})'
|
||||
name: A
|
||||
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment") group_right
|
||||
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
|
||||
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment.name")
|
||||
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
|
||||
> 0
|
||||
type: promql
|
||||
queryType: promql
|
||||
@@ -22569,7 +22697,7 @@ paths:
|
||||
alertType: METRIC_BASED_ALERT
|
||||
annotations:
|
||||
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
|
||||
in {{$deployment.environment}}.
|
||||
in {{$deployment.environment.name}}.
|
||||
summary: Pod CPU above {{$threshold}} of request
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -22588,8 +22716,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60
|
||||
@@ -22620,7 +22748,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -22644,7 +22772,7 @@ paths:
|
||||
alert: API 5xx error rate above 1%
|
||||
alertType: TRACES_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%.'
|
||||
summary: API service error rate elevated
|
||||
condition:
|
||||
@@ -22656,7 +22784,7 @@ paths:
|
||||
- expression: count()
|
||||
disabled: true
|
||||
filter:
|
||||
expression: service.name CONTAINS 'api' AND http.status_code
|
||||
expression: service.name CONTAINS 'api' AND http.response.status_code
|
||||
>= 500
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
@@ -22664,7 +22792,7 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
@@ -22681,14 +22809,14 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{service.name}} ({{deployment.environment}})'
|
||||
legend: '{{service.name}} ({{deployment.environment.name}})'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -22716,7 +22844,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- service.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
newGroupEvalDelay: 2m
|
||||
renotify:
|
||||
alertStates:
|
||||
@@ -23066,8 +23194,8 @@ paths:
|
||||
alert: Payments-api error log rate above 1%
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: Error log rate in {{$deployment.environment}} is
|
||||
{{$value}}%
|
||||
description: Error log rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%
|
||||
summary: Payments-api error rate above {{$threshold}}%
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -23083,7 +23211,7 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -23097,14 +23225,14 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{deployment.environment}}'
|
||||
legend: '{{deployment.environment.name}}'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -23130,7 +23258,7 @@ paths:
|
||||
team: payments
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -23149,7 +23277,7 @@ paths:
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
|
||||
in {{$deployment.environment}}.'
|
||||
in {{$deployment.environment.name}}.'
|
||||
summary: Payments service panic
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -23167,8 +23295,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -23197,7 +23325,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -23267,8 +23395,9 @@ paths:
|
||||
version: v5
|
||||
metric_promql:
|
||||
description: PromQL expression instead of the builder. Dotted OTEL
|
||||
resource attributes are quoted ("deployment.environment"). Useful
|
||||
for queries that combine series with group_right or other Prom operators.
|
||||
resource attributes are quoted ("deployment.environment.name").
|
||||
Useful for queries that combine series with group_right or other
|
||||
Prom operators.
|
||||
summary: Metric threshold PromQL rule
|
||||
value:
|
||||
alert: Kafka consumer group lag above 1000
|
||||
@@ -23284,9 +23413,9 @@ paths:
|
||||
- spec:
|
||||
legend: '{{topic}}/{{partition}} ({{group}})'
|
||||
name: A
|
||||
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment") group_right
|
||||
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
|
||||
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment.name")
|
||||
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
|
||||
> 0
|
||||
type: promql
|
||||
queryType: promql
|
||||
@@ -23422,7 +23551,7 @@ paths:
|
||||
alertType: METRIC_BASED_ALERT
|
||||
annotations:
|
||||
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
|
||||
in {{$deployment.environment}}.
|
||||
in {{$deployment.environment.name}}.
|
||||
summary: Pod CPU above {{$threshold}} of request
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -23441,8 +23570,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60
|
||||
@@ -23473,7 +23602,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -23497,7 +23626,7 @@ paths:
|
||||
alert: API 5xx error rate above 1%
|
||||
alertType: TRACES_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%.'
|
||||
summary: API service error rate elevated
|
||||
condition:
|
||||
@@ -23509,7 +23638,7 @@ paths:
|
||||
- expression: count()
|
||||
disabled: true
|
||||
filter:
|
||||
expression: service.name CONTAINS 'api' AND http.status_code
|
||||
expression: service.name CONTAINS 'api' AND http.response.status_code
|
||||
>= 500
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
@@ -23517,7 +23646,7 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
@@ -23534,14 +23663,14 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{service.name}} ({{deployment.environment}})'
|
||||
legend: '{{service.name}} ({{deployment.environment.name}})'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -23569,7 +23698,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- service.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
newGroupEvalDelay: 2m
|
||||
renotify:
|
||||
alertStates:
|
||||
@@ -24428,8 +24557,8 @@ paths:
|
||||
alert: Payments-api error log rate above 1%
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: Error log rate in {{$deployment.environment}} is
|
||||
{{$value}}%
|
||||
description: Error log rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%
|
||||
summary: Payments-api error rate above {{$threshold}}%
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -24445,7 +24574,7 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -24459,14 +24588,14 @@ paths:
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{deployment.environment}}'
|
||||
legend: '{{deployment.environment.name}}'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -24492,7 +24621,7 @@ paths:
|
||||
team: payments
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -24511,7 +24640,7 @@ paths:
|
||||
alertType: LOGS_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$k8s.pod.name}} emitted {{$value}} panic log(s)
|
||||
in {{$deployment.environment}}.'
|
||||
in {{$deployment.environment.name}}.'
|
||||
summary: Payments service panic
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -24529,8 +24658,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: logs
|
||||
stepInterval: 60
|
||||
@@ -24559,7 +24688,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -24629,8 +24758,9 @@ paths:
|
||||
version: v5
|
||||
metric_promql:
|
||||
description: PromQL expression instead of the builder. Dotted OTEL
|
||||
resource attributes are quoted ("deployment.environment"). Useful
|
||||
for queries that combine series with group_right or other Prom operators.
|
||||
resource attributes are quoted ("deployment.environment.name").
|
||||
Useful for queries that combine series with group_right or other
|
||||
Prom operators.
|
||||
summary: Metric threshold PromQL rule
|
||||
value:
|
||||
alert: Kafka consumer group lag above 1000
|
||||
@@ -24646,9 +24776,9 @@ paths:
|
||||
- spec:
|
||||
legend: '{{topic}}/{{partition}} ({{group}})'
|
||||
name: A
|
||||
query: (max by(topic, partition, "deployment.environment")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment") group_right
|
||||
max by(group, topic, partition, "deployment.environment")(kafka_consumer_committed_offset))
|
||||
query: (max by(topic, partition, "deployment.environment.name")(kafka_log_end_offset)
|
||||
- on(topic, partition, "deployment.environment.name")
|
||||
group_right max by(group, topic, partition, "deployment.environment.name")(kafka_consumer_committed_offset))
|
||||
> 0
|
||||
type: promql
|
||||
queryType: promql
|
||||
@@ -24784,7 +24914,7 @@ paths:
|
||||
alertType: METRIC_BASED_ALERT
|
||||
annotations:
|
||||
description: Pod {{$k8s.pod.name}} CPU is at {{$value}} of request
|
||||
in {{$deployment.environment}}.
|
||||
in {{$deployment.environment.name}}.
|
||||
summary: Pod CPU above {{$threshold}} of request
|
||||
condition:
|
||||
compositeQuery:
|
||||
@@ -24803,8 +24933,8 @@ paths:
|
||||
name: k8s.pod.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment}})'
|
||||
name: deployment.environment.name
|
||||
legend: '{{k8s.pod.name}} ({{deployment.environment.name}})'
|
||||
name: A
|
||||
signal: metrics
|
||||
stepInterval: 60
|
||||
@@ -24835,7 +24965,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- k8s.pod.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
renotify:
|
||||
alertStates:
|
||||
- firing
|
||||
@@ -24859,7 +24989,7 @@ paths:
|
||||
alert: API 5xx error rate above 1%
|
||||
alertType: TRACES_BASED_ALERT
|
||||
annotations:
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment}}
|
||||
description: '{{$service.name}} 5xx rate in {{$deployment.environment.name}}
|
||||
is {{$value}}%.'
|
||||
summary: API service error rate elevated
|
||||
condition:
|
||||
@@ -24871,7 +25001,7 @@ paths:
|
||||
- expression: count()
|
||||
disabled: true
|
||||
filter:
|
||||
expression: service.name CONTAINS 'api' AND http.status_code
|
||||
expression: service.name CONTAINS 'api' AND http.response.status_code
|
||||
>= 500
|
||||
groupBy:
|
||||
- fieldContext: resource
|
||||
@@ -24879,7 +25009,7 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: A
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
@@ -24896,14 +25026,14 @@ paths:
|
||||
name: service.name
|
||||
- fieldContext: resource
|
||||
fieldDataType: string
|
||||
name: deployment.environment
|
||||
name: deployment.environment.name
|
||||
name: B
|
||||
signal: traces
|
||||
stepInterval: 60
|
||||
type: builder_query
|
||||
- spec:
|
||||
expression: (A / B) * 100
|
||||
legend: '{{service.name}} ({{deployment.environment}})'
|
||||
legend: '{{service.name}} ({{deployment.environment.name}})'
|
||||
name: F1
|
||||
type: builder_formula
|
||||
queryType: builder
|
||||
@@ -24931,7 +25061,7 @@ paths:
|
||||
notificationSettings:
|
||||
groupBy:
|
||||
- service.name
|
||||
- deployment.environment
|
||||
- deployment.environment.name
|
||||
newGroupEvalDelay: 2m
|
||||
renotify:
|
||||
alertStates:
|
||||
|
||||
@@ -131,7 +131,7 @@ A `LogicalField` is one meaning: one name, context, and data type, backed by one
|
||||
The resolution order is the same for every storage, in the filter and in every select field, group by, order by, and aggregation:
|
||||
|
||||
1. **Own context.** A key under one of the storage's own contexts (`span.x`, `log.x`) matches its own context first, then a column the storage knows under that context. Only when both miss does it look up as if it had no context. `span.http.method` then corrects to the attribute. Strict contexts (`resource.`, `attribute.`, `scope.`, `body.`) are kept as written.
|
||||
2. **Matches.** The metadata keys under the key's spellings, grouped into families when the flag is on. Each combination of context and data type is one interpretation.
|
||||
2. **Matches.** The metadata keys under the key's spellings, grouped into families when the flag is on. Each combination of context and data type is one interpretation. A key written as `exact(key)` in an expression, or a structured key with `fieldResolution: exact`, reads its own spelling alone.
|
||||
3. **Ambiguity.** A filter settles several interpretations by resource over attribute, with a warning. A select field, group by, order by, or aggregation keeps every interpretation in metadata order and folds them, so it shows the value wherever it is.
|
||||
4. **Intrinsic column first**, bare keys only. A column every row has leads the list. Metadata can report the column, or the storage's `Fallback` can. When only `Fallback` knows the column, its key carries the data type the column reads as (`querybuilder.ColumnDataType`). A same-named metadata key of a contradicting type then drops, and a time column merges none. When metadata reports the column too, every match reads. A metadata gap degrades to the correct column, never to a corrupt metadata key.
|
||||
5. **Fallback.** With no match, the storage's fallback keys for the key. When the storage ignores unknown keys (a side query whose main query owns the error), the key is `Skipped`. Otherwise a key nothing can serve is an error with suggestions. The not-found warning fires only when every fallback key is a guess, that is, none of them is always present.
|
||||
|
||||
9
frontend/src/api/semconv/getMigrationReport.ts
Normal file
9
frontend/src/api/semconv/getMigrationReport.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import axios from 'api';
|
||||
import { SemconvMigrationReport } from 'types/api/semconvMigration';
|
||||
|
||||
async function getSemconvMigrationReport(): Promise<SemconvMigrationReport> {
|
||||
const response = await axios.get('/fields/semconv-migration');
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export default getSemconvMigrationReport;
|
||||
@@ -64,12 +64,12 @@ export const COMMON_FILTERS = {
|
||||
SERVER_SPANS: "kind_string = 'Server'",
|
||||
CLIENT_SPANS: "kind_string = 'Client'",
|
||||
INTERNAL_SPANS: "kind_string = 'Internal'",
|
||||
ERROR_SPANS: 'http.status_code >= 400',
|
||||
SUCCESS_SPANS: 'http.status_code < 400',
|
||||
ERROR_SPANS: 'http.response.status_code >= 400',
|
||||
SUCCESS_SPANS: 'http.response.status_code < 400',
|
||||
|
||||
// Common service filters
|
||||
EXCLUDE_HEALTH_CHECKS: "http.route != '/health' AND http.route != '/ping'",
|
||||
HTTP_REQUESTS: "http.method != ''",
|
||||
HTTP_REQUESTS: "http.request.method != ''",
|
||||
|
||||
// Log filters
|
||||
ERROR_LOGS: "severity_text = 'ERROR'",
|
||||
@@ -87,7 +87,7 @@ export const COMMON_GROUP_BY_FIELDS = {
|
||||
fieldContext: 'resource' as const,
|
||||
},
|
||||
HTTP_METHOD: {
|
||||
name: 'http.method',
|
||||
name: 'http.request.method',
|
||||
fieldDataType: 'string' as const,
|
||||
fieldContext: 'attribute' as const,
|
||||
},
|
||||
@@ -97,7 +97,7 @@ export const COMMON_GROUP_BY_FIELDS = {
|
||||
fieldContext: 'attribute' as const,
|
||||
},
|
||||
HTTP_STATUS_CODE: {
|
||||
name: 'http.status_code',
|
||||
name: 'http.response.status_code',
|
||||
fieldDataType: 'int64' as const,
|
||||
fieldContext: 'attribute' as const,
|
||||
},
|
||||
|
||||
@@ -145,7 +145,7 @@ function CeleryOverviewConfigOptions(): JSX.Element {
|
||||
{
|
||||
placeholder: 'Destination',
|
||||
queryParam: QueryParams.destination,
|
||||
filterType: ['messaging.destination.name', 'messaging.destination'],
|
||||
filterType: ['messaging.destination.name'],
|
||||
},
|
||||
{
|
||||
placeholder: 'Kind',
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Badge, BadgeColor } from '@signozhq/ui/badge';
|
||||
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
|
||||
import { getLogIndicatorType } from 'components/Logs/LogStateIndicator/utils';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { getSemconvMembers } from 'utils/semconv';
|
||||
|
||||
import styles from './LogHighlights.module.scss';
|
||||
import TraceIdField from './TraceIdField';
|
||||
@@ -79,7 +80,9 @@ export const LOG_HIGHLIGHTS: LogHighlightConfig[] = [
|
||||
key: 'environment',
|
||||
label: 'ENVIRONMENT',
|
||||
render: (log): ReactNode | null => {
|
||||
const value = getAttr(log, 'deployment.environment');
|
||||
const value = getSemconvMembers('deployment.environment.name')
|
||||
.map((key) => getAttr(log, key))
|
||||
.find(Boolean);
|
||||
return value ? valueBadge(value) : null;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1758,7 +1758,7 @@ QuerySearch.defaultProps = {
|
||||
signalSource: '',
|
||||
hardcodedAttributeKeys: undefined,
|
||||
placeholder:
|
||||
"Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')",
|
||||
"Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')",
|
||||
showFilterSuggestionsWithoutMetric: false,
|
||||
initialExpression: undefined,
|
||||
};
|
||||
|
||||
@@ -995,6 +995,16 @@ describe('removeKeysFromExpression', () => {
|
||||
expect(result).toBe("status = 'success'");
|
||||
});
|
||||
|
||||
it('should remove a comparison that uses the exact field wrapper', () => {
|
||||
const expression =
|
||||
"exact(resource.deployment.environment) EXISTS AND service.name = 'api-gateway'";
|
||||
const result = removeKeysFromExpression(expression, [
|
||||
'resource.deployment.environment',
|
||||
]);
|
||||
|
||||
expect(result).toBe("service.name = 'api-gateway'");
|
||||
});
|
||||
|
||||
it('should remove multiple keys from expression', () => {
|
||||
const expression =
|
||||
"service.name = 'api-gateway' AND status = 'success' AND region = 'us-east-1'";
|
||||
|
||||
@@ -687,7 +687,16 @@ export const removeKeysFromExpression = (
|
||||
}
|
||||
|
||||
function visitComparison(ctx: ComparisonContext): string | null {
|
||||
const keyText = ctx.key().getText().trim().toLowerCase();
|
||||
const field = ctx.field();
|
||||
// The runtime returns null for the inactive field alternative even though
|
||||
// the generated TypeScript signature is non-nullable.
|
||||
const exactCall = field.exactCall() as unknown as ReturnType<
|
||||
typeof field.exactCall
|
||||
> | null;
|
||||
const keyText = (exactCall ? exactCall.key() : field.key())
|
||||
.getText()
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
if (!keysSet.has(keyText)) {
|
||||
return src(ctx);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ArrowUpRight } from '@signozhq/icons';
|
||||
|
||||
const QUICK_FILTER_DOC_PATHS: Record<string, string> = {
|
||||
severity_text: 'severity-text',
|
||||
'deployment.environment': 'environment',
|
||||
'deployment.environment.name': 'environment',
|
||||
'service.name': 'service-name',
|
||||
'host.name': 'hostname',
|
||||
'k8s.cluster.name': 'k8s-cluster-name',
|
||||
|
||||
32
frontend/src/components/Semconv/SemconvEditorWarning.tsx
Normal file
32
frontend/src/components/Semconv/SemconvEditorWarning.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Alert } from 'antd';
|
||||
import { findOldSemconvNames } from 'utils/semconv';
|
||||
|
||||
interface SemconvEditorWarningProps {
|
||||
value: unknown;
|
||||
editor: string;
|
||||
}
|
||||
|
||||
function SemconvEditorWarning({
|
||||
value,
|
||||
editor,
|
||||
}: SemconvEditorWarningProps): JSX.Element | null {
|
||||
const text = typeof value === 'string' ? value : JSON.stringify(value ?? '');
|
||||
const renames = findOldSemconvNames(text);
|
||||
if (renames.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
data-testid="semconv-editor-warning"
|
||||
message={`${editor} contains renamed OpenTelemetry fields`}
|
||||
description={renames
|
||||
.map(({ old, current }) => `${old} → ${current}`)
|
||||
.join(', ')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SemconvEditorWarning;
|
||||
23
frontend/src/components/Semconv/SemconvOldNameBadge.tsx
Normal file
23
frontend/src/components/Semconv/SemconvOldNameBadge.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { getSemconvRename } from 'utils/semconv';
|
||||
|
||||
interface SemconvOldNameBadgeProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
function SemconvOldNameBadge({
|
||||
name,
|
||||
}: SemconvOldNameBadgeProps): JSX.Element | null {
|
||||
const rename = getSemconvRename(name);
|
||||
if (!rename || rename.family.kind !== 'attribute') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge color="amber" variant="outline" data-testid="semconv-old-name-badge">
|
||||
old name, renamed to {rename.current}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
export default SemconvOldNameBadge;
|
||||
39
frontend/src/components/Semconv/__tests__/Semconv.test.tsx
Normal file
39
frontend/src/components/Semconv/__tests__/Semconv.test.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import SemconvEditorWarning from '../SemconvEditorWarning';
|
||||
import SemconvOldNameBadge from '../SemconvOldNameBadge';
|
||||
|
||||
describe('semantic convention product hints', () => {
|
||||
it('badges an old raw attribute with its current name', () => {
|
||||
render(<SemconvOldNameBadge name="deployment.environment" />);
|
||||
|
||||
expect(screen.getByTestId('semconv-old-name-badge')).toHaveTextContent(
|
||||
'old name, renamed to deployment.environment.name',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not badge a current raw attribute', () => {
|
||||
render(<SemconvOldNameBadge name="deployment.environment.name" />);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('semconv-old-name-badge'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows an informational editor warning without disabling the editor', () => {
|
||||
render(
|
||||
<>
|
||||
<input aria-label="query" defaultValue="deployment.environment = 'prod'" />
|
||||
<SemconvEditorWarning
|
||||
value="deployment.environment = 'prod'"
|
||||
editor="ClickHouse SQL"
|
||||
/>
|
||||
</>,
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText('query')).not.toBeDisabled();
|
||||
expect(screen.getByTestId('semconv-editor-warning')).toHaveTextContent(
|
||||
'deployment.environment → deployment.environment.name',
|
||||
);
|
||||
});
|
||||
});
|
||||
2
frontend/src/components/Semconv/index.ts
Normal file
2
frontend/src/components/Semconv/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as SemconvEditorWarning } from './SemconvEditorWarning';
|
||||
export { default as SemconvOldNameBadge } from './SemconvOldNameBadge';
|
||||
@@ -19,6 +19,51 @@ export type SemconvFamily = {
|
||||
};
|
||||
|
||||
export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
|
||||
{
|
||||
current: 'code.file.path',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'code.filepath',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'code.function.name',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'code.function',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'code.line.number',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'code.lineno',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'container.cpu.usage',
|
||||
kind: 'metric',
|
||||
@@ -34,6 +79,84 @@ export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'container.runtime.name',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'container.runtime',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'db.namespace',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'db.elasticsearch.cluster.name',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'db.name',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'db.cassandra.keyspace',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'db.hbase.namespace',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'db.operation.name',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'db.operation',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'db.query.text',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'db.statement',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'deployment.environment.name',
|
||||
kind: 'attribute',
|
||||
@@ -49,6 +172,36 @@ export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
|
||||
signals: ['logs', 'metrics', 'traces'],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'http.request.method',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'http.method',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'http.response.status_code',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'http.status_code',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'k8s.node.cpu.usage',
|
||||
kind: 'metric',
|
||||
@@ -79,4 +232,175 @@ export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.client.id',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'messaging.client_id',
|
||||
contexts: ['attribute'],
|
||||
signals: ['metrics', 'traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'messaging.kafka.client_id',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'messaging.rocketmq.client_id',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.consumer.group.name',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'messaging.eventhubs.consumer.group',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'messaging.kafka.consumer.group',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'messaging.rocketmq.client_group',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'messaging.kafka.consumer_group',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.destination.name',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'messaging.destination',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'messaging.operation.type',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'messaging.operation',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'rpc.system.name',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'rpc.system',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'service.peer.name',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'peer.service',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'url.full',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'http.url',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'url.scheme',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'http.scheme',
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: [],
|
||||
signals: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'user_agent.original',
|
||||
kind: 'attribute',
|
||||
members: [
|
||||
{
|
||||
name: 'browser.user_agent',
|
||||
contexts: ['resource'],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
{
|
||||
name: 'http.user_agent',
|
||||
contexts: ['attribute'],
|
||||
signals: ['traces'],
|
||||
applyToMetrics: [],
|
||||
},
|
||||
],
|
||||
contexts: ['attribute'],
|
||||
signals: ['logs', 'traces'],
|
||||
valueMap: {},
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -10,7 +10,7 @@ export const AllTraceFilterKeyValue: Record<string, string> = {
|
||||
durationNano: 'Duration',
|
||||
duration_nano: 'Duration',
|
||||
durationNanoMax: 'Duration',
|
||||
'deployment.environment': 'Environment',
|
||||
'deployment.environment.name': 'Environment',
|
||||
hasError: 'Status',
|
||||
has_error: 'Status',
|
||||
serviceName: 'Service Name',
|
||||
@@ -55,11 +55,11 @@ export const traceFilterKeys: Record<AllTraceFilterKeys, BaseAutocompleteData> =
|
||||
id: 'serviceName--string--tag--true',
|
||||
},
|
||||
|
||||
'deployment.environment': {
|
||||
key: 'deployment.environment',
|
||||
'deployment.environment.name': {
|
||||
key: 'deployment.environment.name',
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
id: 'deployment.environment--string--resource--false',
|
||||
id: 'deployment.environment.name--string--resource--false',
|
||||
},
|
||||
name: {
|
||||
key: 'name',
|
||||
|
||||
@@ -155,7 +155,7 @@ function DomainList(): JSX.Element {
|
||||
dataSource={DataSource.TRACES}
|
||||
queryData={query}
|
||||
onChange={handleSearchChange}
|
||||
placeholder="Enter your filter query (e.g., deployment.environment = 'otel-demo' AND service.name = 'frontend')"
|
||||
placeholder="Enter your filter query (e.g., deployment.environment.name = 'otel-demo' AND service.name = 'frontend')"
|
||||
hardcodedAttributeKeys={ApiMonitoringHardcodedAttributeKeys}
|
||||
/>
|
||||
</div>
|
||||
@@ -180,9 +180,8 @@ function DomainList(): JSX.Element {
|
||||
</div>
|
||||
<div className="no-domain-subtitle">
|
||||
Ensure all HTTP client spans are being sent with kind as{' '}
|
||||
<span className="attribute">Client</span> and url set in{' '}
|
||||
<span className="attribute">url.full</span> or{' '}
|
||||
<span className="attribute">http.url</span> attribute.
|
||||
<span className="attribute">Client</span> and the URL set in the{' '}
|
||||
<span className="attribute">url.full</span> attribute.
|
||||
</div>
|
||||
<a
|
||||
href={DOCLINKS.EXTERNAL_API_MONITORING}
|
||||
|
||||
@@ -6,9 +6,9 @@ import { SPAN_ATTRIBUTES } from './Explorer/Domains/DomainDetails/constants';
|
||||
export const ApiMonitoringHardcodedAttributeKeys: QueryKeyDataSuggestionsProps[] =
|
||||
[
|
||||
{
|
||||
label: 'deployment.environment',
|
||||
label: 'deployment.environment.name',
|
||||
type: 'resource',
|
||||
name: 'deployment.environment',
|
||||
name: 'deployment.environment.name',
|
||||
signal: 'traces',
|
||||
fieldDataType: QUERY_BUILDER_KEY_TYPES.STRING,
|
||||
},
|
||||
|
||||
@@ -87,7 +87,7 @@ export const ApiMonitoringQuickFiltersConfig: IQuickFiltersConfig[] = [
|
||||
title: 'Environment',
|
||||
|
||||
attributeKey: {
|
||||
key: 'deployment.environment',
|
||||
key: 'deployment.environment.name',
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { FormatTimezoneAdjustedTimestamp } from 'hooks/useTimezoneFormatter/useTimezoneFormatter';
|
||||
import { getSemconvMembers } from 'utils/semconv';
|
||||
import styles from './traceListColumns.module.scss';
|
||||
|
||||
const keyToLabelMap: Record<string, string> = {
|
||||
@@ -27,7 +28,12 @@ const keyToLabelMap: Record<string, string> = {
|
||||
const keyAliases: Record<string, string[]> = {
|
||||
serviceName: ['serviceName', 'service.name', 'service_name'],
|
||||
durationNano: ['durationNano', 'duration.nano', 'duration_nano'],
|
||||
httpMethod: ['httpMethod', 'http.method', 'http_method'],
|
||||
httpMethod: [
|
||||
'httpMethod',
|
||||
...getSemconvMembers('http.request.method'),
|
||||
'http_request_method',
|
||||
'http_method',
|
||||
],
|
||||
responseStatusCode: [
|
||||
'response_status_code',
|
||||
'response.status.code',
|
||||
|
||||
@@ -128,7 +128,7 @@ export const INFRA_MONITORING_ATTR_KEYS = {
|
||||
K8S_OBJECT_NAME: 'k8s.object.name',
|
||||
|
||||
// Environment
|
||||
DEPLOYMENT_ENVIRONMENT: 'deployment.environment',
|
||||
DEPLOYMENT_ENVIRONMENT: 'deployment.environment.name',
|
||||
|
||||
// Host System
|
||||
OS_TYPE: 'os.type',
|
||||
@@ -907,7 +907,7 @@ export const ENTITY_FILTER_PLACEHOLDERS: Record<InfraMonitoringEntity, string> =
|
||||
[InfraMonitoringEntity.NAMESPACES]:
|
||||
"Enter your filter query (e.g., k8s.namespace.name = 'production' AND k8s.cluster.name = 'prod-cluster')",
|
||||
[InfraMonitoringEntity.CLUSTERS]:
|
||||
"Enter your filter query (e.g., k8s.cluster.name = 'prod-cluster' AND deployment.environment = 'production')",
|
||||
"Enter your filter query (e.g., k8s.cluster.name = 'prod-cluster' AND deployment.environment.name = 'production')",
|
||||
[InfraMonitoringEntity.DEPLOYMENTS]:
|
||||
"Enter your filter query (e.g., k8s.deployment.name = 'api-server' AND k8s.namespace.name = 'production')",
|
||||
[InfraMonitoringEntity.STATEFULSETS]:
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
.semconv-migration-report {
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.ant-table-wrapper {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.ingestion-key-container {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
|
||||
@@ -5,6 +5,8 @@ import getIngestionData from 'api/settings/getIngestionData';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { IngestionDataType } from 'types/api/settings/ingestion';
|
||||
|
||||
import SemconvMigrationReport from './SemconvMigrationReport';
|
||||
|
||||
import './IngestionSettings.styles.scss';
|
||||
|
||||
export default function IngestionSettings(): JSX.Element {
|
||||
@@ -84,6 +86,7 @@ export default function IngestionSettings(): JSX.Element {
|
||||
dataSource={data}
|
||||
bordered
|
||||
/>
|
||||
<SemconvMigrationReport />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ import { MeterAggregateOperator } from 'types/common/queryBuilder';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { getDaysUntilExpiry } from 'utils/timeUtils';
|
||||
|
||||
import SemconvMigrationReport from './SemconvMigrationReport';
|
||||
|
||||
import './IngestionSettings.styles.scss';
|
||||
|
||||
const { Option } = Select;
|
||||
@@ -1705,6 +1707,7 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
}}
|
||||
className="ingestion-keys-table"
|
||||
/>
|
||||
<SemconvMigrationReport />
|
||||
</div>
|
||||
|
||||
{/* Delete Key Modal */}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { useQuery } from 'react-query';
|
||||
import { Alert, Table, TableColumnsType } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import getSemconvMigrationReport from 'api/semconv/getMigrationReport';
|
||||
import dayjs from 'dayjs';
|
||||
import { SemconvMigrationReportEntry } from 'types/api/semconvMigration';
|
||||
|
||||
function SemconvMigrationReport(): JSX.Element {
|
||||
const { data, isLoading, isError } = useQuery({
|
||||
queryKey: ['semconv-migration-report'],
|
||||
queryFn: getSemconvMigrationReport,
|
||||
});
|
||||
|
||||
const columns: TableColumnsType<SemconvMigrationReportEntry> = [
|
||||
{
|
||||
title: 'Old name',
|
||||
dataIndex: 'old',
|
||||
key: 'old',
|
||||
},
|
||||
{
|
||||
title: 'Current name',
|
||||
dataIndex: 'current',
|
||||
key: 'current',
|
||||
},
|
||||
{
|
||||
title: 'Signal',
|
||||
dataIndex: 'signal',
|
||||
key: 'signal',
|
||||
},
|
||||
{
|
||||
title: 'Services still sending only the old name',
|
||||
dataIndex: 'services',
|
||||
key: 'services',
|
||||
render: (services: string[]): string => services.join(', '),
|
||||
},
|
||||
{
|
||||
title: 'Last seen',
|
||||
dataIndex: 'lastSeenUnixMilli',
|
||||
key: 'lastSeenUnixMilli',
|
||||
render: (value: number): string =>
|
||||
dayjs(value).format('YYYY-MM-DD HH:mm:ss'),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="semconv-migration-report">
|
||||
<Typography.Title level={4}>Semantic convention migration</Typography.Title>
|
||||
<Typography.Text>
|
||||
Services in this report sent an old OpenTelemetry field during the last 24
|
||||
hours without sending its current replacement. Update their SDK or
|
||||
instrumentation when practical; SigNoz queries remain backward compatible.
|
||||
</Typography.Text>
|
||||
{isError && (
|
||||
<Alert
|
||||
type="error"
|
||||
showIcon
|
||||
message="Could not load the semantic convention migration report"
|
||||
/>
|
||||
)}
|
||||
<Table
|
||||
loading={isLoading}
|
||||
columns={columns}
|
||||
dataSource={data?.entries ?? []}
|
||||
rowKey={(entry): string => `${entry.current}-${entry.old}-${entry.signal}`}
|
||||
pagination={false}
|
||||
locale={{ emptyText: 'No old-only services found in the last 24 hours' }}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default SemconvMigrationReport;
|
||||
@@ -18,7 +18,7 @@ export const SAMPLE_SPAN_JSON = `{
|
||||
},
|
||||
"resource": {
|
||||
"service.name": "llm-gateway",
|
||||
"deployment.environment": "production"
|
||||
"deployment.environment.name": "production"
|
||||
}
|
||||
}`;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getSemconvMembers } from 'utils/semconv';
|
||||
|
||||
// Field-name allowlists that drive signal-specific cell rendering. Both legacy
|
||||
// camelCase and snake_case variants are listed because the API has shipped both.
|
||||
|
||||
@@ -12,12 +14,10 @@ export const TIMESTAMP_FIELD_NAMES = new Set([
|
||||
export const STATUS_FIELD_NAMES = new Set([
|
||||
'httpMethod',
|
||||
'http_method',
|
||||
'http.method',
|
||||
'http.request.method',
|
||||
...getSemconvMembers('http.request.method'),
|
||||
'responseStatusCode',
|
||||
'response_status_code',
|
||||
'http.status_code',
|
||||
'http.response.status_code',
|
||||
...getSemconvMembers('http.response.status_code'),
|
||||
]);
|
||||
|
||||
// trace_/max_llm_duration_nano are trace-level durations the per-trace query computes.
|
||||
|
||||
@@ -74,7 +74,8 @@ export function tracesRunQueryAction(
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'Attribute key, e.g. service.name, http.status_code',
|
||||
description:
|
||||
'Attribute key, e.g. service.name, http.response.status_code',
|
||||
},
|
||||
op: {
|
||||
type: 'string',
|
||||
@@ -143,7 +144,7 @@ export function tracesAddFilterAction(
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'Attribute key, e.g. service.name, http.status_code',
|
||||
description: 'Attribute key, e.g. service.name, http.response.status_code',
|
||||
},
|
||||
op: {
|
||||
type: 'string',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Divider } from '@signozhq/ui/divider';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { SemconvOldNameBadge } from 'components/Semconv';
|
||||
|
||||
import { TagContainer, TagLabel, TagValue } from './FieldRenderer.styles';
|
||||
import { FieldRendererProps } from './LogDetailedView.types';
|
||||
@@ -28,6 +29,7 @@ function FieldRenderer({ field }: FieldRendererProps): JSX.Element {
|
||||
<Typography.Text truncate={1} className="label">
|
||||
{newField}{' '}
|
||||
</Typography.Text>
|
||||
<SemconvOldNameBadge name={newField} />
|
||||
</TooltipSimple>
|
||||
|
||||
<div className="tags">
|
||||
@@ -47,7 +49,10 @@ function FieldRenderer({ field }: FieldRendererProps): JSX.Element {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<span className="label">{field}</span>
|
||||
<>
|
||||
<span className="label">{field}</span>
|
||||
<SemconvOldNameBadge name={field} />
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -164,7 +164,9 @@ describe('useInitialQuery - Priority-Based Resource Filtering', () => {
|
||||
value: 'frontend-service',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
key: expect.objectContaining({ key: 'deployment.environment' }),
|
||||
key: expect.objectContaining({
|
||||
key: 'deployment.environment.name',
|
||||
}),
|
||||
value: 'production',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
@@ -286,7 +288,9 @@ describe('useInitialQuery - Priority-Based Resource Filtering', () => {
|
||||
value: 'legacy-app',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
key: expect.objectContaining({ key: 'deployment.environment' }),
|
||||
key: expect.objectContaining({
|
||||
key: 'deployment.environment.name',
|
||||
}),
|
||||
value: 'production',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -6,13 +6,14 @@ import {
|
||||
TagFilterItem,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { getSemconvRename } from 'utils/semconv';
|
||||
|
||||
const FALLBACK_STARTS_WITH_REGEX = /^(k8s|cloud|host|deployment)/; // regex to filter out resources that start with the specified keywords
|
||||
const FALLBACK_CONTAINS_REGEX = /(env|service|file|container|tenant)/; // regex to filter out resources that contains the specified keywords
|
||||
|
||||
// Priority categories for filter selection
|
||||
// Strategy:
|
||||
// - Always include: service.name, deployment.environment, env, environment
|
||||
// - Always include: service.name, deployment.environment.name, env, environment
|
||||
// - Select ONE category only: stops at the first category with a matching attribute
|
||||
// - Within category: picks the first available attribute by order
|
||||
// - Order (highest to lowest priority): Kubernetes > Cloud > Host > Container
|
||||
@@ -26,27 +27,36 @@ const PRIORITY_CATEGORIES = [
|
||||
|
||||
const SERVICE_AND_ENVIRONMENT_KEYS = [
|
||||
'service.name',
|
||||
'deployment.environment',
|
||||
'deployment.environment.name',
|
||||
'env',
|
||||
'environment',
|
||||
];
|
||||
|
||||
export const getFiltersFromResources = (
|
||||
resources: ILog['resources_string'],
|
||||
): TagFilterItem[] =>
|
||||
Object.keys(resources).map((key: string) => {
|
||||
): TagFilterItem[] => {
|
||||
const items = new Map<string, TagFilterItem>();
|
||||
Object.keys(resources).forEach((key: string) => {
|
||||
const currentKey = getSemconvRename(key)?.current ?? key;
|
||||
const resourceValue = resources[key] as string;
|
||||
return {
|
||||
const item = {
|
||||
id: uuid(),
|
||||
key: {
|
||||
key,
|
||||
key: currentKey,
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
},
|
||||
op: OPERATORS['='],
|
||||
value: resourceValue,
|
||||
};
|
||||
// If raw data contains both names, retain the current value just like the
|
||||
// backend's current-first resolver.
|
||||
if (!items.has(currentKey) || key === currentKey) {
|
||||
items.set(currentKey, item);
|
||||
}
|
||||
});
|
||||
return Array.from(items.values());
|
||||
};
|
||||
|
||||
export const isServiceOrEnvironmentAttribute = (key: string): boolean =>
|
||||
SERVICE_AND_ENVIRONMENT_KEYS.includes(key);
|
||||
|
||||
@@ -28,7 +28,7 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
export const dbSystemTags: Tags[] = [
|
||||
{
|
||||
Key: 'db.system.(string)',
|
||||
Key: 'db.system.name.(string)',
|
||||
StringValues: [''],
|
||||
NumberValues: [],
|
||||
BoolValues: [],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Form } from 'antd';
|
||||
import { initialQueryBuilderFormValuesMap } from 'constants/queryBuilder';
|
||||
import { SemconvEditorWarning } from 'components/Semconv';
|
||||
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
@@ -55,6 +56,7 @@ function TagFilterInputWithLogsResultPreview({
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<SemconvEditorWarning value={value} editor="Pipeline filter" />
|
||||
<div className="pipeline-filter-input-preview-container">
|
||||
<LogsFilterPreview filter={value} />
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ChangeEvent, useCallback } from 'react';
|
||||
import MEditor, { Monaco } from '@monaco-editor/react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Input } from 'antd';
|
||||
import { SemconvEditorWarning } from 'components/Semconv';
|
||||
import { LEGEND } from 'constants/global';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
@@ -118,6 +119,7 @@ function ClickHouseQueryBuilder({
|
||||
theme={isDarkMode ? 'my-theme' : 'light'}
|
||||
beforeMount={setEditorTheme}
|
||||
/>
|
||||
<SemconvEditorWarning value={queryData?.query} editor="ClickHouse SQL" />
|
||||
<Input
|
||||
onChange={handleUpdateInput}
|
||||
name="legend"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ChangeEvent, useCallback } from 'react';
|
||||
import { Input } from 'antd';
|
||||
import { LEGEND } from 'constants/global';
|
||||
import { SemconvEditorWarning } from 'components/Semconv';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { IPromQLQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
@@ -66,6 +67,7 @@ function PromQLQueryBuilder({
|
||||
style={{ marginBottom: '0.5rem' }}
|
||||
data-testid="promql-query-input"
|
||||
/>
|
||||
<SemconvEditorWarning value={queryData?.query} editor="PromQL" />
|
||||
|
||||
<Input
|
||||
onChange={handleUpdateQuery}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getSemconvMembers } from 'utils/semconv';
|
||||
|
||||
// Field-name allowlists that drive signal-specific cell rendering. Both legacy
|
||||
// camelCase and snake_case variants are listed because the API has shipped both.
|
||||
export const TIMESTAMP_FIELD_NAMES = new Set(['timestamp']);
|
||||
@@ -5,12 +7,10 @@ export const TIMESTAMP_FIELD_NAMES = new Set(['timestamp']);
|
||||
export const STATUS_FIELD_NAMES = new Set([
|
||||
'httpMethod',
|
||||
'http_method',
|
||||
'http.method',
|
||||
'http.request.method',
|
||||
...getSemconvMembers('http.request.method'),
|
||||
'responseStatusCode',
|
||||
'response_status_code',
|
||||
'http.status_code',
|
||||
'http.response.status_code',
|
||||
...getSemconvMembers('http.response.status_code'),
|
||||
]);
|
||||
|
||||
export const DURATION_FIELD_NAMES = new Set(['durationNano', 'duration_nano']);
|
||||
|
||||
@@ -35,13 +35,7 @@ export default function CeleryOverviewDetails({
|
||||
? undefined
|
||||
: getFiltersFromKeyValue('messaging.system', value, 'tag');
|
||||
case 'destination':
|
||||
return getFiltersFromKeyValue(
|
||||
details.messaging_system === 'celery'
|
||||
? 'messaging.destination'
|
||||
: 'messaging.destination.name',
|
||||
value,
|
||||
'tag',
|
||||
);
|
||||
return getFiltersFromKeyValue('messaging.destination.name', value, 'tag');
|
||||
case 'kind_string':
|
||||
return getFiltersFromKeyValue('kind_string', value, '');
|
||||
default:
|
||||
|
||||
@@ -233,7 +233,7 @@ describe('Logs Explorer Tests', () => {
|
||||
);
|
||||
|
||||
const queries = queryAllByText(
|
||||
"Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')",
|
||||
"Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')",
|
||||
);
|
||||
expect(queries).toHaveLength(1);
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ export const LogsQuickFiltersConfig: IQuickFiltersConfig[] = [
|
||||
type: FiltersType.CHECKBOX,
|
||||
title: 'Environment',
|
||||
attributeKey: {
|
||||
key: 'deployment.environment',
|
||||
key: 'deployment.environment.name',
|
||||
dataType: DataTypes.String,
|
||||
type: 'resource',
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Skeleton } from 'antd';
|
||||
import { DetailsHeader, DetailsPanelDrawer } from 'components/DetailsPanel';
|
||||
import { HeaderAction } from 'components/DetailsPanel/DetailsHeader/DetailsHeader';
|
||||
import { DetailsPanelState } from 'components/DetailsPanel/types';
|
||||
import { SemconvOldNameBadge } from 'components/Semconv';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import {
|
||||
initialQueryBuilderFormValuesMap,
|
||||
@@ -108,6 +109,12 @@ function SpanDetailsContent({
|
||||
() => getSpanDisplayData(selectedSpan),
|
||||
[selectedSpan],
|
||||
);
|
||||
const semconvLabelSuffix = useCallback(
|
||||
(fieldKey: string): React.ReactNode => (
|
||||
<SemconvOldNameBadge name={fieldKey} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Map span attribute actions to PrettyView actions format.
|
||||
// Use the last key in fieldKeyPath (the actual attribute key), not the full display path.
|
||||
@@ -329,6 +336,7 @@ function SpanDetailsContent({
|
||||
visibleActions: VISIBLE_ACTIONS,
|
||||
pinnedFieldsValue,
|
||||
onPinnedFieldsChange,
|
||||
labelSuffixRenderer: semconvLabelSuffix,
|
||||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const KEY_ATTRIBUTE_KEYS: Record<string, string[]> = {
|
||||
traces: [
|
||||
'service.name',
|
||||
'service.namespace',
|
||||
'deployment.environment',
|
||||
'deployment.environment.name',
|
||||
'timestamp',
|
||||
'duration_nano',
|
||||
'kind_string',
|
||||
|
||||
@@ -359,7 +359,7 @@ function Filters({
|
||||
onChange={handleExpressionChange}
|
||||
onRun={handleRunQuery}
|
||||
dataSource={DataSource.TRACES}
|
||||
placeholder="Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')"
|
||||
placeholder="Enter your filter query (e.g., http.response.status_code >= 500 AND service.name = 'frontend')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,8 +22,8 @@ export const SPAN_CATEGORIES: readonly SpanCategory[] = [
|
||||
|
||||
// Map each category to the attribute key it filters on
|
||||
const CATEGORY_KEYS: Record<Exclude<SpanCategory, 'All'>, string> = {
|
||||
Database: 'db.system',
|
||||
HTTP: 'http.method',
|
||||
Database: 'db.system.name',
|
||||
HTTP: 'http.request.method',
|
||||
Functions: 'kind_string',
|
||||
Jobs: 'messaging.system',
|
||||
LLM: 'gen_ai.request.model',
|
||||
@@ -34,8 +34,8 @@ const ALL_CATEGORY_KEYS = Object.values(CATEGORY_KEYS);
|
||||
|
||||
// The expression clause to add for each category
|
||||
const CATEGORY_EXPRESSIONS: Record<Exclude<SpanCategory, 'All'>, string> = {
|
||||
Database: 'db.system exists',
|
||||
HTTP: 'http.method exists',
|
||||
Database: 'db.system.name exists',
|
||||
HTTP: 'http.request.method exists',
|
||||
Functions: "kind_string = 'Internal'",
|
||||
Jobs: 'messaging.system exists',
|
||||
LLM: 'gen_ai.request.model exists',
|
||||
|
||||
@@ -74,7 +74,8 @@ export function tracesRunQueryAction(
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'Attribute key, e.g. service.name, http.status_code',
|
||||
description:
|
||||
'Attribute key, e.g. service.name, http.response.status_code',
|
||||
},
|
||||
op: {
|
||||
type: 'string',
|
||||
@@ -143,7 +144,7 @@ export function tracesAddFilterAction(
|
||||
properties: {
|
||||
key: {
|
||||
type: 'string',
|
||||
description: 'Attribute key, e.g. service.name, http.status_code',
|
||||
description: 'Attribute key, e.g. service.name, http.response.status_code',
|
||||
},
|
||||
op: {
|
||||
type: 'string',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,12 +25,13 @@ HAS=24
|
||||
HASANY=25
|
||||
HASALL=26
|
||||
SEARCH=27
|
||||
BOOL=28
|
||||
NUMBER=29
|
||||
QUOTED_TEXT=30
|
||||
KEY=31
|
||||
WS=32
|
||||
FREETEXT=33
|
||||
EXACT=28
|
||||
BOOL=29
|
||||
NUMBER=30
|
||||
QUOTED_TEXT=31
|
||||
KEY=32
|
||||
WS=33
|
||||
FREETEXT=34
|
||||
'('=1
|
||||
')'=2
|
||||
'['=3
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,12 +25,13 @@ HAS=24
|
||||
HASANY=25
|
||||
HASALL=26
|
||||
SEARCH=27
|
||||
BOOL=28
|
||||
NUMBER=29
|
||||
QUOTED_TEXT=30
|
||||
KEY=31
|
||||
WS=32
|
||||
FREETEXT=33
|
||||
EXACT=28
|
||||
BOOL=29
|
||||
NUMBER=30
|
||||
QUOTED_TEXT=31
|
||||
KEY=32
|
||||
WS=33
|
||||
FREETEXT=34
|
||||
'('=1
|
||||
')'=2
|
||||
'['=3
|
||||
|
||||
@@ -39,12 +39,13 @@ export default class FilterQueryLexer extends Lexer {
|
||||
public static readonly HASANY = 25;
|
||||
public static readonly HASALL = 26;
|
||||
public static readonly SEARCH = 27;
|
||||
public static readonly BOOL = 28;
|
||||
public static readonly NUMBER = 29;
|
||||
public static readonly QUOTED_TEXT = 30;
|
||||
public static readonly KEY = 31;
|
||||
public static readonly WS = 32;
|
||||
public static readonly FREETEXT = 33;
|
||||
public static readonly EXACT = 28;
|
||||
public static readonly BOOL = 29;
|
||||
public static readonly NUMBER = 30;
|
||||
public static readonly QUOTED_TEXT = 31;
|
||||
public static readonly KEY = 32;
|
||||
public static readonly WS = 33;
|
||||
public static readonly FREETEXT = 34;
|
||||
public static readonly EOF = Token.EOF;
|
||||
|
||||
public static readonly channelNames: string[] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ];
|
||||
@@ -70,8 +71,8 @@ export default class FilterQueryLexer extends Lexer {
|
||||
"HASTOKEN",
|
||||
"HAS", "HASANY",
|
||||
"HASALL", "SEARCH",
|
||||
"BOOL", "NUMBER",
|
||||
"QUOTED_TEXT",
|
||||
"EXACT", "BOOL",
|
||||
"NUMBER", "QUOTED_TEXT",
|
||||
"KEY", "WS",
|
||||
"FREETEXT" ];
|
||||
public static readonly modeNames: string[] = [ "DEFAULT_MODE", ];
|
||||
@@ -80,8 +81,8 @@ export default class FilterQueryLexer extends Lexer {
|
||||
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
|
||||
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "ILIKE", "BETWEEN", "EXISTS", "REGEXP",
|
||||
"CONTAINS", "IN", "NOT", "AND", "OR", "HASTOKEN", "HAS", "HASANY", "HASALL",
|
||||
"SEARCH", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT", "EMPTY_BRACKS",
|
||||
"OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT",
|
||||
"SEARCH", "EXACT", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT",
|
||||
"EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT",
|
||||
];
|
||||
|
||||
|
||||
@@ -102,122 +103,124 @@ export default class FilterQueryLexer extends Lexer {
|
||||
|
||||
public get modeNames(): string[] { return FilterQueryLexer.modeNames; }
|
||||
|
||||
public static readonly _serializedATN: number[] = [4,0,33,329,6,-1,2,0,
|
||||
public static readonly _serializedATN: number[] = [4,0,34,337,6,-1,2,0,
|
||||
7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,
|
||||
7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,
|
||||
16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,
|
||||
2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,
|
||||
31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,1,0,
|
||||
1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,3,5,91,8,5,1,6,1,6,1,6,
|
||||
1,7,1,7,1,7,1,8,1,8,1,9,1,9,1,9,1,10,1,10,1,11,1,11,1,11,1,12,1,12,1,12,
|
||||
1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,
|
||||
14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,3,15,134,8,15,1,16,1,16,1,16,1,16,
|
||||
1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,151,8,17,1,
|
||||
18,1,18,1,18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,22,
|
||||
1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,24,1,24,1,
|
||||
24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,
|
||||
1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,3,27,210,
|
||||
8,27,1,28,1,28,1,29,3,29,215,8,29,1,29,4,29,218,8,29,11,29,12,29,219,1,
|
||||
29,1,29,5,29,224,8,29,10,29,12,29,227,9,29,3,29,229,8,29,1,29,1,29,3,29,
|
||||
233,8,29,1,29,4,29,236,8,29,11,29,12,29,237,3,29,240,8,29,1,29,3,29,243,
|
||||
8,29,1,29,1,29,4,29,247,8,29,11,29,12,29,248,1,29,1,29,3,29,253,8,29,1,
|
||||
29,4,29,256,8,29,11,29,12,29,257,3,29,260,8,29,3,29,262,8,29,1,30,1,30,
|
||||
1,30,1,30,5,30,268,8,30,10,30,12,30,271,9,30,1,30,1,30,1,30,1,30,1,30,5,
|
||||
30,278,8,30,10,30,12,30,281,9,30,1,30,3,30,284,8,30,1,31,1,31,5,31,288,
|
||||
8,31,10,31,12,31,291,9,31,1,32,1,32,1,32,1,33,1,33,1,33,1,33,1,34,1,34,
|
||||
1,34,1,34,1,34,1,34,1,34,4,34,307,8,34,11,34,12,34,308,5,34,311,8,34,10,
|
||||
34,12,34,314,9,34,1,35,4,35,317,8,35,11,35,12,35,318,1,35,1,35,1,36,1,36,
|
||||
1,37,4,37,326,8,37,11,37,12,37,327,0,0,38,1,1,3,2,5,3,7,4,9,5,11,6,13,7,
|
||||
15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,16,33,17,35,18,37,19,39,
|
||||
20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,55,28,57,0,59,29,61,30,63,
|
||||
0,65,0,67,0,69,31,71,32,73,0,75,33,1,0,29,2,0,76,76,108,108,2,0,73,73,105,
|
||||
105,2,0,75,75,107,107,2,0,69,69,101,101,2,0,66,66,98,98,2,0,84,84,116,116,
|
||||
2,0,87,87,119,119,2,0,78,78,110,110,2,0,88,88,120,120,2,0,83,83,115,115,
|
||||
2,0,82,82,114,114,2,0,71,71,103,103,2,0,80,80,112,112,2,0,67,67,99,99,2,
|
||||
0,79,79,111,111,2,0,65,65,97,97,2,0,68,68,100,100,2,0,72,72,104,104,2,0,
|
||||
89,89,121,121,2,0,85,85,117,117,2,0,70,70,102,102,2,0,43,43,45,45,2,0,34,
|
||||
34,92,92,2,0,39,39,92,92,4,0,35,36,64,90,95,95,97,123,7,0,35,36,45,45,47,
|
||||
58,64,90,95,95,97,123,125,125,3,0,9,10,13,13,32,32,1,0,48,57,8,0,9,10,13,
|
||||
13,32,34,39,41,44,44,60,62,91,91,93,93,353,0,1,1,0,0,0,0,3,1,0,0,0,0,5,
|
||||
1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,
|
||||
0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,
|
||||
0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,
|
||||
0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,
|
||||
0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,
|
||||
0,69,1,0,0,0,0,71,1,0,0,0,0,75,1,0,0,0,1,77,1,0,0,0,3,79,1,0,0,0,5,81,1,
|
||||
0,0,0,7,83,1,0,0,0,9,85,1,0,0,0,11,90,1,0,0,0,13,92,1,0,0,0,15,95,1,0,0,
|
||||
0,17,98,1,0,0,0,19,100,1,0,0,0,21,103,1,0,0,0,23,105,1,0,0,0,25,108,1,0,
|
||||
0,0,27,113,1,0,0,0,29,119,1,0,0,0,31,127,1,0,0,0,33,135,1,0,0,0,35,142,
|
||||
1,0,0,0,37,152,1,0,0,0,39,155,1,0,0,0,41,159,1,0,0,0,43,163,1,0,0,0,45,
|
||||
166,1,0,0,0,47,175,1,0,0,0,49,179,1,0,0,0,51,186,1,0,0,0,53,193,1,0,0,0,
|
||||
55,209,1,0,0,0,57,211,1,0,0,0,59,261,1,0,0,0,61,283,1,0,0,0,63,285,1,0,
|
||||
0,0,65,292,1,0,0,0,67,295,1,0,0,0,69,299,1,0,0,0,71,316,1,0,0,0,73,322,
|
||||
1,0,0,0,75,325,1,0,0,0,77,78,5,40,0,0,78,2,1,0,0,0,79,80,5,41,0,0,80,4,
|
||||
1,0,0,0,81,82,5,91,0,0,82,6,1,0,0,0,83,84,5,93,0,0,84,8,1,0,0,0,85,86,5,
|
||||
44,0,0,86,10,1,0,0,0,87,91,5,61,0,0,88,89,5,61,0,0,89,91,5,61,0,0,90,87,
|
||||
1,0,0,0,90,88,1,0,0,0,91,12,1,0,0,0,92,93,5,33,0,0,93,94,5,61,0,0,94,14,
|
||||
1,0,0,0,95,96,5,60,0,0,96,97,5,62,0,0,97,16,1,0,0,0,98,99,5,60,0,0,99,18,
|
||||
1,0,0,0,100,101,5,60,0,0,101,102,5,61,0,0,102,20,1,0,0,0,103,104,5,62,0,
|
||||
0,104,22,1,0,0,0,105,106,5,62,0,0,106,107,5,61,0,0,107,24,1,0,0,0,108,109,
|
||||
7,0,0,0,109,110,7,1,0,0,110,111,7,2,0,0,111,112,7,3,0,0,112,26,1,0,0,0,
|
||||
113,114,7,1,0,0,114,115,7,0,0,0,115,116,7,1,0,0,116,117,7,2,0,0,117,118,
|
||||
7,3,0,0,118,28,1,0,0,0,119,120,7,4,0,0,120,121,7,3,0,0,121,122,7,5,0,0,
|
||||
122,123,7,6,0,0,123,124,7,3,0,0,124,125,7,3,0,0,125,126,7,7,0,0,126,30,
|
||||
1,0,0,0,127,128,7,3,0,0,128,129,7,8,0,0,129,130,7,1,0,0,130,131,7,9,0,0,
|
||||
131,133,7,5,0,0,132,134,7,9,0,0,133,132,1,0,0,0,133,134,1,0,0,0,134,32,
|
||||
1,0,0,0,135,136,7,10,0,0,136,137,7,3,0,0,137,138,7,11,0,0,138,139,7,3,0,
|
||||
0,139,140,7,8,0,0,140,141,7,12,0,0,141,34,1,0,0,0,142,143,7,13,0,0,143,
|
||||
144,7,14,0,0,144,145,7,7,0,0,145,146,7,5,0,0,146,147,7,15,0,0,147,148,7,
|
||||
1,0,0,148,150,7,7,0,0,149,151,7,9,0,0,150,149,1,0,0,0,150,151,1,0,0,0,151,
|
||||
36,1,0,0,0,152,153,7,1,0,0,153,154,7,7,0,0,154,38,1,0,0,0,155,156,7,7,0,
|
||||
0,156,157,7,14,0,0,157,158,7,5,0,0,158,40,1,0,0,0,159,160,7,15,0,0,160,
|
||||
161,7,7,0,0,161,162,7,16,0,0,162,42,1,0,0,0,163,164,7,14,0,0,164,165,7,
|
||||
10,0,0,165,44,1,0,0,0,166,167,7,17,0,0,167,168,7,15,0,0,168,169,7,9,0,0,
|
||||
169,170,7,5,0,0,170,171,7,14,0,0,171,172,7,2,0,0,172,173,7,3,0,0,173,174,
|
||||
7,7,0,0,174,46,1,0,0,0,175,176,7,17,0,0,176,177,7,15,0,0,177,178,7,9,0,
|
||||
0,178,48,1,0,0,0,179,180,7,17,0,0,180,181,7,15,0,0,181,182,7,9,0,0,182,
|
||||
183,7,15,0,0,183,184,7,7,0,0,184,185,7,18,0,0,185,50,1,0,0,0,186,187,7,
|
||||
17,0,0,187,188,7,15,0,0,188,189,7,9,0,0,189,190,7,15,0,0,190,191,7,0,0,
|
||||
0,191,192,7,0,0,0,192,52,1,0,0,0,193,194,7,9,0,0,194,195,7,3,0,0,195,196,
|
||||
7,15,0,0,196,197,7,10,0,0,197,198,7,13,0,0,198,199,7,17,0,0,199,54,1,0,
|
||||
0,0,200,201,7,5,0,0,201,202,7,10,0,0,202,203,7,19,0,0,203,210,7,3,0,0,204,
|
||||
205,7,20,0,0,205,206,7,15,0,0,206,207,7,0,0,0,207,208,7,9,0,0,208,210,7,
|
||||
3,0,0,209,200,1,0,0,0,209,204,1,0,0,0,210,56,1,0,0,0,211,212,7,21,0,0,212,
|
||||
58,1,0,0,0,213,215,3,57,28,0,214,213,1,0,0,0,214,215,1,0,0,0,215,217,1,
|
||||
0,0,0,216,218,3,73,36,0,217,216,1,0,0,0,218,219,1,0,0,0,219,217,1,0,0,0,
|
||||
219,220,1,0,0,0,220,228,1,0,0,0,221,225,5,46,0,0,222,224,3,73,36,0,223,
|
||||
222,1,0,0,0,224,227,1,0,0,0,225,223,1,0,0,0,225,226,1,0,0,0,226,229,1,0,
|
||||
0,0,227,225,1,0,0,0,228,221,1,0,0,0,228,229,1,0,0,0,229,239,1,0,0,0,230,
|
||||
232,7,3,0,0,231,233,3,57,28,0,232,231,1,0,0,0,232,233,1,0,0,0,233,235,1,
|
||||
0,0,0,234,236,3,73,36,0,235,234,1,0,0,0,236,237,1,0,0,0,237,235,1,0,0,0,
|
||||
237,238,1,0,0,0,238,240,1,0,0,0,239,230,1,0,0,0,239,240,1,0,0,0,240,262,
|
||||
1,0,0,0,241,243,3,57,28,0,242,241,1,0,0,0,242,243,1,0,0,0,243,244,1,0,0,
|
||||
0,244,246,5,46,0,0,245,247,3,73,36,0,246,245,1,0,0,0,247,248,1,0,0,0,248,
|
||||
246,1,0,0,0,248,249,1,0,0,0,249,259,1,0,0,0,250,252,7,3,0,0,251,253,3,57,
|
||||
28,0,252,251,1,0,0,0,252,253,1,0,0,0,253,255,1,0,0,0,254,256,3,73,36,0,
|
||||
255,254,1,0,0,0,256,257,1,0,0,0,257,255,1,0,0,0,257,258,1,0,0,0,258,260,
|
||||
1,0,0,0,259,250,1,0,0,0,259,260,1,0,0,0,260,262,1,0,0,0,261,214,1,0,0,0,
|
||||
261,242,1,0,0,0,262,60,1,0,0,0,263,269,5,34,0,0,264,268,8,22,0,0,265,266,
|
||||
5,92,0,0,266,268,9,0,0,0,267,264,1,0,0,0,267,265,1,0,0,0,268,271,1,0,0,
|
||||
0,269,267,1,0,0,0,269,270,1,0,0,0,270,272,1,0,0,0,271,269,1,0,0,0,272,284,
|
||||
5,34,0,0,273,279,5,39,0,0,274,278,8,23,0,0,275,276,5,92,0,0,276,278,9,0,
|
||||
0,0,277,274,1,0,0,0,277,275,1,0,0,0,278,281,1,0,0,0,279,277,1,0,0,0,279,
|
||||
280,1,0,0,0,280,282,1,0,0,0,281,279,1,0,0,0,282,284,5,39,0,0,283,263,1,
|
||||
0,0,0,283,273,1,0,0,0,284,62,1,0,0,0,285,289,7,24,0,0,286,288,7,25,0,0,
|
||||
287,286,1,0,0,0,288,291,1,0,0,0,289,287,1,0,0,0,289,290,1,0,0,0,290,64,
|
||||
1,0,0,0,291,289,1,0,0,0,292,293,5,91,0,0,293,294,5,93,0,0,294,66,1,0,0,
|
||||
0,295,296,5,91,0,0,296,297,5,42,0,0,297,298,5,93,0,0,298,68,1,0,0,0,299,
|
||||
312,3,63,31,0,300,301,5,46,0,0,301,311,3,63,31,0,302,311,3,65,32,0,303,
|
||||
311,3,67,33,0,304,306,5,46,0,0,305,307,3,73,36,0,306,305,1,0,0,0,307,308,
|
||||
1,0,0,0,308,306,1,0,0,0,308,309,1,0,0,0,309,311,1,0,0,0,310,300,1,0,0,0,
|
||||
310,302,1,0,0,0,310,303,1,0,0,0,310,304,1,0,0,0,311,314,1,0,0,0,312,310,
|
||||
1,0,0,0,312,313,1,0,0,0,313,70,1,0,0,0,314,312,1,0,0,0,315,317,7,26,0,0,
|
||||
316,315,1,0,0,0,317,318,1,0,0,0,318,316,1,0,0,0,318,319,1,0,0,0,319,320,
|
||||
1,0,0,0,320,321,6,35,0,0,321,72,1,0,0,0,322,323,7,27,0,0,323,74,1,0,0,0,
|
||||
324,326,8,28,0,0,325,324,1,0,0,0,326,327,1,0,0,0,327,325,1,0,0,0,327,328,
|
||||
1,0,0,0,328,76,1,0,0,0,29,0,90,133,150,209,214,219,225,228,232,237,239,
|
||||
242,248,252,257,259,261,267,269,277,279,283,289,308,310,312,318,327,1,6,
|
||||
0,0];
|
||||
31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,
|
||||
7,38,1,0,1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,3,5,93,8,5,1,6,
|
||||
1,6,1,6,1,7,1,7,1,7,1,8,1,8,1,9,1,9,1,9,1,10,1,10,1,11,1,11,1,11,1,12,1,
|
||||
12,1,12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,
|
||||
1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,3,15,136,8,15,1,16,1,16,1,
|
||||
16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,3,17,153,
|
||||
8,17,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,1,20,1,21,1,21,1,
|
||||
21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,24,
|
||||
1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,
|
||||
26,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,
|
||||
1,28,1,28,1,28,1,28,1,28,1,28,3,28,218,8,28,1,29,1,29,1,30,3,30,223,8,30,
|
||||
1,30,4,30,226,8,30,11,30,12,30,227,1,30,1,30,5,30,232,8,30,10,30,12,30,
|
||||
235,9,30,3,30,237,8,30,1,30,1,30,3,30,241,8,30,1,30,4,30,244,8,30,11,30,
|
||||
12,30,245,3,30,248,8,30,1,30,3,30,251,8,30,1,30,1,30,4,30,255,8,30,11,30,
|
||||
12,30,256,1,30,1,30,3,30,261,8,30,1,30,4,30,264,8,30,11,30,12,30,265,3,
|
||||
30,268,8,30,3,30,270,8,30,1,31,1,31,1,31,1,31,5,31,276,8,31,10,31,12,31,
|
||||
279,9,31,1,31,1,31,1,31,1,31,1,31,5,31,286,8,31,10,31,12,31,289,9,31,1,
|
||||
31,3,31,292,8,31,1,32,1,32,5,32,296,8,32,10,32,12,32,299,9,32,1,33,1,33,
|
||||
1,33,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,35,4,35,315,8,
|
||||
35,11,35,12,35,316,5,35,319,8,35,10,35,12,35,322,9,35,1,36,4,36,325,8,36,
|
||||
11,36,12,36,326,1,36,1,36,1,37,1,37,1,38,4,38,334,8,38,11,38,12,38,335,
|
||||
0,0,39,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,
|
||||
27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,
|
||||
51,26,53,27,55,28,57,29,59,0,61,30,63,31,65,0,67,0,69,0,71,32,73,33,75,
|
||||
0,77,34,1,0,29,2,0,76,76,108,108,2,0,73,73,105,105,2,0,75,75,107,107,2,
|
||||
0,69,69,101,101,2,0,66,66,98,98,2,0,84,84,116,116,2,0,87,87,119,119,2,0,
|
||||
78,78,110,110,2,0,88,88,120,120,2,0,83,83,115,115,2,0,82,82,114,114,2,0,
|
||||
71,71,103,103,2,0,80,80,112,112,2,0,67,67,99,99,2,0,79,79,111,111,2,0,65,
|
||||
65,97,97,2,0,68,68,100,100,2,0,72,72,104,104,2,0,89,89,121,121,2,0,85,85,
|
||||
117,117,2,0,70,70,102,102,2,0,43,43,45,45,2,0,34,34,92,92,2,0,39,39,92,
|
||||
92,4,0,35,36,64,90,95,95,97,123,7,0,35,36,45,45,47,58,64,90,95,95,97,123,
|
||||
125,125,3,0,9,10,13,13,32,32,1,0,48,57,8,0,9,10,13,13,32,34,39,41,44,44,
|
||||
60,62,91,91,93,93,361,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,
|
||||
9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,1,0,0,0,0,19,1,0,
|
||||
0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,1,0,0,0,0,29,1,0,0,0,0,
|
||||
31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,
|
||||
0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,
|
||||
53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,71,1,0,
|
||||
0,0,0,73,1,0,0,0,0,77,1,0,0,0,1,79,1,0,0,0,3,81,1,0,0,0,5,83,1,0,0,0,7,
|
||||
85,1,0,0,0,9,87,1,0,0,0,11,92,1,0,0,0,13,94,1,0,0,0,15,97,1,0,0,0,17,100,
|
||||
1,0,0,0,19,102,1,0,0,0,21,105,1,0,0,0,23,107,1,0,0,0,25,110,1,0,0,0,27,
|
||||
115,1,0,0,0,29,121,1,0,0,0,31,129,1,0,0,0,33,137,1,0,0,0,35,144,1,0,0,0,
|
||||
37,154,1,0,0,0,39,157,1,0,0,0,41,161,1,0,0,0,43,165,1,0,0,0,45,168,1,0,
|
||||
0,0,47,177,1,0,0,0,49,181,1,0,0,0,51,188,1,0,0,0,53,195,1,0,0,0,55,202,
|
||||
1,0,0,0,57,217,1,0,0,0,59,219,1,0,0,0,61,269,1,0,0,0,63,291,1,0,0,0,65,
|
||||
293,1,0,0,0,67,300,1,0,0,0,69,303,1,0,0,0,71,307,1,0,0,0,73,324,1,0,0,0,
|
||||
75,330,1,0,0,0,77,333,1,0,0,0,79,80,5,40,0,0,80,2,1,0,0,0,81,82,5,41,0,
|
||||
0,82,4,1,0,0,0,83,84,5,91,0,0,84,6,1,0,0,0,85,86,5,93,0,0,86,8,1,0,0,0,
|
||||
87,88,5,44,0,0,88,10,1,0,0,0,89,93,5,61,0,0,90,91,5,61,0,0,91,93,5,61,0,
|
||||
0,92,89,1,0,0,0,92,90,1,0,0,0,93,12,1,0,0,0,94,95,5,33,0,0,95,96,5,61,0,
|
||||
0,96,14,1,0,0,0,97,98,5,60,0,0,98,99,5,62,0,0,99,16,1,0,0,0,100,101,5,60,
|
||||
0,0,101,18,1,0,0,0,102,103,5,60,0,0,103,104,5,61,0,0,104,20,1,0,0,0,105,
|
||||
106,5,62,0,0,106,22,1,0,0,0,107,108,5,62,0,0,108,109,5,61,0,0,109,24,1,
|
||||
0,0,0,110,111,7,0,0,0,111,112,7,1,0,0,112,113,7,2,0,0,113,114,7,3,0,0,114,
|
||||
26,1,0,0,0,115,116,7,1,0,0,116,117,7,0,0,0,117,118,7,1,0,0,118,119,7,2,
|
||||
0,0,119,120,7,3,0,0,120,28,1,0,0,0,121,122,7,4,0,0,122,123,7,3,0,0,123,
|
||||
124,7,5,0,0,124,125,7,6,0,0,125,126,7,3,0,0,126,127,7,3,0,0,127,128,7,7,
|
||||
0,0,128,30,1,0,0,0,129,130,7,3,0,0,130,131,7,8,0,0,131,132,7,1,0,0,132,
|
||||
133,7,9,0,0,133,135,7,5,0,0,134,136,7,9,0,0,135,134,1,0,0,0,135,136,1,0,
|
||||
0,0,136,32,1,0,0,0,137,138,7,10,0,0,138,139,7,3,0,0,139,140,7,11,0,0,140,
|
||||
141,7,3,0,0,141,142,7,8,0,0,142,143,7,12,0,0,143,34,1,0,0,0,144,145,7,13,
|
||||
0,0,145,146,7,14,0,0,146,147,7,7,0,0,147,148,7,5,0,0,148,149,7,15,0,0,149,
|
||||
150,7,1,0,0,150,152,7,7,0,0,151,153,7,9,0,0,152,151,1,0,0,0,152,153,1,0,
|
||||
0,0,153,36,1,0,0,0,154,155,7,1,0,0,155,156,7,7,0,0,156,38,1,0,0,0,157,158,
|
||||
7,7,0,0,158,159,7,14,0,0,159,160,7,5,0,0,160,40,1,0,0,0,161,162,7,15,0,
|
||||
0,162,163,7,7,0,0,163,164,7,16,0,0,164,42,1,0,0,0,165,166,7,14,0,0,166,
|
||||
167,7,10,0,0,167,44,1,0,0,0,168,169,7,17,0,0,169,170,7,15,0,0,170,171,7,
|
||||
9,0,0,171,172,7,5,0,0,172,173,7,14,0,0,173,174,7,2,0,0,174,175,7,3,0,0,
|
||||
175,176,7,7,0,0,176,46,1,0,0,0,177,178,7,17,0,0,178,179,7,15,0,0,179,180,
|
||||
7,9,0,0,180,48,1,0,0,0,181,182,7,17,0,0,182,183,7,15,0,0,183,184,7,9,0,
|
||||
0,184,185,7,15,0,0,185,186,7,7,0,0,186,187,7,18,0,0,187,50,1,0,0,0,188,
|
||||
189,7,17,0,0,189,190,7,15,0,0,190,191,7,9,0,0,191,192,7,15,0,0,192,193,
|
||||
7,0,0,0,193,194,7,0,0,0,194,52,1,0,0,0,195,196,7,9,0,0,196,197,7,3,0,0,
|
||||
197,198,7,15,0,0,198,199,7,10,0,0,199,200,7,13,0,0,200,201,7,17,0,0,201,
|
||||
54,1,0,0,0,202,203,7,3,0,0,203,204,7,8,0,0,204,205,7,15,0,0,205,206,7,13,
|
||||
0,0,206,207,7,5,0,0,207,56,1,0,0,0,208,209,7,5,0,0,209,210,7,10,0,0,210,
|
||||
211,7,19,0,0,211,218,7,3,0,0,212,213,7,20,0,0,213,214,7,15,0,0,214,215,
|
||||
7,0,0,0,215,216,7,9,0,0,216,218,7,3,0,0,217,208,1,0,0,0,217,212,1,0,0,0,
|
||||
218,58,1,0,0,0,219,220,7,21,0,0,220,60,1,0,0,0,221,223,3,59,29,0,222,221,
|
||||
1,0,0,0,222,223,1,0,0,0,223,225,1,0,0,0,224,226,3,75,37,0,225,224,1,0,0,
|
||||
0,226,227,1,0,0,0,227,225,1,0,0,0,227,228,1,0,0,0,228,236,1,0,0,0,229,233,
|
||||
5,46,0,0,230,232,3,75,37,0,231,230,1,0,0,0,232,235,1,0,0,0,233,231,1,0,
|
||||
0,0,233,234,1,0,0,0,234,237,1,0,0,0,235,233,1,0,0,0,236,229,1,0,0,0,236,
|
||||
237,1,0,0,0,237,247,1,0,0,0,238,240,7,3,0,0,239,241,3,59,29,0,240,239,1,
|
||||
0,0,0,240,241,1,0,0,0,241,243,1,0,0,0,242,244,3,75,37,0,243,242,1,0,0,0,
|
||||
244,245,1,0,0,0,245,243,1,0,0,0,245,246,1,0,0,0,246,248,1,0,0,0,247,238,
|
||||
1,0,0,0,247,248,1,0,0,0,248,270,1,0,0,0,249,251,3,59,29,0,250,249,1,0,0,
|
||||
0,250,251,1,0,0,0,251,252,1,0,0,0,252,254,5,46,0,0,253,255,3,75,37,0,254,
|
||||
253,1,0,0,0,255,256,1,0,0,0,256,254,1,0,0,0,256,257,1,0,0,0,257,267,1,0,
|
||||
0,0,258,260,7,3,0,0,259,261,3,59,29,0,260,259,1,0,0,0,260,261,1,0,0,0,261,
|
||||
263,1,0,0,0,262,264,3,75,37,0,263,262,1,0,0,0,264,265,1,0,0,0,265,263,1,
|
||||
0,0,0,265,266,1,0,0,0,266,268,1,0,0,0,267,258,1,0,0,0,267,268,1,0,0,0,268,
|
||||
270,1,0,0,0,269,222,1,0,0,0,269,250,1,0,0,0,270,62,1,0,0,0,271,277,5,34,
|
||||
0,0,272,276,8,22,0,0,273,274,5,92,0,0,274,276,9,0,0,0,275,272,1,0,0,0,275,
|
||||
273,1,0,0,0,276,279,1,0,0,0,277,275,1,0,0,0,277,278,1,0,0,0,278,280,1,0,
|
||||
0,0,279,277,1,0,0,0,280,292,5,34,0,0,281,287,5,39,0,0,282,286,8,23,0,0,
|
||||
283,284,5,92,0,0,284,286,9,0,0,0,285,282,1,0,0,0,285,283,1,0,0,0,286,289,
|
||||
1,0,0,0,287,285,1,0,0,0,287,288,1,0,0,0,288,290,1,0,0,0,289,287,1,0,0,0,
|
||||
290,292,5,39,0,0,291,271,1,0,0,0,291,281,1,0,0,0,292,64,1,0,0,0,293,297,
|
||||
7,24,0,0,294,296,7,25,0,0,295,294,1,0,0,0,296,299,1,0,0,0,297,295,1,0,0,
|
||||
0,297,298,1,0,0,0,298,66,1,0,0,0,299,297,1,0,0,0,300,301,5,91,0,0,301,302,
|
||||
5,93,0,0,302,68,1,0,0,0,303,304,5,91,0,0,304,305,5,42,0,0,305,306,5,93,
|
||||
0,0,306,70,1,0,0,0,307,320,3,65,32,0,308,309,5,46,0,0,309,319,3,65,32,0,
|
||||
310,319,3,67,33,0,311,319,3,69,34,0,312,314,5,46,0,0,313,315,3,75,37,0,
|
||||
314,313,1,0,0,0,315,316,1,0,0,0,316,314,1,0,0,0,316,317,1,0,0,0,317,319,
|
||||
1,0,0,0,318,308,1,0,0,0,318,310,1,0,0,0,318,311,1,0,0,0,318,312,1,0,0,0,
|
||||
319,322,1,0,0,0,320,318,1,0,0,0,320,321,1,0,0,0,321,72,1,0,0,0,322,320,
|
||||
1,0,0,0,323,325,7,26,0,0,324,323,1,0,0,0,325,326,1,0,0,0,326,324,1,0,0,
|
||||
0,326,327,1,0,0,0,327,328,1,0,0,0,328,329,6,36,0,0,329,74,1,0,0,0,330,331,
|
||||
7,27,0,0,331,76,1,0,0,0,332,334,8,28,0,0,333,332,1,0,0,0,334,335,1,0,0,
|
||||
0,335,333,1,0,0,0,335,336,1,0,0,0,336,78,1,0,0,0,29,0,92,135,152,217,222,
|
||||
227,233,236,240,245,247,250,256,260,265,267,269,275,277,285,287,291,297,
|
||||
316,318,320,326,335,1,6,0,0];
|
||||
|
||||
private static __ATN: ATN;
|
||||
public static get _ATN(): ATN {
|
||||
|
||||
@@ -21,6 +21,8 @@ import { FunctionParamContext } from "./FilterQueryParser.js";
|
||||
import { ArrayContext } from "./FilterQueryParser.js";
|
||||
import { ValueContext } from "./FilterQueryParser.js";
|
||||
import { KeyContext } from "./FilterQueryParser.js";
|
||||
import { FieldContext } from "./FilterQueryParser.js";
|
||||
import { ExactCallContext } from "./FilterQueryParser.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -208,5 +210,25 @@ export default class FilterQueryListener extends ParseTreeListener {
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitKey?: (ctx: KeyContext) => void;
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.field`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterField?: (ctx: FieldContext) => void;
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.field`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitField?: (ctx: FieldContext) => void;
|
||||
/**
|
||||
* Enter a parse tree produced by `FilterQueryParser.exactCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
enterExactCall?: (ctx: ExactCallContext) => void;
|
||||
/**
|
||||
* Exit a parse tree produced by `FilterQueryParser.exactCall`.
|
||||
* @param ctx the parse tree
|
||||
*/
|
||||
exitExactCall?: (ctx: ExactCallContext) => void;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,8 @@ import { FunctionParamContext } from "./FilterQueryParser.js";
|
||||
import { ArrayContext } from "./FilterQueryParser.js";
|
||||
import { ValueContext } from "./FilterQueryParser.js";
|
||||
import { KeyContext } from "./FilterQueryParser.js";
|
||||
import { FieldContext } from "./FilterQueryParser.js";
|
||||
import { ExactCallContext } from "./FilterQueryParser.js";
|
||||
|
||||
|
||||
/**
|
||||
@@ -139,5 +141,17 @@ export default class FilterQueryVisitor<Result> extends ParseTreeVisitor<Result>
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitKey?: (ctx: KeyContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.field`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitField?: (ctx: FieldContext) => Result;
|
||||
/**
|
||||
* Visit a parse tree produced by `FilterQueryParser.exactCall`.
|
||||
* @param ctx the parse tree
|
||||
* @return the visitor result
|
||||
*/
|
||||
visitExactCall?: (ctx: ExactCallContext) => Result;
|
||||
}
|
||||
|
||||
|
||||
@@ -246,6 +246,12 @@
|
||||
padding-left: 6px !important;
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
&__pinned-icon {
|
||||
flex-shrink: 0;
|
||||
color: var(--text-robin-400);
|
||||
|
||||
@@ -76,6 +76,7 @@ export interface PrettyViewProps {
|
||||
value: unknown,
|
||||
keyPath: readonly (string | number)[],
|
||||
) => React.ReactNode | undefined;
|
||||
labelSuffixRenderer?: (fieldKey: string) => React.ReactNode;
|
||||
}
|
||||
|
||||
function PrettyView({
|
||||
@@ -88,6 +89,7 @@ function PrettyView({
|
||||
pinnedFieldsValue,
|
||||
onPinnedFieldsChange,
|
||||
renderLeafValue,
|
||||
labelSuffixRenderer,
|
||||
}: PrettyViewProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const [, setCopy] = useCopyToClipboard();
|
||||
@@ -318,10 +320,24 @@ function PrettyView({
|
||||
}}
|
||||
/>
|
||||
<span>{displayKey}</span>
|
||||
{labelSuffixRenderer?.(displayKey)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
[togglePin, pinnedEntries],
|
||||
[togglePin, pinnedEntries, labelSuffixRenderer],
|
||||
);
|
||||
|
||||
const labelRenderer = useCallback(
|
||||
(keyPath: KeyPath): React.ReactNode => {
|
||||
const displayKey = String(keyPath[0]);
|
||||
return (
|
||||
<span className="pretty-view__label">
|
||||
<span>{displayKey}</span>
|
||||
{labelSuffixRenderer?.(displayKey)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
[labelSuffixRenderer],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -364,6 +380,7 @@ function PrettyView({
|
||||
shouldExpandNodeInitially={shouldExpandNodeInitially}
|
||||
valueRenderer={valueRenderer}
|
||||
getItemString={getItemString}
|
||||
labelRenderer={labelRenderer}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
14
frontend/src/types/api/semconvMigration.ts
Normal file
14
frontend/src/types/api/semconvMigration.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export interface SemconvMigrationReportEntry {
|
||||
current: string;
|
||||
old: string;
|
||||
signal: string;
|
||||
services: string[];
|
||||
resourceSets: number;
|
||||
lastSeenUnixMilli: number;
|
||||
}
|
||||
|
||||
export interface SemconvMigrationReport {
|
||||
startUnixMilli: number;
|
||||
endUnixMilli: number;
|
||||
entries: SemconvMigrationReportEntry[];
|
||||
}
|
||||
@@ -123,6 +123,8 @@ export type SpaceAggregation =
|
||||
|
||||
export type ColumnType = 'group' | 'aggregation';
|
||||
|
||||
export type FieldResolution = 'exact';
|
||||
|
||||
// ===================== Variable Types =====================
|
||||
|
||||
export type VariableType = 'query' | 'dynamic' | 'custom' | 'text';
|
||||
@@ -142,6 +144,7 @@ export interface TelemetryFieldKey {
|
||||
signal?: SignalType;
|
||||
fieldContext?: FieldContext;
|
||||
fieldDataType?: FieldDataType;
|
||||
fieldResolution?: FieldResolution;
|
||||
materialized?: boolean;
|
||||
isIndexed?: boolean;
|
||||
}
|
||||
|
||||
40
frontend/src/utils/__tests__/semconv.test.ts
Normal file
40
frontend/src/utils/__tests__/semconv.test.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
findOldSemconvNames,
|
||||
getSemconvMembers,
|
||||
getSemconvRename,
|
||||
} from 'utils/semconv';
|
||||
|
||||
describe('semantic convention helpers', () => {
|
||||
it('returns the current name for an old attribute', () => {
|
||||
expect(getSemconvRename('deployment.environment')).toMatchObject({
|
||||
old: 'deployment.environment',
|
||||
current: 'deployment.environment.name',
|
||||
});
|
||||
});
|
||||
|
||||
it('finds old names in editor text without matching larger custom names', () => {
|
||||
expect(
|
||||
findOldSemconvNames(
|
||||
"deployment.environment = 'prod' AND custom.db.system.value = 'x'",
|
||||
),
|
||||
).toStrictEqual([
|
||||
expect.objectContaining({
|
||||
old: 'deployment.environment',
|
||||
current: 'deployment.environment.name',
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not warn for current names', () => {
|
||||
expect(
|
||||
findOldSemconvNames('deployment.environment.name = prod'),
|
||||
).toStrictEqual([]);
|
||||
});
|
||||
|
||||
it('returns current-first members for compatibility readers', () => {
|
||||
expect(getSemconvMembers('http.request.method')).toStrictEqual([
|
||||
'http.request.method',
|
||||
'http.method',
|
||||
]);
|
||||
});
|
||||
});
|
||||
69
frontend/src/utils/semconv.ts
Normal file
69
frontend/src/utils/semconv.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
SEMCONV_FAMILIES,
|
||||
SemconvFamily,
|
||||
} from 'constants/generated/semconvFamilies.gen';
|
||||
|
||||
export type SemconvRename = {
|
||||
old: string;
|
||||
current: string;
|
||||
family: SemconvFamily;
|
||||
};
|
||||
|
||||
const OLD_NAMES = SEMCONV_FAMILIES.flatMap((family) =>
|
||||
family.members.map((member) => ({
|
||||
old: member.name,
|
||||
current: family.current,
|
||||
family,
|
||||
})),
|
||||
);
|
||||
|
||||
const OLD_NAME_INDEX = new Map(OLD_NAMES.map((rename) => [rename.old, rename]));
|
||||
const FAMILY_BY_NAME = new Map(
|
||||
SEMCONV_FAMILIES.flatMap((family) =>
|
||||
[family.current, ...family.members.map((member) => member.name)].map(
|
||||
(name) => [name, family] as const,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
export function getSemconvRename(name: string): SemconvRename | undefined {
|
||||
return OLD_NAME_INDEX.get(name);
|
||||
}
|
||||
|
||||
/** Returns the current name first, followed by every historical spelling. */
|
||||
export function getSemconvMembers(name: string): readonly string[] {
|
||||
const family = FAMILY_BY_NAME.get(name);
|
||||
return family
|
||||
? [family.current, ...family.members.map((member) => member.name)]
|
||||
: [name];
|
||||
}
|
||||
|
||||
export function findOldSemconvNames(text: string): SemconvRename[] {
|
||||
if (!text) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return OLD_NAMES.filter(({ old }) => containsSemconvName(text, old));
|
||||
}
|
||||
|
||||
function containsSemconvName(text: string, name: string): boolean {
|
||||
let offset = 0;
|
||||
while (offset < text.length) {
|
||||
const index = text.indexOf(name, offset);
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
const before = index === 0 ? '' : text[index - 1];
|
||||
const afterIndex = index + name.length;
|
||||
const after = afterIndex === text.length ? '' : text[afterIndex];
|
||||
if (!isSemconvNameCharacter(before) && !isSemconvNameCharacter(after)) {
|
||||
return true;
|
||||
}
|
||||
offset = index + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSemconvNameCharacter(value: string): boolean {
|
||||
return /[A-Za-z0-9_.-]/.test(value);
|
||||
}
|
||||
@@ -50,30 +50,30 @@ primary
|
||||
* [NOT] BETWEEN, [NOT] IN, [NOT] EXISTS, [NOT] REGEXP, [NOT] CONTAINS, etc.
|
||||
*/
|
||||
comparison
|
||||
: key EQUALS value
|
||||
| key (NOT_EQUALS | NEQ) value
|
||||
| key LT value
|
||||
| key LE value
|
||||
| key GT value
|
||||
| key GE value
|
||||
: field EQUALS value
|
||||
| field (NOT_EQUALS | NEQ) value
|
||||
| field LT value
|
||||
| field LE value
|
||||
| field GT value
|
||||
| field GE value
|
||||
|
||||
| key (LIKE | ILIKE) value
|
||||
| key NOT (LIKE | ILIKE) value
|
||||
| field (LIKE | ILIKE) value
|
||||
| field NOT (LIKE | ILIKE) value
|
||||
|
||||
| key BETWEEN value AND value
|
||||
| key NOT BETWEEN value AND value
|
||||
| field BETWEEN value AND value
|
||||
| field NOT BETWEEN value AND value
|
||||
|
||||
| key inClause
|
||||
| key notInClause
|
||||
| field inClause
|
||||
| field notInClause
|
||||
|
||||
| key EXISTS
|
||||
| key NOT EXISTS
|
||||
| field EXISTS
|
||||
| field NOT EXISTS
|
||||
|
||||
| key REGEXP value
|
||||
| key NOT REGEXP value
|
||||
| field REGEXP value
|
||||
| field NOT REGEXP value
|
||||
|
||||
| key CONTAINS value
|
||||
| key NOT CONTAINS value
|
||||
| field CONTAINS value
|
||||
| field NOT CONTAINS value
|
||||
;
|
||||
|
||||
// in(...) or in[...]
|
||||
@@ -126,7 +126,7 @@ functionParamList
|
||||
;
|
||||
|
||||
functionParam
|
||||
: key
|
||||
: field
|
||||
| value
|
||||
| array
|
||||
;
|
||||
@@ -155,6 +155,17 @@ key
|
||||
: KEY
|
||||
;
|
||||
|
||||
// exact(key) disables semantic-convention family resolution for this field.
|
||||
// It is deliberately a field wrapper rather than a general function.
|
||||
field
|
||||
: key
|
||||
| exactCall
|
||||
;
|
||||
|
||||
exactCall
|
||||
: EXACT LPAREN key RPAREN
|
||||
;
|
||||
|
||||
|
||||
/*
|
||||
* Lexer Rules
|
||||
@@ -195,6 +206,7 @@ HAS : [Hh][Aa][Ss] ;
|
||||
HASANY : [Hh][Aa][Ss][Aa][Nn][Yy] ;
|
||||
HASALL : [Hh][Aa][Ss][Aa][Ll][Ll] ;
|
||||
SEARCH : [Ss][Ee][Aa][Rr][Cc][Hh] ;
|
||||
EXACT : [Ee][Xx][Aa][Cc][Tt] ;
|
||||
|
||||
// Potential boolean constants
|
||||
BOOL
|
||||
|
||||
@@ -46,5 +46,23 @@ func (provider *provider) addFieldsRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/fields/semconv-migration", handler.New(provider.authzMiddleware.ViewAccess(provider.fieldsHandler.GetSemconvMigrationReport), handler.OpenAPIDef{
|
||||
ID: "GetSemconvMigrationReport",
|
||||
Tags: []string{"fields"},
|
||||
Summary: "Get semantic-convention migration report",
|
||||
Description: "Returns services that still emit old semantic-convention names without the current family name",
|
||||
Request: nil,
|
||||
RequestQuery: new(telemetrytypes.PostableSemconvMigrationReportParams),
|
||||
RequestContentType: "",
|
||||
Response: new(telemetrytypes.GettableSemconvMigrationReport),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -51,9 +51,9 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"filter": map[string]any{"expression": "k8s.deployment.name = 'api-service'"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "k8s.pod.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment}})",
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment.name}})",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -74,12 +74,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("15m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment"},
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment.name"},
|
||||
"renotify": renotify("4h", "firing"),
|
||||
},
|
||||
"labels": map[string]any{"severity": "critical", "team": "platform"},
|
||||
"annotations": map[string]any{
|
||||
"description": "Pod {{$k8s.pod.name}} CPU is at {{$value}} of request in {{$deployment.environment}}.",
|
||||
"description": "Pod {{$k8s.pod.name}} CPU is at {{$value}} of request in {{$deployment.environment.name}}.",
|
||||
"summary": "Pod CPU above {{$threshold}} of request",
|
||||
},
|
||||
},
|
||||
@@ -170,7 +170,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
{
|
||||
Name: "metric_promql",
|
||||
Summary: "Metric threshold PromQL rule",
|
||||
Description: "PromQL expression instead of the builder. Dotted OTEL resource attributes are quoted (\"deployment.environment\"). Useful for queries that combine series with group_right or other Prom operators.",
|
||||
Description: "PromQL expression instead of the builder. Dotted OTEL resource attributes are quoted (\"deployment.environment.name\"). Useful for queries that combine series with group_right or other Prom operators.",
|
||||
Value: map[string]any{
|
||||
"alert": "Kafka consumer group lag above 1000",
|
||||
"alertType": "METRIC_BASED_ALERT",
|
||||
@@ -187,7 +187,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"type": "promql",
|
||||
"spec": map[string]any{
|
||||
"name": "A",
|
||||
"query": "(max by(topic, partition, \"deployment.environment\")(kafka_log_end_offset) - on(topic, partition, \"deployment.environment\") group_right max by(group, topic, partition, \"deployment.environment\")(kafka_consumer_committed_offset)) > 0",
|
||||
"query": "(max by(topic, partition, \"deployment.environment.name\")(kafka_log_end_offset) - on(topic, partition, \"deployment.environment.name\") group_right max by(group, topic, partition, \"deployment.environment.name\")(kafka_consumer_committed_offset)) > 0",
|
||||
"legend": "{{topic}}/{{partition}} ({{group}})",
|
||||
},
|
||||
},
|
||||
@@ -299,9 +299,9 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"filter": map[string]any{"expression": "service.name = 'payments-api' AND severity_text = 'ERROR' AND body CONTAINS 'panic'"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "k8s.pod.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment}})",
|
||||
"legend": "{{k8s.pod.name}} ({{deployment.environment.name}})",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -322,12 +322,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("5m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment"},
|
||||
"groupBy": []any{"k8s.pod.name", "deployment.environment.name"},
|
||||
"renotify": renotify("15m", "firing"),
|
||||
},
|
||||
"labels": map[string]any{"severity": "critical", "team": "payments"},
|
||||
"annotations": map[string]any{
|
||||
"description": "{{$k8s.pod.name}} emitted {{$value}} panic log(s) in {{$deployment.environment}}.",
|
||||
"description": "{{$k8s.pod.name}} emitted {{$value}} panic log(s) in {{$deployment.environment.name}}.",
|
||||
"summary": "Payments service panic",
|
||||
},
|
||||
},
|
||||
@@ -358,7 +358,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"disabled": true,
|
||||
"aggregations": []any{map[string]any{"expression": "count()"}},
|
||||
"filter": map[string]any{"expression": "service.name = 'payments-api' AND severity_text IN ['ERROR', 'FATAL']"},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
@@ -370,7 +370,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"disabled": true,
|
||||
"aggregations": []any{map[string]any{"expression": "count()"}},
|
||||
"filter": map[string]any{"expression": "service.name = 'payments-api'"},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
"groupBy": []any{map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"}},
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
@@ -378,7 +378,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"spec": map[string]any{
|
||||
"name": "F1",
|
||||
"expression": "(A / B) * 100",
|
||||
"legend": "{{deployment.environment}}",
|
||||
"legend": "{{deployment.environment.name}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -399,12 +399,12 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("5m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"deployment.environment"},
|
||||
"groupBy": []any{"deployment.environment.name"},
|
||||
"renotify": renotify("30m", "firing"),
|
||||
},
|
||||
"labels": map[string]any{"severity": "critical", "team": "payments"},
|
||||
"annotations": map[string]any{
|
||||
"description": "Error log rate in {{$deployment.environment}} is {{$value}}%",
|
||||
"description": "Error log rate in {{$deployment.environment.name}} is {{$value}}%",
|
||||
"summary": "Payments-api error rate above {{$threshold}}%",
|
||||
},
|
||||
},
|
||||
@@ -669,10 +669,10 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"stepInterval": 60,
|
||||
"disabled": true,
|
||||
"aggregations": []any{map[string]any{"expression": "count()"}},
|
||||
"filter": map[string]any{"expression": "service.name CONTAINS 'api' AND http.status_code >= 500"},
|
||||
"filter": map[string]any{"expression": "service.name CONTAINS 'api' AND http.response.status_code >= 500"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "service.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -687,7 +687,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"filter": map[string]any{"expression": "service.name CONTAINS 'api'"},
|
||||
"groupBy": []any{
|
||||
map[string]any{"name": "service.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
map[string]any{"name": "deployment.environment.name", "fieldContext": "resource", "fieldDataType": "string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -696,7 +696,7 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
"spec": map[string]any{
|
||||
"name": "F1",
|
||||
"expression": "(A / B) * 100",
|
||||
"legend": "{{service.name}} ({{deployment.environment}})",
|
||||
"legend": "{{service.name}} ({{deployment.environment.name}})",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -717,14 +717,14 @@ func postableRuleExamples() []handler.OpenAPIExample {
|
||||
},
|
||||
"evaluation": rolling("5m", "1m"),
|
||||
"notificationSettings": map[string]any{
|
||||
"groupBy": []any{"service.name", "deployment.environment"},
|
||||
"groupBy": []any{"service.name", "deployment.environment.name"},
|
||||
"newGroupEvalDelay": "2m",
|
||||
"usePolicy": false,
|
||||
"renotify": renotify("30m", "firing", "nodata"),
|
||||
},
|
||||
"labels": map[string]any{"team": "platform"},
|
||||
"annotations": map[string]any{
|
||||
"description": "{{$service.name}} 5xx rate in {{$deployment.environment}} is {{$value}}%.",
|
||||
"description": "{{$service.name}} 5xx rate in {{$deployment.environment.name}} is {{$value}}%.",
|
||||
"summary": "API service error rate elevated",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -188,11 +188,15 @@ func (r *WhereClauseRewriter) VisitPrimary(ctx *parser.PrimaryContext) any {
|
||||
|
||||
// VisitComparison visits comparison expressions.
|
||||
func (r *WhereClauseRewriter) VisitComparison(ctx *parser.ComparisonContext) any {
|
||||
if ctx.Key() == nil {
|
||||
if ctx.Field() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
key := ctx.Key().GetText()
|
||||
field := ctx.Field().GetText()
|
||||
key := field
|
||||
if exactCall := ctx.Field().ExactCall(); exactCall != nil {
|
||||
key = exactCall.Key().GetText()
|
||||
}
|
||||
r.keysSeen[key] = struct{}{}
|
||||
|
||||
// Check if this key is in the labels and was part of group by
|
||||
@@ -200,13 +204,13 @@ func (r *WhereClauseRewriter) VisitComparison(ctx *parser.ComparisonContext) any
|
||||
if _, partOfGroup := r.groupBySet[key]; partOfGroup {
|
||||
// Case 1: Replace with actual value
|
||||
escapedValue := escapeValueIfNeeded(value)
|
||||
fmt.Fprintf(&r.rewritten, "%s=%s", key, escapedValue)
|
||||
fmt.Fprintf(&r.rewritten, "%s=%s", field, escapedValue)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, keep the original comparison
|
||||
r.rewritten.WriteString(key)
|
||||
r.rewritten.WriteString(field)
|
||||
|
||||
if ctx.EQUALS() != nil {
|
||||
r.rewritten.WriteString("=")
|
||||
@@ -409,8 +413,8 @@ func (r *WhereClauseRewriter) VisitFunctionParamList(ctx *parser.FunctionParamLi
|
||||
|
||||
// VisitFunctionParam visits function parameters.
|
||||
func (r *WhereClauseRewriter) VisitFunctionParam(ctx *parser.FunctionParamContext) any {
|
||||
if ctx.Key() != nil {
|
||||
ctx.Key().Accept(r)
|
||||
if ctx.Field() != nil {
|
||||
r.rewritten.WriteString(ctx.Field().GetText())
|
||||
} else if ctx.Value() != nil {
|
||||
ctx.Value().Accept(r)
|
||||
} else if ctx.Array() != nil {
|
||||
|
||||
@@ -234,6 +234,18 @@ func TestPrepareFiltersV5(t *testing.T) {
|
||||
expected: "(error_details EXISTS) AND service.name='serviceA'",
|
||||
description: "Should preserve EXISTS operator",
|
||||
},
|
||||
{
|
||||
name: "exact_field_label_replacement",
|
||||
labels: map[string]string{
|
||||
"deployment.environment": "production",
|
||||
},
|
||||
whereClause: "exact(deployment.environment) = 'staging'",
|
||||
groupByItems: []qbtypes.GroupByKey{
|
||||
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "deployment.environment"}},
|
||||
},
|
||||
expected: "exact(deployment.environment)='production'",
|
||||
description: "Should keep the exact wrapper when replacing a grouped label",
|
||||
},
|
||||
|
||||
{
|
||||
name: "empty_where_clause_with_labels",
|
||||
|
||||
@@ -23,13 +23,13 @@ func TestSource(t *testing.T) {
|
||||
err := json.Unmarshal(buf.Bytes(), &m)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, m, "code.filepath")
|
||||
assert.Contains(t, m, "code.function")
|
||||
assert.Contains(t, m, "code.lineno")
|
||||
assert.Contains(t, m, "code.file.path")
|
||||
assert.Contains(t, m, "code.function.name")
|
||||
assert.Contains(t, m, "code.line.number")
|
||||
|
||||
assert.Contains(t, m["code.filepath"], "source_test.go")
|
||||
assert.Contains(t, m["code.function"], "TestSource")
|
||||
assert.NotZero(t, m["code.lineno"])
|
||||
assert.Contains(t, m["code.file.path"], "source_test.go")
|
||||
assert.Contains(t, m["code.function.name"], "TestSource")
|
||||
assert.NotZero(t, m["code.line.number"])
|
||||
|
||||
// Ensure the nested "source" key is not present.
|
||||
assert.NotContains(t, m, "source")
|
||||
|
||||
@@ -8,4 +8,7 @@ type Handler interface {
|
||||
|
||||
// Gets the fields values for the given field value selector
|
||||
GetFieldsValues(http.ResponseWriter, *http.Request)
|
||||
|
||||
// Gets services that still emit only historical semantic-convention names.
|
||||
GetSemconvMigrationReport(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package implfields
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||
@@ -16,6 +17,43 @@ type handler struct {
|
||||
telemetryMetadataStore telemetrytypes.MetadataStore
|
||||
}
|
||||
|
||||
func (handler *handler) GetSemconvMigrationReport(rw http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
|
||||
var params telemetrytypes.PostableSemconvMigrationReportParams
|
||||
if err := binding.Query.BindQuery(req.URL.Query(), ¶ms); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
if params.EndUnixMilli == 0 {
|
||||
params.EndUnixMilli = now.UnixMilli()
|
||||
}
|
||||
if params.StartUnixMilli == 0 {
|
||||
params.StartUnixMilli = now.Add(-24 * time.Hour).UnixMilli()
|
||||
}
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
report, err := handler.telemetryMetadataStore.GetSemconvMigrationReport(
|
||||
ctx,
|
||||
valuer.MustNewUUID(claims.OrgID),
|
||||
params.StartUnixMilli,
|
||||
params.EndUnixMilli,
|
||||
)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, report)
|
||||
}
|
||||
|
||||
func NewHandler(settings factory.ProviderSettings, telemetryMetadataStore telemetrytypes.MetadataStore) fields.Handler {
|
||||
return &handler{
|
||||
telemetryMetadataStore: telemetryMetadataStore,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,12 +25,13 @@ HAS=24
|
||||
HASANY=25
|
||||
HASALL=26
|
||||
SEARCH=27
|
||||
BOOL=28
|
||||
NUMBER=29
|
||||
QUOTED_TEXT=30
|
||||
KEY=31
|
||||
WS=32
|
||||
FREETEXT=33
|
||||
EXACT=28
|
||||
BOOL=29
|
||||
NUMBER=30
|
||||
QUOTED_TEXT=31
|
||||
KEY=32
|
||||
WS=33
|
||||
FREETEXT=34
|
||||
'('=1
|
||||
')'=2
|
||||
'['=3
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,12 +25,13 @@ HAS=24
|
||||
HASANY=25
|
||||
HASALL=26
|
||||
SEARCH=27
|
||||
BOOL=28
|
||||
NUMBER=29
|
||||
QUOTED_TEXT=30
|
||||
KEY=31
|
||||
WS=32
|
||||
FREETEXT=33
|
||||
EXACT=28
|
||||
BOOL=29
|
||||
NUMBER=30
|
||||
QUOTED_TEXT=31
|
||||
KEY=32
|
||||
WS=33
|
||||
FREETEXT=34
|
||||
'('=1
|
||||
')'=2
|
||||
'['=3
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
|
||||
|
||||
package parser // FilterQuery
|
||||
|
||||
import "github.com/antlr4-go/antlr/v4"
|
||||
|
||||
// BaseFilterQueryListener is a complete listener for a parse tree produced by FilterQueryParser.
|
||||
@@ -128,3 +127,15 @@ func (s *BaseFilterQueryListener) EnterKey(ctx *KeyContext) {}
|
||||
|
||||
// ExitKey is called when production key is exited.
|
||||
func (s *BaseFilterQueryListener) ExitKey(ctx *KeyContext) {}
|
||||
|
||||
// EnterField is called when production field is entered.
|
||||
func (s *BaseFilterQueryListener) EnterField(ctx *FieldContext) {}
|
||||
|
||||
// ExitField is called when production field is exited.
|
||||
func (s *BaseFilterQueryListener) ExitField(ctx *FieldContext) {}
|
||||
|
||||
// EnterExactCall is called when production exactCall is entered.
|
||||
func (s *BaseFilterQueryListener) EnterExactCall(ctx *ExactCallContext) {}
|
||||
|
||||
// ExitExactCall is called when production exactCall is exited.
|
||||
func (s *BaseFilterQueryListener) ExitExactCall(ctx *ExactCallContext) {}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
|
||||
|
||||
package parser // FilterQuery
|
||||
|
||||
import "github.com/antlr4-go/antlr/v4"
|
||||
|
||||
type BaseFilterQueryVisitor struct {
|
||||
@@ -79,3 +78,11 @@ func (v *BaseFilterQueryVisitor) VisitValue(ctx *ValueContext) interface{} {
|
||||
func (v *BaseFilterQueryVisitor) VisitKey(ctx *KeyContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFilterQueryVisitor) VisitField(ctx *FieldContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
func (v *BaseFilterQueryVisitor) VisitExactCall(ctx *ExactCallContext) interface{} {
|
||||
return v.VisitChildren(ctx)
|
||||
}
|
||||
|
||||
@@ -50,66 +50,69 @@ func filterquerylexerLexerInit() {
|
||||
"", "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
|
||||
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "ILIKE", "BETWEEN", "EXISTS",
|
||||
"REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR", "HASTOKEN", "HAS", "HASANY",
|
||||
"HASALL", "SEARCH", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY", "WS", "FREETEXT",
|
||||
"HASALL", "SEARCH", "EXACT", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY",
|
||||
"WS", "FREETEXT",
|
||||
}
|
||||
staticData.RuleNames = []string{
|
||||
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
|
||||
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "ILIKE", "BETWEEN", "EXISTS",
|
||||
"REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR", "HASTOKEN", "HAS", "HASANY",
|
||||
"HASALL", "SEARCH", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", "SEGMENT",
|
||||
"EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", "FREETEXT",
|
||||
"HASALL", "SEARCH", "EXACT", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT",
|
||||
"SEGMENT", "EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT",
|
||||
"FREETEXT",
|
||||
}
|
||||
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
|
||||
staticData.serializedATN = []int32{
|
||||
4, 0, 33, 329, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
|
||||
4, 0, 34, 337, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
|
||||
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
|
||||
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
|
||||
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
|
||||
20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
|
||||
2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
|
||||
31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
|
||||
7, 36, 2, 37, 7, 37, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1,
|
||||
4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 91, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7,
|
||||
1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11,
|
||||
1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1,
|
||||
13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15,
|
||||
1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 134, 8, 15, 1, 16, 1, 16, 1, 16, 1,
|
||||
16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17,
|
||||
1, 17, 3, 17, 151, 8, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1,
|
||||
19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22,
|
||||
1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1,
|
||||
24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25,
|
||||
1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1,
|
||||
27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 210,
|
||||
8, 27, 1, 28, 1, 28, 1, 29, 3, 29, 215, 8, 29, 1, 29, 4, 29, 218, 8, 29,
|
||||
11, 29, 12, 29, 219, 1, 29, 1, 29, 5, 29, 224, 8, 29, 10, 29, 12, 29, 227,
|
||||
9, 29, 3, 29, 229, 8, 29, 1, 29, 1, 29, 3, 29, 233, 8, 29, 1, 29, 4, 29,
|
||||
236, 8, 29, 11, 29, 12, 29, 237, 3, 29, 240, 8, 29, 1, 29, 3, 29, 243,
|
||||
8, 29, 1, 29, 1, 29, 4, 29, 247, 8, 29, 11, 29, 12, 29, 248, 1, 29, 1,
|
||||
29, 3, 29, 253, 8, 29, 1, 29, 4, 29, 256, 8, 29, 11, 29, 12, 29, 257, 3,
|
||||
29, 260, 8, 29, 3, 29, 262, 8, 29, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 268,
|
||||
8, 30, 10, 30, 12, 30, 271, 9, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5,
|
||||
30, 278, 8, 30, 10, 30, 12, 30, 281, 9, 30, 1, 30, 3, 30, 284, 8, 30, 1,
|
||||
31, 1, 31, 5, 31, 288, 8, 31, 10, 31, 12, 31, 291, 9, 31, 1, 32, 1, 32,
|
||||
1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1,
|
||||
34, 1, 34, 4, 34, 307, 8, 34, 11, 34, 12, 34, 308, 5, 34, 311, 8, 34, 10,
|
||||
34, 12, 34, 314, 9, 34, 1, 35, 4, 35, 317, 8, 35, 11, 35, 12, 35, 318,
|
||||
1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 4, 37, 326, 8, 37, 11, 37, 12, 37, 327,
|
||||
0, 0, 38, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19,
|
||||
10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37,
|
||||
19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55,
|
||||
28, 57, 0, 59, 29, 61, 30, 63, 0, 65, 0, 67, 0, 69, 31, 71, 32, 73, 0,
|
||||
75, 33, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0,
|
||||
75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 66, 66, 98, 98, 2, 0, 84,
|
||||
84, 116, 116, 2, 0, 87, 87, 119, 119, 2, 0, 78, 78, 110, 110, 2, 0, 88,
|
||||
88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71,
|
||||
71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 79, 79,
|
||||
111, 111, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104,
|
||||
104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102,
|
||||
102, 2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92,
|
||||
4, 0, 35, 36, 64, 90, 95, 95, 97, 123, 7, 0, 35, 36, 45, 45, 47, 58, 64,
|
||||
90, 95, 95, 97, 123, 125, 125, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57,
|
||||
8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 353,
|
||||
7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2,
|
||||
1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 93, 8, 5, 1, 6, 1, 6, 1,
|
||||
6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11,
|
||||
1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1,
|
||||
13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14,
|
||||
1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 136, 8, 15, 1, 16, 1,
|
||||
16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17,
|
||||
1, 17, 1, 17, 1, 17, 3, 17, 153, 8, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1,
|
||||
19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 22,
|
||||
1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1,
|
||||
23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25,
|
||||
1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1,
|
||||
26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28,
|
||||
1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 3, 28, 218, 8, 28, 1, 29, 1,
|
||||
29, 1, 30, 3, 30, 223, 8, 30, 1, 30, 4, 30, 226, 8, 30, 11, 30, 12, 30,
|
||||
227, 1, 30, 1, 30, 5, 30, 232, 8, 30, 10, 30, 12, 30, 235, 9, 30, 3, 30,
|
||||
237, 8, 30, 1, 30, 1, 30, 3, 30, 241, 8, 30, 1, 30, 4, 30, 244, 8, 30,
|
||||
11, 30, 12, 30, 245, 3, 30, 248, 8, 30, 1, 30, 3, 30, 251, 8, 30, 1, 30,
|
||||
1, 30, 4, 30, 255, 8, 30, 11, 30, 12, 30, 256, 1, 30, 1, 30, 3, 30, 261,
|
||||
8, 30, 1, 30, 4, 30, 264, 8, 30, 11, 30, 12, 30, 265, 3, 30, 268, 8, 30,
|
||||
3, 30, 270, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 276, 8, 31, 10, 31,
|
||||
12, 31, 279, 9, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 286, 8, 31,
|
||||
10, 31, 12, 31, 289, 9, 31, 1, 31, 3, 31, 292, 8, 31, 1, 32, 1, 32, 5,
|
||||
32, 296, 8, 32, 10, 32, 12, 32, 299, 9, 32, 1, 33, 1, 33, 1, 33, 1, 34,
|
||||
1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 4,
|
||||
35, 315, 8, 35, 11, 35, 12, 35, 316, 5, 35, 319, 8, 35, 10, 35, 12, 35,
|
||||
322, 9, 35, 1, 36, 4, 36, 325, 8, 36, 11, 36, 12, 36, 326, 1, 36, 1, 36,
|
||||
1, 37, 1, 37, 1, 38, 4, 38, 334, 8, 38, 11, 38, 12, 38, 335, 0, 0, 39,
|
||||
1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11,
|
||||
23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20,
|
||||
41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29,
|
||||
59, 0, 61, 30, 63, 31, 65, 0, 67, 0, 69, 0, 71, 32, 73, 33, 75, 0, 77,
|
||||
34, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75,
|
||||
75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 66, 66, 98, 98, 2, 0, 84, 84,
|
||||
116, 116, 2, 0, 87, 87, 119, 119, 2, 0, 78, 78, 110, 110, 2, 0, 88, 88,
|
||||
120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71,
|
||||
103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 79, 79, 111,
|
||||
111, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104, 104,
|
||||
2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102, 102,
|
||||
2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 4, 0,
|
||||
35, 36, 64, 90, 95, 95, 97, 123, 7, 0, 35, 36, 45, 45, 47, 58, 64, 90,
|
||||
95, 95, 97, 123, 125, 125, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8,
|
||||
0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 361,
|
||||
0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0,
|
||||
0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0,
|
||||
0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0,
|
||||
@@ -117,107 +120,110 @@ func filterquerylexerLexerInit() {
|
||||
0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39,
|
||||
1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0,
|
||||
47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0,
|
||||
0, 55, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 69, 1, 0, 0,
|
||||
0, 0, 71, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 1, 77, 1, 0, 0, 0, 3, 79, 1, 0,
|
||||
0, 0, 5, 81, 1, 0, 0, 0, 7, 83, 1, 0, 0, 0, 9, 85, 1, 0, 0, 0, 11, 90,
|
||||
1, 0, 0, 0, 13, 92, 1, 0, 0, 0, 15, 95, 1, 0, 0, 0, 17, 98, 1, 0, 0, 0,
|
||||
19, 100, 1, 0, 0, 0, 21, 103, 1, 0, 0, 0, 23, 105, 1, 0, 0, 0, 25, 108,
|
||||
1, 0, 0, 0, 27, 113, 1, 0, 0, 0, 29, 119, 1, 0, 0, 0, 31, 127, 1, 0, 0,
|
||||
0, 33, 135, 1, 0, 0, 0, 35, 142, 1, 0, 0, 0, 37, 152, 1, 0, 0, 0, 39, 155,
|
||||
1, 0, 0, 0, 41, 159, 1, 0, 0, 0, 43, 163, 1, 0, 0, 0, 45, 166, 1, 0, 0,
|
||||
0, 47, 175, 1, 0, 0, 0, 49, 179, 1, 0, 0, 0, 51, 186, 1, 0, 0, 0, 53, 193,
|
||||
1, 0, 0, 0, 55, 209, 1, 0, 0, 0, 57, 211, 1, 0, 0, 0, 59, 261, 1, 0, 0,
|
||||
0, 61, 283, 1, 0, 0, 0, 63, 285, 1, 0, 0, 0, 65, 292, 1, 0, 0, 0, 67, 295,
|
||||
1, 0, 0, 0, 69, 299, 1, 0, 0, 0, 71, 316, 1, 0, 0, 0, 73, 322, 1, 0, 0,
|
||||
0, 75, 325, 1, 0, 0, 0, 77, 78, 5, 40, 0, 0, 78, 2, 1, 0, 0, 0, 79, 80,
|
||||
5, 41, 0, 0, 80, 4, 1, 0, 0, 0, 81, 82, 5, 91, 0, 0, 82, 6, 1, 0, 0, 0,
|
||||
83, 84, 5, 93, 0, 0, 84, 8, 1, 0, 0, 0, 85, 86, 5, 44, 0, 0, 86, 10, 1,
|
||||
0, 0, 0, 87, 91, 5, 61, 0, 0, 88, 89, 5, 61, 0, 0, 89, 91, 5, 61, 0, 0,
|
||||
90, 87, 1, 0, 0, 0, 90, 88, 1, 0, 0, 0, 91, 12, 1, 0, 0, 0, 92, 93, 5,
|
||||
33, 0, 0, 93, 94, 5, 61, 0, 0, 94, 14, 1, 0, 0, 0, 95, 96, 5, 60, 0, 0,
|
||||
96, 97, 5, 62, 0, 0, 97, 16, 1, 0, 0, 0, 98, 99, 5, 60, 0, 0, 99, 18, 1,
|
||||
0, 0, 0, 100, 101, 5, 60, 0, 0, 101, 102, 5, 61, 0, 0, 102, 20, 1, 0, 0,
|
||||
0, 103, 104, 5, 62, 0, 0, 104, 22, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106,
|
||||
107, 5, 61, 0, 0, 107, 24, 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110,
|
||||
7, 1, 0, 0, 110, 111, 7, 2, 0, 0, 111, 112, 7, 3, 0, 0, 112, 26, 1, 0,
|
||||
0, 0, 113, 114, 7, 1, 0, 0, 114, 115, 7, 0, 0, 0, 115, 116, 7, 1, 0, 0,
|
||||
116, 117, 7, 2, 0, 0, 117, 118, 7, 3, 0, 0, 118, 28, 1, 0, 0, 0, 119, 120,
|
||||
7, 4, 0, 0, 120, 121, 7, 3, 0, 0, 121, 122, 7, 5, 0, 0, 122, 123, 7, 6,
|
||||
0, 0, 123, 124, 7, 3, 0, 0, 124, 125, 7, 3, 0, 0, 125, 126, 7, 7, 0, 0,
|
||||
126, 30, 1, 0, 0, 0, 127, 128, 7, 3, 0, 0, 128, 129, 7, 8, 0, 0, 129, 130,
|
||||
7, 1, 0, 0, 130, 131, 7, 9, 0, 0, 131, 133, 7, 5, 0, 0, 132, 134, 7, 9,
|
||||
0, 0, 133, 132, 1, 0, 0, 0, 133, 134, 1, 0, 0, 0, 134, 32, 1, 0, 0, 0,
|
||||
135, 136, 7, 10, 0, 0, 136, 137, 7, 3, 0, 0, 137, 138, 7, 11, 0, 0, 138,
|
||||
139, 7, 3, 0, 0, 139, 140, 7, 8, 0, 0, 140, 141, 7, 12, 0, 0, 141, 34,
|
||||
1, 0, 0, 0, 142, 143, 7, 13, 0, 0, 143, 144, 7, 14, 0, 0, 144, 145, 7,
|
||||
7, 0, 0, 145, 146, 7, 5, 0, 0, 146, 147, 7, 15, 0, 0, 147, 148, 7, 1, 0,
|
||||
0, 148, 150, 7, 7, 0, 0, 149, 151, 7, 9, 0, 0, 150, 149, 1, 0, 0, 0, 150,
|
||||
151, 1, 0, 0, 0, 151, 36, 1, 0, 0, 0, 152, 153, 7, 1, 0, 0, 153, 154, 7,
|
||||
7, 0, 0, 154, 38, 1, 0, 0, 0, 155, 156, 7, 7, 0, 0, 156, 157, 7, 14, 0,
|
||||
0, 157, 158, 7, 5, 0, 0, 158, 40, 1, 0, 0, 0, 159, 160, 7, 15, 0, 0, 160,
|
||||
161, 7, 7, 0, 0, 161, 162, 7, 16, 0, 0, 162, 42, 1, 0, 0, 0, 163, 164,
|
||||
7, 14, 0, 0, 164, 165, 7, 10, 0, 0, 165, 44, 1, 0, 0, 0, 166, 167, 7, 17,
|
||||
0, 0, 167, 168, 7, 15, 0, 0, 168, 169, 7, 9, 0, 0, 169, 170, 7, 5, 0, 0,
|
||||
170, 171, 7, 14, 0, 0, 171, 172, 7, 2, 0, 0, 172, 173, 7, 3, 0, 0, 173,
|
||||
174, 7, 7, 0, 0, 174, 46, 1, 0, 0, 0, 175, 176, 7, 17, 0, 0, 176, 177,
|
||||
7, 15, 0, 0, 177, 178, 7, 9, 0, 0, 178, 48, 1, 0, 0, 0, 179, 180, 7, 17,
|
||||
0, 0, 180, 181, 7, 15, 0, 0, 181, 182, 7, 9, 0, 0, 182, 183, 7, 15, 0,
|
||||
0, 183, 184, 7, 7, 0, 0, 184, 185, 7, 18, 0, 0, 185, 50, 1, 0, 0, 0, 186,
|
||||
187, 7, 17, 0, 0, 187, 188, 7, 15, 0, 0, 188, 189, 7, 9, 0, 0, 189, 190,
|
||||
7, 15, 0, 0, 190, 191, 7, 0, 0, 0, 191, 192, 7, 0, 0, 0, 192, 52, 1, 0,
|
||||
0, 0, 193, 194, 7, 9, 0, 0, 194, 195, 7, 3, 0, 0, 195, 196, 7, 15, 0, 0,
|
||||
196, 197, 7, 10, 0, 0, 197, 198, 7, 13, 0, 0, 198, 199, 7, 17, 0, 0, 199,
|
||||
54, 1, 0, 0, 0, 200, 201, 7, 5, 0, 0, 201, 202, 7, 10, 0, 0, 202, 203,
|
||||
7, 19, 0, 0, 203, 210, 7, 3, 0, 0, 204, 205, 7, 20, 0, 0, 205, 206, 7,
|
||||
15, 0, 0, 206, 207, 7, 0, 0, 0, 207, 208, 7, 9, 0, 0, 208, 210, 7, 3, 0,
|
||||
0, 209, 200, 1, 0, 0, 0, 209, 204, 1, 0, 0, 0, 210, 56, 1, 0, 0, 0, 211,
|
||||
212, 7, 21, 0, 0, 212, 58, 1, 0, 0, 0, 213, 215, 3, 57, 28, 0, 214, 213,
|
||||
1, 0, 0, 0, 214, 215, 1, 0, 0, 0, 215, 217, 1, 0, 0, 0, 216, 218, 3, 73,
|
||||
36, 0, 217, 216, 1, 0, 0, 0, 218, 219, 1, 0, 0, 0, 219, 217, 1, 0, 0, 0,
|
||||
219, 220, 1, 0, 0, 0, 220, 228, 1, 0, 0, 0, 221, 225, 5, 46, 0, 0, 222,
|
||||
224, 3, 73, 36, 0, 223, 222, 1, 0, 0, 0, 224, 227, 1, 0, 0, 0, 225, 223,
|
||||
1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 229, 1, 0, 0, 0, 227, 225, 1, 0,
|
||||
0, 0, 228, 221, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 239, 1, 0, 0, 0,
|
||||
230, 232, 7, 3, 0, 0, 231, 233, 3, 57, 28, 0, 232, 231, 1, 0, 0, 0, 232,
|
||||
233, 1, 0, 0, 0, 233, 235, 1, 0, 0, 0, 234, 236, 3, 73, 36, 0, 235, 234,
|
||||
1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 235, 1, 0, 0, 0, 237, 238, 1, 0,
|
||||
0, 0, 238, 240, 1, 0, 0, 0, 239, 230, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0,
|
||||
240, 262, 1, 0, 0, 0, 241, 243, 3, 57, 28, 0, 242, 241, 1, 0, 0, 0, 242,
|
||||
243, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 246, 5, 46, 0, 0, 245, 247,
|
||||
3, 73, 36, 0, 246, 245, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 246, 1,
|
||||
0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 259, 1, 0, 0, 0, 250, 252, 7, 3, 0,
|
||||
0, 251, 253, 3, 57, 28, 0, 252, 251, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0,
|
||||
253, 255, 1, 0, 0, 0, 254, 256, 3, 73, 36, 0, 255, 254, 1, 0, 0, 0, 256,
|
||||
257, 1, 0, 0, 0, 257, 255, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 260,
|
||||
1, 0, 0, 0, 259, 250, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 262, 1, 0,
|
||||
0, 0, 261, 214, 1, 0, 0, 0, 261, 242, 1, 0, 0, 0, 262, 60, 1, 0, 0, 0,
|
||||
263, 269, 5, 34, 0, 0, 264, 268, 8, 22, 0, 0, 265, 266, 5, 92, 0, 0, 266,
|
||||
268, 9, 0, 0, 0, 267, 264, 1, 0, 0, 0, 267, 265, 1, 0, 0, 0, 268, 271,
|
||||
1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 272, 1, 0,
|
||||
0, 0, 271, 269, 1, 0, 0, 0, 272, 284, 5, 34, 0, 0, 273, 279, 5, 39, 0,
|
||||
0, 274, 278, 8, 23, 0, 0, 275, 276, 5, 92, 0, 0, 276, 278, 9, 0, 0, 0,
|
||||
277, 274, 1, 0, 0, 0, 277, 275, 1, 0, 0, 0, 278, 281, 1, 0, 0, 0, 279,
|
||||
277, 1, 0, 0, 0, 279, 280, 1, 0, 0, 0, 280, 282, 1, 0, 0, 0, 281, 279,
|
||||
1, 0, 0, 0, 282, 284, 5, 39, 0, 0, 283, 263, 1, 0, 0, 0, 283, 273, 1, 0,
|
||||
0, 0, 284, 62, 1, 0, 0, 0, 285, 289, 7, 24, 0, 0, 286, 288, 7, 25, 0, 0,
|
||||
287, 286, 1, 0, 0, 0, 288, 291, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 289,
|
||||
290, 1, 0, 0, 0, 290, 64, 1, 0, 0, 0, 291, 289, 1, 0, 0, 0, 292, 293, 5,
|
||||
91, 0, 0, 293, 294, 5, 93, 0, 0, 294, 66, 1, 0, 0, 0, 295, 296, 5, 91,
|
||||
0, 0, 296, 297, 5, 42, 0, 0, 297, 298, 5, 93, 0, 0, 298, 68, 1, 0, 0, 0,
|
||||
299, 312, 3, 63, 31, 0, 300, 301, 5, 46, 0, 0, 301, 311, 3, 63, 31, 0,
|
||||
302, 311, 3, 65, 32, 0, 303, 311, 3, 67, 33, 0, 304, 306, 5, 46, 0, 0,
|
||||
305, 307, 3, 73, 36, 0, 306, 305, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308,
|
||||
306, 1, 0, 0, 0, 308, 309, 1, 0, 0, 0, 309, 311, 1, 0, 0, 0, 310, 300,
|
||||
1, 0, 0, 0, 310, 302, 1, 0, 0, 0, 310, 303, 1, 0, 0, 0, 310, 304, 1, 0,
|
||||
0, 0, 311, 314, 1, 0, 0, 0, 312, 310, 1, 0, 0, 0, 312, 313, 1, 0, 0, 0,
|
||||
313, 70, 1, 0, 0, 0, 314, 312, 1, 0, 0, 0, 315, 317, 7, 26, 0, 0, 316,
|
||||
315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 316, 1, 0, 0, 0, 318, 319,
|
||||
1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 321, 6, 35, 0, 0, 321, 72, 1, 0,
|
||||
0, 0, 322, 323, 7, 27, 0, 0, 323, 74, 1, 0, 0, 0, 324, 326, 8, 28, 0, 0,
|
||||
325, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 325, 1, 0, 0, 0, 327,
|
||||
328, 1, 0, 0, 0, 328, 76, 1, 0, 0, 0, 29, 0, 90, 133, 150, 209, 214, 219,
|
||||
225, 228, 232, 237, 239, 242, 248, 252, 257, 259, 261, 267, 269, 277, 279,
|
||||
283, 289, 308, 310, 312, 318, 327, 1, 6, 0, 0,
|
||||
0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0,
|
||||
0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 1, 79, 1, 0,
|
||||
0, 0, 3, 81, 1, 0, 0, 0, 5, 83, 1, 0, 0, 0, 7, 85, 1, 0, 0, 0, 9, 87, 1,
|
||||
0, 0, 0, 11, 92, 1, 0, 0, 0, 13, 94, 1, 0, 0, 0, 15, 97, 1, 0, 0, 0, 17,
|
||||
100, 1, 0, 0, 0, 19, 102, 1, 0, 0, 0, 21, 105, 1, 0, 0, 0, 23, 107, 1,
|
||||
0, 0, 0, 25, 110, 1, 0, 0, 0, 27, 115, 1, 0, 0, 0, 29, 121, 1, 0, 0, 0,
|
||||
31, 129, 1, 0, 0, 0, 33, 137, 1, 0, 0, 0, 35, 144, 1, 0, 0, 0, 37, 154,
|
||||
1, 0, 0, 0, 39, 157, 1, 0, 0, 0, 41, 161, 1, 0, 0, 0, 43, 165, 1, 0, 0,
|
||||
0, 45, 168, 1, 0, 0, 0, 47, 177, 1, 0, 0, 0, 49, 181, 1, 0, 0, 0, 51, 188,
|
||||
1, 0, 0, 0, 53, 195, 1, 0, 0, 0, 55, 202, 1, 0, 0, 0, 57, 217, 1, 0, 0,
|
||||
0, 59, 219, 1, 0, 0, 0, 61, 269, 1, 0, 0, 0, 63, 291, 1, 0, 0, 0, 65, 293,
|
||||
1, 0, 0, 0, 67, 300, 1, 0, 0, 0, 69, 303, 1, 0, 0, 0, 71, 307, 1, 0, 0,
|
||||
0, 73, 324, 1, 0, 0, 0, 75, 330, 1, 0, 0, 0, 77, 333, 1, 0, 0, 0, 79, 80,
|
||||
5, 40, 0, 0, 80, 2, 1, 0, 0, 0, 81, 82, 5, 41, 0, 0, 82, 4, 1, 0, 0, 0,
|
||||
83, 84, 5, 91, 0, 0, 84, 6, 1, 0, 0, 0, 85, 86, 5, 93, 0, 0, 86, 8, 1,
|
||||
0, 0, 0, 87, 88, 5, 44, 0, 0, 88, 10, 1, 0, 0, 0, 89, 93, 5, 61, 0, 0,
|
||||
90, 91, 5, 61, 0, 0, 91, 93, 5, 61, 0, 0, 92, 89, 1, 0, 0, 0, 92, 90, 1,
|
||||
0, 0, 0, 93, 12, 1, 0, 0, 0, 94, 95, 5, 33, 0, 0, 95, 96, 5, 61, 0, 0,
|
||||
96, 14, 1, 0, 0, 0, 97, 98, 5, 60, 0, 0, 98, 99, 5, 62, 0, 0, 99, 16, 1,
|
||||
0, 0, 0, 100, 101, 5, 60, 0, 0, 101, 18, 1, 0, 0, 0, 102, 103, 5, 60, 0,
|
||||
0, 103, 104, 5, 61, 0, 0, 104, 20, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106,
|
||||
22, 1, 0, 0, 0, 107, 108, 5, 62, 0, 0, 108, 109, 5, 61, 0, 0, 109, 24,
|
||||
1, 0, 0, 0, 110, 111, 7, 0, 0, 0, 111, 112, 7, 1, 0, 0, 112, 113, 7, 2,
|
||||
0, 0, 113, 114, 7, 3, 0, 0, 114, 26, 1, 0, 0, 0, 115, 116, 7, 1, 0, 0,
|
||||
116, 117, 7, 0, 0, 0, 117, 118, 7, 1, 0, 0, 118, 119, 7, 2, 0, 0, 119,
|
||||
120, 7, 3, 0, 0, 120, 28, 1, 0, 0, 0, 121, 122, 7, 4, 0, 0, 122, 123, 7,
|
||||
3, 0, 0, 123, 124, 7, 5, 0, 0, 124, 125, 7, 6, 0, 0, 125, 126, 7, 3, 0,
|
||||
0, 126, 127, 7, 3, 0, 0, 127, 128, 7, 7, 0, 0, 128, 30, 1, 0, 0, 0, 129,
|
||||
130, 7, 3, 0, 0, 130, 131, 7, 8, 0, 0, 131, 132, 7, 1, 0, 0, 132, 133,
|
||||
7, 9, 0, 0, 133, 135, 7, 5, 0, 0, 134, 136, 7, 9, 0, 0, 135, 134, 1, 0,
|
||||
0, 0, 135, 136, 1, 0, 0, 0, 136, 32, 1, 0, 0, 0, 137, 138, 7, 10, 0, 0,
|
||||
138, 139, 7, 3, 0, 0, 139, 140, 7, 11, 0, 0, 140, 141, 7, 3, 0, 0, 141,
|
||||
142, 7, 8, 0, 0, 142, 143, 7, 12, 0, 0, 143, 34, 1, 0, 0, 0, 144, 145,
|
||||
7, 13, 0, 0, 145, 146, 7, 14, 0, 0, 146, 147, 7, 7, 0, 0, 147, 148, 7,
|
||||
5, 0, 0, 148, 149, 7, 15, 0, 0, 149, 150, 7, 1, 0, 0, 150, 152, 7, 7, 0,
|
||||
0, 151, 153, 7, 9, 0, 0, 152, 151, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153,
|
||||
36, 1, 0, 0, 0, 154, 155, 7, 1, 0, 0, 155, 156, 7, 7, 0, 0, 156, 38, 1,
|
||||
0, 0, 0, 157, 158, 7, 7, 0, 0, 158, 159, 7, 14, 0, 0, 159, 160, 7, 5, 0,
|
||||
0, 160, 40, 1, 0, 0, 0, 161, 162, 7, 15, 0, 0, 162, 163, 7, 7, 0, 0, 163,
|
||||
164, 7, 16, 0, 0, 164, 42, 1, 0, 0, 0, 165, 166, 7, 14, 0, 0, 166, 167,
|
||||
7, 10, 0, 0, 167, 44, 1, 0, 0, 0, 168, 169, 7, 17, 0, 0, 169, 170, 7, 15,
|
||||
0, 0, 170, 171, 7, 9, 0, 0, 171, 172, 7, 5, 0, 0, 172, 173, 7, 14, 0, 0,
|
||||
173, 174, 7, 2, 0, 0, 174, 175, 7, 3, 0, 0, 175, 176, 7, 7, 0, 0, 176,
|
||||
46, 1, 0, 0, 0, 177, 178, 7, 17, 0, 0, 178, 179, 7, 15, 0, 0, 179, 180,
|
||||
7, 9, 0, 0, 180, 48, 1, 0, 0, 0, 181, 182, 7, 17, 0, 0, 182, 183, 7, 15,
|
||||
0, 0, 183, 184, 7, 9, 0, 0, 184, 185, 7, 15, 0, 0, 185, 186, 7, 7, 0, 0,
|
||||
186, 187, 7, 18, 0, 0, 187, 50, 1, 0, 0, 0, 188, 189, 7, 17, 0, 0, 189,
|
||||
190, 7, 15, 0, 0, 190, 191, 7, 9, 0, 0, 191, 192, 7, 15, 0, 0, 192, 193,
|
||||
7, 0, 0, 0, 193, 194, 7, 0, 0, 0, 194, 52, 1, 0, 0, 0, 195, 196, 7, 9,
|
||||
0, 0, 196, 197, 7, 3, 0, 0, 197, 198, 7, 15, 0, 0, 198, 199, 7, 10, 0,
|
||||
0, 199, 200, 7, 13, 0, 0, 200, 201, 7, 17, 0, 0, 201, 54, 1, 0, 0, 0, 202,
|
||||
203, 7, 3, 0, 0, 203, 204, 7, 8, 0, 0, 204, 205, 7, 15, 0, 0, 205, 206,
|
||||
7, 13, 0, 0, 206, 207, 7, 5, 0, 0, 207, 56, 1, 0, 0, 0, 208, 209, 7, 5,
|
||||
0, 0, 209, 210, 7, 10, 0, 0, 210, 211, 7, 19, 0, 0, 211, 218, 7, 3, 0,
|
||||
0, 212, 213, 7, 20, 0, 0, 213, 214, 7, 15, 0, 0, 214, 215, 7, 0, 0, 0,
|
||||
215, 216, 7, 9, 0, 0, 216, 218, 7, 3, 0, 0, 217, 208, 1, 0, 0, 0, 217,
|
||||
212, 1, 0, 0, 0, 218, 58, 1, 0, 0, 0, 219, 220, 7, 21, 0, 0, 220, 60, 1,
|
||||
0, 0, 0, 221, 223, 3, 59, 29, 0, 222, 221, 1, 0, 0, 0, 222, 223, 1, 0,
|
||||
0, 0, 223, 225, 1, 0, 0, 0, 224, 226, 3, 75, 37, 0, 225, 224, 1, 0, 0,
|
||||
0, 226, 227, 1, 0, 0, 0, 227, 225, 1, 0, 0, 0, 227, 228, 1, 0, 0, 0, 228,
|
||||
236, 1, 0, 0, 0, 229, 233, 5, 46, 0, 0, 230, 232, 3, 75, 37, 0, 231, 230,
|
||||
1, 0, 0, 0, 232, 235, 1, 0, 0, 0, 233, 231, 1, 0, 0, 0, 233, 234, 1, 0,
|
||||
0, 0, 234, 237, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 236, 229, 1, 0, 0, 0,
|
||||
236, 237, 1, 0, 0, 0, 237, 247, 1, 0, 0, 0, 238, 240, 7, 3, 0, 0, 239,
|
||||
241, 3, 59, 29, 0, 240, 239, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 243,
|
||||
1, 0, 0, 0, 242, 244, 3, 75, 37, 0, 243, 242, 1, 0, 0, 0, 244, 245, 1,
|
||||
0, 0, 0, 245, 243, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 248, 1, 0, 0,
|
||||
0, 247, 238, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 270, 1, 0, 0, 0, 249,
|
||||
251, 3, 59, 29, 0, 250, 249, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 252,
|
||||
1, 0, 0, 0, 252, 254, 5, 46, 0, 0, 253, 255, 3, 75, 37, 0, 254, 253, 1,
|
||||
0, 0, 0, 255, 256, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 256, 257, 1, 0, 0,
|
||||
0, 257, 267, 1, 0, 0, 0, 258, 260, 7, 3, 0, 0, 259, 261, 3, 59, 29, 0,
|
||||
260, 259, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 263, 1, 0, 0, 0, 262,
|
||||
264, 3, 75, 37, 0, 263, 262, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 263,
|
||||
1, 0, 0, 0, 265, 266, 1, 0, 0, 0, 266, 268, 1, 0, 0, 0, 267, 258, 1, 0,
|
||||
0, 0, 267, 268, 1, 0, 0, 0, 268, 270, 1, 0, 0, 0, 269, 222, 1, 0, 0, 0,
|
||||
269, 250, 1, 0, 0, 0, 270, 62, 1, 0, 0, 0, 271, 277, 5, 34, 0, 0, 272,
|
||||
276, 8, 22, 0, 0, 273, 274, 5, 92, 0, 0, 274, 276, 9, 0, 0, 0, 275, 272,
|
||||
1, 0, 0, 0, 275, 273, 1, 0, 0, 0, 276, 279, 1, 0, 0, 0, 277, 275, 1, 0,
|
||||
0, 0, 277, 278, 1, 0, 0, 0, 278, 280, 1, 0, 0, 0, 279, 277, 1, 0, 0, 0,
|
||||
280, 292, 5, 34, 0, 0, 281, 287, 5, 39, 0, 0, 282, 286, 8, 23, 0, 0, 283,
|
||||
284, 5, 92, 0, 0, 284, 286, 9, 0, 0, 0, 285, 282, 1, 0, 0, 0, 285, 283,
|
||||
1, 0, 0, 0, 286, 289, 1, 0, 0, 0, 287, 285, 1, 0, 0, 0, 287, 288, 1, 0,
|
||||
0, 0, 288, 290, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 290, 292, 5, 39, 0, 0,
|
||||
291, 271, 1, 0, 0, 0, 291, 281, 1, 0, 0, 0, 292, 64, 1, 0, 0, 0, 293, 297,
|
||||
7, 24, 0, 0, 294, 296, 7, 25, 0, 0, 295, 294, 1, 0, 0, 0, 296, 299, 1,
|
||||
0, 0, 0, 297, 295, 1, 0, 0, 0, 297, 298, 1, 0, 0, 0, 298, 66, 1, 0, 0,
|
||||
0, 299, 297, 1, 0, 0, 0, 300, 301, 5, 91, 0, 0, 301, 302, 5, 93, 0, 0,
|
||||
302, 68, 1, 0, 0, 0, 303, 304, 5, 91, 0, 0, 304, 305, 5, 42, 0, 0, 305,
|
||||
306, 5, 93, 0, 0, 306, 70, 1, 0, 0, 0, 307, 320, 3, 65, 32, 0, 308, 309,
|
||||
5, 46, 0, 0, 309, 319, 3, 65, 32, 0, 310, 319, 3, 67, 33, 0, 311, 319,
|
||||
3, 69, 34, 0, 312, 314, 5, 46, 0, 0, 313, 315, 3, 75, 37, 0, 314, 313,
|
||||
1, 0, 0, 0, 315, 316, 1, 0, 0, 0, 316, 314, 1, 0, 0, 0, 316, 317, 1, 0,
|
||||
0, 0, 317, 319, 1, 0, 0, 0, 318, 308, 1, 0, 0, 0, 318, 310, 1, 0, 0, 0,
|
||||
318, 311, 1, 0, 0, 0, 318, 312, 1, 0, 0, 0, 319, 322, 1, 0, 0, 0, 320,
|
||||
318, 1, 0, 0, 0, 320, 321, 1, 0, 0, 0, 321, 72, 1, 0, 0, 0, 322, 320, 1,
|
||||
0, 0, 0, 323, 325, 7, 26, 0, 0, 324, 323, 1, 0, 0, 0, 325, 326, 1, 0, 0,
|
||||
0, 326, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 1, 0, 0, 0, 328,
|
||||
329, 6, 36, 0, 0, 329, 74, 1, 0, 0, 0, 330, 331, 7, 27, 0, 0, 331, 76,
|
||||
1, 0, 0, 0, 332, 334, 8, 28, 0, 0, 333, 332, 1, 0, 0, 0, 334, 335, 1, 0,
|
||||
0, 0, 335, 333, 1, 0, 0, 0, 335, 336, 1, 0, 0, 0, 336, 78, 1, 0, 0, 0,
|
||||
29, 0, 92, 135, 152, 217, 222, 227, 233, 236, 240, 245, 247, 250, 256,
|
||||
260, 265, 267, 269, 275, 277, 285, 287, 291, 297, 316, 318, 320, 326, 335,
|
||||
1, 6, 0, 0,
|
||||
}
|
||||
deserializer := antlr.NewATNDeserializer(nil)
|
||||
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
|
||||
@@ -285,10 +291,11 @@ const (
|
||||
FilterQueryLexerHASANY = 25
|
||||
FilterQueryLexerHASALL = 26
|
||||
FilterQueryLexerSEARCH = 27
|
||||
FilterQueryLexerBOOL = 28
|
||||
FilterQueryLexerNUMBER = 29
|
||||
FilterQueryLexerQUOTED_TEXT = 30
|
||||
FilterQueryLexerKEY = 31
|
||||
FilterQueryLexerWS = 32
|
||||
FilterQueryLexerFREETEXT = 33
|
||||
FilterQueryLexerEXACT = 28
|
||||
FilterQueryLexerBOOL = 29
|
||||
FilterQueryLexerNUMBER = 30
|
||||
FilterQueryLexerQUOTED_TEXT = 31
|
||||
FilterQueryLexerKEY = 32
|
||||
FilterQueryLexerWS = 33
|
||||
FilterQueryLexerFREETEXT = 34
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
|
||||
|
||||
package parser // FilterQuery
|
||||
|
||||
import "github.com/antlr4-go/antlr/v4"
|
||||
|
||||
// FilterQueryListener is a complete listener for a parse tree produced by FilterQueryParser.
|
||||
@@ -62,6 +61,12 @@ type FilterQueryListener interface {
|
||||
// EnterKey is called when entering the key production.
|
||||
EnterKey(c *KeyContext)
|
||||
|
||||
// EnterField is called when entering the field production.
|
||||
EnterField(c *FieldContext)
|
||||
|
||||
// EnterExactCall is called when entering the exactCall production.
|
||||
EnterExactCall(c *ExactCallContext)
|
||||
|
||||
// ExitQuery is called when exiting the query production.
|
||||
ExitQuery(c *QueryContext)
|
||||
|
||||
@@ -115,4 +120,10 @@ type FilterQueryListener interface {
|
||||
|
||||
// ExitKey is called when exiting the key production.
|
||||
ExitKey(c *KeyContext)
|
||||
|
||||
// ExitField is called when exiting the field production.
|
||||
ExitField(c *FieldContext)
|
||||
|
||||
// ExitExactCall is called when exiting the exactCall production.
|
||||
ExitExactCall(c *ExactCallContext)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
// Code generated from FilterQuery.g4 by ANTLR 4.13.2. DO NOT EDIT.
|
||||
|
||||
package parser // FilterQuery
|
||||
|
||||
import "github.com/antlr4-go/antlr/v4"
|
||||
|
||||
// A complete Visitor for a parse tree produced by FilterQueryParser.
|
||||
@@ -61,4 +60,10 @@ type FilterQueryVisitor interface {
|
||||
|
||||
// Visit a parse tree produced by FilterQueryParser#key.
|
||||
VisitKey(ctx *KeyContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FilterQueryParser#field.
|
||||
VisitField(ctx *FieldContext) interface{}
|
||||
|
||||
// Visit a parse tree produced by FilterQueryParser#exactCall.
|
||||
VisitExactCall(ctx *ExactCallContext) interface{}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (v *Visitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
|
||||
}
|
||||
|
||||
func (v *Visitor) VisitComparison(ctx *grammar.ComparisonContext) any {
|
||||
key := strings.TrimSpace(ctx.Key().GetText())
|
||||
key := strings.TrimSpace(fieldKeyText(ctx.Field()))
|
||||
operation, ok := v.extractOperation(ctx)
|
||||
if !ok {
|
||||
return ""
|
||||
@@ -512,3 +512,11 @@ func trimQuotes(s string) string {
|
||||
s = strings.ReplaceAll(s, `\'`, `'`)
|
||||
return s
|
||||
}
|
||||
|
||||
// fieldKeyText reads the key of a field; exact() carries no meaning here.
|
||||
func fieldKeyText(field grammar.IFieldContext) string {
|
||||
if exactCall := field.ExactCall(); exactCall != nil {
|
||||
return exactCall.Key().GetText()
|
||||
}
|
||||
return field.Key().GetText()
|
||||
}
|
||||
|
||||
@@ -116,6 +116,9 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
|
||||
// safely assume ms regardless of the resolution the caller sent.
|
||||
req.Start = querybuilder.ToMilliSecs(req.Start)
|
||||
req.End = querybuilder.ToMilliSecs(req.End)
|
||||
// Key resolution rewrites parts of the builder queries, so the caller's
|
||||
// spelling is read before that.
|
||||
semconvResolutions := semconvResolutionsForRequest(req)
|
||||
|
||||
event := &qbtypes.QBEvent{
|
||||
Version: "v5",
|
||||
@@ -164,6 +167,7 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
|
||||
qbResp, qbErr := q.run(ctx, orgID, queries, req, steps, event, preseededResults)
|
||||
if qbResp != nil {
|
||||
qbResp.QBEvent = event
|
||||
qbResp.Meta.SemconvResolutions = semconvResolutions
|
||||
if len(intervalWarnings) != 0 && req.RequestType == qbtypes.RequestTypeTimeSeries {
|
||||
if qbResp.Warning == nil {
|
||||
qbResp.Warning = &qbtypes.QueryWarnData{
|
||||
|
||||
197
pkg/querier/semconv_resolutions.go
Normal file
197
pkg/querier/semconv_resolutions.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package querier
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
grammar "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar"
|
||||
"github.com/SigNoz/signoz/pkg/querybuilder"
|
||||
"github.com/SigNoz/signoz/pkg/semconv"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/antlr4-go/antlr/v4"
|
||||
)
|
||||
|
||||
// semconvResolutionsForRequest reports the family spellings a query builder
|
||||
// request names. Raw ClickHouse SQL and PromQL are not rewritten, so they
|
||||
// report nothing.
|
||||
func semconvResolutionsForRequest(req *qbtypes.QueryRangeRequest) []qbtypes.SemconvResolution {
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
resolutions := make([]qbtypes.SemconvResolution, 0)
|
||||
seen := make(map[string]struct{})
|
||||
for _, envelope := range req.CompositeQuery.Queries {
|
||||
signal, metric, applies := semconvResolutionSignal(envelope.Spec)
|
||||
if !applies {
|
||||
continue
|
||||
}
|
||||
|
||||
text, err := semconvResolutionText(envelope.Spec)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for family := range semconv.All() {
|
||||
if family.Kind() == semconv.KindMetric && signal != telemetrytypes.SignalMetrics {
|
||||
continue
|
||||
}
|
||||
selector := telemetrytypes.FieldKeySelector{Name: family.Current(), Signal: signal, MetricContext: metric}
|
||||
spellings := semconv.Members(family.Kind(), selector)
|
||||
if family.Kind() == semconv.KindAttribute && signal == telemetrytypes.SignalMetrics {
|
||||
spellings = querybuilder.MetricLabelSpellings(selector)
|
||||
}
|
||||
if len(spellings) <= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, requested := range spellings {
|
||||
if !containsSemconvName(text, requested) {
|
||||
continue
|
||||
}
|
||||
identity := family.Kind().StringValue() + "\x00" + requested + "\x00" + family.Current()
|
||||
if _, ok := seen[identity]; ok {
|
||||
continue
|
||||
}
|
||||
seen[identity] = struct{}{}
|
||||
resolutions = append(resolutions, qbtypes.SemconvResolution{
|
||||
Requested: requested,
|
||||
Current: family.Current(),
|
||||
Members: append([]string{family.Current()}, family.Old()...),
|
||||
Kind: family.Kind().StringValue(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(resolutions) == 0 {
|
||||
return nil
|
||||
}
|
||||
return resolutions
|
||||
}
|
||||
|
||||
// semconvResolutionText renders the spec without the fields that opt out of
|
||||
// family resolution: exact(key) in expressions and keys with an exact
|
||||
// fieldResolution.
|
||||
func semconvResolutionText(spec any) (string, error) {
|
||||
payload, err := json.Marshal(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var document any
|
||||
if err := json.Unmarshal(payload, &document); err != nil {
|
||||
return "", err
|
||||
}
|
||||
scrubExactFields(document)
|
||||
payload, err = json.Marshal(document)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(payload), nil
|
||||
}
|
||||
|
||||
func scrubExactFields(value any) {
|
||||
switch typed := value.(type) {
|
||||
case map[string]any:
|
||||
if resolution, ok := typed["fieldResolution"].(string); ok && resolution == telemetrytypes.FieldResolutionExact.StringValue() {
|
||||
delete(typed, "name")
|
||||
}
|
||||
for key, child := range typed {
|
||||
if text, ok := child.(string); ok {
|
||||
typed[key] = scrubExactCalls(text)
|
||||
continue
|
||||
}
|
||||
scrubExactFields(child)
|
||||
}
|
||||
case []any:
|
||||
for _, child := range typed {
|
||||
scrubExactFields(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// scrubExactCalls blanks every exact(key) in an expression.
|
||||
func scrubExactCalls(input string) string {
|
||||
lexer := grammar.NewFilterQueryLexer(antlr.NewInputStream(input))
|
||||
tokens := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
|
||||
tokens.Fill()
|
||||
all := tokens.GetAllTokens()
|
||||
result := []rune(input)
|
||||
for index := 0; index+3 < len(all); index++ {
|
||||
if all[index].GetTokenType() != grammar.FilterQueryLexerEXACT ||
|
||||
all[index+1].GetTokenType() != grammar.FilterQueryLexerLPAREN ||
|
||||
all[index+2].GetTokenType() != grammar.FilterQueryLexerKEY ||
|
||||
all[index+3].GetTokenType() != grammar.FilterQueryLexerRPAREN {
|
||||
continue
|
||||
}
|
||||
start, stop := all[index].GetStart(), all[index+3].GetStop()
|
||||
if start < 0 || stop >= len(result) {
|
||||
continue
|
||||
}
|
||||
for position := start; position <= stop; position++ {
|
||||
result[position] = ' '
|
||||
}
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func semconvResolutionSignal(spec any) (telemetrytypes.Signal, *telemetrytypes.MetricContext, bool) {
|
||||
switch query := spec.(type) {
|
||||
case qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation], qbtypes.QueryBuilderTraceOperator:
|
||||
return telemetrytypes.SignalTraces, nil, true
|
||||
case qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]:
|
||||
return telemetrytypes.SignalLogs, nil, true
|
||||
case qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]:
|
||||
var metric *telemetrytypes.MetricContext
|
||||
if len(query.Aggregations) > 0 {
|
||||
metric = &telemetrytypes.MetricContext{MetricName: query.Aggregations[0].MetricName}
|
||||
}
|
||||
return telemetrytypes.SignalMetrics, metric, true
|
||||
case qbtypes.QueryBuilderJoin:
|
||||
// A join can hold several signals. The unspecified signal keeps the
|
||||
// family signal gates in force and admits every applicable family.
|
||||
return telemetrytypes.SignalUnspecified, nil, true
|
||||
default:
|
||||
return telemetrytypes.SignalUnspecified, nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// containsSemconvName reports whether name occurs in text as a whole key. A
|
||||
// field context prefix counts as a boundary, so `resource.x` names `x`.
|
||||
func containsSemconvName(text, name string) bool {
|
||||
for offset := 0; offset < len(text); {
|
||||
index := strings.Index(text[offset:], name)
|
||||
if index < 0 {
|
||||
return false
|
||||
}
|
||||
start := offset + index
|
||||
end := start + len(name)
|
||||
if hasSemconvNameStartBoundary(text, start) &&
|
||||
(end == len(text) || !isSemconvNameRune(rune(text[end]))) {
|
||||
return true
|
||||
}
|
||||
offset = start + 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasSemconvNameStartBoundary(text string, start int) bool {
|
||||
if start == 0 || !isSemconvNameRune(rune(text[start-1])) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, qualifier := range []string{"resource.", "attribute.", "tag.", "point."} {
|
||||
qualifierStart := start - len(qualifier)
|
||||
if qualifierStart >= 0 && text[qualifierStart:start] == qualifier &&
|
||||
(qualifierStart == 0 || !isSemconvNameRune(rune(text[qualifierStart-1]))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isSemconvNameRune(r rune) bool {
|
||||
return unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' || r == '.' || r == '-'
|
||||
}
|
||||
134
pkg/querier/semconv_resolutions_test.go
Normal file
134
pkg/querier/semconv_resolutions_test.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package querier
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSemconvResolutionsReportsOldTraceAttribute(t *testing.T) {
|
||||
req := &qbtypes.QueryRangeRequest{
|
||||
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
Filter: &qbtypes.Filter{Expression: "deployment.environment = 'prod'"},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
assert.Equal(t, []qbtypes.SemconvResolution{{
|
||||
Requested: "deployment.environment",
|
||||
Current: "deployment.environment.name",
|
||||
Members: []string{"deployment.environment.name", "deployment.environment"},
|
||||
Kind: "attribute",
|
||||
}}, semconvResolutionsForRequest(req), "old trace attribute should be reported as a family resolution")
|
||||
}
|
||||
|
||||
func TestSemconvResolutionsReportsContextQualifiedOldAttribute(t *testing.T) {
|
||||
var req qbtypes.QueryRangeRequest
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"start": 1,
|
||||
"end": 2,
|
||||
"requestType": "raw",
|
||||
"compositeQuery": {
|
||||
"queries": [{
|
||||
"type": "builder_query",
|
||||
"spec": {
|
||||
"signal": "traces",
|
||||
"name": "A",
|
||||
"filter": {"expression": "resource.deployment.environment EXISTS"}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}`), &req)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, []qbtypes.SemconvResolution{{
|
||||
Requested: "deployment.environment",
|
||||
Current: "deployment.environment.name",
|
||||
Members: []string{"deployment.environment.name", "deployment.environment"},
|
||||
Kind: "attribute",
|
||||
}}, semconvResolutionsForRequest(&req), "qualified old trace attribute should be reported after request decoding")
|
||||
}
|
||||
|
||||
func TestSemconvResolutionsReportsCurrentLogAttribute(t *testing.T) {
|
||||
req := &qbtypes.QueryRangeRequest{
|
||||
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.LogAggregation]{
|
||||
Signal: telemetrytypes.SignalLogs,
|
||||
SelectFields: []telemetrytypes.TelemetryFieldKey{{
|
||||
Name: "deployment.environment.name",
|
||||
}},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
assert.Equal(t, []qbtypes.SemconvResolution{{
|
||||
Requested: "deployment.environment.name",
|
||||
Current: "deployment.environment.name",
|
||||
Members: []string{"deployment.environment.name", "deployment.environment"},
|
||||
Kind: "attribute",
|
||||
}}, semconvResolutionsForRequest(req), "current log attribute should identify its complete family")
|
||||
}
|
||||
|
||||
func TestSemconvResolutionsReportsSpanMetricResourceLabel(t *testing.T) {
|
||||
req := &qbtypes.QueryRangeRequest{
|
||||
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{{MetricName: "signoz_calls_total"}},
|
||||
Filter: &qbtypes.Filter{Expression: "resource_deployment.environment = 'prod'"},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
assert.Equal(t, []qbtypes.SemconvResolution{{
|
||||
Requested: "resource_deployment.environment",
|
||||
Current: "deployment.environment.name",
|
||||
Members: []string{"deployment.environment.name", "deployment.environment"},
|
||||
Kind: "attribute",
|
||||
}}, semconvResolutionsForRequest(req), "a span-metrics resource label is reported under its stored spelling")
|
||||
}
|
||||
|
||||
func TestSemconvResolutionsIgnoresRawSQL(t *testing.T) {
|
||||
req := &qbtypes.QueryRangeRequest{
|
||||
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
|
||||
Spec: qbtypes.ClickHouseQuery{Query: "SELECT attributes_string['deployment.environment']"},
|
||||
}}},
|
||||
}
|
||||
|
||||
assert.Empty(t, semconvResolutionsForRequest(req), "raw SQL is not rewritten and should not report a resolution")
|
||||
}
|
||||
|
||||
func TestSemconvResolutionsRequiresNameBoundary(t *testing.T) {
|
||||
req := &qbtypes.QueryRangeRequest{
|
||||
CompositeQuery: qbtypes.CompositeQuery{Queries: []qbtypes.QueryEnvelope{{
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.TraceAggregation]{
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
Filter: &qbtypes.Filter{Expression: "custom.deployment.environment = 'prod'"},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
||||
assert.Empty(t, semconvResolutionsForRequest(req), "a family name embedded in a larger custom key must not match")
|
||||
}
|
||||
|
||||
func TestSemconvResolutionsForDecodedRequest(t *testing.T) {
|
||||
var req qbtypes.QueryRangeRequest
|
||||
require.NoError(t, json.Unmarshal([]byte(`{
|
||||
"requestType":"raw",
|
||||
"compositeQuery":{"queries":[{"type":"builder_query","spec":{
|
||||
"signal":"traces","name":"A","filter":{"expression":"resource.deployment.environment EXISTS"}
|
||||
}}]}
|
||||
}`), &req))
|
||||
assert.Equal(t, []qbtypes.SemconvResolution{{
|
||||
Requested: "deployment.environment",
|
||||
Current: "deployment.environment.name",
|
||||
Members: []string{"deployment.environment.name", "deployment.environment"},
|
||||
Kind: "attribute",
|
||||
}}, semconvResolutionsForRequest(&req))
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
errorsV2 "github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/semconv"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
func (r *ClickHouseReader) GetQBFilterSuggestionsForLogs(
|
||||
@@ -248,17 +250,24 @@ func newRankingStrategy() attribRankingStrategy {
|
||||
|
||||
// Synonyms of interesting attributes should come next
|
||||
resourceHierarchy := fingerprint.ResourceHierarchy()
|
||||
for _, attr := range []string{
|
||||
for _, current := range []string{
|
||||
"service.name",
|
||||
"deployment.environment",
|
||||
"deployment.environment.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.pod.name",
|
||||
"k8s.container.name",
|
||||
"k8s.node.name",
|
||||
} {
|
||||
interestingResourceAttrsInDescRank = append(
|
||||
interestingResourceAttrsInDescRank, resourceHierarchy.Synonyms(attr)...,
|
||||
)
|
||||
members := semconv.Members(semconv.KindAttribute, telemetrytypes.FieldKeySelector{
|
||||
Name: current,
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
})
|
||||
for _, member := range members {
|
||||
interestingResourceAttrsInDescRank = append(interestingResourceAttrsInDescRank, member)
|
||||
interestingResourceAttrsInDescRank = append(
|
||||
interestingResourceAttrsInDescRank, resourceHierarchy.Synonyms(member)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interestingResourceAttrsInAscRank := interestingResourceAttrsInDescRank[:]
|
||||
|
||||
@@ -1808,10 +1808,10 @@ func (aH *APIHandler) onboardConsumers(
|
||||
status = "1"
|
||||
}
|
||||
} else if key == "cgroup" {
|
||||
attribute = "messaging.kafka.consumer.group"
|
||||
attribute = "messaging.consumer.group.name"
|
||||
if intValue != 0 {
|
||||
status = "0"
|
||||
message = "messaging.kafka.consumer.group attribute is not present in your spans"
|
||||
message = "messaging.consumer.group.name attribute is not present in your spans"
|
||||
} else {
|
||||
status = "1"
|
||||
}
|
||||
@@ -1824,10 +1824,10 @@ func (aH *APIHandler) onboardConsumers(
|
||||
status = "1"
|
||||
}
|
||||
} else if key == "clientid" {
|
||||
attribute = "messaging.client_id"
|
||||
attribute = "messaging.client.id"
|
||||
if intValue != 0 {
|
||||
status = "0"
|
||||
message = "messaging.client_id attribute is not present in your spans"
|
||||
message = "messaging.client.id attribute is not present in your spans"
|
||||
} else {
|
||||
status = "1"
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ Response in query range `table` format
|
||||
"status": "1"
|
||||
},
|
||||
{
|
||||
"attribute": "messaging.client_id",
|
||||
"attribute": "messaging.client.id",
|
||||
"error_message": "",
|
||||
"status": "1"
|
||||
},
|
||||
@@ -350,7 +350,7 @@ Response in query range `table` format
|
||||
"status": "1"
|
||||
},
|
||||
{
|
||||
"attribute": "messaging.kafka.consumer.group",
|
||||
"attribute": "messaging.consumer.group.name",
|
||||
"error_message": "",
|
||||
"status": "1"
|
||||
},
|
||||
@@ -392,7 +392,7 @@ Response in query range `table` format
|
||||
"status": "0"
|
||||
},
|
||||
{
|
||||
"attribute": "messaging.client_id",
|
||||
"attribute": "messaging.client.id",
|
||||
"error_message": "",
|
||||
"status": "1"
|
||||
},
|
||||
@@ -407,8 +407,8 @@ Response in query range `table` format
|
||||
"status": "1"
|
||||
},
|
||||
{
|
||||
"attribute": "messaging.kafka.consumer.group",
|
||||
"error_message": "messaging.kafka.consumer.group attribute is not present in your spans",
|
||||
"attribute": "messaging.consumer.group.name",
|
||||
"error_message": "messaging.consumer.group.name attribute is not present in your spans",
|
||||
"status": "0"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,8 +2,20 @@ package kafka
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/semconvsql"
|
||||
)
|
||||
|
||||
var kafkaSemconvAttributes = []string{
|
||||
"messaging.destination.name",
|
||||
"messaging.consumer.group.name",
|
||||
"messaging.client.id",
|
||||
}
|
||||
|
||||
func resolveKafkaSemconvSQL(query string) string {
|
||||
return semconvsql.ResolveTraceStringAttributes(query, kafkaSemconvAttributes...)
|
||||
}
|
||||
|
||||
func generateConsumerSQL(start, end int64, topic, partition, consumerGroup, queueType string) string {
|
||||
timeRange := (end - start) / 1000000000
|
||||
tsBucketStart := (start / 1000000000) - 1800
|
||||
@@ -26,7 +38,7 @@ WITH consumer_query AS (
|
||||
AND attribute_string_messaging$$system = '%s'
|
||||
AND attributes_string['messaging.destination.name'] = '%s'
|
||||
AND attributes_string['messaging.destination.partition.id'] = '%s'
|
||||
AND attributes_string['messaging.kafka.consumer.group'] = '%s'
|
||||
AND attributes_string['messaging.consumer.group.name'] = '%s'
|
||||
GROUP BY resource_string_service$$name
|
||||
)
|
||||
|
||||
@@ -41,7 +53,7 @@ FROM
|
||||
ORDER BY
|
||||
resource_string_service$$name;
|
||||
`, start, end, tsBucketStart, tsBucketEnd, queueType, topic, partition, consumerGroup, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
// S1 landing
|
||||
@@ -77,7 +89,7 @@ FROM
|
||||
ORDER BY
|
||||
topic;
|
||||
`, start, end, tsBucketStart, tsBucketEnd, queueType, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
// S1 consumer
|
||||
@@ -88,7 +100,7 @@ func generateConsumerPartitionLatencySQL(start, end int64, topic, partition, que
|
||||
query := fmt.Sprintf(`
|
||||
WITH consumer_pl AS (
|
||||
SELECT
|
||||
attributes_string['messaging.kafka.consumer.group'] AS consumer_group,
|
||||
attributes_string['messaging.consumer.group.name'] AS consumer_group,
|
||||
resource_string_service$$name,
|
||||
quantile(0.99)(durationNano) / 1000000 AS p99,
|
||||
COUNT(*) AS total_requests,
|
||||
@@ -117,7 +129,7 @@ FROM
|
||||
ORDER BY
|
||||
consumer_group;
|
||||
`, start, end, tsBucketStart, tsBucketEnd, queueType, topic, partition, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
// S3, producer overview
|
||||
@@ -153,7 +165,7 @@ SELECT
|
||||
FROM
|
||||
producer_latency
|
||||
`, start, end, tsBucketStart, tsBucketEnd, queueType, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
// S3, producer topic/service overview
|
||||
@@ -189,7 +201,7 @@ SELECT
|
||||
FROM
|
||||
consumer_latency
|
||||
`, start, end, tsBucketStart, tsBucketEnd, service, queueType, topic, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
// S3 consumer overview
|
||||
@@ -229,7 +241,7 @@ FROM
|
||||
ORDER BY
|
||||
topic;
|
||||
`, start, end, tsBucketStart, tsBucketEnd, queueType, timeRange, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
// S3 consumer topic/service
|
||||
@@ -265,7 +277,7 @@ SELECT
|
||||
FROM
|
||||
consumer_latency
|
||||
`, start, end, tsBucketStart, tsBucketEnd, service, queueType, topic, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
// s4
|
||||
@@ -315,7 +327,7 @@ GROUP BY
|
||||
producer_service,
|
||||
consumer_service
|
||||
`, start, end, start, end, queueType, queueType, evalTime, evalTime)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
func generateProducerSQL(start, end int64, topic, partition, queueType string) string {
|
||||
@@ -352,7 +364,7 @@ FROM
|
||||
ORDER BY
|
||||
resource_string_service$$name;
|
||||
`, start, end, tsBucketStart, tsBucketEnd, queueType, topic, partition, timeRange)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
func generateNetworkLatencyThroughputSQL(start, end int64, consumerGroup, partitionID, queueType string) string {
|
||||
@@ -361,7 +373,7 @@ func generateNetworkLatencyThroughputSQL(start, end int64, consumerGroup, partit
|
||||
tsBucketEnd := end / 1000000000
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
attributes_string['messaging.client_id'] AS client_id,
|
||||
attributes_string['messaging.client.id'] AS client_id,
|
||||
resources_string['service.instance.id'] AS service_instance_id,
|
||||
resource_string_service$$name AS service_name,
|
||||
count(*) / %d AS throughput
|
||||
@@ -373,12 +385,12 @@ WHERE
|
||||
AND ts_bucket_start <= '%d'
|
||||
AND kind = 5
|
||||
AND attribute_string_messaging$$system = '%s'
|
||||
AND attributes_string['messaging.kafka.consumer.group'] = '%s'
|
||||
AND attributes_string['messaging.consumer.group.name'] = '%s'
|
||||
AND attributes_string['messaging.destination.partition.id'] = '%s'
|
||||
GROUP BY service_name, client_id, service_instance_id
|
||||
ORDER BY throughput DESC
|
||||
`, timeRange, start, end, tsBucketStart, tsBucketEnd, queueType, consumerGroup, partitionID)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
func onboardProducersSQL(start, end int64, queueType string) string {
|
||||
@@ -398,7 +410,7 @@ WHERE
|
||||
AND timestamp <= '%d'
|
||||
AND ts_bucket_start >= '%d'
|
||||
AND ts_bucket_start <= '%d';`, queueType, start, end, tsBucketStart, tsBucketEnd)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
func onboardConsumerSQL(start, end int64, queueType string) string {
|
||||
@@ -412,9 +424,9 @@ SELECT
|
||||
COUNT(resource_string_service$$name) = 0 AS svc,
|
||||
COUNT(IF(has(attributes_string, 'messaging.destination.name'), 1, NULL)) = 0 AS destination,
|
||||
COUNT(IF(has(attributes_string, 'messaging.destination.partition.id'), 1, NULL)) = 0 AS partition,
|
||||
COUNT(IF(has(attributes_string, 'messaging.kafka.consumer.group'), 1, NULL)) = 0 AS cgroup,
|
||||
COUNT(IF(has(attributes_string, 'messaging.consumer.group.name'), 1, NULL)) = 0 AS cgroup,
|
||||
COUNT(IF(has(attributes_number, 'messaging.message.body.size'), 1, NULL)) = 0 AS bodysize,
|
||||
COUNT(IF(has(attributes_string, 'messaging.client_id'), 1, NULL)) = 0 AS clientid,
|
||||
COUNT(IF(has(attributes_string, 'messaging.client.id'), 1, NULL)) = 0 AS clientid,
|
||||
COUNT(IF(has(resources_string, 'service.instance.id'), 1, NULL)) = 0 AS instanceid
|
||||
FROM signoz_traces.distributed_signoz_index_v3
|
||||
WHERE
|
||||
@@ -422,5 +434,5 @@ WHERE
|
||||
AND timestamp <= '%d'
|
||||
AND ts_bucket_start >= '%d'
|
||||
AND ts_bucket_start <= '%d' ;`, queueType, start, end, tsBucketStart, tsBucketEnd)
|
||||
return query
|
||||
return resolveKafkaSemconvSQL(query)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/semconvsql"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
format "github.com/SigNoz/signoz/pkg/query-service/utils"
|
||||
)
|
||||
@@ -59,10 +60,7 @@ WITH
|
||||
ELSE 'undefined'
|
||||
END AS messaging_system,
|
||||
kind_string,
|
||||
COALESCE(
|
||||
NULLIF(attributes_string['messaging.destination.name'], ''),
|
||||
NULLIF(attributes_string['messaging.destination'], '')
|
||||
) AS destination,
|
||||
attributes_string['messaging.destination.name'] AS destination,
|
||||
durationNano,
|
||||
status_code
|
||||
FROM signoz_traces.distributed_signoz_index_v3
|
||||
@@ -113,5 +111,5 @@ ORDER BY
|
||||
whereSQL, timeRangeSecs,
|
||||
)
|
||||
|
||||
return query
|
||||
return semconvsql.ResolveTraceStringAttributes(query, "messaging.destination.name")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Package semconvsql expands current semantic-convention attribute names in
|
||||
// integration-owned SQL into current-first compatibility expressions.
|
||||
package semconvsql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/clickhousesql"
|
||||
"github.com/SigNoz/signoz/pkg/semconv"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
var traceAttributeSelector = telemetrytypes.FieldKeySelector{
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
FieldContext: telemetrytypes.FieldContextAttribute,
|
||||
}
|
||||
|
||||
// ResolveTraceStringAttributes replaces direct map reads and existence checks
|
||||
// for currentNames. Callers keep only current names in their SQL templates;
|
||||
// the generated family table supplies every historical spelling.
|
||||
func ResolveTraceStringAttributes(query string, currentNames ...string) string {
|
||||
for _, current := range currentNames {
|
||||
selector := traceAttributeSelector
|
||||
selector.Name = current
|
||||
members := semconv.Members(semconv.KindAttribute, selector)
|
||||
if len(members) < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
directRead := attributeRead(current)
|
||||
reads := make([]string, 0, len(members))
|
||||
exists := make([]string, 0, len(members))
|
||||
for _, member := range members {
|
||||
reads = append(reads, fmt.Sprintf("NULLIF(%s, '')", attributeRead(member)))
|
||||
exists = append(exists, "has(attributes_string, "+clickhousesql.StringLiteral(member)+")")
|
||||
}
|
||||
|
||||
query = strings.ReplaceAll(query, directRead, "COALESCE("+strings.Join(reads, ", ")+")")
|
||||
query = strings.ReplaceAll(
|
||||
query,
|
||||
"has(attributes_string, "+clickhousesql.StringLiteral(current)+")",
|
||||
"("+strings.Join(exists, " OR ")+")",
|
||||
)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func attributeRead(name string) string {
|
||||
return "attributes_string[" + clickhousesql.StringLiteral(name) + "]"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package semconvsql
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestResolveTraceStringAttributesExpandsDirectReads(t *testing.T) {
|
||||
query := `SELECT attributes_string['messaging.destination.name'] AS destination
|
||||
FROM spans`
|
||||
|
||||
resolved := ResolveTraceStringAttributes(query, "messaging.destination.name")
|
||||
|
||||
assert.Contains(t, resolved, "COALESCE(NULLIF(attributes_string['messaging.destination.name'], ''), NULLIF(attributes_string['messaging.destination'], ''))", "direct attribute read should coalesce every family member")
|
||||
}
|
||||
|
||||
func TestResolveTraceStringAttributesExpandsExistenceChecks(t *testing.T) {
|
||||
query := `SELECT count() FROM spans
|
||||
WHERE has(attributes_string, 'messaging.destination.name')`
|
||||
|
||||
resolved := ResolveTraceStringAttributes(query, "messaging.destination.name")
|
||||
|
||||
assert.Contains(t, resolved, "(has(attributes_string, 'messaging.destination.name') OR has(attributes_string, 'messaging.destination'))", "existence check should cover every family member")
|
||||
}
|
||||
|
||||
func TestResolveTraceStringAttributesKeepsCurrentNameFirst(t *testing.T) {
|
||||
query := `SELECT attributes_string['messaging.destination.name'] FROM spans`
|
||||
|
||||
resolved := ResolveTraceStringAttributes(query, "messaging.destination.name")
|
||||
|
||||
assert.Less(t, strings.Index(resolved, "messaging.destination.name"), strings.Index(resolved, "messaging.destination'"), "current name should remain the first fallback")
|
||||
}
|
||||
|
||||
func TestResolveTraceStringAttributesLeavesUnknownNameAlone(t *testing.T) {
|
||||
query := "SELECT attributes_string['custom.attribute']"
|
||||
|
||||
resolved := ResolveTraceStringAttributes(query, "custom.attribute")
|
||||
|
||||
assert.Equal(t, query, resolved, "unknown attributes should not be rewritten")
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func ExpandKeySelectorsForFamilies(ctx context.Context, orgID valuer.UUID, fl fl
|
||||
}
|
||||
|
||||
for _, selector := range selectors {
|
||||
if selector.SelectorMatchType == telemetrytypes.FieldSelectorMatchTypeFuzzy {
|
||||
if selector.SelectorMatchType == telemetrytypes.FieldSelectorMatchTypeFuzzy || selector.FieldResolution.IsExact() {
|
||||
continue
|
||||
}
|
||||
members := familySpellings(telemetrytypes.FieldKeySelector{
|
||||
|
||||
@@ -32,7 +32,7 @@ var friendly = map[string]string{
|
||||
"BETWEEN": "BETWEEN", "IN": "IN", "EXISTS": "EXISTS",
|
||||
"REGEXP": "REGEXP", "CONTAINS": "CONTAINS",
|
||||
"HAS": "has()", "HASANY": "hasAny()", "HASALL": "hasAll()",
|
||||
"HASTOKEN": "hasToken()", "SEARCH": "search()",
|
||||
"HASTOKEN": "hasToken()", "SEARCH": "search()", "EXACT": "exact()",
|
||||
|
||||
// literals / identifiers
|
||||
"NUMBER": "number",
|
||||
|
||||
@@ -141,7 +141,9 @@ func SynthesizeKeys(field *telemetrytypes.TelemetryFieldKey, value any) []*telem
|
||||
dataTypes := inferDataTypesFromOperand(value)
|
||||
keys := make([]*telemetrytypes.TelemetryFieldKey, 0, len(dataTypes))
|
||||
for _, dt := range dataTypes {
|
||||
keys = append(keys, telemetrytypes.NewTelemetryFieldKey(field.Name, fieldContext, dt))
|
||||
key := telemetrytypes.NewTelemetryFieldKey(field.Name, fieldContext, dt)
|
||||
key.FieldResolution = field.FieldResolution
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
@@ -52,8 +52,12 @@ func TestSynthesizeKeys(t *testing.T) {
|
||||
wantDataTypes: []telemetrytypes.FieldDataType{telemetrytypes.FieldDataTypeString},
|
||||
},
|
||||
{
|
||||
name: "qualified resource context honored as single string key",
|
||||
field: telemetrytypes.TelemetryFieldKey{Name: "k8s.cluster.name", FieldContext: telemetrytypes.FieldContextResource},
|
||||
name: "qualified exact resource context honored as single string key",
|
||||
field: telemetrytypes.TelemetryFieldKey{
|
||||
Name: "k8s.cluster.name",
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
FieldResolution: telemetrytypes.FieldResolutionExact,
|
||||
},
|
||||
value: nil,
|
||||
wantContexts: []telemetrytypes.FieldContext{telemetrytypes.FieldContextResource},
|
||||
wantDataTypes: []telemetrytypes.FieldDataType{telemetrytypes.FieldDataTypeString},
|
||||
@@ -83,6 +87,7 @@ func TestSynthesizeKeys(t *testing.T) {
|
||||
assert.Equal(t, tt.field.Name, k.Name, "name preserved")
|
||||
assert.Equal(t, tt.wantContexts[i], k.FieldContext)
|
||||
assert.Equal(t, tt.wantDataTypes[i], k.FieldDataType)
|
||||
assert.Equal(t, tt.field.FieldResolution, k.FieldResolution, "field resolution should be preserved")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -342,4 +342,9 @@ func TestExpandKeySelectorsForFamiliesDeduplicatesAndSkipsFuzzy(t *testing.T) {
|
||||
{Name: "deployment.environment.name", Signal: telemetrytypes.SignalTraces, SelectorMatchType: telemetrytypes.FieldSelectorMatchTypeFuzzy},
|
||||
}
|
||||
assert.Len(t, ExpandKeySelectorsForFamilies(context.Background(), valuer.UUID{}, familiesOn(t), fuzzy), 1, "fuzzy (search-style) selectors never expand")
|
||||
|
||||
exact := []*telemetrytypes.FieldKeySelector{
|
||||
{Name: "deployment.environment.name", Signal: telemetrytypes.SignalTraces, SelectorMatchType: telemetrytypes.FieldSelectorMatchTypeExact, FieldResolution: telemetrytypes.FieldResolutionExact},
|
||||
}
|
||||
assert.Len(t, ExpandKeySelectorsForFamilies(context.Background(), valuer.UUID{}, familiesOn(t), exact), 1, "an exact selector reads its own spelling alone")
|
||||
}
|
||||
|
||||
@@ -204,11 +204,12 @@ func (d *LogicalContradictionDetector) VisitPrimary(ctx *grammar.PrimaryContext)
|
||||
|
||||
// VisitComparison extracts constraints from comparisons.
|
||||
func (d *LogicalContradictionDetector) VisitComparison(ctx *grammar.ComparisonContext) any {
|
||||
if ctx.Key() == nil {
|
||||
if ctx.Field() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
field := ctx.Key().GetText()
|
||||
// exact(key) and key read different spellings, so they stay distinct.
|
||||
field := ctx.Field().GetText()
|
||||
notContext := d.inNotContext()
|
||||
|
||||
// Handle EXISTS
|
||||
|
||||
@@ -28,6 +28,8 @@ import (
|
||||
func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector {
|
||||
lexer := grammar.NewFilterQueryLexer(antlr.NewInputStream(query))
|
||||
keys := []*telemetrytypes.FieldKeySelector{}
|
||||
// exact(key) is EXACT LPAREN KEY; the two previous tokens mark the key.
|
||||
previous := make([]int, 0, 2)
|
||||
for {
|
||||
tok := lexer.NextToken()
|
||||
if tok.GetTokenType() == antlr.TokenEOF {
|
||||
@@ -36,11 +38,15 @@ func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector
|
||||
|
||||
if tok.GetTokenType() == grammar.FilterQueryLexerKEY {
|
||||
key := telemetrytypes.GetFieldKeyFromKeyText(tok.GetText())
|
||||
if len(previous) == 2 && previous[0] == grammar.FilterQueryLexerEXACT && previous[1] == grammar.FilterQueryLexerLPAREN {
|
||||
key.FieldResolution = telemetrytypes.FieldResolutionExact
|
||||
}
|
||||
keys = append(keys, &telemetrytypes.FieldKeySelector{
|
||||
Name: key.Name,
|
||||
Signal: key.Signal,
|
||||
FieldContext: key.FieldContext,
|
||||
FieldDataType: key.FieldDataType,
|
||||
Name: key.Name,
|
||||
Signal: key.Signal,
|
||||
FieldContext: key.FieldContext,
|
||||
FieldDataType: key.FieldDataType,
|
||||
FieldResolution: key.FieldResolution,
|
||||
})
|
||||
|
||||
if key.FieldContext == telemetrytypes.FieldContextLog ||
|
||||
@@ -50,10 +56,11 @@ func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector
|
||||
// span.kind in metrics or metric.max_count in span etc.. should get the search on span.kind
|
||||
// see note in where_clause_visitor.go in VisitKey(...)
|
||||
keys = append(keys, &telemetrytypes.FieldKeySelector{
|
||||
Name: key.FieldContext.StringValue() + "." + key.Name,
|
||||
Signal: key.Signal,
|
||||
FieldContext: telemetrytypes.FieldContextAttribute, // do not keep the original context because this is attribute
|
||||
FieldDataType: key.FieldDataType,
|
||||
Name: key.FieldContext.StringValue() + "." + key.Name,
|
||||
Signal: key.Signal,
|
||||
FieldContext: telemetrytypes.FieldContextAttribute, // do not keep the original context because this is attribute
|
||||
FieldDataType: key.FieldDataType,
|
||||
FieldResolution: key.FieldResolution,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,13 +68,18 @@ func QueryStringToKeysSelectors(query string) []*telemetrytypes.FieldKeySelector
|
||||
// https://github.com/SigNoz/signoz/issues/11374
|
||||
if key.FieldContext == telemetrytypes.FieldContextScope {
|
||||
keys = append(keys, &telemetrytypes.FieldKeySelector{
|
||||
Name: key.FieldContext.StringValue() + "." + key.Name,
|
||||
Signal: key.Signal,
|
||||
FieldContext: telemetrytypes.FieldContextUnspecified, // this allows 'scope.' prefix for keys with other context as well
|
||||
FieldDataType: key.FieldDataType,
|
||||
Name: key.FieldContext.StringValue() + "." + key.Name,
|
||||
Signal: key.Signal,
|
||||
FieldContext: telemetrytypes.FieldContextUnspecified, // this allows 'scope.' prefix for keys with other context as well
|
||||
FieldDataType: key.FieldDataType,
|
||||
FieldResolution: key.FieldResolution,
|
||||
})
|
||||
}
|
||||
}
|
||||
previous = append(previous, tok.GetTokenType())
|
||||
if len(previous) > 2 {
|
||||
previous = previous[1:]
|
||||
}
|
||||
}
|
||||
|
||||
return keys
|
||||
|
||||
@@ -33,6 +33,24 @@ func TestQueryToKeys(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
query: `exact(resource.deployment.environment) EXISTS AND deployment.environment.name="prod"`,
|
||||
expectedKeys: []telemetrytypes.FieldKeySelector{
|
||||
{
|
||||
Name: "deployment.environment",
|
||||
Signal: telemetrytypes.SignalUnspecified,
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeUnspecified,
|
||||
FieldResolution: telemetrytypes.FieldResolutionExact,
|
||||
},
|
||||
{
|
||||
Name: "deployment.environment.name",
|
||||
Signal: telemetrytypes.SignalUnspecified,
|
||||
FieldContext: telemetrytypes.FieldContextUnspecified,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeUnspecified,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
query: `service.name="redis" AND http.status_code=200`,
|
||||
expectedKeys: []telemetrytypes.FieldKeySelector{
|
||||
|
||||
@@ -61,7 +61,8 @@ func Resolve(
|
||||
traits := storage.Traits()
|
||||
|
||||
lookup := key
|
||||
matches := matchingLogicalFields(q.FamiliesOn, q.Signal, q.Metric, key, fieldKeys)
|
||||
familiesOn := q.FamiliesOn && !key.FieldResolution.IsExact()
|
||||
matches := matchingLogicalFields(familiesOn, q.Signal, q.Metric, key, fieldKeys)
|
||||
if len(matches) == 0 && slices.Contains(traits.OwnContexts, key.FieldContext) {
|
||||
// a column the storage knows under the key's own context is the key
|
||||
// as written, and only a miss corrects to the bare spelling
|
||||
@@ -71,7 +72,7 @@ func Resolve(
|
||||
}
|
||||
}
|
||||
lookup = telemetrytypes.NewTelemetryFieldKey(key.Name, telemetrytypes.FieldContextUnspecified, key.FieldDataType)
|
||||
matches = matchingLogicalFields(q.FamiliesOn, q.Signal, q.Metric, lookup, fieldKeys)
|
||||
matches = matchingLogicalFields(familiesOn, q.Signal, q.Metric, lookup, fieldKeys)
|
||||
}
|
||||
|
||||
resolved := qbtypes.Resolved{Key: key, Ambiguous: len(matches) > 1}
|
||||
|
||||
@@ -241,7 +241,7 @@ func (visitor *whereClauseNormalizer) visitPrimary(ctx grammar.IPrimaryContext)
|
||||
}
|
||||
|
||||
func (visitor *whereClauseNormalizer) visitComparison(ctx grammar.IComparisonContext) normalizedPart {
|
||||
key := normalizeKeyText(ctx.Key().GetText())
|
||||
key := normalizeFieldText(ctx.Field())
|
||||
|
||||
if ctx.EXISTS() != nil {
|
||||
operator := "EXISTS"
|
||||
@@ -361,8 +361,8 @@ func (visitor *whereClauseNormalizer) visitFunctionCall(ctx grammar.IFunctionCal
|
||||
raws := make([]string, 0)
|
||||
for index, param := range ctx.FunctionParamList().AllFunctionParam() {
|
||||
switch {
|
||||
case param.Key() != nil:
|
||||
keyText := normalizeKeyText(param.Key().GetText())
|
||||
case param.Field() != nil:
|
||||
keyText := normalizeFieldText(param.Field())
|
||||
if index == 0 {
|
||||
key = keyText
|
||||
} else {
|
||||
@@ -537,6 +537,13 @@ func normalizeKeyText(keyText string) string {
|
||||
return telemetrytypes.TelemetryFieldKeyToText(&fieldKey)
|
||||
}
|
||||
|
||||
func normalizeFieldText(field grammar.IFieldContext) string {
|
||||
if exactCall := field.ExactCall(); exactCall != nil {
|
||||
return "exact(" + normalizeKeyText(exactCall.Key().GetText()) + ")"
|
||||
}
|
||||
return normalizeKeyText(field.Key().GetText())
|
||||
}
|
||||
|
||||
// FilterStringLiteral quotes value for the filter expression grammar, not for
|
||||
// ClickHouse: the grammar's quoted text takes backslash escapes and no hex escapes.
|
||||
func FilterStringLiteral(value string) string {
|
||||
|
||||
@@ -243,6 +243,14 @@ func TestNormalizeWhereClauseAtoms(t *testing.T) {
|
||||
assert.ElementsMatch(t, expected, canonical.Conditions)
|
||||
}
|
||||
|
||||
func TestNormalizeWhereClausePreservesExactField(t *testing.T) {
|
||||
normalized, err := NormalizeWhereClause("EXACT(resource.deployment.environment:string) EXISTS", nil)
|
||||
require.NoError(t, err, "valid exact expression should normalize")
|
||||
assert.Equal(t, "exact(resource.deployment.environment:string) EXISTS", normalized.Expression, "normalization should preserve the exact wrapper")
|
||||
require.Len(t, normalized.Conditions, 1, "exact expression should produce one canonical condition")
|
||||
assert.Equal(t, "exact(resource.deployment.environment:string)", normalized.Conditions[0].Key, "canonical condition key should retain exact resolution")
|
||||
}
|
||||
|
||||
func TestNormalizeWhereClauseTopLevel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user