Compare commits

...

11 Commits

Author SHA1 Message Date
srikanthccv
33d813600b feat: support exact semconv fields and all aliases 2026-08-07 01:21:30 +05:30
srikanthccv
6735c92c9a feat: complete semantic convention rollout 2026-08-07 01:21:30 +05:30
srikanthccv
303d6a0363 feat: add semconv migration product surfaces 2026-08-07 01:21:14 +05:30
srikanthccv
6b4bb82efb feat: resolve semconv families across logs and metrics 2026-08-07 01:20:58 +05:30
srikanthccv
487d00b6c9 test: add semantic convention phase one closure gate 2026-08-07 01:20:55 +05:30
srikanthccv
59329d3aaa feat: support semconv evolution in services 2026-08-07 01:20:49 +05:30
srikanthccv
7c58b6af49 feat: resolve semantic convention names in trace queries 2026-08-07 01:20:08 +05:30
srikanthccv
e730192885 feat: generate semantic convention families 2026-08-07 01:20:01 +05:30
Jugal Kishore
8a481d8a75 feat(onboarding): add Apache, HAProxy, Elasticsearch, RabbitMQ, Open WebUI, DSPy datasources (#12363)
Some checks are pending
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
cacheci / tests (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
2026-08-06 15:28:41 +00:00
Shivam Gupta
84eeacd084 feat(onboarding): add HCP Vault, OpenTelemetry eBPF, Langflow, Cohere, Auth0, and PgBouncer datasources (#12098)
* feat(onboarding): add HCP Vault, OpenTelemetry eBPF, Langflow, Cohere, and Auth0 datasources

Add UI onboarding configurations (logos + datasource entries) so these tools
appear in the Add Data Source onboarding flow. SVG logos optimized with svgo.

Closes signoz.io#3614 (HCP Vault)
Closes signoz.io#3626 (OpenTelemetry eBPF / OBI)
Closes signoz.io#3680 (Langflow)
Closes signoz.io#3702 (Cohere)
Closes signoz.io#3562 (Auth0)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(onboarding): add PgBouncer datasource

Add PgBouncer (PostgreSQL connection pooler) metrics onboarding entry,
reusing the existing PostgreSQL logo.

Closes signoz.io#3553 (PgBouncer)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(onboarding): update SVG logos and category of datasources

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Jugal Kishore <jugal@signoz.io>
2026-08-06 13:25:53 +00:00
Vinicius Lourenço
41576954c4 fix(query-context): ensure operator is correctly suggested (#11575)
Co-authored-by: Nikhil Mantri <nikhil.mantri1999@gmail.com>
2026-08-06 13:24:06 +00:00
206 changed files with 10865 additions and 1734 deletions

View File

@@ -53,6 +53,21 @@ jobs:
with:
PRIMUS_REF: main
GO_VERSION: 1.24
semconv-generated:
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
runs-on: ubuntu-latest
steps:
- name: self-checkout
uses: actions/checkout@v4
- name: go-install
uses: actions/setup-go@v5
with:
go-version: "1.24"
- name: check-semconv-generated-files-and-product-literals
run: make semconv-check
build:
if: |
github.event_name == 'merge_group' ||

View File

@@ -220,6 +220,25 @@ py-test-teardown: ## Tear down the shared SigNoz backend
py-test: ## Runs integration tests
@cd tests && uv run pytest --basetemp=./tmp/ -vv --capture=no integration/tests/
.PHONY: py-test-semconv-phase1
py-test-semconv-phase1: py-test-setup ## Rebuild the shared stack and run the semantic-convention Phase 1 matrix
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py
.PHONY: py-test-semconv-phase2
py-test-semconv-phase2: py-test-setup ## Rebuild the shared stack and run the Phase 1-2 cross-signal matrices
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py integration/tests/queriersemconv/02_cross_signal.py
.PHONY: py-test-semconv-phase3
py-test-semconv-phase3: py-test-setup ## Rebuild the shared stack and run the Phase 1-3 compatibility and migration-report matrices
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py integration/tests/queriersemconv/02_cross_signal.py
.PHONY: py-test-semconv-phase4
py-test-semconv-phase4: py-test-setup ## Rebuild the shared stack and run all semantic-convention compatibility matrices
@cd tests && uv run pytest --basetemp=./tmp/ -vv --reuse --capture=no integration/tests/queriertraces/13_semconv_evolution.py integration/tests/queriersemconv/02_cross_signal.py integration/tests/queriersemconv/04_phase4_families.py
.PHONY: py-test-semconv
py-test-semconv: py-test-semconv-phase4 ## Run the complete semantic-convention evolution closure gate
.PHONY: py-clean
py-clean: ## Clear all pycache and pytest cache from tests directory recursively
@echo ">> cleaning python cache files from tests directory"
@@ -233,6 +252,14 @@ py-clean: ## Clear all pycache and pytest cache from tests directory recursively
##############################################################
# generate commands
##############################################################
.PHONY: semconv-generate
semconv-generate: ## Regenerate semantic-convention families for Go and TypeScript
@go run ./scripts/semconv
.PHONY: 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:
@echo ">> Generating mocks"

View File

@@ -6401,6 +6401,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
meta:
properties:
unit:
@@ -6445,6 +6447,10 @@ components:
rowsScanned:
minimum: 0
type: integer
semconvResolutions:
items:
$ref: '#/components/schemas/Querybuildertypesv5SemconvResolution'
type: array
stepIntervals:
additionalProperties:
minimum: 0
@@ -6511,6 +6517,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
name:
type: string
signal:
@@ -6582,6 +6590,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
name:
type: string
signal:
@@ -7149,6 +7159,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).
@@ -8554,6 +8578,11 @@ components:
- number
- ""
type: string
TelemetrytypesFieldResolution:
enum:
- exact
- ""
type: string
TelemetrytypesGettableFieldKeys:
properties:
complete:
@@ -8579,6 +8608,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
@@ -8599,6 +8664,8 @@ components:
$ref: '#/components/schemas/TelemetrytypesFieldContext'
fieldDataType:
$ref: '#/components/schemas/TelemetrytypesFieldDataType'
fieldResolution:
$ref: '#/components/schemas/TelemetrytypesFieldResolution'
name:
type: string
signal:
@@ -11095,6 +11162,64 @@ paths:
summary: Get field keys
tags:
- fields
/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
/api/v1/fields/values:
get:
deprecated: false
@@ -18680,8 +18805,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:
@@ -18697,7 +18822,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -18711,14 +18836,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
@@ -18744,7 +18869,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -18763,7 +18888,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:
@@ -18781,8 +18906,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
@@ -18811,7 +18936,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -18881,8 +19006,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
@@ -18898,9 +19024,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
@@ -19036,7 +19162,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:
@@ -19055,8 +19181,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
@@ -19087,7 +19213,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -19111,7 +19237,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:
@@ -19123,7 +19249,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
@@ -19131,7 +19257,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -19148,14 +19274,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
@@ -19183,7 +19309,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:
@@ -19629,8 +19755,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:
@@ -19646,7 +19772,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -19660,14 +19786,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
@@ -19693,7 +19819,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -19712,7 +19838,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:
@@ -19730,8 +19856,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
@@ -19760,7 +19886,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -19830,8 +19956,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
@@ -19847,9 +19974,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
@@ -19985,7 +20112,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:
@@ -20004,8 +20131,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
@@ -20036,7 +20163,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -20060,7 +20187,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:
@@ -20072,7 +20199,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
@@ -20080,7 +20207,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -20097,14 +20224,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
@@ -20132,7 +20259,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:
@@ -20481,8 +20608,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:
@@ -20498,7 +20625,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -20512,14 +20639,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
@@ -20545,7 +20672,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -20564,7 +20691,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:
@@ -20582,8 +20709,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
@@ -20612,7 +20739,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -20682,8 +20809,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
@@ -20699,9 +20827,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
@@ -20837,7 +20965,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:
@@ -20856,8 +20984,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
@@ -20888,7 +21016,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -20912,7 +21040,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:
@@ -20924,7 +21052,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
@@ -20932,7 +21060,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -20949,14 +21077,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
@@ -20984,7 +21112,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:
@@ -21836,8 +21964,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:
@@ -21853,7 +21981,7 @@ paths:
groupBy:
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: logs
stepInterval: 60
@@ -21867,14 +21995,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
@@ -21900,7 +22028,7 @@ paths:
team: payments
notificationSettings:
groupBy:
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -21919,7 +22047,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:
@@ -21937,8 +22065,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
@@ -21967,7 +22095,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -22037,8 +22165,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
@@ -22054,9 +22183,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
@@ -22192,7 +22321,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:
@@ -22211,8 +22340,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
@@ -22243,7 +22372,7 @@ paths:
notificationSettings:
groupBy:
- k8s.pod.name
- deployment.environment
- deployment.environment.name
renotify:
alertStates:
- firing
@@ -22267,7 +22396,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:
@@ -22279,7 +22408,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
@@ -22287,7 +22416,7 @@ paths:
name: service.name
- fieldContext: resource
fieldDataType: string
name: deployment.environment
name: deployment.environment.name
name: A
signal: traces
stepInterval: 60
@@ -22304,14 +22433,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
@@ -22339,7 +22468,7 @@ paths:
notificationSettings:
groupBy:
- service.name
- deployment.environment
- deployment.environment.name
newGroupEvalDelay: 2m
renotify:
alertStates:

View File

@@ -176,7 +176,7 @@ func (m *module) Create(ctx context.Context, orgID valuer.UUID, userEmail string
if err := m.checkAccess(ctx, orgID); err != nil {
return nil, err
}
if err := req.Validate(); err != nil {
if err := metricreductionrule.ValidatePostableReductionRule(req); err != nil {
return nil, err
}
if err := m.validateMetricForReduction(ctx, orgID, req.MetricName); err != nil {
@@ -218,7 +218,7 @@ func (m *module) UpdateByID(ctx context.Context, orgID valuer.UUID, userEmail st
if err != nil {
return nil, err
}
if err := req.Validate(); err != nil {
if err := metricreductionrule.ValidateUpdatableReductionRule(req); err != nil {
return nil, err
}
@@ -543,7 +543,7 @@ func resolveDroppedKept(matchType metricreductionruletypes.MatchType, ruleLabels
}
for _, k := range keys {
if metricreductionruletypes.IsProtectedLabel(k) {
if metricreductionrule.IsProtectedLabel(k) {
kept = append(kept, k)
continue
}

View File

@@ -19,6 +19,8 @@ import type {
GetFieldsKeysParams,
GetFieldsValues200,
GetFieldsValuesParams,
GetSemconvMigrationReport200,
GetSemconvMigrationReportParams,
RenderErrorResponseDTO,
} from '../sigNoz.schemas';
@@ -120,6 +122,108 @@ export const invalidateGetFieldsKeys = async (
return queryClient;
};
/**
* Returns services that still emit old semantic-convention names without the current family name
* @summary Get semantic-convention migration report
*/
export const getSemconvMigrationReport = (
params?: GetSemconvMigrationReportParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetSemconvMigrationReport200>({
url: `/api/v1/fields/semconv-migration`,
method: 'GET',
params,
signal,
});
};
export const getGetSemconvMigrationReportQueryKey = (
params?: GetSemconvMigrationReportParams,
) => {
return [
`/api/v1/fields/semconv-migration`,
...(params ? [params] : []),
] as const;
};
export const getGetSemconvMigrationReportQueryOptions = <
TData = Awaited<ReturnType<typeof getSemconvMigrationReport>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: GetSemconvMigrationReportParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getSemconvMigrationReport>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetSemconvMigrationReportQueryKey(params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getSemconvMigrationReport>>
> = ({ signal }) => getSemconvMigrationReport(params, signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getSemconvMigrationReport>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetSemconvMigrationReportQueryResult = NonNullable<
Awaited<ReturnType<typeof getSemconvMigrationReport>>
>;
export type GetSemconvMigrationReportQueryError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get semantic-convention migration report
*/
export function useGetSemconvMigrationReport<
TData = Awaited<ReturnType<typeof getSemconvMigrationReport>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: GetSemconvMigrationReportParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getSemconvMigrationReport>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetSemconvMigrationReportQueryOptions(params, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Get semantic-convention migration report
*/
export const invalidateGetSemconvMigrationReport = async (
queryClient: QueryClient,
params?: GetSemconvMigrationReportParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetSemconvMigrationReportQueryKey(params) },
options,
);
return queryClient;
};
/**
* This endpoint returns field values
* @summary Get field values

View File

@@ -3482,6 +3482,10 @@ export enum TelemetrytypesFieldDataTypeDTO {
number = 'number',
'' = '',
}
export enum TelemetrytypesFieldResolutionDTO {
exact = 'exact',
'' = '',
}
export enum TelemetrytypesSignalDTO {
traces = 'traces',
logs = 'logs',
@@ -3495,6 +3499,7 @@ export interface Querybuildertypesv5GroupByKeyDTO {
description?: string;
fieldContext?: TelemetrytypesFieldContextDTO;
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
fieldResolution?: TelemetrytypesFieldResolutionDTO;
/**
* @type string
*/
@@ -3535,6 +3540,7 @@ export interface Querybuildertypesv5OrderByKeyDTO {
description?: string;
fieldContext?: TelemetrytypesFieldContextDTO;
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
fieldResolution?: TelemetrytypesFieldResolutionDTO;
/**
* @type string
*/
@@ -3588,6 +3594,7 @@ export interface TelemetrytypesTelemetryFieldKeyDTO {
description?: string;
fieldContext?: TelemetrytypesFieldContextDTO;
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
fieldResolution?: TelemetrytypesFieldResolutionDTO;
/**
* @type string
*/
@@ -7936,6 +7943,7 @@ export interface Querybuildertypesv5ColumnDescriptorDTO {
description?: string;
fieldContext?: TelemetrytypesFieldContextDTO;
fieldDataType?: TelemetrytypesFieldDataTypeDTO;
fieldResolution?: TelemetrytypesFieldResolutionDTO;
/**
* @type object
*/
@@ -7959,6 +7967,25 @@ export type Querybuildertypesv5ExecStatsDTOStepIntervals = {
[key: string]: number;
};
export interface Querybuildertypesv5SemconvResolutionDTO {
/**
* @type string
*/
current?: string;
/**
* @type string
*/
kind?: string;
/**
* @type array,null
*/
members?: string[] | null;
/**
* @type string
*/
requested?: string;
}
/**
* Execution statistics for the query, including rows scanned, bytes scanned, and duration.
*/
@@ -7978,6 +8005,10 @@ export interface Querybuildertypesv5ExecStatsDTO {
* @minimum 0
*/
rowsScanned?: number;
/**
* @type array
*/
semconvResolutions?: Querybuildertypesv5SemconvResolutionDTO[];
/**
* @type object
*/
@@ -9710,6 +9741,52 @@ export interface TelemetrytypesGettableFieldValuesDTO {
values: TelemetrytypesTelemetryFieldValuesDTO;
}
export interface TelemetrytypesSemconvMigrationReportEntryDTO {
/**
* @type string
*/
current?: string;
/**
* @type integer
* @format int64
*/
lastSeenUnixMilli?: number;
/**
* @type string
*/
old?: string;
/**
* @type integer
* @minimum 0
*/
resourceSets?: number;
/**
* @type array,null
*/
services?: string[] | null;
/**
* @type string
*/
signal?: string;
}
export interface TelemetrytypesGettableSemconvMigrationReportDTO {
/**
* @type integer
* @format int64
*/
endUnixMilli?: number;
/**
* @type array,null
*/
entries: TelemetrytypesSemconvMigrationReportEntryDTO[] | null;
/**
* @type integer
* @format int64
*/
startUnixMilli?: number;
}
export interface TypesChangePasswordRequestDTO {
/**
* @type string
@@ -10476,6 +10553,29 @@ export type GetFieldsKeys200 = {
status: string;
};
export type GetSemconvMigrationReportParams = {
/**
* @type integer
* @format int64
* @description undefined
*/
startUnixMilli?: number;
/**
* @type integer
* @format int64
* @description undefined
*/
endUnixMilli?: number;
};
export type GetSemconvMigrationReport200 = {
data: TelemetrytypesGettableSemconvMigrationReportDTO;
/**
* @type string
*/
status: string;
};
export type GetFieldsValuesParams = {
/**
* @description undefined

View 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;

View File

@@ -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,
},

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" fill-rule="evenodd" style="flex:none;line-height:1" viewBox="0 0 24 24"><title>AWS</title><path d="M6.763 11.212q.002.446.088.71c.064.176.144.368.256.576.04.063.056.127.056.183q.002.12-.152.24l-.503.335a.4.4 0 0 1-.208.072q-.12-.002-.239-.112a2.5 2.5 0 0 1-.287-.375 6 6 0 0 1-.248-.471q-.934 1.101-2.347 1.101c-.67 0-1.205-.191-1.596-.574-.39-.384-.59-.894-.59-1.533 0-.678.24-1.23.726-1.644.487-.415 1.133-.623 1.955-.623.272 0 .551.024.846.064.296.04.6.104.918.176v-.583q-.001-.908-.375-1.277c-.255-.248-.686-.367-1.3-.367-.28 0-.568.031-.863.103s-.583.16-.862.272a2 2 0 0 1-.28.104.5.5 0 0 1-.127.023q-.168.002-.168-.247v-.391c0-.128.016-.224.056-.28a.6.6 0 0 1 .224-.167 4.6 4.6 0 0 1 1.005-.36 4.8 4.8 0 0 1 1.246-.151c.95 0 1.644.216 2.091.647q.661.646.662 1.963v2.586zm-3.24 1.214c.263 0 .534-.048.822-.144a1.8 1.8 0 0 0 .758-.51 1.3 1.3 0 0 0 .272-.512c.047-.191.08-.423.08-.694v-.335a7 7 0 0 0-.735-.136 6 6 0 0 0-.75-.048c-.535 0-.926.104-1.19.32-.263.215-.39.518-.39.917 0 .375.095.655.295.846.191.2.47.296.838.296m6.41.862c-.144 0-.24-.024-.304-.08-.064-.048-.12-.16-.168-.311L7.586 6.726a1.4 1.4 0 0 1-.072-.32c0-.128.064-.2.191-.2h.783q.227-.001.31.08c.065.048.113.16.16.312l1.342 5.284 1.245-5.284q.058-.24.151-.312a.55.55 0 0 1 .32-.08h.638c.152 0 .256.025.32.08.063.048.12.16.151.312l1.261 5.348 1.381-5.348q.074-.24.16-.312a.52.52 0 0 1 .311-.08h.743c.127 0 .2.065.2.2 0 .04-.009.08-.017.128a1 1 0 0 1-.056.2l-1.923 6.17q-.072.24-.168.311a.5.5 0 0 1-.303.08h-.687c-.15 0-.255-.024-.32-.08-.063-.056-.119-.16-.15-.32L12.32 7.747l-1.23 5.14c-.04.16-.087.264-.15.32-.065.056-.177.08-.32.08zm10.256.215c-.415 0-.83-.048-1.229-.143-.399-.096-.71-.2-.918-.32-.128-.071-.215-.151-.247-.223a.6.6 0 0 1-.048-.224v-.407c0-.167.064-.247.183-.247q.072 0 .144.024c.048.016.12.048.2.08q.408.181.878.279c.32.064.63.096.95.096.502 0 .894-.088 1.165-.264a.86.86 0 0 0 .415-.758.78.78 0 0 0-.215-.559c-.144-.151-.416-.287-.807-.415l-1.157-.36c-.583-.183-1.014-.454-1.277-.813a1.9 1.9 0 0 1-.4-1.158q0-.502.216-.886c.144-.255.335-.479.575-.654.24-.184.51-.32.83-.415.32-.096.655-.136 1.006-.136.175 0 .36.008.535.032.183.024.35.056.518.088q.24.058.455.127.216.072.336.144a.7.7 0 0 1 .24.2.43.43 0 0 1 .071.263v.375q-.002.254-.184.256a.8.8 0 0 1-.303-.096 3.65 3.65 0 0 0-1.532-.311c-.455 0-.815.071-1.062.223s-.375.383-.375.71c0 .224.08.416.24.567.16.152.454.304.877.44l1.134.358c.574.184.99.44 1.237.767s.367.702.367 1.117c0 .343-.072.655-.207.926a2.2 2.2 0 0 1-.583.703c-.248.2-.543.343-.886.447-.36.111-.734.167-1.142.167"/><path fill="#f90" d="M.378 15.475c3.384 1.963 7.56 3.153 11.877 3.153 2.914 0 6.114-.607 9.06-1.852.44-.2.814.287.383.607-2.626 1.94-6.442 2.969-9.722 2.969-4.598 0-8.74-1.7-11.87-4.526-.247-.223-.024-.527.272-.351m23.531-.2c.287.36-.08 2.826-1.485 4.007-.215.184-.423.088-.327-.151l.175-.439c.343-.88.802-2.198.52-2.555-.336-.43-2.22-.207-3.074-.103-.255.032-.295-.192-.063-.36 1.5-1.053 3.967-.75 4.254-.399"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="#9CA3AF" fill-rule="evenodd" style="flex:none;line-height:1" viewBox="0 0 24 24"><title>AWS</title><path d="M6.763 11.212q.002.446.088.71c.064.176.144.368.256.576.04.063.056.127.056.183q.002.12-.152.24l-.503.335a.4.4 0 0 1-.208.072q-.12-.002-.239-.112a2.5 2.5 0 0 1-.287-.375 6 6 0 0 1-.248-.471q-.934 1.101-2.347 1.101c-.67 0-1.205-.191-1.596-.574-.39-.384-.59-.894-.59-1.533 0-.678.24-1.23.726-1.644.487-.415 1.133-.623 1.955-.623.272 0 .551.024.846.064.296.04.6.104.918.176v-.583q-.001-.908-.375-1.277c-.255-.248-.686-.367-1.3-.367-.28 0-.568.031-.863.103s-.583.16-.862.272a2 2 0 0 1-.28.104.5.5 0 0 1-.127.023q-.168.002-.168-.247v-.391c0-.128.016-.224.056-.28a.6.6 0 0 1 .224-.167 4.6 4.6 0 0 1 1.005-.36 4.8 4.8 0 0 1 1.246-.151c.95 0 1.644.216 2.091.647q.661.646.662 1.963v2.586zm-3.24 1.214c.263 0 .534-.048.822-.144a1.8 1.8 0 0 0 .758-.51 1.3 1.3 0 0 0 .272-.512c.047-.191.08-.423.08-.694v-.335a7 7 0 0 0-.735-.136 6 6 0 0 0-.75-.048c-.535 0-.926.104-1.19.32-.263.215-.39.518-.39.917 0 .375.095.655.295.846.191.2.47.296.838.296m6.41.862c-.144 0-.24-.024-.304-.08-.064-.048-.12-.16-.168-.311L7.586 6.726a1.4 1.4 0 0 1-.072-.32c0-.128.064-.2.191-.2h.783q.227-.001.31.08c.065.048.113.16.16.312l1.342 5.284 1.245-5.284q.058-.24.151-.312a.55.55 0 0 1 .32-.08h.638c.152 0 .256.025.32.08.063.048.12.16.151.312l1.261 5.348 1.381-5.348q.074-.24.16-.312a.52.52 0 0 1 .311-.08h.743c.127 0 .2.065.2.2 0 .04-.009.08-.017.128a1 1 0 0 1-.056.2l-1.923 6.17q-.072.24-.168.311a.5.5 0 0 1-.303.08h-.687c-.15 0-.255-.024-.32-.08-.063-.056-.119-.16-.15-.32L12.32 7.747l-1.23 5.14c-.04.16-.087.264-.15.32-.065.056-.177.08-.32.08zm10.256.215c-.415 0-.83-.048-1.229-.143-.399-.096-.71-.2-.918-.32-.128-.071-.215-.151-.247-.223a.6.6 0 0 1-.048-.224v-.407c0-.167.064-.247.183-.247q.072 0 .144.024c.048.016.12.048.2.08q.408.181.878.279c.32.064.63.096.95.096.502 0 .894-.088 1.165-.264a.86.86 0 0 0 .415-.758.78.78 0 0 0-.215-.559c-.144-.151-.416-.287-.807-.415l-1.157-.36c-.583-.183-1.014-.454-1.277-.813a1.9 1.9 0 0 1-.4-1.158q0-.502.216-.886c.144-.255.335-.479.575-.654.24-.184.51-.32.83-.415.32-.096.655-.136 1.006-.136.175 0 .36.008.535.032.183.024.35.056.518.088q.24.058.455.127.216.072.336.144a.7.7 0 0 1 .24.2.43.43 0 0 1 .071.263v.375q-.002.254-.184.256a.8.8 0 0 1-.303-.096 3.65 3.65 0 0 0-1.532-.311c-.455 0-.815.071-1.062.223s-.375.383-.375.71c0 .224.08.416.24.567.16.152.454.304.877.44l1.134.358c.574.184.99.44 1.237.767s.367.702.367 1.117c0 .343-.072.655-.207.926a2.2 2.2 0 0 1-.583.703c-.248.2-.543.343-.886.447-.36.111-.734.167-1.142.167"/><path fill="#f90" d="M.378 15.475c3.384 1.963 7.56 3.153 11.877 3.153 2.914 0 6.114-.607 9.06-1.852.44-.2.814.287.383.607-2.626 1.94-6.442 2.969-9.722 2.969-4.598 0-8.74-1.7-11.87-4.526-.247-.223-.024-.527.272-.351m23.531-.2c.287.36-.08 2.826-1.485 4.007-.215.184-.423.088-.327-.151l.175-.439c.343-.88.802-2.198.52-2.555-.336-.43-2.22-.207-3.074-.103-.255.032-.295-.192-.063-.36 1.5-1.053 3.967-.75 4.254-.399"/></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1,3 +1,3 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M8.932 20.806c-.369 0-.738.007-1.109 0-.35-.007-.587-.206-.623-.5a.587.587 0 0 1 .53-.636c.79-.062 1.582-.063 2.372-.003a.548.548 0 0 1 .522.602c-.024.326-.253.526-.616.54zM1.792 8.345c-.392 0-.782.008-1.173.002-.327-.006-.577-.22-.614-.512-.037-.293.146-.544.499-.615.192-.032.388-.045.583-.039a81.515 81.515 0 0 1 1.597 0c.163 0 .325.019.483.056.288.073.445.318.411.617-.034.298-.214.477-.515.487-.424.014-.848.004-1.272.004zm7.588 8.417H4.292a2.464 2.464 0 0 1-.326-.007c-.294-.04-.48-.209-.508-.506-.029-.298.11-.501.391-.606.179-.065.365-.051.549-.051 3.347 0 6.695.005 10.042-.006 1.174-.004 2.187-.439 2.993-1.3.69-.738 1.053-1.63 1.16-2.635.085-.788-.027-1.513-.516-2.156-.544-.718-1.28-1.078-2.163-1.082-3.163-.013-6.328-.005-9.487-.01-.336 0-.673-.027-1.007-.058-.29-.027-.45-.201-.469-.492-.021-.317.141-.545.429-.6a1.55 1.55 0 0 1 .29-.015h10.177c1.71.004 3.187 1.038 3.726 2.654.383 1.147.246 2.304-.182 3.416-.824 2.135-2.762 3.448-5.055 3.454-1.652.005-3.304 0-4.956 0zm2.906-13.568c1.533 0 3.066-.008 4.598 0 2.935.018 5.629 1.892 6.653 4.626.442 1.181.538 2.403.412 3.657-.185 1.842-.735 3.552-1.776 5.084-1.608 2.365-3.873 3.68-6.679 4.118-.95.148-1.905.13-2.86.13-.397 0-.61-.181-.633-.51-.025-.351.196-.621.587-.645.434-.026.87-.004 1.305-.016 2.641-.072 4.928-.982 6.74-2.935 1.269-1.37 1.912-3.039 2.13-4.878.151-1.275.135-2.544-.37-3.752-.773-1.85-2.159-2.983-4.068-3.509-.74-.204-1.5-.243-2.26-.247-2.837-.017-5.675-.007-8.511-.007-.12 0-.24.004-.359-.006a.57.57 0 0 1-.517-.536.557.557 0 0 1 .456-.557c.13-.018.261-.024.392-.019h4.762Z"/>
<path fill="#29F1FB" d="M8.932 20.806c-.369 0-.738.007-1.109 0-.35-.007-.587-.206-.623-.5a.587.587 0 0 1 .53-.636c.79-.062 1.582-.063 2.372-.003a.548.548 0 0 1 .522.602c-.024.326-.253.526-.616.54zM1.792 8.345c-.392 0-.782.008-1.173.002-.327-.006-.577-.22-.614-.512-.037-.293.146-.544.499-.615.192-.032.388-.045.583-.039a81.515 81.515 0 0 1 1.597 0c.163 0 .325.019.483.056.288.073.445.318.411.617-.034.298-.214.477-.515.487-.424.014-.848.004-1.272.004zm7.588 8.417H4.292a2.464 2.464 0 0 1-.326-.007c-.294-.04-.48-.209-.508-.506-.029-.298.11-.501.391-.606.179-.065.365-.051.549-.051 3.347 0 6.695.005 10.042-.006 1.174-.004 2.187-.439 2.993-1.3.69-.738 1.053-1.63 1.16-2.635.085-.788-.027-1.513-.516-2.156-.544-.718-1.28-1.078-2.163-1.082-3.163-.013-6.328-.005-9.487-.01-.336 0-.673-.027-1.007-.058-.29-.027-.45-.201-.469-.492-.021-.317.141-.545.429-.6a1.55 1.55 0 0 1 .29-.015h10.177c1.71.004 3.187 1.038 3.726 2.654.383 1.147.246 2.304-.182 3.416-.824 2.135-2.762 3.448-5.055 3.454-1.652.005-3.304 0-4.956 0zm2.906-13.568c1.533 0 3.066-.008 4.598 0 2.935.018 5.629 1.892 6.653 4.626.442 1.181.538 2.403.412 3.657-.185 1.842-.735 3.552-1.776 5.084-1.608 2.365-3.873 3.68-6.679 4.118-.95.148-1.905.13-2.86.13-.397 0-.61-.181-.633-.51-.025-.351.196-.621.587-.645.434-.026.87-.004 1.305-.016 2.641-.072 4.928-.982 6.74-2.935 1.269-1.37 1.912-3.039 2.13-4.878.151-1.275.135-2.544-.37-3.752-.773-1.85-2.159-2.983-4.068-3.509-.74-.204-1.5-.243-2.26-.247-2.837-.017-5.675-.007-8.511-.007-.12 0-.24.004-.359-.006a.57.57 0 0 1-.517-.536.557.557 0 0 1 .456-.557c.13-.018.261-.024.392-.019h4.762Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#D22128" d="M17.805 2.197v.066h.156v.44h.072v-.44h.156v-.066zm.9 0l-.175.353-.172-.353h-.087v.506h.067V2.3l.172.35h.045l.172-.35v.404h.066v-.506zm-4.257 1c-.204.31-.424.66-.66 1.06l-.04.062a44.457 44.457 0 00-1.265 2.29c-.187.36-.38.742-.577 1.146l2.267-.25c.66-.302.955-.578 1.242-.976a15.5 15.5 0 00.23-.342c.23-.363.46-.763.663-1.16.197-.386.37-.767.505-1.11.083-.22.15-.422.198-.6.042-.158.074-.307.1-.45-.884.15-1.965.295-2.668.33zM11.894 7.78l-.077.16c-.078.16-.157.32-.236.488-.086.18-.172.364-.26.552l-.132.287a75.265 75.265 0 00-1.427 3.3c-.163.397-.327.807-.493 1.23-.15.38-.297.765-.45 1.164l-.02.06c-.15.396-.3.802-.453 1.22l-.01.027.72-.08a.213.213 0 01-.042-.006c.863-.106 2.01-.75 2.75-1.547.342-.367.652-.8.94-1.306.213-.377.413-.795.604-1.258.168-.405.328-.843.48-1.318-.196.105-.423.18-.673.235a2.184 2.184 0 01-.273.046c.806-.31 1.314-.905 1.683-1.64a2.816 2.816 0 01-.968.428c-.06.012-.116.022-.174.03l-.043.006h.002c.278-.118.514-.248.718-.403a2.571 2.571 0 00.637-.698l.063-.104.077-.154a8.107 8.107 0 00.367-.85l.03-.088a3.04 3.04 0 00.123-.463.733.733 0 01-.094.065c-.243.145-.66.277-.996.34l.663-.074-.664.073h-.017l-.1.017c.006-.003.01-.006.017-.008l-2.265.25-.013.022zM8.27 16.45c-.117.323-.236.654-.355.992l-.005.015c-.016.046-.032.094-.05.142-.08.227-.15.432-.31.9.264.12.475.435.675.793a1.44 1.44 0 00-.466-.99c1.293.06 2.41-.27 2.99-1.217.05-.084.096-.173.14-.268-.26.333-.59.474-1.2.44 0 0-.004 0-.005.002l.004-.002c.9-.404 1.354-.79 1.754-1.433.094-.153.186-.32.28-.503-.788.81-1.702 1.04-2.664.865l-.72.078a6.43 6.43 0 00-.067.183zM15.42.112c-.376.222-1 .85-1.748 1.763l.686 1.294c.48-.687.97-1.307 1.462-1.836l.058-.062c-.02.02-.04.04-.057.062-.16.176-.644.74-1.375 1.863.703-.035 1.784-.18 2.666-.33.262-1.47-.258-2.142-.258-2.142s-.66-1.07-1.436-.61zm-3.084 6.402a40.253 40.253 0 011.306-2.26l.04-.064c.224-.352.45-.693.677-1.02l-.685-1.293-.157.192c-.197.245-.403.51-.613.79a39.853 39.853 0 00-2.016 2.97l-.022.038.893 1.763c.19-.378.38-.752.575-1.118zm-3.73 8.32c.158-.406.319-.81.483-1.225.156-.394.32-.79.484-1.19a91.133 91.133 0 011.6-3.604l.205-.424c.12-.243.237-.485.36-.724a.125.125 0 01.02-.04l-.895-1.763-.044.07c-.207.34-.414.687-.617 1.042a38.056 38.056 0 00-1.092 2.04l-.094.193a24.573 24.573 0 00-1.258 3.087 18.492 18.492 0 00-.52 1.997l.896 1.77c.117-.317.24-.638.364-.963zm-1.376-.476a13.38 13.38 0 00-.234 1.692c0 .02-.004.04-.005.06-.28-.45-1.03-.888-1.026-.884.537.778.944 1.55 1.005 2.31-.29.058-.684-.027-1.14-.195.475.436.83.556.97.588-.434.03-.89.328-1.346.67.668-.27 1.21-.38 1.596-.29-.61 1.74-1.23 3.655-1.843 5.69a.538.538 0 00.364-.354c.11-.368.84-2.786 1.978-5.965l.097-.27.028-.078c.12-.332.246-.672.374-1.02l.09-.237v-.004L7.24 14.3c-.003.02-.01.04-.012.06z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#EB5424" d="M21.98 7.448 19.62 0H4.347L2.02 7.448c-1.352 4.312.03 9.206 3.815 12.015L12.007 24l6.157-4.552c3.755-2.81 5.182-7.688 3.815-12.015l-6.16 4.58 2.343 7.45-6.157-4.597-6.158 4.58 2.358-7.433-6.188-4.55 7.63-.045L12.008 0l2.356 7.404 7.615.044z"/></svg>

After

Width:  |  Height:  |  Size: 333 B

View File

@@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" height="34" viewBox="0 0 131 34">
<path fill="currentColor" d="M.36 8.6h16.7v5.6H6.04c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h11.02v5.6h-5.33c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h4.98c.2 0 .35-.15.35-.35V25.4h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34v-5.6h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34V3.35c0-.2-.16-.35-.35-.35H.36c-.2 0-.36.16-.36.35v4.9c0 .2.16.35.36.35ZM44.41 14.7c-.5-.5-1.1-.9-1.76-1.18a5.62 5.62 0 0 0-4.6.17c-.73.37-1.32.91-1.75 1.62h-.17V8.59H34.1v16.83h2.04v-1.81h.17c.21.36.47.67.77.94.31.25.65.48 1.01.67.37.18.77.31 1.18.39a6.2 6.2 0 0 0 3.39-.24 5.36 5.36 0 0 0 3.02-3.1c.29-.75.44-1.62.44-2.6v-.47c0-.96-.16-1.83-.47-2.58-.3-.75-.7-1.4-1.23-1.9v-.01Zm-5.87.66a3.9 3.9 0 0 1 4.34.84c.36.35.64.8.83 1.3.2.5.3 1.07.3 1.7v.47c0 .64-.1 1.23-.3 1.74a3.75 3.75 0 0 1-2.06 2.15 4.27 4.27 0 0 1-3.12-.03 3.86 3.86 0 0 1-2.09-2.2c-.2-.52-.3-1.11-.3-1.75v-.29c0-.62.1-1.2.3-1.7v-.01c.21-.53.5-.99.84-1.36.36-.37.78-.66 1.26-.86ZM97.04 8.59H95v4.86h-2.94v1.86H95v8.17c0 .56.17 1.03.53 1.4.37.35.84.54 1.4.54h4.18v-1.87h-3.5c-.2 0-.33-.05-.43-.15-.1-.1-.14-.27-.14-.49v-7.6h4.65v-1.86h-4.65V8.59ZM114.61 15a5.48 5.48 0 0 0-1.8-1.33 5.6 5.6 0 0 0-2.57-.56 6.17 6.17 0 0 0-4.26 1.7 5.6 5.6 0 0 0-1.72 4.2v.57c0 .9.15 1.75.44 2.5a5.58 5.58 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.35 5.35 0 0 0 1.91-2.73l.03-.07-1.94-.52-.02.07c-.11.33-.27.64-.46.94-.17.27-.4.52-.7.74-.28.22-.63.39-1.04.51-.41.13-.9.19-1.46.19a3.8 3.8 0 0 1-2.84-1.05 4.07 4.07 0 0 1-1.1-2.7h9.68v-1.6c0-.54-.11-1.12-.34-1.75a5.04 5.04 0 0 0-1.03-1.74Zm-8.25 3.21a3.8 3.8 0 0 1 1.22-2.25 4.19 4.19 0 0 1 3.99-.7c.44.16.83.38 1.17.66.34.27.62.62.82 1.02.21.38.34.8.38 1.27h-7.58ZM129.09 14.42a4.47 4.47 0 0 0-3.37-1.3c-.93 0-1.73.2-2.4.59-.64.39-1.15.97-1.52 1.74h-.17v-2h-2.04v11.97h2.04v-6.23c0-1.26.32-2.28.95-3.02a3.31 3.31 0 0 1 2.65-1.14c.94 0 1.7.3 2.24.9.56.6.83 1.52.83 2.74v6.75h2.04v-7.13c0-1.71-.42-3.02-1.25-3.87ZM88.1 15a5.48 5.48 0 0 0-1.78-1.33 5.6 5.6 0 0 0-2.58-.56 6.17 6.17 0 0 0-4.27 1.7 5.59 5.59 0 0 0-1.71 4.2v.56c0 .92.14 1.76.44 2.51a5.6 5.6 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.36 5.36 0 0 0 1.91-2.73l.03-.07-1.94-.52-.03.07c-.1.32-.26.64-.45.94-.17.27-.4.52-.7.74-.29.21-.64.39-1.05.51-.4.12-.9.19-1.45.19a3.8 3.8 0 0 1-2.85-1.05 4.07 4.07 0 0 1-1.09-2.7h9.68v-1.61c0-.53-.12-1.12-.34-1.74A5.03 5.03 0 0 0 88.1 15Zm-8.24 3.21a3.83 3.83 0 0 1 1.22-2.25 4.2 4.2 0 0 1 3.99-.7c.44.16.83.38 1.16.66.35.27.62.62.83 1.02.2.38.33.8.37 1.27h-7.57ZM73.65 19.42a6.11 6.11 0 0 0-3.23-1.02 6.63 6.63 0 0 1-2.68-.58c-.47-.3-.7-.7-.7-1.25 0-.27.08-.5.21-.7.14-.2.33-.38.56-.52a4.05 4.05 0 0 1 1.78-.42c.85 0 1.54.21 2.06.63.53.41.83 1 .91 1.73l.01.1 1.95-.47-.01-.07c-.07-.45-.22-.91-.45-1.36a3.46 3.46 0 0 0-.94-1.2 4.6 4.6 0 0 0-1.52-.84 6.05 6.05 0 0 0-2.1-.34c-.58 0-1.13.07-1.65.22-.52.14-1 .36-1.43.65-.41.3-.74.66-.99 1.1a2.9 2.9 0 0 0-.37 1.49v.14c0 1.06.38 1.87 1.14 2.42a6.2 6.2 0 0 0 3.24.97c1.18.08 2.04.26 2.56.54.5.28.75.72.75 1.36 0 .6-.25 1.06-.78 1.4-.52.32-1.22.48-2.1.48a3.68 3.68 0 0 1-2.46-.79 3.13 3.13 0 0 1-1.04-2.14v-.09l-1.93.46h-.02v.07a4.4 4.4 0 0 0 3.1 4c.7.24 1.52.36 2.46.36.7 0 1.35-.09 1.93-.27.6-.16 1.12-.4 1.53-.72A3.38 3.38 0 0 0 74.8 22v-.14c0-1.07-.39-1.9-1.14-2.44ZM60.25 23.4c-.1-.1-.14-.27-.14-.49v-9.46h-2.05v1.85h-.16a3.78 3.78 0 0 0-1.61-1.63 4.62 4.62 0 0 0-2.26-.56c-.77 0-1.5.14-2.19.41a5.27 5.27 0 0 0-3.02 3.12c-.29.75-.44 1.63-.44 2.6v.38c0 .99.15 1.87.44 2.63.3.75.7 1.4 1.2 1.93a5.48 5.48 0 0 0 4.05 1.57c.8 0 1.51-.2 2.2-.58.69-.38 1.24-.97 1.63-1.75h.16v.06c0 .56.18 1.03.53 1.4.37.35.85.54 1.41.54h1.36v-1.87h-.68c-.2 0-.34-.05-.43-.15Zm-4.46.13c-.46.2-.97.3-1.52.3a3.68 3.68 0 0 1-2.75-1.09 4.42 4.42 0 0 1-1.05-3.12v-.38c0-.62.1-1.2.29-1.71a3.65 3.65 0 0 1 5-2.17c.47.2.88.49 1.21.86.35.37.62.83.8 1.36.2.5.3 1.08.3 1.7v.3c0 .63-.1 1.23-.3 1.76-.18.5-.45.96-.78 1.33-.33.37-.73.66-1.2.86Z"/>
<svg xmlns="http://www.w3.org/2000/svg" fill="#9CA3AF" height="34" viewBox="0 0 131 34">
<path fill="#9CA3AF" d="M.36 8.6h16.7v5.6H6.04c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h11.02v5.6h-5.33c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h4.98c.2 0 .35-.15.35-.35V25.4h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34v-5.6h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34V3.35c0-.2-.16-.35-.35-.35H.36c-.2 0-.36.16-.36.35v4.9c0 .2.16.35.36.35ZM44.41 14.7c-.5-.5-1.1-.9-1.76-1.18a5.62 5.62 0 0 0-4.6.17c-.73.37-1.32.91-1.75 1.62h-.17V8.59H34.1v16.83h2.04v-1.81h.17c.21.36.47.67.77.94.31.25.65.48 1.01.67.37.18.77.31 1.18.39a6.2 6.2 0 0 0 3.39-.24 5.36 5.36 0 0 0 3.02-3.1c.29-.75.44-1.62.44-2.6v-.47c0-.96-.16-1.83-.47-2.58-.3-.75-.7-1.4-1.23-1.9v-.01Zm-5.87.66a3.9 3.9 0 0 1 4.34.84c.36.35.64.8.83 1.3.2.5.3 1.07.3 1.7v.47c0 .64-.1 1.23-.3 1.74a3.75 3.75 0 0 1-2.06 2.15 4.27 4.27 0 0 1-3.12-.03 3.86 3.86 0 0 1-2.09-2.2c-.2-.52-.3-1.11-.3-1.75v-.29c0-.62.1-1.2.3-1.7v-.01c.21-.53.5-.99.84-1.36.36-.37.78-.66 1.26-.86ZM97.04 8.59H95v4.86h-2.94v1.86H95v8.17c0 .56.17 1.03.53 1.4.37.35.84.54 1.4.54h4.18v-1.87h-3.5c-.2 0-.33-.05-.43-.15-.1-.1-.14-.27-.14-.49v-7.6h4.65v-1.86h-4.65V8.59ZM114.61 15a5.48 5.48 0 0 0-1.8-1.33 5.6 5.6 0 0 0-2.57-.56 6.17 6.17 0 0 0-4.26 1.7 5.6 5.6 0 0 0-1.72 4.2v.57c0 .9.15 1.75.44 2.5a5.58 5.58 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.35 5.35 0 0 0 1.91-2.73l.03-.07-1.94-.52-.02.07c-.11.33-.27.64-.46.94-.17.27-.4.52-.7.74-.28.22-.63.39-1.04.51-.41.13-.9.19-1.46.19a3.8 3.8 0 0 1-2.84-1.05 4.07 4.07 0 0 1-1.1-2.7h9.68v-1.6c0-.54-.11-1.12-.34-1.75a5.04 5.04 0 0 0-1.03-1.74Zm-8.25 3.21a3.8 3.8 0 0 1 1.22-2.25 4.19 4.19 0 0 1 3.99-.7c.44.16.83.38 1.17.66.34.27.62.62.82 1.02.21.38.34.8.38 1.27h-7.58ZM129.09 14.42a4.47 4.47 0 0 0-3.37-1.3c-.93 0-1.73.2-2.4.59-.64.39-1.15.97-1.52 1.74h-.17v-2h-2.04v11.97h2.04v-6.23c0-1.26.32-2.28.95-3.02a3.31 3.31 0 0 1 2.65-1.14c.94 0 1.7.3 2.24.9.56.6.83 1.52.83 2.74v6.75h2.04v-7.13c0-1.71-.42-3.02-1.25-3.87ZM88.1 15a5.48 5.48 0 0 0-1.78-1.33 5.6 5.6 0 0 0-2.58-.56 6.17 6.17 0 0 0-4.27 1.7 5.59 5.59 0 0 0-1.71 4.2v.56c0 .92.14 1.76.44 2.51a5.6 5.6 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.36 5.36 0 0 0 1.91-2.73l.03-.07-1.94-.52-.03.07c-.1.32-.26.64-.45.94-.17.27-.4.52-.7.74-.29.21-.64.39-1.05.51-.4.12-.9.19-1.45.19a3.8 3.8 0 0 1-2.85-1.05 4.07 4.07 0 0 1-1.09-2.7h9.68v-1.61c0-.53-.12-1.12-.34-1.74A5.03 5.03 0 0 0 88.1 15Zm-8.24 3.21a3.83 3.83 0 0 1 1.22-2.25 4.2 4.2 0 0 1 3.99-.7c.44.16.83.38 1.16.66.35.27.62.62.83 1.02.2.38.33.8.37 1.27h-7.57ZM73.65 19.42a6.11 6.11 0 0 0-3.23-1.02 6.63 6.63 0 0 1-2.68-.58c-.47-.3-.7-.7-.7-1.25 0-.27.08-.5.21-.7.14-.2.33-.38.56-.52a4.05 4.05 0 0 1 1.78-.42c.85 0 1.54.21 2.06.63.53.41.83 1 .91 1.73l.01.1 1.95-.47-.01-.07c-.07-.45-.22-.91-.45-1.36a3.46 3.46 0 0 0-.94-1.2 4.6 4.6 0 0 0-1.52-.84 6.05 6.05 0 0 0-2.1-.34c-.58 0-1.13.07-1.65.22-.52.14-1 .36-1.43.65-.41.3-.74.66-.99 1.1a2.9 2.9 0 0 0-.37 1.49v.14c0 1.06.38 1.87 1.14 2.42a6.2 6.2 0 0 0 3.24.97c1.18.08 2.04.26 2.56.54.5.28.75.72.75 1.36 0 .6-.25 1.06-.78 1.4-.52.32-1.22.48-2.1.48a3.68 3.68 0 0 1-2.46-.79 3.13 3.13 0 0 1-1.04-2.14v-.09l-1.93.46h-.02v.07a4.4 4.4 0 0 0 3.1 4c.7.24 1.52.36 2.46.36.7 0 1.35-.09 1.93-.27.6-.16 1.12-.4 1.53-.72A3.38 3.38 0 0 0 74.8 22v-.14c0-1.07-.39-1.9-1.14-2.44ZM60.25 23.4c-.1-.1-.14-.27-.14-.49v-9.46h-2.05v1.85h-.16a3.78 3.78 0 0 0-1.61-1.63 4.62 4.62 0 0 0-2.26-.56c-.77 0-1.5.14-2.19.41a5.27 5.27 0 0 0-3.02 3.12c-.29.75-.44 1.63-.44 2.6v.38c0 .99.15 1.87.44 2.63.3.75.7 1.4 1.2 1.93a5.48 5.48 0 0 0 4.05 1.57c.8 0 1.51-.2 2.2-.58.69-.38 1.24-.97 1.63-1.75h.16v.06c0 .56.18 1.03.53 1.4.37.35.85.54 1.41.54h1.36v-1.87h-.68c-.2 0-.34-.05-.43-.15Zm-4.46.13c-.46.2-.97.3-1.52.3a3.68 3.68 0 0 1-2.75-1.09 4.42 4.42 0 0 1-1.05-3.12v-.38c0-.62.1-1.2.29-1.71a3.65 3.65 0 0 1 5-2.17c.47.2.88.49 1.21.86.35.37.62.83.8 1.36.2.5.3 1.08.3 1.7v.3c0 .63-.1 1.23-.3 1.76-.18.5-.45.96-.78 1.33-.33.37-.73.66-1.2.86Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="#355146" fill-rule="evenodd" d="M6.82 11.908c.525 0 1.57-.03 3.013-.639 1.682-.71 5.029-2 7.443-3.323 1.689-.926 2.429-2.151 2.429-3.8 0-2.29-1.81-4.146-4.043-4.146H6.307C3.1 0 .5 2.666.5 5.954s2.434 5.954 6.32 5.954" clip-rule="evenodd"/><path fill="#d18ee2" fill-rule="evenodd" d="M8.402 16.01c0-1.611.947-3.064 2.399-3.682l2.946-1.254c2.98-1.268 6.26.977 6.26 4.286 0 2.563-2.027 4.64-4.527 4.64l-3.19-.002c-2.147 0-3.888-1.785-3.888-3.987" clip-rule="evenodd"/><path fill="#ff7759" d="M3.848 12.691C1.998 12.691.5 14.228.5 16.124v.444C.5 18.464 1.999 20 3.848 20s3.347-1.536 3.347-3.432v-.444c0-1.896-1.499-3.433-3.347-3.433"/></svg>

After

Width:  |  Height:  |  Size: 709 B

View File

@@ -1,9 +1,9 @@
<svg width="109" height="24" viewBox="0 0 109 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_125_22125)">
<path d="M0 -2.08616e-07V24H17.9352C22.9911 24 26.0999 21.0858 26.0999 17.04V6.96C26.0999 2.91432 22.9911 -2.08616e-07 17.9352 -2.08616e-07H0ZM6.76413 5.82864H19.1992V18.1714H6.76413V5.82864Z" fill="currentColor"/>
<path d="M46.7659 18.6172H35.0824V14.16H46.7659V18.6172ZM46.595 5.38296V9.5658H35.0824V5.38296H46.595ZM50.2846 12.1373V11.5886C52.5734 10.5258 53.7008 8.8458 53.7008 6.13728C53.7008 2.64012 50.9337 0.000116183 45.5361 0.000116183H28.3184V24H45.7752C51.1728 24 53.9399 21.8401 53.9399 18.0685C53.9399 15.0172 52.6418 13.2001 50.2846 12.1373Z" fill="currentColor"/>
<path d="M62.397 18.1714H74.8319V5.82864H62.397V18.1714ZM63.6609 24C58.6049 24 55.4961 21.0858 55.4961 17.04V6.96012C55.4961 2.91432 58.6049 0.000116183 63.6609 0.000116183H73.568C78.6238 0.000116183 81.7326 2.91432 81.7326 6.96012V17.04C81.7326 21.0858 78.6238 24 73.568 24H63.6609Z" fill="currentColor"/>
<path d="M101.66 15.12L90.8995 14.3658C85.5361 13.9886 83.418 11.1772 83.418 7.47432V6.96012C83.418 2.91432 86.5266 0.000116183 91.5827 0.000116183H100.157C105.214 0.000116183 108.323 2.91432 108.323 6.96012V7.98864H101.968V5.14284H90.2504V8.43432L100.601 9.18864C105.999 9.56568 108.493 12.8572 108.493 16.5257V17.04C108.493 20.7428 105.384 24 100.328 24H91.5827C86.5266 24 83.418 20.7428 83.418 17.04V16.0115H89.7722V18.8572H101.66V15.12Z" fill="currentColor"/>
<path d="M0 -2.08616e-07V24H17.9352C22.9911 24 26.0999 21.0858 26.0999 17.04V6.96C26.0999 2.91432 22.9911 -2.08616e-07 17.9352 -2.08616e-07H0ZM6.76413 5.82864H19.1992V18.1714H6.76413V5.82864Z" fill="#9CA3AF"/>
<path d="M46.7659 18.6172H35.0824V14.16H46.7659V18.6172ZM46.595 5.38296V9.5658H35.0824V5.38296H46.595ZM50.2846 12.1373V11.5886C52.5734 10.5258 53.7008 8.8458 53.7008 6.13728C53.7008 2.64012 50.9337 0.000116183 45.5361 0.000116183H28.3184V24H45.7752C51.1728 24 53.9399 21.8401 53.9399 18.0685C53.9399 15.0172 52.6418 13.2001 50.2846 12.1373Z" fill="#9CA3AF"/>
<path d="M62.397 18.1714H74.8319V5.82864H62.397V18.1714ZM63.6609 24C58.6049 24 55.4961 21.0858 55.4961 17.04V6.96012C55.4961 2.91432 58.6049 0.000116183 63.6609 0.000116183H73.568C78.6238 0.000116183 81.7326 2.91432 81.7326 6.96012V17.04C81.7326 21.0858 78.6238 24 73.568 24H63.6609Z" fill="#9CA3AF"/>
<path d="M101.66 15.12L90.8995 14.3658C85.5361 13.9886 83.418 11.1772 83.418 7.47432V6.96012C83.418 2.91432 86.5266 0.000116183 91.5827 0.000116183H100.157C105.214 0.000116183 108.323 2.91432 108.323 6.96012V7.98864H101.968V5.14284H90.2504V8.43432L100.601 9.18864C105.999 9.56568 108.493 12.8572 108.493 16.5257V17.04C108.493 20.7428 105.384 24 100.328 24H91.5827C86.5266 24 83.418 20.7428 83.418 17.04V16.0115H89.7722V18.8572H101.66V15.12Z" fill="#9CA3AF"/>
</g>
<defs>
<clipPath id="clip0_125_22125">

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,9 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="#EF4136" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round">
<rect x="2.2" y="2.2" width="19.6" height="19.6" rx="0.6"/>
<path d="M12 2.2V4.7a1.75 1.75 0 1 0 0 3.5V12"/>
<path d="M12 12v2.5a1.75 1.75 0 1 1 0 3.5v3.8"/>
<path d="M2.2 12h2.5a1.75 1.75 0 1 0 3.5 0H12"/>
<path d="M12 12h2.8a1.75 1.75 0 1 1 3.5 0h3.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 463 B

View File

@@ -0,0 +1,3 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#00BFB3" d="M13.394 0C8.683 0 4.609 2.716 2.644 6.667h15.641a4.77 4.77 0 0 0 3.073-1.11c.446-.375.864-.785 1.247-1.243l.001-.002A11.974 11.974 0 0 0 13.394 0zM1.804 8.889a12.009 12.009 0 0 0 0 6.222h14.7a3.111 3.111 0 1 0 0-6.222zm.84 8.444C4.61 21.283 8.684 24 13.395 24c3.701 0 7.011-1.677 9.212-4.312l-.001-.002a9.958 9.958 0 0 0-1.247-1.243 4.77 4.77 0 0 0-3.073-1.11z"/>
</svg>

After

Width:  |  Height:  |  Size: 469 B

View File

@@ -1 +1 @@
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Groq</title><path d="M12.036 2c-3.853-.035-7 3-7.036 6.781-.035 3.782 3.055 6.872 6.908 6.907h2.42v-2.566h-2.292c-2.407.028-4.38-1.866-4.408-4.23-.029-2.362 1.901-4.298 4.308-4.326h.1c2.407 0 4.358 1.915 4.365 4.278v6.305c0 2.342-1.944 4.25-4.323 4.279a4.375 4.375 0 01-3.033-1.252l-1.851 1.818A7 7 0 0012.029 22h.092c3.803-.056 6.858-3.083 6.879-6.816v-6.5C18.907 4.963 15.817 2 12.036 2z"></path></svg>
<svg fill="#F55036" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Groq</title><path d="M12.036 2c-3.853-.035-7 3-7.036 6.781-.035 3.782 3.055 6.872 6.908 6.907h2.42v-2.566h-2.292c-2.407.028-4.38-1.866-4.408-4.23-.029-2.362 1.901-4.298 4.308-4.326h.1c2.407 0 4.358 1.915 4.365 4.278v6.305c0 2.342-1.944 4.25-4.323 4.279a4.375 4.375 0 01-3.033-1.252l-1.851 1.818A7 7 0 0012.029 22h.092c3.803-.056 6.858-3.083 6.879-6.816v-6.5C18.907 4.963 15.817 2 12.036 2z"></path></svg>

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 563 B

View File

@@ -0,0 +1,74 @@
<svg role="img" viewBox="0 0 102.04 102.04" xmlns="http://www.w3.org/2000/svg">
<rect x="45.34" y="23.92" width=".41" height="15.95" transform="translate(-9.7 38.24) rotate(-41.55)" fill="#106DA9"/>
<rect x="32.04" y="45.06" width="11.92" height="11.92" transform="translate(-13.28 88.67) rotate(-89.6)" fill="#106DA9"/>
<rect x="45.04" y="32.06" width="11.92" height="11.92" transform="translate(12.63 88.76) rotate(-89.6)" fill="#106DA9"/>
<rect x="45.04" y="58.06" width="11.92" height="11.92" transform="translate(-13.37 114.58) rotate(-89.6)" fill="#106DA9"/>
<rect x="58.04" y="45.06" width="11.92" height="11.92" transform="translate(12.54 114.67) rotate(-89.6)" fill="#106DA9"/>
<rect x="57.03" y="22.05" width="7.94" height="7.94" transform="translate(34.56 86.84) rotate(-89.6)" fill="#106DA9"/>
<rect x="36.03" y="22.05" width="7.94" height="7.94" transform="translate(13.7 65.84) rotate(-89.6)" fill="#106DA9"/>
<rect x="22.03" y="37.05" width="7.94" height="7.94" transform="translate(-15.2 66.73) rotate(-89.6)" fill="#106DA9"/>
<rect x="22.03" y="58.05" width="7.94" height="7.94" transform="translate(-36.2 87.59) rotate(-89.6)" fill="#106DA9"/>
<rect x="72.03" y="58.05" width="7.94" height="7.94" transform="translate(13.45 137.58) rotate(-89.6)" fill="#106DA9"/>
<rect x="72.03" y="37.05" width="7.94" height="7.94" transform="translate(34.45 116.73) rotate(-89.6)" fill="#106DA9"/>
<rect x="15.02" y="26.04" width="5.96" height="5.96" transform="translate(-11.15 46.82) rotate(-89.6)" fill="#106DA9"/>
<rect x="25.02" y="14.04" width="5.96" height="5.96" transform="translate(10.79 44.9) rotate(-89.61)" fill="#106DA9"/>
<rect x="40.02" y="9.04" width="5.96" height="5.96" transform="translate(30.68 54.94) rotate(-89.6)" fill="#106DA9"/>
<rect x="11.02" y="41.04" width="5.96" height="5.96" transform="translate(-30.12 57.71) rotate(-89.6)" fill="#106DA9"/>
<rect x="81.98" y="26" width="5.04" height="5.04" transform="translate(-.2 .59) rotate(-.4)" fill="#106DA9"/>
<rect x="70.98" y="15" width="5.04" height="5.04" transform="translate(-.12 .51) rotate(-.4)" fill="#106DA9"/>
<rect x="55.98" y="9" width="5.04" height="5.04" transform="translate(-.08 .41) rotate(-.4)" fill="#106DA9"/>
<rect x="84.98" y="41" width="5.04" height="5.03" transform="translate(-.3 .6) rotate(-.4)" fill="#106DA9"/>
<rect x="36.03" y="72.05" width="7.94" height="7.94" transform="translate(-36.3 115.49) rotate(-89.6)" fill="#106DA9"/>
<rect x="57.03" y="72.05" width="7.94" height="7.94" transform="translate(-15.44 136.49) rotate(-89.6)" fill="#106DA9"/>
<rect x="81.02" y="70.04" width="5.96" height="5.96" transform="translate(10.39 156.51) rotate(-89.6)" fill="#106DA9"/>
<rect x="70.02" y="82.04" width="5.96" height="5.96" transform="translate(-12.52 157.43) rotate(-89.6)" fill="#106DA9"/>
<rect x="56.02" y="87.04" width="5.96" height="5.96" transform="translate(-31.44 148.37) rotate(-89.59)" fill="#106DA9"/>
<rect x="84.02" y="55.04" width="5.96" height="5.96" transform="translate(28.35 144.6) rotate(-89.59)" fill="#106DA9"/>
<rect x="14.98" y="71" width="5.04" height="5.04" transform="translate(-.51 .12) rotate(-.4)" fill="#106DA9"/>
<rect x="25.98" y="82" width="5.03" height="5.04" transform="translate(-.61 .21) rotate(-.41)" fill="#106DA9"/>
<rect x="40.98" y="87" width="5.04" height="5.04" transform="translate(-.62 .3) rotate(-.4)" fill="#106DA9"/>
<rect x="11.98" y="55" width="5.04" height="5.04" transform="translate(-.4 .1) rotate(-.4)" fill="#106DA9"/>
<rect x="18.01" y="12.03" width="2.98" height="2.98" transform="translate(5.85 32.93) rotate(-89.61)" fill="#106DA9"/>
<rect x=".01" y="45.03" width="2.98" height="2.98" transform="translate(-45.03 47.71) rotate(-89.61)" fill="#106DA9"/>
<rect x="2.01" y="35.03" width="2.98" height="2.98" transform="translate(-33.04 39.77) rotate(-89.61)" fill="#106DA9"/>
<rect x="35.01" y="2.03" width="2.98" height="2.98" transform="translate(32.73 40) rotate(-89.61)" fill="#106DA9"/>
<rect x="6.01" y="26.03" width="2.98" height="2.98" transform="translate(-20.07 34.83) rotate(-89.61)" fill="#106DA9"/>
<rect x="11.01" y="18.03" width="2.98" height="2.98" transform="translate(-7.11 31.89) rotate(-89.61)" fill="#106DA9"/>
<rect x="25.99" y="6.01" width="3.02" height="3.02" transform="translate(-.05 .19) rotate(-.39)" fill="#106DA9"/>
<rect x="44.01" y=".03" width="2.98" height="2.98" transform="translate(0 .31) rotate(-.39)" fill="#106DA9"/>
<rect x="93.01" y="26.03" width="2.98" height="2.98" transform="translate(66.34 121.83) rotate(-89.61)" fill="#106DA9"/>
<rect x="97.01" y="35.03" width="2.98" height="2.98" transform="translate(61.31 134.77) rotate(-89.61)" fill="#106DA9"/>
<rect x="88.01" y="18.03" width="2.98" height="2.98" transform="translate(69.37 108.89) rotate(-89.61)" fill="#106DA9"/>
<rect x="98.01" y="45.03" width="2.98" height="2.98" transform="translate(52.3 145.7) rotate(-89.61)" fill="#106DA9"/>
<rect x="53.99" y=".01" width="3.02" height="3.02" transform="translate(53.6 57.01) rotate(-89.61)" fill="#106DA9"/>
<rect x="64.01" y="2.03" width="2.98" height="2.98" transform="translate(61.53 69) rotate(-89.61)" fill="#106DA9"/>
<rect x="18.01" y="12.03" width="2.98" height="2.98" transform="translate(5.85 32.93) rotate(-89.61)" fill="#106DA9"/>
<rect x=".01" y="45.03" width="2.98" height="2.98" transform="translate(-45.03 47.71) rotate(-89.61)" fill="#106DA9"/>
<rect x="81.01" y="12.03" width="2.98" height="2.98" transform="translate(68.42 95.93) rotate(-89.61)" fill="#106DA9"/>
<rect x="2.01" y="35.03" width="2.98" height="2.98" transform="translate(-33.04 39.77) rotate(-89.61)" fill="#106DA9"/>
<rect x="35.01" y="2.03" width="2.98" height="2.98" transform="translate(32.73 40) rotate(-89.61)" fill="#106DA9"/>
<rect x="6.01" y="26.03" width="2.98" height="2.98" transform="translate(-20.07 34.83) rotate(-89.61)" fill="#106DA9"/>
<rect x="11.01" y="18.03" width="2.98" height="2.98" transform="translate(-7.11 31.89) rotate(-89.61)" fill="#106DA9"/>
<rect x="25.99" y="6.01" width="3.02" height="3.02" transform="translate(-.05 .19) rotate(-.39)" fill="#106DA9"/>
<rect x="44.01" y=".03" width="2.98" height="2.98" transform="translate(0 .31) rotate(-.39)" fill="#106DA9"/>
<rect x="73.01" y="6.03" width="2.98" height="2.98" transform="translate(66.47 81.97) rotate(-89.61)" fill="#106DA9"/>
<rect x="98.01" y="45.03" width="2.98" height="2.98" transform="translate(52.3 145.7) rotate(-89.61)" fill="#106DA9"/>
<rect x="53.99" y=".01" width="3.02" height="3.02" transform="translate(53.6 57.01) rotate(-89.61)" fill="#106DA9"/>
<rect x="64.01" y="2.03" width="2.98" height="2.98" transform="translate(61.53 69) rotate(-89.61)" fill="#106DA9"/>
<rect x="81.01" y="88.03" width="2.98" height="2.98" transform="translate(-7.58 171.41) rotate(-89.61)" fill="#106DA9"/>
<rect x="98.01" y="54.03" width="2.98" height="2.98" transform="translate(43.31 154.64) rotate(-89.61)" fill="#106DA9"/>
<rect x="18.01" y="88.03" width="2.98" height="2.98" transform="translate(-70.15 108.42) rotate(-89.61)" fill="#106DA9"/>
<rect x="97.01" y="64.03" width="2.98" height="2.98" transform="translate(32.32 163.58) rotate(-89.61)" fill="#106DA9"/>
<rect x="64.01" y="97.04" width="2.97" height="2.97" transform="translate(-33.67 163.03) rotate(-89.43)" fill="#106DA9"/>
<rect x="93.01" y="73.03" width="2.98" height="2.98" transform="translate(19.31 168.49) rotate(-89.59)" fill="#106DA9"/>
<rect x="88.01" y="81.03" width="2.98" height="2.98" transform="translate(6.37 171.45) rotate(-89.61)" fill="#106DA9"/>
<rect x="72.99" y="93.01" width="3.02" height="3.02" transform="translate(-.65 .51) rotate(-.39)" fill="#106DA9"/>
<rect x="53.99" y="99.01" width="3.02" height="3.02" transform="translate(-.68 .38) rotate(-.39)" fill="#106DA9"/>
<rect x="26.01" y="93.03" width="2.98" height="2.98" transform="translate(-67.21 121.37) rotate(-89.61)" fill="#106DA9"/>
<rect x="6.01" y="73.03" width="2.98" height="2.98" transform="translate(-67.07 81.51) rotate(-89.61)" fill="#106DA9"/>
<rect x="2.01" y="64.03" width="2.98" height="2.98" transform="translate(-62.04 68.57) rotate(-89.61)" fill="#106DA9"/>
<rect x="11.01" y="81.03" width="2.98" height="2.98" transform="translate(-70.1 94.46) rotate(-89.61)" fill="#106DA9"/>
<rect x=".01" y="54.03" width="2.98" height="2.98" transform="translate(-54.03 56.65) rotate(-89.61)" fill="#106DA9"/>
<rect x="45.01" y="99.03" width="2.98" height="2.98" transform="translate(-54.33 146.34) rotate(-89.61)" fill="#106DA9"/>
<rect x="35.01" y="97.03" width="2.98" height="2.98" transform="translate(-62.27 134.34) rotate(-89.61)" fill="#106DA9"/>
</svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#FFEC6E" d="m0 0 11.955 24L24 0zm13.366 4.827h1.393v1.38h-1.393zm-2.77 5.569H9.22V8.993h1.389zm0-2.087H9.22V6.906h1.389zm0-2.086H9.22V4.819h1.389zm2.087 6.263h-1.377V11.08h1.388zm0-2.09h-1.377V8.993h1.388zm0-2.087h-1.377V6.906h1.388zm0-2.086h-1.377V4.819h1.388zm.683.683h1.393v1.389h-1.393zm0 3.475V8.993h1.389v1.388Z"/></svg>

After

Width:  |  Height:  |  Size: 398 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#9CA3AF" d="M9.755 1.52h-.001c-.31 0-.608.124-.828.343L4.037 6.752a1.17 1.17 0 0 1-.827.343H1.17A1.17 1.17 0 0 0 0 8.295l.052 1.984a1.17 1.17 0 0 0 1.17 1.14h2.37c.31 0 .607-.124.827-.344l4.93-4.93c.22-.22.517-.343.827-.343h2.874a1.17 1.17 0 0 0 1.17-1.17V2.69a1.17 1.17 0 0 0-1.17-1.17zm9.78 2.503c-.31 0-.608.123-.828.343l-4.889 4.889a1.17 1.17 0 0 1-.827.342h-2.756c-.31 0-.608.124-.827.344L4.15 15.197a1.17 1.17 0 0 1-.827.343H1.32a1.17 1.17 0 0 0-1.17 1.17v1.996c0 .646.524 1.17 1.17 1.17h2.017c.302 0 .592-.116.81-.325l5.535-5.304a1.17 1.17 0 0 1 .81-.326h2.88c.31 0 .607-.123.827-.342l4.93-4.93c.22-.22.517-.344.827-.344h2.873A1.17 1.17 0 0 0 24 7.135V5.193a1.17 1.17 0 0 0-1.17-1.17h-3.294zm0 8.559c-.31 0-.608.123-.828.343l-4.889 4.889a1.17 1.17 0 0 1-.827.343h-2.04a1.17 1.17 0 0 0-1.17 1.2l.052 1.984a1.17 1.17 0 0 0 1.17 1.14h2.37c.31 0 .607-.124.827-.343l4.93-4.93c.22-.22.517-.343.827-.343h2.873a1.17 1.17 0 0 0 1.17-1.17v-1.943a1.17 1.17 0 0 0-1.17-1.17h-3.294Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,5 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<rect x="2.34" y="2.34" width="19.27" height="19.27" rx="4.69" fill="#fff"/>
<circle cx="10.41" cy="11.95" r="2.77" fill="none" stroke="#000" stroke-width="1.36"/>
<rect x="15" y="8.53" width="1.36" height="6.94" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 312 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24"><title>PlanetScale</title><path d="M0 12C0 5.373 5.373 0 12 0c4.873 0 9.067 2.904 10.947 7.077l-15.87 15.87a12 12 0 0 1-1.935-1.099L14.99 12H12l-8.485 8.485A11.96 11.96 0 0 1 0 12m12.004 12L24 12.004C23.998 18.628 18.628 23.998 12.004 24"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="#9CA3AF" viewBox="0 0 24 24"><title>PlanetScale</title><path d="M0 12C0 5.373 5.373 0 12 0c4.873 0 9.067 2.904 10.947 7.077l-15.87 15.87a12 12 0 0 1-1.935-1.099L14.99 12H12l-8.485 8.485A11.96 11.96 0 0 1 0 12m12.004 12L24 12.004C23.998 18.628 18.628 23.998 12.004 24"/></svg>

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 321 B

View File

@@ -0,0 +1,3 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#FF6600" d="M23.035 9.601h-7.677a.956.956 0 01-.962-.962V.962a.956.956 0 00-.962-.956H10.56a.956.956 0 00-.962.956V8.64a.956.956 0 01-.962.962H5.762a.956.956 0 01-.961-.962V.962A.956.956 0 003.839 0H.959a.956.956 0 00-.956.962v22.076A.956.956 0 00.965 24h22.07a.956.956 0 00.962-.962V10.58a.956.956 0 00-.962-.98zm-3.86 8.152a1.437 1.437 0 01-1.437 1.443h-1.924a1.437 1.437 0 01-1.436-1.443v-1.917a1.437 1.437 0 011.436-1.443h1.924a1.437 1.437 0 011.437 1.443z"/>
</svg>

After

Width:  |  Height:  |  Size: 557 B

View File

@@ -1,3 +1,3 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M.113 10.27A13.026 13.026 0 000 11.48h18.23c-.064-.125-.15-.237-.235-.347-3.117-4.027-4.793-3.677-7.19-3.78-.8-.034-1.34-.048-4.524-.048-1.704 0-3.555.005-5.358.01-.234.63-.459 1.24-.567 1.737h9.342v1.216H.113v.002zm18.26 2.426H.009c.02.326.05.645.094.961h16.955c.754 0 1.179-.429 1.315-.96zm-17.318 4.28s2.81 6.902 10.93 7.024c4.855 0 9.027-2.883 10.92-7.024H1.056zM11.988 0C7.5 0 3.593 2.466 1.531 6.108l4.75-.005v-.002c3.71 0 3.849.016 4.573.047l.448.016c1.563.052 3.485.22 4.996 1.364.82.621 2.007 1.99 2.712 2.965.654.902.842 1.94.396 2.934-.408.914-1.289 1.458-2.353 1.458H.391s.099.42.249.886h22.748A12.026 12.026 0 0024 12.005C24 5.377 18.621 0 11.988 0z"/>
<path fill="#9CA3AF" d="M.113 10.27A13.026 13.026 0 000 11.48h18.23c-.064-.125-.15-.237-.235-.347-3.117-4.027-4.793-3.677-7.19-3.78-.8-.034-1.34-.048-4.524-.048-1.704 0-3.555.005-5.358.01-.234.63-.459 1.24-.567 1.737h9.342v1.216H.113v.002zm18.26 2.426H.009c.02.326.05.645.094.961h16.955c.754 0 1.179-.429 1.315-.96zm-17.318 4.28s2.81 6.902 10.93 7.024c4.855 0 9.027-2.883 10.92-7.024H1.056zM11.988 0C7.5 0 3.593 2.466 1.531 6.108l4.75-.005v-.002c3.71 0 3.849.016 4.573.047l.448.016c1.563.052 3.485.22 4.996 1.364.82.621 2.007 1.99 2.712 2.965.654.902.842 1.94.396 2.934-.408.914-1.289 1.458-2.353 1.458H.391s.099.42.249.886h22.748A12.026 12.026 0 0024 12.005C24 5.377 18.621 0 11.988 0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 776 B

After

Width:  |  Height:  |  Size: 771 B

View File

@@ -1,13 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 590 270">
<path d="M30.36,109.14v.48h0A3.73,3.73,0,0,1,30.36,109.14Z" fill="currentColor" fill-rule="evenodd"/>
<path d="M30.36,109.14v.48h0A3.73,3.73,0,0,1,30.36,109.14Z" fill="#de3423" fill-rule="evenodd"/>
<path d="M138.66,28.78C107.2,37.87,57.29,43,30.4,43h0V94.35a.8.8,0,0,0,.19.48c18.35,0,75-6,109.18-15.4a129,129,0,0,0,17.49-5.81c4.18-1.88,6.88-3.86,6.88-5.92V15.91C164.1,20.79,151.39,25.11,138.66,28.78Z" fill="#de3423" fill-rule="evenodd"/>
<path d="M138.66,95.37c-18.83,5.43-44.24,9.47-67.39,11.83-15.54,1.59-30.06,2.42-40.87,2.42h0v51.31a.8.8,0,0,0,.19.48c18.35,0,75-6,109.18-15.39a130.38,130.38,0,0,0,17.49-5.81c4.18-1.89,6.88-3.86,6.88-5.92V82.5C164.1,87.37,151.39,91.69,138.66,95.37Z" fill="#de3423" fill-rule="evenodd"/>
<path d="M138.66,162c-18.83,5.43-44.24,9.46-67.39,11.83-15.56,1.59-30.1,2.42-40.91,2.42V228c18.16,0,75.1-5.95,109.37-15.39,12.63-3.48,24.37-7.44,24.37-11.74V149.08C164.1,154,151.39,158.28,138.66,162Z" fill="#de3423" fill-rule="evenodd"/>
<path d="M30.55,94.83C32.4,97.38,48,102.19,71.27,107.2c23.27,4.46,47.47,22.07,66.29,16.64,12.73-3.68,26.54-36.47,26.54-41.34V82c0-3.4-2.55-6.13-6.88-8.4-17.75-9.07-21.11-12.41-27.69-10.6C95.37,72.43,35.06,67.61,30.55,94.83Z" fill="currentColor" fill-rule="evenodd"/>
<path d="M30.55,161.41C32.4,164,48,168.77,71.27,173.79c26,4.74,48.61,20.19,67.44,14.75,12.73-3.68,25.39-34.58,25.39-39.46v-.48c0-3.39-2.55-6.13-6.88-8.39-13.54-7.2-31.43-15.13-38-13.32C85,136.3,39.26,138.37,30.55,161.41Z" fill="currentColor" fill-rule="evenodd"/>
<path d="M200.7,142.39c6,11.79,15.6,17.6,29.05,17.6,14.44,0,19.59-7.64,19.59-15.11,0-5.15-1.83-8.63-6.64-11.79-4.82-3.32-8.3-4.81-16.93-8-10.63-4-16.77-7-23.41-12.29-6.64-5.48-9.79-13-9.79-22.74a28.28,28.28,0,0,1,10.29-22.58c7-5.81,15.44-8.63,25.56-8.63,15.77,0,27.72,6.31,35.69,18.76L249.34,87.78c-4.48-6.81-11.29-10.3-20.59-10.3-9.13,0-15.77,5.15-15.77,12.29,0,4.81,2,7.14,4.82,10,1.82,1.33,6.47,3.32,8.63,4.48l6,2.32,6.8,2.66c11,4.48,18.76,9.3,23.57,14.44s7.31,12.12,7.31,20.75c0,20.42-14.11,34.2-40.51,34.2-21.41,0-37.18-10-44.48-26.4Z" fill="currentColor"/>
<path d="M354.25,104.71,342,117.49a28.14,28.14,0,0,0-21.24-9.13,25,25,0,0,0-18.43,7.47,27.76,27.76,0,0,0,0,37.52,25,25,0,0,0,18.43,7.47A28.14,28.14,0,0,0,342,151.69l12.29,12.78c-9,9.63-20.09,14.44-33.53,14.44-12.79,0-23.58-4.15-32.37-12.62s-13.12-19.09-13.12-31.7,4.32-23.08,13.12-31.54,19.58-12.78,32.37-12.78C334.16,90.27,345.28,95.08,354.25,104.71Z" fill="currentColor"/>
<path d="M393.88,125.62C408,124.3,413,122.47,413,116c0-5.15-4.64-9.13-13.94-9.13q-13.44,0-22.41,10.95l-12.28-10.46c8.13-11.45,19.58-17.09,34.36-17.09,20.75,0,33.7,10,33.7,27.05v37c0,5.81,2.15,6.48,7,6.48h.5v15.43c-2,1.17-5.15,1.83-9.3,1.83-4.48,0-8-1.33-10.62-4a14.06,14.06,0,0,1-3-5.48c-5.81,6.8-15.27,10.29-28.39,10.29-18.42,0-30.87-10.13-30.87-25.4C357.7,136.41,369.15,127.78,393.88,125.62ZM391.56,162c13.28,0,21.41-6,21.41-16.6v-9.3a9.75,9.75,0,0,1-4.14,2.49c-3.82,1.33-6.31,1.66-14.28,2.49-11.62,1.33-17.43,5-17.43,10.79C377.12,158.33,382.43,162,391.56,162Z" fill="currentColor"/>
<path d="M444.84,60.88h19.92V149.2c0,8.13,2.66,11.62,10,11.62a21.15,21.15,0,0,0,6-.67v17.76a35.56,35.56,0,0,1-9.47,1c-17.59,0-26.39-9-26.39-27.06Z" fill="currentColor"/>
<path d="M521.71,125.62c14.11-1.32,19.09-3.15,19.09-9.62,0-5.15-4.64-9.13-13.94-9.13q-13.44,0-22.41,10.95l-12.28-10.46c8.13-11.45,19.58-17.09,34.36-17.09,20.75,0,33.7,10,33.7,27.05v37c0,5.81,2.15,6.48,7,6.48h.5v15.43c-2,1.17-5.15,1.83-9.3,1.83-4.48,0-8-1.33-10.62-4a13.94,13.94,0,0,1-3-5.48c-5.81,6.8-15.27,10.29-28.39,10.29-18.42,0-30.87-10.13-30.87-25.4C485.53,136.41,497,127.78,521.71,125.62ZM519.39,162c13.28,0,21.41-6,21.41-16.6v-9.3a9.73,9.73,0,0,1-4.15,2.49c-3.81,1.33-6.3,1.66-14.27,2.49-11.62,1.33-17.43,5-17.43,10.79C505,158.33,510.26,162,519.39,162Z" fill="currentColor"/>
<path d="M30.55,94.83C32.4,97.38,48,102.19,71.27,107.2c23.27,4.46,47.47,22.07,66.29,16.64,12.73-3.68,26.54-36.47,26.54-41.34V82c0-3.4-2.55-6.13-6.88-8.4-17.75-9.07-21.11-12.41-27.69-10.6C95.37,72.43,35.06,67.61,30.55,94.83Z" fill="#de3423" fill-rule="evenodd"/>
<path d="M30.55,161.41C32.4,164,48,168.77,71.27,173.79c26,4.74,48.61,20.19,67.44,14.75,12.73-3.68,25.39-34.58,25.39-39.46v-.48c0-3.39-2.55-6.13-6.88-8.39-13.54-7.2-31.43-15.13-38-13.32C85,136.3,39.26,138.37,30.55,161.41Z" fill="#de3423" fill-rule="evenodd"/>
<path d="M200.7,142.39c6,11.79,15.6,17.6,29.05,17.6,14.44,0,19.59-7.64,19.59-15.11,0-5.15-1.83-8.63-6.64-11.79-4.82-3.32-8.3-4.81-16.93-8-10.63-4-16.77-7-23.41-12.29-6.64-5.48-9.79-13-9.79-22.74a28.28,28.28,0,0,1,10.29-22.58c7-5.81,15.44-8.63,25.56-8.63,15.77,0,27.72,6.31,35.69,18.76L249.34,87.78c-4.48-6.81-11.29-10.3-20.59-10.3-9.13,0-15.77,5.15-15.77,12.29,0,4.81,2,7.14,4.82,10,1.82,1.33,6.47,3.32,8.63,4.48l6,2.32,6.8,2.66c11,4.48,18.76,9.3,23.57,14.44s7.31,12.12,7.31,20.75c0,20.42-14.11,34.2-40.51,34.2-21.41,0-37.18-10-44.48-26.4Z" fill="#de3423"/>
<path d="M354.25,104.71,342,117.49a28.14,28.14,0,0,0-21.24-9.13,25,25,0,0,0-18.43,7.47,27.76,27.76,0,0,0,0,37.52,25,25,0,0,0,18.43,7.47A28.14,28.14,0,0,0,342,151.69l12.29,12.78c-9,9.63-20.09,14.44-33.53,14.44-12.79,0-23.58-4.15-32.37-12.62s-13.12-19.09-13.12-31.7,4.32-23.08,13.12-31.54,19.58-12.78,32.37-12.78C334.16,90.27,345.28,95.08,354.25,104.71Z" fill="#de3423"/>
<path d="M393.88,125.62C408,124.3,413,122.47,413,116c0-5.15-4.64-9.13-13.94-9.13q-13.44,0-22.41,10.95l-12.28-10.46c8.13-11.45,19.58-17.09,34.36-17.09,20.75,0,33.7,10,33.7,27.05v37c0,5.81,2.15,6.48,7,6.48h.5v15.43c-2,1.17-5.15,1.83-9.3,1.83-4.48,0-8-1.33-10.62-4a14.06,14.06,0,0,1-3-5.48c-5.81,6.8-15.27,10.29-28.39,10.29-18.42,0-30.87-10.13-30.87-25.4C357.7,136.41,369.15,127.78,393.88,125.62ZM391.56,162c13.28,0,21.41-6,21.41-16.6v-9.3a9.75,9.75,0,0,1-4.14,2.49c-3.82,1.33-6.31,1.66-14.28,2.49-11.62,1.33-17.43,5-17.43,10.79C377.12,158.33,382.43,162,391.56,162Z" fill="#de3423"/>
<path d="M444.84,60.88h19.92V149.2c0,8.13,2.66,11.62,10,11.62a21.15,21.15,0,0,0,6-.67v17.76a35.56,35.56,0,0,1-9.47,1c-17.59,0-26.39-9-26.39-27.06Z" fill="#de3423"/>
<path d="M521.71,125.62c14.11-1.32,19.09-3.15,19.09-9.62,0-5.15-4.64-9.13-13.94-9.13q-13.44,0-22.41,10.95l-12.28-10.46c8.13-11.45,19.58-17.09,34.36-17.09,20.75,0,33.7,10,33.7,27.05v37c0,5.81,2.15,6.48,7,6.48h.5v15.43c-2,1.17-5.15,1.83-9.3,1.83-4.48,0-8-1.33-10.62-4a13.94,13.94,0,0,1-3-5.48c-5.81,6.8-15.27,10.29-28.39,10.29-18.42,0-30.87-10.13-30.87-25.4C485.53,136.41,497,127.78,521.71,125.62ZM519.39,162c13.28,0,21.41-6,21.41-16.6v-9.3a9.73,9.73,0,0,1-4.15,2.49c-3.81,1.33-6.3,1.66-14.27,2.49-11.62,1.33-17.43,5-17.43,10.79C505,158.33,510.26,162,519.39,162Z" fill="#de3423"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -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',

View File

@@ -1746,7 +1746,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,
};

View File

@@ -152,15 +152,18 @@ describe('QuerySearch (Integration with Real CodeMirror)', () => {
/>,
);
// Wait for debounced API call (300ms debounce + some buffer)
await waitFor(() => expect(mockedGetKeysOnMount).toHaveBeenCalled(), {
timeout: 2000,
});
const lastArgs = mockedGetKeysOnMount.mock.calls[
mockedGetKeysOnMount.mock.calls.length - 1
]?.[0] as { signal: unknown; searchText: string };
expect(lastArgs).toMatchObject({ signal: DataSource.LOGS, searchText: '' });
// Wait for this mount's debounced call. A debounce from the preceding
// real-CodeMirror test can finish after mockClear(), so do not assume the
// first or last recorded call belongs to this render.
await waitFor(
() =>
expect(
mockedGetKeysOnMount.mock.calls.some(
([args]) => args.signal === DataSource.LOGS && args.searchText === '',
),
).toBe(true),
{ timeout: 2000 },
);
});
it('calls provided onRun on Mod-Enter', async () => {

View File

@@ -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'";

View File

@@ -652,7 +652,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);

View File

@@ -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',

View 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;

View 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;

View 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="db.system = 'postgresql'" />
<SemconvEditorWarning
value="db.system = 'postgresql'"
editor="ClickHouse SQL"
/>
</>,
);
expect(screen.getByLabelText('query')).not.toBeDisabled();
expect(screen.getByTestId('semconv-editor-warning')).toHaveTextContent(
'db.system → db.system.name',
);
});
});

View File

@@ -0,0 +1,2 @@
export { default as SemconvEditorWarning } from './SemconvEditorWarning';
export { default as SemconvOldNameBadge } from './SemconvOldNameBadge';

View File

@@ -0,0 +1,235 @@
// Code generated by scripts/semconv. DO NOT EDIT.
export type SemconvFamily = {
readonly current: string;
readonly old: readonly string[];
readonly kind: 'attribute' | 'metric';
readonly contexts: readonly string[];
readonly signals: readonly string[];
readonly applyToMetrics: readonly string[];
readonly valueMap: Readonly<Record<string, string>>;
};
export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
{
current: 'code.file.path',
old: ['code.filepath'],
kind: 'attribute',
contexts: [],
signals: [],
applyToMetrics: [],
valueMap: {},
},
{
current: 'code.function.name',
old: ['code.function'],
kind: 'attribute',
contexts: [],
signals: [],
applyToMetrics: [],
valueMap: {},
},
{
current: 'code.line.number',
old: ['code.lineno'],
kind: 'attribute',
contexts: [],
signals: [],
applyToMetrics: [],
valueMap: {},
},
{
current: 'container.cpu.usage',
old: ['container.cpu.utilization'],
kind: 'metric',
contexts: ['metric'],
signals: ['metrics'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'container.runtime.name',
old: ['container.runtime'],
kind: 'attribute',
contexts: [],
signals: [],
applyToMetrics: [],
valueMap: {},
},
{
current: 'db.namespace',
old: [
'db.elasticsearch.cluster.name',
'db.name',
'db.cassandra.keyspace',
'db.hbase.namespace',
],
kind: 'attribute',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'db.operation.name',
old: ['db.operation'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'db.query.text',
old: ['db.statement'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'db.system.name',
old: ['db.system'],
kind: 'attribute',
contexts: ['attribute', 'resource'],
signals: ['logs', 'metrics', 'traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'deployment.environment.name',
old: ['deployment.environment'],
kind: 'attribute',
contexts: ['attribute', 'resource'],
signals: ['logs', 'metrics', 'traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'http.request.method',
old: ['http.method'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'http.response.status_code',
old: ['http.status_code'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'k8s.node.cpu.usage',
old: ['k8s.node.cpu.utilization'],
kind: 'metric',
contexts: ['metric'],
signals: ['metrics'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'k8s.pod.cpu.usage',
old: ['k8s.pod.cpu.utilization'],
kind: 'metric',
contexts: ['metric'],
signals: ['metrics'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'messaging.client.id',
old: [
'messaging.client_id',
'messaging.kafka.client_id',
'messaging.rocketmq.client_id',
],
kind: 'attribute',
contexts: ['attribute'],
signals: ['metrics', 'traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'messaging.consumer.group.name',
old: [
'messaging.eventhubs.consumer.group',
'messaging.kafka.consumer.group',
'messaging.rocketmq.client_group',
'messaging.kafka.consumer_group',
],
kind: 'attribute',
contexts: [],
signals: [],
applyToMetrics: [],
valueMap: {},
},
{
current: 'messaging.destination.name',
old: ['messaging.destination'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'messaging.operation.type',
old: ['messaging.operation'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'rpc.system.name',
old: ['rpc.system'],
kind: 'attribute',
contexts: [],
signals: [],
applyToMetrics: [],
valueMap: {},
},
{
current: 'service.peer.name',
old: ['peer.service'],
kind: 'attribute',
contexts: [],
signals: [],
applyToMetrics: [],
valueMap: {},
},
{
current: 'url.full',
old: ['http.url'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'url.scheme',
old: ['http.scheme'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
valueMap: {},
},
{
current: 'user_agent.original',
old: ['browser.user_agent', 'http.user_agent'],
kind: 'attribute',
contexts: ['attribute'],
signals: ['logs', 'traces'],
applyToMetrics: [],
valueMap: {},
},
] as const;

View File

@@ -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}

View File

@@ -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,
},

View File

@@ -87,7 +87,7 @@ export const ApiMonitoringQuickFiltersConfig: IQuickFiltersConfig[] = [
title: 'Environment',
attributeKey: {
key: 'deployment.environment',
key: 'deployment.environment.name',
dataType: DataTypes.String,
type: 'resource',
},

View File

@@ -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',

View File

@@ -112,7 +112,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',
@@ -733,7 +733,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]:

View File

@@ -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;

View File

@@ -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>
);
}

View File

@@ -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 */}

View File

@@ -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;

View File

@@ -15,7 +15,7 @@ export const SAMPLE_SPAN_JSON = `{
},
"resource": {
"service.name": "llm-gateway",
"deployment.environment": "production"
"deployment.environment.name": "production"
}
}`;

View File

@@ -1120,7 +1120,7 @@
"plugin": {
"kind": "signoz/QueryVariable",
"spec": {
"queryValue": "SELECT DISTINCT resources_string['deployment.environment'] AS environment FROM signoz_traces.distributed_signoz_index_v3 WHERE mapContains(resources_string, 'deployment.environment') AND timestamp >= now() - INTERVAL 1 DAY"
"queryValue": "SELECT DISTINCT resources_string['deployment.environment.name'] AS environment FROM signoz_traces.distributed_signoz_index_v3 WHERE mapContains(resources_string, 'deployment.environment.name') AND timestamp >= now() - INTERVAL 1 DAY"
}
}
}

View File

@@ -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>
);

View File

@@ -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({

View File

@@ -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);

View File

@@ -94,7 +94,7 @@ function DBCall(): JSX.Element {
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
?.active || false;
const legend = dotMetricsEnabled ? '{{db.system}}' : '{{db_system}}';
const legend = dotMetricsEnabled ? '{{db.system.name}}' : '{{db_system_name}}';
const databaseCallsRPSWidget = useMemo(
() =>

View File

@@ -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: [],

View File

@@ -103,7 +103,7 @@ export enum WidgetKeys {
SignozExternalCallLatencySum = 'signoz_external_call_latency_sum',
Signoz_latency_bucket_norm = 'signoz_latency_bucket',
Signoz_latency_bucket = 'signoz_latency.bucket',
Db_system = 'db.system',
Db_system = 'db.system.name',
Db_system_norm = 'db_system',
}

View File

@@ -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"

View File

@@ -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}

View File

@@ -5,9 +5,11 @@ import androidJavaMonitoringUrl from '@/assets/Logos/android-java-monitoring.svg
import androidKotlinMonitoringUrl from '@/assets/Logos/android-kotlin-monitoring.svg';
import anthropicApiMonitoringUrl from '@/assets/Logos/anthropic-api-monitoring.svg';
import apacheDruidUrl from '@/assets/Logos/apache-druid.svg';
import apacheUrl from '@/assets/Logos/apache.svg';
import apiGatewayUrl from '@/assets/Logos/api-gateway.svg';
import argocdUrl from '@/assets/Logos/argocd.svg';
import aspnetUrl from '@/assets/Logos/aspnet.svg';
import auth0Url from '@/assets/Logos/auth0.svg';
import autogenUrl from '@/assets/Logos/autogen.svg';
import awsAlbUrl from '@/assets/Logos/aws-alb.svg';
import azureAppServiceUrl from '@/assets/Logos/azure-app-service.svg';
@@ -27,6 +29,7 @@ import claudeCodeUrl from '@/assets/Logos/claude-code.svg';
import clickhouseUrl from '@/assets/Logos/clickhouse.svg';
import cloudflareUrl from '@/assets/Logos/cloudflare.svg';
import cloudwatchLogsUrl from '@/assets/Logos/cloudwatch-logs.svg';
import cohereUrl from '@/assets/Logos/cohere.svg';
import confluentKafkaUrl from '@/assets/Logos/confluent-kafka.svg';
import convexLogoUrl from '@/assets/Logos/convex-logo.svg';
import cppUrl from '@/assets/Logos/cpp.svg';
@@ -39,11 +42,13 @@ import denoUrl from '@/assets/Logos/deno.svg';
import dockerUrl from '@/assets/Logos/docker.svg';
import documentLoadUrl from '@/assets/Logos/document-load.svg';
import dotnetUrl from '@/assets/Logos/dotnet.svg';
import dspyUrl from '@/assets/Logos/dspy.svg';
import dynamodbUrl from '@/assets/Logos/dynamodb.svg';
import ec2Url from '@/assets/Logos/ec2.svg';
import ecsUrl from '@/assets/Logos/ecs.svg';
import eksUrl from '@/assets/Logos/eks.svg';
import elasticacheUrl from '@/assets/Logos/elasticache.svg';
import elasticsearchUrl from '@/assets/Logos/elasticsearch.svg';
import elbUrl from '@/assets/Logos/elb.svg';
import elixirUrl from '@/assets/Logos/elixir.svg';
import elkUrl from '@/assets/Logos/elk.svg';
@@ -73,8 +78,10 @@ import grafanaUrl from '@/assets/Logos/grafana.svg';
import graphqlUrl from '@/assets/Logos/graphql.svg';
import grokUrl from '@/assets/Logos/grok.svg';
import groqUrl from '@/assets/Logos/groq.svg';
import haproxyUrl from '@/assets/Logos/haproxy.svg';
import hasuraUrl from '@/assets/Logos/hasura.svg';
import haystackUrl from '@/assets/Logos/haystack.svg';
import hcpVaultUrl from '@/assets/Logos/hcp-vault.svg';
import herokuUrl from '@/assets/Logos/heroku.svg';
import honeycombUrl from '@/assets/Logos/honeycomb.svg';
import hostmetricsUrl from '@/assets/Logos/hostmetrics.svg';
@@ -92,6 +99,7 @@ import kafkaUrl from '@/assets/Logos/kafka.svg';
import kubernetesUrl from '@/assets/Logos/kubernetes.svg';
import lambdaUrl from '@/assets/Logos/lambda.svg';
import langchainUrl from '@/assets/Logos/langchain.svg';
import langflowUrl from '@/assets/Logos/langflow.svg';
import langtraceUrl from '@/assets/Logos/langtrace.svg';
import litellmUrl from '@/assets/Logos/litellm.svg';
import livekitUrl from '@/assets/Logos/livekit.svg';
@@ -117,6 +125,7 @@ import ollamaUrl from '@/assets/Logos/ollama.svg';
import openaiUrl from '@/assets/Logos/openai.svg';
import openclawUrl from '@/assets/Logos/openclaw.svg';
import opencodeUrl from '@/assets/Logos/opencode.svg';
import openWebuiUrl from '@/assets/Logos/open-webui.svg';
import openlitUrl from '@/assets/Logos/openlit.svg';
import openrouterUrl from '@/assets/Logos/openrouter.svg';
import opentelemetryUrl from '@/assets/Logos/opentelemetry.svg';
@@ -131,6 +140,7 @@ import pythonUrl from '@/assets/Logos/python.svg';
import quarkusUrl from '@/assets/Logos/quarkus.svg';
import quickstartUrl from '@/assets/Logos/quickstart.svg';
import qwenUrl from '@/assets/Logos/qwen.svg';
import rabbitmqUrl from '@/assets/Logos/rabbitmq.svg';
import railwayUrl from '@/assets/Logos/railway.svg';
import rdsUrl from '@/assets/Logos/rds.svg';
import reactjsUrl from '@/assets/Logos/reactjs.svg';
@@ -3937,6 +3947,58 @@ const onboardingConfigWithLinks = [
],
link: '/docs/claude-code-monitoring/',
},
{
dataSource: 'cohere',
label: 'Cohere',
imgUrl: cohereUrl,
tags: ['LLM Monitoring'],
module: 'apm',
relatedSearchKeywords: [
'cohere',
'cohere api',
'cohere logs',
'cohere metrics',
'cohere monitoring',
'cohere observability',
'cohere traces',
'llm',
'llm monitoring',
'logging',
'logs',
'metrics',
'monitoring',
'observability',
'otel cohere integration',
'telemetry',
],
link: '/docs/cohere-monitoring/',
},
{
dataSource: 'langflow',
label: 'Langflow',
imgUrl: langflowUrl,
tags: ['LLM Monitoring'],
module: 'apm',
relatedSearchKeywords: [
'langflow',
'langflow logs',
'langflow metrics',
'langflow monitoring',
'langflow observability',
'langflow traces',
'llm',
'llm monitoring',
'logging',
'logs',
'low code ai',
'metrics',
'monitoring',
'observability',
'otel langflow integration',
'telemetry',
],
link: '/docs/langflow-observability/',
},
{
dataSource: 'deepseek-api',
label: 'DeepSeek API',
@@ -5483,12 +5545,32 @@ const onboardingConfigWithLinks = [
relatedSearchKeywords: [
'infrastructure',
'traefik',
'traefik access logs',
'traefik logs',
'traefik metrics',
'traefik monitoring',
'traefik observability',
'traefik tracing',
],
link: '/docs/tutorial/traefik-observability/',
question: {
desc: 'Which Traefik signals do you want to send to SigNoz?',
type: 'select',
options: [
{
key: 'traefik-metrics-traces',
label: 'Metrics & Traces',
imgUrl: opentelemetryUrl,
link: '/docs/tutorial/traefik-observability/',
},
{
key: 'traefik-logs',
label: 'Access Logs',
imgUrl: opentelemetryUrl,
link: '/docs/integrations/opentelemetry-traefik/',
},
],
},
},
{
dataSource: 'mongodb-atlas',
@@ -5518,11 +5600,32 @@ const onboardingConfigWithLinks = [
relatedSearchKeywords: [
'database',
'mysql',
'mysql error log',
'mysql logs',
'mysql metrics',
'mysql monitoring',
'mysql observability',
'mysql slow query log',
],
link: '/docs/metrics-management/mysql-metrics/',
question: {
desc: 'Which MySQL signals do you want to send to SigNoz?',
type: 'select',
options: [
{
key: 'mysql-metrics',
label: 'Metrics',
imgUrl: opentelemetryUrl,
link: '/docs/metrics-management/mysql-metrics/',
},
{
key: 'mysql-logs',
label: 'Logs',
imgUrl: opentelemetryUrl,
link: '/docs/integrations/opentelemetry-mysql/',
},
],
},
},
{
dataSource: 'jmx',
@@ -6467,6 +6570,30 @@ const onboardingConfigWithLinks = [
id: 'cert-manager',
link: '/docs/infrastructure-monitoring/cert-manager/',
},
{
dataSource: 'pgbouncer',
label: 'PgBouncer',
imgUrl: postgresqlUrl,
tags: ['infrastructure monitoring', 'metrics'],
module: 'metrics',
relatedSearchKeywords: [
'connection pooler',
'connection pooling',
'database',
'metrics',
'monitoring',
'observability',
'opentelemetry pgbouncer',
'pgbouncer',
'pgbouncer metrics',
'pgbouncer monitoring',
'pgbouncer observability',
'postgres',
'postgresql',
],
id: 'pgbouncer',
link: '/docs/metrics-management/opentelemetry-pgbouncer/',
},
{
dataSource: 'graphql',
label: 'GraphQL',
@@ -6491,6 +6618,28 @@ const onboardingConfigWithLinks = [
id: 'graphql',
link: '/docs/instrumentation/javascript/opentelemetry-graphql/',
},
{
dataSource: 'opentelemetry-ebpf',
label: 'OpenTelemetry eBPF (OBI)',
imgUrl: opentelemetryUrl,
tags: ['apm/traces'],
module: 'apm',
relatedSearchKeywords: [
'auto instrumentation',
'ebpf',
'obi',
'opentelemetry ebpf',
'opentelemetry obi',
'otel ebpf',
'zero code instrumentation',
'monitoring',
'observability',
'traces',
'tracing',
],
id: 'opentelemetry-ebpf',
link: '/docs/instrumentation/opentelemetry-ebpf/',
},
{
dataSource: 'railway',
label: 'Railway',
@@ -6513,6 +6662,54 @@ const onboardingConfigWithLinks = [
id: 'railway',
link: '/docs/integrations/outposts/railway/',
},
{
dataSource: 'hcp-vault',
label: 'HCP Vault',
imgUrl: hcpVaultUrl,
tags: ['logs'],
module: 'logs',
relatedSearchKeywords: [
'hashicorp',
'hashicorp vault',
'hcp',
'hcp vault',
'hcp vault logs',
'hcp vault monitoring',
'hcp vault observability',
'log forwarding',
'logging',
'logs',
'monitoring',
'observability',
'secrets management',
'vault',
],
id: 'hcp-vault',
link: '/docs/integrations/outposts/hcp-vault/',
},
{
dataSource: 'auth0',
label: 'Auth0',
imgUrl: auth0Url,
tags: ['logs'],
module: 'logs',
relatedSearchKeywords: [
'auth0',
'auth0 logs',
'auth0 monitoring',
'auth0 observability',
'authentication',
'authorization',
'identity',
'log forwarding',
'logging',
'logs',
'monitoring',
'observability',
],
id: 'auth0',
link: '/docs/integrations/outposts/auth0/',
},
{
dataSource: 'aspnet-core-metrics',
label: 'ASP.NET Core Metrics',
@@ -6632,5 +6829,164 @@ const onboardingConfigWithLinks = [
id: 'apache-druid',
link: '/docs/integrations/opentelemetry-apache-druid/',
},
{
dataSource: 'apache',
label: 'Apache HTTP Server',
imgUrl: apacheUrl,
tags: ['infrastructure monitoring', 'metrics', 'logs'],
module: 'metrics',
relatedSearchKeywords: [
'apache',
'apache access logs',
'apache error logs',
'apache http server',
'apache httpd',
'apache logs',
'apache metrics',
'apache monitoring',
'apache observability',
'httpd',
'infrastructure monitoring',
'logs',
'metrics',
'mod_status',
'monitoring',
'observability',
'opentelemetry apache',
'web server',
],
id: 'apache',
link: '/docs/integrations/opentelemetry-apache/',
},
{
dataSource: 'haproxy',
label: 'HAProxy',
imgUrl: haproxyUrl,
tags: ['infrastructure monitoring', 'metrics', 'logs'],
module: 'metrics',
relatedSearchKeywords: [
'haproxy',
'haproxy logs',
'haproxy metrics',
'haproxy monitoring',
'haproxy observability',
'infrastructure monitoring',
'load balancer',
'logs',
'metrics',
'monitoring',
'observability',
'opentelemetry haproxy',
'proxy',
'reverse proxy',
'syslog',
],
id: 'haproxy',
link: '/docs/integrations/opentelemetry-haproxy/',
},
{
dataSource: 'elasticsearch',
label: 'Elasticsearch',
imgUrl: elasticsearchUrl,
tags: ['database'],
module: 'metrics',
relatedSearchKeywords: [
'cluster health',
'database',
'elastic',
'elasticsearch',
'elasticsearch logs',
'elasticsearch metrics',
'elasticsearch monitoring',
'elasticsearch observability',
'logs',
'metrics',
'monitoring',
'observability',
'opentelemetry elasticsearch',
'search engine',
],
id: 'elasticsearch',
link: '/docs/integrations/opentelemetry-elasticsearch/',
},
{
dataSource: 'rabbitmq',
label: 'RabbitMQ',
imgUrl: rabbitmqUrl,
tags: ['Messaging Queues'],
module: 'metrics',
relatedSearchKeywords: [
'amqp',
'broker',
'logs',
'messaging',
'messaging queues',
'metrics',
'monitoring',
'observability',
'opentelemetry rabbitmq',
'queues',
'rabbitmq',
'rabbitmq logs',
'rabbitmq metrics',
'rabbitmq monitoring',
'rabbitmq observability',
],
id: 'rabbitmq',
link: '/docs/integrations/opentelemetry-rabbitmq/',
},
{
dataSource: 'open-webui',
label: 'Open WebUI',
imgUrl: openWebuiUrl,
tags: ['LLM Monitoring'],
module: 'apm',
relatedSearchKeywords: [
'llm',
'llm monitoring',
'logs',
'metrics',
'monitoring',
'observability',
'open webui',
'open webui logs',
'open webui metrics',
'open webui monitoring',
'open webui observability',
'open webui traces',
'openlit',
'openwebui',
'otel open webui integration',
'self hosted chat ui',
'traces',
'tracing',
],
id: 'open-webui',
link: '/docs/open-webui-monitoring/',
},
{
dataSource: 'dspy',
label: 'DSPy',
imgUrl: dspyUrl,
tags: ['LLM Monitoring'],
module: 'apm',
relatedSearchKeywords: [
'dspy',
'dspy monitoring',
'dspy observability',
'dspy traces',
'llm',
'llm monitoring',
'monitoring',
'observability',
'openinference',
'otel dspy integration',
'prompt optimization',
'traces',
'tracing',
],
id: 'dspy',
link: '/docs/dspy-observability/',
},
];
export default onboardingConfigWithLinks;

View File

@@ -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>

View File

@@ -424,7 +424,7 @@ describe('ResourceProvider', () => {
await waitFor(() => {
expect(result.current.queries).toHaveLength(1);
expect(result.current.queries[0]).toMatchObject({
tagKey: 'resource_deployment_environment',
tagKey: 'resource_deployment_environment_name',
operator: 'IN',
tagValue: ['production'],
});
@@ -435,7 +435,7 @@ describe('ResourceProvider', () => {
const seeded = [
{
id: 'env',
tagKey: 'resource_deployment_environment',
tagKey: 'resource_deployment_environment_name',
operator: 'IN',
tagValue: ['production'],
},
@@ -459,7 +459,7 @@ describe('ResourceProvider', () => {
await waitFor(() => {
const tagKeys = result.current.queries.map((q) => q.tagKey);
expect(tagKeys).not.toContain('resource_deployment_environment');
expect(tagKeys).not.toContain('resource_deployment_environment_name');
expect(tagKeys).toContain('resource_service_name');
});
});
@@ -468,7 +468,7 @@ describe('ResourceProvider', () => {
const seeded = [
{
id: 'env',
tagKey: 'resource_deployment_environment',
tagKey: 'resource_deployment_environment_name',
operator: 'IN',
tagValue: ['production'],
},
@@ -486,7 +486,7 @@ describe('ResourceProvider', () => {
await waitFor(() => {
const envQueries = result.current.queries.filter(
(q) => q.tagKey === 'resource_deployment_environment',
(q) => q.tagKey === 'resource_deployment_environment_name',
);
expect(envQueries).toHaveLength(1);
expect(envQueries[0].tagValue).toStrictEqual(['staging']);
@@ -518,7 +518,7 @@ describe('ResourceProvider', () => {
await waitFor(() => {
expect(result.current.queries[0].tagKey).toBe(
'resource_deployment.environment',
'resource_deployment.environment.name',
);
});
});

View File

@@ -6,13 +6,13 @@ import { mappingWithRoutesAndKeys } from '../utils';
describe('useResourceAttribute config', () => {
describe('whilelistedKeys', () => {
it('should include underscore-notation keys (DOT_METRICS_ENABLED=false)', () => {
expect(whilelistedKeys).toContain('resource_deployment_environment');
expect(whilelistedKeys).toContain('resource_deployment_environment_name');
expect(whilelistedKeys).toContain('resource_k8s_cluster_name');
expect(whilelistedKeys).toContain('resource_k8s_cluster_namespace');
});
it('should include dot-notation keys (DOT_METRICS_ENABLED=true)', () => {
expect(whilelistedKeys).toContain('resource_deployment.environment');
expect(whilelistedKeys).toContain('resource_deployment.environment.name');
expect(whilelistedKeys).toContain('resource_k8s.cluster.name');
expect(whilelistedKeys).toContain('resource_k8s.cluster.namespace');
});
@@ -21,8 +21,8 @@ describe('useResourceAttribute config', () => {
describe('mappingWithRoutesAndKeys', () => {
const dotNotationFilters = [
{
label: 'deployment.environment',
value: 'resource_deployment.environment',
label: 'deployment.environment.name',
value: 'resource_deployment.environment.name',
},
{ label: 'k8s.cluster.name', value: 'resource_k8s.cluster.name' },
{ label: 'k8s.cluster.namespace', value: 'resource_k8s.cluster.namespace' },
@@ -30,8 +30,8 @@ describe('useResourceAttribute config', () => {
const underscoreNotationFilters = [
{
label: 'deployment.environment',
value: 'resource_deployment_environment',
label: 'deployment.environment.name',
value: 'resource_deployment_environment_name',
},
{ label: 'k8s.cluster.name', value: 'resource_k8s_cluster_name' },
{ label: 'k8s.cluster.namespace', value: 'resource_k8s_cluster_namespace' },

View File

@@ -1,6 +1,6 @@
export const whilelistedKeys = [
'resource_deployment_environment',
'resource_deployment.environment',
'resource_deployment_environment_name',
'resource_deployment.environment.name',
'resource_k8s_cluster_name',
'resource_k8s.cluster.name',
'resource_k8s_cluster_namespace',

View File

@@ -148,9 +148,9 @@ export const getResourceDeploymentKeys = (
dotMetricsEnabled: boolean,
): string => {
if (dotMetricsEnabled) {
return 'resource_deployment.environment';
return 'resource_deployment.environment.name';
}
return 'resource_deployment_environment';
return 'resource_deployment_environment_name';
};
export const GetTagKeys = async (

View File

@@ -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:

View File

@@ -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);
});

View File

@@ -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',
},

View File

@@ -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>

View File

@@ -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',

View File

@@ -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>

View File

@@ -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',

View File

@@ -38,7 +38,7 @@ export function Section(props: SectionProps): JSX.Element {
'hasError',
'durationNano',
'serviceName',
'deployment.environment',
'deployment.environment.name',
]),
),
[selectedFilters],

View File

@@ -14,7 +14,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',
@@ -208,11 +208,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',

View File

@@ -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

View File

@@ -24,12 +24,14 @@ HASTOKEN=23
HAS=24
HASANY=25
HASALL=26
BOOL=27
NUMBER=28
QUOTED_TEXT=29
KEY=30
WS=31
FREETEXT=32
SEARCH=27
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

View File

@@ -24,12 +24,14 @@ HASTOKEN=23
HAS=24
HASANY=25
HASALL=26
BOOL=27
NUMBER=28
QUOTED_TEXT=29
KEY=30
WS=31
FREETEXT=32
SEARCH=27
EXACT=28
BOOL=29
NUMBER=30
QUOTED_TEXT=31
KEY=32
WS=33
FREETEXT=34
'('=1
')'=2
'['=3

View File

@@ -1,4 +1,4 @@
// Generated from FilterQuery.g4 by ANTLR 4.13.1
// Generated from grammar/FilterQuery.g4 by ANTLR 4.13.2
// noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols
import {
ATN,
@@ -38,12 +38,14 @@ export default class FilterQueryLexer extends Lexer {
public static readonly HAS = 24;
public static readonly HASANY = 25;
public static readonly HASALL = 26;
public static readonly BOOL = 27;
public static readonly NUMBER = 28;
public static readonly QUOTED_TEXT = 29;
public static readonly KEY = 30;
public static readonly WS = 31;
public static readonly FREETEXT = 32;
public static readonly SEARCH = 27;
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" ];
@@ -68,7 +70,8 @@ export default class FilterQueryLexer extends Lexer {
"AND", "OR",
"HASTOKEN",
"HAS", "HASANY",
"HASALL", "BOOL",
"HASALL", "SEARCH",
"EXACT", "BOOL",
"NUMBER", "QUOTED_TEXT",
"KEY", "WS",
"FREETEXT" ];
@@ -78,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",
"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",
];
@@ -100,119 +103,124 @@ export default class FilterQueryLexer extends Lexer {
public get modeNames(): string[] { return FilterQueryLexer.modeNames; }
public static readonly _serializedATN: number[] = [4,0,32,320,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,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,89,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,132,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,149,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,26,1,26,3,26,201,8,26,1,27,1,27,1,28,3,28,206,8,28,1,28,4,28,
209,8,28,11,28,12,28,210,1,28,1,28,5,28,215,8,28,10,28,12,28,218,9,28,3,
28,220,8,28,1,28,1,28,3,28,224,8,28,1,28,4,28,227,8,28,11,28,12,28,228,
3,28,231,8,28,1,28,3,28,234,8,28,1,28,1,28,4,28,238,8,28,11,28,12,28,239,
1,28,1,28,3,28,244,8,28,1,28,4,28,247,8,28,11,28,12,28,248,3,28,251,8,28,
3,28,253,8,28,1,29,1,29,1,29,1,29,5,29,259,8,29,10,29,12,29,262,9,29,1,
29,1,29,1,29,1,29,1,29,5,29,269,8,29,10,29,12,29,272,9,29,1,29,3,29,275,
8,29,1,30,1,30,5,30,279,8,30,10,30,12,30,282,9,30,1,31,1,31,1,31,1,32,1,
32,1,32,1,32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,4,33,298,8,33,11,33,12,
33,299,5,33,302,8,33,10,33,12,33,305,9,33,1,34,4,34,308,8,34,11,34,12,34,
309,1,34,1,34,1,35,1,35,1,36,4,36,317,8,36,11,36,12,36,318,0,0,37,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,
0,57,28,59,29,61,0,63,0,65,0,67,30,69,31,71,0,73,32,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,344,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,57,1,0,0,0,0,
59,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0,0,0,73,1,0,0,0,1,75,1,0,0,0,3,77,1,0,
0,0,5,79,1,0,0,0,7,81,1,0,0,0,9,83,1,0,0,0,11,88,1,0,0,0,13,90,1,0,0,0,
15,93,1,0,0,0,17,96,1,0,0,0,19,98,1,0,0,0,21,101,1,0,0,0,23,103,1,0,0,0,
25,106,1,0,0,0,27,111,1,0,0,0,29,117,1,0,0,0,31,125,1,0,0,0,33,133,1,0,
0,0,35,140,1,0,0,0,37,150,1,0,0,0,39,153,1,0,0,0,41,157,1,0,0,0,43,161,
1,0,0,0,45,164,1,0,0,0,47,173,1,0,0,0,49,177,1,0,0,0,51,184,1,0,0,0,53,
200,1,0,0,0,55,202,1,0,0,0,57,252,1,0,0,0,59,274,1,0,0,0,61,276,1,0,0,0,
63,283,1,0,0,0,65,286,1,0,0,0,67,290,1,0,0,0,69,307,1,0,0,0,71,313,1,0,
0,0,73,316,1,0,0,0,75,76,5,40,0,0,76,2,1,0,0,0,77,78,5,41,0,0,78,4,1,0,
0,0,79,80,5,91,0,0,80,6,1,0,0,0,81,82,5,93,0,0,82,8,1,0,0,0,83,84,5,44,
0,0,84,10,1,0,0,0,85,89,5,61,0,0,86,87,5,61,0,0,87,89,5,61,0,0,88,85,1,
0,0,0,88,86,1,0,0,0,89,12,1,0,0,0,90,91,5,33,0,0,91,92,5,61,0,0,92,14,1,
0,0,0,93,94,5,60,0,0,94,95,5,62,0,0,95,16,1,0,0,0,96,97,5,60,0,0,97,18,
1,0,0,0,98,99,5,60,0,0,99,100,5,61,0,0,100,20,1,0,0,0,101,102,5,62,0,0,
102,22,1,0,0,0,103,104,5,62,0,0,104,105,5,61,0,0,105,24,1,0,0,0,106,107,
7,0,0,0,107,108,7,1,0,0,108,109,7,2,0,0,109,110,7,3,0,0,110,26,1,0,0,0,
111,112,7,1,0,0,112,113,7,0,0,0,113,114,7,1,0,0,114,115,7,2,0,0,115,116,
7,3,0,0,116,28,1,0,0,0,117,118,7,4,0,0,118,119,7,3,0,0,119,120,7,5,0,0,
120,121,7,6,0,0,121,122,7,3,0,0,122,123,7,3,0,0,123,124,7,7,0,0,124,30,
1,0,0,0,125,126,7,3,0,0,126,127,7,8,0,0,127,128,7,1,0,0,128,129,7,9,0,0,
129,131,7,5,0,0,130,132,7,9,0,0,131,130,1,0,0,0,131,132,1,0,0,0,132,32,
1,0,0,0,133,134,7,10,0,0,134,135,7,3,0,0,135,136,7,11,0,0,136,137,7,3,0,
0,137,138,7,8,0,0,138,139,7,12,0,0,139,34,1,0,0,0,140,141,7,13,0,0,141,
142,7,14,0,0,142,143,7,7,0,0,143,144,7,5,0,0,144,145,7,15,0,0,145,146,7,
1,0,0,146,148,7,7,0,0,147,149,7,9,0,0,148,147,1,0,0,0,148,149,1,0,0,0,149,
36,1,0,0,0,150,151,7,1,0,0,151,152,7,7,0,0,152,38,1,0,0,0,153,154,7,7,0,
0,154,155,7,14,0,0,155,156,7,5,0,0,156,40,1,0,0,0,157,158,7,15,0,0,158,
159,7,7,0,0,159,160,7,16,0,0,160,42,1,0,0,0,161,162,7,14,0,0,162,163,7,
10,0,0,163,44,1,0,0,0,164,165,7,17,0,0,165,166,7,15,0,0,166,167,7,9,0,0,
167,168,7,5,0,0,168,169,7,14,0,0,169,170,7,2,0,0,170,171,7,3,0,0,171,172,
7,7,0,0,172,46,1,0,0,0,173,174,7,17,0,0,174,175,7,15,0,0,175,176,7,9,0,
0,176,48,1,0,0,0,177,178,7,17,0,0,178,179,7,15,0,0,179,180,7,9,0,0,180,
181,7,15,0,0,181,182,7,7,0,0,182,183,7,18,0,0,183,50,1,0,0,0,184,185,7,
17,0,0,185,186,7,15,0,0,186,187,7,9,0,0,187,188,7,15,0,0,188,189,7,0,0,
0,189,190,7,0,0,0,190,52,1,0,0,0,191,192,7,5,0,0,192,193,7,10,0,0,193,194,
7,19,0,0,194,201,7,3,0,0,195,196,7,20,0,0,196,197,7,15,0,0,197,198,7,0,
0,0,198,199,7,9,0,0,199,201,7,3,0,0,200,191,1,0,0,0,200,195,1,0,0,0,201,
54,1,0,0,0,202,203,7,21,0,0,203,56,1,0,0,0,204,206,3,55,27,0,205,204,1,
0,0,0,205,206,1,0,0,0,206,208,1,0,0,0,207,209,3,71,35,0,208,207,1,0,0,0,
209,210,1,0,0,0,210,208,1,0,0,0,210,211,1,0,0,0,211,219,1,0,0,0,212,216,
5,46,0,0,213,215,3,71,35,0,214,213,1,0,0,0,215,218,1,0,0,0,216,214,1,0,
0,0,216,217,1,0,0,0,217,220,1,0,0,0,218,216,1,0,0,0,219,212,1,0,0,0,219,
220,1,0,0,0,220,230,1,0,0,0,221,223,7,3,0,0,222,224,3,55,27,0,223,222,1,
0,0,0,223,224,1,0,0,0,224,226,1,0,0,0,225,227,3,71,35,0,226,225,1,0,0,0,
227,228,1,0,0,0,228,226,1,0,0,0,228,229,1,0,0,0,229,231,1,0,0,0,230,221,
1,0,0,0,230,231,1,0,0,0,231,253,1,0,0,0,232,234,3,55,27,0,233,232,1,0,0,
0,233,234,1,0,0,0,234,235,1,0,0,0,235,237,5,46,0,0,236,238,3,71,35,0,237,
236,1,0,0,0,238,239,1,0,0,0,239,237,1,0,0,0,239,240,1,0,0,0,240,250,1,0,
0,0,241,243,7,3,0,0,242,244,3,55,27,0,243,242,1,0,0,0,243,244,1,0,0,0,244,
246,1,0,0,0,245,247,3,71,35,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,251,1,0,0,0,250,241,1,0,0,0,250,251,1,0,0,0,251,
253,1,0,0,0,252,205,1,0,0,0,252,233,1,0,0,0,253,58,1,0,0,0,254,260,5,34,
0,0,255,259,8,22,0,0,256,257,5,92,0,0,257,259,9,0,0,0,258,255,1,0,0,0,258,
256,1,0,0,0,259,262,1,0,0,0,260,258,1,0,0,0,260,261,1,0,0,0,261,263,1,0,
0,0,262,260,1,0,0,0,263,275,5,34,0,0,264,270,5,39,0,0,265,269,8,23,0,0,
266,267,5,92,0,0,267,269,9,0,0,0,268,265,1,0,0,0,268,266,1,0,0,0,269,272,
1,0,0,0,270,268,1,0,0,0,270,271,1,0,0,0,271,273,1,0,0,0,272,270,1,0,0,0,
273,275,5,39,0,0,274,254,1,0,0,0,274,264,1,0,0,0,275,60,1,0,0,0,276,280,
7,24,0,0,277,279,7,25,0,0,278,277,1,0,0,0,279,282,1,0,0,0,280,278,1,0,0,
0,280,281,1,0,0,0,281,62,1,0,0,0,282,280,1,0,0,0,283,284,5,91,0,0,284,285,
5,93,0,0,285,64,1,0,0,0,286,287,5,91,0,0,287,288,5,42,0,0,288,289,5,93,
0,0,289,66,1,0,0,0,290,303,3,61,30,0,291,292,5,46,0,0,292,302,3,61,30,0,
293,302,3,63,31,0,294,302,3,65,32,0,295,297,5,46,0,0,296,298,3,71,35,0,
297,296,1,0,0,0,298,299,1,0,0,0,299,297,1,0,0,0,299,300,1,0,0,0,300,302,
1,0,0,0,301,291,1,0,0,0,301,293,1,0,0,0,301,294,1,0,0,0,301,295,1,0,0,0,
302,305,1,0,0,0,303,301,1,0,0,0,303,304,1,0,0,0,304,68,1,0,0,0,305,303,
1,0,0,0,306,308,7,26,0,0,307,306,1,0,0,0,308,309,1,0,0,0,309,307,1,0,0,
0,309,310,1,0,0,0,310,311,1,0,0,0,311,312,6,34,0,0,312,70,1,0,0,0,313,314,
7,27,0,0,314,72,1,0,0,0,315,317,8,28,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,74,1,0,0,0,29,0,88,131,148,200,205,
210,216,219,223,228,230,233,239,243,248,250,252,258,260,268,270,274,280,
299,301,303,309,318,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 {
@@ -225,4 +233,4 @@ export default class FilterQueryLexer extends Lexer {
static DecisionsToDFA = FilterQueryLexer._ATN.decisionToState.map( (ds: DecisionState, index: number) => new DFA(ds, index) );
}
}

View File

@@ -1,25 +1,28 @@
// Generated from FilterQuery.g4 by ANTLR 4.13.1
// Generated from grammar/FilterQuery.g4 by ANTLR 4.13.2
import {ParseTreeListener} from "antlr4";
import { QueryContext } from "./FilterQueryParser";
import { ExpressionContext } from "./FilterQueryParser";
import { OrExpressionContext } from "./FilterQueryParser";
import { AndExpressionContext } from "./FilterQueryParser";
import { UnaryExpressionContext } from "./FilterQueryParser";
import { PrimaryContext } from "./FilterQueryParser";
import { ComparisonContext } from "./FilterQueryParser";
import { InClauseContext } from "./FilterQueryParser";
import { NotInClauseContext } from "./FilterQueryParser";
import { ValueListContext } from "./FilterQueryParser";
import { FullTextContext } from "./FilterQueryParser";
import { FunctionCallContext } from "./FilterQueryParser";
import { FunctionParamListContext } from "./FilterQueryParser";
import { FunctionParamContext } from "./FilterQueryParser";
import { ArrayContext } from "./FilterQueryParser";
import { ValueContext } from "./FilterQueryParser";
import { KeyContext } from "./FilterQueryParser";
import { QueryContext } from "./FilterQueryParser.js";
import { ExpressionContext } from "./FilterQueryParser.js";
import { OrExpressionContext } from "./FilterQueryParser.js";
import { AndExpressionContext } from "./FilterQueryParser.js";
import { UnaryExpressionContext } from "./FilterQueryParser.js";
import { PrimaryContext } from "./FilterQueryParser.js";
import { ComparisonContext } from "./FilterQueryParser.js";
import { InClauseContext } from "./FilterQueryParser.js";
import { NotInClauseContext } from "./FilterQueryParser.js";
import { ValueListContext } from "./FilterQueryParser.js";
import { FullTextContext } from "./FilterQueryParser.js";
import { FunctionCallContext } from "./FilterQueryParser.js";
import { SearchCallContext } from "./FilterQueryParser.js";
import { FunctionParamListContext } from "./FilterQueryParser.js";
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";
/**
@@ -147,6 +150,16 @@ export default class FilterQueryListener extends ParseTreeListener {
* @param ctx the parse tree
*/
exitFunctionCall?: (ctx: FunctionCallContext) => void;
/**
* Enter a parse tree produced by `FilterQueryParser.searchCall`.
* @param ctx the parse tree
*/
enterSearchCall?: (ctx: SearchCallContext) => void;
/**
* Exit a parse tree produced by `FilterQueryParser.searchCall`.
* @param ctx the parse tree
*/
exitSearchCall?: (ctx: SearchCallContext) => void;
/**
* Enter a parse tree produced by `FilterQueryParser.functionParamList`.
* @param ctx the parse tree
@@ -197,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

View File

@@ -1,25 +1,28 @@
// Generated from FilterQuery.g4 by ANTLR 4.13.1
// Generated from grammar/FilterQuery.g4 by ANTLR 4.13.2
import {ParseTreeVisitor} from 'antlr4';
import { QueryContext } from "./FilterQueryParser";
import { ExpressionContext } from "./FilterQueryParser";
import { OrExpressionContext } from "./FilterQueryParser";
import { AndExpressionContext } from "./FilterQueryParser";
import { UnaryExpressionContext } from "./FilterQueryParser";
import { PrimaryContext } from "./FilterQueryParser";
import { ComparisonContext } from "./FilterQueryParser";
import { InClauseContext } from "./FilterQueryParser";
import { NotInClauseContext } from "./FilterQueryParser";
import { ValueListContext } from "./FilterQueryParser";
import { FullTextContext } from "./FilterQueryParser";
import { FunctionCallContext } from "./FilterQueryParser";
import { FunctionParamListContext } from "./FilterQueryParser";
import { FunctionParamContext } from "./FilterQueryParser";
import { ArrayContext } from "./FilterQueryParser";
import { ValueContext } from "./FilterQueryParser";
import { KeyContext } from "./FilterQueryParser";
import { QueryContext } from "./FilterQueryParser.js";
import { ExpressionContext } from "./FilterQueryParser.js";
import { OrExpressionContext } from "./FilterQueryParser.js";
import { AndExpressionContext } from "./FilterQueryParser.js";
import { UnaryExpressionContext } from "./FilterQueryParser.js";
import { PrimaryContext } from "./FilterQueryParser.js";
import { ComparisonContext } from "./FilterQueryParser.js";
import { InClauseContext } from "./FilterQueryParser.js";
import { NotInClauseContext } from "./FilterQueryParser.js";
import { ValueListContext } from "./FilterQueryParser.js";
import { FullTextContext } from "./FilterQueryParser.js";
import { FunctionCallContext } from "./FilterQueryParser.js";
import { SearchCallContext } from "./FilterQueryParser.js";
import { FunctionParamListContext } from "./FilterQueryParser.js";
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";
/**
@@ -102,6 +105,12 @@ export default class FilterQueryVisitor<Result> extends ParseTreeVisitor<Result>
* @return the visitor result
*/
visitFunctionCall?: (ctx: FunctionCallContext) => Result;
/**
* Visit a parse tree produced by `FilterQueryParser.searchCall`.
* @param ctx the parse tree
* @return the visitor result
*/
visitSearchCall?: (ctx: SearchCallContext) => Result;
/**
* Visit a parse tree produced by `FilterQueryParser.functionParamList`.
* @param ctx the parse tree
@@ -132,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;
}

View File

@@ -223,6 +223,12 @@
padding-left: 6px !important;
}
&__label {
display: inline-flex;
align-items: baseline;
gap: 6px;
}
&__pinned-icon {
flex-shrink: 0;
color: var(--text-robin-400);

View File

@@ -67,6 +67,7 @@ export interface PrettyViewProps {
*/
pinnedFieldsValue?: string[];
onPinnedFieldsChange?: (next: string[]) => void;
labelSuffixRenderer?: (fieldKey: string) => React.ReactNode;
}
function PrettyView({
@@ -78,6 +79,7 @@ function PrettyView({
drawerKey = 'default',
pinnedFieldsValue,
onPinnedFieldsChange,
labelSuffixRenderer,
}: PrettyViewProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const [, setCopy] = useCopyToClipboard();
@@ -305,10 +307,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 (
@@ -351,6 +367,7 @@ function PrettyView({
shouldExpandNodeInitially={shouldExpandNodeInitially}
valueRenderer={valueRenderer}
getItemString={getItemString}
labelRenderer={labelRenderer}
/>
</div>
);

View 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[];
}

View File

@@ -117,6 +117,8 @@ export type SpaceAggregation =
export type ColumnType = 'group' | 'aggregation';
export type FieldResolution = 'exact';
// ===================== Variable Types =====================
export type VariableType = 'query' | 'dynamic' | 'custom' | 'text';
@@ -136,6 +138,7 @@ export interface TelemetryFieldKey {
signal?: SignalType;
fieldContext?: FieldContext;
fieldDataType?: FieldDataType;
fieldResolution?: FieldResolution;
materialized?: boolean;
isIndexed?: boolean;
}

View File

@@ -648,3 +648,176 @@ describe('getQueryContextAtCursor - trailing dot in key/value', () => {
expect(ctx.keyToken).toBe('k8s.namespace');
});
});
describe('getQueryContextAtCursor - partial operator', () => {
it('treats text after an incomplete key as an operator prefix', () => {
const q = 'service.name c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.isInKey).toBe(false);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('c');
expect(ctx.currentPair).toStrictEqual(
expect.objectContaining({
key: 'service.name',
operator: 'c',
position: expect.objectContaining({
operatorStart: 13,
operatorEnd: 13,
}),
}),
);
});
it('keeps the operator context while completing contains', () => {
const q = 'service.name cont';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('cont');
});
it('treats cursor mid-token as operator context', () => {
const q = 'service.name cont';
// cursor sits between "con" and "t" — user still typing the operator
const ctx = getQueryContextAtCursor(q, 15);
expect(ctx.isInOperator).toBe(true);
expect(ctx.isInKey).toBe(false);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('cont');
});
it('keeps operator context when an AND conjunction precedes the pair', () => {
const q = 'a = 1 AND service.name c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.isInKey).toBe(false);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('c');
expect(ctx.currentPair).toStrictEqual(
expect.objectContaining({
key: 'service.name',
operator: 'c',
position: expect.objectContaining({
operatorStart: 23,
operatorEnd: 23,
}),
}),
);
});
it('keeps operator context when an open parenthesis precedes the pair', () => {
const q = '(service.name c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.isInKey).toBe(false);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('c');
});
it('re-glues a partial operator that follows a NOT negation', () => {
const q = 'service.name NOT c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.isInKey).toBe(false);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('NOT c');
// operatorStart points at the partial operator (post-NOT), not at the
// negation — so suggestion selection only replaces the partial, never
// the user's typed NOT.
expect(ctx.currentPair).toStrictEqual(
expect.objectContaining({
key: 'service.name',
operator: 'NOT c',
hasNegation: true,
position: expect.objectContaining({
negationStart: 13,
negationEnd: 15,
operatorStart: 17,
operatorEnd: 17,
}),
}),
);
});
it('re-glues a multi-character partial operator after NOT', () => {
const q = 'service.name NOT lik';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('NOT lik');
expect(ctx.currentPair?.hasNegation).toBe(true);
});
it('re-glues an uppercase partial operator after NOT', () => {
const q = 'service.name NOT EXI';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('NOT EXI');
expect(ctx.currentPair?.hasNegation).toBe(true);
});
it('preserves original NOT casing in the operator text', () => {
const q = 'service.name not c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('not c');
expect(ctx.currentPair?.hasNegation).toBe(true);
});
it('tolerates extra whitespace between NOT and the partial operator', () => {
const q = 'service.name NOT c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('service.name');
// Display text uses a canonical single space between NOT and the
// partial, regardless of how many spaces the user typed.
expect(ctx.operatorToken).toBe('NOT c');
expect(ctx.currentPair?.hasNegation).toBe(true);
});
it('keeps operator context for NOT-prefixed partial inside parentheses', () => {
const q = '(service.name NOT c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('NOT c');
expect(ctx.currentPair?.hasNegation).toBe(true);
});
it('keeps operator context for NOT-prefixed partial after an AND conjunction', () => {
const q = 'a = 1 AND service.name NOT c';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('service.name');
expect(ctx.operatorToken).toBe('NOT c');
expect(ctx.currentPair?.hasNegation).toBe(true);
});
it('re-glues the most recent incomplete pair when three partial tokens are typed', () => {
// Pins documented behavior: with two trailing partial pairs (`c` and
// `k`), the heuristic pairs the most recent two — `c` becomes the
// key, `k` becomes the partial operator. The earlier `service.name`
// is dropped from the current pair view.
const q = 'service.name c k';
const ctx = getQueryContextAtCursor(q, q.length);
expect(ctx.isInOperator).toBe(true);
expect(ctx.keyToken).toBe('c');
expect(ctx.operatorToken).toBe('k');
});
});

View 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',
]);
});
});

View File

@@ -605,6 +605,98 @@ export function getQueryContextAtCursor(
queryPairs,
);
// Re-glue a partial operator that ANTLR has lexed as a second key.
//
// When the user types `service.name c` (or `service.name NOT c`), the
// lexer sees two KEY tokens (`service.name`, `c`) instead of a key +
// partial operator, so `extractQueryPairs` emits two consecutive
// key-only incomplete pairs. Downstream, that makes the dropdown
// suggest keys when it should be suggesting operators.
//
// Detect that pattern — a previous incomplete key-only pair followed
// by another incomplete key-only `currentPair`, separated only by
// whitespace (or by a negation token attached to the previous pair) —
// and rebuild a single synthetic pair where the previous pair's key
// is the key and the current pair's key is treated as the partial
// operator. The synthetic pair inherits the previous pair's negation
// flag and positions via the spread, so `NOT <partial>` propagates
// correctly to consumers.
const previousIncompletePair = queryPairs
.filter(
(pair) =>
!pair.isComplete &&
!!pair.key &&
!pair.operator &&
pair.position.keyEnd < (currentPair?.position.keyStart ?? cursorIndex),
)
.sort((a, b) => b.position.keyEnd - a.position.keyEnd)[0];
if (
previousIncompletePair &&
currentPair &&
currentPair !== previousIncompletePair &&
!currentPair.operator &&
currentPair.position.keyStart > previousIncompletePair.position.keyEnd
) {
const negationStart = previousIncompletePair.position.negationStart ?? 0;
const negationEnd = previousIncompletePair.position.negationEnd ?? 0;
const negationAfterKey =
previousIncompletePair.hasNegation &&
negationStart > previousIncompletePair.position.keyEnd;
const gapStart = negationAfterKey
? negationEnd + 1
: previousIncompletePair.position.keyEnd + 1;
const textBetweenPairs = query.slice(
gapStart,
currentPair.position.keyStart,
);
if (textBetweenPairs.trim() === '') {
// The replacement range (operatorStart/operatorEnd) must point
// at the partial operator only, NOT the leading negation.
// Consumers like QuerySearch use it to splice the chosen
// suggestion in-place, so including the negation would let a
// `NOT lik` -> `LIKE` selection erase the user's typed `NOT`.
// Matches the convention used for complete pairs in
// extractQueryPairs, where operatorStart starts after the
// negation token.
const operatorStart = currentPair.position.keyStart;
const operatorEnd = currentPair.position.keyEnd;
const partialOperator = query.slice(operatorStart, operatorEnd + 1);
const operatorText = negationAfterKey
? `${query.slice(negationStart, negationEnd + 1)} ${partialOperator}`
: partialOperator;
return {
tokenType: -1,
text: '',
start: cursorIndex,
stop: cursorIndex,
currentToken: operatorText,
isInKey: false,
isInNegation: false,
isInOperator: true,
isInValue: false,
isInConjunction: false,
isInFunction: false,
isInParenthesis: false,
isInBracketList: false,
keyToken: previousIncompletePair.key,
operatorToken: operatorText,
queryPairs,
currentPair: {
...previousIncompletePair,
operator: operatorText,
position: {
...previousIncompletePair.position,
operatorStart,
operatorEnd,
},
},
};
}
}
// Check if cursor is within any of the specific context boundaries
// FIXED: Include the case where the cursor is exactly at the end of a boundary
const isInKeyBoundary =

View File

@@ -0,0 +1,61 @@
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.old.map((old) => ({ old, 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.old].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.old] : [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);
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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",
},
},

View File

@@ -6,6 +6,7 @@ import (
"strings"
parser "github.com/SigNoz/signoz/pkg/parser/filterquery/grammar"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/antlr4-go/antlr/v4"
"golang.org/x/exp/maps"
@@ -187,25 +188,35 @@ 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{}{}
parsedKey := telemetrytypes.GetFieldKeyFromKeyText(key)
r.keysSeen[parsedKey.Name] = struct{}{}
labelKey := key
if _, exists := r.labels[labelKey]; !exists {
labelKey = parsedKey.Name
}
// Check if this key is in the labels and was part of group by
if value, exists := r.labels[key]; exists {
if _, partOfGroup := r.groupBySet[key]; partOfGroup {
if value, exists := r.labels[labelKey]; exists {
if _, partOfGroup := r.groupBySet[labelKey]; 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("=")
@@ -408,8 +419,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 {

View File

@@ -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(resource.deployment.environment) = 'staging'",
groupByItems: []qbtypes.GroupByKey{
{TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{Name: "deployment.environment"}},
},
expected: "exact(resource.deployment.environment)='production'",
description: "Should keep the exact wrapper when replacing a grouped label",
},
{
name: "empty_where_clause_with_labels",

View File

@@ -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")

View File

@@ -136,7 +136,12 @@ func (v *visitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
// predicate; any other identifier is treated as a tag key — the operator
// applies to the tag's value, with a case-insensitive match on the tag's key.
func (v *visitor) VisitComparison(ctx *grammar.ComparisonContext) any {
key := strings.ToLower(strings.TrimSpace(ctx.Key().GetText()))
field := ctx.Field()
keyText := field.GetText()
if exactCall := field.ExactCall(); exactCall != nil {
keyText = exactCall.Key().GetText()
}
key := strings.ToLower(strings.TrimSpace(keyText))
operation, ok := v.extractOperation(ctx)
if !ok {
@@ -427,7 +432,7 @@ func (v *visitor) buildFreeTextTerm(value string) string {
}
// buildFreeTextContains emits a case-insensitive contains as
// LOWER(COALESCE(col, '')) LIKE LOWER(?), identical on SQLite and Postgres.
// LOWER(COALESCE(col, )) LIKE LOWER(?), identical on SQLite and Postgres.
// COALESCE keeps a NULL column (an absent description) false rather than NULL —
// otherwise `NOT (…)` goes NULL and drops every description-less dashboard. The
// value's % and _ are escaped, and ESCAPE pins backslash as the escape char.

View File

@@ -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)
}

View File

@@ -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(), &params); 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,

View File

@@ -0,0 +1,65 @@
package metricreductionrule
import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/semconv"
"github.com/SigNoz/signoz/pkg/types/metricreductionruletypes"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
)
var protectedLabels = buildProtectedLabels()
// ValidatePostableReductionRule validates request structure and protected-label
// policy before a rule reaches storage.
func ValidatePostableReductionRule(req *metricreductionruletypes.PostableReductionRule) error {
if err := req.Validate(); err != nil {
return err
}
return validateProtectedLabels(req.MatchType, req.Labels)
}
// ValidateUpdatableReductionRule validates request structure and
// protected-label policy before a rule reaches storage.
func ValidateUpdatableReductionRule(req *metricreductionruletypes.UpdatableReductionRule) error {
if err := req.Validate(); err != nil {
return err
}
return validateProtectedLabels(req.MatchType, req.Labels)
}
// IsProtectedLabel reports whether metric reduction must always retain label.
func IsProtectedLabel(label string) bool {
_, ok := protectedLabels[label]
return ok
}
func buildProtectedLabels() map[string]struct{} {
labels := map[string]struct{}{
"le": {},
"quantile": {},
"__name__": {},
"__temporality__": {},
}
selector := telemetrytypes.FieldKeySelector{
Name: "deployment.environment.name",
Signal: telemetrytypes.SignalMetrics,
FieldContext: telemetrytypes.FieldContextResource,
}
for _, member := range semconv.Members(semconv.KindAttribute, selector) {
labels[member] = struct{}{}
}
return labels
}
func validateProtectedLabels(matchType metricreductionruletypes.MatchType, labels []string) error {
if matchType != metricreductionruletypes.MatchTypeDrop {
return nil
}
for _, label := range labels {
if IsProtectedLabel(label) {
return errors.Newf(errors.TypeInvalidInput, metricreductionruletypes.ErrCodeMetricReductionRuleProtectedLabel,
"label %q is protected and cannot be dropped", label)
}
}
return nil
}

View File

@@ -0,0 +1,64 @@
package metricreductionrule_test
import (
"testing"
"github.com/SigNoz/signoz/pkg/modules/metricreductionrule"
"github.com/SigNoz/signoz/pkg/types/metricreductionruletypes"
"github.com/stretchr/testify/assert"
)
func TestDropRuleRejectsBuiltInProtectedLabel(t *testing.T) {
req := &metricreductionruletypes.UpdatableReductionRule{
MatchType: metricreductionruletypes.MatchTypeDrop,
Labels: []string{"le"},
}
err := metricreductionrule.ValidateUpdatableReductionRule(req)
assert.Error(t, err, "histogram boundary label must remain protected")
}
func TestDropRuleRejectsCurrentEnvironmentLabel(t *testing.T) {
req := &metricreductionruletypes.UpdatableReductionRule{
MatchType: metricreductionruletypes.MatchTypeDrop,
Labels: []string{"deployment.environment.name"},
}
err := metricreductionrule.ValidateUpdatableReductionRule(req)
assert.Error(t, err, "current deployment environment label must remain protected")
}
func TestDropRuleRejectsHistoricalEnvironmentLabel(t *testing.T) {
req := &metricreductionruletypes.UpdatableReductionRule{
MatchType: metricreductionruletypes.MatchTypeDrop,
Labels: []string{"deployment.environment"},
}
err := metricreductionrule.ValidateUpdatableReductionRule(req)
assert.Error(t, err, "historical deployment environment label must remain protected")
}
func TestKeepRuleAllowsProtectedLabel(t *testing.T) {
req := &metricreductionruletypes.UpdatableReductionRule{
MatchType: metricreductionruletypes.MatchTypeKeep,
Labels: []string{"le"},
}
err := metricreductionrule.ValidateUpdatableReductionRule(req)
assert.NoError(t, err, "a keep rule may retain a protected label")
}
func TestDropRuleAllowsUnprotectedLabel(t *testing.T) {
req := &metricreductionruletypes.UpdatableReductionRule{
MatchType: metricreductionruletypes.MatchTypeDrop,
Labels: []string{"host.name"},
}
err := metricreductionrule.ValidateUpdatableReductionRule(req)
assert.NoError(t, err, "an ordinary label may be dropped")
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More