Compare commits

..

1 Commits

Author SHA1 Message Date
SagarRajput-7
e0a6adf177 feat: added new multiselect component 2025-03-26 12:11:16 +05:30
156 changed files with 2430 additions and 5933 deletions

View File

@@ -1,44 +0,0 @@
name: build-community
on:
push:
branches:
- main
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+
jobs:
js-build:
uses: signoz/primus.workflows/.github/workflows/js-build.yaml@feat/new-build
secrets: inherit
with:
PRIMUS_REF: dockerhub
JS_WORKDIR: frontend
JS_UPLOAD_ARTIFACT_NAME: community-jsbuild-${{ github.sha }}
JS_UPLOAD_ARTIFACT_PATH: frontend/build
DOCKER_BUILD: false
DOCKER_MANIFEST: false
go-build:
uses: signoz/primus.workflows/.github/workflows/go-build.yaml@feat/new-build
needs: js-build
secrets: inherit
with:
PRIMUS_REF: dockerhub
GO_NAME: signoz-community
GO_DOWNLOAD_ARTIFACT_NAME: community-jsbuild-${{ github.sha }}
GO_DOWNLOAD_ARTIFACT_PATH: frontend/build
GO_BUILD_CONTEXT: ./pkg/query-service
GO_BUILD_FLAGS: >-
-tags timetzdata
-ldflags='-linkmode external -extldflags \"-static\" -s -w
-X github.com/signoz/zeus/pkg/version.Version=\$($MAKE info-version)
-X github.com/signoz/zeus/pkg/version.variant=community
-X github.com/signoz/zeus/pkg/version.hash=\$($MAKE info-commit-short)
-X github.com/signoz/zeus/pkg/version.time=\$($MAKE info-timestamp)
-X github.com/signoz/zeus/pkg/version.branch=\$($MAKE info-branch)'
GO_CGO_ENABLED: 1
DOCKER_BASE_IMAGES: '{"alpine": "alpine:3.20.3"}'
DOCKER_DOCKERFILE_PATH: ./pkg/query-service/Dockerfile.multi-arch
DOCKER_MANIFEST: true
DOCKER_PROVIDERS: hub

View File

@@ -1,82 +0,0 @@
name: build-enterprise
on:
push:
branches:
- main
tags:
- v*
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
docker_providers: ${{ steps.set-docker-providers.outputs.providers }}
steps:
- name: create-dotenv
run: |
mkdir -p frontend
echo 'CI=1' > frontend/.env
echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' >> frontend/.env
echo 'SEGMENT_ID="${{ secrets.SEGMENT_ID }}"' >> frontend/.env
echo 'SENTRY_AUTH_TOKEN="${{ secrets.SENTRY_AUTH_TOKEN }}"' >> frontend/.env
echo 'SENTRY_ORG="${{ secrets.SENTRY_ORG }}"' >> frontend/.env
echo 'SENTRY_PROJECT_ID="${{ secrets.SENTRY_PROJECT_ID }}"' >> frontend/.env
echo 'SENTRY_DSN="${{ secrets.SENTRY_DSN }}"' >> frontend/.env
echo 'TUNNEL_URL="${{ secrets.TUNNEL_URL }}"' >> frontend/.env
echo 'TUNNEL_DOMAIN="${{ secrets.TUNNEL_DOMAIN }}"' >> frontend/.env
echo 'POSTHOG_KEY="${{ secrets.POSTHOG_KEY }}"' >> frontend/.env
echo 'CUSTOMERIO_ID="${{ secrets.CUSTOMERIO_ID }}"' >> frontend/.env
echo 'CUSTOMERIO_SITE_ID="${{ secrets.CUSTOMERIO_SITE_ID }}"' >> frontend/.env
- name: upload-dotenv
uses: actions/upload-artifact@v4
with:
name: enterprise-dotenv-${{ github.sha }}
path: frontend/.env
if-no-files-found: error
include-hidden-files: true
- name: set-docker-providers
id: set-docker-providers
run: |
if [[ ${{ github.event.ref }} =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ || ${{ github.event.ref }} =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+-rc.[0-9]+$ ]]; then
echo "providers=hub,gcp" >> $GITHUB_OUTPUT
else
echo "providers=gcp" >> $GITHUB_OUTPUT
fi
js-build:
uses: signoz/primus.workflows/.github/workflows/js-build.yaml@feat/new-build
needs: prepare
secrets: inherit
with:
PRIMUS_REF: dockerhub
JS_WORKDIR: frontend
JS_DOWNLOAD_ARTIFACT_NAME: enterprise-dotenv-${{ github.sha }}
JS_DOWNLOAD_ARTIFACT_PATH: frontend/.env
JS_UPLOAD_ARTIFACT_NAME: enterprise-jsbuild-${{ github.sha }}
JS_UPLOAD_ARTIFACT_PATH: frontend/build
DOCKER_BUILD: false
DOCKER_MANIFEST: false
go-build:
uses: signoz/primus.workflows/.github/workflows/go-build.yaml@feat/new-build
needs: js-build
secrets: inherit
with:
PRIMUS_REF: dockerhub
GO_DOWNLOAD_ARTIFACT_NAME: enterprise-jsbuild-${{ github.sha }}
GO_DOWNLOAD_ARTIFACT_PATH: frontend/build
GO_BUILD_CONTEXT: ./ee/query-service
GO_BUILD_FLAGS: >-
-tags timetzdata
-ldflags='-linkmode external -extldflags \"-static\" -s -w
-X github.com/signoz/zeus/pkg/version.Version=\$($MAKE info-version)
-X github.com/signoz/zeus/pkg/version.variant=community
-X github.com/signoz/zeus/pkg/version.hash=\$($MAKE info-commit-short)
-X github.com/signoz/zeus/pkg/version.time=\$($MAKE info-timestamp)
-X github.com/signoz/zeus/pkg/version.branch=\$($MAKE info-branch)
-X github.com/signoz/zeus/ee/query-service/constants.ZeusURL=https://api.signoz.cloud
-X github.com/signoz/zeus/ee/query-service/constants.LicenseSignozIo=https://license.signoz.io/api/v1'
GO_CGO_ENABLED: 1
DOCKER_BASE_IMAGES: '{"alpine": "alpine:3.20.3"}'
DOCKER_DOCKERFILE_PATH: ./ee/query-service/Dockerfile.multi-arch
DOCKER_MANIFEST: true
DOCKER_PROVIDERS: ${{ needs.prepare.outputs.docker_providers }}

122
.github/workflows/build.yaml vendored Normal file
View File

@@ -0,0 +1,122 @@
name: build
on:
push:
branches:
- main
tags:
- v*
jobs:
enterprise:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: setup-qemu
uses: docker/setup-qemu-action@v3
- name: setup-buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
- name: docker-login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: create-env-file
run: |
echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env
echo 'SEGMENT_ID="${{ secrets.SEGMENT_ID }}"' >> frontend/.env
echo 'SENTRY_AUTH_TOKEN="${{ secrets.SENTRY_AUTH_TOKEN }}"' >> frontend/.env
echo 'SENTRY_ORG="${{ secrets.SENTRY_ORG }}"' >> frontend/.env
echo 'SENTRY_PROJECT_ID="${{ secrets.SENTRY_PROJECT_ID }}"' >> frontend/.env
echo 'SENTRY_DSN="${{ secrets.SENTRY_DSN }}"' >> frontend/.env
echo 'TUNNEL_URL="${{ secrets.TUNNEL_URL }}"' >> frontend/.env
echo 'TUNNEL_DOMAIN="${{ secrets.TUNNEL_DOMAIN }}"' >> frontend/.env
echo 'POSTHOG_KEY="${{ secrets.POSTHOG_KEY }}"' >> frontend/.env
echo 'CUSTOMERIO_ID="${{ secrets.CUSTOMERIO_ID }}"' >> frontend/.env
echo 'CUSTOMERIO_SITE_ID="${{ secrets.CUSTOMERIO_SITE_ID }}"' >> frontend/.env
- name: github-ref-info
shell: bash
run: |
GH_REF=${{ github.ref }}
if [[ "${{ github.ref_type }}" == "tag" ]]; then
PREFIX="refs/tags/"
echo "GH_IS_TAG=true" >> $GITHUB_ENV
echo "GH_TAG=${GH_REF#$PREFIX}" >> $GITHUB_ENV
else
PREFIX="refs/heads/"
echo "GH_IS_TAG=false" >> $GITHUB_ENV
echo "GH_BRANCH_NAME=${GH_REF#$PREFIX}" >> $GITHUB_ENV
fi
- name: set-version
run: |
if [ '${{ env.GH_IS_TAG }}' == 'true' ]; then
echo "VERSION=${{ env.GH_TAG }}" >> $GITHUB_ENV
elif [ '${{ env.GH_BRANCH_NAME }}' == 'main' ]; then
echo "VERSION=latest" >> $GITHUB_ENV
else
echo "VERSION=${{ env.GH_BRANCH_NAME }}" >> $GITHUB_ENV
fi
- name: cross-compilation-tools
run: |
set -ex
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu musl-tools
- name: publish
run: make docker-buildx-enterprise
community:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: setup-go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: setup-qemu
uses: docker/setup-qemu-action@v3
- name: setup-buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
- name: docker-login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: github-ref-info
shell: bash
run: |
GH_REF=${{ github.ref }}
if [[ "${{ github.ref_type }}" == "tag" ]]; then
PREFIX="refs/tags/"
echo "GH_IS_TAG=true" >> $GITHUB_ENV
echo "GH_TAG=${GH_REF#$PREFIX}" >> $GITHUB_ENV
else
PREFIX="refs/heads/"
echo "GH_IS_TAG=false" >> $GITHUB_ENV
echo "GH_BRANCH_NAME=${GH_REF#$PREFIX}" >> $GITHUB_ENV
fi
- name: set-version
run: |
if [ '${{ env.GH_IS_TAG }}' == 'true' ]; then
echo "VERSION=${{ env.GH_TAG }}" >> $GITHUB_ENV
elif [ '${{ env.GH_BRANCH_NAME }}' == 'main' ]; then
echo "VERSION=latest" >> $GITHUB_ENV
else
echo "VERSION=${{ env.GH_BRANCH_NAME }}" >> $GITHUB_ENV
fi
- name: cross-compilation-tools
run: |
set -ex
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu musl-tools
- name: publish
run: make docker-buildx-community

1
.gitignore vendored
View File

@@ -54,7 +54,6 @@ ee/query-service/tests/test-deploy/data/
bin/
.local/
*/query-service/queries.active
ee/query-service/db
# e2e

View File

@@ -77,4 +77,4 @@ Need assistance? Join our Slack community:
## Where do I go from here?
- Set up your [development environment](docs/contributing/development.md)
- Deploy and observe [SigNoz in action with OpenTelemetry Demo Application](docs/otel-demo-docs.md)
- Deploy and observe [SigNoz in action with OpenTelemetry Demo Application](docs/otel-demo/otel-demo-docs.md)

View File

@@ -74,10 +74,6 @@ go-run-enterprise: ## Runs the enterprise go backend server
--use-logs-new-schema true \
--use-trace-new-schema true
.PHONY: go-test
go-test: ## Runs go unit tests
@go test -race ./...
.PHONY: go-run-community
go-run-community: ## Runs the community go backend server
@SIGNOZ_INSTRUMENTATION_LOGS_LEVEL=debug \

View File

@@ -72,6 +72,7 @@ sqlstore:
# The path to the SQLite database file.
path: /var/lib/signoz/signoz.db
##################### APIServer #####################
apiserver:
timeout:
@@ -90,29 +91,20 @@ apiserver:
- /api/v1/version
- /
##################### TelemetryStore #####################
telemetrystore:
# Specifies the telemetrystore provider to use.
provider: clickhouse
# Maximum number of idle connections in the connection pool.
max_idle_conns: 50
# Maximum number of open connections to the database.
max_open_conns: 100
# Maximum time to wait for a connection to be established.
dial_timeout: 5s
# Specifies the telemetrystore provider to use.
provider: clickhouse
clickhouse:
# The DSN to use for clickhouse.
dsn: tcp://localhost:9000
##################### Prometheus #####################
prometheus:
active_query_tracker:
# Whether to enable the active query tracker.
enabled: true
# The path to use for the active query tracker.
path: ""
# The maximum number of concurrent queries.
max_concurrent: 20
# The DSN to use for ClickHouse.
dsn: http://localhost:9000
##################### Alertmanager #####################
alertmanager:
@@ -125,7 +117,7 @@ alertmanager:
# The poll interval for periodically syncing the alertmanager with the config in the store.
poll_interval: 1m
# The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy). Used for generating relative and absolute links back to Alertmanager itself.
external_url: http://localhost:8080
external_url: http://localhost:9093
# The global configuration for the alertmanager. All the exahustive fields can be found in the upstream: https://github.com/prometheus/alertmanager/blob/efa05feffd644ba4accb526e98a8c6545d26a783/config/config.go#L833
global:
# ResolveTimeout is the time after which an alert is declared resolved if it has not been updated.

View File

