diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index c8d9e54421..a36fa711a4 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -133,7 +133,7 @@ services: # - ./data/clickhouse-3/:/var/lib/clickhouse/ alertmanager: - image: signoz/alertmanager:0.23.5 + image: signoz/alertmanager:0.23.7 volumes: - ./data/alertmanager:/data command: @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.55.0 + image: signoz/query-service:0.56.0 command: [ "-config=/root/config/prometheus.yml", @@ -186,7 +186,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:0.55.0 + image: signoz/frontend:0.56.0 deploy: restart_policy: condition: on-failure @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.102.10 + image: signoz/signoz-otel-collector:0.102.12 command: [ "--config=/etc/otel-collector-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index 7a2bcdfa75..0e902a9e28 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -1,5 +1,8 @@ version: "2.4" +include: + - test-app-docker-compose.yaml + services: zookeeper-1: image: bitnami/zookeeper:3.7.1 @@ -54,7 +57,7 @@ services: alertmanager: container_name: signoz-alertmanager - image: signoz/alertmanager:0.23.5 + image: signoz/alertmanager:0.23.7 volumes: - ./data/alertmanager:/data depends_on: @@ -81,7 +84,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.102.10 + image: signoz/signoz-otel-collector:0.102.12 command: [ "--config=/etc/otel-collector-config.yaml", @@ -128,29 +131,3 @@ services: depends_on: - otel-collector restart: on-failure - - hotrod: - image: jaegertracing/example-hotrod:1.30 - container_name: hotrod - logging: - options: - max-size: 50m - max-file: "3" - command: [ "all" ] - environment: - - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces - - load-hotrod: - image: "signoz/locust:1.2.3" - container_name: load-hotrod - hostname: load-hotrod - environment: - ATTACKED_HOST: http://hotrod:8080 - LOCUST_MODE: standalone - NO_PROXY: standalone - TASK_DELAY_FROM: 5 - TASK_DELAY_TO: 30 - QUIET_MODE: "${QUIET_MODE:-false}" - LOCUST_OPTS: "--headless -u 10 -r 1" - volumes: - - ../common/locust-scripts:/locust diff --git a/deploy/docker/clickhouse-setup/docker-compose-minimal.yaml b/deploy/docker/clickhouse-setup/docker-compose-minimal.yaml new file mode 100644 index 0000000000..e12fb3831c --- /dev/null +++ b/deploy/docker/clickhouse-setup/docker-compose-minimal.yaml @@ -0,0 +1,279 @@ +x-clickhouse-defaults: &clickhouse-defaults + restart: on-failure + # addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab + image: clickhouse/clickhouse-server:24.1.2-alpine + tty: true + depends_on: + - zookeeper-1 + # - zookeeper-2 + # - zookeeper-3 + logging: + options: + max-size: 50m + max-file: "3" + healthcheck: + # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "0.0.0.0:8123/ping" + ] + interval: 30s + timeout: 5s + retries: 3 + ulimits: + nproc: 65535 + nofile: + soft: 262144 + hard: 262144 + +x-db-depend: &db-depend + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + +services: + + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + container_name: signoz-zookeeper-1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-2: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-2 + # hostname: zookeeper-2 + # user: root + # ports: + # - "2182:2181" + # - "2889:2888" + # - "3889:3888" + # volumes: + # - ./data/zookeeper-2:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=2 + # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-3: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-3 + # hostname: zookeeper-3 + # user: root + # ports: + # - "2183:2181" + # - "2890:2888" + # - "3890:3888" + # volumes: + # - ./data/zookeeper-3:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=3 + # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + <<: *clickhouse-defaults + container_name: signoz-clickhouse + hostname: clickhouse + ports: + - "9000:9000" + - "8123:8123" + - "9181:9181" + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + # clickhouse-2: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-2 + # hostname: clickhouse-2 + # ports: + # - "9001:9000" + # - "8124:8123" + # - "9182:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-2/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + + # clickhouse-3: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-3 + # hostname: clickhouse-3 + # ports: + # - "9002:9000" + # - "8125:8123" + # - "9183:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-3/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + alertmanager: + image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.7} + container_name: signoz-alertmanager + volumes: + - ./data/alertmanager:/data + depends_on: + query-service: + condition: service_healthy + restart: on-failure + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + + # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` + + query-service: + image: signoz/query-service:${DOCKER_TAG:-0.56.0} + container_name: signoz-query-service + command: + [ + "-config=/root/config/prometheus.yml", + "--use-logs-new-schema=true" + ] + # ports: + # - "6060:6060" # pprof port + # - "8080:8080" # query-service port + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + - DEPLOYMENT_TYPE=docker-standalone-amd + restart: on-failure + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + <<: *db-depend + + frontend: + image: signoz/frontend:${DOCKER_TAG:-0.56.0} + container_name: signoz-frontend + restart: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.10} + container_name: otel-migrator + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + clickhouse: + condition: service_healthy + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + + + otel-collector: + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.12} + container_name: signoz-otel-collector + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--copy-path=/var/tmp/collector-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /:/hostfs:ro + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux + - DOCKER_MULTI_NODE_CLUSTER=false + - LOW_CARDINAL_EXCEPTION_GROUPING=false + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + restart: on-failure + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + query-service: + condition: service_healthy + + logspout: + image: "gliderlabs/logspout:v3.2.14" + container_name: signoz-logspout + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + restart: on-failure diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index 174f0914a9..2bf0996da4 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -1,5 +1,8 @@ version: "2.4" +include: + - test-app-docker-compose.yaml + x-clickhouse-defaults: &clickhouse-defaults restart: on-failure # addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab @@ -149,7 +152,7 @@ services: # - ./user_scripts:/var/lib/clickhouse/user_scripts/ alertmanager: - image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5} + image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.7} container_name: signoz-alertmanager volumes: - ./data/alertmanager:/data @@ -164,7 +167,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.55.0} + image: signoz/query-service:${DOCKER_TAG:-0.56.0} container_name: signoz-query-service command: [ @@ -204,7 +207,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.55.0} + image: signoz/frontend:${DOCKER_TAG:-0.56.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -230,7 +233,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.10} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.12} container_name: signoz-otel-collector command: [ @@ -280,29 +283,3 @@ services: depends_on: - otel-collector restart: on-failure - - hotrod: - image: jaegertracing/example-hotrod:1.30 - container_name: hotrod - logging: - options: - max-size: 50m - max-file: "3" - command: [ "all" ] - environment: - - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces - - load-hotrod: - image: "signoz/locust:1.2.3" - container_name: load-hotrod - hostname: load-hotrod - environment: - ATTACKED_HOST: http://hotrod:8080 - LOCUST_MODE: standalone - NO_PROXY: standalone - TASK_DELAY_FROM: 5 - TASK_DELAY_TO: 30 - QUIET_MODE: "${QUIET_MODE:-false}" - LOCUST_OPTS: "--headless -u 10 -r 1" - volumes: - - ../common/locust-scripts:/locust diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 0c333899aa..251ca25344 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -1,307 +1,3 @@ -version: "2.4" - -x-clickhouse-defaults: &clickhouse-defaults - restart: on-failure - # addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab - image: clickhouse/clickhouse-server:24.1.2-alpine - tty: true - depends_on: - - zookeeper-1 - # - zookeeper-2 - # - zookeeper-3 - logging: - options: - max-size: 50m - max-file: "3" - healthcheck: - # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" - test: - [ - "CMD", - "wget", - "--spider", - "-q", - "0.0.0.0:8123/ping" - ] - interval: 30s - timeout: 5s - retries: 3 - ulimits: - nproc: 65535 - nofile: - soft: 262144 - hard: 262144 - -x-db-depend: &db-depend - depends_on: - clickhouse: - condition: service_healthy - otel-collector-migrator: - condition: service_completed_successfully - # clickhouse-2: - # condition: service_healthy - # clickhouse-3: - # condition: service_healthy - -services: - - zookeeper-1: - image: bitnami/zookeeper:3.7.1 - container_name: signoz-zookeeper-1 - hostname: zookeeper-1 - user: root - ports: - - "2181:2181" - - "2888:2888" - - "3888:3888" - volumes: - - ./data/zookeeper-1:/bitnami/zookeeper - environment: - - ZOO_SERVER_ID=1 - # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 - - ALLOW_ANONYMOUS_LOGIN=yes - - ZOO_AUTOPURGE_INTERVAL=1 - - # zookeeper-2: - # image: bitnami/zookeeper:3.7.0 - # container_name: signoz-zookeeper-2 - # hostname: zookeeper-2 - # user: root - # ports: - # - "2182:2181" - # - "2889:2888" - # - "3889:3888" - # volumes: - # - ./data/zookeeper-2:/bitnami/zookeeper - # environment: - # - ZOO_SERVER_ID=2 - # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 - # - ALLOW_ANONYMOUS_LOGIN=yes - # - ZOO_AUTOPURGE_INTERVAL=1 - - # zookeeper-3: - # image: bitnami/zookeeper:3.7.0 - # container_name: signoz-zookeeper-3 - # hostname: zookeeper-3 - # user: root - # ports: - # - "2183:2181" - # - "2890:2888" - # - "3890:3888" - # volumes: - # - ./data/zookeeper-3:/bitnami/zookeeper - # environment: - # - ZOO_SERVER_ID=3 - # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 - # - ALLOW_ANONYMOUS_LOGIN=yes - # - ZOO_AUTOPURGE_INTERVAL=1 - - clickhouse: - <<: *clickhouse-defaults - container_name: signoz-clickhouse - hostname: clickhouse - ports: - - "9000:9000" - - "8123:8123" - - "9181:9181" - volumes: - - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml - - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml - - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml - - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml - # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml - - ./data/clickhouse/:/var/lib/clickhouse/ - - ./user_scripts:/var/lib/clickhouse/user_scripts/ - - # clickhouse-2: - # <<: *clickhouse-defaults - # container_name: signoz-clickhouse-2 - # hostname: clickhouse-2 - # ports: - # - "9001:9000" - # - "8124:8123" - # - "9182:9181" - # volumes: - # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml - # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml - # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml - # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml - # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml - # - ./data/clickhouse-2/:/var/lib/clickhouse/ - # - ./user_scripts:/var/lib/clickhouse/user_scripts/ - - - # clickhouse-3: - # <<: *clickhouse-defaults - # container_name: signoz-clickhouse-3 - # hostname: clickhouse-3 - # ports: - # - "9002:9000" - # - "8125:8123" - # - "9183:9181" - # volumes: - # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml - # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml - # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml - # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml - # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml - # - ./data/clickhouse-3/:/var/lib/clickhouse/ - # - ./user_scripts:/var/lib/clickhouse/user_scripts/ - - alertmanager: - image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5} - container_name: signoz-alertmanager - volumes: - - ./data/alertmanager:/data - depends_on: - query-service: - condition: service_healthy - restart: on-failure - command: - - --queryService.url=http://query-service:8085 - - --storage.path=/data - - # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` - - query-service: - image: signoz/query-service:${DOCKER_TAG:-0.55.0} - container_name: signoz-query-service - command: - [ - "-config=/root/config/prometheus.yml", - "--use-logs-new-schema=true" - ] - # ports: - # - "6060:6060" # pprof port - # - "8080:8080" # query-service port - volumes: - - ./prometheus.yml:/root/config/prometheus.yml - - ../dashboards:/root/config/dashboards - - ./data/signoz/:/var/lib/signoz/ - environment: - - ClickHouseUrl=tcp://clickhouse:9000 - - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ - - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db - - DASHBOARDS_PATH=/root/config/dashboards - - STORAGE=clickhouse - - GODEBUG=netdns=go - - TELEMETRY_ENABLED=true - - DEPLOYMENT_TYPE=docker-standalone-amd - restart: on-failure - healthcheck: - test: - [ - "CMD", - "wget", - "--spider", - "-q", - "localhost:8080/api/v1/health" - ] - interval: 30s - timeout: 5s - retries: 3 - <<: *db-depend - - frontend: - image: signoz/frontend:${DOCKER_TAG:-0.55.0} - container_name: signoz-frontend - restart: on-failure - depends_on: - - alertmanager - - query-service - ports: - - "3301:3301" - volumes: - - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf - - otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.10} - container_name: otel-migrator - command: - - "--dsn=tcp://clickhouse:9000" - depends_on: - clickhouse: - condition: service_healthy - # clickhouse-2: - # condition: service_healthy - # clickhouse-3: - # condition: service_healthy - - - otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.10} - container_name: signoz-otel-collector - command: - [ - "--config=/etc/otel-collector-config.yaml", - "--manager-config=/etc/manager-config.yaml", - "--copy-path=/var/tmp/collector-config.yaml", - "--feature-gates=-pkg.translator.prometheus.NormalizeName" - ] - user: root # required for reading docker container logs - volumes: - - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml - - /var/lib/docker/containers:/var/lib/docker/containers:ro - - /:/hostfs:ro - environment: - - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux - - DOCKER_MULTI_NODE_CLUSTER=false - - LOW_CARDINAL_EXCEPTION_GROUPING=false - ports: - # - "1777:1777" # pprof extension - - "4317:4317" # OTLP gRPC receiver - - "4318:4318" # OTLP HTTP receiver - # - "8888:8888" # OtelCollector internal metrics - # - "8889:8889" # signoz spanmetrics exposed by the agent - # - "9411:9411" # Zipkin port - # - "13133:13133" # health check extension - # - "14250:14250" # Jaeger gRPC - # - "14268:14268" # Jaeger thrift HTTP - # - "55678:55678" # OpenCensus receiver - # - "55679:55679" # zPages extension - restart: on-failure - depends_on: - clickhouse: - condition: service_healthy - otel-collector-migrator: - condition: service_completed_successfully - query-service: - condition: service_healthy - - logspout: - image: "gliderlabs/logspout:v3.2.14" - container_name: signoz-logspout - volumes: - - /etc/hostname:/etc/host_hostname:ro - - /var/run/docker.sock:/var/run/docker.sock - command: syslog+tcp://otel-collector:2255 - depends_on: - - otel-collector - restart: on-failure - - hotrod: - image: jaegertracing/example-hotrod:1.30 - container_name: hotrod - logging: - options: - max-size: 50m - max-file: "3" - command: [ "all" ] - environment: - - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces - - load-hotrod: - image: "signoz/locust:1.2.3" - container_name: load-hotrod - hostname: load-hotrod - environment: - ATTACKED_HOST: http://hotrod:8080 - LOCUST_MODE: standalone - NO_PROXY: standalone - TASK_DELAY_FROM: 5 - TASK_DELAY_TO: 30 - QUIET_MODE: "${QUIET_MODE:-false}" - LOCUST_OPTS: "--headless -u 10 -r 1" - volumes: - - ../common/locust-scripts:/locust +include: + - test-app-docker-compose.yaml + - docker-compose-minimal.yaml diff --git a/deploy/docker/clickhouse-setup/test-app-docker-compose.yaml b/deploy/docker/clickhouse-setup/test-app-docker-compose.yaml new file mode 100644 index 0000000000..c043d75d74 --- /dev/null +++ b/deploy/docker/clickhouse-setup/test-app-docker-compose.yaml @@ -0,0 +1,26 @@ +services: + hotrod: + image: jaegertracing/example-hotrod:1.30 + container_name: hotrod + logging: + options: + max-size: 50m + max-file: "3" + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + + load-hotrod: + image: "signoz/locust:1.2.3" + container_name: load-hotrod + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/ee/query-service/app/api/queryrange.go b/ee/query-service/app/api/queryrange.go index d4f3eb975a..f5ad490ed5 100644 --- a/ee/query-service/app/api/queryrange.go +++ b/ee/query-service/app/api/queryrange.go @@ -106,12 +106,11 @@ func (aH *APIHandler) queryRangeV4(w http.ResponseWriter, r *http.Request) { RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil) return } - uniqueResults := make(map[string]*v3.Result) - for _, anomaly := range anomalies.Results { - uniqueResults[anomaly.QueryName] = anomaly - uniqueResults[anomaly.QueryName].IsAnomaly = true + resp := v3.QueryRangeResponse{ + Result: anomalies.Results, + ResultType: "anomaly", } - aH.Respond(w, uniqueResults) + aH.Respond(w, resp) } else { r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) aH.QueryRangeV4(w, r) diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index 54eb7bd1e5..7468be4698 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -757,7 +757,7 @@ func makeRulesManager( RepoURL: ruleRepoURL, DBConn: db, Context: context.Background(), - Logger: nil, + Logger: zap.L(), DisableRules: disableRules, FeatureFlags: fm, Reader: ch, diff --git a/ee/query-service/main.go b/ee/query-service/main.go index 75a49500d0..41cc69aa49 100644 --- a/ee/query-service/main.go +++ b/ee/query-service/main.go @@ -20,6 +20,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + prommodel "github.com/prometheus/common/model" + zapotlpencoder "github.com/SigNoz/zap_otlp/zap_otlp_encoder" zapotlpsync "github.com/SigNoz/zap_otlp/zap_otlp_sync" @@ -77,6 +79,10 @@ func initZapLog(enableQueryServiceLogOTLPExport bool) *zap.Logger { return logger } +func init() { + prommodel.NameValidationScheme = prommodel.UTF8Validation +} + func main() { var promConfigPath, skipTopLvlOpsPath string diff --git a/ee/query-service/rules/anomaly.go b/ee/query-service/rules/anomaly.go index a04bfc2840..08ff3afcda 100644 --- a/ee/query-service/rules/anomaly.go +++ b/ee/query-service/rules/anomaly.go @@ -250,7 +250,7 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (interface{}, erro } lb := labels.NewBuilder(smpl.Metric).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel) - resultLabels := labels.NewBuilder(smpl.MetricOrig).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel).Labels() + resultLabels := labels.NewBuilder(smpl.Metric).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel).Labels() for name, value := range r.Labels().Map() { lb.Set(name, expand(value)) @@ -262,7 +262,7 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (interface{}, erro annotations := make(labels.Labels, 0, len(r.Annotations().Map())) for name, value := range r.Annotations().Map() { - annotations = append(annotations, labels.Label{Name: common.NormalizeLabelName(name), Value: expand(value)}) + annotations = append(annotations, labels.Label{Name: name, Value: expand(value)}) } if smpl.IsMissing { lb.Set(labels.AlertNameLabel, "[No data] "+r.Name()) diff --git a/ee/query-service/rules/manager.go b/ee/query-service/rules/manager.go index 5ed35d4d34..e44bbcf82b 100644 --- a/ee/query-service/rules/manager.go +++ b/ee/query-service/rules/manager.go @@ -73,7 +73,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) task = newTask(baserules.TaskTypeCh, opts.TaskName, time.Duration(opts.Rule.Frequency), rules, opts.ManagerOpts, opts.NotifyFunc, opts.RuleDB) } else { - return nil, fmt.Errorf("unsupported rule type. Supported types: %s, %s", baserules.RuleTypeProm, baserules.RuleTypeThreshold) + return nil, fmt.Errorf("unsupported rule type %s. Supported types: %s, %s", opts.Rule.RuleType, baserules.RuleTypeProm, baserules.RuleTypeThreshold) } return task, nil diff --git a/frontend/public/Logos/signoz-brand-logo.svg b/frontend/public/Logos/signoz-brand-logo.svg index aaa8a77669..a9cce28993 100644 --- a/frontend/public/Logos/signoz-brand-logo.svg +++ b/frontend/public/Logos/signoz-brand-logo.svg @@ -1,11 +1,7 @@ - - - - - - - - - - - + + + + + + + diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico index 98a6d5f7dc..b83e8b5610 100644 Binary files a/frontend/public/favicon.ico and b/frontend/public/favicon.ico differ diff --git a/frontend/public/locales/en-GB/alerts.json b/frontend/public/locales/en-GB/alerts.json index 86f21c8c78..5c5c3b851e 100644 --- a/frontend/public/locales/en-GB/alerts.json +++ b/frontend/public/locales/en-GB/alerts.json @@ -118,6 +118,8 @@ "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", "field_unit": "Threshold unit", "text_alert_on_absent": "Send a notification if data is missing for", + "text_require_min_points": "Run alert evaluation only when there are minimum of", + "text_num_points": "data points in each result group", "text_alert_frequency": "Run alert every", "text_for": "minutes", "selected_query_placeholder": "Select query" diff --git a/frontend/public/locales/en/alerts.json b/frontend/public/locales/en/alerts.json index 02d20a2977..3ad8390731 100644 --- a/frontend/public/locales/en/alerts.json +++ b/frontend/public/locales/en/alerts.json @@ -118,6 +118,8 @@ "exceptions_based_alert_desc": "Send a notification when a condition occurs in the exceptions data.", "field_unit": "Threshold unit", "text_alert_on_absent": "Send a notification if data is missing for", + "text_require_min_points": "Run alert evaluation only when there are minimum of", + "text_num_points": "data points in each result group", "text_alert_frequency": "Run alert every", "text_for": "minutes", "selected_query_placeholder": "Select query" diff --git a/frontend/public/signoz-signup.svg b/frontend/public/signoz-signup.svg deleted file mode 100644 index 67c45b1c23..0000000000 --- a/frontend/public/signoz-signup.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx index 8400afbde3..8cb2bf0a8b 100644 --- a/frontend/src/AppRoutes/index.tsx +++ b/frontend/src/AppRoutes/index.tsx @@ -12,6 +12,7 @@ import useAnalytics from 'hooks/analytics/useAnalytics'; import { KeyboardHotkeysProvider } from 'hooks/hotkeys/useKeyboardHotkeys'; import { useIsDarkMode, useThemeConfig } from 'hooks/useDarkMode'; import { THEME_MODE } from 'hooks/useDarkMode/constant'; +import useFeatureFlags from 'hooks/useFeatureFlag'; import useGetFeatureFlag from 'hooks/useGetFeatureFlag'; import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense'; import { NotificationProvider } from 'hooks/useNotifications'; @@ -58,23 +59,13 @@ function App(): JSX.Element { const isDarkMode = useIsDarkMode(); + const isChatSupportEnabled = + useFeatureFlags(FeatureKeys.CHAT_SUPPORT)?.active || false; + + const isPremiumSupportEnabled = + useFeatureFlags(FeatureKeys.PREMIUM_SUPPORT)?.active || false; + const featureResponse = useGetFeatureFlag((allFlags) => { - const isOnboardingEnabled = - allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active || - false; - - const isChatSupportEnabled = - allFlags.find((flag) => flag.name === FeatureKeys.CHAT_SUPPORT)?.active || - false; - - const isPremiumSupportEnabled = - allFlags.find((flag) => flag.name === FeatureKeys.PREMIUM_SUPPORT)?.active || - false; - - const showAddCreditCardModal = - !isPremiumSupportEnabled && - !licenseData?.payload?.trialConvertedToSubscription; - dispatch({ type: UPDATE_FEATURE_FLAG_RESPONSE, payload: { @@ -83,6 +74,10 @@ function App(): JSX.Element { }, }); + const isOnboardingEnabled = + allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active || + false; + if (!isOnboardingEnabled || !isCloudUserVal) { const newRoutes = routes.filter( (route) => route?.path !== ROUTES.GET_STARTED, @@ -90,16 +85,6 @@ function App(): JSX.Element { setRoutes(newRoutes); } - - if (isLoggedInState && isChatSupportEnabled && !showAddCreditCardModal) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - window.Intercom('boot', { - app_id: process.env.INTERCOM_APP_ID, - email: user?.email || '', - name: user?.name || '', - }); - } }); const isOnBasicPlan = @@ -201,6 +186,26 @@ function App(): JSX.Element { // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathname]); + useEffect(() => { + const showAddCreditCardModal = + !isPremiumSupportEnabled && + !licenseData?.payload?.trialConvertedToSubscription; + + if (isLoggedInState && isChatSupportEnabled && !showAddCreditCardModal) { + window.Intercom('boot', { + app_id: process.env.INTERCOM_APP_ID, + email: user?.email || '', + name: user?.name || '', + }); + } + }, [ + isLoggedInState, + isChatSupportEnabled, + user, + licenseData, + isPremiumSupportEnabled, + ]); + useEffect(() => { if (user && user?.email && user?.userId && user?.name) { try { @@ -227,6 +232,10 @@ function App(): JSX.Element { // eslint-disable-next-line react-hooks/exhaustive-deps }, [user]); + useEffect(() => { + console.info('We are hiring! https://jobs.gem.com/signoz'); + }, []); + return ( diff --git a/frontend/src/components/ClientSideQBSearch/ClientSideQBSearch.styles.scss b/frontend/src/components/ClientSideQBSearch/ClientSideQBSearch.styles.scss new file mode 100644 index 0000000000..bb1fa9678c --- /dev/null +++ b/frontend/src/components/ClientSideQBSearch/ClientSideQBSearch.styles.scss @@ -0,0 +1,5 @@ +.client-side-qb-search { + .ant-select-selection-search { + width: max-content !important; + } +} diff --git a/frontend/src/components/ClientSideQBSearch/ClientSideQBSearch.tsx b/frontend/src/components/ClientSideQBSearch/ClientSideQBSearch.tsx new file mode 100644 index 0000000000..1a6d501ff6 --- /dev/null +++ b/frontend/src/components/ClientSideQBSearch/ClientSideQBSearch.tsx @@ -0,0 +1,654 @@ +/* eslint-disable sonarjs/cognitive-complexity */ + +import './ClientSideQBSearch.styles.scss'; + +import { Color } from '@signozhq/design-tokens'; +import { Select, Tag, Tooltip } from 'antd'; +import { + OPERATORS, + QUERY_BUILDER_OPERATORS_BY_TYPES, + QUERY_BUILDER_SEARCH_VALUES, +} from 'constants/queryBuilder'; +import { CustomTagProps } from 'container/QueryBuilder/filters/QueryBuilderSearch'; +import { selectStyle } from 'container/QueryBuilder/filters/QueryBuilderSearch/config'; +import { PLACEHOLDER } from 'container/QueryBuilder/filters/QueryBuilderSearch/constant'; +import { TypographyText } from 'container/QueryBuilder/filters/QueryBuilderSearch/style'; +import { + checkCommaInValue, + getOperatorFromValue, + getOperatorValue, + getTagToken, + isInNInOperator, +} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils'; +import { + DropdownState, + ITag, + Option, +} from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2'; +import Suggestions from 'container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions'; +import { WhereClauseConfig } from 'hooks/queryBuilder/useAutoComplete'; +import { validationMapper } from 'hooks/queryBuilder/useIsValidTag'; +import { operatorTypeMapper } from 'hooks/queryBuilder/useOperatorType'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { isArray, isEmpty, isEqual, isObject } from 'lodash-es'; +import { ChevronDown, ChevronUp } from 'lucide-react'; +import type { BaseSelectRef } from 'rc-select'; +import { + KeyboardEvent, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { + BaseAutocompleteData, + DataTypes, +} from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { + IBuilderQuery, + TagFilter, +} from 'types/api/queryBuilder/queryBuilderData'; +import { popupContainer } from 'utils/selectPopupContainer'; +import { v4 as uuid } from 'uuid'; + +export interface AttributeKey { + key: string; +} + +export interface AttributeValuesMap { + [key: string]: AttributeValue; +} + +interface ClientSideQBSearchProps { + filters: TagFilter; + onChange: (value: TagFilter) => void; + whereClauseConfig?: WhereClauseConfig; + placeholder?: string; + className?: string; + suffixIcon?: React.ReactNode; + attributeValuesMap?: AttributeValuesMap; + attributeKeys: AttributeKey[]; +} + +interface AttributeValue { + stringAttributeValues: string[] | []; + numberAttributeValues: number[] | []; + boolAttributeValues: boolean[] | []; +} + +function ClientSideQBSearch( + props: ClientSideQBSearchProps, +): React.ReactElement { + const { + onChange, + placeholder, + className, + suffixIcon, + whereClauseConfig, + attributeValuesMap, + attributeKeys, + filters, + } = props; + + const isDarkMode = useIsDarkMode(); + + const selectRef = useRef(null); + + const [isOpen, setIsOpen] = useState(false); + + // create the tags from the initial query here, this should only be computed on the first load as post that tags and query will be always in sync. + const [tags, setTags] = useState(filters.items as ITag[]); + + // this will maintain the current state of in process filter item + const [currentFilterItem, setCurrentFilterItem] = useState(); + + const [currentState, setCurrentState] = useState( + DropdownState.ATTRIBUTE_KEY, + ); + + // to maintain the current running state until the tokenization happens for the tag + const [searchValue, setSearchValue] = useState(''); + + const [dropdownOptions, setDropdownOptions] = useState([]); + + const attributeValues = useMemo(() => { + if (currentFilterItem?.key?.key) { + return attributeValuesMap?.[currentFilterItem.key.key]; + } + return { + stringAttributeValues: [], + numberAttributeValues: [], + boolAttributeValues: [], + }; + }, [attributeValuesMap, currentFilterItem?.key?.key]); + + const handleDropdownSelect = useCallback( + (value: string) => { + let parsedValue: BaseAutocompleteData | string; + + try { + parsedValue = JSON.parse(value); + } catch { + parsedValue = value; + } + if (currentState === DropdownState.ATTRIBUTE_KEY) { + setCurrentFilterItem((prev) => ({ + ...prev, + key: parsedValue as BaseAutocompleteData, + op: '', + value: '', + })); + setCurrentState(DropdownState.OPERATOR); + setSearchValue((parsedValue as BaseAutocompleteData)?.key); + } else if (currentState === DropdownState.OPERATOR) { + if (value === OPERATORS.EXISTS || value === OPERATORS.NOT_EXISTS) { + setTags((prev) => [ + ...prev, + { + key: currentFilterItem?.key, + op: value, + value: '', + } as ITag, + ]); + setCurrentFilterItem(undefined); + setSearchValue(''); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + } else { + setCurrentFilterItem((prev) => ({ + key: prev?.key as BaseAutocompleteData, + op: value as string, + value: '', + })); + setCurrentState(DropdownState.ATTRIBUTE_VALUE); + setSearchValue(`${currentFilterItem?.key?.key} ${value}`); + } + } else if (currentState === DropdownState.ATTRIBUTE_VALUE) { + const operatorType = + operatorTypeMapper[currentFilterItem?.op || ''] || 'NOT_VALID'; + const isMulti = operatorType === QUERY_BUILDER_SEARCH_VALUES.MULTIPLY; + + if (isMulti) { + const { tagKey, tagOperator, tagValue } = getTagToken(searchValue); + // this condition takes care of adding the IN/NIN multi values when pressed enter on an already existing value. + // not the best interaction but in sync with what we have today! + if (tagValue.includes(String(value))) { + setSearchValue(''); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + setCurrentFilterItem(undefined); + setTags((prev) => [ + ...prev, + { + key: currentFilterItem?.key, + op: currentFilterItem?.op, + value: tagValue, + } as ITag, + ]); + return; + } + // this is for adding subsequent comma seperated values + const newSearch = [...tagValue]; + newSearch[newSearch.length === 0 ? 0 : newSearch.length - 1] = value; + const newSearchValue = newSearch.join(','); + setSearchValue(`${tagKey} ${tagOperator} ${newSearchValue},`); + } else { + setSearchValue(''); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + setCurrentFilterItem(undefined); + setTags((prev) => [ + ...prev, + { + key: currentFilterItem?.key, + op: currentFilterItem?.op, + value, + } as ITag, + ]); + } + } + }, + [currentFilterItem?.key, currentFilterItem?.op, currentState, searchValue], + ); + + const handleSearch = useCallback((value: string) => { + setSearchValue(value); + }, []); + + const onInputKeyDownHandler = useCallback( + (event: KeyboardEvent): void => { + if (event.key === 'Backspace' && !searchValue) { + event.stopPropagation(); + setTags((prev) => prev.slice(0, -1)); + } + }, + [searchValue], + ); + + const handleOnBlur = useCallback((): void => { + if (searchValue) { + const operatorType = + operatorTypeMapper[currentFilterItem?.op || ''] || 'NOT_VALID'; + // if key is added and operator is not present then convert to body CONTAINS key + if ( + currentFilterItem?.key && + isEmpty(currentFilterItem?.op) && + whereClauseConfig?.customKey === 'body' && + whereClauseConfig?.customOp === OPERATORS.CONTAINS + ) { + setTags((prev) => [ + ...prev, + { + key: { + key: 'body', + dataType: DataTypes.String, + type: '', + isColumn: true, + isJSON: false, + id: 'body--string----true', + }, + op: OPERATORS.CONTAINS, + value: currentFilterItem?.key?.key, + }, + ]); + setCurrentFilterItem(undefined); + setSearchValue(''); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + } else if ( + currentFilterItem?.op === OPERATORS.EXISTS || + currentFilterItem?.op === OPERATORS.NOT_EXISTS + ) { + // is exists and not exists operator is present then convert directly to tag! no need of value here + setTags((prev) => [ + ...prev, + { + key: currentFilterItem?.key, + op: currentFilterItem?.op, + value: '', + }, + ]); + setCurrentFilterItem(undefined); + setSearchValue(''); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + } else if ( + // if the current state is in sync with the kind of operator used then convert into a tag + validationMapper[operatorType]?.( + isArray(currentFilterItem?.value) + ? currentFilterItem?.value.length || 0 + : 1, + ) + ) { + setTags((prev) => [ + ...prev, + { + key: currentFilterItem?.key as BaseAutocompleteData, + op: currentFilterItem?.op as string, + value: currentFilterItem?.value || '', + }, + ]); + setCurrentFilterItem(undefined); + setSearchValue(''); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + } + } + }, [ + currentFilterItem?.key, + currentFilterItem?.op, + currentFilterItem?.value, + searchValue, + whereClauseConfig?.customKey, + whereClauseConfig?.customOp, + ]); + + // this useEffect takes care of tokenisation based on the search state + useEffect(() => { + // if there is no search value reset to the default state + if (!searchValue) { + setCurrentFilterItem(undefined); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + } + + // split the current search value based on delimiters + const { tagKey, tagOperator, tagValue } = getTagToken(searchValue); + + if ( + // Case 1 - if key is defined but the search text doesn't match with the set key, + // can happen when user selects from dropdown and then deletes a few characters + currentFilterItem?.key && + currentFilterItem?.key?.key !== tagKey.split(' ')[0] + ) { + setCurrentFilterItem(undefined); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + } else if (tagOperator && isEmpty(currentFilterItem?.op)) { + // Case 2 -> key is set and now typing for the operator + if ( + tagOperator === OPERATORS.EXISTS || + tagOperator === OPERATORS.NOT_EXISTS + ) { + setTags((prev) => [ + ...prev, + { + key: currentFilterItem?.key, + op: tagOperator, + value: '', + } as ITag, + ]); + setCurrentFilterItem(undefined); + setSearchValue(''); + setCurrentState(DropdownState.ATTRIBUTE_KEY); + } else { + setCurrentFilterItem((prev) => ({ + key: prev?.key as BaseAutocompleteData, + op: tagOperator, + value: '', + })); + + setCurrentState(DropdownState.ATTRIBUTE_VALUE); + } + } else if ( + // Case 3 -> selected operator from dropdown and then erased a part of it + !isEmpty(currentFilterItem?.op) && + tagOperator !== currentFilterItem?.op + ) { + setCurrentFilterItem((prev) => ({ + key: prev?.key as BaseAutocompleteData, + op: '', + value: '', + })); + setCurrentState(DropdownState.OPERATOR); + } else if (currentState === DropdownState.ATTRIBUTE_VALUE) { + // Case 4 -> the final value state where we set the current filter values and the tokenisation happens on either + // dropdown click or blur event + const currentValue = { + key: currentFilterItem?.key as BaseAutocompleteData, + op: currentFilterItem?.op as string, + value: tagValue, + }; + if (!isEqual(currentValue, currentFilterItem)) { + setCurrentFilterItem((prev) => ({ + key: prev?.key as BaseAutocompleteData, + op: prev?.op as string, + value: tagValue, + })); + } + } + }, [ + currentFilterItem, + currentFilterItem?.key, + currentFilterItem?.op, + searchValue, + currentState, + ]); + + // the useEffect takes care of setting the dropdown values correctly on change of the current state + useEffect(() => { + if (currentState === DropdownState.ATTRIBUTE_KEY) { + const filteredAttributeKeys = attributeKeys.filter((key) => + key.key.startsWith(searchValue), + ); + setDropdownOptions( + filteredAttributeKeys?.map( + (key) => + ({ + label: key.key, + value: key, + } as Option), + ) || [], + ); + } + if (currentState === DropdownState.OPERATOR) { + const keyOperator = searchValue.split(' '); + const partialOperator = keyOperator?.[1]; + const strippedKey = keyOperator?.[0]; + + let operatorOptions; + if (currentFilterItem?.key?.dataType) { + operatorOptions = QUERY_BUILDER_OPERATORS_BY_TYPES[ + currentFilterItem.key + .dataType as keyof typeof QUERY_BUILDER_OPERATORS_BY_TYPES + ].map((operator) => ({ + label: operator, + value: operator, + })); + + if (partialOperator) { + operatorOptions = operatorOptions.filter((op) => + op.label.startsWith(partialOperator.toLocaleUpperCase()), + ); + } + setDropdownOptions(operatorOptions); + } else if (strippedKey.endsWith('[*]') && strippedKey.startsWith('body.')) { + operatorOptions = [OPERATORS.HAS, OPERATORS.NHAS].map((operator) => ({ + label: operator, + value: operator, + })); + setDropdownOptions(operatorOptions); + } else { + operatorOptions = QUERY_BUILDER_OPERATORS_BY_TYPES.universal.map( + (operator) => ({ + label: operator, + value: operator, + }), + ); + + if (partialOperator) { + operatorOptions = operatorOptions.filter((op) => + op.label.startsWith(partialOperator.toLocaleUpperCase()), + ); + } + setDropdownOptions(operatorOptions); + } + } + + if (currentState === DropdownState.ATTRIBUTE_VALUE) { + const values: Array = []; + const { tagValue } = getTagToken(searchValue); + if (isArray(tagValue)) { + if (!isEmpty(tagValue[tagValue.length - 1])) + values.push(tagValue[tagValue.length - 1]); + } else if (!isEmpty(tagValue)) values.push(tagValue); + + const currentAttributeValues = + attributeValues?.stringAttributeValues || + attributeValues?.numberAttributeValues || + attributeValues?.boolAttributeValues || + []; + + values.push(...currentAttributeValues); + + if (attributeValuesMap) { + setDropdownOptions( + values.map( + (val) => + ({ + label: checkCommaInValue(String(val)), + value: val, + } as Option), + ), + ); + } else { + // If attributeValuesMap is not provided, don't set dropdown options + setDropdownOptions([]); + } + } + }, [ + attributeValues, + currentFilterItem?.key?.dataType, + currentState, + attributeKeys, + searchValue, + attributeValuesMap, + ]); + + useEffect(() => { + const filterTags: IBuilderQuery['filters'] = { + op: 'AND', + items: [], + }; + tags.forEach((tag) => { + const computedTagValue = + tag.value && + Array.isArray(tag.value) && + tag.value[tag.value.length - 1] === '' + ? tag.value?.slice(0, -1) + : tag.value ?? ''; + filterTags.items.push({ + id: tag.id || uuid().slice(0, 8), + key: tag.key, + op: getOperatorValue(tag.op), + value: computedTagValue, + }); + }); + + if (!isEqual(filters, filterTags)) { + onChange(filterTags); + setTags( + filterTags.items.map((tag) => ({ + ...tag, + op: getOperatorFromValue(tag.op), + })) as ITag[], + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [tags]); + + const queryTags = useMemo( + () => tags.map((tag) => `${tag.key.key} ${tag.op} ${tag.value}`), + [tags], + ); + + const onTagRender = ({ + value, + closable, + onClose, + }: CustomTagProps): React.ReactElement => { + const { tagOperator } = getTagToken(value); + const isInNin = isInNInOperator(tagOperator); + const chipValue = isInNin + ? value?.trim()?.replace(/,\s*$/, '') + : value?.trim(); + + const indexInQueryTags = queryTags.findIndex((qTag) => isEqual(qTag, value)); + const tagDetails = tags[indexInQueryTags]; + + const onCloseHandler = (): void => { + onClose(); + setSearchValue(''); + setTags((prev) => prev.filter((t) => !isEqual(t, tagDetails))); + }; + + const tagEditHandler = (value: string): void => { + setCurrentFilterItem(tagDetails); + setSearchValue(value); + setCurrentState(DropdownState.ATTRIBUTE_VALUE); + setTags((prev) => prev.filter((t) => !isEqual(t, tagDetails))); + }; + + const isDisabled = !!searchValue; + + return ( + + + + { + if (!isDisabled) tagEditHandler(value); + }} + > + {chipValue} + + + + + ); + }; + + const suffixIconContent = useMemo(() => { + if (suffixIcon) { + return suffixIcon; + } + return isOpen ? ( + + ) : ( + + ); + }, [isDarkMode, isOpen, suffixIcon]); + + return ( +
+ +
+ ); +} + +ClientSideQBSearch.defaultProps = { + placeholder: PLACEHOLDER, + className: '', + suffixIcon: null, + whereClauseConfig: {}, + attributeValuesMap: {}, +}; + +export default ClientSideQBSearch; diff --git a/frontend/src/components/ExplorerCard/types.ts b/frontend/src/components/ExplorerCard/types.ts index 9f4eed3f32..94e7ab788f 100644 --- a/frontend/src/components/ExplorerCard/types.ts +++ b/frontend/src/components/ExplorerCard/types.ts @@ -22,7 +22,13 @@ export type GetViewDetailsUsingViewKey = ( viewKey: string, data: ViewProps[] | undefined, ) => - | { query: Query; name: string; uuid: string; panelType: PANEL_TYPES } + | { + query: Query; + name: string; + uuid: string; + panelType: PANEL_TYPES; + extraData?: string; + } | undefined; export interface IsQueryUpdatedInViewProps { diff --git a/frontend/src/components/ExplorerCard/utils.ts b/frontend/src/components/ExplorerCard/utils.ts index 48af06bb3d..0c1ef66469 100644 --- a/frontend/src/components/ExplorerCard/utils.ts +++ b/frontend/src/components/ExplorerCard/utils.ts @@ -29,9 +29,9 @@ export const getViewDetailsUsingViewKey: GetViewDetailsUsingViewKey = ( ) => { const selectedView = data?.find((view) => view.uuid === viewKey); if (selectedView) { - const { compositeQuery, name, uuid } = selectedView; + const { compositeQuery, name, uuid, extraData } = selectedView; const query = mapQueryDataFromApi(compositeQuery); - return { query, name, uuid, panelType: compositeQuery.panelType }; + return { query, name, uuid, panelType: compositeQuery.panelType, extraData }; } return undefined; }; diff --git a/frontend/src/components/WelcomeLeftContainer/index.tsx b/frontend/src/components/WelcomeLeftContainer/index.tsx index 3e8ec68e42..ea238723ae 100644 --- a/frontend/src/components/WelcomeLeftContainer/index.tsx +++ b/frontend/src/components/WelcomeLeftContainer/index.tsx @@ -16,7 +16,7 @@ function WelcomeLeftContainer({ - + SigNoz {t('monitor_signup')} diff --git a/frontend/src/constants/app.ts b/frontend/src/constants/app.ts index d260806856..c82e2a34e8 100644 --- a/frontend/src/constants/app.ts +++ b/frontend/src/constants/app.ts @@ -6,7 +6,6 @@ export const AUTH0_REDIRECT_PATH = '/redirect'; export const DEFAULT_AUTH0_APP_REDIRECTION_PATH = ROUTES.APPLICATION; -export const IS_SIDEBAR_COLLAPSED = 'isSideBarCollapsed'; export const INVITE_MEMBERS_HASH = '#invite-team-members'; export const SIGNOZ_UPGRADE_PLAN_URL = diff --git a/frontend/src/constants/shortcuts/globalShortcuts.ts b/frontend/src/constants/shortcuts/globalShortcuts.ts index 4ab7752fac..8b68b7195e 100644 --- a/frontend/src/constants/shortcuts/globalShortcuts.ts +++ b/frontend/src/constants/shortcuts/globalShortcuts.ts @@ -1,8 +1,4 @@ -import { getUserOperatingSystem, UserOperatingSystem } from 'utils/getUserOS'; - -const userOS = getUserOperatingSystem(); export const GlobalShortcuts = { - SidebarCollapse: '\\+meta', NavigateToServices: 's+shift', NavigateToTraces: 't+shift', NavigateToLogs: 'l+shift', @@ -13,7 +9,6 @@ export const GlobalShortcuts = { }; export const GlobalShortcutsName = { - SidebarCollapse: `${userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'}+\\`, NavigateToServices: 'shift+s', NavigateToTraces: 'shift+t', NavigateToLogs: 'shift+l', @@ -24,7 +19,6 @@ export const GlobalShortcutsName = { }; export const GlobalShortcutsDescription = { - SidebarCollapse: 'Collpase the sidebar', NavigateToServices: 'Navigate to Services page', NavigateToTraces: 'Navigate to Traces page', NavigateToLogs: 'Navigate to logs page', diff --git a/frontend/src/container/AlertHistory/Timeline/Graph/Graph.tsx b/frontend/src/container/AlertHistory/Timeline/Graph/Graph.tsx index 5adf1c481a..b3eecda24c 100644 --- a/frontend/src/container/AlertHistory/Timeline/Graph/Graph.tsx +++ b/frontend/src/container/AlertHistory/Timeline/Graph/Graph.tsx @@ -1,10 +1,15 @@ import { Color } from '@signozhq/design-tokens'; import Uplot from 'components/Uplot'; +import { QueryParams } from 'constants/query'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; +import useUrlQuery from 'hooks/useUrlQuery'; +import history from 'lib/history'; import heatmapPlugin from 'lib/uPlotLib/plugins/heatmapPlugin'; import timelinePlugin from 'lib/uPlotLib/plugins/timelinePlugin'; import { useMemo, useRef } from 'react'; +import { useDispatch } from 'react-redux'; +import { UpdateTimeInterval } from 'store/actions'; import { AlertRuleTimelineGraphResponse } from 'types/api/alerts/def'; import uPlot, { AlignedData } from 'uplot'; @@ -41,11 +46,13 @@ function HorizontalTimelineGraph({ return [timestamps, states]; }, [data]); + const urlQuery = useUrlQuery(); + const dispatch = useDispatch(); + const options: uPlot.Options = useMemo( () => ({ width, height: 85, - cursor: { show: false }, axes: [ { @@ -66,6 +73,40 @@ function HorizontalTimelineGraph({ label: 'States', }, ], + hooks: { + setSelect: [ + (self): void => { + const selection = self.select; + if (selection) { + const startTime = self.posToVal(selection.left, 'x'); + const endTime = self.posToVal(selection.left + selection.width, 'x'); + + const diff = endTime - startTime; + + if (diff > 0) { + if (urlQuery.has(QueryParams.relativeTime)) { + urlQuery.delete(QueryParams.relativeTime); + } + + const startTimestamp = Math.floor(startTime * 1000); + const endTimestamp = Math.floor(endTime * 1000); + + if (startTimestamp !== endTimestamp) { + dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); + } + + urlQuery.set(QueryParams.startTime, startTimestamp.toString()); + urlQuery.set(QueryParams.endTime, endTimestamp.toString()); + + history.push({ + search: urlQuery.toString(), + }); + } + } + }, + ], + }, + plugins: transformedData?.length > 1 ? [ @@ -76,7 +117,7 @@ function HorizontalTimelineGraph({ ] : [], }), - [width, isDarkMode, transformedData], + [width, isDarkMode, transformedData.length, urlQuery, dispatch], ); return ; } diff --git a/frontend/src/container/AlertHistory/Timeline/Table/Table.styles.scss b/frontend/src/container/AlertHistory/Timeline/Table/Table.styles.scss index 26e2266ef6..5a143dd1b8 100644 --- a/frontend/src/container/AlertHistory/Timeline/Table/Table.styles.scss +++ b/frontend/src/container/AlertHistory/Timeline/Table/Table.styles.scss @@ -109,8 +109,8 @@ } .alert-rule { - &-value, - &-created-at { + &__value, + &__created-at { color: var(--text-ink-400); } } diff --git a/frontend/src/container/AlertHistory/Timeline/Table/Table.tsx b/frontend/src/container/AlertHistory/Timeline/Table/Table.tsx index f3144b88e6..be5c7ae9c3 100644 --- a/frontend/src/container/AlertHistory/Timeline/Table/Table.tsx +++ b/frontend/src/container/AlertHistory/Timeline/Table/Table.tsx @@ -1,16 +1,20 @@ import './Table.styles.scss'; import { Table } from 'antd'; +import { initialFilters } from 'constants/queryBuilder'; import { useGetAlertRuleDetailsTimelineTable, useTimelineTable, } from 'pages/AlertDetails/hooks'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; import { timelineTableColumns } from './useTimelineTable'; function TimelineTable(): JSX.Element { + const [filters, setFilters] = useState(initialFilters); + const { isLoading, isRefetching, @@ -18,13 +22,14 @@ function TimelineTable(): JSX.Element { data, isValidRuleId, ruleId, - } = useGetAlertRuleDetailsTimelineTable(); + } = useGetAlertRuleDetailsTimelineTable({ filters }); - const { timelineData, totalItems } = useMemo(() => { + const { timelineData, totalItems, labels } = useMemo(() => { const response = data?.payload?.data; return { timelineData: response?.items, totalItems: response?.total, + labels: response?.labels, }; }, [data?.payload?.data]); @@ -42,7 +47,11 @@ function TimelineTable(): JSX.Element {
`${row.fingerprint}-${row.value}-${row.unixMilli}`} - columns={timelineTableColumns()} + columns={timelineTableColumns({ + filters, + labels: labels ?? {}, + setFilters, + })} dataSource={timelineData} pagination={paginationConfig} size="middle" diff --git a/frontend/src/container/AlertHistory/Timeline/Table/useTimelineTable.tsx b/frontend/src/container/AlertHistory/Timeline/Table/useTimelineTable.tsx index 1eb43fc417..5c67caa984 100644 --- a/frontend/src/container/AlertHistory/Timeline/Table/useTimelineTable.tsx +++ b/frontend/src/container/AlertHistory/Timeline/Table/useTimelineTable.tsx @@ -1,13 +1,84 @@ import { EllipsisOutlined } from '@ant-design/icons'; +import { Color } from '@signozhq/design-tokens'; import { Button } from 'antd'; import { ColumnsType } from 'antd/es/table'; +import ClientSideQBSearch, { + AttributeKey, +} from 'components/ClientSideQBSearch/ClientSideQBSearch'; import { ConditionalAlertPopover } from 'container/AlertHistory/AlertPopover/AlertPopover'; -import AlertLabels from 'pages/AlertDetails/AlertHeader/AlertLabels/AlertLabels'; +import { transformKeyValuesToAttributeValuesMap } from 'container/QueryBuilder/filters/utils'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { Search } from 'lucide-react'; +import AlertLabels, { + AlertLabelsProps, +} from 'pages/AlertDetails/AlertHeader/AlertLabels/AlertLabels'; import AlertState from 'pages/AlertDetails/AlertHeader/AlertState/AlertState'; +import { useMemo } from 'react'; import { AlertRuleTimelineTableResponse } from 'types/api/alerts/def'; +import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; import { formatEpochTimestamp } from 'utils/timeUtils'; -export const timelineTableColumns = (): ColumnsType => [ +const transformLabelsToQbKeys = ( + labels: AlertRuleTimelineTableResponse['labels'], +): AttributeKey[] => Object.keys(labels).flatMap((key) => [{ key }]); + +function LabelFilter({ + filters, + setFilters, + labels, +}: { + setFilters: (filters: TagFilter) => void; + filters: TagFilter; + labels: AlertLabelsProps['labels']; +}): JSX.Element | null { + const isDarkMode = useIsDarkMode(); + + const { transformedKeys, attributesMap } = useMemo( + () => ({ + transformedKeys: transformLabelsToQbKeys(labels || {}), + attributesMap: transformKeyValuesToAttributeValuesMap(labels), + }), + [labels], + ); + + const handleSearch = (tagFilters: TagFilter): void => { + const tagFiltersLength = tagFilters.items.length; + + if ( + (!tagFiltersLength && (!filters || !filters.items.length)) || + tagFiltersLength === filters?.items.length + ) { + return; + } + setFilters(tagFilters); + }; + + return ( + + } + /> + ); +} + +export const timelineTableColumns = ({ + filters, + labels, + setFilters, +}: { + filters: TagFilter; + labels: AlertLabelsProps['labels']; + setFilters: (filters: TagFilter) => void; +}): ColumnsType => [ { title: 'STATE', dataIndex: 'state', @@ -20,7 +91,9 @@ export const timelineTableColumns = (): ColumnsType + ), dataIndex: 'labels', render: (labels): JSX.Element => (
diff --git a/frontend/src/container/AppLayout/AppLayout.styles.scss b/frontend/src/container/AppLayout/AppLayout.styles.scss index 2ae1531c79..a991f08351 100644 --- a/frontend/src/container/AppLayout/AppLayout.styles.scss +++ b/frontend/src/container/AppLayout/AppLayout.styles.scss @@ -16,12 +16,6 @@ width: 100%; } } - - &.docked { - .app-content { - width: calc(100% - 240px); - } - } } .chat-support-gateway { diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 4cf2e0f5bb..9193decd6b 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -5,13 +5,11 @@ import './AppLayout.styles.scss'; import * as Sentry from '@sentry/react'; import { Flex } from 'antd'; -import getLocalStorageKey from 'api/browser/localstorage/get'; import getUserLatestVersion from 'api/user/getLatestVersion'; import getUserVersion from 'api/user/getVersion'; import cx from 'classnames'; import ChatSupportGateway from 'components/ChatSupportGateway/ChatSupportGateway'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; -import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; import { FeatureKeys } from 'constants/features'; import ROUTES from 'constants/routes'; import SideNav from 'container/SideNav'; @@ -22,22 +20,13 @@ import useLicense from 'hooks/useLicense'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; -import { - ReactNode, - useCallback, - useEffect, - useLayoutEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { ReactNode, useEffect, useMemo, useRef, useState } from 'react'; import { Helmet } from 'react-helmet-async'; import { useTranslation } from 'react-i18next'; import { useQueries } from 'react-query'; import { useDispatch, useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { Dispatch } from 'redux'; -import { sideBarCollapse } from 'store/actions'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; import { @@ -59,10 +48,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element { (state) => state.app, ); - const [collapsed, setCollapsed] = useState( - getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true', - ); - const { notifications } = useNotifications(); const isDarkMode = useIsDarkMode(); @@ -117,14 +102,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element { const latestCurrentCounter = useRef(0); const latestVersionCounter = useRef(0); - const onCollapse = useCallback(() => { - setCollapsed((collapsed) => !collapsed); - }, []); - - useLayoutEffect(() => { - dispatch(sideBarCollapse(collapsed)); - }, [collapsed, dispatch]); - useEffect(() => { if ( getUserLatestVersionResponse.isFetched && @@ -255,19 +232,16 @@ function AppLayout(props: AppLayoutProps): JSX.Element { const isDashboardListView = (): boolean => routeKey === 'ALL_DASHBOARD'; const isAlertHistory = (): boolean => routeKey === 'ALERT_HISTORY'; const isAlertOverview = (): boolean => routeKey === 'ALERT_OVERVIEW'; - const isDashboardView = (): boolean => { - /** - * need to match using regex here as the getRoute function will not work for - * routes with id - */ - const regex = /^\/dashboard\/[a-zA-Z0-9_-]+$/; - return regex.test(pathname); - }; + const isPathMatch = (regex: RegExp): boolean => regex.test(pathname); - const isDashboardWidgetView = (): boolean => { - const regex = /^\/dashboard\/[a-zA-Z0-9_-]+\/new$/; - return regex.test(pathname); - }; + const isDashboardView = (): boolean => + isPathMatch(/^\/dashboard\/[a-zA-Z0-9_-]+$/); + + const isDashboardWidgetView = (): boolean => + isPathMatch(/^\/dashboard\/[a-zA-Z0-9_-]+\/new$/); + + const isTraceDetailsView = (): boolean => + isPathMatch(/^\/trace\/[a-zA-Z0-9]+(\?.*)?$/); useEffect(() => { if (isDarkMode) { @@ -279,23 +253,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element { } }, [isDarkMode]); - const isSideNavCollapsed = getLocalStorageKey(IS_SIDEBAR_COLLAPSED); - - /** - * Note: Right now we don't have a page-level method to pass the sidebar collapse state. - * Since the use case for overriding is not widely needed, we are setting it here - * so that the workspace locked page will have an expanded sidebar regardless of how users - * have set it or what is stored in localStorage. This will not affect the localStorage config. - */ - const isWorkspaceLocked = pathname === ROUTES.WORKSPACE_LOCKED; - return ( - + {pageTitle} @@ -321,25 +280,11 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
)} - + {isToDisplayLayout && !renderFullScreen && ( - + )} -
+
}> @@ -356,6 +301,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element { isMessagingQueues() ? 0 : '0 1rem', + + ...(isTraceDetailsView() ? { marginRight: 0 } : {}), }} > {isToDisplayLayout && !renderFullScreen && } diff --git a/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx index 3afee8d0d6..4a4e81c1f2 100644 --- a/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx +++ b/frontend/src/container/BillingContainer/BillingUsageGraph/BillingUsageGraph.tsx @@ -58,6 +58,21 @@ const calculateStartEndTime = ( export function BillingUsageGraph(props: BillingUsageGraphProps): JSX.Element { const { data, billAmount } = props; + // Added this to fix the issue where breakdown with one day data are causing the bars to spread across multiple days + data?.details?.breakdown?.forEach((breakdown: any) => { + if (breakdown?.dayWiseBreakdown?.breakdown?.length === 1) { + const currentDay = breakdown.dayWiseBreakdown.breakdown[0]; + const nextDay = { + ...currentDay, + timestamp: currentDay.timestamp + 86400, + count: 0, + size: 0, + quantity: 0, + total: 0, + }; + breakdown.dayWiseBreakdown.breakdown.push(nextDay); + } + }); const graphCompatibleData = useMemo( () => convertDataToMetricRangePayload(data), [data], diff --git a/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx b/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx index 52f4d52215..48075649b7 100644 --- a/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx +++ b/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx @@ -55,6 +55,7 @@ function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element { onClick={(): void => { onSelect(option.selection); }} + data-testid={`alert-type-card-${option.selection}`} > {option.description}{' '} void, + ): void => { + const currentViewDetails = getViewDetailsUsingViewKey(key, allViewsData); + if (!currentViewDetails) { + return; + } + + let extraData: ExtraData = {}; + try { + extraData = JSON.parse(currentViewDetails?.extraData ?? '{}') as ExtraData; + } catch (error) { + console.error('Error parsing extraData:', error); + } + + if (extraData.selectColumns?.length) { + handleOptionsChange({ + ...options, + selectColumns: extraData.selectColumns, + }); + } else if (!isEqual(defaultTraceSelectedColumns, options.selectColumns)) { + handleOptionsChange({ + ...options, + selectColumns: defaultTraceSelectedColumns, + }); + } + }; const onMenuItemSelectHandler = useCallback( ({ key }: { key: string }): void => { const currentViewDetails = getViewDetailsUsingViewKey( @@ -321,6 +366,13 @@ function ExplorerOptions({ updatePreservedViewInLocalStorage(option); + updateOrRestoreSelectColumns( + option.key, + viewsData?.data?.data, + options, + handleOptionsChange, + ); + if (ref.current) { ref.current.blur(); } @@ -360,14 +412,20 @@ function ExplorerOptions({ viewName: newViewName || '', compositeQuery, sourcePage: sourcepage, - extraData: JSON.stringify({ color }), + extraData: JSON.stringify({ + color, + selectColumns: options.selectColumns, + }), }); const onSaveHandler = (): void => { saveNewViewHandler({ compositeQuery, handlePopOverClose: hideSaveViewModal, - extraData: JSON.stringify({ color }), + extraData: JSON.stringify({ + color, + selectColumns: options.selectColumns, + }), notifications, panelType: panelType || PANEL_TYPES.LIST, redirectWithQueryBuilderData, diff --git a/frontend/src/container/FormAlertRules/BasicInfo.tsx b/frontend/src/container/FormAlertRules/BasicInfo.tsx index d047ed617b..177c9e0fde 100644 --- a/frontend/src/container/FormAlertRules/BasicInfo.tsx +++ b/frontend/src/container/FormAlertRules/BasicInfo.tsx @@ -189,6 +189,7 @@ function BasicInfo({ checked={shouldBroadCastToAllChannels} onChange={handleBroadcastToAllChannels} disabled={noChannels || !!channels.loading} + data-testid="alert-broadcast-to-all-channels" /> diff --git a/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx b/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx index ad1b9fb327..209369c229 100644 --- a/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx +++ b/frontend/src/container/FormAlertRules/ChannelSelect/index.tsx @@ -63,6 +63,7 @@ function ChannelSelect({ mode="multiple" style={{ width: '100%' }} placeholder={t('placeholder_channel_select')} + data-testid="alert-channel-select" value={currentValue} onChange={(value): void => { handleChange(value as string[]); diff --git a/frontend/src/container/FormAlertRules/QuerySection.tsx b/frontend/src/container/FormAlertRules/QuerySection.tsx index aa56c84571..ff958fd253 100644 --- a/frontend/src/container/FormAlertRules/QuerySection.tsx +++ b/frontend/src/container/FormAlertRules/QuerySection.tsx @@ -99,7 +99,7 @@ function QuerySection({ { label: ( - diff --git a/frontend/src/container/FormAlertRules/RuleOptions.tsx b/frontend/src/container/FormAlertRules/RuleOptions.tsx index da265f34cc..ed0792d9d5 100644 --- a/frontend/src/container/FormAlertRules/RuleOptions.tsx +++ b/frontend/src/container/FormAlertRules/RuleOptions.tsx @@ -323,6 +323,45 @@ function RuleOptions({ {t('text_for')} + + + + + { + setAlertDef({ + ...alertDef, + condition: { + ...alertDef.condition, + requireMinPoints: e.target.checked, + }, + }); + }} + /> + + {t('text_require_min_points')} + + + { + setAlertDef({ + ...alertDef, + condition: { + ...alertDef.condition, + requiredNumPoints: Number(value) || 0, + }, + }); + }} + type="number" + onWheel={(e): void => e.currentTarget.blur()} + /> + + {t('text_num_points')} + + diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss b/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss index 11659e9a3e..1af189fc15 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss +++ b/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss @@ -20,6 +20,14 @@ } } +.widget-header-title-container { + display: flex; + align-items: center; + gap: 8px; + overflow: hidden; + text-overflow: ellipsis; +} + .widget-header-title { max-width: 80%; } @@ -58,3 +66,17 @@ } } } + +.long-tooltip { + .ant-tooltip-content { + max-height: 500px; + overflow: auto; + } + &.ant-tooltip { + max-width: 500px; + } +} + +.info-tooltip { + cursor: pointer; +} diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx index d4aa6a4c09..6d7839aa4f 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx +++ b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx @@ -6,8 +6,8 @@ import { CopyOutlined, DeleteOutlined, EditFilled, - ExclamationCircleOutlined, FullscreenOutlined, + InfoCircleOutlined, MoreOutlined, SearchOutlined, WarningOutlined, @@ -21,7 +21,7 @@ import useComponentPermission from 'hooks/useComponentPermission'; import history from 'lib/history'; import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { isEmpty } from 'lodash-es'; -import { X } from 'lucide-react'; +import { CircleX, X } from 'lucide-react'; import { unparse } from 'papaparse'; import { ReactNode, useCallback, useMemo, useState } from 'react'; import { UseQueryResult } from 'react-query'; @@ -234,13 +234,25 @@ function WidgetHeader({ /> ) : ( <> - - {title} - +
+ + {title} + + {widget.description && ( + + + + )} +
{threshold}
{isFetchingResponse && !queryResponse.isError && ( @@ -252,7 +264,7 @@ function WidgetHeader({ placement={errorTooltipPosition} className="widget-api-actions" > - + )} diff --git a/frontend/src/container/GridTableComponent/GridTableComponent.styles.scss b/frontend/src/container/GridTableComponent/GridTableComponent.styles.scss new file mode 100644 index 0000000000..80491e991a --- /dev/null +++ b/frontend/src/container/GridTableComponent/GridTableComponent.styles.scss @@ -0,0 +1,5 @@ +.long-text-tooltip { + max-width: 500px; + max-height: 500px; + overflow-y: auto; +} diff --git a/frontend/src/container/GridTableComponent/index.tsx b/frontend/src/container/GridTableComponent/index.tsx index fbd3892c48..dfa90b8255 100644 --- a/frontend/src/container/GridTableComponent/index.tsx +++ b/frontend/src/container/GridTableComponent/index.tsx @@ -1,3 +1,5 @@ +import './GridTableComponent.styles.scss'; + import { ExclamationCircleFilled } from '@ant-design/icons'; import { Space, Tooltip } from 'antd'; import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig'; @@ -5,6 +7,7 @@ import { Events } from 'constants/events'; import { QueryTable } from 'container/QueryTable'; import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { cloneDeep, get, isEmpty } from 'lodash-es'; +import LineClampedText from 'periscope/components/LineClampedText/LineClampedText'; import { memo, ReactNode, useCallback, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { eventEmitter } from 'utils/getEventEmitter'; @@ -116,7 +119,16 @@ function GridTableComponent({ } > - {text} + + {hasMultipleMatches && ( @@ -127,7 +139,19 @@ function GridTableComponent({ ); } } - return
{text}
; + return ( +
+ +
+ ); }, })); diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss b/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss index b355c90551..99f93a7935 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss @@ -63,8 +63,7 @@ height: 40px; justify-content: end; padding: 0 8px; - margin-top: 12px; - margin-bottom: 2px; + margin: 12px 0 2px; } } diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index da7bbbfc60..9ce242582b 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -14,10 +14,12 @@ import { resourceAttributesToTagFilterItems, } from 'hooks/useResourceAttribute/utils'; import useUrlQuery from 'hooks/useUrlQuery'; +import getStep from 'lib/getStep'; import history from 'lib/history'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useLocation, useParams } from 'react-router-dom'; +import store from 'store'; import { UpdateTimeInterval } from 'store/actions'; import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; @@ -123,6 +125,16 @@ function DBCall(): JSX.Element { [servicename, tagFilterItems], ); + const stepInterval = useMemo( + () => + getStep({ + end: store.getState().globalTime.maxTime, + inputFormat: 'ns', + start: store.getState().globalTime.minTime, + }), + [], + ); + const logEventCalledRef = useRef(false); useEffect(() => { @@ -158,6 +170,7 @@ function DBCall(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery, + stepInterval, })} > View Traces @@ -192,6 +205,7 @@ function DBCall(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery, + stepInterval, })} > View Traces diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index 5ba3d3df6c..328a544472 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -16,10 +16,12 @@ import { resourceAttributesToTagFilterItems, } from 'hooks/useResourceAttribute/utils'; import useUrlQuery from 'hooks/useUrlQuery'; +import getStep from 'lib/getStep'; import history from 'lib/history'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useLocation, useParams } from 'react-router-dom'; +import store from 'store'; import { UpdateTimeInterval } from 'store/actions'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { EQueryType } from 'types/common/dashboard'; @@ -141,6 +143,15 @@ function External(): JSX.Element { ], }); + const stepInterval = useMemo( + () => + getStep({ + end: store.getState().globalTime.maxTime, + inputFormat: 'ns', + start: store.getState().globalTime.minTime, + }), + [], + ); const logEventCalledRef = useRef(false); useEffect(() => { if (!logEventCalledRef.current) { @@ -222,6 +233,7 @@ function External(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery: errorApmToTraceQuery, + stepInterval, })} > View Traces @@ -257,6 +269,7 @@ function External(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery, + stepInterval, })} > View Traces @@ -295,6 +308,7 @@ function External(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery, + stepInterval, })} > View Traces @@ -330,6 +344,7 @@ function External(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery, + stepInterval, })} > View Traces diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 241395b23a..77bf86b80f 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -15,6 +15,7 @@ import { resourceAttributesToTagFilterItems, } from 'hooks/useResourceAttribute/utils'; import useUrlQuery from 'hooks/useUrlQuery'; +import getStep from 'lib/getStep'; import history from 'lib/history'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { defaultTo } from 'lodash-es'; @@ -38,6 +39,7 @@ import { } from '../MetricsPageQueries/OverviewQueries'; import { Col, ColApDexContainer, ColErrorContainer, Row } from '../styles'; import ApDex from './Overview/ApDex'; +import GraphControlsPanel from './Overview/GraphControlsPanel/GraphControlsPanel'; import ServiceOverview from './Overview/ServiceOverview'; import TopLevelOperation from './Overview/TopLevelOperations'; import TopOperation from './Overview/TopOperation'; @@ -45,9 +47,11 @@ import TopOperationMetrics from './Overview/TopOperationMetrics'; import { Button, Card } from './styles'; import { IServiceName } from './types'; import { + generateExplorerPath, handleNonInQueryRange, onGraphClickHandler, onViewTracePopupClick, + useGetAPMToLogsQueries, useGetAPMToTracesQueries, } from './util'; @@ -177,6 +181,16 @@ function Application(): JSX.Element { id: SERVICE_CHART_ID.errorPercentage, }); + const stepInterval = useMemo( + () => + getStep({ + end: maxTime, + inputFormat: 'ns', + start: minTime, + }), + [maxTime, minTime], + ); + const onDragSelect = useCallback( (start: number, end: number) => { const startTimestamp = Math.trunc(start); @@ -194,33 +208,60 @@ function Application(): JSX.Element { [dispatch, pathname, urlQuery], ); - const onErrorTrackHandler = ( - timestamp: number, - apmToTraceQuery: Query, - ): (() => void) => (): void => { - const currentTime = timestamp; - const tPlusOne = timestamp + 60 * 1000; + const onErrorTrackHandler = useCallback( + ( + timestamp: number, + apmToTraceQuery: Query, + isViewLogsClicked?: boolean, + ): (() => void) => (): void => { + const currentTime = timestamp; + const endTime = timestamp + stepInterval; + console.log(endTime, stepInterval); - const urlParams = new URLSearchParams(search); - urlParams.set(QueryParams.startTime, currentTime.toString()); - urlParams.set(QueryParams.endTime, tPlusOne.toString()); + const urlParams = new URLSearchParams(search); + urlParams.set(QueryParams.startTime, currentTime.toString()); + urlParams.set(QueryParams.endTime, endTime.toString()); + urlParams.delete(QueryParams.relativeTime); + const avialableParams = routeConfig[ROUTES.TRACE]; + const queryString = getQueryString(avialableParams, urlParams); - const avialableParams = routeConfig[ROUTES.TRACE]; - const queryString = getQueryString(avialableParams, urlParams); + const JSONCompositeQuery = encodeURIComponent( + JSON.stringify(apmToTraceQuery), + ); - const JSONCompositeQuery = encodeURIComponent( - JSON.stringify(apmToTraceQuery), - ); + const newPath = generateExplorerPath( + isViewLogsClicked, + urlParams, + servicename, + selectedTraceTags, + JSONCompositeQuery, + queryString, + ); - const newTraceExplorerPath = `${ - ROUTES.TRACES_EXPLORER - }?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&${ - QueryParams.compositeQuery - }=${JSONCompositeQuery}&${queryString.join('&')}`; - - history.push(newTraceExplorerPath); - }; + history.push(newPath); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [stepInterval], + ); + const logErrorQuery = useGetAPMToLogsQueries({ + servicename, + filters: [ + { + id: uuid().slice(0, 8), + key: { + key: 'severity_text', + dataType: DataTypes.String, + type: '', + isColumn: true, + isJSON: false, + id: 'severity_text--string----true', + }, + op: 'in', + value: ['ERROR', 'FATAL', 'error', 'fatal'], + }, + ], + }); const errorTrackQuery = useGetAPMToTracesQueries({ servicename, filters: [ @@ -251,6 +292,7 @@ function Application(): JSX.Element { selectedTraceTags={selectedTraceTags} topLevelOperationsRoute={topLevelOperationsRoute} topLevelOperationsIsLoading={topLevelOperationsIsLoading} + stepInterval={stepInterval} /> @@ -264,6 +306,7 @@ function Application(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery, + stepInterval, })} > View Traces @@ -292,6 +335,7 @@ function Application(): JSX.Element { selectedTraceTags, timestamp: selectedTimeStamp, apmToTraceQuery, + stepInterval, })} > View Traces @@ -304,14 +348,18 @@ function Application(): JSX.Element { /> - + onViewLogsClick={onErrorTrackHandler( + selectedTimeStamp, + logErrorQuery, + true, + )} + onViewTracesClick={onErrorTrackHandler( + selectedTimeStamp, + errorTrackQuery, + )} + /> void; + onViewTracesClick: () => void; +} + +function GraphControlsPanel({ + id, + onViewLogsClick, + onViewTracesClick, +}: GraphControlsPanelProps): JSX.Element { + return ( +
+ + +
+ ); +} + +export default GraphControlsPanel; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx index 9651e16d3a..24e2233244 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx @@ -19,13 +19,14 @@ import { useParams } from 'react-router-dom'; import { EQueryType } from 'types/common/dashboard'; import { v4 as uuid } from 'uuid'; -import { Button } from '../styles'; import { IServiceName } from '../types'; import { handleNonInQueryRange, onViewTracePopupClick, + useGetAPMToLogsQueries, useGetAPMToTracesQueries, } from '../util'; +import GraphControlsPanel from './GraphControlsPanel/GraphControlsPanel'; function ServiceOverview({ onDragSelect, @@ -34,6 +35,7 @@ function ServiceOverview({ selectedTimeStamp, topLevelOperationsRoute, topLevelOperationsIsLoading, + stepInterval, }: ServiceOverviewProps): JSX.Element { const { servicename: encodedServiceName } = useParams(); const servicename = decodeURIComponent(encodedServiceName); @@ -75,21 +77,28 @@ function ServiceOverview({ const apmToTraceQuery = useGetAPMToTracesQueries({ servicename }); + const apmToLogQuery = useGetAPMToLogsQueries({ servicename }); + return ( <> - + /> {topLevelOperationsIsLoading && ( @@ -114,8 +123,8 @@ function ServiceOverview({ ); } - interface ServiceOverviewProps { + stepInterval: number; selectedTimeStamp: number; selectedTraceTags: string; onDragSelect: (start: number, end: number) => void; diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index 3e4dbeceb4..e6d58831a0 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -7,6 +7,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { resourceAttributesToTracesFilterItems } from 'hooks/useResourceAttribute/utils'; import history from 'lib/history'; +import { prepareQueryWithDefaultTimestamp } from 'pages/LogsExplorer/utils'; import { traceFilterKeys } from 'pages/TracesExplorer/Filter/filterUtils'; import { Dispatch, SetStateAction, useMemo } from 'react'; import { @@ -33,21 +34,44 @@ interface OnViewTracePopupClickProps { selectedTraceTags: string; timestamp: number; apmToTraceQuery: Query; + isViewLogsClicked?: boolean; + stepInterval?: number; } + +export function generateExplorerPath( + isViewLogsClicked: boolean | undefined, + urlParams: URLSearchParams, + servicename: string | undefined, + selectedTraceTags: string, + JSONCompositeQuery: string, + queryString: string[], +): string { + const basePath = isViewLogsClicked + ? ROUTES.LOGS_EXPLORER + : ROUTES.TRACES_EXPLORER; + + return `${basePath}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&${ + QueryParams.compositeQuery + }=${JSONCompositeQuery}&${queryString.join('&')}`; +} + +// TODO(@rahul-signoz): update the name of this function once we have view logs button in every panel export function onViewTracePopupClick({ selectedTraceTags, servicename, timestamp, apmToTraceQuery, + isViewLogsClicked, + stepInterval, }: OnViewTracePopupClickProps): VoidFunction { return (): void => { const currentTime = timestamp; - - const tPlusOne = timestamp + 60; + const endTime = timestamp + (stepInterval || 60); const urlParams = new URLSearchParams(window.location.search); urlParams.set(QueryParams.startTime, currentTime.toString()); - urlParams.set(QueryParams.endTime, tPlusOne.toString()); + urlParams.set(QueryParams.endTime, endTime.toString()); + urlParams.delete(QueryParams.relativeTime); const avialableParams = routeConfig[ROUTES.TRACE]; const queryString = getQueryString(avialableParams, urlParams); @@ -55,13 +79,16 @@ export function onViewTracePopupClick({ JSON.stringify(apmToTraceQuery), ); - const newTraceExplorerPath = `${ - ROUTES.TRACES_EXPLORER - }?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&${ - QueryParams.compositeQuery - }=${JSONCompositeQuery}&${queryString.join('&')}`; + const newPath = generateExplorerPath( + isViewLogsClicked, + urlParams, + servicename, + selectedTraceTags, + JSONCompositeQuery, + queryString, + ); - history.push(newTraceExplorerPath); + history.push(newPath); }; } @@ -108,12 +135,13 @@ export function handleQueryChange( attributeKeys: BaseAutocompleteData, serviceAttribute: string, filters?: TagFilterItem[], + logs?: boolean, ): Query { const filterItem: TagFilterItem[] = [ { id: uuid().slice(0, 8), key: attributeKeys, - op: 'in', + op: logs ? '=' : 'in', value: serviceAttribute, }, ]; @@ -132,6 +160,42 @@ export function handleQueryChange( }; } +export function useGetAPMToLogsQueries({ + servicename, + filters, +}: { + servicename: string; + filters?: TagFilterItem[]; +}): Query { + const finalFilters: TagFilterItem[] = []; + const { updateAllQueriesOperators } = useQueryBuilder(); + let updatedQuery = updateAllQueriesOperators( + initialQueriesMap.logs, + PANEL_TYPES.LIST, + DataSource.LOGS, + ); + const serviceName = { + id: 'service.name--string--resource--true', + dataType: DataTypes.String, + isColumn: false, + key: 'service.name', + type: 'resource', + isJSON: false, + }; + + if (filters?.length) { + finalFilters.push(...filters); + } + updatedQuery = prepareQueryWithDefaultTimestamp(updatedQuery); + return handleQueryChange( + updatedQuery, + serviceName, + servicename, + finalFilters, + true, + ); +} + export function useGetAPMToTracesQueries({ servicename, isExternalCall, diff --git a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx index ea59dc4bcf..d6f63165d5 100644 --- a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx @@ -182,6 +182,15 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element { const { t } = useTranslation(['dashboard', 'common']); + // used to set the initial value for the updatedTitle + // the context value is sometimes not available during the initial render + // due to which the updatedTitle is set to some previous value + useEffect(() => { + if (selectedDashboard) { + setUpdatedTitle(selectedDashboard.data.title); + } + }, [selectedDashboard]); + useEffect(() => { if (state.error) { notifications.error({ diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx index 38fe984165..d98bd25e1c 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx @@ -1,14 +1,17 @@ import './QuerySection.styles.scss'; import { Color } from '@signozhq/design-tokens'; -import { Button, Tabs, Tooltip, Typography } from 'antd'; +import { Button, Tabs, Typography } from 'antd'; import logEvent from 'api/common/logEvent'; import PromQLIcon from 'assets/Dashboard/PromQl'; import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport'; import TextToolTip from 'components/TextToolTip'; import { PANEL_TYPES } from 'constants/queryBuilder'; import { QBShortcuts } from 'constants/shortcuts/QBShortcuts'; -import { getDefaultWidgetData } from 'container/NewWidget/utils'; +import { + getDefaultWidgetData, + PANEL_TYPE_TO_QUERY_TYPES, +} from 'container/NewWidget/utils'; import { QueryBuilder } from 'container/QueryBuilder'; import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces'; import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys'; @@ -112,16 +115,18 @@ function QuerySection({ ], ); - const handleQueryCategoryChange = (qCategory: string): void => { - const currentQueryType = qCategory; - - featureResponse.refetch().then(() => { - handleStageQuery({ - ...currentQuery, - queryType: currentQueryType as EQueryType, + const handleQueryCategoryChange = useCallback( + (qCategory: string): void => { + const currentQueryType = qCategory; + featureResponse.refetch().then(() => { + handleStageQuery({ + ...currentQuery, + queryType: currentQueryType as EQueryType, + }); }); - }); - }; + }, + [currentQuery, featureResponse, handleStageQuery], + ); const handleRunQuery = (): void => { const widgetId = urlQuery.get('widgetId'); @@ -147,72 +152,55 @@ function QuerySection({ return config; }, []); - const listItems = [ - { - key: EQueryType.QUERY_BUILDER, - label: ( - - ), - tab: Query Builder, - children: ( - - ), - }, - ]; + const items = useMemo(() => { + const supportedQueryTypes = PANEL_TYPE_TO_QUERY_TYPES[selectedGraph] || []; - const items = [ - { - key: EQueryType.QUERY_BUILDER, + const queryTypeComponents = { + [EQueryType.QUERY_BUILDER]: { + icon: , + label: 'Query Builder', + component: ( + + ), + }, + [EQueryType.CLICKHOUSE]: { + icon: , + label: 'ClickHouse Query', + component: , + }, + [EQueryType.PROM]: { + icon: ( + + ), + label: 'PromQL', + component: , + }, + }; + + return supportedQueryTypes.map((queryType) => ({ + key: queryType, label: ( ), - tab: Query Builder, - children: ( - - ), - }, - { - key: EQueryType.CLICKHOUSE, - label: ( - - ), - tab: ClickHouse Query, - children: , - }, - { - key: EQueryType.PROM, - label: ( - - - - ), - tab: PromQL, - children: , - }, - ]; + tab: {queryTypeComponents[queryType].label}, + children: queryTypeComponents[queryType].component, + })); + }, [ + selectedGraph, + filterConfigs, + selectedDashboard?.data?.version, + isDarkMode, + ]); useEffect(() => { registerShortcut(QBShortcuts.StageAndRunQuery, handleRunQuery); @@ -223,6 +211,16 @@ function QuerySection({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [handleRunQuery]); + useEffect(() => { + // switch to query builder if query type is not supported + if ( + (selectedGraph === PANEL_TYPES.TABLE || selectedGraph === PANEL_TYPES.PIE) && + currentQuery.queryType === EQueryType.PROM + ) { + handleQueryCategoryChange(EQueryType.QUERY_BUILDER); + } + }, [currentQuery, handleQueryCategoryChange, selectedGraph]); + return (
} - items={selectedGraph === PANEL_TYPES.LIST ? listItems : items} + items={items} />
); diff --git a/frontend/src/container/NewWidget/utils.ts b/frontend/src/container/NewWidget/utils.ts index 22d4fa763b..f8eef3157d 100644 --- a/frontend/src/container/NewWidget/utils.ts +++ b/frontend/src/container/NewWidget/utils.ts @@ -11,6 +11,7 @@ import { import { cloneDeep, isEqual, set, unset } from 'lodash-es'; import { Widgets } from 'types/api/dashboard/getAll'; import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData'; +import { EQueryType } from 'types/common/dashboard'; import { DataSource } from 'types/common/queryBuilder'; export const getIsQueryModified = ( @@ -492,3 +493,39 @@ export const getDefaultWidgetData = ( ...listViewInitialTraceQuery.builder.queryData[0].selectColumns, ], }); + +export const PANEL_TYPE_TO_QUERY_TYPES: Record = { + [PANEL_TYPES.TIME_SERIES]: [ + EQueryType.QUERY_BUILDER, + EQueryType.CLICKHOUSE, + EQueryType.PROM, + ], + [PANEL_TYPES.TABLE]: [EQueryType.QUERY_BUILDER, EQueryType.CLICKHOUSE], + [PANEL_TYPES.VALUE]: [ + EQueryType.QUERY_BUILDER, + EQueryType.CLICKHOUSE, + EQueryType.PROM, + ], + [PANEL_TYPES.LIST]: [EQueryType.QUERY_BUILDER], + [PANEL_TYPES.TRACE]: [ + EQueryType.QUERY_BUILDER, + EQueryType.CLICKHOUSE, + EQueryType.PROM, + ], + [PANEL_TYPES.BAR]: [ + EQueryType.QUERY_BUILDER, + EQueryType.CLICKHOUSE, + EQueryType.PROM, + ], + [PANEL_TYPES.PIE]: [EQueryType.QUERY_BUILDER, EQueryType.CLICKHOUSE], + [PANEL_TYPES.HISTOGRAM]: [ + EQueryType.QUERY_BUILDER, + EQueryType.CLICKHOUSE, + EQueryType.PROM, + ], + [PANEL_TYPES.EMPTY_WIDGET]: [ + EQueryType.QUERY_BUILDER, + EQueryType.CLICKHOUSE, + EQueryType.PROM, + ], +}; diff --git a/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap index 1a930f740c..6de3e82778 100644 --- a/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap +++ b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap @@ -266,14 +266,22 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="ant-table-cell" >
- demo-app +
+ demo-app +
@@ -284,14 +292,22 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="ant-table-cell" >
- customer +
+ customer +
@@ -302,14 +318,22 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="ant-table-cell" >
- mysql +
+ mysql +
@@ -320,14 +344,22 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="ant-table-cell" >
- frontend +
+ frontend +
@@ -338,14 +370,22 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="ant-table-cell" >
- driver +
+ driver +
@@ -356,14 +396,22 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="ant-table-cell" >
- route +
+ route +
@@ -374,14 +422,22 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="ant-table-cell" >
- redis +
+ redis +
diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.tsx b/frontend/src/container/QueryBuilder/components/Query/Query.tsx index 453cf063f8..e35086f3b0 100644 --- a/frontend/src/container/QueryBuilder/components/Query/Query.tsx +++ b/frontend/src/container/QueryBuilder/components/Query/Query.tsx @@ -333,7 +333,7 @@ export const Query = memo(function Query({ const isVersionV4 = version && version === ENTITY_VERSION_V4; return ( - + ( initialHavingValues, ); + const [errorMessage, setErrorMessage] = useState(null); const { isMulti } = useTagValidation( currentFormValue.op, @@ -198,6 +200,29 @@ export function HavingFilter({ resetChanges(); }; + const handleFocus = useCallback(() => { + setErrorMessage(null); + }, []); + + const handleBlur = useCallback((): void => { + if (searchText) { + const { columnName, op, value } = getHavingObject(searchText); + const isCompleteHavingClause = + columnName && op && value.every((v) => v !== ''); + + if (isCompleteHavingClause && isValidHavingValue(searchText)) { + setLocalValues((prev) => { + const updatedValues = [...prev, searchText]; + onChange(updatedValues.map(transformFromStringToHaving)); + return updatedValues; + }); + setSearchText(''); + } else { + setErrorMessage('Invalid HAVING clause'); + } + } + }, [searchText, onChange]); + useEffect(() => { parseSearchText(searchText); }, [searchText, parseSearchText]); @@ -209,28 +234,36 @@ export function HavingFilter({ const isMetricsDataSource = query.dataSource === DataSource.METRICS; return ( - + <> + + {errorMessage && ( +
{errorMessage}
+ )} + ); } diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss index 624546fed5..7aee4f9414 100644 --- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss +++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.styles.scss @@ -334,8 +334,8 @@ .qb-search-bar-tokenised-tags { .ant-tag { - border: 1px solid var(--bg-slate-100); background: var(--bg-vanilla-300); + border: 1px solid var(--bg-slate-100); box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); .ant-typography { diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx index 0925c10d97..d07ba7169c 100644 --- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx +++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2.tsx @@ -244,7 +244,7 @@ function QueryBuilderSearchV2( isFetching: isFetchingSuggestions, } = useGetAttributeSuggestions( { - searchText: searchValue.split(' ')[0], + searchText: searchValue?.split(' ')[0], dataSource: query.dataSource, filters: query.filters, }, @@ -691,7 +691,7 @@ function QueryBuilderSearchV2( } } if (currentState === DropdownState.OPERATOR) { - const keyOperator = searchValue.split(' '); + const keyOperator = searchValue?.split(' '); const partialOperator = keyOperator?.[1]; const strippedKey = keyOperator?.[0]; diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss index 153f32e5ee..1b434316e5 100644 --- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss +++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearchV2/Suggestions.styles.scss @@ -13,6 +13,7 @@ width: 5px; border-radius: 50%; background-color: var(--bg-slate-300); + flex-shrink: 0; } .option { @@ -207,6 +208,10 @@ background: var(--bg-vanilla-300); } } + + .value { + color: var(--bg-ink-100); + } } } .option:hover { diff --git a/frontend/src/container/QueryBuilder/filters/utils.ts b/frontend/src/container/QueryBuilder/filters/utils.ts index 7e4c12d9d0..ff52e7cb70 100644 --- a/frontend/src/container/QueryBuilder/filters/utils.ts +++ b/frontend/src/container/QueryBuilder/filters/utils.ts @@ -1,3 +1,4 @@ +import { AttributeValuesMap } from 'components/ClientSideQBSearch/ClientSideQBSearch'; import { HAVING_FILTER_REGEXP } from 'constants/regExp'; import { IOption } from 'hooks/useResourceAttribute/types'; import uniqWith from 'lodash-es/unionWith'; @@ -92,3 +93,20 @@ export const getValidOrderByResult = (result: IOption[]): IOption[] => return acc; }, []); + +export const transformKeyValuesToAttributeValuesMap = ( + attributeValuesMap: Record, +): AttributeValuesMap => + Object.fromEntries( + Object.entries(attributeValuesMap || {}).map(([key, values]) => [ + key, + { + stringAttributeValues: + typeof values[0] === 'string' ? (values as string[]) : [], + numberAttributeValues: + typeof values[0] === 'number' ? (values as number[]) : [], + boolAttributeValues: + typeof values[0] === 'boolean' ? (values as boolean[]) : [], + }, + ]), + ); diff --git a/frontend/src/container/SideNav/SideNav.styles.scss b/frontend/src/container/SideNav/SideNav.styles.scss index 2f5167f104..1a148e2469 100644 --- a/frontend/src/container/SideNav/SideNav.styles.scss +++ b/frontend/src/container/SideNav/SideNav.styles.scss @@ -3,10 +3,6 @@ height: 100%; position: relative; z-index: 1; - - &.docked { - width: 240px; - } } .sideNav { @@ -229,39 +225,6 @@ display: block; } } - - &.docked { - flex: 0 0 240px; - max-width: 240px; - min-width: 240px; - width: 240px; - - .secondary-nav-items { - width: 240px; - } - - .brand { - justify-content: space-between; - } - - .get-started-nav-items { - .get-started-btn { - justify-content: flex-start; - } - } - - .collapse-expand-handlers { - display: block; - } - - .nav-item-label { - display: block; - } - - .nav-item-beta { - display: block; - } - } } .lightMode { diff --git a/frontend/src/container/SideNav/SideNav.tsx b/frontend/src/container/SideNav/SideNav.tsx index 1ba863d8ec..16787bc3d8 100644 --- a/frontend/src/container/SideNav/SideNav.tsx +++ b/frontend/src/container/SideNav/SideNav.tsx @@ -3,7 +3,7 @@ import './SideNav.styles.scss'; import { Color } from '@signozhq/design-tokens'; -import { Button, Tooltip } from 'antd'; +import { Button } from 'antd'; import logEvent from 'api/common/logEvent'; import cx from 'classnames'; import { FeatureKeys } from 'constants/features'; @@ -16,9 +16,6 @@ import history from 'lib/history'; import { AlertTriangle, CheckSquare, - ChevronLeftCircle, - ChevronRightCircle, - PanelRight, RocketIcon, UserCircle, } from 'lucide-react'; @@ -55,13 +52,9 @@ interface UserManagementMenuItems { function SideNav({ licenseData, isFetching, - onCollapse, - collapsed, }: { licenseData: any; isFetching: boolean; - onCollapse: () => void; - collapsed: boolean; }): JSX.Element { const [menuItems, setMenuItems] = useState(defaultMenuItems); @@ -330,8 +323,6 @@ function SideNav({ }; useEffect(() => { - registerShortcut(GlobalShortcuts.SidebarCollapse, onCollapse); - registerShortcut(GlobalShortcuts.NavigateToServices, () => onClickHandler(ROUTES.APPLICATION, null), ); @@ -359,7 +350,6 @@ function SideNav({ ); return (): void => { - deregisterShortcut(GlobalShortcuts.SidebarCollapse); deregisterShortcut(GlobalShortcuts.NavigateToServices); deregisterShortcut(GlobalShortcuts.NavigateToTraces); deregisterShortcut(GlobalShortcuts.NavigateToLogs); @@ -368,11 +358,11 @@ function SideNav({ deregisterShortcut(GlobalShortcuts.NavigateToExceptions); deregisterShortcut(GlobalShortcuts.NavigateToMessagingQueues); }; - }, [deregisterShortcut, onClickHandler, onCollapse, registerShortcut]); + }, [deregisterShortcut, onClickHandler, registerShortcut]); return ( -
-
+
+
{licenseTag}
)}
- - -
{isCloudUserVal && ( @@ -504,14 +483,6 @@ function SideNav({ }} /> )} - -
- {collapsed ? ( - - ) : ( - - )} -
diff --git a/frontend/src/container/TraceDetail/SelectedSpanDetails/config.ts b/frontend/src/container/TraceDetail/SelectedSpanDetails/config.ts index 145b79f2ff..2b6dd6ffea 100644 --- a/frontend/src/container/TraceDetail/SelectedSpanDetails/config.ts +++ b/frontend/src/container/TraceDetail/SelectedSpanDetails/config.ts @@ -1,4 +1,5 @@ import { initialAutocompleteData, OPERATORS } from 'constants/queryBuilder'; +import { getOperatorValue } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils'; import getStep from 'lib/getStep'; import { BaseAutocompleteData, @@ -27,7 +28,8 @@ export const getTraceToLogsQuery = ( items: [ { id: uuid(), - op: OPERATORS.IN, + // for generating query we use in instead of IN + op: getOperatorValue(OPERATORS.IN), value: traceId, key, }, diff --git a/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx b/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx index 39e180ee12..d8eb1a6972 100644 --- a/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx +++ b/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx @@ -1,4 +1,4 @@ -import { Button, Modal, Tabs, Tooltip, Typography } from 'antd'; +import { Button, Modal, Row, Tabs, Tooltip, Typography } from 'antd'; import Editor from 'components/Editor'; import { StyledSpace } from 'components/Styled'; import { QueryParams } from 'constants/query'; @@ -6,7 +6,8 @@ import ROUTES from 'constants/routes'; import { useIsDarkMode } from 'hooks/useDarkMode'; import createQueryParams from 'lib/createQueryParams'; import history from 'lib/history'; -import { useState } from 'react'; +import { PanelRight } from 'lucide-react'; +import { Dispatch, SetStateAction, useState } from 'react'; import { useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import { AppState } from 'store/reducers'; @@ -28,6 +29,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { firstSpanStartTime, traceStartTime = minTime, traceEndTime = maxTime, + setCollapsed, } = props; const { id: traceId } = useParams(); @@ -96,14 +98,14 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { styledclass={[styles.selectedSpanDetailsContainer, styles.overflow]} direction="vertical" > - - Details for selected Span - + + Details for selected Span +
{renderCustomerStories((index) => index % 2 === 0)} - {renderCustomerStories((index) => index % 2 !== 0)} + + {renderCustomerStories((index) => index % 2 === 0)} + + + {renderCustomerStories((index) => index % 2 !== 0)} + {isAdmin && ( @@ -214,13 +243,21 @@ export default function WorkspaceBlocked(): JSX.Element { // children: 'Our Pricing', // }, { - key: '4', + key: 'faqs', label: t('faqs'), children: ( - - - + + + {isAdmin && ( @@ -324,7 +362,7 @@ export default function WorkspaceBlocked(): JSX.Element { loading={isLoading} onClick={handleUpdateCreditCard} > - continue my journey + Continue my Journey @@ -340,9 +378,13 @@ export default function WorkspaceBlocked(): JSX.Element { )} - - - +
+ +
)} diff --git a/frontend/src/pages/WorkspaceLocked/workspaceLocked.data.ts b/frontend/src/pages/WorkspaceLocked/workspaceLocked.data.ts index 0f4d07b96e..bebcdaf64a 100644 --- a/frontend/src/pages/WorkspaceLocked/workspaceLocked.data.ts +++ b/frontend/src/pages/WorkspaceLocked/workspaceLocked.data.ts @@ -42,7 +42,7 @@ export const enterpriseGradeValuesData = [ export const customerStoriesData = [ { - key: 'c-story-1', + key: 'story-subomi-oluwalana', avatar: 'https://signoz.io/img/users/subomi-oluwalana.webp', personName: 'Subomi Oluwalana', role: 'Founder & CEO at Convoy', @@ -53,7 +53,7 @@ export const customerStoriesData = [ 'https://www.linkedin.com/feed/update/urn:li:activity:7212117589068591105/', }, { - key: 'c-story-2', + key: 'story-dhruv-garg', avatar: 'https://signoz.io/img/users/dhruv-garg.webp', personName: 'Dhruv Garg', role: 'Tech Lead at Nudge', @@ -64,7 +64,7 @@ export const customerStoriesData = [ 'https://www.linkedin.com/posts/dhruv-garg79_signoz-docker-kubernetes-activity-7205163679028240384-Otlb/', }, { - key: 'c-story-3', + key: 'story-vivek-bhakta', avatar: 'https://signoz.io/img/users/vivek-bhakta.webp', personName: 'Vivek Bhakta', role: 'CTO at Wombo AI', @@ -74,7 +74,7 @@ export const customerStoriesData = [ link: 'https://x.com/notorious_VB/status/1701773119696904242', }, { - key: 'c-story-4', + key: 'story-pranay-narang', avatar: 'https://signoz.io/img/users/pranay-narang.webp', personName: 'Pranay Narang', role: 'Engineering at Azodha', @@ -84,7 +84,7 @@ export const customerStoriesData = [ link: 'https://x.com/PranayNarang/status/1676247073396752387', }, { - key: 'c-story-4', + key: 'story-Sheheryar-Sewani', avatar: 'https://signoz.io/img/users/shey.webp', personName: 'Sheheryar Sewani', role: 'Seasoned Rails Dev & Founder', @@ -95,7 +95,7 @@ export const customerStoriesData = [ 'https://www.linkedin.com/feed/update/urn:li:activity:7181011853915926528/', }, { - key: 'c-story-5', + key: 'story-daniel-schell', avatar: 'https://signoz.io/img/users/daniel.webp', personName: 'Daniel Schell', role: 'Founder & CTO at Airlockdigital', @@ -115,7 +115,7 @@ export const customerStoriesData = [ link: 'https://x.com/gofrendiasgard/status/1680139003658641408', }, { - key: 'c-story-7', + key: 'story-anselm-eickhoff', avatar: 'https://signoz.io/img/users/anselm.jpg', personName: 'Anselm Eickhoff', role: 'Software Architect', @@ -129,26 +129,26 @@ export const customerStoriesData = [ export const faqData = [ { - key: '1', + key: 'signoz-cloud-vs-community', label: 'What is the difference between SigNoz Cloud(Teams) and Community Edition?', children: 'You can self-host and manage the community edition yourself. You should choose SigNoz Cloud if you don’t want to worry about managing the SigNoz cluster. There are some exclusive features like SSO & SAML support, which come with SigNoz cloud offering. Our team also offers support on the initial configuration of dashboards & alerts and advises on best practices for setting up your observability stack in the SigNoz cloud offering.', }, { - key: '2', + key: 'calc-for-metrics', label: 'How are number of samples calculated for metrics pricing?', children: "If a timeseries sends data every 30s, then it will generate 2 samples per min. So, if you have 10,000 time series sending data every 30s then you will be sending 20,000 samples per min to SigNoz. This will be around 864 mn samples per month and would cost 86.4 USD/month. Here's an explainer video on how metrics pricing is calculated - Link: https://vimeo.com/973012522", }, { - key: '3', + key: 'enterprise-support-plans', label: 'Do you offer enterprise support plans?', children: 'Yes, feel free to reach out to us on hello@signoz.io if you need a dedicated support plan or paid support for setting up your initial SigNoz setup.', }, { - key: '4', + key: 'who-should-use-enterprise-plans', label: 'Who should use Enterprise plans?', children: 'Teams which need enterprise support or features like SSO, Audit logs, etc. may find our enterprise plans valuable.', diff --git a/frontend/src/periscope/components/LineClampedText/LineClampedText.tsx b/frontend/src/periscope/components/LineClampedText/LineClampedText.tsx index 6b90704b0c..9c77d514e0 100644 --- a/frontend/src/periscope/components/LineClampedText/LineClampedText.tsx +++ b/frontend/src/periscope/components/LineClampedText/LineClampedText.tsx @@ -1,14 +1,16 @@ import './LineClampedText.styles.scss'; -import { Tooltip } from 'antd'; +import { Tooltip, TooltipProps } from 'antd'; import { useEffect, useRef, useState } from 'react'; function LineClampedText({ text, lines, + tooltipProps, }: { text: string; lines?: number; + tooltipProps?: TooltipProps; }): JSX.Element { const [isOverflowing, setIsOverflowing] = useState(false); const textRef = useRef(null); @@ -42,11 +44,22 @@ function LineClampedText({ ); - return isOverflowing ? {content} : content; + return isOverflowing ? ( + + {content} + + ) : ( + content + ); } LineClampedText.defaultProps = { lines: 1, + tooltipProps: {}, }; export default LineClampedText; diff --git a/frontend/src/providers/Alert.tsx b/frontend/src/providers/Alert.tsx index 337eec9ba5..50b6584491 100644 --- a/frontend/src/providers/Alert.tsx +++ b/frontend/src/providers/Alert.tsx @@ -1,10 +1,8 @@ import React, { createContext, useContext, useState } from 'react'; interface AlertRuleContextType { - isAlertRuleDisabled: boolean | undefined; - setIsAlertRuleDisabled: React.Dispatch< - React.SetStateAction - >; + alertRuleState: string | undefined; + setAlertRuleState: React.Dispatch>; } const AlertRuleContext = createContext( @@ -16,13 +14,16 @@ function AlertRuleProvider({ }: { children: React.ReactNode; }): JSX.Element { - const [isAlertRuleDisabled, setIsAlertRuleDisabled] = useState< - boolean | undefined - >(undefined); + const [alertRuleState, setAlertRuleState] = useState( + undefined, + ); const value = React.useMemo( - () => ({ isAlertRuleDisabled, setIsAlertRuleDisabled }), - [isAlertRuleDisabled], + () => ({ + alertRuleState, + setAlertRuleState, + }), + [alertRuleState], ); return ( diff --git a/frontend/src/store/actions/app/index.ts b/frontend/src/store/actions/app/index.ts deleted file mode 100644 index cb2a26f9e6..0000000000 --- a/frontend/src/store/actions/app/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './sideBarCollapse'; diff --git a/frontend/src/store/actions/app/sideBarCollapse.ts b/frontend/src/store/actions/app/sideBarCollapse.ts deleted file mode 100644 index c99d79b6fb..0000000000 --- a/frontend/src/store/actions/app/sideBarCollapse.ts +++ /dev/null @@ -1,16 +0,0 @@ -import setLocalStorageKey from 'api/browser/localstorage/set'; -import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; -import { Dispatch } from 'redux'; -import AppActions from 'types/actions'; - -export const sideBarCollapse = ( - collapseState: boolean, -): ((dispatch: Dispatch) => void) => { - setLocalStorageKey(IS_SIDEBAR_COLLAPSED, `${collapseState}`); - return (dispatch: Dispatch): void => { - dispatch({ - type: 'SIDEBAR_COLLAPSE', - payload: collapseState, - }); - }; -}; diff --git a/frontend/src/store/actions/index.ts b/frontend/src/store/actions/index.ts index 3074cdb3a2..f50fb7477c 100644 --- a/frontend/src/store/actions/index.ts +++ b/frontend/src/store/actions/index.ts @@ -1,4 +1,3 @@ -export * from './app'; export * from './global'; export * from './metrics'; export * from './serviceMap'; diff --git a/frontend/src/store/reducers/app.ts b/frontend/src/store/reducers/app.ts index 4db3965cad..bdb80d2565 100644 --- a/frontend/src/store/reducers/app.ts +++ b/frontend/src/store/reducers/app.ts @@ -1,11 +1,9 @@ import getLocalStorageKey from 'api/browser/localstorage/get'; -import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; import { LOCALSTORAGE } from 'constants/localStorage'; import { getInitialUserTokenRefreshToken } from 'store/utils'; import { AppAction, LOGGED_IN, - SIDEBAR_COLLAPSE, UPDATE_CONFIGS, UPDATE_CURRENT_ERROR, UPDATE_CURRENT_VERSION, @@ -44,7 +42,6 @@ const getInitialUser = (): User | null => { const InitialValue: InitialValueTypes = { isLoggedIn: getLocalStorageKey(LOCALSTORAGE.IS_LOGGED_IN) === 'true', - isSideBarCollapsed: getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true', currentVersion: '', latestVersion: '', featureResponse: { @@ -76,13 +73,6 @@ const appReducer = ( }; } - case SIDEBAR_COLLAPSE: { - return { - ...state, - isSideBarCollapsed: action.payload, - }; - } - case UPDATE_FEATURE_FLAG_RESPONSE: { return { ...state, diff --git a/frontend/src/types/actions/app.ts b/frontend/src/types/actions/app.ts index 78a5da72ad..54b1992af2 100644 --- a/frontend/src/types/actions/app.ts +++ b/frontend/src/types/actions/app.ts @@ -34,11 +34,6 @@ export interface LoggedInUser { }; } -export interface SideBarCollapse { - type: typeof SIDEBAR_COLLAPSE; - payload: boolean; -} - export interface UpdateAppVersion { type: typeof UPDATE_CURRENT_VERSION; payload: { @@ -137,7 +132,6 @@ export interface UpdateFeatureFlag { export type AppAction = | LoggedInUser - | SideBarCollapse | UpdateAppVersion | UpdateLatestVersion | UpdateVersionError diff --git a/frontend/src/types/api/alerts/def.ts b/frontend/src/types/api/alerts/def.ts index 9393ccd5a0..2897b6219b 100644 --- a/frontend/src/types/api/alerts/def.ts +++ b/frontend/src/types/api/alerts/def.ts @@ -1,3 +1,4 @@ +import { AlertLabelsProps } from 'pages/AlertDetails/AlertHeader/AlertLabels/AlertLabels'; import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery'; // default match type for threshold @@ -37,6 +38,8 @@ export interface RuleCondition { selectedQueryName?: string; alertOnAbsent?: boolean | undefined; absentFor?: number | undefined; + requireMinPoints?: boolean | undefined; + requiredNumPoints?: number | undefined; } export interface Labels { [key: string]: string; @@ -94,7 +97,11 @@ export interface AlertRuleTimelineTableResponse { relatedLogsLink: string; } export type AlertRuleTimelineTableResponsePayload = { - data: { items: AlertRuleTimelineTableResponse[]; total: number }; + data: { + items: AlertRuleTimelineTableResponse[]; + total: number; + labels: AlertLabelsProps['labels']; + }; }; type AlertState = 'firing' | 'normal' | 'no-data' | 'muted'; diff --git a/frontend/src/types/reducer/app.ts b/frontend/src/types/reducer/app.ts index 545cff7156..c51defcfb0 100644 --- a/frontend/src/types/reducer/app.ts +++ b/frontend/src/types/reducer/app.ts @@ -17,7 +17,6 @@ export interface User { export default interface AppReducer { isLoggedIn: boolean; - isSideBarCollapsed: boolean; currentVersion: string; latestVersion: string; isCurrentVersionError: boolean; diff --git a/frontend/tests/auth.json b/frontend/tests/auth.json index 2dd3d40466..0de47618ab 100644 --- a/frontend/tests/auth.json +++ b/frontend/tests/auth.json @@ -1,38 +1,34 @@ { - "cookies": [], - "origins": [ - { - "origin": "http://localhost:3301", - "localStorage": [ - { - "name": "isSideBarCollapsed", - "value": "false" - }, - { - "name": "metricsTimeDurations", - "value": "{}" - }, - { - "name": "i18nextLng", - "value": "en-US" - }, - { - "name": "reactQueryDevtoolsSortFn", - "value": "\"Status > Last Updated\"" - }, - { - "name": "AUTH_TOKEN", - "value": "authtoken" - }, - { - "name": "IS_LOGGED_IN", - "value": "true" - }, - { - "name": "REFRESH_AUTH_TOKEN", - "value": "refreshJwt" - } - ] - } - ] -} \ No newline at end of file + "cookies": [], + "origins": [ + { + "origin": "http://localhost:3301", + "localStorage": [ + { + "name": "metricsTimeDurations", + "value": "{}" + }, + { + "name": "i18nextLng", + "value": "en-US" + }, + { + "name": "reactQueryDevtoolsSortFn", + "value": "\"Status > Last Updated\"" + }, + { + "name": "AUTH_TOKEN", + "value": "authtoken" + }, + { + "name": "IS_LOGGED_IN", + "value": "true" + }, + { + "name": "REFRESH_AUTH_TOKEN", + "value": "refreshJwt" + } + ] + } + ] +} diff --git a/go.mod b/go.mod index 628384c2fe..04ec7d710a 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,14 @@ module go.signoz.io/signoz -go 1.21.3 +go 1.22.0 + +toolchain go1.22.7 require ( - github.com/ClickHouse/clickhouse-go/v2 v2.23.2 + github.com/ClickHouse/clickhouse-go/v2 v2.25.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd - github.com/SigNoz/signoz-otel-collector v0.102.10 + github.com/SigNoz/signoz-otel-collector v0.102.12 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 @@ -37,7 +39,7 @@ require ( github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 - github.com/prometheus/common v0.55.0 + github.com/prometheus/common v0.59.1 github.com/prometheus/prometheus v2.5.0+incompatible github.com/rs/cors v1.11.0 github.com/russellhaering/gosaml2 v0.9.0 @@ -57,41 +59,44 @@ require ( go.opentelemetry.io/collector/exporter v0.103.0 go.opentelemetry.io/collector/extension v0.103.0 go.opentelemetry.io/collector/otelcol v0.103.0 - go.opentelemetry.io/collector/pdata v1.10.0 + go.opentelemetry.io/collector/pdata v1.14.1 go.opentelemetry.io/collector/processor v0.103.0 go.opentelemetry.io/collector/receiver v0.103.0 go.opentelemetry.io/collector/service v0.103.0 go.opentelemetry.io/contrib/bridges/otelzap v0.0.0-20240820072021-3fab5f5f20fb go.opentelemetry.io/contrib/config v0.8.0 - go.opentelemetry.io/otel v1.28.0 + go.opentelemetry.io/otel v1.29.0 go.opentelemetry.io/otel/log v0.4.0 - go.opentelemetry.io/otel/metric v1.28.0 - go.opentelemetry.io/otel/sdk v1.28.0 - go.opentelemetry.io/otel/trace v1.28.0 + go.opentelemetry.io/otel/metric v1.29.0 + go.opentelemetry.io/otel/sdk v1.29.0 + go.opentelemetry.io/otel/trace v1.29.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.24.0 + golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/net v0.26.0 - golang.org/x/oauth2 v0.21.0 - golang.org/x/text v0.16.0 - google.golang.org/grpc v1.65.0 + golang.org/x/net v0.28.0 + golang.org/x/oauth2 v0.23.0 + golang.org/x/text v0.18.0 + google.golang.org/grpc v1.66.0 google.golang.org/protobuf v1.34.2 gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.29.3 + k8s.io/apimachinery v0.31.0 ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect + cloud.google.com/go/auth v0.9.3 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/ClickHouse/ch-go v0.61.5 // indirect - github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect + github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect github.com/andybalholm/brotli v1.1.0 // indirect - github.com/aws/aws-sdk-go v1.53.16 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect github.com/beevik/etree v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect @@ -117,10 +122,12 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gosimple/unidecode v1.0.0 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -128,7 +135,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect - github.com/klauspost/compress v1.17.8 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid v1.2.3 // indirect github.com/knadh/koanf/v2 v2.1.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect @@ -153,7 +160,7 @@ require ( github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect - github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_golang v1.20.4 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -181,16 +188,16 @@ require ( go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.103.0 // indirect go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.103.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect - go.opentelemetry.io/collector/semconv v0.103.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect + go.opentelemetry.io/collector/semconv v0.108.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect go.opentelemetry.io/contrib/propagators/b3 v1.27.0 // indirect go.opentelemetry.io/otel/bridge/opencensus v1.27.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.50.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect @@ -198,15 +205,16 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.11.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/time v0.5.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/time v0.6.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/api v0.195.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - k8s.io/client-go v0.29.3 // indirect - k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect + k8s.io/client-go v0.31.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect ) -replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.11.1 +replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.12.0 diff --git a/go.sum b/go.sum index 332f4507e1..0b935bba50 100644 --- a/go.sum +++ b/go.sum @@ -13,12 +13,18 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U= +cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= +cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -34,12 +40,12 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 h1:LkHbJbgF3YyvC53aqYGR+wWQDn2Rdp9AQdGndf9QvY4= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0/go.mod h1:QyiQdW4f4/BIfB8ZutZ2s+28RAgfa/pT+zS++ZHyM1I= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 h1:bXwSugBiSbgtz7rOtbfGf+woewp4f06orW9OP5BjHLA= @@ -50,8 +56,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= -github.com/ClickHouse/clickhouse-go/v2 v2.23.2 h1:+DAKPMnxLS7pduQZsrJc8OhdLS2L9MfDEJ2TS+hpYDM= -github.com/ClickHouse/clickhouse-go/v2 v2.23.2/go.mod h1:aNap51J1OM3yxQJRgM+AlP/MPkGBCL8A74uQThoQhR0= +github.com/ClickHouse/clickhouse-go/v2 v2.25.0 h1:rKscwqgQHzWBTZySZDcHKxgs0Ad+xFULfZvo26W5UlY= +github.com/ClickHouse/clickhouse-go/v2 v2.25.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys+fBkdD1UMRnXlwmhijhQ= github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU= github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= @@ -62,10 +68,10 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkbj57eGXx8H3ZJ4zhmQXBnrW523ktj8= github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc= -github.com/SigNoz/prometheus v1.11.1 h1:roM8ugYf4UxaeKKujEeBvoX7ybq3IrS+TB26KiRtIJg= -github.com/SigNoz/prometheus v1.11.1/go.mod h1:uv4mQwZQtx7y4GQ6EdHOi8Wsk07uHNn2XHd1zM85m6I= -github.com/SigNoz/signoz-otel-collector v0.102.10 h1:1zjU31OcRZL6fS0IIag8LA8bdhP4S28dzovDwuOg7Lg= -github.com/SigNoz/signoz-otel-collector v0.102.10/go.mod h1:APoBVD4aRu9vIny1vdzZSi2wPY3elyjHA/I/rh1hKfs= +github.com/SigNoz/prometheus v1.12.0 h1:+BXeIHyMOOWWa+xjhJ+x80JFva7r1WzWIfIhQ5PUmIE= +github.com/SigNoz/prometheus v1.12.0/go.mod h1:EqNM27OwmPfqMUk+E+XG1L9rfDFcyXnzzDrg0EPOfxA= +github.com/SigNoz/signoz-otel-collector v0.102.12 h1:5yY0IBtNz6SHMzKzwHmKfIx99Ij8mr72nDI2Xi08pDQ= +github.com/SigNoz/signoz-otel-collector v0.102.12/go.mod h1:tcNyU+NSn7ZkzZcLa+k+dJIPOPV+CjHn3+z1SICAfdA= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= @@ -77,8 +83,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= -github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg= +github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -93,8 +99,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/auth0/go-jwt-middleware v1.0.1 h1:/fsQ4vRr4zod1wKReUH+0A3ySRjGiT9G34kypO/EKwI= github.com/auth0/go-jwt-middleware v1.0.1/go.mod h1:YSeUX3z6+TF2H+7padiEqNJ73Zy9vXW72U//IgN0BIM= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.53.16 h1:8oZjKQO/ml1WLUZw5hvF7pvYjPf8o9f57Wldoy/q9Qc= -github.com/aws/aws-sdk-go v1.53.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= @@ -147,12 +153,12 @@ github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/digitalocean/godo v1.117.0 h1:WVlTe09melDYTd7VCVyvHcNWbgB+uI1O115+5LOtdSw= -github.com/digitalocean/godo v1.117.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo= +github.com/digitalocean/godo v1.122.0 h1:ziytLQi8QKtDp2K1A+YrYl2dWLHLh2uaMzWvcz9HkKg= +github.com/digitalocean/godo v1.122.0/go.mod h1:WQVH83OHUy6gC4gXpEVQKtxTd4L5oCp+5OialidkPLY= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v26.1.3+incompatible h1:lLCzRbrVZrljpVNobJu1J2FHk8V0s4BawoZippkc+xo= -github.com/docker/docker v26.1.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4= +github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -168,11 +174,11 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= -github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= @@ -192,6 +198,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-co-op/gocron v1.30.1 h1:tjWUvJl5KrcwpkEkSXFSQFr4F9h5SfV/m4+RX0cV2fs= github.com/go-co-op/gocron v1.30.1/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y= @@ -242,8 +250,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78 github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= -github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I= +github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -322,13 +330,17 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gophercloud/gophercloud v1.12.0 h1:Jrz16vPAL93l80q16fp8NplrTCp93y7rZh2P3Q4Yq7g= -github.com/gophercloud/gophercloud v1.12.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.14.0 h1:Bt9zQDhPrbd4qX7EILGmy+i7GP35cc+AAL2+wIJpUE8= +github.com/gophercloud/gophercloud v1.14.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= @@ -348,11 +360,11 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= -github.com/hashicorp/consul/api v1.29.1 h1:UEwOjYJrd3lG1x5w7HxDRMGiAUPrb3f103EoeKuuEcc= -github.com/hashicorp/consul/api v1.29.1/go.mod h1:lumfRkY/coLuqMICkI7Fh3ylMG31mQSRZyef2c5YvJI= +github.com/hashicorp/consul/api v1.29.4 h1:P6slzxDLBOxUSj3fWo2o65VuKtbtOXFi7TSSgtXutuE= +github.com/hashicorp/consul/api v1.29.4/go.mod h1:HUlfw+l2Zy68ceJavv2zAyArl2fqhGWnMycyt56sBgg= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= @@ -400,8 +412,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/nomad/api v0.0.0-20240604134157-e73d8bb1140d h1:KHq+mAzWSkumj4PDoXc5VZbycPGcmYu8tohgVLQ6SIc= -github.com/hashicorp/nomad/api v0.0.0-20240604134157-e73d8bb1140d/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= +github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3 h1:fgVfQ4AC1avVOnu2cfms8VAiD8lUq3vWI8mTocOXN/w= +github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= @@ -409,8 +421,8 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hetznercloud/hcloud-go/v2 v2.9.0 h1:s0N6R7Zoi2DPfMtUF5o9VeUBzTtHVY6MIkHOQnfu/AY= -github.com/hetznercloud/hcloud-go/v2 v2.9.0/go.mod h1:qtW/TuU7Bs16ibXl/ktJarWqU2LwHr7eGlwoilHxtgg= +github.com/hetznercloud/hcloud-go/v2 v2.13.1 h1:jq0GP4QaYE5d8xR/Zw17s9qoaESRJMXfGmtD1a/qckQ= +github.com/hetznercloud/hcloud-go/v2 v2.13.1/go.mod h1:dhix40Br3fDiBhwaSG/zgaYOFFddpfBm/6R1Zz0IiF0= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -420,8 +432,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/ionos-cloud/sdk-go/v6 v6.1.11 h1:J/uRN4UWO3wCyGOeDdMKv8LWRzKu6UIkLEaes38Kzh8= -github.com/ionos-cloud/sdk-go/v6 v6.1.11/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/ionos-cloud/sdk-go/v6 v6.2.1 h1:mxxN+frNVmbFrmmFfXnBC3g2USYJrl6mc1LW2iNYbFY= +github.com/ionos-cloud/sdk-go/v6 v6.2.1/go.mod h1:SXrO9OGyWjd2rZhAhEpdYN6VUAODzzqRdqA9BCviQtI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -452,8 +464,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= @@ -484,8 +496,8 @@ github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b/go.mod h1: github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linode/linodego v1.35.0 h1:rIhUeCHBLEDlkoRnOTwzSGzljQ3ksXwLxacmXnrV+Do= -github.com/linode/linodego v1.35.0/go.mod h1:JxuhOEAMfSxun6RU5/MgTKH2GGTmFrhKRj3wL1NFin0= +github.com/linode/linodego v1.40.0 h1:7ESY0PwK94hoggoCtIroT1Xk6b1flrFBNZ6KwqbTqlI= +github.com/linode/linodego v1.40.0/go.mod h1:NsUw4l8QrLdIofRg1NYFBbW5ZERnmbZykVBszPZLORM= github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -510,8 +522,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/minio-go/v6 v6.0.57 h1:ixPkbKkyD7IhnluRgQpGSpHdpvNVaW6OD5R9IAO/9Tw= @@ -571,9 +583,8 @@ github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g= github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M= github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.102.0 h1:7QHxeMnKzMXMw9oh5lnOHakfPpGSglxiZfbYUn6l6yc= @@ -596,8 +607,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/ovh/go-ovh v1.5.1 h1:P8O+7H+NQuFK9P/j4sFW5C0fvSS2DnHYGPwdVCp45wI= -github.com/ovh/go-ovh v1.5.1/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= +github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI= +github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -618,6 +629,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -630,8 +643,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -642,8 +655,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= +github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -674,8 +687,8 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 h1:yGAraK1uUjlhSXgNMIy8o/J4LFNcy7yeipBqt9N9mVg= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30/go.mod h1:sH0u6fq6x4R5M7WxkoQFY/o7UaiItec0o1LinLCJNq8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= @@ -732,6 +745,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -747,6 +761,8 @@ github.com/vjeantet/grok v1.0.1 h1:2rhIR7J4gThTgcZ1m2JY4TrJZNgjn985U28kT2wQrJ4= github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= @@ -819,30 +835,30 @@ go.opentelemetry.io/collector/featuregate v1.13.0 h1:rc84eCf5hesXQ8/bP6Zc15wqthb go.opentelemetry.io/collector/featuregate v1.13.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= go.opentelemetry.io/collector/otelcol v0.103.0 h1:Skqnc2mxDdk3eiYioUuG7ST6ur5k83SOv7mIBt60fBw= go.opentelemetry.io/collector/otelcol v0.103.0/go.mod h1:iJF3ghCv+nRZI6+hI7z3kGRZrgH///Fd9tNXY82X90g= -go.opentelemetry.io/collector/pdata v1.10.0 h1:oLyPLGvPTQrcRT64ZVruwvmH/u3SHTfNo01pteS4WOE= -go.opentelemetry.io/collector/pdata v1.10.0/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= +go.opentelemetry.io/collector/pdata v1.14.1 h1:wXZjtQA7Vy5HFqco+yA95ENyMQU5heBB1IxMHQf6mUk= +go.opentelemetry.io/collector/pdata v1.14.1/go.mod h1:z1dTjwwtcoXxZx2/nkHysjxMeaxe9pEmYTEr4SMNIx8= go.opentelemetry.io/collector/pdata/testdata v0.103.0 h1:iI6NOE0L2je/bxlWzAWHQ/yCtnGupgv42Hl9Al1q/g4= go.opentelemetry.io/collector/pdata/testdata v0.103.0/go.mod h1:tLzRhb/h37/9wFRQVr+CxjKi5qmhSRpCAiOlhwRkeEk= go.opentelemetry.io/collector/processor v0.103.0 h1:YZ+LRuHKtOam7SCeLkJAP6bS1d6XxeYP22OyMN3VP0s= go.opentelemetry.io/collector/processor v0.103.0/go.mod h1:/mxyh0NpJgpZycm7iHDpM7i5PdtWvKKdCZf0cyADJfU= go.opentelemetry.io/collector/receiver v0.103.0 h1:V3JBKkX+7e/NYpDDZVyeu2VQB1/lLFuoJFPfupdCcZs= go.opentelemetry.io/collector/receiver v0.103.0/go.mod h1:Yybv4ynKFdMOYViWWPMmjkugR89FSQN0P37wP6mX6qM= -go.opentelemetry.io/collector/semconv v0.103.0 h1:5tlVoZlo9USHAU2Bz4YrEste0Vm5AMufXkYJhAVve1Q= -go.opentelemetry.io/collector/semconv v0.103.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= +go.opentelemetry.io/collector/semconv v0.108.1 h1:Txk9tauUnamZaxS5vlf1O0uZ4VD6nioRBR0nX8L/fU4= +go.opentelemetry.io/collector/semconv v0.108.1/go.mod h1:zCJ5njhWpejR+A40kiEoeFm1xq1uzyZwMnRNX6/D82A= go.opentelemetry.io/collector/service v0.103.0 h1:e4Eri4jo+YOuEK0+/JE9SUdT/NZaJ2jz/ROJlmLn96s= go.opentelemetry.io/collector/service v0.103.0/go.mod h1:p1mlniiC1MuPN5FANYJYgf5V5CGFP0hNqWfI8t7Aw8M= go.opentelemetry.io/contrib/bridges/otelzap v0.0.0-20240820072021-3fab5f5f20fb h1:ZqncifxU0B1q64FRbhKxsJugRsrEToakmYUsgQ5tGbY= go.opentelemetry.io/contrib/bridges/otelzap v0.0.0-20240820072021-3fab5f5f20fb/go.mod h1:mzv0k5dTnSUE5/ZerXUwGiNKzcPJTakuCh6Wm1emNvU= go.opentelemetry.io/contrib/config v0.8.0 h1:OD7aDMhL+2EpzdSHfkDmcdD/uUA+PgKM5faFyF9XFT0= go.opentelemetry.io/contrib/config v0.8.0/go.mod h1:dGeVZWE//3wrxYHHP0iCBYJU1QmOmPcbV+FNB7pjDYI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= go.opentelemetry.io/contrib/zpages v0.52.0 h1:MPgkMy0Cp3O5EdfVXP0ss3ujhEibysTM4eszx7E7d+E= go.opentelemetry.io/contrib/zpages v0.52.0/go.mod h1:fqG5AFdoYru3A3DnhibVuaaEfQV2WKxE7fYE1jgDRwk= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs= go.opentelemetry.io/otel/bridge/opencensus v1.27.0/go.mod h1:uRvWtAAXzyVOST0WMPX5JHGBaAvBws+2F8PcC5gMnTk= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.4.0 h1:zBPZAISA9NOc5cE8zydqDiS0itvg/P/0Hn9m72a5gvM= @@ -851,12 +867,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2g go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 h1:dIIDULZJpgdiHz5tXrTgKIMLkus6jEFa7x5SOKcyR7E= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0/go.mod h1:jlRVBe7+Z1wyxFSUs48L6OBQZ5JwH2Hg/Vbl+t9rAgI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0 h1:nSiV3s7wiCam610XcLbYOmMfJxB9gO4uK3Xgv5gmTgg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.29.0/go.mod h1:hKn/e/Nmd19/x1gvIHwtOwVWM+VhuITSWip3JUDghj0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0 h1:JAv0Jwtl01UFiyWZEMiJZBiTlv5A50zNs8lsthXqIio= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.29.0/go.mod h1:QNKLmUEAq2QUbPQUfvw4fmv0bgbK7UlOSFCnXyfvSNc= go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng= go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9lc7aFmBwkWxY/RI1RDdXepSF6y8TPE= @@ -865,16 +881,16 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bE go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= go.opentelemetry.io/otel/log v0.4.0 h1:/vZ+3Utqh18e8TPjuc3ecg284078KWrR8BRz+PQAj3o= go.opentelemetry.io/otel/log v0.4.0/go.mod h1:DhGnQvky7pHy82MIRV43iXh3FlKN8UUKftn0KbLOq6I= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= +go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= go.opentelemetry.io/otel/sdk/log v0.4.0 h1:1mMI22L82zLqf6KtkjrRy5BbagOTWdJsqMY/HSqILAA= go.opentelemetry.io/otel/sdk/log v0.4.0/go.mod h1:AYJ9FVF0hNOgAVzUG/ybg/QttnXhUePWAupmCqtdESo= go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -898,8 +914,8 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -934,8 +950,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -977,16 +993,16 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -998,8 +1014,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1063,11 +1079,11 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1077,13 +1093,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1130,8 +1146,8 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1154,6 +1170,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU= +google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1192,10 +1210,10 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= +google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1213,8 +1231,8 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1269,18 +1287,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= -k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= -k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= -k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= -k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= -k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= -k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= -k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= +k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= +k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= +k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= -k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/query-service/README.md b/pkg/query-service/README.md index 83925eece7..0087230d8d 100644 --- a/pkg/query-service/README.md +++ b/pkg/query-service/README.md @@ -13,7 +13,7 @@ https://github.com/SigNoz/signoz/blob/main/CONTRIBUTING.md#to-run-clickhouse-set - Change the alertmanager section in `signoz/deploy/docker/clickhouse-setup/docker-compose.yaml` as follows: ```console alertmanager: - image: signoz/alertmanager:0.23.5 + image: signoz/alertmanager:0.23.7 volumes: - ./data/alertmanager:/data expose: diff --git a/pkg/query-service/app/clickhouseReader/filter_suggestions.go b/pkg/query-service/app/clickhouseReader/filter_suggestions.go index 1b80ccbef4..fe78fc5555 100644 --- a/pkg/query-service/app/clickhouseReader/filter_suggestions.go +++ b/pkg/query-service/app/clickhouseReader/filter_suggestions.go @@ -8,6 +8,7 @@ import ( "slices" "strings" + "github.com/SigNoz/signoz-otel-collector/exporter/clickhouselogsexporter/logsv2" "go.signoz.io/signoz/pkg/query-service/model" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" "go.uber.org/zap" @@ -36,26 +37,7 @@ func (r *ClickHouseReader) GetQBFilterSuggestionsForLogs( suggestions.AttributeKeys = attribKeysResp.AttributeKeys // Rank suggested attributes - slices.SortFunc(suggestions.AttributeKeys, func(a v3.AttributeKey, b v3.AttributeKey) int { - - // Higher score => higher rank - attribKeyScore := func(a v3.AttributeKey) int { - - // Scoring criteria is expected to get more sophisticated in follow up changes - if a.Type == v3.AttributeKeyTypeResource { - return 2 - } - - if a.Type == v3.AttributeKeyTypeTag { - return 1 - } - - return 0 - } - - // To sort in descending order of score the return value must be negative when a > b - return attribKeyScore(b) - attribKeyScore(a) - }) + attribRanker.sort(suggestions.AttributeKeys) // Put together suggested example queries. @@ -268,3 +250,59 @@ func (r *ClickHouseReader) getValuesForLogAttributes( return result, nil } + +var attribRanker = newRankingStrategy() + +func newRankingStrategy() attribRankingStrategy { + // Some special resource attributes should get ranked above all others. + interestingResourceAttrsInDescRank := []string{ + "service", "service.name", "env", "k8s.namespace.name", + } + + // Synonyms of interesting attributes should come next + resourceHierarchy := logsv2.ResourceHierarchy() + for _, attr := range []string{ + "service.name", + "deployment.environment", + "k8s.namespace.name", + "k8s.pod.name", + "k8s.container.name", + "k8s.node.name", + } { + interestingResourceAttrsInDescRank = append( + interestingResourceAttrsInDescRank, resourceHierarchy.Synonyms(attr)..., + ) + } + + interestingResourceAttrsInAscRank := interestingResourceAttrsInDescRank[:] + slices.Reverse(interestingResourceAttrsInAscRank) + + return attribRankingStrategy{ + interestingResourceAttrsInAscRank: interestingResourceAttrsInAscRank, + } +} + +type attribRankingStrategy struct { + interestingResourceAttrsInAscRank []string +} + +// The higher the score, the higher the rank +func (s *attribRankingStrategy) score(attrib v3.AttributeKey) int { + if attrib.Type == v3.AttributeKeyTypeResource { + // 3 + (-1) if attrib.Key is not an interesting resource attribute + return 3 + slices.Index(s.interestingResourceAttrsInAscRank, attrib.Key) + } + + if attrib.Type == v3.AttributeKeyTypeTag { + return 1 + } + + return 0 +} + +func (s *attribRankingStrategy) sort(attribKeys []v3.AttributeKey) { + slices.SortFunc(attribKeys, func(a v3.AttributeKey, b v3.AttributeKey) int { + // To sort in descending order of score the return value must be negative when a > b + return s.score(b) - s.score(a) + }) +} diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index b3aee7ca60..18078c0931 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -53,21 +53,31 @@ import ( ) const ( - primaryNamespace = "clickhouse" - archiveNamespace = "clickhouse-archive" - signozTraceDBName = "signoz_traces" - signozHistoryDBName = "signoz_analytics" - ruleStateHistoryTableName = "distributed_rule_state_history_v0" - signozDurationMVTable = "distributed_durationSort" - signozUsageExplorerTable = "distributed_usage_explorer" - signozSpansTable = "distributed_signoz_spans" - signozErrorIndexTable = "distributed_signoz_error_index_v2" - signozTraceTableName = "distributed_signoz_index_v2" - signozTraceLocalTableName = "signoz_index_v2" - signozMetricDBName = "signoz_metrics" + primaryNamespace = "clickhouse" + archiveNamespace = "clickhouse-archive" + signozTraceDBName = "signoz_traces" + signozHistoryDBName = "signoz_analytics" + ruleStateHistoryTableName = "distributed_rule_state_history_v0" + signozDurationMVTable = "distributed_durationSort" + signozUsageExplorerTable = "distributed_usage_explorer" + signozSpansTable = "distributed_signoz_spans" + signozErrorIndexTable = "distributed_signoz_error_index_v2" + signozTraceTableName = "distributed_signoz_index_v2" + signozTraceLocalTableName = "signoz_index_v2" + signozMetricDBName = "signoz_metrics" + signozSampleLocalTableName = "samples_v4" signozSampleTableName = "distributed_samples_v4" + signozSamplesAgg5mLocalTableName = "samples_v4_agg_5m" + signozSamplesAgg5mTableName = "distributed_samples_v4_agg_5m" + + signozSamplesAgg30mLocalTableName = "samples_v4_agg_30m" + signozSamplesAgg30mTableName = "distributed_samples_v4_agg_30m" + + signozExpHistLocalTableName = "exp_hist" + signozExpHistTableName = "distributed_exp_hist" + signozTSLocalTableNameV4 = "time_series_v4" signozTSTableNameV4 = "distributed_time_series_v4" @@ -77,6 +87,9 @@ const ( signozTSLocalTableNameV41Day = "time_series_v4_1day" signozTSTableNameV41Day = "distributed_time_series_v4_1day" + signozTSLocalTableNameV41Week = "time_series_v4_1week" + signozTSTableNameV41Week = "distributed_time_series_v4_1week" + minTimespanForProgressiveSearch = time.Hour minTimespanForProgressiveSearchMargin = time.Minute maxProgressiveSteps = 4 @@ -267,6 +280,7 @@ func (r *ClickHouseReader) Start(readerReady chan bool) { "", time.Duration(1*time.Minute), nil, + false, ) cfg := struct { @@ -2115,8 +2129,15 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, switch params.Type { case constants.TraceTTL: - tableNameArray := []string{signozTraceDBName + "." + signozTraceTableName, signozTraceDBName + "." + signozDurationMVTable, signozTraceDBName + "." + signozSpansTable, signozTraceDBName + "." + signozErrorIndexTable, signozTraceDBName + "." + signozUsageExplorerTable, signozTraceDBName + "." + defaultDependencyGraphTable} - for _, tableName := range tableNameArray { + tableNames := []string{ + signozTraceDBName + "." + signozTraceTableName, + signozTraceDBName + "." + signozDurationMVTable, + signozTraceDBName + "." + signozSpansTable, + signozTraceDBName + "." + signozErrorIndexTable, + signozTraceDBName + "." + signozUsageExplorerTable, + signozTraceDBName + "." + defaultDependencyGraphTable, + } + for _, tableName := range tableNames { tableName := getLocalTableName(tableName) statusItem, err := r.checkTTLStatusItem(ctx, tableName) if err != nil { @@ -2126,7 +2147,7 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, return nil, &model.ApiError{Typ: model.ErrorConflict, Err: fmt.Errorf("TTL is already running")} } } - for _, tableName := range tableNameArray { + for _, tableName := range tableNames { tableName := getLocalTableName(tableName) // TODO: DB queries should be implemented with transactional statements but currently clickhouse doesn't support them. Issue: https://github.com/ClickHouse/ClickHouse/issues/22086 go func(tableName string) { @@ -2155,7 +2176,7 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, } return } - req += " SETTINGS distributed_ddl_task_timeout = -1;" + req += " SETTINGS materialize_ttl_after_modify=0;" zap.L().Error("Executing TTL request: ", zap.String("request", req)) statusItem, _ := r.checkTTLStatusItem(ctx, tableName) if err := r.db.Exec(context.Background(), req); err != nil { @@ -2176,7 +2197,16 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, } case constants.MetricsTTL: - tableNames := []string{signozMetricDBName + "." + signozSampleLocalTableName, signozMetricDBName + "." + signozTSLocalTableNameV4, signozMetricDBName + "." + signozTSLocalTableNameV46Hrs, signozMetricDBName + "." + signozTSLocalTableNameV41Day} + tableNames := []string{ + signozMetricDBName + "." + signozSampleLocalTableName, + signozMetricDBName + "." + signozSamplesAgg5mLocalTableName, + signozMetricDBName + "." + signozSamplesAgg30mLocalTableName, + signozMetricDBName + "." + signozExpHistLocalTableName, + signozMetricDBName + "." + signozTSLocalTableNameV4, + signozMetricDBName + "." + signozTSLocalTableNameV46Hrs, + signozMetricDBName + "." + signozTSLocalTableNameV41Day, + signozMetricDBName + "." + signozTSLocalTableNameV41Week, + } for _, tableName := range tableNames { statusItem, err := r.checkTTLStatusItem(ctx, tableName) if err != nil { @@ -2193,7 +2223,7 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, return } timeColumn := "timestamp_ms" - if strings.Contains(tableName, "v4") { + if strings.Contains(tableName, "v4") || strings.Contains(tableName, "exp_hist") { timeColumn = "unix_milli" } @@ -2218,7 +2248,7 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, } return } - req += " SETTINGS distributed_ddl_task_timeout = -1" + req += " SETTINGS materialize_ttl_after_modify=0" zap.L().Info("Executing TTL request: ", zap.String("request", req)) statusItem, _ := r.checkTTLStatusItem(ctx, tableName) if err := r.db.Exec(ctx, req); err != nil { @@ -2279,7 +2309,7 @@ func (r *ClickHouseReader) SetTTL(ctx context.Context, } return } - req += " SETTINGS distributed_ddl_task_timeout = -1" + req += " SETTINGS materialize_ttl_after_modify=0" zap.L().Info("Executing TTL request: ", zap.String("request", req)) statusItem, _ := r.checkTTLStatusItem(ctx, tableName) if err := r.db.Exec(ctx, req); err != nil { diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 5055913113..7078566eca 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -11,6 +11,7 @@ import ( "net/http" "regexp" "slices" + "sort" "strconv" "strings" "sync" @@ -2463,12 +2464,19 @@ func (aH *APIHandler) WriteJSON(w http.ResponseWriter, r *http.Request, response // RegisterMessagingQueuesRoutes adds messaging-queues routes func (aH *APIHandler) RegisterMessagingQueuesRoutes(router *mux.Router, am *AuthMiddleware) { - // SubRouter for kafka - kafkaSubRouter := router.PathPrefix("/api/v1/messaging-queues/kafka/consumer-lag").Subrouter() - kafkaSubRouter.HandleFunc("/producer-details", am.ViewAccess(aH.getProducerData)).Methods(http.MethodPost) - kafkaSubRouter.HandleFunc("/consumer-details", am.ViewAccess(aH.getConsumerData)).Methods(http.MethodPost) - kafkaSubRouter.HandleFunc("/network-latency", am.ViewAccess(aH.getNetworkData)).Methods(http.MethodPost) + // SubRouter for kafka + kafkaRouter := router.PathPrefix("/api/v1/messaging-queues/kafka").Subrouter() + + consumerLagRouter := kafkaRouter.PathPrefix("/consumer-lag").Subrouter() + consumerLagRouter.HandleFunc("/producer-details", am.ViewAccess(aH.getProducerData)).Methods(http.MethodPost) + consumerLagRouter.HandleFunc("/consumer-details", am.ViewAccess(aH.getConsumerData)).Methods(http.MethodPost) + consumerLagRouter.HandleFunc("/network-latency", am.ViewAccess(aH.getNetworkData)).Methods(http.MethodPost) + + onboardingRouter := kafkaRouter.PathPrefix("/onboarding").Subrouter() + onboardingRouter.HandleFunc("/producers", am.ViewAccess(aH.onboardProducers)).Methods(http.MethodPost) + onboardingRouter.HandleFunc("/consumers", am.ViewAccess(aH.onboardConsumers)).Methods(http.MethodPost) + onboardingRouter.HandleFunc("/kafka", am.ViewAccess(aH.onboardKafka)).Methods(http.MethodPost) // for other messaging queues, add SubRouters here } @@ -2478,6 +2486,332 @@ func uniqueIdentifier(clientID, serviceInstanceID, serviceName, separator string return clientID + separator + serviceInstanceID + separator + serviceName } +func (aH *APIHandler) onboardProducers( + + w http.ResponseWriter, r *http.Request, + +) { + messagingQueue, apiErr := ParseMessagingQueueBody(r) + if apiErr != nil { + zap.L().Error(apiErr.Err.Error()) + RespondError(w, apiErr, nil) + return + } + + chq, err := mq.BuildClickHouseQuery(messagingQueue, mq.KafkaQueue, "onboard_producers") + + if err != nil { + zap.L().Error(err.Error()) + RespondError(w, apiErr, nil) + return + } + + results, err := aH.reader.GetListResultV3(r.Context(), chq.Query) + + if err != nil { + apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err} + RespondError(w, apiErrObj, err) + return + } + + var entries []mq.OnboardingResponse + + for _, result := range results { + + for key, value := range result.Data { + var message, attribute, status string + + intValue := int(*value.(*uint8)) + + if key == "entries" { + attribute = "telemetry ingestion" + if intValue != 0 { + entries = nil + entry := mq.OnboardingResponse{ + Attribute: attribute, + Message: "No data available in the given time range", + Status: "0", + } + entries = append(entries, entry) + break + } else { + status = "1" + } + } else if key == "queue" { + attribute = "messaging.system" + if intValue != 0 { + status = "0" + message = "messaging.system attribute is not present or not equal to kafka in your spans" + } else { + status = "1" + } + } else if key == "kind" { + attribute = "kind" + if intValue != 0 { + status = "0" + message = "check if your producer spans has kind=4 as attribute" + } else { + status = "1" + } + } else if key == "destination" { + attribute = "messaging.destination.name" + if intValue != 0 { + status = "0" + message = "messaging.destination.name attribute is not present in your spans" + } else { + status = "1" + } + } else if key == "partition" { + attribute = "messaging.destination.partition.id" + if intValue != 0 { + status = "0" + message = "messaging.destination.partition.id attribute is not present in your spans" + } else { + status = "1" + } + } + + entry := mq.OnboardingResponse{ + Attribute: attribute, + Message: message, + Status: status, + } + + entries = append(entries, entry) + } + } + + sort.Slice(entries, func(i, j int) bool { + return entries[i].Attribute < entries[j].Attribute + }) + + aH.Respond(w, entries) +} + +func (aH *APIHandler) onboardConsumers( + + w http.ResponseWriter, r *http.Request, + +) { + messagingQueue, apiErr := ParseMessagingQueueBody(r) + if apiErr != nil { + zap.L().Error(apiErr.Err.Error()) + RespondError(w, apiErr, nil) + return + } + + chq, err := mq.BuildClickHouseQuery(messagingQueue, mq.KafkaQueue, "onboard_consumers") + + if err != nil { + zap.L().Error(err.Error()) + RespondError(w, apiErr, nil) + return + } + + result, err := aH.reader.GetListResultV3(r.Context(), chq.Query) + + if err != nil { + apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err} + RespondError(w, apiErrObj, err) + return + } + + var entries []mq.OnboardingResponse + + for _, result := range result { + for key, value := range result.Data { + var message, attribute, status string + + intValue := int(*value.(*uint8)) + + if key == "entries" { + attribute = "telemetry ingestion" + if intValue != 0 { + entries = nil + entry := mq.OnboardingResponse{ + Attribute: attribute, + Message: "No data available in the given time range", + Status: "0", + } + entries = append(entries, entry) + break + } else { + status = "1" + } + } else if key == "queue" { + attribute = "messaging.system" + if intValue != 0 { + status = "0" + message = "messaging.system attribute is not present or not equal to kafka in your spans" + } else { + status = "1" + } + } else if key == "kind" { + attribute = "kind" + if intValue != 0 { + status = "0" + message = "check if your consumer spans has kind=5 as attribute" + } else { + status = "1" + } + } else if key == "destination" { + attribute = "messaging.destination.name" + if intValue != 0 { + status = "0" + message = "messaging.destination.name attribute is not present in your spans" + } else { + status = "1" + } + } else if key == "partition" { + attribute = "messaging.destination.partition.id" + if intValue != 0 { + status = "0" + message = "messaging.destination.partition.id attribute is not present in your spans" + } else { + status = "1" + } + } else if key == "svc" { + attribute = "service_name" + if intValue != 0 { + status = "0" + message = "service_name attribute is not present in your spans" + } else { + status = "1" + } + } else if key == "cgroup" { + attribute = "messaging.kafka.consumer.group" + if intValue != 0 { + status = "0" + message = "messaging.kafka.consumer.group attribute is not present in your spans" + } else { + status = "1" + } + } else if key == "bodysize" { + attribute = "messaging.message.body.size" + if intValue != 0 { + status = "0" + message = "messaging.message.body.size attribute is not present in your spans" + } else { + status = "1" + } + } else if key == "clientid" { + attribute = "messaging.client_id" + if intValue != 0 { + status = "0" + message = "messaging.client_id attribute is not present in your spans" + } else { + status = "1" + } + } else if key == "instanceid" { + attribute = "service.instance.id" + if intValue != 0 { + status = "0" + message = "service.instance.id attribute is not present in your spans" + } else { + status = "1" + } + } + + entry := mq.OnboardingResponse{ + Attribute: attribute, + Message: message, + Status: status, + } + entries = append(entries, entry) + } + } + + sort.Slice(entries, func(i, j int) bool { + return entries[i].Attribute < entries[j].Attribute + }) + + aH.Respond(w, entries) +} + +func (aH *APIHandler) onboardKafka( + + w http.ResponseWriter, r *http.Request, + +) { + messagingQueue, apiErr := ParseMessagingQueueBody(r) + if apiErr != nil { + zap.L().Error(apiErr.Err.Error()) + RespondError(w, apiErr, nil) + return + } + + chq, err := mq.BuildClickHouseQuery(messagingQueue, mq.KafkaQueue, "onboard_kafka") + + if err != nil { + zap.L().Error(err.Error()) + RespondError(w, apiErr, nil) + return + } + + result, err := aH.reader.GetListResultV3(r.Context(), chq.Query) + + if err != nil { + apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err} + RespondError(w, apiErrObj, err) + return + } + + var entries []mq.OnboardingResponse + + for _, result := range result { + for key, value := range result.Data { + var message, attribute, status string + + intValue := int(*value.(*uint8)) + + if key == "entries" { + attribute = "telemetry ingestion" + if intValue != 0 { + entries = nil + entry := mq.OnboardingResponse{ + Attribute: attribute, + Message: "No data available in the given time range", + Status: "0", + } + entries = append(entries, entry) + break + } else { + status = "1" + } + } else if key == "fetchlatency" { + attribute = "kafka_consumer_fetch_latency_avg" + if intValue != 0 { + status = "0" + message = "Metric kafka_consumer_fetch_latency_avg is not present in the given time range." + } else { + status = "1" + } + } else if key == "grouplag" { + attribute = "kafka_consumer_group_lag" + if intValue != 0 { + status = "0" + message = "Metric kafka_consumer_group_lag is not present in the given time range." + } else { + status = "1" + } + } + + entry := mq.OnboardingResponse{ + Attribute: attribute, + Message: message, + Status: status, + } + entries = append(entries, entry) + } + } + + sort.Slice(entries, func(i, j int) bool { + return entries[i].Attribute < entries[j].Attribute + }) + + aH.Respond(w, entries) +} + func (aH *APIHandler) getNetworkData( w http.ResponseWriter, r *http.Request, ) { diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md index 38b61669ff..d338a9acb9 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md @@ -226,3 +226,296 @@ Response in query range `table` format } } ``` + +### Onboarding APIs + +``` +/api/v1/messaging-queues/kafka/onboarding/producers +``` + +```json +{ + "start": 1727620544260611424, + "end": 1727620556401481428 +} +``` + +```json +// everything is present +{ + "status": "success", + "data": [ + { + "attribute": "kind", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.destination.name", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.destination.partition.id", + "error_message": "messaging.destination.partition.id attribute is not present in your spans", + "status": "0" + }, + { + "attribute": "messaging.system", + "error_message": "", + "status": "1" + }, + { + "attribute": "telemetry ingestion", + "error_message": "", + "status": "1" + } + ] +} + +// partial attributes are present +{ + "status": "success", + "data": [ + { + "attribute": "kind", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.destination.name", + "error_message": "messaging.destination.name attribute is not present in your spans", + "status": "0" + }, + { + "attribute": "messaging.destination.partition.id", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.system", + "error_message": "", + "status": "1" + }, + { + "attribute": "telemetry ingestion", + "error_message": "", + "status": "1" + } + ] +} + +// no data available +{ + "status": "success", + "data": [ + { + "attribute": "telemetry ingestion", + "error_message": "No data available in the given time range", + "status": "0" + } + ] +} +``` + + +``` +/api/v1/messaging-queues/kafka/onboarding/consumers +``` + +```json +// everything is present +{ + "status": "success", + "data": [ + { + "attribute": "kind", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.client_id", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.destination.name", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.destination.partition.id", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.kafka.consumer.group", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.message.body.size", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.system", + "error_message": "", + "status": "1" + }, + { + "attribute": "service.instance.id", + "error_message": "", + "status": "1" + }, + { + "attribute": "service_name", + "error_message": "", + "status": "1" + }, + { + "attribute": "telemetry ingestion", + "error_message": "", + "status": "1" + } + ] +} + +// partial attributes are present +{ + "status": "success", + "data": [ + { + "attribute": "kind", + "error_message": "check if your consumer spans has kind=5 as attribute", + "status": "0" + }, + { + "attribute": "messaging.client_id", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.destination.name", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.destination.partition.id", + "error_message": "", + "status": "1" + }, + { + "attribute": "messaging.kafka.consumer.group", + "error_message": "messaging.kafka.consumer.group attribute is not present in your spans", + "status": "0" + }, + { + "attribute": "messaging.message.body.size", + "error_message": "messaging.message.body.size attribute is not present in your spans", + "status": "0" + }, + { + "attribute": "messaging.system", + "error_message": "", + "status": "1" + }, + { + "attribute": "service.instance.id", + "error_message": "", + "status": "1" + }, + { + "attribute": "service_name", + "error_message": "", + "status": "1" + }, + { + "attribute": "telemetry ingestion", + "error_message": "", + "status": "1" + } + ] +} + +// no data available +{ + "status": "success", + "data": [ + { + "attribute": "telemetry ingestion", + "error_message": "No data available in the given time range", + "status": "0" + } + ] +} +``` + +``` +/api/v1/messaging-queues/kafka/onboarding/kafka +``` + +```json +{ + "start": 1728485200000000000, + "end": 1728749800000000000 +} +``` + +```json +// everything is present +{ + "status": "success", + "data": [ + { + "attribute": "telemetry ingestion", + "error_message": "", + "status": "1" + }, + { + "attribute": "kafka_consumer_fetch_latency_avg", + "error_message": "Metric kafka_consumer_fetch_latency_avg is not present in the given time range.", + "status": "0" + }, + { + "attribute": "kafka_consumer_group_lag", + "error_message": "Metric kafka_consumer_group_lag is not present in the given time range.", + "status": "0" + } + ] +} + +// partial attributes are present +{ + "status": "success", + "data": [ + { + "attribute": "telemetry ingestion", + "error_message": "", + "status": "1" + }, + { + "attribute": "kafka_consumer_fetch_latency_avg", + "error_message": "", + "status": "1" + }, + { + "attribute": "kafka_consumer_group_lag", + "error_message": "", + "status": "1" + } + ] +} + +// no data available +{ + "status": "success", + "data": [ + { + "attribute": "telemetry ingestion", + "error_message": "No data available in the given time range", + "status": "0" + } + ] +} +``` diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/model.go b/pkg/query-service/app/integrations/messagingQueues/kafka/model.go index f587868610..803912c17f 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/model.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/model.go @@ -1,6 +1,6 @@ package kafka -const kafkaQueue = "kafka" +const KafkaQueue = "kafka" type MessagingQueue struct { Start int64 `json:"start"` @@ -14,3 +14,9 @@ type Clients struct { ServiceInstanceID []string ServiceName []string } + +type OnboardingResponse struct { + Attribute string `json:"attribute"` + Message string `json:"error_message"` + Status string `json:"status"` +} diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go index eb06689ef3..a4d3191fe1 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go @@ -95,3 +95,54 @@ ORDER BY throughput DESC `, timeRange, start, end, queueType, consumerGroup, partitionID) return query } + +func onboardProducersSQL(start, end int64, queueType string) string { + query := fmt.Sprintf(` +SELECT + COUNT(*) = 0 AS entries, + COUNT(IF(msgSystem = '%s', 1, NULL)) = 0 AS queue, + COUNT(IF(kind = 4, 1, NULL)) = 0 AS kind, + COUNT(IF(has(stringTagMap, 'messaging.destination.name'), 1, NULL)) = 0 AS destination, + COUNT(IF(has(stringTagMap, 'messaging.destination.partition.id'), 1, NULL)) = 0 AS partition +FROM + signoz_traces.distributed_signoz_index_v2 +WHERE + timestamp >= '%d' + AND timestamp <= '%d';`, queueType, start, end) + return query +} + +func onboardConsumerSQL(start, end int64, queueType string) string { + query := fmt.Sprintf(` +SELECT + COUNT(*) = 0 AS entries, + COUNT(IF(msgSystem = '%s', 1, NULL)) = 0 AS queue, + COUNT(IF(kind = 5, 1, NULL)) = 0 AS kind, + COUNT(serviceName) = 0 AS svc, + COUNT(IF(has(stringTagMap, 'messaging.destination.name'), 1, NULL)) = 0 AS destination, + COUNT(IF(has(stringTagMap, 'messaging.destination.partition.id'), 1, NULL)) = 0 AS partition, + COUNT(IF(has(stringTagMap, 'messaging.kafka.consumer.group'), 1, NULL)) = 0 AS cgroup, + COUNT(IF(has(numberTagMap, 'messaging.message.body.size'), 1, NULL)) = 0 AS bodysize, + COUNT(IF(has(stringTagMap, 'messaging.client_id'), 1, NULL)) = 0 AS clientid, + COUNT(IF(has(stringTagMap, 'service.instance.id'), 1, NULL)) = 0 AS instanceid +FROM signoz_traces.distributed_signoz_index_v2 +WHERE + timestamp >= '%d' + AND timestamp <= '%d';`, queueType, start, end) + return query +} + +func onboardKafkaSQL(start, end int64) string { + query := fmt.Sprintf(` +SELECT + COUNT(*) = 0 AS entries, + COUNT(IF(metric_name = 'kafka_consumer_fetch_latency_avg', 1, NULL)) = 0 AS fetchlatency, + COUNT(IF(metric_name = 'kafka_consumer_group_lag', 1, NULL)) = 0 AS grouplag +FROM + signoz_metrics.time_series_v4_1day +WHERE + metric_name IN ('kafka_consumer_fetch_latency_avg', 'kafka_consumer_group_lag') + AND unix_milli >= '%d' + AND unix_milli < '%d';`, start/1000000, end/1000000) + return query +} diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go index 7f4266df67..4dca2a2cda 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go @@ -13,16 +13,16 @@ var defaultStepInterval int64 = 60 func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) (*v3.QueryRangeParamsV3, error) { // ToDo: propagate this through APIs when there are different handlers - queueType := kafkaQueue + queueType := KafkaQueue - var cq *v3.CompositeQuery - - chq, err := buildClickHouseQuery(messagingQueue, queueType, queryContext) + chq, err := BuildClickHouseQuery(messagingQueue, queueType, queryContext) if err != nil { return nil, err } + var cq *v3.CompositeQuery + cq, err = buildCompositeQuery(chq, queryContext) queryRangeParams := &v3.QueryRangeParamsV3{ @@ -37,6 +37,14 @@ func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) return queryRangeParams, nil } +func PrepareClikhouseQueries(messagingQueue *MessagingQueue, queryContext string) (*v3.ClickHouseQuery, error) { + queueType := KafkaQueue + + chq, err := BuildClickHouseQuery(messagingQueue, queueType, queryContext) + + return chq, err +} + func buildClickHouseQueryNetwork(messagingQueue *MessagingQueue, queueType string) (*v3.ClickHouseQuery, error) { start := messagingQueue.Start end := messagingQueue.End @@ -137,7 +145,7 @@ func buildBuilderQueriesNetwork(unixMilliStart, unixMilliEnd int64, attributeCac func BuildQRParamsNetwork(messagingQueue *MessagingQueue, queryContext string, attributeCache *Clients) (*v3.QueryRangeParamsV3, error) { - queueType := kafkaQueue + queueType := KafkaQueue unixMilliStart := messagingQueue.Start / 1000000 unixMilliEnd := messagingQueue.End / 1000000 @@ -177,17 +185,22 @@ func BuildQRParamsNetwork(messagingQueue *MessagingQueue, queryContext string, a return queryRangeParams, nil } -func buildClickHouseQuery(messagingQueue *MessagingQueue, queueType string, queryContext string) (*v3.ClickHouseQuery, error) { +func BuildClickHouseQuery(messagingQueue *MessagingQueue, queueType string, queryContext string) (*v3.ClickHouseQuery, error) { start := messagingQueue.Start end := messagingQueue.End - topic, ok := messagingQueue.Variables["topic"] - if !ok { - return nil, fmt.Errorf("invalid type for Topic") - } - partition, ok := messagingQueue.Variables["partition"] - if !ok { - return nil, fmt.Errorf("invalid type for Partition") + var topic, partition string + + if queryContext == "producer" || queryContext == "consumer" { + var ok bool + topic, ok = messagingQueue.Variables["topic"] + if !ok { + return nil, fmt.Errorf("invalid type for Topic") + } + partition, ok = messagingQueue.Variables["partition"] + if !ok { + return nil, fmt.Errorf("invalid type for Partition") + } } var query string @@ -199,6 +212,12 @@ func buildClickHouseQuery(messagingQueue *MessagingQueue, queueType string, quer return nil, fmt.Errorf("invalid type for consumer group") } query = generateConsumerSQL(start, end, topic, partition, consumerGroup, queueType) + } else if queryContext == "onboard_producers" { + query = onboardProducersSQL(start, end, queueType) + } else if queryContext == "onboard_consumers" { + query = onboardConsumerSQL(start, end, queueType) + } else if queryContext == "onboard_kafka" { + query = onboardKafkaSQL(start, end) } return &v3.ClickHouseQuery{ diff --git a/pkg/query-service/app/logs/v3/enrich_query.go b/pkg/query-service/app/logs/v3/enrich_query.go index b8ed0ff801..49ff422fd5 100644 --- a/pkg/query-service/app/logs/v3/enrich_query.go +++ b/pkg/query-service/app/logs/v3/enrich_query.go @@ -7,6 +7,7 @@ import ( "go.signoz.io/signoz/pkg/query-service/constants" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "go.signoz.io/signoz/pkg/query-service/utils" ) func EnrichmentRequired(params *v3.QueryRangeParamsV3) bool { @@ -61,26 +62,23 @@ func EnrichmentRequired(params *v3.QueryRangeParamsV3) bool { return false } +// if the field is timestamp/id/value we don't need to enrich +// if the field is static we don't need to enrich +// for all others we need to enrich +// an attribute/resource can be materialized/dematerialized +// but the query should work regardless and shouldn't fail func isEnriched(field v3.AttributeKey) bool { // if it is timestamp/id dont check if field.Key == "timestamp" || field.Key == "id" || field.Key == constants.SigNozOrderByValue { return true } - if field.IsColumn { + // don't need to enrich the static fields as they will be always used a column + if _, ok := constants.StaticFieldsLogsV3[field.Key]; ok && field.IsColumn { return true } - if field.Type == v3.AttributeKeyTypeUnspecified || field.DataType == v3.AttributeKeyDataTypeUnspecified { - return false - } - - // try to enrich all attributes which doesn't have isColumn = true - if !field.IsColumn { - return false - } - - return true + return false } func Enrich(params *v3.QueryRangeParamsV3, fields map[string]v3.AttributeKey) { @@ -144,13 +142,9 @@ func enrichFieldWithMetadata(field v3.AttributeKey, fields map[string]v3.Attribu } // check if the field is present in the fields map - if existingField, ok := fields[field.Key]; ok { - // don't update if type is not the same - if (field.Type == "" && field.DataType == "") || - (field.Type == existingField.Type && field.DataType == existingField.DataType) || - (field.Type == "" && field.DataType == existingField.DataType) || - (field.DataType == "" && field.Type == existingField.Type) { - return existingField + for _, key := range utils.GenerateLogEnrichmentKeys(field) { + if val, ok := fields[key]; ok { + return val } } diff --git a/pkg/query-service/app/logs/v3/enrich_query_test.go b/pkg/query-service/app/logs/v3/enrich_query_test.go index 32d714925c..20b5d90487 100644 --- a/pkg/query-service/app/logs/v3/enrich_query_test.go +++ b/pkg/query-service/app/logs/v3/enrich_query_test.go @@ -51,7 +51,7 @@ var testEnrichmentRequiredData = []struct { EnrichmentRequired: true, }, { - Name: "filter enrichment not required", + Name: "filter enrichment required", Params: v3.QueryRangeParamsV3{ CompositeQuery: &v3.CompositeQuery{ BuilderQueries: map[string]*v3.BuilderQuery{ @@ -87,7 +87,7 @@ var testEnrichmentRequiredData = []struct { EnrichmentRequired: true, }, { - Name: "filter enrichment not required required json", + Name: "filter enrichment required required json", Params: v3.QueryRangeParamsV3{ CompositeQuery: &v3.CompositeQuery{ BuilderQueries: map[string]*v3.BuilderQuery{ @@ -105,7 +105,7 @@ var testEnrichmentRequiredData = []struct { EnrichmentRequired: true, }, { - Name: "groupBy enrichment not required", + Name: "groupBy enrichment required", Params: v3.QueryRangeParamsV3{ CompositeQuery: &v3.CompositeQuery{ BuilderQueries: map[string]*v3.BuilderQuery{ @@ -194,8 +194,9 @@ var testEnrichmentRequiredData = []struct { QueryName: "test", Expression: "test", DataSource: v3.DataSourceLogs, - GroupBy: []v3.AttributeKey{{Key: "trace_id", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}}, - OrderBy: []v3.OrderBy{{ColumnName: "#SIGNOZ_VALUE", Order: "ASC"}}, + // here we have to fallback to trace_id attribute instead of column + GroupBy: []v3.AttributeKey{{Key: "trace_id", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}}, + OrderBy: []v3.OrderBy{{ColumnName: "#SIGNOZ_VALUE", Order: "ASC"}}, }, }, }, @@ -241,17 +242,17 @@ var testEnrichParamsData = []struct { }, }, Fields: map[string]v3.AttributeKey{ - "test": { + "test##tag##int64": { Key: "test", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeInt64, }, - "user_name": { + "user_name##tag##string": { Key: "user_name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, }, - "response_time": { + "response_time##tag##int64": { Key: "response_time", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeInt64, @@ -302,18 +303,18 @@ var testEnrichParamsData = []struct { }, }, Fields: map[string]v3.AttributeKey{ - "method.name": { + "method.name##tag##string": { Key: "method.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true, }, - "service.name": { + "service.name##tag##string": { Key: "service.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, }, - "host.name": { + "host.name##tag##string": { Key: "host.name", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, @@ -365,7 +366,7 @@ var testEnrichParamsData = []struct { }, }, Fields: map[string]v3.AttributeKey{ - "test": { + "test##tag##string": { Key: "test", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, @@ -393,6 +394,71 @@ var testEnrichParamsData = []struct { }, }, }, + { + Name: "Enrich if an attribute/resource attribute is materialized/dematerialized", + Params: v3.QueryRangeParamsV3{ + CompositeQuery: &v3.CompositeQuery{ + BuilderQueries: map[string]*v3.BuilderQuery{ + "test": { + QueryName: "test", + Expression: "test", + DataSource: v3.DataSourceLogs, + AggregateAttribute: v3.AttributeKey{ + Key: "mat_resource", + Type: v3.AttributeKeyTypeResource, + DataType: v3.AttributeKeyDataTypeInt64, + IsColumn: true, + }, + Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{ + {Key: v3.AttributeKey{Key: "mat_attr", Type: v3.AttributeKeyTypeTag, IsColumn: true}, Value: "test", Operator: "="}, + {Key: v3.AttributeKey{Key: "normal_attr", DataType: v3.AttributeKeyDataTypeString, IsColumn: false}, Value: "test1", Operator: "="}, + }}, + }, + }, + }, + }, + Fields: map[string]v3.AttributeKey{ + "mat_resource##resource##int64": { + Key: "mat_resource", + Type: v3.AttributeKeyTypeResource, + DataType: v3.AttributeKeyDataTypeInt64, + IsColumn: false, + }, + "mat_attr##tag##string": { + Key: "mat_attr", + Type: v3.AttributeKeyTypeTag, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: false, + }, + "normal_attr##tag##string": { + Key: "normal_attr", + Type: v3.AttributeKeyTypeTag, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: true, + }, + }, + Result: v3.QueryRangeParamsV3{ + CompositeQuery: &v3.CompositeQuery{ + BuilderQueries: map[string]*v3.BuilderQuery{ + "test": { + QueryName: "test", + Expression: "test", + DataSource: v3.DataSourceLogs, + AggregateAttribute: v3.AttributeKey{ + Key: "mat_resource", + Type: v3.AttributeKeyTypeResource, + DataType: v3.AttributeKeyDataTypeInt64, + IsColumn: false, + }, + Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{ + {Key: v3.AttributeKey{Key: "mat_attr", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: false}, Value: "test", Operator: "="}, + {Key: v3.AttributeKey{Key: "normal_attr", Type: v3.AttributeKeyTypeTag, DataType: v3.AttributeKeyDataTypeString, IsColumn: true}, Value: "test1", Operator: "="}, + }}, + }, + }, + }, + }, + }, } func TestEnrichParams(t *testing.T) { diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index b71df63781..f16597aa31 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -742,7 +742,7 @@ func makeRulesManager( RepoURL: ruleRepoURL, DBConn: db, Context: context.Background(), - Logger: nil, + Logger: zap.L(), DisableRules: disableRules, FeatureFlags: fm, Reader: ch, diff --git a/pkg/query-service/contextlinks/links.go b/pkg/query-service/contextlinks/links.go index d0d8400e74..260745eda3 100644 --- a/pkg/query-service/contextlinks/links.go +++ b/pkg/query-service/contextlinks/links.go @@ -8,6 +8,7 @@ import ( tracesV3 "go.signoz.io/signoz/pkg/query-service/app/traces/v3" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "go.signoz.io/signoz/pkg/query-service/utils" ) func PrepareLinksToTraces(start, end time.Time, filterItems []v3.FilterItem) string { @@ -178,8 +179,26 @@ func PrepareFilters(labels map[string]string, whereClauseItems []v3.FilterItem, for key, value := range labels { if _, ok := added[key]; !ok { // start by taking the attribute key from the keys map, if not present, create a new one - attributeKey, ok := keys[key] - if !ok { + var attributeKey v3.AttributeKey + var attrFound bool + + // as of now this logic will only apply for logs + for _, tKey := range utils.GenerateLogEnrichmentKeys(v3.AttributeKey{Key: key}) { + if val, ok := keys[tKey]; ok { + attributeKey = val + attrFound = true + break + } + } + + // check if the attribute key is directly present, as of now this will always be false for logs + // as for logs it will be satisfied in the condition above + if !attrFound { + attributeKey, attrFound = keys[key] + } + + // if the attribute key is not present, create a new one + if !attrFound { attributeKey = v3.AttributeKey{Key: key} } diff --git a/pkg/query-service/main.go b/pkg/query-service/main.go index d1b191f248..cd6a542220 100644 --- a/pkg/query-service/main.go +++ b/pkg/query-service/main.go @@ -8,6 +8,7 @@ import ( "syscall" "time" + prommodel "github.com/prometheus/common/model" "go.signoz.io/signoz/pkg/query-service/app" "go.signoz.io/signoz/pkg/query-service/auth" "go.signoz.io/signoz/pkg/query-service/constants" @@ -27,6 +28,10 @@ func initZapLog() *zap.Logger { return logger } +func init() { + prommodel.NameValidationScheme = prommodel.UTF8Validation +} + func main() { var promConfigPath, skipTopLvlOpsPath string diff --git a/pkg/query-service/model/logs.go b/pkg/query-service/model/logs.go index 0319581516..c6a9276008 100644 --- a/pkg/query-service/model/logs.go +++ b/pkg/query-service/model/logs.go @@ -49,7 +49,8 @@ func GetLogFieldsV3(ctx context.Context, queryRangeParams *v3.QueryRangeParamsV3 if pass { continue } - data[selectedField.Name] = v3.AttributeKey{ + name := selectedField.Name + "##" + fieldType.String() + "##" + strings.ToLower(selectedField.DataType) + data[name] = v3.AttributeKey{ Key: selectedField.Name, Type: fieldType, DataType: v3.AttributeKeyDataType(strings.ToLower(selectedField.DataType)), @@ -61,12 +62,14 @@ func GetLogFieldsV3(ctx context.Context, queryRangeParams *v3.QueryRangeParamsV3 if pass { continue } - data[interestingField.Name] = v3.AttributeKey{ + name := interestingField.Name + "##" + fieldType.String() + "##" + strings.ToLower(interestingField.DataType) + data[name] = v3.AttributeKey{ Key: interestingField.Name, Type: fieldType, DataType: v3.AttributeKeyDataType(strings.ToLower(interestingField.DataType)), IsColumn: false, } + } break } diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index 2d99118533..a58575cd08 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -287,6 +287,10 @@ func (q AttributeKeyDataType) Validate() error { } } +func (q AttributeKeyDataType) String() string { + return string(q) +} + // FilterAttributeValueRequest is a request to fetch possible attribute values // for a selected aggregate operator, aggregate attribute, filter attribute key // and search text. @@ -317,6 +321,10 @@ const ( AttributeKeyTypeResource AttributeKeyType = "resource" ) +func (t AttributeKeyType) String() string { + return string(t) +} + type AttributeKey struct { Key string `json:"key"` DataType AttributeKeyDataType `json:"dataType"` @@ -1158,7 +1166,6 @@ type Result struct { AnomalyScores []*Series `json:"anomalyScores,omitempty"` List []*Row `json:"list,omitempty"` Table *Table `json:"table,omitempty"` - IsAnomaly bool `json:"isAnomaly,omitempty"` } type Series struct { diff --git a/pkg/query-service/pqlEngine/engine.go b/pkg/query-service/pqlEngine/engine.go index 0ecb7fc766..7362e8f0c9 100644 --- a/pkg/query-service/pqlEngine/engine.go +++ b/pkg/query-service/pqlEngine/engine.go @@ -76,6 +76,7 @@ func NewPqlEngine(config *pconfig.Config) (*PqlEngine, error) { "", time.Duration(1*time.Minute), nil, + false, ) fanoutStorage := pstorage.NewFanout(logger, remoteStorage) diff --git a/pkg/query-service/rules/alerting.go b/pkg/query-service/rules/alerting.go index ec2b0c8016..660b1896f3 100644 --- a/pkg/query-service/rules/alerting.go +++ b/pkg/query-service/rules/alerting.go @@ -106,16 +106,18 @@ const ( ) type RuleCondition struct { - CompositeQuery *v3.CompositeQuery `json:"compositeQuery,omitempty" yaml:"compositeQuery,omitempty"` - CompareOp CompareOp `yaml:"op,omitempty" json:"op,omitempty"` - Target *float64 `yaml:"target,omitempty" json:"target,omitempty"` - AlertOnAbsent bool `yaml:"alertOnAbsent,omitempty" json:"alertOnAbsent,omitempty"` - AbsentFor uint64 `yaml:"absentFor,omitempty" json:"absentFor,omitempty"` - MatchType MatchType `json:"matchType,omitempty"` - TargetUnit string `json:"targetUnit,omitempty"` - Algorithm string `json:"algorithm,omitempty"` - Seasonality string `json:"seasonality,omitempty"` - SelectedQuery string `json:"selectedQueryName,omitempty"` + CompositeQuery *v3.CompositeQuery `json:"compositeQuery,omitempty" yaml:"compositeQuery,omitempty"` + CompareOp CompareOp `yaml:"op,omitempty" json:"op,omitempty"` + Target *float64 `yaml:"target,omitempty" json:"target,omitempty"` + AlertOnAbsent bool `yaml:"alertOnAbsent,omitempty" json:"alertOnAbsent,omitempty"` + AbsentFor uint64 `yaml:"absentFor,omitempty" json:"absentFor,omitempty"` + MatchType MatchType `json:"matchType,omitempty"` + TargetUnit string `json:"targetUnit,omitempty"` + Algorithm string `json:"algorithm,omitempty"` + Seasonality string `json:"seasonality,omitempty"` + SelectedQuery string `json:"selectedQueryName,omitempty"` + RequireMinPoints bool `yaml:"requireMinPoints,omitempty" json:"requireMinPoints,omitempty"` + RequiredNumPoints int `yaml:"requiredNumPoints,omitempty" json:"requiredNumPoints,omitempty"` } func (rc *RuleCondition) GetSelectedQueryName() string { diff --git a/pkg/query-service/rules/base_rule.go b/pkg/query-service/rules/base_rule.go index 181eaa3a28..b6d2db0a3c 100644 --- a/pkg/query-service/rules/base_rule.go +++ b/pkg/query-service/rules/base_rule.go @@ -8,7 +8,6 @@ import ( "sync" "time" - "go.signoz.io/signoz/pkg/query-service/common" "go.signoz.io/signoz/pkg/query-service/converter" "go.signoz.io/signoz/pkg/query-service/interfaces" "go.signoz.io/signoz/pkg/query-service/model" @@ -339,11 +338,9 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { var alertSmpl Sample var shouldAlert bool var lbls qslabels.Labels - var lblsNormalized qslabels.Labels for name, value := range series.Labels { lbls = append(lbls, qslabels.Label{Name: name, Value: value}) - lblsNormalized = append(lblsNormalized, qslabels.Label{Name: common.NormalizeLabelName(name), Value: value}) } series.Points = removeGroupinSetPoints(series) @@ -353,13 +350,20 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { return alertSmpl, false } + if r.ruleCondition.RequireMinPoints { + if len(series.Points) < r.ruleCondition.RequiredNumPoints { + zap.L().Info("not enough data points to evaluate series, skipping", zap.String("ruleid", r.ID()), zap.Int("numPoints", len(series.Points)), zap.Int("requiredPoints", r.ruleCondition.RequiredNumPoints)) + return alertSmpl, false + } + } + switch r.matchType() { case AtleastOnce: // If any sample matches the condition, the rule is firing. if r.compareOp() == ValueIsAbove { for _, smpl := range series.Points { if smpl.Value > r.targetVal() { - alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lbls} shouldAlert = true break } @@ -367,7 +371,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { } else if r.compareOp() == ValueIsBelow { for _, smpl := range series.Points { if smpl.Value < r.targetVal() { - alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lbls} shouldAlert = true break } @@ -375,7 +379,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { } else if r.compareOp() == ValueIsEq { for _, smpl := range series.Points { if smpl.Value == r.targetVal() { - alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lbls} shouldAlert = true break } @@ -383,7 +387,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { } else if r.compareOp() == ValueIsNotEq { for _, smpl := range series.Points { if smpl.Value != r.targetVal() { - alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lbls} shouldAlert = true break } @@ -391,7 +395,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { } else if r.compareOp() == ValueOutsideBounds { for _, smpl := range series.Points { if math.Abs(smpl.Value) >= r.targetVal() { - alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lbls} shouldAlert = true break } @@ -400,7 +404,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { case AllTheTimes: // If all samples match the condition, the rule is firing. shouldAlert = true - alertSmpl = Sample{Point: Point{V: r.targetVal()}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: r.targetVal()}, Metric: lbls} if r.compareOp() == ValueIsAbove { for _, smpl := range series.Points { if smpl.Value <= r.targetVal() { @@ -416,7 +420,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { minValue = smpl.Value } } - alertSmpl = Sample{Point: Point{V: minValue}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: minValue}, Metric: lbls} } } else if r.compareOp() == ValueIsBelow { for _, smpl := range series.Points { @@ -432,7 +436,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { maxValue = smpl.Value } } - alertSmpl = Sample{Point: Point{V: maxValue}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: maxValue}, Metric: lbls} } } else if r.compareOp() == ValueIsEq { for _, smpl := range series.Points { @@ -452,7 +456,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { if shouldAlert { for _, smpl := range series.Points { if !math.IsInf(smpl.Value, 0) && !math.IsNaN(smpl.Value) { - alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lbls} break } } @@ -460,7 +464,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { } else if r.compareOp() == ValueOutsideBounds { for _, smpl := range series.Points { if math.Abs(smpl.Value) >= r.targetVal() { - alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: smpl.Value}, Metric: lbls} shouldAlert = true break } @@ -477,7 +481,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { count++ } avg := sum / count - alertSmpl = Sample{Point: Point{V: avg}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: avg}, Metric: lbls} if r.compareOp() == ValueIsAbove { if avg > r.targetVal() { shouldAlert = true @@ -509,7 +513,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { } sum += smpl.Value } - alertSmpl = Sample{Point: Point{V: sum}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: sum}, Metric: lbls} if r.compareOp() == ValueIsAbove { if sum > r.targetVal() { shouldAlert = true @@ -534,7 +538,7 @@ func (r *BaseRule) ShouldAlert(series v3.Series) (Sample, bool) { case Last: // If the last sample matches the condition, the rule is firing. shouldAlert = false - alertSmpl = Sample{Point: Point{V: series.Points[len(series.Points)-1].Value}, Metric: lblsNormalized, MetricOrig: lbls} + alertSmpl = Sample{Point: Point{V: series.Points[len(series.Points)-1].Value}, Metric: lbls} if r.compareOp() == ValueIsAbove { if series.Points[len(series.Points)-1].Value > r.targetVal() { shouldAlert = true diff --git a/pkg/query-service/rules/base_rule_test.go b/pkg/query-service/rules/base_rule_test.go new file mode 100644 index 0000000000..a73fa9ebea --- /dev/null +++ b/pkg/query-service/rules/base_rule_test.go @@ -0,0 +1,64 @@ +package rules + +import ( + "testing" + + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" +) + +func TestBaseRule_RequireMinPoints(t *testing.T) { + threshold := 1.0 + tests := []struct { + name string + rule *BaseRule + shouldAlert bool + series *v3.Series + }{ + { + name: "test should skip if less than min points", + rule: &BaseRule{ + ruleCondition: &RuleCondition{ + RequireMinPoints: true, + RequiredNumPoints: 4, + }, + }, + series: &v3.Series{ + Points: []v3.Point{ + {Value: 1}, + {Value: 2}, + }, + }, + shouldAlert: false, + }, + { + name: "test should alert if more than min points", + rule: &BaseRule{ + ruleCondition: &RuleCondition{ + RequireMinPoints: true, + RequiredNumPoints: 4, + CompareOp: ValueIsAbove, + MatchType: AtleastOnce, + Target: &threshold, + }, + }, + series: &v3.Series{ + Points: []v3.Point{ + {Value: 1}, + {Value: 2}, + {Value: 3}, + {Value: 4}, + }, + }, + shouldAlert: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, shouldAlert := test.rule.ShouldAlert(*test.series) + if shouldAlert != test.shouldAlert { + t.Errorf("expected shouldAlert to be %v, got %v", test.shouldAlert, shouldAlert) + } + }) + } +} diff --git a/pkg/query-service/rules/manager.go b/pkg/query-service/rules/manager.go index 75b2b5fade..c41d0bbe50 100644 --- a/pkg/query-service/rules/manager.go +++ b/pkg/query-service/rules/manager.go @@ -173,7 +173,7 @@ func defaultPrepareTaskFunc(opts PrepareTaskOptions) (Task, error) { task = newTask(TaskTypeProm, opts.TaskName, taskNamesuffix, time.Duration(opts.Rule.Frequency), rules, opts.ManagerOpts, opts.NotifyFunc, opts.RuleDB) } else { - return nil, fmt.Errorf("unsupported rule type. Supported types: %s, %s", RuleTypeProm, RuleTypeThreshold) + return nil, fmt.Errorf("unsupported rule type %s. Supported types: %s, %s", opts.Rule.RuleType, RuleTypeProm, RuleTypeThreshold) } return task, nil diff --git a/pkg/query-service/rules/prom_rule.go b/pkg/query-service/rules/prom_rule.go index 473fac0d5d..c11525d796 100644 --- a/pkg/query-service/rules/prom_rule.go +++ b/pkg/query-service/rules/prom_rule.go @@ -43,6 +43,7 @@ func NewPromRule( BaseRule: baseRule, pqlEngine: pqlEngine, } + p.logger = logger query, err := p.getPqlQuery() diff --git a/pkg/query-service/rules/result_types.go b/pkg/query-service/rules/result_types.go index 5f39208b01..1f6304c6d5 100644 --- a/pkg/query-service/rules/result_types.go +++ b/pkg/query-service/rules/result_types.go @@ -17,10 +17,6 @@ type Sample struct { Metric labels.Labels - // Label keys as-is from the result query. - // The original labels are used to prepare the related{logs, traces} link in alert notification - MetricOrig labels.Labels - IsMissing bool } diff --git a/pkg/query-service/rules/templates.go b/pkg/query-service/rules/templates.go index 5f29621c68..519ff3859b 100644 --- a/pkg/query-service/rules/templates.go +++ b/pkg/query-service/rules/templates.go @@ -16,6 +16,7 @@ import ( "golang.org/x/text/cases" + "go.signoz.io/signoz/pkg/query-service/common" "go.signoz.io/signoz/pkg/query-service/utils/times" ) @@ -204,17 +205,47 @@ func NewTemplateExpander( // AlertTemplateData returns the interface to be used in expanding the template. func AlertTemplateData(labels map[string]string, value string, threshold string) interface{} { + // This exists here for backwards compatibility. + // The labels map passed in no longer contains the normalized labels. + // To continue supporting the old way of referencing labels, we need to + // add the normalized labels just for the template expander. + // This is done by creating a new map and adding the normalized labels to it. + newLabels := make(map[string]string) + for k, v := range labels { + newLabels[k] = v + newLabels[common.NormalizeLabelName(k)] = v + } + return struct { Labels map[string]string Value string Threshold string }{ - Labels: labels, + Labels: newLabels, Value: value, Threshold: threshold, } } +// preprocessTemplate preprocesses the template to replace our custom $variable syntax with the correct Go template syntax. +// example, $service.name in the template is replaced with {{index $labels "service.name"}} +// While we could use go template functions to do this, we need to keep the syntax +// consistent across the platform. +// If there is a go template block, it won't be replaced. +// The example for existing go template block is: {{$threshold}} or {{$value}} or any other valid go template syntax. +func (te *TemplateExpander) preprocessTemplate() { + re := regexp.MustCompile(`({{.*?}})|(\$(\w+(?:\.\w+)*))`) + te.text = re.ReplaceAllStringFunc(te.text, func(match string) string { + if strings.HasPrefix(match, "{{") { + // If it's a Go template block, leave it unchanged + return match + } + // Otherwise, it's our custom $variable syntax + path := strings.Split(match[1:], ".") + return "{{index $labels \"" + strings.Join(path, ".") + "\"}}" + }) +} + // Funcs adds the functions in fm to the Expander's function map. // Existing functions will be overwritten in case of conflict. func (te TemplateExpander) Funcs(fm text_template.FuncMap) { @@ -237,6 +268,8 @@ func (te TemplateExpander) Expand() (result string, resultErr error) { } }() + te.preprocessTemplate() + tmpl, err := text_template.New(te.name).Funcs(te.funcMap).Option("missingkey=zero").Parse(te.text) if err != nil { return "", fmt.Errorf("error parsing template %v: %v", te.name, err) diff --git a/pkg/query-service/rules/templates_test.go b/pkg/query-service/rules/templates_test.go new file mode 100644 index 0000000000..737a3d20ca --- /dev/null +++ b/pkg/query-service/rules/templates_test.go @@ -0,0 +1,65 @@ +package rules + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.signoz.io/signoz/pkg/query-service/utils/times" +) + +func TestTemplateExpander(t *testing.T) { + defs := "{{$labels := .Labels}}{{$value := .Value}}{{$threshold := .Threshold}}" + data := AlertTemplateData(map[string]string{"service.name": "my-service"}, "100", "200") + expander := NewTemplateExpander(context.Background(), defs+"test $service.name", "test", data, times.Time(time.Now().Unix()), nil) + result, err := expander.Expand() + if err != nil { + t.Fatal(err) + } + require.Equal(t, "test my-service", result) +} + +func TestTemplateExpander_WithThreshold(t *testing.T) { + defs := "{{$labels := .Labels}}{{$value := .Value}}{{$threshold := .Threshold}}" + data := AlertTemplateData(map[string]string{"service.name": "my-service"}, "200", "100") + expander := NewTemplateExpander(context.Background(), defs+"test $service.name exceeds {{$threshold}} and observed at {{$value}}", "test", data, times.Time(time.Now().Unix()), nil) + result, err := expander.Expand() + if err != nil { + t.Fatal(err) + } + require.Equal(t, "test my-service exceeds 100 and observed at 200", result) +} + +func TestTemplateExpanderOldVariableSyntax(t *testing.T) { + defs := "{{$labels := .Labels}}{{$value := .Value}}{{$threshold := .Threshold}}" + data := AlertTemplateData(map[string]string{"service.name": "my-service"}, "200", "100") + expander := NewTemplateExpander(context.Background(), defs+"test {{.Labels.service_name}} exceeds {{$threshold}} and observed at {{$value}}", "test", data, times.Time(time.Now().Unix()), nil) + result, err := expander.Expand() + if err != nil { + t.Fatal(err) + } + require.Equal(t, "test my-service exceeds 100 and observed at 200", result) +} + +func TestTemplateExpander_WithAlreadyNormalizedKey(t *testing.T) { + defs := "{{$labels := .Labels}}{{$value := .Value}}{{$threshold := .Threshold}}" + data := AlertTemplateData(map[string]string{"service_name": "my-service"}, "200", "100") + expander := NewTemplateExpander(context.Background(), defs+"test {{.Labels.service_name}} exceeds {{$threshold}} and observed at {{$value}}", "test", data, times.Time(time.Now().Unix()), nil) + result, err := expander.Expand() + if err != nil { + t.Fatal(err) + } + require.Equal(t, "test my-service exceeds 100 and observed at 200", result) +} + +func TestTemplateExpander_WithMissingKey(t *testing.T) { + defs := "{{$labels := .Labels}}{{$value := .Value}}{{$threshold := .Threshold}}" + data := AlertTemplateData(map[string]string{"service_name": "my-service"}, "200", "100") + expander := NewTemplateExpander(context.Background(), defs+"test {{.Labels.missing_key}} exceeds {{$threshold}} and observed at {{$value}}", "test", data, times.Time(time.Now().Unix()), nil) + result, err := expander.Expand() + if err != nil { + t.Fatal(err) + } + require.Equal(t, "test exceeds 100 and observed at 200", result) +} diff --git a/pkg/query-service/rules/threshold_rule.go b/pkg/query-service/rules/threshold_rule.go index 8453f1a268..72b00e2412 100644 --- a/pkg/query-service/rules/threshold_rule.go +++ b/pkg/query-service/rules/threshold_rule.go @@ -401,7 +401,7 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time) (interface{}, er } lb := labels.NewBuilder(smpl.Metric).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel) - resultLabels := labels.NewBuilder(smpl.MetricOrig).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel).Labels() + resultLabels := labels.NewBuilder(smpl.Metric).Del(labels.MetricNameLabel).Del(labels.TemporalityLabel).Labels() for name, value := range r.labels.Map() { lb.Set(name, expand(value)) @@ -413,7 +413,7 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time) (interface{}, er annotations := make(labels.Labels, 0, len(r.annotations.Map())) for name, value := range r.annotations.Map() { - annotations = append(annotations, labels.Label{Name: common.NormalizeLabelName(name), Value: expand(value)}) + annotations = append(annotations, labels.Label{Name: name, Value: expand(value)}) } if smpl.IsMissing { lb.Set(labels.AlertNameLabel, "[No data] "+r.Name()) @@ -423,13 +423,13 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time) (interface{}, er // is used alert grouping, and we want to group alerts with the same // label set, but different timestamps, together. if r.typ == AlertTypeTraces { - link := r.prepareLinksToTraces(ts, smpl.MetricOrig) + link := r.prepareLinksToTraces(ts, smpl.Metric) if link != "" && r.hostFromSource() != "" { zap.L().Info("adding traces link to annotations", zap.String("link", fmt.Sprintf("%s/traces-explorer?%s", r.hostFromSource(), link))) annotations = append(annotations, labels.Label{Name: "related_traces", Value: fmt.Sprintf("%s/traces-explorer?%s", r.hostFromSource(), link)}) } } else if r.typ == AlertTypeLogs { - link := r.prepareLinksToLogs(ts, smpl.MetricOrig) + link := r.prepareLinksToLogs(ts, smpl.Metric) if link != "" && r.hostFromSource() != "" { zap.L().Info("adding logs link to annotations", zap.String("link", fmt.Sprintf("%s/logs/logs-explorer?%s", r.hostFromSource(), link))) annotations = append(annotations, labels.Label{Name: "related_logs", Value: fmt.Sprintf("%s/logs/logs-explorer?%s", r.hostFromSource(), link)}) diff --git a/pkg/query-service/rules/threshold_rule_test.go b/pkg/query-service/rules/threshold_rule_test.go index e23ba0d05c..d3d84f06a7 100644 --- a/pkg/query-service/rules/threshold_rule_test.go +++ b/pkg/query-service/rules/threshold_rule_test.go @@ -1010,7 +1010,7 @@ func TestThresholdRuleLabelNormalization(t *testing.T) { sample, shoulAlert := rule.ShouldAlert(c.values) for name, value := range c.values.Labels { - assert.Equal(t, value, sample.Metric.Get(common.NormalizeLabelName(name))) + assert.Equal(t, value, sample.Metric.Get(name)) } assert.Equal(t, c.expectAlert, shoulAlert, "Test case %d", idx) diff --git a/pkg/query-service/tests/integration/filter_suggestions_test.go b/pkg/query-service/tests/integration/filter_suggestions_test.go index a1f56115c5..6c8224be50 100644 --- a/pkg/query-service/tests/integration/filter_suggestions_test.go +++ b/pkg/query-service/tests/integration/filter_suggestions_test.go @@ -138,6 +138,62 @@ func TestLogsFilterSuggestionsWithExistingFilter(t *testing.T) { } } +func TestResourceAttribsRankedHigherInLogsFilterSuggestions(t *testing.T) { + require := require.New(t) + + tagKeys := []v3.AttributeKey{} + for _, k := range []string{"user_id", "user_email"} { + tagKeys = append(tagKeys, v3.AttributeKey{ + Key: k, + Type: v3.AttributeKeyTypeTag, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: false, + }) + } + + specialResourceAttrKeys := []v3.AttributeKey{} + for _, k := range []string{"service", "env"} { + specialResourceAttrKeys = append(specialResourceAttrKeys, v3.AttributeKey{ + Key: k, + Type: v3.AttributeKeyTypeResource, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: false, + }) + } + + otherResourceAttrKeys := []v3.AttributeKey{} + for _, k := range []string{"container_name", "container_id"} { + otherResourceAttrKeys = append(otherResourceAttrKeys, v3.AttributeKey{ + Key: k, + Type: v3.AttributeKeyTypeResource, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: false, + }) + } + + tb := NewFilterSuggestionsTestBed(t) + + mockAttrKeysInDB := append(tagKeys, otherResourceAttrKeys...) + mockAttrKeysInDB = append(mockAttrKeysInDB, specialResourceAttrKeys...) + + tb.mockAttribKeysQueryResponse(mockAttrKeysInDB) + + expectedTopSuggestions := append(specialResourceAttrKeys, otherResourceAttrKeys...) + expectedTopSuggestions = append(expectedTopSuggestions, tagKeys...) + + tb.mockAttribValuesQueryResponse( + expectedTopSuggestions[:2], [][]string{{"test"}, {"test"}}, + ) + + suggestionsQueryParams := map[string]string{"examplesLimit": "2"} + suggestionsResp := tb.GetQBFilterSuggestionsForLogs(suggestionsQueryParams) + + require.Equal( + expectedTopSuggestions, + suggestionsResp.AttributeKeys[:len(expectedTopSuggestions)], + ) +} + // Mocks response for CH queries made by reader.GetLogAttributeKeys func (tb *FilterSuggestionsTestBed) mockAttribKeysQueryResponse( attribsToReturn []v3.AttributeKey, diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 1b4cdf6f0d..562f19d83a 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -138,7 +138,7 @@ services: # - ./data/clickhouse-3/:/var/lib/clickhouse/ alertmanager: - image: signoz/alertmanager:0.23.5 + image: signoz/alertmanager:0.23.7 container_name: signoz-alertmanager volumes: - ./data/alertmanager:/data @@ -205,7 +205,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:0.102.10 + image: signoz/signoz-otel-collector:0.102.12 container_name: signoz-otel-collector command: [ diff --git a/pkg/query-service/utils/logs.go b/pkg/query-service/utils/logs.go index 2f536ef857..8efa026b52 100644 --- a/pkg/query-service/utils/logs.go +++ b/pkg/query-service/utils/logs.go @@ -1,5 +1,7 @@ package utils +import v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + const HOUR_NANO = int64(3600000000000) type LogsListTsRange struct { @@ -36,3 +38,34 @@ func GetLogsListTsRanges(start, end int64) []LogsListTsRange { } return result } + +// This tries to see all possible fields that it can fall back to if some meta is missing +// check Test_GenerateLogEnrichmentKeys for example +func GenerateLogEnrichmentKeys(field v3.AttributeKey) []string { + names := []string{} + if field.Type != v3.AttributeKeyTypeUnspecified && field.DataType != v3.AttributeKeyDataTypeUnspecified { + names = append(names, field.Key+"##"+field.Type.String()+"##"+field.DataType.String()) + return names + } + + types := []v3.AttributeKeyType{} + dTypes := []v3.AttributeKeyDataType{} + if field.Type != v3.AttributeKeyTypeUnspecified { + types = append(types, field.Type) + } else { + types = append(types, v3.AttributeKeyTypeTag, v3.AttributeKeyTypeResource) + } + if field.DataType != v3.AttributeKeyDataTypeUnspecified { + dTypes = append(dTypes, field.DataType) + } else { + dTypes = append(dTypes, v3.AttributeKeyDataTypeFloat64, v3.AttributeKeyDataTypeInt64, v3.AttributeKeyDataTypeString, v3.AttributeKeyDataTypeBool) + } + + for _, t := range types { + for _, d := range dTypes { + names = append(names, field.Key+"##"+t.String()+"##"+d.String()) + } + } + + return names +} diff --git a/pkg/query-service/utils/logs_test.go b/pkg/query-service/utils/logs_test.go index 939fa5fa1b..e1efd813d1 100644 --- a/pkg/query-service/utils/logs_test.go +++ b/pkg/query-service/utils/logs_test.go @@ -1,6 +1,11 @@ package utils -import "testing" +import ( + "reflect" + "testing" + + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" +) func TestLogsListTsRange(t *testing.T) { startEndData := []struct { @@ -47,3 +52,53 @@ func TestLogsListTsRange(t *testing.T) { } } } + +func Test_GenerateLogEnrichmentKeys(t *testing.T) { + type args struct { + field v3.AttributeKey + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "all are present", + args: args{ + field: v3.AttributeKey{ + Key: "data", + DataType: v3.AttributeKeyDataTypeString, + Type: v3.AttributeKeyTypeTag, + }, + }, + want: []string{"data##tag##string"}, + }, + { + name: "type present", + args: args{ + field: v3.AttributeKey{ + Key: "data", + Type: v3.AttributeKeyTypeTag, + }, + }, + want: []string{"data##tag##float64", "data##tag##int64", "data##tag##string", "data##tag##bool"}, + }, + { + name: "dataType present", + args: args{ + field: v3.AttributeKey{ + Key: "data", + DataType: v3.AttributeKeyDataTypeString, + }, + }, + want: []string{"data##tag##string", "data##resource##string"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GenerateLogEnrichmentKeys(tt.args.field); !reflect.DeepEqual(got, tt.want) { + t.Errorf("generateLogEnrichmentKeys() = %v, want %v", got, tt.want) + } + }) + } +}
- 4.35 s +
+ 4.35 s +
- 431 ms +
+ 431 ms +
- 431 ms +
+ 431 ms +
- 287 ms +
+ 287 ms +
- 230 ms +
+ 230 ms +
- 66.4 ms +
+ 66.4 ms +
- 31.3 ms +
+ 31.3 ms +