@@ -1,22 +0,0 @@
ARG ALPINE_SHA="pass-a-valid-docker-sha-otherwise-this-will-fail"
FROM alpine:${ALPINE_SHA}
LABEL maintainer="signoz"
WORKDIR /root
ARG OS="linux"
ARG ARCH
RUN apk update && \
apk add ca-certificates && \
rm -rf /var/cache/apk/*
COPY ./target/${OS}-${ARCH}/signoz /root/signoz
COPY ./conf/prometheus.yml /root/config/prometheus.yml
COPY ./templates/email /root/templates
COPY frontend/build/ /root/signoz/web/
RUN chmod 755 /root /root/signoz
ENTRYPOINT ["./signoz"]
CMD ["-config", "/root/config/prometheus.yml"]

View File

@@ -313,9 +313,6 @@ func (p *BaseSeasonalProvider) getScore(
series, prevSeries, weekSeries, weekPrevSeries, past2SeasonSeries, past3SeasonSeries *v3.Series, value float64, idx int,
) float64 {
expectedValue := p.getExpectedValue(series, prevSeries, weekSeries, weekPrevSeries, past2SeasonSeries, past3SeasonSeries, idx)
if expectedValue < 0 {
expectedValue = p.getMovingAvg(prevSeries, movingAvgWindowSize, idx)
}
return (value - expectedValue) / p.getStdDev(weekSeries)
}

View File

@@ -8,10 +8,8 @@ import (
"github.com/jmoiron/sqlx"
"github.com/SigNoz/signoz/pkg/cache"
"github.com/SigNoz/signoz/pkg/prometheus"
basechr "github.com/SigNoz/signoz/pkg/query-service/app/clickhouseReader"
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
"github.com/SigNoz/signoz/pkg/telemetrystore"
)
type ClickhouseReader struct {
@@ -22,8 +20,8 @@ type ClickhouseReader struct {
func NewDataConnector(
localDB *sqlx.DB,
telemetryStore telemetrystore.TelemetryStore,
prometheus prometheus.Prometheus,
ch clickhouse.Conn,
promConfigPath string,
lm interfaces.FeatureLookup,
cluster string,
useLogsNewSchema bool,
@@ -31,10 +29,14 @@ func NewDataConnector(
fluxIntervalForTraceDetail time.Duration,
cache cache.Cache,
) *ClickhouseReader {
chReader := basechr.NewReader(localDB, telemetryStore, prometheus, lm, cluster, useLogsNewSchema, useTraceNewSchema, fluxIntervalForTraceDetail, cache)
chReader := basechr.NewReader(localDB, ch, promConfigPath, lm, cluster, useLogsNewSchema, useTraceNewSchema, fluxIntervalForTraceDetail, cache)
return &ClickhouseReader{
conn: telemetryStore.ClickhouseDB(),
conn: ch,
appdb: localDB,
ClickHouseReader: chReader,
}
}
func (r *ClickhouseReader) Start(readerReady chan bool) {
r.ClickHouseReader.Start(readerReady)
}

View File

@@ -18,14 +18,13 @@ import (
"github.com/SigNoz/signoz/ee/query-service/constants"
"github.com/SigNoz/signoz/ee/query-service/dao"
"github.com/SigNoz/signoz/ee/query-service/integrations/gateway"
"github.com/SigNoz/signoz/ee/query-service/interfaces"
"github.com/SigNoz/signoz/ee/query-service/rules"
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/http/middleware"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/query-service/auth"
"github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/web"
@@ -50,6 +49,7 @@ import (
"github.com/SigNoz/signoz/pkg/query-service/healthcheck"
baseint "github.com/SigNoz/signoz/pkg/query-service/interfaces"
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
pqle "github.com/SigNoz/signoz/pkg/query-service/pqlEngine"
baserules "github.com/SigNoz/signoz/pkg/query-service/rules"
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
"github.com/SigNoz/signoz/pkg/query-service/utils"
@@ -137,16 +137,18 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
// set license manager as feature flag provider in dao
modelDao.SetFlagProvider(lm)
readerReady := make(chan bool)
fluxIntervalForTraceDetail, err := time.ParseDuration(serverOptions.FluxIntervalForTraceDetail)
if err != nil {
return nil, err
}
reader := db.NewDataConnector(
var reader interfaces.DataConnector
qb := db.NewDataConnector(
serverOptions.SigNoz.SQLStore.SQLxDB(),
serverOptions.SigNoz.TelemetryStore,
serverOptions.SigNoz.Prometheus,
serverOptions.SigNoz.TelemetryStore.ClickHouseDB(),
serverOptions.PromConfigPath,
lm,
serverOptions.Cluster,
serverOptions.UseLogsNewSchema,
@@ -154,6 +156,8 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
fluxIntervalForTraceDetail,
serverOptions.SigNoz.Cache,
)
go qb.Start(readerReady)
reader = qb
skipConfig := &basemodel.SkipConfig{}
if serverOptions.SkipTopLvlOpsPath != "" {
@@ -172,7 +176,9 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
c = cache.NewCache(cacheOpts)
}
<-readerReady
rm, err := makeRulesManager(
serverOptions.PromConfigPath,
serverOptions.RuleRepoURL,
serverOptions.SigNoz.SQLStore.SQLxDB(),
reader,
@@ -183,8 +189,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
serverOptions.UseTraceNewSchema,
serverOptions.SigNoz.Alertmanager,
serverOptions.SigNoz.SQLStore,
serverOptions.SigNoz.TelemetryStore,
serverOptions.SigNoz.Prometheus,
)
if err != nil {
@@ -229,7 +233,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
}
// start the usagemanager
usageManager, err := usage.New(modelDao, lm.GetRepo(), serverOptions.SigNoz.TelemetryStore.ClickhouseDB(), serverOptions.Config.TelemetryStore.Clickhouse.DSN)
usageManager, err := usage.New(modelDao, lm.GetRepo(), serverOptions.SigNoz.TelemetryStore.ClickHouseDB(), serverOptions.Config.TelemetryStore.ClickHouse.DSN)
if err != nil {
return nil, err
}
@@ -300,7 +304,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
&opAmpModel.AllAgents, agentConfMgr,
)
errorList := reader.PreloadMetricsMetadata(context.Background())
errorList := qb.PreloadMetricsMetadata(context.Background())
for _, er := range errorList {
zap.L().Error("failed to preload metrics metadata", zap.Error(er))
}
@@ -533,6 +537,7 @@ func (s *Server) Stop() error {
}
func makeRulesManager(
promConfigPath,
ruleRepoURL string,
db *sqlx.DB,
ch baseint.Reader,
@@ -543,13 +548,16 @@ func makeRulesManager(
useTraceNewSchema bool,
alertmanager alertmanager.Alertmanager,
sqlstore sqlstore.SQLStore,
telemetryStore telemetrystore.TelemetryStore,
prometheus prometheus.Prometheus,
) (*baserules.Manager, error) {
// create engine
pqle, err := pqle.FromConfigPath(promConfigPath)
if err != nil {
return nil, fmt.Errorf("failed to create pql engine : %v", err)
}
// create manager opts
managerOpts := &baserules.ManagerOptions{
TelemetryStore: telemetryStore,
Prometheus: prometheus,
PqlEngine: pqle,
RepoURL: ruleRepoURL,
DBConn: db,
Context: context.Background(),

View File

@@ -7,5 +7,6 @@ import (
// Connector defines methods for interaction
// with o11y data. for example - clickhouse
type DataConnector interface {
Start(readerReady chan bool)
baseint.Reader
}

View File

@@ -7,17 +7,17 @@ import (
"time"
"github.com/SigNoz/signoz/ee/query-service/app"
"github.com/SigNoz/signoz/ee/sqlstore/postgressqlstore"
"github.com/SigNoz/signoz/pkg/config"
"github.com/SigNoz/signoz/pkg/config/envprovider"
"github.com/SigNoz/signoz/pkg/config/fileprovider"
"github.com/SigNoz/signoz/pkg/query-service/auth"
baseconst "github.com/SigNoz/signoz/pkg/query-service/constants"
"github.com/SigNoz/signoz/pkg/signoz"
"github.com/SigNoz/signoz/pkg/sqlstore/sqlstorehook"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/version"
prommodel "github.com/prometheus/common/model"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
@@ -30,6 +30,10 @@ func initZapLog() *zap.Logger {
return logger
}
func init() {
prommodel.NameValidationScheme = prommodel.UTF8Validation
}
func main() {
var promConfigPath, skipTopLvlOpsPath string
@@ -83,7 +87,6 @@ func main() {
MaxIdleConns: maxIdleConns,
MaxOpenConns: maxOpenConns,
DialTimeout: dialTimeout,
Config: promConfigPath,
})
if err != nil {
zap.L().Fatal("Failed to create config", zap.Error(err))
@@ -91,21 +94,16 @@ func main() {
version.Info.PrettyPrint(config.Version)
sqlStoreFactories := signoz.NewSQLStoreProviderFactories()
if err := sqlStoreFactories.Add(postgressqlstore.NewFactory(sqlstorehook.NewLoggingFactory())); err != nil {
zap.L().Fatal("Failed to add postgressqlstore factory", zap.Error(err))
}
signoz, err := signoz.New(
context.Background(),
config,
signoz.NewCacheProviderFactories(),
signoz.NewWebProviderFactories(),
sqlStoreFactories,
signoz.NewSQLStoreProviderFactories(),
signoz.NewTelemetryStoreProviderFactories(),
)
if err != nil {
zap.L().Fatal("Failed to create signoz", zap.Error(err))
zap.L().Fatal("Failed to create signoz struct", zap.Error(err))
}
jwtSecret := os.Getenv("SIGNOZ_JWT_SECRET")

View File

@@ -157,13 +157,6 @@ var BasicPlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.TraceFunnels,
Active: false,
Usage: 0,
UsageLimit: -1,
Route: "",
},
}
var ProPlan = basemodel.FeatureSet{
@@ -286,13 +279,6 @@ var ProPlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.TraceFunnels,
Active: false,
Usage: 0,
UsageLimit: -1,
Route: "",
},
}
var EnterprisePlan = basemodel.FeatureSet{
@@ -429,11 +415,4 @@ var EnterprisePlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.TraceFunnels,
Active: false,
Usage: 0,
UsageLimit: -1,
Route: "",
},
}

View File

@@ -48,7 +48,7 @@ func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error)
opts.Rule,
opts.Logger,
opts.Reader,
opts.ManagerOpts.Prometheus,
opts.ManagerOpts.PqlEngine,
baserules.WithSQLStore(opts.SQLStore),
)
@@ -145,7 +145,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, *basemodel.Ap
parsedRule,
opts.Logger,
opts.Reader,
opts.ManagerOpts.Prometheus,
opts.ManagerOpts.PqlEngine,
baserules.WithSendAlways(),
baserules.WithSendUnmatched(),
baserules.WithSQLStore(opts.SQLStore),

View File

@@ -60,14 +60,10 @@
"INTEGRATIONS": "SigNoz | Integrations",
"ALERT_HISTORY": "SigNoz | Alert Rule History",
"ALERT_OVERVIEW": "SigNoz | Alert Rule Overview",
"MESSAGING_QUEUES_OVERVIEW": "SigNoz | Messaging Queues",
"MESSAGING_QUEUES_KAFKA": "SigNoz | Messaging Queues | Kafka",
"MESSAGING_QUEUES_KAFKA_DETAIL": "SigNoz | Messaging Queues | Kafka",
"MESSAGING_QUEUES_CELERY_TASK": "SigNoz | Messaging Queues | Celery",
"MESSAGING_QUEUES": "SigNoz | Messaging Queues",
"INFRASTRUCTURE_MONITORING_HOSTS": "SigNoz | Infra Monitoring",
"INFRASTRUCTURE_MONITORING_KUBERNETES": "SigNoz | Infra Monitoring",
"METRICS_EXPLORER": "SigNoz | Metrics Explorer",
"METRICS_EXPLORER_EXPLORER": "SigNoz | Metrics Explorer",
"METRICS_EXPLORER_VIEWS": "SigNoz | Metrics Explorer",
"API_MONITORING": "SigNoz | API Monitoring"
"METRICS_EXPLORER_VIEWS": "SigNoz | Metrics Explorer"
}

View File

@@ -26,7 +26,6 @@ import { Route, Router, Switch } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { extractDomain } from 'utils/app';
import { Home } from './pageComponents';
import PrivateRoute from './Private';
import defaultRoutes, {
AppRoutes,
@@ -311,7 +310,6 @@ function App(): JSX.Element {
/>
))}
<Route exact path="/" component={Home} />
<Route path="*" component={NotFound} />
</Switch>
</Suspense>

View File

@@ -207,7 +207,7 @@ export const PasswordReset = Loadable(
export const SomethingWentWrong = Loadable(
() =>
import(
/* webpackChunkName: "ErrorBoundaryFallback" */ 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'
/* webpackChunkName: "SomethingWentWrong" */ 'pages/SomethingWentWrong'
),
);
@@ -299,3 +299,10 @@ export const MetricsExplorer = Loadable(
export const ApiMonitoring = Loadable(
() => import(/* webpackChunkName: "ApiMonitoring" */ 'pages/ApiMonitoring'),
);
export const DynamicVariableTest = Loadable(
() =>
import(
/* webpackChunkName: "DynamicVariableTest" */ 'pages/DynamicVariableTest'
),
);

View File

@@ -15,6 +15,7 @@ import {
CustomDomainSettings,
DashboardPage,
DashboardWidget,
DynamicVariableTest,
EditAlertChannelsAlerts,
EditRulesPage,
ErrorDetails,
@@ -505,6 +506,13 @@ const routes: AppRoutes[] = [
key: 'API_MONITORING',
isPrivate: true,
},
{
path: ROUTES.DYNAMIC_VARIABLE_TEST,
exact: true,
component: DynamicVariableTest,
key: 'DYNAMIC_VARIABLE_TEST',
isPrivate: true,
},
];
export const SUPPORT_ROUTE: AppRoutes = {

View File

@@ -521,7 +521,7 @@ export default function CeleryOverviewTable({
locale={{
emptyText: isLoading ? null : <Typography.Text>No data</Typography.Text>,
}}
scroll={{ x: 'max-content' }}
scroll={{ x: true }}
showSorterTooltip
onDragColumn={handleDragColumn}
onRow={(record): { onClick: () => void; className: string } => ({

View File

@@ -18,7 +18,6 @@ function CopyClipboardHOC({
notifications.success({
message: notificationMessage,
key: notificationMessage,
});
}
}, [value, notifications, entityKey]);

View File

@@ -0,0 +1,231 @@
.multi-select-container {
position: relative;
width: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, sans-serif;
}
.multi-select-label {
margin-bottom: 4px;
font-size: 14px;
color: rgba(0, 0, 0, 0.85);
}
.multi-select-input {
width: 100%;
min-height: 40px;
border: 1px solid #d9d9d9;
border-radius: 6px;
padding: 4px 8px;
display: flex;
align-items: center;
cursor: text;
background-color: #fff;
transition: all 0.3s;
&:hover {
border-color: #40a9ff;
}
&:focus,
&.multi-select-input-focused {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
outline: 0;
}
}
.multi-select-chips {
display: flex;
flex-wrap: wrap;
width: 100%;
gap: 4px;
align-items: center;
}
.multi-select-chip {
background-color: #f0f0f0;
border-radius: 4px;
padding: 2px 8px;
display: flex;
align-items: center;
gap: 4px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
line-height: 22px;
.multi-select-chip-remove {
cursor: pointer;
font-size: 12px;
background: none;
border: none;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
color: rgba(0, 0, 0, 0.45);
&:hover {
color: #ff4d4f;
}
}
}
.multi-select-search {
flex: 1;
min-width: 50px;
border: none;
outline: none;
background: transparent;
&:focus {
border: none;
box-shadow: none;
}
// Override Ant Design's styles
.ant-input {
background: transparent;
&:focus {
box-shadow: none;
}
}
}
.multi-select-clear-all {
background: none;
border: none;
padding: 4px;
cursor: pointer;
color: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
&:hover {
color: #ff4d4f;
}
}
.multi-select-dropdown {
position: absolute;
top: 100%;
left: 0;
width: 100%;
max-height: 400px;
border: 1px solid #d9d9d9;
border-radius: 6px;
margin-top: 4px;
background-color: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 1050;
overflow: hidden;
display: flex;
flex-direction: column;
}
.multi-select-option {
padding: 8px 12px;
cursor: pointer;
&:hover {
background-color: #f5f5f5;
}
}
.multi-select-divider {
height: 1px;
background-color: #f0f0f0;
margin: 0;
}
.multi-select-section-label {
padding: 8px 12px;
font-weight: 500;
color: rgba(0, 0, 0, 0.45);
font-size: 12px;
background-color: #fafafa;
}
.multi-select-loading {
padding: 12px;
display: flex;
align-items: center;
gap: 12px;
justify-content: center;
}
.multi-select-no-results {
padding: 12px;
text-align: center;
color: rgba(0, 0, 0, 0.45);
}
.multi-select-options-container,
.multi-select-section-content {
overflow-y: auto;
/* For WebKit browsers */
&::-webkit-scrollbar {
width: 6px;
height: 6px;
}
&::-webkit-scrollbar-track {
background: #f0f0f0;
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 3px;
}
&::-webkit-scrollbar-thumb:hover {
background: #aaa;
}
/* For Firefox */
scrollbar-width: thin;
scrollbar-color: #ccc #f0f0f0;
}
.multi-select-error {
border-color: #ff4d4f;
&:hover,
&:focus,
&.multi-select-input-focused {
border-color: #ff7875;
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2);
}
}
.multi-select-error-text {
color: #ff4d4f;
font-size: 14px;
line-height: 1.5;
margin-top: 4px;
}
.multi-select-disabled {
.multi-select-input {
background-color: #f5f5f5;
cursor: not-allowed;
color: rgba(0, 0, 0, 0.25);
border-color: #d9d9d9;
&:hover {
border-color: #d9d9d9;
}
}
.multi-select-chip {
color: rgba(0, 0, 0, 0.25);
background-color: #eee;
}
}

View File

@@ -0,0 +1,595 @@
/* eslint-disable sonarjs/cognitive-complexity */
import './MultiSelect.styles.scss';
import { CloseOutlined, SearchOutlined } from '@ant-design/icons';
import { Checkbox, Input, Spin } from 'antd';
import { CheckboxChangeEvent } from 'antd/es/checkbox';
import { InputRef } from 'antd/lib/input';
import { useCallback, useEffect, useRef, useState } from 'react';
export interface MultiSelectOption {
label: string;
value: string;
selected?: boolean;
disabled?: boolean;
}
export interface MultiSelectSection {
title: string;
options: MultiSelectOption[];
}
export interface MultiSelectProps {
/** Array of options to display in the dropdown */
options: MultiSelectOption[];
/** Callback when selected values change */
onChange: (selectedValues: string[]) => void;
/** Currently selected values */
value?: string[];
/** Placeholder text for the search input */
placeholder?: string;
/** Whether the component is in loading state */
loading?: boolean;
/** Allow users to add custom values */
allowCustomValues?: boolean;
/** Callback when search text changes - can be used for server filtering */
onSearch?: (searchText: string) => void;
/** Custom class name */
className?: string;
/** Additional sections to display (e.g., "Related Values") */
additionalSections?: MultiSelectSection[];
/** Show "Select All" option */
showSelectAll?: boolean;
/** Maximum height of dropdown in pixels */
dropdownMaxHeight?: number;
/** Maximum width of dropdown in pixels (defaults to matching input width) */
dropdownMaxWidth?: number;
/** Disable the component */
disabled?: boolean;
/** Error message to display */
error?: string;
/** Label text */
label?: string;
/** Allow users to clear all selections */
allowClear?: boolean;
/** Maximum height of a section */
sectionMaxHeight?: number;
}
function MultiSelect({
options,
onChange,
value = [],
placeholder = 'Search...',
loading = false,
allowCustomValues = true,
onSearch,
className = '',
additionalSections = [],
showSelectAll = true,
dropdownMaxHeight = 400,
dropdownMaxWidth,
disabled = false,
error,
label,
allowClear = true,
sectionMaxHeight = 150,
}: MultiSelectProps): JSX.Element {
const [isDropdownOpen, setIsDropdownOpen] = useState<boolean>(false);
const [searchText, setSearchText] = useState<string>('');
const [selectedValues, setSelectedValues] = useState<string[]>(value);
const [displayOptions, setDisplayOptions] = useState<MultiSelectOption[]>(
options,
);
const containerRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<InputRef>(null);
const dropdownRef = useRef<HTMLDivElement>(null);
const [focusedChipIndex, setFocusedChipIndex] = useState<number>(-1);
const chipRefs = useRef<(HTMLDivElement | null)[]>([]);
// Handle save action - memoize with useCallback
const handleSave = useCallback((): void => {
setIsDropdownOpen(false);
setSearchText('');
onChange(selectedValues);
}, [onChange, selectedValues]);
// Synchronize value prop with internal state
useEffect(() => {
setSelectedValues(value);
}, [value]);
// Filter and sort options based on search text
useEffect(() => {
// Filter options based on search text
const filteredOptions = options.filter((option) =>
option.label.toLowerCase().includes(searchText.toLowerCase()),
);
// Add custom value option if no matches found and allowCustomValues is true
if (
allowCustomValues &&
searchText &&
!filteredOptions.some(
(option) => option.label.toLowerCase() === searchText.toLowerCase(),
) &&
!filteredOptions.some(
(option) => option.value.toLowerCase() === searchText.toLowerCase(),
)
) {
filteredOptions.unshift({
label: `Add "${searchText}"`,
value: searchText,
});
}
// Sort options: selected first, then matching search term
const sortedOptions = [...filteredOptions].sort((a, b) => {
// First by selection status
if (selectedValues.includes(a.value) && !selectedValues.includes(b.value))
return -1;
if (!selectedValues.includes(a.value) && selectedValues.includes(b.value))
return 1;
// Then by match position (exact matches or starts with come first)
const aLower = a.label.toLowerCase();
const bLower = b.label.toLowerCase();
const searchLower = searchText.toLowerCase();
if (aLower === searchLower && bLower !== searchLower) return -1;
if (aLower !== searchLower && bLower === searchLower) return 1;
if (aLower.startsWith(searchLower) && !bLower.startsWith(searchLower))
return -1;
if (!aLower.startsWith(searchLower) && bLower.startsWith(searchLower))
return 1;
return 0;
});
setDisplayOptions(sortedOptions);
}, [options, searchText, selectedValues, allowCustomValues]);
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent): void => {
if (
containerRef.current &&
!containerRef.current.contains(event.target as Node)
) {
handleSave();
}
};
document.addEventListener('mousedown', handleClickOutside);
return (): void => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [selectedValues, handleSave]);
// Adjust dropdown position if needed
useEffect(() => {
if (isDropdownOpen && dropdownRef.current && containerRef.current) {
const containerRect = containerRef.current.getBoundingClientRect();
const dropdownHeight = dropdownRef.current.offsetHeight;
const viewportHeight = window.innerHeight;
// Check if dropdown extends beyond viewport bottom
if (
containerRect.bottom + dropdownHeight > viewportHeight &&
containerRect.top > dropdownHeight
) {
dropdownRef.current.style.top = 'auto';
dropdownRef.current.style.bottom = '100%';
dropdownRef.current.style.marginTop = '0';
dropdownRef.current.style.marginBottom = '4px';
}
}
}, [isDropdownOpen, displayOptions]);
// Handle search input change
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
const text = e.target.value;
setSearchText(text);
if (onSearch) {
onSearch(text);
}
};
// Handle selection change
const handleSelectionChange = (
option: MultiSelectOption,
e: CheckboxChangeEvent,
): void => {
const { checked } = e.target;
let newSelectedValues: string[];
if (checked) {
newSelectedValues = [...selectedValues, option.value];
} else {
newSelectedValues = selectedValues.filter((val) => val !== option.value);
}
setSelectedValues(newSelectedValues);
};
// Handle "All" checkbox change
const handleSelectAll = (e: CheckboxChangeEvent): void => {
if (e.target.checked) {
const allValues = options
.filter((option) => !option.disabled)
.map((option) => option.value);
setSelectedValues(allValues);
} else {
setSelectedValues([]);
}
};
// Remove a selected item
const handleRemoveItem = useCallback(
(value: string): void => {
const newSelectedValues = selectedValues.filter((val) => val !== value);
setSelectedValues(newSelectedValues);
},
[selectedValues],
);
// Handle clicking the input area
const handleInputClick = (): void => {
if (!disabled) {
setIsDropdownOpen(true);
inputRef.current?.focus();
}
};
// Handle clear all selections
const handleClearAll = (): void => {
setSelectedValues([]);
setSearchText('');
inputRef.current?.focus();
};
// Get display value of a selection (chips)
const getSelectedOptions = (): MultiSelectOption[] =>
selectedValues.map((value) => {
const option = options.find((opt) => opt.value === value);
return {
label: option?.label || value,
value,
};
});
const selectedOptions = getSelectedOptions();
const allSelectableOptions = options.filter((option) => !option.disabled);
const allSelected =
allSelectableOptions.length > 0 &&
selectedValues.length === allSelectableOptions.length;
const containerClasses = [
'multi-select-container',
className,
disabled ? 'multi-select-disabled' : '',
error ? 'multi-select-error' : '',
]
.filter(Boolean)
.join(' ');
const inputClasses = [
'multi-select-input',
isDropdownOpen ? 'multi-select-input-focused' : '',
]
.filter(Boolean)
.join(' ');
// Reset chip refs array when selected options change
useEffect(() => {
chipRefs.current = Array(selectedOptions.length).fill(null);
}, [selectedOptions.length]);
// Handle chip keyboard navigation
const handleChipKeyDown = useCallback(
(e: React.KeyboardEvent, index: number) => {
e.stopPropagation(); // Prevent bubbling to container
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
// Move focus to previous chip
if (index > 0) {
setFocusedChipIndex(index - 1);
}
break;
case 'ArrowRight':
e.preventDefault();
// Move focus to next chip or input
if (index < selectedOptions.length - 1) {
setFocusedChipIndex(index + 1);
} else {
// Focus the input when at the last chip
setFocusedChipIndex(-1);
inputRef.current?.focus();
}
break;
case 'Delete':
case 'Backspace':
e.preventDefault();
// Remove current chip
handleRemoveItem(selectedOptions[index].value);
// Adjust focus after deletion
if (selectedOptions.length > 1) {
// Focus previous chip if not at beginning
const newIndex = Math.min(index, selectedOptions.length - 2);
setFocusedChipIndex(newIndex);
} else {
// If this was the last chip, focus input
setFocusedChipIndex(-1);
inputRef.current?.focus();
}
break;
case 'Escape':
e.preventDefault();
// Return focus to input
setFocusedChipIndex(-1);
inputRef.current?.focus();
break;
default:
// No-op for unhandled keys
break;
}
},
[selectedOptions, handleRemoveItem],
);
// Handle key events in the input
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {
if (e.key === 'Enter') {
// Add custom value on Enter if it doesn't exist
if (
allowCustomValues &&
searchText &&
!options.some(
(option) => option.value.toLowerCase() === searchText.toLowerCase(),
) &&
!options.some(
(option) => option.label.toLowerCase() === searchText.toLowerCase(),
)
) {
const newSelectedValues = [...selectedValues, searchText];
setSelectedValues(newSelectedValues);
setSearchText('');
} else if (isDropdownOpen) {
handleSave();
}
} else if (e.key === 'Escape') {
handleSave();
} else if (
e.key === 'Backspace' &&
!searchText &&
selectedValues.length > 0
) {
// Remove the last selected item when pressing backspace in an empty input
const newSelectedValues = [...selectedValues];
newSelectedValues.pop();
setSelectedValues(newSelectedValues);
} else if (e.key === 'Tab' && isDropdownOpen) {
// Close dropdown but keep focus within component
e.preventDefault();
handleSave();
}
// Add navigation TO chips when in input field
if (e.key === 'ArrowLeft' && !searchText && selectedOptions.length > 0) {
e.preventDefault();
setFocusedChipIndex(selectedOptions.length - 1);
}
};
// Focus the appropriate chip when focusedChipIndex changes
useEffect(() => {
if (focusedChipIndex >= 0 && chipRefs.current[focusedChipIndex]) {
chipRefs.current[focusedChipIndex]?.focus();
}
}, [focusedChipIndex]);
return (
<div className={containerClasses} ref={containerRef}>
{label && <div className="multi-select-label">{label}</div>}
<div
className={inputClasses}
onClick={handleInputClick}
onKeyDown={(e): void => {
if (e.key === 'Enter' || e.key === ' ') {
handleInputClick();
}
}}
role="combobox"
aria-expanded={isDropdownOpen}
aria-haspopup="listbox"
aria-controls="multi-select-dropdown"
aria-owns="multi-select-dropdown"
tabIndex={disabled ? -1 : 0}
>
<div className="multi-select-chips">
{selectedOptions.map((option, index) => (
<div
key={option.value}
className={`multi-select-chip ${
focusedChipIndex === index ? 'multi-select-chip-focused' : ''
}`}
ref={(el): void => {
chipRefs.current[index] = el;
}}
role="button"
tabIndex={0}
onKeyDown={(e): void => handleChipKeyDown(e, index)}
onFocus={(): void => setFocusedChipIndex(index)}
onClick={(e): void => e.stopPropagation()}
aria-label={`Selected option: ${option.label}`}
>
{option.label}
{!disabled && (
<button
type="button"
className="multi-select-chip-remove"
onClick={(e): void => {
e.stopPropagation();
handleRemoveItem(option.value);
}}
aria-label={`Remove ${option.label}`}
tabIndex={-1} // Don't make the inner button tabbable
>
<CloseOutlined />
</button>
)}
</div>
))}
<Input
ref={inputRef}
className="multi-select-search"
placeholder={selectedOptions.length === 0 ? placeholder : ''}
value={searchText}
onChange={handleSearchChange}
onKeyDown={handleKeyDown}
onFocus={(): void => setIsDropdownOpen(true)}
suffix={<SearchOutlined />}
bordered={false}
disabled={disabled}
/>
{allowClear && selectedValues.length > 0 && !disabled && (
<button
type="button"
className="multi-select-clear-all"
onClick={(e): void => {
e.stopPropagation();
handleClearAll();
}}
aria-label="Clear all selections"
>
<CloseOutlined />
</button>
)}
</div>
</div>
{error && <div className="multi-select-error-text">{error}</div>}
{isDropdownOpen && !disabled && (
<div
className="multi-select-dropdown"
ref={dropdownRef}
style={{
maxHeight: `${dropdownMaxHeight}px`,
maxWidth: dropdownMaxWidth ? `${dropdownMaxWidth}px` : undefined,
}}
id="multi-select-dropdown"
role="listbox"
aria-multiselectable="true"
>
{loading ? (
<div className="multi-select-loading">
<Spin size="small" />
<span>We are updating the values ...</span>
</div>
) : (
<>
{showSelectAll && (
<>
<div className="multi-select-option">
<Checkbox checked={allSelected} onChange={handleSelectAll}>
ALL
</Checkbox>
</div>
<div className="multi-select-divider" />
</>
)}
<div
className="multi-select-options-container"
style={{ maxHeight: `${sectionMaxHeight}px` }}
>
{displayOptions.length > 0 ? (
displayOptions.map((option) => (
<div
key={option.value}
className="multi-select-option"
role="option"
aria-selected={selectedValues.includes(option.value)}
>
<Checkbox
checked={selectedValues.includes(option.value)}
onChange={(e): void => handleSelectionChange(option, e)}
disabled={option.disabled}
>
{option.label}
</Checkbox>
</div>
))
) : (
<div className="multi-select-no-results">
{allowCustomValues && searchText
? `Add "${searchText}"`
: 'No results found'}
</div>
)}
</div>
{additionalSections.map(
(section) =>
section.options.length > 0 && (
<div key={`section-${section.title}`}>
<div className="multi-select-divider" />
<div className="multi-select-section-label">{section.title}</div>
<div
className="multi-select-section-content"
style={{ maxHeight: `${sectionMaxHeight}px` }}
>
{section.options.map((option) => (
<div
key={option.value}
className="multi-select-option"
role="option"
aria-selected={selectedValues.includes(option.value)}
>
<Checkbox
checked={selectedValues.includes(option.value)}
onChange={(e): void => handleSelectionChange(option, e)}
disabled={option.disabled}
>
{option.label}
</Checkbox>
</div>
))}
</div>
</div>
),
)}
</>
)}
</div>
)}
</div>
);
}
// Define defaultProps to fix linter warnings
MultiSelect.defaultProps = {
value: [],
placeholder: 'Search...',
loading: false,
allowCustomValues: true,
onSearch: undefined,
className: '',
additionalSections: [],
showSelectAll: true,
dropdownMaxHeight: 400,
dropdownMaxWidth: undefined,
disabled: false,
error: undefined,
label: undefined,
allowClear: true,
sectionMaxHeight: 150,
};
export default MultiSelect;

View File

@@ -0,0 +1,8 @@
import MultiSelect from './MultiSelect';
export type {
MultiSelectOption,
MultiSelectProps,
MultiSelectSection,
} from './MultiSelect';
export default MultiSelect;

View File

@@ -1,5 +1,3 @@
import './ResizeTable.styles.scss';
import { SyntheticEvent, useMemo } from 'react';
import { Resizable, ResizeCallbackData } from 'react-resizable';
@@ -12,8 +10,8 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element {
const handle = useMemo(
() => (
<SpanStyle
className="react-resizable-handle"
onClick={(e): void => e.stopPropagation()}
className="resize-handle"
/>
),
[],
@@ -21,7 +19,7 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element {
if (!width) {
// eslint-disable-next-line react/jsx-props-no-spreading
return <th {...restProps} className="resizable-header" />;
return <th {...restProps} />;
}
return (
@@ -31,10 +29,9 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element {
handle={handle}
onResize={onResize}
draggableOpts={enableUserSelectHack}
minConstraints={[150, 0]}
>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<th {...restProps} className="resizable-header" />
<th {...restProps} />
</Resizable>
);
}

View File

@@ -1,53 +0,0 @@
.resizable-header {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
position: relative;
.ant-table-column-title {
white-space: normal;
overflow: hidden;
text-overflow: ellipsis;
}
}
.resize-main-table {
.ant-table-body {
.ant-table-tbody {
.ant-table-row {
.ant-table-cell {
.ant-typography {
white-space: unset;
}
}
}
}
}
}
.logs-table,
.traces-table {
.resize-table {
.resize-handle {
position: absolute;
top: 0;
bottom: 0;
inset-inline-end: -5px;
width: 10px;
cursor: col-resize;
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 1px;
height: 1.6em;
background-color: var(--bg-slate-200);
transition: background-color 0.2s;
}
}
}
}

View File

@@ -2,63 +2,35 @@
import { Table } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import cx from 'classnames';
import { dragColumnParams } from 'hooks/useDragColumns/configs';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { debounce, set } from 'lodash-es';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { set } from 'lodash-es';
import {
SyntheticEvent,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import ReactDragListView from 'react-drag-listview';
import { ResizeCallbackData } from 'react-resizable';
import { Widgets } from 'types/api/dashboard/getAll';
import ResizableHeader from './ResizableHeader';
import { DragSpanStyle } from './styles';
import { ResizeTableProps } from './types';
// eslint-disable-next-line sonarjs/cognitive-complexity
function ResizeTable({
columns,
onDragColumn,
pagination,
widgetId,
shouldPersistColumnWidths = false,
...restProps
}: ResizeTableProps): JSX.Element {
const [columnsData, setColumns] = useState<ColumnsType>([]);
const { setColumnWidths, selectedDashboard } = useDashboard();
const columnWidths = shouldPersistColumnWidths
? (selectedDashboard?.data?.widgets?.find(
(widget) => widget.id === widgetId,
) as Widgets)?.columnWidths
: undefined;
const updateAllColumnWidths = useRef(
debounce((widthsConfig: Record<string, number>) => {
if (!widgetId || !shouldPersistColumnWidths) return;
setColumnWidths?.((prev) => ({
...prev,
[widgetId]: widthsConfig,
}));
}, 1000),
).current;
const handleResize = useCallback(
(index: number) => (
e: SyntheticEvent<Element>,
_e: SyntheticEvent<Element>,
{ size }: ResizeCallbackData,
): void => {
e.preventDefault();
e.stopPropagation();
const newColumns = [...columnsData];
newColumns[index] = {
...newColumns[index],
@@ -93,7 +65,6 @@ function ResizeTable({
...restProps,
components: { header: { cell: ResizableHeader } },
columns: mergedColumns,
className: cx('resize-main-table', restProps.className),
};
set(
@@ -107,39 +78,9 @@ function ResizeTable({
useEffect(() => {
if (columns) {
// Apply stored column widths from widget configuration
const columnsWithStoredWidths = columns.map((col) => {
const dataIndex = (col as RowData).dataIndex as string;
if (dataIndex && columnWidths && columnWidths[dataIndex]) {
return {
...col,
width: columnWidths[dataIndex], // Apply stored width
};
}
return col;
});
setColumns(columnsWithStoredWidths);
setColumns(columns);
}
}, [columns, columnWidths]);
useEffect(() => {
if (!shouldPersistColumnWidths) return;
// Collect all column widths in a single object
const newColumnWidths: Record<string, number> = {};
mergedColumns.forEach((col) => {
if (col.width && (col as RowData).dataIndex) {
const dataIndex = (col as RowData).dataIndex as string;
newColumnWidths[dataIndex] = col.width as number;
}
});
// Only update if there are actual widths to set
if (Object.keys(newColumnWidths).length > 0) {
updateAllColumnWidths(newColumnWidths);
}
}, [mergedColumns, updateAllColumnWidths, shouldPersistColumnWidths]);
}, [columns]);
return onDragColumn ? (
<ReactDragListView.DragColumn {...dragColumnParams} onDragEnd={onDragColumn}>

View File

@@ -8,8 +8,6 @@ export const SpanStyle = styled.span`
width: 0.625rem;
height: 100%;
cursor: col-resize;
margin-left: 4px;
margin-right: 4px;
`;
export const DragSpanStyle = styled.span`

View File

@@ -9,8 +9,6 @@ import { TableDataSource } from './contants';
export interface ResizeTableProps extends TableProps<any> {
onDragColumn?: (fromIndex: number, toIndex: number) => void;
widgetId?: string;
shouldPersistColumnWidths?: boolean;
}
export interface DynamicColumnTableProps extends TableProps<any> {
tablesource: typeof TableDataSource[keyof typeof TableDataSource];

View File

@@ -25,6 +25,4 @@ export enum FeatureKeys {
ANOMALY_DETECTION = 'ANOMALY_DETECTION',
AWS_INTEGRATION = 'AWS_INTEGRATION',
ONBOARDING_V3 = 'ONBOARDING_V3',
THIRD_PARTY_API = 'THIRD_PARTY_API',
TRACE_FUNNELS = 'TRACE_FUNNELS',
}

View File

@@ -75,6 +75,7 @@ const ROUTES = {
METRICS_EXPLORER_BASE: '/metrics-explorer',
WORKSPACE_ACCESS_RESTRICTED: '/workspace-access-restricted',
HOME_PAGE: '/',
DYNAMIC_VARIABLE_TEST: '/dynamic-variable-test',
} as const;
export default ROUTES;

View File

@@ -1,4 +1,3 @@
import ROUTES from 'constants/routes';
import AlertChannels from 'container/AllAlertChannels';
import { allAlertChannels } from 'mocks-server/__mockdata__/alerts';
import { act, fireEvent, render, screen, waitFor } from 'tests/test-utils';
@@ -21,13 +20,6 @@ jest.mock('hooks/useNotifications', () => ({
})),
}));
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: (): { pathname: string } => ({
pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.ALL_CHANNELS}`,
}),
}));
describe('Alert Channels Settings List page', () => {
beforeEach(() => {
render(<AlertChannels />);

View File

@@ -1,4 +1,3 @@
import ROUTES from 'constants/routes';
import AlertChannels from 'container/AllAlertChannels';
import { allAlertChannels } from 'mocks-server/__mockdata__/alerts';
import { fireEvent, render, screen, waitFor } from 'tests/test-utils';
@@ -26,13 +25,6 @@ jest.mock('hooks/useComponentPermission', () => ({
default: jest.fn().mockImplementation(() => [false]),
}));
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: (): { pathname: string } => ({
pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.ALL_CHANNELS}`,
}),
}));
describe('Alert Channels Settings List page (Normal User)', () => {
beforeEach(() => {
render(<AlertChannels />);

View File

@@ -1,6 +1,5 @@
import { LoadingOutlined } from '@ant-design/icons';
import { Select, Spin, Table, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import { ENTITY_VERSION_V4 } from 'constants/app';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import {
@@ -152,7 +151,6 @@ function AllEndPoints({
if (groupBy.length === 0) {
setSelectedEndPointName(record.endpointName); // this will open up the endpoint details tab
setSelectedView(VIEW_TYPES.ENDPOINT_DETAILS);
logEvent('API Monitoring: Endpoint name row clicked', {});
} else {
handleGroupByRowClick(record); // this will prepare the nested query payload
}

View File

@@ -392,39 +392,6 @@
gap: 20px;
padding-top: 20px;
.endpoint-meta-data {
display: flex;
gap: 8px;
.endpoint-meta-data-pill {
display: flex;
align-items: flex-start;
border-radius: 4px;
border: 1px solid var(--bg-slate-300);
width: fit-content;
.endpoint-meta-data-label {
display: flex;
padding: 6px 8px;
align-items: center;
gap: 4px;
border-right: 1px solid var(--bg-slate-300);
color: var(--text-vanilla-100);
background: var(--bg-slate-500);
height: calc(100% - 12px);
}
.endpoint-meta-data-value {
display: flex;
padding: 6px 8px;
justify-content: center;
align-items: center;
gap: 10px;
color: var(--text-vanilla-400);
background: var(--bg-slate-400);
height: calc(100% - 12px);
}
}
}
.endpoint-details-filters-container {
display: flex;
flex-direction: row;
@@ -438,13 +405,6 @@
}
}
.ant-select-item,
.ant-select-item-option-content {
flex: auto;
white-space: normal;
overflow-wrap: break-word;
}
.status-code-table-container {
border-radius: 3px;
border: 1px solid var(--bg-slate-500);
@@ -849,13 +809,6 @@
width: 100%;
}
}
.ant-select-item,
.ant-select-item-option-content {
flex: auto;
white-space: normal;
overflow-wrap: break-word;
}
}
.lightMode {
@@ -964,20 +917,6 @@
}
}
.endpoint-meta-data {
.endpoint-meta-data-pill {
.endpoint-meta-data-label {
color: var(--text-ink-300);
background: var(--bg-vanilla-100);
}
.endpoint-meta-data-value {
color: var(--text-ink-300);
background: var(--bg-vanilla-100);
}
}
}
.status-code-table-container {
.ant-table {
.ant-table-thead > tr > th {

View File

@@ -19,14 +19,12 @@ function DomainDetails({
selectedDomainIndex,
setSelectedDomainIndex,
domainListLength,
domainListFilters,
}: {
domainData: any;
handleClose: () => void;
selectedDomainIndex: number;
setSelectedDomainIndex: (index: number) => void;
domainListLength: number;
domainListFilters: IBuilderQuery['filters'];
}): JSX.Element {
const [selectedView, setSelectedView] = useState<VIEWS>(VIEWS.ALL_ENDPOINTS);
const [selectedEndPointName, setSelectedEndPointName] = useState<string>('');
@@ -134,7 +132,6 @@ function DomainDetails({
domainName={domainData.domainName}
endPointName={selectedEndPointName}
setSelectedEndPointName={setSelectedEndPointName}
domainListFilters={domainListFilters}
/>
)}
</>

View File

@@ -2,10 +2,7 @@ import { ENTITY_VERSION_V4 } from 'constants/app';
import { initialQueriesMap } from 'constants/queryBuilder';
import {
END_POINT_DETAILS_QUERY_KEYS_ARRAY,
extractPortAndEndpoint,
getEndPointDetailsQueryPayload,
getLatencyOverTimeWidgetData,
getRateOverTimeWidgetData,
} from 'container/ApiMonitoring/utils';
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
@@ -30,12 +27,10 @@ function EndPointDetails({
domainName,
endPointName,
setSelectedEndPointName,
domainListFilters,
}: {
domainName: string;
endPointName: string;
setSelectedEndPointName: (value: string) => void;
domainListFilters: IBuilderQuery['filters'];
}): JSX.Element {
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
@@ -106,6 +101,8 @@ function EndPointDetails({
const [
endPointMetricsDataQuery,
endPointStatusCodeDataQuery,
endPointRateOverTimeDataQuery,
endPointLatencyOverTimeDataQuery,
endPointDropDownDataQuery,
endPointDependentServicesDataQuery,
endPointStatusCodeBarChartsDataQuery,
@@ -118,29 +115,12 @@ function EndPointDetails({
endPointDetailsDataQueries[3],
endPointDetailsDataQueries[4],
endPointDetailsDataQueries[5],
endPointDetailsDataQueries[6],
endPointDetailsDataQueries[7],
],
[endPointDetailsDataQueries],
);
const { endpoint, port } = useMemo(
() => extractPortAndEndpoint(endPointName),
[endPointName],
);
const [rateOverTimeWidget, latencyOverTimeWidget] = useMemo(
() => [
getRateOverTimeWidgetData(domainName, endPointName, {
items: [...domainListFilters.items, ...filters.items],
op: filters.op,
}),
getLatencyOverTimeWidgetData(domainName, endPointName, {
items: [...domainListFilters.items, ...filters.items],
op: filters.op,
}),
],
[domainName, endPointName, filters, domainListFilters],
);
return (
<div className="endpoint-details-container">
<div className="endpoint-details-filters-container">
@@ -149,8 +129,6 @@ function EndPointDetails({
selectedEndPointName={endPointName}
setSelectedEndPointName={setSelectedEndPointName}
endPointDropDownDataQuery={endPointDropDownDataQuery}
parentContainerDiv=".endpoint-details-filters-container"
dropdownStyle={{ width: 'calc(100% - 36px)' }}
/>
</div>
<div className="endpoint-details-filters-container-search">
@@ -163,16 +141,6 @@ function EndPointDetails({
/>
</div>
</div>
<div className="endpoint-meta-data">
<div className="endpoint-meta-data-pill">
<div className="endpoint-meta-data-label">Endpoint</div>
<div className="endpoint-meta-data-value">{endpoint || '-'}</div>
</div>
<div className="endpoint-meta-data-pill">
<div className="endpoint-meta-data-label">Port</div>
<div className="endpoint-meta-data-value">{port || '-'}</div>
</div>
</div>
<EndPointMetrics endPointMetricsDataQuery={endPointMetricsDataQuery} />
{!isServicesFilterApplied && (
<DependentServices
@@ -184,14 +152,18 @@ function EndPointDetails({
endPointStatusCodeLatencyBarChartsDataQuery={
endPointStatusCodeLatencyBarChartsDataQuery
}
domainName={domainName}
endPointName={endPointName}
domainListFilters={domainListFilters}
filters={filters}
/>
<StatusCodeTable endPointStatusCodeDataQuery={endPointStatusCodeDataQuery} />
<MetricOverTimeGraph widget={rateOverTimeWidget} />
<MetricOverTimeGraph widget={latencyOverTimeWidget} />
<MetricOverTimeGraph
metricOverTimeDataQuery={endPointRateOverTimeDataQuery}
widgetInfoIndex={0}
endPointName={endPointName}
/>
<MetricOverTimeGraph
metricOverTimeDataQuery={endPointLatencyOverTimeDataQuery}
widgetInfoIndex={1}
endPointName={endPointName}
/>
</div>
);
}

View File

@@ -8,7 +8,6 @@ import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { SuccessResponse } from 'types/api';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import EndPointDetailsZeroState from './components/EndPointDetailsZeroState';
@@ -18,12 +17,10 @@ function EndPointDetailsWrapper({
domainName,
endPointName,
setSelectedEndPointName,
domainListFilters,
}: {
domainName: string;
endPointName: string;
setSelectedEndPointName: (value: string) => void;
domainListFilters: IBuilderQuery['filters'];
}): JSX.Element {
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
@@ -72,7 +69,6 @@ function EndPointDetailsWrapper({
domainName={domainName}
endPointName={endPointName}
setSelectedEndPointName={setSelectedEndPointName}
domainListFilters={domainListFilters}
/>
);
}

View File

@@ -28,8 +28,6 @@ function EndPointDetailsZeroState({
<EndPointsDropDown
setSelectedEndPointName={setSelectedEndPointName}
endPointDropDownDataQuery={endPointDropDownDataQuery}
parentContainerDiv=".end-point-details-zero-state-wrapper"
dropdownStyle={{ width: '60%' }}
/>
</div>
</div>

View File

@@ -70,7 +70,7 @@ function EndPointMetrics({
<Skeleton.Button active size="small" />
) : (
<Tooltip title={metricsData?.rate}>
<span className="round-metric-tag">{metricsData?.rate} ops/sec</span>
<span className="round-metric-tag">{metricsData?.rate}/sec</span>
</Tooltip>
)}
</Typography.Text>

View File

@@ -8,22 +8,16 @@ interface EndPointsDropDownProps {
selectedEndPointName?: string;
setSelectedEndPointName: (value: string) => void;
endPointDropDownDataQuery: UseQueryResult<SuccessResponse<any>, unknown>;
parentContainerDiv?: string;
dropdownStyle?: React.CSSProperties;
}
const defaultProps = {
selectedEndPointName: '',
parentContainerDiv: '',
dropdownStyle: {},
};
function EndPointsDropDown({
selectedEndPointName,
setSelectedEndPointName,
endPointDropDownDataQuery,
parentContainerDiv,
dropdownStyle,
}: EndPointsDropDownProps): JSX.Element {
const { data, isLoading, isFetching } = endPointDropDownDataQuery;
@@ -45,13 +39,6 @@ function EndPointsDropDown({
style={{ width: '100%' }}
onChange={handleChange}
options={formattedData}
getPopupContainer={
parentContainerDiv
? (): HTMLElement =>
document.querySelector(parentContainerDiv) as HTMLElement
: (triggerNode): HTMLElement => triggerNode.parentNode as HTMLElement
}
dropdownStyle={dropdownStyle}
/>
);
}

View File

@@ -1,7 +1,6 @@
import { LoadingOutlined } from '@ant-design/icons';
import { Spin, Table } from 'antd';
import { ColumnType } from 'antd/lib/table';
import logEvent from 'api/common/logEvent';
import { ENTITY_VERSION_V4 } from 'constants/app';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import {
@@ -115,7 +114,6 @@ function ExpandedRow({
onClick: (): void => {
setSelectedEndPointName(record.endpointName);
setSelectedView(VIEW_TYPES.ENDPOINT_DETAILS);
logEvent('API Monitoring: Endpoint name row clicked', {});
},
className: 'expanded-clickable-row',
})}

View File

@@ -1,18 +1,110 @@
import { Card } from 'antd';
import GridCard from 'container/GridCardLayout/GridCard';
import { Widgets } from 'types/api/dashboard/getAll';
import { Card, Skeleton, Typography } from 'antd';
import cx from 'classnames';
import Uplot from 'components/Uplot';
import { PANEL_TYPES } from 'constants/queryBuilder';
import {
apiWidgetInfo,
extractPortAndEndpoint,
getFormattedChartData,
} from 'container/ApiMonitoring/utils';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useCallback, useMemo, useRef } from 'react';
import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { SuccessResponse } from 'types/api';
import { GlobalReducer } from 'types/reducer/globalTime';
import { Options } from 'uplot';
import ErrorState from './ErrorState';
function MetricOverTimeGraph({
metricOverTimeDataQuery,
widgetInfoIndex,
endPointName,
}: {
metricOverTimeDataQuery: UseQueryResult<SuccessResponse<any>, unknown>;
widgetInfoIndex: number;
endPointName: string;
}): JSX.Element {
const { data } = metricOverTimeDataQuery;
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const graphRef = useRef<HTMLDivElement>(null);
const dimensions = useResizeObserver(graphRef);
const { endpoint } = extractPortAndEndpoint(endPointName);
const formattedChartData = useMemo(
() => getFormattedChartData(data?.payload, [endpoint]),
[data?.payload, endpoint],
);
const chartData = useMemo(() => getUPlotChartData(formattedChartData), [
formattedChartData,
]);
const isDarkMode = useIsDarkMode();
const options = useMemo(
() =>
getUPlotChartOptions({
apiResponse: formattedChartData,
isDarkMode,
dimensions,
yAxisUnit: apiWidgetInfo[widgetInfoIndex].yAxisUnit,
softMax: null,
softMin: null,
minTimeScale: Math.floor(minTime / 1e9),
maxTimeScale: Math.floor(maxTime / 1e9),
panelType: PANEL_TYPES.TIME_SERIES,
}),
[
formattedChartData,
minTime,
maxTime,
widgetInfoIndex,
dimensions,
isDarkMode,
],
);
const renderCardContent = useCallback(
(query: UseQueryResult<SuccessResponse<any>, unknown>): JSX.Element => {
if (query.isLoading) {
return <Skeleton />;
}
if (query.error) {
return <ErrorState refetch={query.refetch} />;
}
return (
<div
className={cx('chart-container', {
'no-data-container':
!query.isLoading && !query?.data?.payload?.data?.result?.length,
})}
>
<Uplot options={options as Options} data={chartData} />
</div>
);
},
[options, chartData],
);
function MetricOverTimeGraph({ widget }: { widget: Widgets }): JSX.Element {
return (
<div>
<Card bordered className="endpoint-details-card">
<div className="graph-container">
<GridCard
widget={widget}
isQueryEnabled
onDragSelect={(): void => {}}
customOnDragSelect={(): void => {}}
/>
<Typography.Text>{apiWidgetInfo[widgetInfoIndex].title}</Typography.Text>
<div className="graph-container" ref={graphRef}>
{renderCardContent(metricOverTimeDataQuery)}
</div>
</Card>
</div>

View File

@@ -1,22 +1,13 @@
import { Color } from '@signozhq/design-tokens';
import { Button, Card, Skeleton, Typography } from 'antd';
import cx from 'classnames';
import { useGetGraphCustomSeries } from 'components/CeleryTask/useGetGraphCustomSeries';
import { useNavigateToExplorer } from 'components/CeleryTask/useNavigateToExplorer';
import Uplot from 'components/Uplot';
import { PANEL_TYPES } from 'constants/queryBuilder';
import {
getCustomFiltersForBarChart,
getFormattedEndPointStatusCodeChartData,
getStatusCodeBarChartWidgetData,
statusCodeWidgetInfo,
} from 'container/ApiMonitoring/utils';
import { handleGraphClick } from 'container/GridCardLayout/GridCard/utils';
import { useGraphClickToShowButton } from 'container/GridCardLayout/useGraphClickToShowButton';
import useNavigateToExplorerPages from 'container/GridCardLayout/useNavigateToExplorerPages';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { useNotifications } from 'hooks/useNotifications';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { useCallback, useMemo, useRef, useState } from 'react';
@@ -24,8 +15,6 @@ import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { Options } from 'uplot';
@@ -34,10 +23,6 @@ import ErrorState from './ErrorState';
function StatusCodeBarCharts({
endPointStatusCodeBarChartsDataQuery,
endPointStatusCodeLatencyBarChartsDataQuery,
domainName,
endPointName,
domainListFilters,
filters,
}: {
endPointStatusCodeBarChartsDataQuery: UseQueryResult<
SuccessResponse<any>,
@@ -47,10 +32,6 @@ function StatusCodeBarCharts({
SuccessResponse<any>,
unknown
>;
domainName: string;
endPointName: string;
domainListFilters: IBuilderQuery['filters'];
filters: IBuilderQuery['filters'];
}): JSX.Element {
// 0 : Status Code Count
// 1 : Status Code Latency
@@ -104,72 +85,6 @@ function StatusCodeBarCharts({
const isDarkMode = useIsDarkMode();
const graphClick = useGraphClickToShowButton({
graphRef,
isButtonEnabled: true,
buttonClassName: 'view-onclick-show-button',
});
const navigateToExplorer = useNavigateToExplorer();
const navigateToExplorerPages = useNavigateToExplorerPages();
const { notifications } = useNotifications();
const { getCustomSeries } = useGetGraphCustomSeries({
isDarkMode,
drawStyle: 'bars',
colorMapping: {
'200-299': Color.BG_FOREST_500,
'300-399': Color.BG_AMBER_400,
'400-499': Color.BG_CHERRY_500,
'500-599': Color.BG_ROBIN_500,
Other: Color.BG_SIENNA_500,
},
});
const widget = useMemo<Widgets>(
() =>
getStatusCodeBarChartWidgetData(domainName, endPointName, {
items: [...domainListFilters.items, ...filters.items],
op: filters.op,
}),
[domainName, endPointName, domainListFilters, filters],
);
const graphClickHandler = useCallback(
(
xValue: number,
yValue: number,
mouseX: number,
mouseY: number,
metric?: { [key: string]: string },
queryData?: { queryName: string; inFocusOrNot: boolean },
): void => {
const customFilters = getCustomFiltersForBarChart(metric);
handleGraphClick({
xValue,
yValue,
mouseX,
mouseY,
metric,
queryData,
widget,
navigateToExplorerPages,
navigateToExplorer,
notifications,
graphClick,
customFilters,
});
},
[
widget,
navigateToExplorerPages,
navigateToExplorer,
notifications,
graphClick,
],
);
const options = useMemo(
() =>
getUPlotChartOptions({
@@ -185,8 +100,6 @@ function StatusCodeBarCharts({
minTimeScale: Math.floor(minTime / 1e9),
maxTimeScale: Math.floor(maxTime / 1e9),
panelType: PANEL_TYPES.BAR,
onClickHandler: graphClickHandler,
customSeries: getCustomSeries,
}),
[
minTime,
@@ -196,8 +109,6 @@ function StatusCodeBarCharts({
formattedEndPointStatusCodeBarChartsDataPayload,
formattedEndPointStatusCodeLatencyBarChartsDataPayload,
isDarkMode,
graphClickHandler,
getCustomSeries,
],
);

View File

@@ -3,7 +3,6 @@ import '../Explorer.styles.scss';
import { LoadingOutlined } from '@ant-design/icons';
import { Spin, Table, Typography } from 'antd';
import axios from 'api';
import logEvent from 'api/common/logEvent';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import cx from 'classnames';
@@ -131,7 +130,6 @@ function DomainList({
(item) => item.key === record.key,
);
setSelectedDomainIndex(dataIndex);
logEvent('API Monitoring: Domain name row clicked', {});
}
},
className: 'expanded-clickable-row',
@@ -149,7 +147,6 @@ function DomainList({
handleClose={(): void => {
setSelectedDomainIndex(-1);
}}
domainListFilters={query?.filters}
/>
)}
</section>

View File

@@ -3,14 +3,13 @@ import './Explorer.styles.scss';
import { FilterOutlined } from '@ant-design/icons';
import * as Sentry from '@sentry/react';
import { Switch, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';
import QuickFilters from 'components/QuickFilters/QuickFilters';
import { QuickFiltersSource } from 'components/QuickFilters/types';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { useEffect, useMemo, useState } from 'react';
import { useMemo, useState } from 'react';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
@@ -22,10 +21,6 @@ function Explorer(): JSX.Element {
const { currentQuery } = useQueryBuilder();
useEffect(() => {
logEvent('API Monitoring: Landing page visited', {});
}, []);
const { handleChangeQueryData } = useQueryOperations({
index: 0,
query: currentQuery.builder.queryData[0],
@@ -69,12 +64,7 @@ function Explorer(): JSX.Element {
style={{ marginLeft: 'auto' }}
checked={showIP}
onClick={(): void => {
setShowIP((showIP): boolean => {
logEvent('API Monitoring: Show IP addresses clicked', {
showIP: !showIP,
});
return !showIP;
});
setShowIP((showIP) => !showIP);
}}
/>
</div>

View File

@@ -8,23 +8,16 @@ import {
} from 'components/QuickFilters/types';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { GraphClickMetaData } from 'container/GridCardLayout/useNavigateToExplorerPages';
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
import dayjs from 'dayjs';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import { cloneDeep } from 'lodash-es';
import { ArrowUpDown, ChevronDown, ChevronRight } from 'lucide-react';
import { getWidgetQuery } from 'pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
IBuilderQuery,
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { QueryData } from 'types/api/widgets/getQuery';
import { EQueryType } from 'types/common/dashboard';
import { DataSource } from 'types/common/queryBuilder';
@@ -135,15 +128,12 @@ export const columnsConfig: ColumnType<APIDomainsRowData>[] = [
sorter: false,
align: 'right',
className: `column`,
render: (lastUsed: number | string): string =>
lastUsed === 'n/a' || lastUsed === '-'
? '-'
: getLastUsedRelativeTime(lastUsed as number),
render: (lastUsed: number): string => getLastUsedRelativeTime(lastUsed),
},
{
title: (
<div>
Rate <span className="round-metric-tag">ops/s</span>
Rate <span className="round-metric-tag">/s</span>
</div>
),
dataIndex: 'rate',
@@ -165,26 +155,21 @@ export const columnsConfig: ColumnType<APIDomainsRowData>[] = [
sorter: false,
align: 'right',
className: `column`,
render: (errorRate: number | string): React.ReactNode => {
if (errorRate === 'n/a' || errorRate === '-') {
return '-';
}
return (
<Progress
status="active"
percent={Number(((errorRate as number) * 100).toFixed(1))}
strokeLinecap="butt"
size="small"
strokeColor={((): string => {
const errorRatePercent = Number(((errorRate as number) * 100).toFixed(1));
if (errorRatePercent >= 90) return Color.BG_SAKURA_500;
if (errorRatePercent >= 60) return Color.BG_AMBER_500;
return Color.BG_FOREST_500;
})()}
className="progress-bar error-rate"
/>
);
},
render: (errorRate: number): React.ReactNode => (
<Progress
status="active"
percent={Number((errorRate * 100).toFixed(1))}
strokeLinecap="butt"
size="small"
strokeColor={((): string => {
const errorRatePercent = Number((errorRate * 100).toFixed(1));
if (errorRatePercent >= 90) return Color.BG_SAKURA_500;
if (errorRatePercent >= 60) return Color.BG_AMBER_500;
return Color.BG_FOREST_500;
})()}
className="progress-bar error-rate"
/>
),
},
{
title: (
@@ -232,9 +217,9 @@ interface APIMonitoringResponseRow {
data: {
endpoints: number;
error_rate: number;
lastseen: number | string;
lastseen: number;
[domainNameKey]: string;
p99: number | string;
p99: number;
rps: number;
};
}
@@ -247,12 +232,12 @@ interface EndPointsResponseRow {
export interface APIDomainsRowData {
key: string;
domainName: string;
endpointCount: number | string;
rate: number | string;
errorRate: number | string;
latency: number | string;
lastUsed: string;
domainName: React.ReactNode;
endpointCount: React.ReactNode;
rate: React.ReactNode;
errorRate: React.ReactNode;
latency: React.ReactNode;
lastUsed: React.ReactNode;
}
// Rename this to a proper name
@@ -261,20 +246,12 @@ export const formatDataForTable = (
): APIDomainsRowData[] =>
data?.map((domain) => ({
key: v4(),
domainName: domain?.data[domainNameKey] || '-',
endpointCount: domain?.data?.endpoints || '-',
rate: domain.data.rps || '-',
errorRate: domain.data.error_rate || '-',
latency:
domain.data.p99 === 'n/a'
? '-'
: Math.round(Number(domain.data.p99) / 1000000), // Convert from nanoseconds to milliseconds
lastUsed:
domain.data.lastseen === 'n/a'
? '-'
: new Date(
Math.floor(Number(domain.data.lastseen) / 1000000),
).toISOString(), // Convert from nanoseconds to milliseconds
domainName: domain.data[domainNameKey] || '',
endpointCount: domain.data.endpoints,
rate: domain.data.rps,
errorRate: domain.data.error_rate,
latency: Math.round(domain.data.p99 / 1000000), // Convert from nanoseconds to milliseconds
lastUsed: new Date(Math.floor(domain.data.lastseen / 1000000)).toISOString(), // Convert from nanoseconds to milliseconds
}));
// Rename this to a proper name
@@ -491,6 +468,7 @@ export const extractPortAndEndpoint = (
}
};
// Add icons in the below column headers
export const getEndPointsColumnsConfig = (
isGroupedByAttribute: boolean,
expandedRowKeys: React.Key[],
@@ -598,7 +576,7 @@ export const formatEndPointsDataForTable = (
);
return {
key: v4(),
endpointName: (endpoint.data['http.url'] as string) || '-',
endpointName: (endpoint.data['http.url'] as string) || '',
port,
callCount: endpoint.data.A || '-',
latency:
@@ -615,6 +593,7 @@ export const formatEndPointsDataForTable = (
const groupedByAttributeData = groupBy.map((attribute) => attribute.key);
// TODO: Use tags to show the concatenated attribute values
return data?.map((endpoint) => {
const newEndpointName = groupedByAttributeData
.map((attribute) => endpoint.data[attribute])
@@ -660,7 +639,7 @@ export const createFiltersForSelectedRowData = (
type: null,
},
op: '=',
value: groupedByMeta[key] || '',
value: groupedByMeta[key],
id: key,
})),
);
@@ -670,10 +649,12 @@ export const createFiltersForSelectedRowData = (
// First query payload for endpoint metrics
// Second query payload for endpoint status code
// Third query payload for endpoint dropdown selection
// Fourth query payload for endpoint dependant services
// Fifth query payload for endpoint response status count bar chart
// Sixth query payload for endpoint response status code latency bar chart
// Third query payload for endpoint rate over time graph
// Fourth query payload for endpoint latency over time graph
// Fifth query payload for endpoint dropdown selection
// Sixth query payload for endpoint dependant services
// Seventh query payload for endpoint response status count bar chart
// Eighth query payload for endpoint response status code latency bar chart
export const getEndPointDetailsQueryPayload = (
domainName: string,
endPointName: string,
@@ -1120,6 +1101,205 @@ export const getEndPointDetailsQueryPayload = (
end,
step: 60,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.String,
id: '------false',
isColumn: false,
key: '',
type: '',
},
aggregateOperator: 'rate',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'B',
filters: {
items: [
{
id: '3c76fe0b',
key: {
dataType: DataTypes.String,
id: 'net.peer.name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'net.peer.name',
type: 'tag',
},
op: '=',
value: domainName,
},
{
id: '30710f04',
key: {
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
op: '=',
value: endPointName,
},
...filters.items,
],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
],
having: [],
legend: '',
limit: null,
orderBy: [],
queryName: 'B',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: '315b15fa-ff0c-442f-89f8-2bf4fb1af2f2',
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
variables: {},
formatForWeb: false,
start,
end,
step: 60,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TIME_SERIES,
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'duration_nano--float64----true',
isColumn: true,
isJSON: false,
key: 'duration_nano',
type: '',
},
aggregateOperator: 'p99',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'B',
filters: {
items: [
{
id: '63adb3ff',
key: {
dataType: DataTypes.String,
id: 'net.peer.name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'net.peer.name',
type: 'tag',
},
op: '=',
value: domainName,
},
{
id: '50142500',
key: {
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
op: '=',
value: endPointName,
},
...filters.items,
],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
],
having: [],
legend: '',
limit: null,
orderBy: [],
queryName: 'B',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'p99',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: '315b15fa-ff0c-442f-89f8-2bf4fb1af2f2',
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
variables: {},
formatForWeb: false,
start,
end,
step: 60,
},
{
selectedTime: 'GLOBAL_TIME',
graphType: PANEL_TYPES.TABLE,
@@ -1621,7 +1801,7 @@ interface EndPointMetricsData {
interface EndPointStatusCodeData {
key: string;
statusCode: string;
count: number | string;
count: number;
p99Latency: number | string;
}
@@ -1644,8 +1824,8 @@ export const getFormattedEndPointStatusCodeData = (
): EndPointStatusCodeData[] =>
data?.map((row) => ({
key: v4(),
statusCode: row.data.response_status_code || '-',
count: row.data.A || '-',
statusCode: row.data.response_status_code,
count: row.data.A,
p99Latency:
row.data.B === 'n/a' ? '-' : Math.round(Number(row.data.B) / 1000000), // Convert from nanoseconds to milliseconds,
}));
@@ -1677,6 +1857,11 @@ export const endPointStatusCodeColumns: ColumnType<EndPointStatusCodeData>[] = [
},
];
export const apiWidgetInfo = [
{ title: 'Rate over time', yAxisUnit: 'ops/s' },
{ title: 'Latency over time', yAxisUnit: 'ns' },
];
export const statusCodeWidgetInfo = [
{ yAxisUnit: 'calls' },
{ yAxisUnit: 'ns' },
@@ -1700,8 +1885,8 @@ export const getFormattedEndPointDropDownData = (
): EndPointDropDownData[] =>
data?.map((row) => ({
key: v4(),
label: row.data['http.url'] || '-',
value: row.data['http.url'] || '-',
label: row.data['http.url'],
value: row.data['http.url'],
}));
interface DependentServicesResponseRow {
@@ -1718,7 +1903,6 @@ interface DependentServicesData {
percentage: number;
}
// Discuss once about type safety of this function
export const getFormattedDependentServicesData = (
data: DependentServicesResponseRow[],
): DependentServicesData[] => {
@@ -1799,7 +1983,7 @@ export const groupStatusCodes = (
// Track all timestamps
series.values.forEach((value) => {
allTimestamps.add(Number(value[0]));
allTimestamps.add(value[0]);
});
// Initialize or update the grouped series
@@ -1865,114 +2049,8 @@ export const groupStatusCodes = (
});
});
// Define the order of status code ranges
const statusCodeOrder = ['200-299', '300-399', '400-499', '500-599', 'Other'];
// Return the grouped series in the specified order
return statusCodeOrder
.filter((code) => groupedSeries[code]) // Only include codes that exist in the data
.map((code) => groupedSeries[code]);
return Object.values(groupedSeries);
};
export const getStatusCodeBarChartWidgetData = (
domainName: string,
endPointName: string,
filters: IBuilderQuery['filters'],
): Widgets => ({
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.String,
id: '------false',
isColumn: false,
key: '',
type: '',
},
aggregateOperator: 'count',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: 'c6724407',
key: {
dataType: DataTypes.String,
id: 'net.peer.name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'net.peer.name',
type: 'tag',
},
op: '=',
value: domainName,
},
{
id: '8b1be6f0',
key: {
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
op: '=',
value: endPointName,
},
...filters.items,
],
op: 'AND',
},
functions: [],
groupBy: [],
having: [],
legend: '',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: '315b15fa-ff0c-442f-89f8-2bf4fb1af2f2',
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: EQueryType.QUERY_BUILDER,
},
description: '',
id: '315b15fa-ff0c-442f-89f8-2bf4fb1af2f2',
isStacked: false,
panelTypes: PANEL_TYPES.BAR,
title: '',
opacity: '',
nullZeroValues: '',
timePreferance: 'GLOBAL_TIME',
softMin: null,
softMax: null,
selectedLogFields: null,
selectedTracesFields: null,
});
interface EndPointStatusCodePayloadData {
data: {
result: QueryData[];
@@ -2007,277 +2085,3 @@ export const END_POINT_DETAILS_QUERY_KEYS_ARRAY = [
REACT_QUERY_KEY.GET_ENDPOINT_STATUS_CODE_BAR_CHARTS_DATA,
REACT_QUERY_KEY.GET_ENDPOINT_STATUS_CODE_LATENCY_BAR_CHARTS_DATA,
];
export const getRateOverTimeWidgetData = (
domainName: string,
endPointName: string,
filters: IBuilderQuery['filters'],
): Widgets => {
const { endpoint, port } = extractPortAndEndpoint(endPointName);
const legend = `${
port !== '-' && port !== 'n/a' ? `${port}:` : ''
}${endpoint}`;
return getWidgetQueryBuilder(
getWidgetQuery({
title: 'Rate Over Time',
description: 'Rate over time.',
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.String,
id: '------false',
isColumn: false,
key: '',
type: '',
},
aggregateOperator: 'rate',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '3c76fe0b',
key: {
dataType: DataTypes.String,
id: 'net.peer.name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'net.peer.name',
type: 'tag',
},
op: '=',
value: domainName,
},
{
id: '30710f04',
key: {
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
op: '=',
value: endPointName,
},
...filters.items,
],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
],
having: [],
legend,
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
},
],
yAxisUnit: 'ops/s',
}),
);
};
export const getLatencyOverTimeWidgetData = (
domainName: string,
endPointName: string,
filters: IBuilderQuery['filters'],
): Widgets => {
const { endpoint, port } = extractPortAndEndpoint(endPointName);
const legend = `${port}:${endpoint}`;
return getWidgetQueryBuilder(
getWidgetQuery({
title: 'Latency Over Time',
description: 'Latency over time.',
queryData: [
{
aggregateAttribute: {
dataType: DataTypes.Float64,
id: 'duration_nano--float64----true',
isColumn: true,
isJSON: false,
key: 'duration_nano',
type: '',
},
aggregateOperator: 'p99',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'A',
filters: {
items: [
{
id: '63adb3ff',
key: {
dataType: DataTypes.String,
id: 'net.peer.name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'net.peer.name',
type: 'tag',
},
op: '=',
value: domainName,
},
{
id: '50142500',
key: {
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
op: '=',
value: endPointName,
},
...filters.items,
],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: DataTypes.String,
id: 'http.url--string--tag--false',
isColumn: false,
isJSON: false,
key: 'http.url',
type: 'tag',
},
],
having: [],
legend,
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'p99',
},
],
yAxisUnit: 'ns',
}),
);
};
/**
* Helper function to get the start and end status codes from a status code range string
* @param value Status code range string (e.g. '200-299') or boolean
* @returns Tuple of [startStatusCode, endStatusCode] as strings
*/
const getStartAndEndStatusCode = (
value: string | boolean,
): [string, string] => {
if (!value) {
return ['', ''];
}
switch (value) {
case '100-199':
return ['100', '199'];
case '200-299':
return ['200', '299'];
case '300-399':
return ['300', '399'];
case '400-499':
return ['400', '499'];
case '500-599':
return ['500', '599'];
default:
return ['', ''];
}
};
/**
* Creates filter items for bar chart based on group by fields and request data
* Used specifically for filtering status code ranges in bar charts
* @param groupBy Array of group by fields to create filters for
* @param requestData Data from graph click containing values to filter on
* @returns Array of TagFilterItems with >= and < operators for status code ranges
*/
export const createGroupByFiltersForBarChart = (
groupBy: BaseAutocompleteData[],
requestData: GraphClickMetaData,
): TagFilterItem[] =>
groupBy
.map((gb) => {
const value = requestData[gb.key];
const [startStatusCode, endStatusCode] = getStartAndEndStatusCode(value);
return value
? [
{
id: v4(),
key: gb,
op: '>=',
value: startStatusCode,
},
{
id: v4(),
key: gb,
op: '<=',
value: endStatusCode,
},
]
: [];
})
.flat();
export const getCustomFiltersForBarChart = (
metric:
| {
[key: string]: string;
}
| undefined,
): TagFilterItem[] => {
if (!metric?.response_status_code) {
return [];
}
const [startStatusCode, endStatusCode] = getStartAndEndStatusCode(
metric.response_status_code,
);
return [
{
id: v4(),
key: {
dataType: DataTypes.String,
id: 'response_status_code--string--tag--false',
isColumn: false,
isJSON: false,
key: 'response_status_code',
type: 'tag',
},
op: '>=',
value: startStatusCode,
},
{
id: v4(),
key: {
dataType: DataTypes.String,
id: 'response_status_code--string--tag--false',
isColumn: false,
isJSON: false,
key: 'response_status_code',
type: 'tag',
},
op: '<=',
value: endStatusCode,
},
];
};

View File

@@ -49,7 +49,6 @@ function FullView({
isDependedDataLoaded = false,
onToggleModelHandler,
onClickHandler,
customOnDragSelect,
setCurrentGraphRef,
}: FullViewProps): JSX.Element {
const { safeNavigate } = useSafeNavigate();
@@ -253,7 +252,7 @@ function FullView({
onToggleModelHandler={onToggleModelHandler}
setGraphVisibility={setGraphsVisibilityStates}
graphVisibility={graphsVisibilityStates}
onDragSelect={customOnDragSelect ?? onDragSelect}
onDragSelect={onDragSelect}
tableProcessedDataRef={tableProcessedDataRef}
searchTerm={searchTerm}
onClickHandler={onClickHandler}

View File

@@ -50,7 +50,6 @@ export interface FullViewProps {
widget: Widgets;
fullViewOptions?: boolean;
onClickHandler?: OnClickPluginOpts['onClick'];
customOnDragSelect?: (start: number, end: number) => void;
name: string;
tableProcessedDataRef: MutableRefObject<RowData[]>;
version?: string;

View File

@@ -50,7 +50,6 @@ function WidgetGraphComponent({
setRequestData,
onClickHandler,
onDragSelect,
customOnDragSelect,
customTooltipElement,
openTracesButton,
onOpenTraceBtnClick,
@@ -328,7 +327,6 @@ function WidgetGraphComponent({
onToggleModelHandler={onToggleModelHandler}
tableProcessedDataRef={tableProcessedDataRef}
onClickHandler={onClickHandler ?? graphClickHandler}
customOnDragSelect={customOnDragSelect}
setCurrentGraphRef={setCurrentGraphRef}
/>
</Modal>

View File

@@ -36,7 +36,6 @@ function GridCardGraph({
version,
onClickHandler,
onDragSelect,
customOnDragSelect,
customTooltipElement,
dataAvailable,
getGraphData,
@@ -273,7 +272,6 @@ function GridCardGraph({
setRequestData={setRequestData}
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}
customOnDragSelect={customOnDragSelect}
customTooltipElement={customTooltipElement}
openTracesButton={openTracesButton}
onOpenTraceBtnClick={onOpenTraceBtnClick}

View File

@@ -33,7 +33,6 @@ export interface WidgetGraphComponentProps {
setRequestData?: Dispatch<SetStateAction<GetQueryResultsProps>>;
onClickHandler?: OnClickPluginOpts['onClick'];
onDragSelect: (start: number, end: number) => void;
customOnDragSelect?: (start: number, end: number) => void;
customTooltipElement?: HTMLDivElement;
openTracesButton?: boolean;
onOpenTraceBtnClick?: (record: RowData) => void;
@@ -50,7 +49,6 @@ export interface GridCardGraphProps {
variables?: Dashboard['data']['variables'];
version?: string;
onDragSelect: (start: number, end: number) => void;
customOnDragSelect?: (start: number, end: number) => void;
customTooltipElement?: HTMLDivElement;
dataAvailable?: (isDataAvailable: boolean) => void;
getGraphData?: (graphData?: MetricRangePayloadProps['data']) => void;

View File

@@ -178,7 +178,6 @@ interface HandleGraphClickParams {
navigateToExplorer: (props: NavigateToExplorerProps) => void;
notifications: NotificationInstance;
graphClick: (props: GraphClickProps) => void;
customFilters?: TagFilterItem[];
}
export const handleGraphClick = async ({
@@ -193,7 +192,6 @@ export const handleGraphClick = async ({
navigateToExplorer,
notifications,
graphClick,
customFilters,
}: HandleGraphClickParams): Promise<void> => {
const { stepInterval } = widget?.query?.builder?.queryData?.[0] ?? {};
@@ -223,7 +221,7 @@ export const handleGraphClick = async ({
}: ${key}`,
onClick: (): void =>
navigateToExplorer({
filters: [...result[key].filters, ...(customFilters || [])],
filters: result[key].filters,
dataSource: result[key].dataSource as DataSource,
startTime: xValue,
endTime: xValue + (stepInterval ?? 60),

View File

@@ -44,10 +44,7 @@ import { EditMenuAction, ViewMenuAction } from './config';
import DashboardEmptyState from './DashboardEmptyState/DashboardEmptyState';
import GridCard from './GridCard';
import { Card, CardContainer, ReactGridLayout } from './styles';
import {
hasColumnWidthsChanged,
removeUndefinedValuesFromLayout,
} from './utils';
import { removeUndefinedValuesFromLayout } from './utils';
import { MenuItemKeys } from './WidgetHeader/contants';
import { WidgetRowHeader } from './WidgetRow';
@@ -71,7 +68,6 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
setDashboardQueryRangeCalled,
setSelectedRowWidgetId,
isDashboardFetching,
columnWidths,
} = useDashboard();
const { data } = selectedDashboard || {};
const { pathname } = useLocation();
@@ -166,7 +162,6 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
logEventCalledRef.current = true;
}
}, [data]);
const onSaveHandler = (): void => {
if (!selectedDashboard) return;
@@ -176,15 +171,6 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
...selectedDashboard.data,
panelMap: { ...currentPanelMap },
layout: dashboardLayout.filter((e) => e.i !== PANEL_TYPES.EMPTY_WIDGET),
widgets: selectedDashboard?.data?.widgets?.map((widget) => {
if (columnWidths?.[widget.id]) {
return {
...widget,
columnWidths: columnWidths[widget.id],
};
}
return widget;
}),
},
uuid: selectedDashboard.uuid,
};
@@ -241,31 +227,20 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
useEffect(() => {
if (
isDashboardLocked ||
!saveLayoutPermission ||
updateDashboardMutation.isLoading ||
isDashboardFetching
dashboardLayout &&
Array.isArray(dashboardLayout) &&
dashboardLayout.length > 0 &&
!isEqual(layouts, dashboardLayout) &&
!isDashboardLocked &&
saveLayoutPermission &&
!updateDashboardMutation.isLoading &&
!isDashboardFetching
) {
return;
}
const shouldSaveLayout =
dashboardLayout &&
Array.isArray(dashboardLayout) &&
dashboardLayout.length > 0 &&
!isEqual(layouts, dashboardLayout);
const shouldSaveColumnWidths =
dashboardLayout &&
Array.isArray(dashboardLayout) &&
dashboardLayout.length > 0 &&
hasColumnWidthsChanged(columnWidths, selectedDashboard);
if (shouldSaveLayout || shouldSaveColumnWidths) {
onSaveHandler();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dashboardLayout, columnWidths]);
}, [dashboardLayout]);
const onSettingsModalSubmit = (): void => {
const newTitle = form.getFieldValue('title');

View File

@@ -12,7 +12,7 @@ import { v4 } from 'uuid';
import { extractQueryNamesFromExpression } from './utils';
export type GraphClickMetaData = {
type GraphClickMetaData = {
[key: string]: string | boolean;
queryName: string;
inFocusOrNot: boolean;

View File

@@ -1,7 +1,5 @@
import { FORMULA_REGEXP } from 'constants/regExp';
import { isEmpty, isEqual } from 'lodash-es';
import { Layout } from 'react-grid-layout';
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
export const removeUndefinedValuesFromLayout = (layout: Layout[]): Layout[] =>
layout.map((obj) =>
@@ -27,27 +25,3 @@ export function extractQueryNamesFromExpression(expression: string): string[] {
// Extract matches and deduplicate
return [...new Set(expression.match(queryNameRegex) || [])];
}
export const hasColumnWidthsChanged = (
columnWidths: Record<string, Record<string, number>>,
selectedDashboard?: Dashboard,
): boolean => {
// If no column widths stored, no changes
if (isEmpty(columnWidths) || !selectedDashboard) return false;
// Check each widget's column widths
return Object.keys(columnWidths).some((widgetId) => {
const dashboardWidget = selectedDashboard?.data?.widgets?.find(
(widget) => widget.id === widgetId,
) as Widgets;
const newWidths = columnWidths[widgetId];
const existingWidths = dashboardWidget?.columnWidths;
// If both are empty/undefined, no change
if (isEmpty(newWidths) || isEmpty(existingWidths)) return false;
// Compare stored column widths with dashboard widget's column widths
return !isEqual(newWidths, existingWidths);
});
};

View File

@@ -43,7 +43,6 @@ function GridTableComponent({
sticky,
openTracesButton,
onOpenTraceBtnClick,
widgetId,
...props
}: GridTableComponentProps): JSX.Element {
const { t } = useTranslation(['valueGraph']);
@@ -230,7 +229,6 @@ function GridTableComponent({
columns={openTracesButton ? columnDataWithOpenTracesButton : newColumnData}
dataSource={dataSource}
sticky={sticky}
widgetId={widgetId}
onRow={
openTracesButton
? (record): React.HTMLAttributes<HTMLElement> => ({

View File

@@ -17,7 +17,6 @@ export type GridTableComponentProps = {
searchTerm?: string;
openTracesButton?: boolean;
onOpenTraceBtnClick?: (record: RowData) => void;
widgetId?: string;
} & Pick<LogsExplorerTableProps, 'data'> &
Omit<TableProps<RowData>, 'columns' | 'dataSource'>;

View File

@@ -1,9 +1,9 @@
import './LogsPanelComponent.styles.scss';
import { Table } from 'antd';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { ResizeTable } from 'components/ResizeTable';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import { PANEL_TYPES } from 'constants/queryBuilder';
import Controls from 'container/Controls';
@@ -79,14 +79,9 @@ function LogsPanelComponent({
const { formatTimezoneAdjustedTimestamp } = useTimezone();
const columns = useMemo(
() =>
getLogPanelColumnsList(
widget.selectedLogFields,
formatTimezoneAdjustedTimestamp,
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[widget.selectedLogFields],
const columns = getLogPanelColumnsList(
widget.selectedLogFields,
formatTimezoneAdjustedTimestamp,
);
const dataLength =
@@ -221,18 +216,16 @@ function LogsPanelComponent({
<div className="logs-table">
<div className="resize-table">
<OverlayScrollbar>
<ResizeTable
<Table
pagination={false}
tableLayout="fixed"
scroll={{ x: `max-content` }}
scroll={{ x: `calc(50vw - 10px)` }}
sticky
loading={queryResponse.isFetching}
style={tableStyles}
dataSource={flattenLogData}
columns={columns}
onRow={handleRow}
widgetId={widget.id}
shouldPersistColumnWidths
/>
</OverlayScrollbar>
</div>

View File

@@ -1,16 +0,0 @@
.inspect-metrics-modal {
.inspect-metrics-title {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 6px;
.inspect-metrics-button {
display: flex;
align-items: center;
justify-content: center;
cursor: default;
color: var(--text-vanilla-500);
}
}
}

View File

@@ -1,48 +0,0 @@
import './Inspect.styles.scss';
import * as Sentry from '@sentry/react';
import { Color } from '@signozhq/design-tokens';
import { Button, Drawer, Typography } from 'antd';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { Compass } from 'lucide-react';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { InspectProps } from './types';
function Inspect({ metricName, isOpen, onClose }: InspectProps): JSX.Element {
const isDarkMode = useIsDarkMode();
return (
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<Drawer
width="100%"
title={
<div className="inspect-metrics-title">
<Typography.Text>Metrics Explorer </Typography.Text>
<Button
className="inspect-metrics-button"
size="small"
icon={<Compass size={14} />}
disabled
>
Inspect Metric
</Button>
</div>
}
open={isOpen}
onClose={onClose}
style={{
overscrollBehavior: 'contain',
background: isDarkMode ? Color.BG_INK_400 : Color.BG_VANILLA_100,
}}
className="inspect-metrics-modal"
destroyOnClose
>
<div>Inspect</div>
<div>{metricName}</div>
</Drawer>
</Sentry.ErrorBoundary>
);
}
export default Inspect;

View File

@@ -1 +0,0 @@
export const INSPECT_FEATURE_FLAG_KEY = 'metrics-explorer-inspect-feature-flag';

View File

@@ -1,3 +0,0 @@
import Inspect from './Inspect';
export default Inspect;

View File

@@ -1,5 +0,0 @@
export type InspectProps = {
metricName: string | null;
isOpen: boolean;
onClose: () => void;
};

View File

@@ -1,11 +0,0 @@
import { INSPECT_FEATURE_FLAG_KEY } from './constants';
/**
* Check if the inspect feature flag is enabled
* returns true if the feature flag is enabled, false otherwise
* Show the inspect button in metrics explorer if the feature flag is enabled
*/
export function isInspectEnabled(): boolean {
const featureFlag = localStorage.getItem(INSPECT_FEATURE_FLAG_KEY);
return featureFlag === 'true';
}

View File

@@ -20,12 +20,6 @@
gap: 4px;
background-color: var(--bg-slate-300);
}
.inspect-metrics-button {
display: flex;
align-items: center;
justify-content: center;
}
}
.metric-details-content {

View File

@@ -2,21 +2,12 @@ import './MetricDetails.styles.scss';
import '../Summary/Summary.styles.scss';
import { Color } from '@signozhq/design-tokens';
import {
Button,
Divider,
Drawer,
Empty,
Skeleton,
Tooltip,
Typography,
} from 'antd';
import { Divider, Drawer, Empty, Skeleton, Tooltip, Typography } from 'antd';
import { useGetMetricDetails } from 'hooks/metricsExplorer/useGetMetricDetails';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { Compass, X } from 'lucide-react';
import { X } from 'lucide-react';
import { useMemo } from 'react';
import { isInspectEnabled } from '../Inspect/utils';
import { formatNumberIntoHumanReadableFormat } from '../Summary/utils';
import AllAttributes from './AllAttributes';
import DashboardsAndAlertsPopover from './DashboardsAndAlertsPopover';
@@ -31,7 +22,6 @@ function MetricDetails({
onClose,
isOpen,
metricName,
openInspectModal,
}: MetricDetailsProps): JSX.Element {
const isDarkMode = useIsDarkMode();
// const { safeNavigate } = useSafeNavigate();
@@ -53,8 +43,6 @@ function MetricDetails({
return formatTimestampToReadableDate(metric.lastReceived);
}, [metric]);
const showInspectFeature = useMemo(() => isInspectEnabled(), []);
const isMetricDetailsLoading = isLoading || isFetching;
const timeSeries = useMemo(() => {
@@ -104,19 +92,6 @@ function MetricDetails({
>
Open in Explorer
</Button> */}
{/* Show the based on the feature flag. Will remove before releasing the feature */}
{showInspectFeature && (
<Button
className="inspect-metrics-button"
aria-label="Inspect Metric"
icon={<Compass size={18} />}
onClick={(): void => {
if (metric?.name) {
openInspectModal(metric.name);
}
}}
/>
)}
</div>
}
placement="right"

View File

@@ -10,7 +10,6 @@ export interface MetricDetailsProps {
isOpen: boolean;
metricName: string | null;
isModalTimeSelection: boolean;
openInspectModal: (metricName: string) => void;
}
export interface DashboardsAndAlertsPopoverProps {

View File

@@ -13,7 +13,6 @@ import { AppState } from 'store/reducers';
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import InspectModal from '../Inspect';
import MetricDetails from '../MetricDetails';
import MetricsSearch from './MetricsSearch';
import MetricsTable from './MetricsTable';
@@ -36,7 +35,6 @@ function Summary(): JSX.Element {
TreemapViewType.TIMESERIES,
);
const [isMetricDetailsOpen, setIsMetricDetailsOpen] = useState(false);
const [isInspectModalOpen, setIsInspectModalOpen] = useState(false);
const [selectedMetricName, setSelectedMetricName] = useState<string | null>(
null,
);
@@ -152,17 +150,6 @@ function Summary(): JSX.Element {
setIsMetricDetailsOpen(false);
};
const openInspectModal = (metricName: string): void => {
setSelectedMetricName(metricName);
setIsInspectModalOpen(true);
setIsMetricDetailsOpen(false);
};
const closeInspectModal = (): void => {
setIsInspectModalOpen(false);
setSelectedMetricName(null);
};
return (
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<div className="metrics-explorer-summary-tab">
@@ -197,14 +184,6 @@ function Summary(): JSX.Element {
onClose={closeMetricDetails}
metricName={selectedMetricName}
isModalTimeSelection={false}
openInspectModal={openInspectModal}
/>
)}
{isInspectModalOpen && (
<InspectModal
isOpen={isInspectModalOpen}
onClose={closeInspectModal}
metricName={selectedMetricName}
/>
)}
</Sentry.ErrorBoundary>

View File

@@ -74,7 +74,6 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
setToScrollWidgetId,
selectedRowWidgetId,
setSelectedRowWidgetId,
columnWidths,
} = useDashboard();
const { t } = useTranslation(['dashboard']);
@@ -239,10 +238,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
selectedLogFields,
selectedTracesFields,
isLogScale,
columnWidths: columnWidths?.[selectedWidget?.id],
};
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
columnUnits,
currentQuery,
@@ -263,7 +260,6 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
combineHistogram,
stackedBarChart,
isLogScale,
columnWidths,
]);
const closeModal = (): void => {

View File

@@ -26,7 +26,6 @@ function TablePanelWrapper({
searchTerm={searchTerm}
openTracesButton={openTracesButton}
onOpenTraceBtnClick={onOpenTraceBtnClick}
widgetId={widget.id}
// eslint-disable-next-line react/jsx-props-no-spreading
{...GRID_TABLE_CONFIG}
/>

View File

@@ -9,8 +9,6 @@ exports[`Table panel wrappper tests table should render fine with the query resp
width: 0.625rem;
height: 100%;
cursor: col-resize;
margin-left: 4px;
margin-right: 4px;
}
.c0 {
@@ -56,7 +54,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
class="query-table"
>
<div
class="ant-table-wrapper resize-main-table css-dev-only-do-not-override-2i2tap"
class="ant-table-wrapper css-dev-only-do-not-override-2i2tap"
>
<div
class="ant-spin-nested-loading css-dev-only-do-not-override-2i2tap"
@@ -84,7 +82,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
<tr>
<th
aria-label="service_name"
class="resizable-header react-resizable"
class="ant-table-cell ant-table-column-has-sorters react-resizable"
scope="col"
tabindex="0"
>
@@ -145,12 +143,12 @@ exports[`Table panel wrappper tests table should render fine with the query resp
</span>
</div>
<span
class="c1 resize-handle"
class="c1 react-resizable-handle"
/>
</th>
<th
aria-label="latency-per-service"
class="resizable-header react-resizable"
class="ant-table-cell ant-table-column-has-sorters react-resizable"
scope="col"
tabindex="0"
>
@@ -211,7 +209,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
</span>
</div>
<span
class="c1 resize-handle"
class="c1 react-resizable-handle"
/>
</th>
</tr>
@@ -223,7 +221,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
style="overflow-x: auto; overflow-y: hidden;"
>
<table
style="min-width: 100%; table-layout: fixed;"
style="width: auto; min-width: 100%; table-layout: fixed;"
>
<colgroup>
<col

View File

@@ -453,7 +453,7 @@ export const Query = memo(function Query({
</Col>
)}
<Col flex="1" className="qb-search-container">
{[DataSource.LOGS, DataSource.TRACES].includes(query.dataSource) ? (
{query.dataSource === DataSource.LOGS ? (
<QueryBuilderSearchV2
query={query}
onChange={handleChangeTagFilters}

View File

@@ -2,7 +2,6 @@
import './QueryBuilderSearchV2.styles.scss';
import { Typography } from 'antd';
import cx from 'classnames';
import {
ArrowDown,
ArrowUp,
@@ -26,7 +25,6 @@ interface ICustomDropdownProps {
exampleQueries: TagFilter[];
onChange: (value: TagFilter) => void;
currentFilterItem?: ITag;
isLogsDataSource: boolean;
}
export default function QueryBuilderSearchDropdown(
@@ -40,14 +38,11 @@ export default function QueryBuilderSearchDropdown(
exampleQueries,
options,
onChange,
isLogsDataSource,
} = props;
const userOs = getUserOperatingSystem();
return (
<>
<div
className={cx('content', { 'non-logs-data-source': !isLogsDataSource })}
>
<div className="content">
{!currentFilterItem?.key ? (
<div className="suggested-filters">Suggested Filters</div>
) : !currentFilterItem?.op ? (

View File

@@ -11,11 +11,6 @@
.rc-virtual-list-holder {
height: 115px;
}
&.non-logs-data-source {
.rc-virtual-list-holder {
height: 256px;
}
}
}
}

View File

@@ -689,29 +689,12 @@ function QueryBuilderSearchV2(
})),
);
} else {
setDropdownOptions([
// Add user typed option if it doesn't exist in the payload
...(!isEmpty(tagKey) &&
!data?.payload?.attributeKeys?.some((val) => isEqual(val.key, tagKey))
? [
{
label: tagKey,
value: {
key: tagKey,
dataType: DataTypes.EMPTY,
type: '',
isColumn: false,
isJSON: false,
},
},
]
: []),
// Map existing attribute keys from payload
...(data?.payload?.attributeKeys?.map((key) => ({
setDropdownOptions(
data?.payload?.attributeKeys?.map((key) => ({
label: key.key,
value: key,
})) || []),
]);
})) || [],
);
}
}
if (currentState === DropdownState.OPERATOR) {
@@ -981,7 +964,6 @@ function QueryBuilderSearchV2(
exampleQueries={suggestionsData?.payload?.example_queries || []}
tags={tags}
currentFilterItem={currentFilterItem}
isLogsDataSource={isLogsDataSource}
/>
)}
>

View File

@@ -20,5 +20,4 @@ export type QueryTableProps = Omit<
dataSource?: RowData[];
sticky?: TableProps<RowData>['sticky'];
searchTerm?: string;
widgetId?: string;
};

View File

@@ -24,7 +24,6 @@ export function QueryTable({
dataSource,
sticky,
searchTerm,
widgetId,
...props
}: QueryTableProps): JSX.Element {
const { isDownloadEnabled = false, fileName = '' } = downloadOption || {};
@@ -96,10 +95,8 @@ export function QueryTable({
columns={tableColumns}
tableLayout="fixed"
dataSource={filterTable === null ? newDataSource : filterTable}
scroll={{ x: 'max-content' }}
scroll={{ x: true }}
pagination={paginationConfig}
widgetId={widgetId}
shouldPersistColumnWidths
sticky={sticky}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}

View File

@@ -279,17 +279,6 @@ function SideNav(): JSX.Element {
let updatedUserManagementItems: UserManagementMenuItems[] = [
manageLicenseMenuItem,
];
const isApiMonitoringEnabled = featureFlags?.find(
(flag) => flag.name === FeatureKeys.THIRD_PARTY_API,
)?.active;
if (!isApiMonitoringEnabled) {
updatedMenuItems = updatedMenuItems.filter(
(item) => item.key !== ROUTES.API_MONITORING,
);
}
if (isCloudUserVal || isEECloudUserVal) {
const isOnboardingEnabled =
featureFlags?.find((feature) => feature.name === FeatureKeys.ONBOARDING)

View File

@@ -112,6 +112,7 @@ const menuItems: SidebarItem[] = [
key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
label: 'Infra Monitoring',
icon: <Boxes size={16} />,
isNew: true,
},
{
key: ROUTES.ALL_DASHBOARD,
@@ -127,7 +128,6 @@ const menuItems: SidebarItem[] = [
key: ROUTES.API_MONITORING,
label: 'API Monitoring',
icon: <Binoculars size={16} />,
isNew: true,
},
{
key: ROUTES.LIST_ALL_ALERT,

View File

@@ -1,7 +1,7 @@
import './TracesTableComponent.styles.scss';
import { Table } from 'antd';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { ResizeTable } from 'components/ResizeTable';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import Controls from 'container/Controls';
import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs';
@@ -54,14 +54,9 @@ function TracesTableComponent({
const { formatTimezoneAdjustedTimestamp } = useTimezone();
const columns = useMemo(
() =>
getListColumns(
widget.selectedTracesFields || [],
formatTimezoneAdjustedTimestamp,
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[widget.selectedTracesFields],
const columns = getListColumns(
widget.selectedTracesFields || [],
formatTimezoneAdjustedTimestamp,
);
const dataLength =
@@ -121,18 +116,16 @@ function TracesTableComponent({
<div className="traces-table">
<div className="resize-table">
<OverlayScrollbar>
<ResizeTable
<Table
pagination={false}
tableLayout="fixed"
scroll={{ x: 'max-content' }}
scroll={{ x: true }}
loading={queryResponse.isFetching}
style={tableStyles}
dataSource={transformedQueryTableData}
columns={columns}
onRow={handleRow}
sticky
widgetId={widget.id}
shouldPersistColumnWidths
/>
</OverlayScrollbar>
</div>

View File

@@ -170,7 +170,11 @@ export const useOptions = (
(option, index, self) =>
index ===
self.findIndex(
(o) => o.label === option.label && o.value === option.value, // to remove duplicate & empty options from list
(o) =>
// to remove duplicate & empty options from list
o.label === option.label &&
o.value === option.value &&
o.dataType?.toLowerCase() === option.dataType?.toLowerCase(), // handle case sensitivity
) && option.value !== '',
) || []
).map((option) => {

View File

@@ -0,0 +1,211 @@
import './styles.scss';
import { Button, Card, Col, Divider, Row, Switch, Typography } from 'antd';
import MultiSelect, {
MultiSelectOption,
MultiSelectSection,
} from 'components/MultiSelect';
import { useState } from 'react';
const { Title, Text, Paragraph } = Typography;
// Sample data for the component
const sampleOptions: MultiSelectOption[] = [
{ label: 'abc', value: 'abc' },
{ label: 'acbewc', value: 'acbewc' },
{ label: 'custom-value', value: 'custom-value' },
{ label: 'option1', value: 'option1' },
{ label: 'option2', value: 'option2' },
{ label: 'another-option', value: 'another-option' },
{ label: 'test-option', value: 'test-option' },
{ label: 'disabled-option', value: 'disabled-option', disabled: true },
];
// Sample related values for the "Related Values" section
const relatedValues: MultiSelectOption[] = [
{ label: 'gke-mgmt-pl-generator-e2st4-sp-f1c1bde8-skbl', value: 'gke-1' },
{ label: 'gke-mgmt-pl-generator-e2st4-sp-f1c1bde8-skb2', value: 'gke-2' },
{ label: 'gke-mgmt-pl-generator-e2st4-sp-f1c1bde8-skb3', value: 'gke-3' },
];
// Sample all values for the "All Values" section
const allValues: MultiSelectOption[] = Array.from({ length: 20 }, (_, i) => ({
label: `gke-mgmt-pl-generator-e2st4-sp-f1c1bde8-7a7w-${i + 1}`,
value: `all-${i + 1}`,
}));
// Creating sections
const sections: MultiSelectSection[] = [
{
title: 'Related Values',
options: relatedValues,
},
{
title: 'ALL Values',
options: allValues,
},
];
function DynamicVariableTestPage(): JSX.Element {
const [selectedValues, setSelectedValues] = useState<string[]>([
'abc',
'acbewc',
]);
const [loadingDemo, setLoadingDemo] = useState<boolean>(false);
const [allowCustom, setAllowCustom] = useState<boolean>(true);
const [showError, setShowError] = useState<boolean>(false);
const [disabled, setDisabled] = useState<boolean>(false);
const handleChange = (values: string[]): void => {
setSelectedValues(values);
};
const toggleLoading = (): void => {
setLoadingDemo((prev) => !prev);
};
return (
<div className="dynamic-variable-page">
<Card>
<Title level={3}>Dynamic Variable MultiSelect</Title>
<Paragraph>
This page demonstrates the MultiSelect component with various features. The
component is now fully reusable and production-ready with support for
dynamic data from APIs, proper error states, accessibility, and UI
improvements.
</Paragraph>
<Divider />
<Row gutter={[16, 16]}>
<Col xs={24} md={12}>
<Title level={5}>Basic MultiSelect</Title>
<Text>This example shows the basic usage with pre-selected values.</Text>
<div className="multiselect-demo-container">
<MultiSelect
options={sampleOptions}
value={selectedValues}
onChange={handleChange}
placeholder="Search or add values..."
label="Select options"
/>
</div>
<div className="selected-values-display">
<Title level={5}>Selected Values:</Title>
<pre>{JSON.stringify(selectedValues, null, 2)}</pre>
</div>
</Col>
<Col xs={24} md={12}>
<Title level={5}>With Sections & All Values</Title>
<Text>This example shows the component with additional sections.</Text>
<div className="multiselect-demo-container">
<MultiSelect
options={sampleOptions}
value={selectedValues}
onChange={handleChange}
placeholder="Search or add values..."
additionalSections={sections}
sectionMaxHeight={120}
/>
</div>
</Col>
</Row>
<Divider />
<Row gutter={[16, 16]}>
<Col xs={24} md={12}>
<Title level={5}>Loading State</Title>
<Text>This example demonstrates the loading state.</Text>
<div className="multiselect-demo-container">
<MultiSelect
options={sampleOptions}
value={[]}
onChange={(): void => {}}
loading={loadingDemo}
placeholder="This shows loading state..."
/>
<Button onClick={toggleLoading} style={{ marginTop: 16 }}>
{loadingDemo ? 'Stop Loading' : 'Simulate Loading'}
</Button>
</div>
</Col>
<Col xs={24} md={12}>
<Title level={5}>Custom Values Configuration</Title>
<Text>Toggle to enable/disable custom values.</Text>
<div className="multiselect-demo-container">
<MultiSelect
options={sampleOptions}
value={[]}
onChange={(): void => {}}
allowCustomValues={allowCustom}
placeholder={
allowCustom ? 'Custom values allowed...' : 'Only predefined values...'
}
/>
<div style={{ marginTop: 16 }}>
<Switch
checked={allowCustom}
onChange={setAllowCustom}
checkedChildren="Custom values on"
unCheckedChildren="Custom values off"
/>
</div>
</div>
</Col>
</Row>
<Divider />
<Row gutter={[16, 16]}>
<Col xs={24} md={12}>
<Title level={5}>Error State</Title>
<Text>This example shows the component with an error.</Text>
<div className="multiselect-demo-container">
<MultiSelect
options={sampleOptions}
value={[]}
onChange={(): void => {}}
placeholder="Select some options..."
error={showError ? 'Please select at least one option' : undefined}
/>
<Button
onClick={(): void => setShowError(!showError)}
style={{ marginTop: 16 }}
type={showError ? 'primary' : 'default'}
>
{showError ? 'Hide Error' : 'Show Error'}
</Button>
</div>
</Col>
<Col xs={24} md={12}>
<Title level={5}>Disabled State</Title>
<Text>This example shows the disabled state of the component.</Text>
<div className="multiselect-demo-container">
<MultiSelect
options={sampleOptions}
value={['abc', 'option1']}
onChange={(): void => {}}
placeholder="This component is disabled..."
disabled={disabled}
/>
<div style={{ marginTop: 16 }}>
<Switch
checked={disabled}
onChange={setDisabled}
checkedChildren="Disabled"
unCheckedChildren="Enabled"
/>
</div>
</div>
</Col>
</Row>
</Card>
</div>
);
}
export default DynamicVariableTestPage;

View File

@@ -0,0 +1,20 @@
.dynamic-variable-page {
padding: 24px;
max-width: 800px;
margin: 0 auto;
}
.multiselect-demo-container {
margin-top: 16px;
width: 100%;
}
.selected-values-display {
margin-top: 24px;
pre {
background-color: #f5f5f5;
padding: 16px;
border-radius: 6px;
}
}

View File

@@ -2,24 +2,18 @@ import './TracesModulePage.styles.scss';
import RouteTab from 'components/RouteTab';
import { TabRoutes } from 'components/RouteTab/types';
import { FeatureKeys } from 'constants/features';
import history from 'lib/history';
import { useAppContext } from 'providers/App/App';
import { useLocation } from 'react-router-dom';
import { tracesExplorer, tracesFunnel, tracesSaveView } from './constants';
function TracesModulePage(): JSX.Element {
const { pathname } = useLocation();
const { featureFlags } = useAppContext();
const isTraceFunnelsEnabled =
featureFlags?.find((flag) => flag.name === FeatureKeys.TRACE_FUNNELS)
?.active || false;
const routes: TabRoutes[] = [
tracesExplorer,
isTraceFunnelsEnabled ? tracesFunnel : null,
// TODO(shaheer): remove this check after everything is ready
process.env.NODE_ENV === 'development' ? tracesFunnel : null,
tracesSaveView,
].filter(Boolean) as TabRoutes[];

View File

@@ -40,11 +40,7 @@ import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
import { GlobalReducer } from 'types/reducer/globalTime';
import { v4 as generateUUID } from 'uuid';
import {
DashboardSortOrder,
IDashboardContext,
WidgetColumnWidths,
} from './types';
import { DashboardSortOrder, IDashboardContext } from './types';
import { sortLayout } from './util';
const DashboardContext = createContext<IDashboardContext>({
@@ -78,8 +74,6 @@ const DashboardContext = createContext<IDashboardContext>({
selectedRowWidgetId: '',
setSelectedRowWidgetId: () => {},
isDashboardFetching: false,
columnWidths: {},
setColumnWidths: () => {},
});
interface Props {
@@ -414,8 +408,6 @@ export function DashboardProvider({
}
};
const [columnWidths, setColumnWidths] = useState<WidgetColumnWidths>({});
const value: IDashboardContext = useMemo(
() => ({
toScrollWidgetId,
@@ -443,8 +435,6 @@ export function DashboardProvider({
selectedRowWidgetId,
setSelectedRowWidgetId,
isDashboardFetching,
columnWidths,
setColumnWidths,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[
@@ -467,8 +457,6 @@ export function DashboardProvider({
selectedRowWidgetId,
setSelectedRowWidgetId,
isDashboardFetching,
columnWidths,
setColumnWidths,
],
);

View File

@@ -10,10 +10,6 @@ export interface DashboardSortOrder {
search: string;
}
export type WidgetColumnWidths = {
[widgetId: string]: Record<string, number>;
};
export interface IDashboardContext {
isDashboardSliderOpen: boolean;
isDashboardLocked: boolean;
@@ -52,6 +48,4 @@ export interface IDashboardContext {
selectedRowWidgetId: string | null;
setSelectedRowWidgetId: React.Dispatch<React.SetStateAction<string | null>>;
isDashboardFetching: boolean;
columnWidths: WidgetColumnWidths;
setColumnWidths: React.Dispatch<React.SetStateAction<WidgetColumnWidths>>;
}

View File

@@ -764,7 +764,10 @@ export function QueryBuilderProvider({
);
const { safeNavigate } = useSafeNavigate({
preventSameUrlNavigation: false,
preventSameUrlNavigation: !(
initialDataSource === DataSource.LOGS ||
initialDataSource === DataSource.TRACES
),
});
const redirectWithQueryBuilderData = useCallback(

View File

@@ -109,7 +109,6 @@ export interface IBaseWidget {
selectedLogFields: IField[] | null;
selectedTracesFields: BaseAutocompleteData[] | null;
isLogScale?: boolean;
columnWidths?: Record<string, number>;
}
export interface Widgets extends IBaseWidget {
query: Query;

View File

@@ -120,4 +120,5 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
API_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'],
WORKSPACE_ACCESS_RESTRICTED: ['ADMIN', 'EDITOR', 'VIEWER'],
METRICS_EXPLORER_BASE: ['ADMIN', 'EDITOR', 'VIEWER'],
DYNAMIC_VARIABLE_TEST: ['ADMIN', 'EDITOR', 'VIEWER'],
};

73
go.mod
View File

@@ -1,12 +1,11 @@
module github.com/SigNoz/signoz
go 1.22.7
go 1.22.0
toolchain go1.22.11
toolchain go1.22.7
require (
dario.cat/mergo v1.0.1
github.com/AfterShip/clickhouse-sql-parser v0.4.4
github.com/ClickHouse/clickhouse-go/v2 v2.30.0
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd
@@ -16,12 +15,12 @@ require (
github.com/coreos/go-oidc/v3 v3.11.0
github.com/dustin/go-humanize v1.0.1
github.com/go-co-op/gocron v1.30.1
github.com/go-kit/log v0.2.1
github.com/go-openapi/runtime v0.28.0
github.com/go-openapi/strfmt v0.23.0
github.com/go-redis/redis/v8 v8.11.5
github.com/go-redis/redismock/v8 v8.11.5
github.com/go-viper/mapstructure/v2 v2.1.0
github.com/gojek/heimdall/v7 v7.0.3
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
github.com/gorilla/handlers v1.5.1
@@ -35,6 +34,7 @@ require (
github.com/knadh/koanf/v2 v2.1.1
github.com/mailru/easyjson v0.7.7
github.com/mattn/go-sqlite3 v1.14.24
github.com/oklog/oklog v0.3.2
github.com/open-telemetry/opamp-go v0.5.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.111.0
github.com/opentracing/opentracing-go v1.2.0
@@ -43,7 +43,7 @@ require (
github.com/prometheus/alertmanager v0.28.0
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/common v0.61.0
github.com/prometheus/prometheus v0.300.1
github.com/prometheus/prometheus v2.5.0+incompatible
github.com/rs/cors v1.11.1
github.com/russellhaering/gosaml2 v0.9.0
github.com/russellhaering/goxmldsig v1.2.0
@@ -61,7 +61,6 @@ require (
go.opentelemetry.io/collector/pdata v1.17.0
go.opentelemetry.io/collector/processor v0.111.0
go.opentelemetry.io/contrib/config v0.10.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel/metric v1.34.0
go.opentelemetry.io/otel/sdk v1.34.0
@@ -73,30 +72,27 @@ require (
golang.org/x/oauth2 v0.24.0
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
google.golang.org/protobuf v1.36.0
google.golang.org/protobuf v1.35.2
gopkg.in/segmentio/analytics-go.v3 v3.1.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
honnef.co/go/tools v0.0.1-2020.1.4
k8s.io/apimachinery v0.31.3
k8s.io/apimachinery v0.31.1
)
require (
cloud.google.com/go/auth v0.13.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect
cloud.google.com/go/auth v0.9.5 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // 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/BurntSushi/toml v0.3.1 // indirect
github.com/ClickHouse/ch-go v0.61.5 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.55.5 // indirect
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 // 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
@@ -108,15 +104,16 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.8.0 // indirect
github.com/edsrzf/mmap-go v1.2.0 // indirect
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/elastic/lunes v0.1.0 // indirect
github.com/expr-lang/expr v1.16.9 // indirect
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@@ -131,18 +128,16 @@ require (
github.com/goccy/go-json v0.10.3 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.0 // 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.24.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect
@@ -163,7 +158,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.11 // indirect
github.com/klauspost/compress v1.17.10 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/leodido/go-syslog/v4 v4.2.0 // indirect
github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b // indirect
@@ -202,10 +197,10 @@ require (
github.com/shopspring/decimal v1.4.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smarty/assertions v1.15.0 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
@@ -219,6 +214,7 @@ require (
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/collector v0.111.0 // indirect
go.opentelemetry.io/collector/component v0.111.0 // indirect
@@ -247,38 +243,39 @@ require (
go.opentelemetry.io/collector/processor/processorprofiles v0.111.0 // indirect
go.opentelemetry.io/collector/receiver v0.111.0 // indirect
go.opentelemetry.io/collector/receiver/receiverprofiles v0.111.0 // indirect
go.opentelemetry.io/collector/semconv v0.116.0 // indirect
go.opentelemetry.io/collector/semconv v0.111.0 // indirect
go.opentelemetry.io/collector/service v0.111.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.6.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.52.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.6.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.30.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.30.0 // indirect
go.opentelemetry.io/otel/log v0.10.0 // indirect
go.opentelemetry.io/otel/sdk/log v0.10.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.30.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/goleak v1.3.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.28.0 // indirect
gonum.org/v1/gonum v0.15.1 // indirect
google.golang.org/api v0.213.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/grpc v1.69.0 // indirect
google.golang.org/api v0.199.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.67.1 // indirect
gopkg.in/telebot.v3 v3.3.8 // indirect
k8s.io/client-go v0.31.3 // indirect
k8s.io/client-go v0.31.1 // 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.13.0

174
go.sum
View File

@@ -29,10 +29,10 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
cloud.google.com/go/auth v0.9.5 h1:4CTn43Eynw40aFVr3GpPqsQponx2jv0BQpjvajsbbzw=
cloud.google.com/go/auth v0.9.5/go.mod h1:Xo0n7n66eHyOWWCnitop6870Ilwo3PiZyodVkkH1xWM=
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=
@@ -44,8 +44,8 @@ cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJW
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
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/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
@@ -64,26 +64,19 @@ dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AfterShip/clickhouse-sql-parser v0.4.4 h1:iLRwjzz1mWmUEf5UNrSYOceQ+PX9SdBJ8Xw0DNrL114=
github.com/AfterShip/clickhouse-sql-parser v0.4.4/go.mod h1:W0Z82wJWkJxz2RVun/RMwxue3g7ut47Xxl+SFqdJGus=
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.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ=
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=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0/go.mod h1:Y/HgrePTmGy9HjdSGTqZNa+apUpTVIEVKXJyARP2lrk=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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=
@@ -95,15 +88,15 @@ github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1v
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
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.13.0 h1:hsUql1zd83ifXtswO9Qk1rpCgVjE/ItQvgdNocBSqRI=
github.com/SigNoz/prometheus v1.13.0/go.mod h1:4PC0dxmx6y3kNI2d9oOTvEFTPkH6QnxDxERyqeL1hvI=
github.com/SigNoz/signoz-otel-collector v0.111.16 h1:535uKH5Oux+35EsI+L3C6pnAP/Ye0PTCbVizXoL+VqE=
github.com/SigNoz/signoz-otel-collector v0.111.16/go.mod h1:HJ4m0LY1MPsuZmuRF7Ixb+bY8rxgRzI0VXzOedESsjg=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -149,7 +142,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -173,8 +165,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cncf/xds/go v0.0.0-20240822171458-6449f94b4d59 h1:fLZ97KE86ELjEYJCEUVzmbhfzDxHHGwBrDVMd4XL6Bs=
github.com/cncf/xds/go v0.0.0-20240822171458-6449f94b4d59/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/coder/quartz v0.1.2 h1:PVhc9sJimTdKd3VbygXtS4826EOCpB1fXoRlLnCrE+s=
github.com/coder/quartz v0.1.2/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA=
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
@@ -193,12 +185,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.126.0 h1:+Znh7VMQj/E8ArbjWnc7OKGjWfzC+I8OCSRp7r1MdD8=
github.com/digitalocean/godo v1.126.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
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.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.3.0+incompatible h1:BNb1QY6o4JdKpqwi9IB+HUYcRRrVN4aGFUTvDmWYK1A=
github.com/docker/docker v27.3.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=
@@ -208,8 +200,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/elastic/lunes v0.1.0 h1:amRtLPjwkWtzDF/RKzcEPMvSsSseLDLW+bnhfNSLRe4=
github.com/elastic/lunes v0.1.0/go.mod h1:xGphYIt3XdZRtyWosHQTErsQTd4OP1p9wsbVoHelrd4=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
@@ -227,8 +219,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
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.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
@@ -250,8 +242,8 @@ github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUork
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
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=
@@ -269,10 +261,14 @@ github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JS
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -330,16 +326,13 @@ github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gojek/heimdall/v7 v7.0.3 h1:+5sAhl8S0m+qRRL8IVeHCJudFh/XkG3wyO++nvOg+gc=
github.com/gojek/heimdall/v7 v7.0.3/go.mod h1:Z43HtMid7ysSjmsedPTXAki6jcdcNVnjn5pmsTyiMic=
github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf h1:5xRGbUdOmZKoDXkGx5evVLehuCMpuO1hl701bEQqXOM=
github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf/go.mod h1:QzhUKaYKJmcbTnCYCAVQrroCOY7vOOI8cSQ4NbuhYf0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -435,12 +428,9 @@ github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw=
github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
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 v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
@@ -457,8 +447,8 @@ 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.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
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.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
github.com/hashicorp/consul/api v1.29.4 h1:P6slzxDLBOxUSj3fWo2o65VuKtbtOXFi7TSSgtXutuE=
@@ -585,14 +575,12 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM=
@@ -623,8 +611,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.41.0 h1:GcP7JIBr9iLRJ9FwAtb9/WCT1DuPJS/xUApapfdjtiY=
github.com/linode/linodego v1.41.0/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY=
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-20240408141607-282e7b5d6b74 h1:1KuuSOy4ZNgW0KA2oYIngXVFhQcXxhLqCVK7cBcldkk=
github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
@@ -702,6 +690,8 @@ github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnu
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
@@ -808,13 +798,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prometheus v0.300.1 h1:9KKcTTq80gkzmXW0Et/QCFSrBPgmwiS3Hlcxc6o8KlM=
github.com/prometheus/prometheus v0.300.1/go.mod h1:gtTPY/XVyCdqqnjA3NzDMb0/nc5H9hOu1RMame+gHyM=
github.com/puzpuzpuz/xsync/v3 v3.5.0 h1:i+cMcpEDY1BkNm7lPDkCtE4oElsYLn+EKF8kAu2vXT4=
github.com/puzpuzpuz/xsync/v3 v3.5.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
@@ -842,14 +827,10 @@ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dul
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 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N++y4=
github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
@@ -863,10 +844,10 @@ github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92/go.mod h1:7/OT02F6
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
@@ -884,7 +865,6 @@ github.com/srikanthccv/ClickHouse-go-mock v0.11.0 h1:hKY9l7SbhI4IPPs7hjKAL1iDgKc
github.com/srikanthccv/ClickHouse-go-mock v0.11.0/go.mod h1:CzFC21J4tLn7cEYdU5k6hg7yyf052xtZXUY2e3UF6+I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
@@ -898,6 +878,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -967,6 +948,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/collector v0.111.0 h1:D3LJTYrrK2ac94E2PXPSbVkArqxbklbCLsE4MAJQdRo=
@@ -1049,16 +1032,14 @@ go.opentelemetry.io/collector/receiver v0.111.0 h1:6cRHZ9cUxYfRPkArUCkIhoo7Byf6t
go.opentelemetry.io/collector/receiver v0.111.0/go.mod h1:QSl/n9ikDP+6n39QcRY/VLjwQI0qbT1RQp512uBQl3g=
go.opentelemetry.io/collector/receiver/receiverprofiles v0.111.0 h1:oYLAdGMQQR7gB6wVkbV0G4EMsrmiOs3O0qf3hh/3avw=
go.opentelemetry.io/collector/receiver/receiverprofiles v0.111.0/go.mod h1:M/OfdEGnvyB+fSTSW4RPKj5N06FXL8oKSIf60FlrKmM=
go.opentelemetry.io/collector/semconv v0.116.0 h1:63xCZomsKJAWmKGWD3lnORiE3WKW6AO4LjnzcHzGx3Y=
go.opentelemetry.io/collector/semconv v0.116.0/go.mod h1:N6XE8Q0JKgBN2fAhkUQtqK9LT7rEGR6+Wu/Rtbal1iI=
go.opentelemetry.io/collector/semconv v0.111.0 h1:ELleMtLBzeZ3xhfhYPmFcLc0hJMqRxhOB0eY60WLivw=
go.opentelemetry.io/collector/semconv v0.111.0/go.mod h1:zCJ5njhWpejR+A40kiEoeFm1xq1uzyZwMnRNX6/D82A=
go.opentelemetry.io/collector/service v0.111.0 h1:6yGjjbZvlYbir+vzi/9ACF965m8i96ScPTjpVvki3ms=
go.opentelemetry.io/collector/service v0.111.0/go.mod h1:tti8TAosPuRj51/bbrSvf6OIJoSyTkywEvTdY/fAuwY=
go.opentelemetry.io/contrib/config v0.10.0 h1:2JknAzMaYjxrHkTnZh3eOme/Y2P5eHE2SWfhfV6Xd6c=
go.opentelemetry.io/contrib/config v0.10.0/go.mod h1:aND2M6/KfNkntI5cyvHriR/zvZgPf8j9yETdSmvpfmc=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
go.opentelemetry.io/contrib/propagators/b3 v1.30.0 h1:vumy4r1KMyaoQRltX7cJ37p3nluzALX9nugCjNNefuY=
go.opentelemetry.io/contrib/propagators/b3 v1.30.0/go.mod h1:fRbvRsaeVZ82LIl3u0rIvusIel2UUf+JcaaIpy5taho=
go.opentelemetry.io/contrib/zpages v0.55.0 h1:F+xj261Ulwl79QC+2O+IO1b3NbwppUDwN+7LbDSdQcY=
@@ -1071,12 +1052,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0 h1:Wyp
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0/go.mod h1:U79SV99vtvGSEBeeHnpgGJfTsnsdkWLpPN/CcHAzBSI=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 h1:VrMAbeJz4gnVDg2zEzjHG4dEH86j4jO6VYB+NgtGD8s=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0/go.mod h1:qqN/uFdpeitTvm+JDqqnjm517pmQRYxTORbETHq5tOc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 h1:umZgi92IyxfXd/l4kaDhnKgY8rnN/cZcF1LKc6I8OQ8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0/go.mod h1:4lVs6obhSVRb1EW5FhOuBTyiQhtRtAnnva9vD3yRfq8=
go.opentelemetry.io/otel/exporters/prometheus v0.52.0 h1:kmU3H0b9ufFSi8IQCcxack+sWUblKkFbqWYs6YiACGQ=
go.opentelemetry.io/otel/exporters/prometheus v0.52.0/go.mod h1:+wsAp2+JhuGXX7YRkjlkx6hyWY3ogFPfNA4x3nyiAh0=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.6.0 h1:bZHOb8k/CwwSt0DgvgaoOhBXWNdWqFWaIsGTtg1H3KE=
@@ -1093,13 +1074,13 @@ go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7W
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/log v0.10.0 h1:lR4teQGWfeDVGoute6l0Ou+RpFqQ9vaPdrNJlST0bvw=
go.opentelemetry.io/otel/sdk/log v0.10.0/go.mod h1:A+V1UTWREhWAittaQEG4bYm4gAZa6xnvVu+xKrIRkzo=
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
go.opentelemetry.io/otel/sdk/metric v1.30.0 h1:QJLT8Pe11jyHBHfSAgYH7kEmT24eX792jZO1bo4BXkM=
go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
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=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
@@ -1341,6 +1322,7 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -1363,15 +1345,14 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.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=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -1471,8 +1452,8 @@ google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRR
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
google.golang.org/api v0.213.0 h1:KmF6KaDyFqB417T68tMPbVmmwtIXs2VB60OJKIHB0xQ=
google.golang.org/api v0.213.0/go.mod h1:V0T5ZhNUUNpYAlL306gFZPFt5F5D/IeyLoktduYYnvQ=
google.golang.org/api v0.199.0 h1:aWUXClp+VFJmqE0JPvpZOK3LDQMyFKYIow4etYd9qxs=
google.golang.org/api v0.199.0/go.mod h1:ohG4qSztDJmZdjK/Ar6MhbAmb/Rpi4JHOqagsh90K28=
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=
@@ -1559,10 +1540,10 @@ google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 h1:ChAdCYNQFDk5fYvFZMywKLIijG7TC2m1C2CMEu11G3o=
google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484/go.mod h1:KRUmxRI4JmbpAm8gcZM4Jsffi859fo5LQjILwuqj9z8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
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=
@@ -1595,8 +1576,8 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI=
google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
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=
@@ -1612,8 +1593,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1653,14 +1634,13 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8=
k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE=
k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4=
k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4=
k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs=
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
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=

View File

@@ -1,58 +0,0 @@
package client
import (
"log/slog"
"net/http"
"time"
"github.com/SigNoz/signoz/pkg/http/client/plugin"
"github.com/gojek/heimdall/v7/httpclient"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/trace"
)
type Client struct {
c *httpclient.Client
netc *http.Client
}
func New(logger *slog.Logger, tracerProvider trace.TracerProvider, meterProvider metric.MeterProvider, opts ...Option) (*Client, error) {
clientOpts := options{
retryCount: 3,
requestResponseLog: false,
timeout: 5 * time.Second,
}
for _, opt := range opts {
opt(&clientOpts)
}
netc := &http.Client{
Timeout: clientOpts.timeout,
Transport: otelhttp.NewTransport(http.DefaultTransport, otelhttp.WithTracerProvider(tracerProvider), otelhttp.WithMeterProvider(meterProvider)),
}
c := httpclient.NewClient(
httpclient.WithHTTPClient(netc),
httpclient.WithRetrier(clientOpts.retriable),
httpclient.WithRetryCount(clientOpts.retryCount),
)
if clientOpts.requestResponseLog {
c.AddPlugin(plugin.NewLog(logger))
}
return &Client{
netc: netc,
c: c,
}, nil
}
func (c *Client) Do(request *http.Request) (*http.Response, error) {
return c.c.Do(request)
}
func (c *Client) Client() *http.Client {
return c.netc
}

View File

@@ -1,42 +0,0 @@
package client
import (
"time"
"github.com/gojek/heimdall/v7"
)
type Retriable = heimdall.Retriable
type options struct {
retryCount int
requestResponseLog bool
timeout time.Duration
retriable Retriable
}
type Option func(*options)
func WithRetryCount(i int) Option {
return func(o *options) {
o.retryCount = i
}
}
func WithTimeout(i time.Duration) Option {
return func(o *options) {
o.timeout = i
}
}
func WithRequestResponseLog(b bool) Option {
return func(o *options) {
o.requestResponseLog = b
}
}
func WithRetriable(retriable Retriable) Option {
return func(o *options) {
o.retriable = retriable
}
}

View File

@@ -1,77 +0,0 @@
package plugin
import (
"bytes"
"io"
"log/slog"
"net"
"net/http"
"github.com/gojek/heimdall/v7"
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
)
type reqResLog struct {
logger *slog.Logger
}
func NewLog(logger *slog.Logger) heimdall.Plugin {
return &reqResLog{
logger: logger,
}
}
func (plugin *reqResLog) OnRequestStart(request *http.Request) {
host, port, _ := net.SplitHostPort(request.Host)
fields := []any{
string(semconv.HTTPRequestMethodKey), request.Method,
string(semconv.URLPathKey), request.URL.Path,
string(semconv.URLSchemeKey), request.URL.Scheme,
string(semconv.UserAgentOriginalKey), request.UserAgent(),
string(semconv.ServerAddressKey), host,
string(semconv.ServerPortKey), port,
string(semconv.HTTPRequestSizeKey), request.ContentLength,
"http.request.headers", request.Header,
}
plugin.logger.InfoContext(request.Context(), "::SENT-REQUEST::", fields...)
}
func (plugin *reqResLog) OnRequestEnd(request *http.Request, response *http.Response) {
fields := []any{
string(semconv.HTTPResponseStatusCodeKey), response.StatusCode,
string(semconv.HTTPResponseBodySizeKey), response.ContentLength,
}
bodybytes, err := io.ReadAll(response.Body)
if err != nil {
plugin.logger.DebugContext(request.Context(), "::UNABLE-TO-LOG-RESPONSE-BODY::", "error", err)
} else {
_ = response.Body.Close()
response.Body = io.NopCloser(bytes.NewBuffer(bodybytes))
if len(bodybytes) > 0 {
fields = append(fields, "http.response.body", string(bodybytes))
} else {
fields = append(fields, "http.response.body", "(empty)")
}
}
plugin.logger.InfoContext(request.Context(), "::RECEIVED-RESPONSE::", fields...)
}
func (plugin *reqResLog) OnError(request *http.Request, err error) {
host, port, _ := net.SplitHostPort(request.Host)
fields := []any{
err,
string(semconv.HTTPRequestMethodKey), request.Method,
string(semconv.URLPathKey), request.URL.Path,
string(semconv.URLSchemeKey), request.URL.Scheme,
string(semconv.UserAgentOriginalKey), request.UserAgent(),
string(semconv.ServerAddressKey), host,
string(semconv.ServerPortKey), port,
string(semconv.HTTPRequestSizeKey), request.ContentLength,
}
plugin.logger.ErrorContext(request.Context(), "::UNABLE-TO-SEND-REQUEST::", fields...)
}

View File

@@ -1,270 +0,0 @@
package clickhouseprometheus
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/prometheus/prometheus/prompb"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/storage/remote"
)
type client struct {
settings factory.ScopedProviderSettings
telemetryStore telemetrystore.TelemetryStore
}
func NewReadClient(settings factory.ScopedProviderSettings, telemetryStore telemetrystore.TelemetryStore) remote.ReadClient {
return &client{
settings: settings,
telemetryStore: telemetryStore,
}
}
func (client *client) Read(ctx context.Context, query *prompb.Query, sortSeries bool) (storage.SeriesSet, error) {
if len(query.Matchers) == 2 {
var hasJob bool
var queryString string
for _, m := range query.Matchers {
if m.Type == prompb.LabelMatcher_EQ && m.Name == "job" && m.Value == "rawsql" {
hasJob = true
}
if m.Type == prompb.LabelMatcher_EQ && m.Name == "query" {
queryString = m.Value
}
}
if hasJob && queryString != "" {
res, err := client.queryRaw(ctx, queryString, int64(query.EndTimestampMs))
if err != nil {
return nil, err
}
return remote.FromQueryResult(sortSeries, res), nil
}
}
var metricName string
for _, matcher := range query.Matchers {
if matcher.Name == "__name__" {
metricName = matcher.Value
}
}
clickhouseQuery, args, err := client.queryToClickhouseQuery(ctx, query, metricName, false)
if err != nil {
return nil, err
}
fingerprints, err := client.getFingerprintsFromClickhouseQuery(ctx, clickhouseQuery, args)
if err != nil {
return nil, err
}
if len(fingerprints) == 0 {
return remote.FromQueryResult(sortSeries, new(prompb.QueryResult)), nil
}
clickhouseSubQuery, args, err := client.queryToClickhouseQuery(ctx, query, metricName, true)
if err != nil {
return nil, err
}
res := new(prompb.QueryResult)
timeseries, err := client.querySamples(ctx, int64(query.StartTimestampMs), int64(query.EndTimestampMs), fingerprints, metricName, clickhouseSubQuery, args)
if err != nil {
return nil, err
}
res.Timeseries = timeseries
return remote.FromQueryResult(sortSeries, res), nil
}
func (client *client) queryToClickhouseQuery(_ context.Context, query *prompb.Query, metricName string, subQuery bool) (string, []any, error) {
var clickHouseQuery string
var conditions []string
var argCount int = 0
var selectString string = "fingerprint, any(labels)"
if subQuery {
argCount = 1
selectString = "fingerprint"
}
start, end, tableName := getStartAndEndAndTableName(query.StartTimestampMs, query.EndTimestampMs)
var args []any
conditions = append(conditions, fmt.Sprintf("metric_name = $%d", argCount+1))
conditions = append(conditions, "temporality IN ['Cumulative', 'Unspecified']")
conditions = append(conditions, "__normalized = true")
conditions = append(conditions, fmt.Sprintf("unix_milli >= %d AND unix_milli < %d", start, end))
args = append(args, metricName)
for _, m := range query.Matchers {
switch m.Type {
case prompb.LabelMatcher_EQ:
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, $%d) = $%d", argCount+2, argCount+3))
case prompb.LabelMatcher_NEQ:
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, $%d) != $%d", argCount+2, argCount+3))
case prompb.LabelMatcher_RE:
conditions = append(conditions, fmt.Sprintf("match(JSONExtractString(labels, $%d), $%d)", argCount+2, argCount+3))
case prompb.LabelMatcher_NRE:
conditions = append(conditions, fmt.Sprintf("not match(JSONExtractString(labels, $%d), $%d)", argCount+2, argCount+3))
default:
return "", nil, fmt.Errorf("unexpected matcher found in query: %s", m.Type.String())
}
args = append(args, m.Name, m.Value)
argCount += 2
}
whereClause := strings.Join(conditions, " AND ")
clickHouseQuery = fmt.Sprintf(`SELECT %s FROM %s.%s WHERE %s GROUP BY fingerprint`, selectString, databaseName, tableName, whereClause)
return clickHouseQuery, args, nil
}
func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, query string, args []any) (map[uint64][]prompb.Label, error) {
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
if err != nil {
return nil, err
}
defer rows.Close()
fingerprints := make(map[uint64][]prompb.Label)
var fingerprint uint64
var labelString string
for rows.Next() {
if err = rows.Scan(&fingerprint, &labelString); err != nil {
return nil, err
}
labels, _, err := unmarshalLabels(labelString)
if err != nil {
return nil, err
}
fingerprints[fingerprint] = labels
}
if err := rows.Err(); err != nil {
return nil, err
}
return fingerprints, nil
}
func (client *client) querySamples(ctx context.Context, start int64, end int64, fingerprints map[uint64][]prompb.Label, metricName string, subQuery string, args []any) ([]*prompb.TimeSeries, error) {
argCount := len(args)
query := fmt.Sprintf(`
SELECT metric_name, fingerprint, unix_milli, value
FROM %s.%s
WHERE metric_name = $1 AND fingerprint GLOBAL IN (%s) AND unix_milli >= $%s AND unix_milli <= $%s ORDER BY fingerprint, unix_milli;`,
databaseName, distributedSamplesV4, subQuery, strconv.Itoa(argCount+2), strconv.Itoa(argCount+3))
query = strings.TrimSpace(query)
allArgs := append([]any{metricName}, args...)
allArgs = append(allArgs, start, end)
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query, allArgs...)
if err != nil {
return nil, err
}
defer rows.Close()
var res []*prompb.TimeSeries
var ts *prompb.TimeSeries
var fingerprint, prevFingerprint uint64
var timestampMs int64
var value float64
for rows.Next() {
if err := rows.Scan(&metricName, &fingerprint, &timestampMs, &value); err != nil {
return nil, err
}
// collect samples in time series
if fingerprint != prevFingerprint {
// add collected time series to result
prevFingerprint = fingerprint
if ts != nil {
res = append(res, ts)
}
labels := fingerprints[fingerprint]
ts = &prompb.TimeSeries{
Labels: labels,
}
}
// add samples to current time series
ts.Samples = append(ts.Samples, prompb.Sample{
Timestamp: timestampMs,
Value: value,
})
}
// add last time series
if ts != nil {
res = append(res, ts)
}
if err := rows.Err(); err != nil {
return nil, err
}
return res, nil
}
func (client *client) queryRaw(ctx context.Context, query string, ts int64) (*prompb.QueryResult, error) {
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query)
if err != nil {
return nil, err
}
defer rows.Close()
columns := rows.Columns()
var res prompb.QueryResult
targets := make([]any, len(columns))
for i := range targets {
targets[i] = new(scanner)
}
for rows.Next() {
if err = rows.Scan(targets...); err != nil {
return nil, err
}
labels := make([]prompb.Label, 0, len(columns))
var value float64
for i, c := range columns {
v := targets[i].(*scanner)
switch c {
case "value":
value = v.f
default:
labels = append(labels, prompb.Label{
Name: c,
Value: v.s,
})
}
}
res.Timeseries = append(res.Timeseries, &prompb.TimeSeries{
Labels: labels,
Samples: []prompb.Sample{{
Value: value,
Timestamp: ts,
}},
})
}
if err = rows.Err(); err != nil {
return nil, err
}
return &res, nil
}

View File

@@ -1,27 +0,0 @@
package clickhouseprometheus
import (
"encoding/json"
"github.com/prometheus/prometheus/prompb"
)
// Unmarshals JSON into Prometheus labels. It does not preserve order.
func unmarshalLabels(s string) ([]prompb.Label, string, error) {
var metricName string
m := make(map[string]string)
if err := json.Unmarshal([]byte(s), &m); err != nil {
return nil, metricName, err
}
res := make([]prompb.Label, 0, len(m))
for n, v := range m {
if n == "__name__" {
metricName = v
}
res = append(res, prompb.Label{
Name: n,
Value: v,
})
}
return res, metricName, nil
}

View File

@@ -1,60 +0,0 @@
package clickhouseprometheus
import (
"context"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/prometheus"
"github.com/SigNoz/signoz/pkg/telemetrystore"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/storage"
"github.com/prometheus/prometheus/storage/remote"
)
var stCallback = func() (int64, error) {
return int64(model.Latest), nil
}
type provider struct {
settings factory.ScopedProviderSettings
telemetryStore telemetrystore.TelemetryStore
engine *prometheus.Engine
queryable storage.SampleAndChunkQueryable
}
func NewFactory(telemetryStore telemetrystore.TelemetryStore) factory.ProviderFactory[prometheus.Prometheus, prometheus.Config] {
return factory.NewProviderFactory(factory.MustNewName("clickhouse"), func(ctx context.Context, providerSettings factory.ProviderSettings, config prometheus.Config) (prometheus.Prometheus, error) {
return New(ctx, providerSettings, config, telemetryStore)
})
}
func New(ctx context.Context, providerSettings factory.ProviderSettings, config prometheus.Config, telemetryStore telemetrystore.TelemetryStore) (prometheus.Prometheus, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/prometheus/clickhouseprometheus")
readClient := NewReadClient(settings, telemetryStore)
return &provider{
settings: settings,
telemetryStore: telemetryStore,
engine: prometheus.NewEngine(settings.Logger(), config),
queryable: remote.NewSampleAndChunkQueryableClient(readClient, labels.EmptyLabels(), []*labels.Matcher{}, false, stCallback),
}, nil
}
func (provider *provider) Engine() *prometheus.Engine {
return provider.engine
}
func (provider *provider) Storage() storage.Queryable {
return provider
}
func (provider *provider) Querier(mint, maxt int64) (storage.Querier, error) {
querier, err := provider.queryable.Querier(mint, maxt)
if err != nil {
return nil, err
}
return storage.NewMergeQuerier(nil, []storage.Querier{querier}, storage.ChainedSeriesMerge), nil
}

View File

@@ -1,31 +0,0 @@
package clickhouseprometheus
import (
"database/sql"
"fmt"
)
var _ sql.Scanner = (*scanner)(nil)
type scanner struct {
f float64
s string
}
func (s *scanner) Scan(val any) error {
s.f = 0
s.s = ""
s.s = fmt.Sprintf("%v", val)
switch val := val.(type) {
case int64:
s.f = float64(val)
case uint64:
s.f = float64(val)
case float64:
s.f = val
case []byte:
s.s = string(val)
}
return nil
}

View File

@@ -1,41 +0,0 @@
package clickhouseprometheus
import "time"
const (
databaseName string = "signoz_metrics"
distributedTimeSeriesV4 string = "distributed_time_series_v4"
distributedTimeSeriesV46hrs string = "distributed_time_series_v4_6hrs"
distributedTimeSeriesV41day string = "distributed_time_series_v4_1day"
distributedSamplesV4 string = "distributed_samples_v4"
)
var (
sixHoursInMilliseconds = time.Hour.Milliseconds() * 6
oneDayInMilliseconds = time.Hour.Milliseconds() * 24
)
// Returns the start time, end time and the table name to use for the query.
//
// If time range is less than 6 hours, we need to use the `time_series_v4` table
// else if time range is less than 1 day and greater than 6 hours, we need to use the `time_series_v4_6hrs` table
// else we need to use the `time_series_v4_1day` table
func getStartAndEndAndTableName(start, end int64) (int64, int64, string) {
var tableName string
if end-start <= sixHoursInMilliseconds {
// adjust the start time to nearest 1 hour
start = start - (start % (time.Hour.Milliseconds() * 1))
tableName = distributedTimeSeriesV4
} else if end-start <= oneDayInMilliseconds {
// adjust the start time to nearest 6 hours
start = start - (start % (time.Hour.Milliseconds() * 6))
tableName = distributedTimeSeriesV46hrs
} else {
// adjust the start time to nearest 1 day
start = start - (start % (time.Hour.Milliseconds() * 24))
tableName = distributedTimeSeriesV41day
}
return start, end, tableName
}

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