From 4d375e7cc3021d2d0e49aab1e71375ff721b5699 Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Wed, 5 Jun 2024 19:12:50 +0530 Subject: [PATCH 001/281] =?UTF-8?q?chore(signoz):=20=F0=9F=93=8C=20pin=20v?= =?UTF-8?q?ersions:=20SigNoz=200.47.0,=20SigNoz=20OtelCollector=200.88.26?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Prashant Shahi --- deploy/docker-swarm/clickhouse-setup/docker-compose.yaml | 8 ++++---- deploy/docker/clickhouse-setup/docker-compose-core.yaml | 4 ++-- .../docker/clickhouse-setup/docker-compose.testing.yaml | 8 ++++---- deploy/docker/clickhouse-setup/docker-compose.yaml | 8 ++++---- go.mod | 2 +- go.sum | 4 ++-- pkg/query-service/tests/test-deploy/docker-compose.yaml | 4 ++-- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index e6354bb35e..755d61c919 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.46.0 + image: signoz/query-service:0.47.0 command: [ "-config=/root/config/prometheus.yml", @@ -186,7 +186,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:0.46.0 + image: signoz/frontend:0.47.0 deploy: restart_policy: condition: on-failure @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.88.24 + image: signoz/signoz-otel-collector:0.88.26 command: [ "--config=/etc/otel-collector-config.yaml", @@ -237,7 +237,7 @@ services: - query-service otel-collector-migrator: - image: signoz/signoz-schema-migrator:0.88.24 + image: signoz/signoz-schema-migrator:0.88.26 deploy: restart_policy: condition: on-failure diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index cf1e5f1ed4..d18c10f913 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -66,7 +66,7 @@ services: - --storage.path=/data otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -81,7 +81,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.88.24 + image: signoz/signoz-otel-collector:0.88.26 command: [ "--config=/etc/otel-collector-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index efd61986b5..48d77b98df 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.46.0} + image: signoz/query-service:${DOCKER_TAG:-0.47.0} container_name: signoz-query-service command: [ @@ -204,7 +204,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.46.0} + image: signoz/frontend:${DOCKER_TAG:-0.47.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -216,7 +216,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -230,7 +230,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.26} container_name: signoz-otel-collector command: [ diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 5e2096e38a..d47b7acd46 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.46.0} + image: signoz/query-service:${DOCKER_TAG:-0.47.0} container_name: signoz-query-service command: [ @@ -203,7 +203,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.46.0} + image: signoz/frontend:${DOCKER_TAG:-0.47.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -215,7 +215,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -229,7 +229,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.26} container_name: signoz-otel-collector command: [ diff --git a/go.mod b/go.mod index d6c4eb4b36..a8de8d1c41 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.20.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd - github.com/SigNoz/signoz-otel-collector v0.88.24 + github.com/SigNoz/signoz-otel-collector v0.88.26 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 diff --git a/go.sum b/go.sum index c413c7545b..e6d22788a4 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,8 @@ github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkb github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc= github.com/SigNoz/prometheus v1.11.0 h1:toX7fU2wqY1TnzvPzDglIYx6OxpqrZ0NNlM/H5S5+u8= github.com/SigNoz/prometheus v1.11.0/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww= -github.com/SigNoz/signoz-otel-collector v0.88.24 h1:6ESLmQtYPHmik9ZZFSJSbfuj4VQ1/0IC3v1qV9hm5Nk= -github.com/SigNoz/signoz-otel-collector v0.88.24/go.mod h1:sT1EM9PFDaOJLbAz5npWpgXK6OhpWJ9PpSwyhHWs9rU= +github.com/SigNoz/signoz-otel-collector v0.88.26 h1:+dbBOzIN6nKWD6DSAbsTtm9fcsUC5dSkhDLk6IfQuxg= +github.com/SigNoz/signoz-otel-collector v0.88.26/go.mod h1:sT1EM9PFDaOJLbAz5npWpgXK6OhpWJ9PpSwyhHWs9rU= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 396b059157..67d6f6d508 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -192,7 +192,7 @@ services: <<: *db-depend otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -205,7 +205,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:0.88.24 + image: signoz/signoz-otel-collector:0.88.26 container_name: signoz-otel-collector command: [ From f1b5da99168ce736654caf416f00b5bbdf61996f Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 17 Jun 2024 09:00:55 +0530 Subject: [PATCH 002/281] chore: fix elapsed time formatting (#5238) --- ee/query-service/app/db/metrics.go | 2 +- .../app/clickhouseReader/reader.go | 28 +++++++++++-------- pkg/query-service/utils/time.go | 15 +++++----- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/ee/query-service/app/db/metrics.go b/ee/query-service/app/db/metrics.go index 5a694edab6..0cc8a55c32 100644 --- a/ee/query-service/app/db/metrics.go +++ b/ee/query-service/app/db/metrics.go @@ -21,7 +21,7 @@ import ( // GetMetricResultEE runs the query and returns list of time series func (r *ClickhouseReader) GetMetricResultEE(ctx context.Context, query string) ([]*basemodel.Series, string, error) { - defer utils.Elapsed("GetMetricResult")() + defer utils.Elapsed("GetMetricResult", nil)() zap.L().Info("Executing metric result query: ", zap.String("query", query)) var hash string diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 3b00087a88..4d2b369956 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -3081,7 +3081,7 @@ func (r *ClickHouseReader) GetMetricResultEE(ctx context.Context, query string) // GetMetricResult runs the query and returns list of time series func (r *ClickHouseReader) GetMetricResult(ctx context.Context, query string) ([]*model.Series, error) { - defer utils.Elapsed("GetMetricResult")() + defer utils.Elapsed("GetMetricResult", nil)() zap.L().Info("Executing metric result query: ", zap.String("query", query)) @@ -4594,26 +4594,27 @@ func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNam return seriesList, getPersonalisedError(rows.Err()) } -func logComment(ctx context.Context) string { - // Get the key-value pairs from context for log comment +func logCommentKVs(ctx context.Context) map[string]string { kv := ctx.Value(common.LogCommentKey) if kv == nil { - return "" + return nil } - logCommentKVs, ok := kv.(map[string]string) if !ok { - return "" + return nil } - - x, _ := json.Marshal(logCommentKVs) - return string(x) + return logCommentKVs } // GetTimeSeriesResultV3 runs the query and returns list of time series func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query string) ([]*v3.Series, error) { - defer utils.Elapsed("GetTimeSeriesResultV3", query, fmt.Sprintf("logComment: %s", logComment(ctx)))() + ctxArgs := map[string]interface{}{"query": query} + for k, v := range logCommentKVs(ctx) { + ctxArgs[k] = v + } + + defer utils.Elapsed("GetTimeSeriesResultV3", ctxArgs)() rows, err := r.db.Query(ctx, query) @@ -4655,7 +4656,12 @@ func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query stri // GetListResultV3 runs the query and returns list of rows func (r *ClickHouseReader) GetListResultV3(ctx context.Context, query string) ([]*v3.Row, error) { - defer utils.Elapsed("GetListResultV3", query, fmt.Sprintf("logComment: %s", logComment(ctx)))() + ctxArgs := map[string]interface{}{"query": query} + for k, v := range logCommentKVs(ctx) { + ctxArgs[k] = v + } + + defer utils.Elapsed("GetListResultV3", ctxArgs)() rows, err := r.db.Query(ctx, query) diff --git a/pkg/query-service/utils/time.go b/pkg/query-service/utils/time.go index 274b032cdb..612da4f4bc 100644 --- a/pkg/query-service/utils/time.go +++ b/pkg/query-service/utils/time.go @@ -1,20 +1,19 @@ package utils import ( - "fmt" "time" "go.uber.org/zap" ) -func Elapsed(funcName string, args ...interface{}) func() { +func Elapsed(funcName string, args map[string]interface{}) func() { start := time.Now() - argsStr := "" - for _, v := range args { - argsStr += fmt.Sprintf("%v, ", v) - } - argsStr = argsStr[:len(argsStr)-2] return func() { - zap.L().Info("Elapsed time", zap.String("func_name", funcName), zap.Duration("duration", time.Since(start)), zap.String("args", argsStr)) + var zapFields []zap.Field + zapFields = append(zapFields, zap.String("func_name", funcName), zap.Duration("duration", time.Since(start))) + for k, v := range args { + zapFields = append(zapFields, zap.Any(k, v)) + } + zap.L().Info("Elapsed time", zapFields...) } } From 2a4e97f8da412de99086d599840c59ef13a3ca61 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 17 Jun 2024 15:51:04 +0530 Subject: [PATCH 003/281] fix: table sorting when units are present (#5249) --- frontend/src/container/GridTableComponent/index.tsx | 4 ++-- frontend/src/lib/query/createTableColumnsFromQuery.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/GridTableComponent/index.tsx b/frontend/src/container/GridTableComponent/index.tsx index 26bdda1a49..171f09da07 100644 --- a/frontend/src/container/GridTableComponent/index.tsx +++ b/frontend/src/container/GridTableComponent/index.tsx @@ -62,10 +62,11 @@ function GridTableComponent({ mutateDataSource = mutateDataSource.map( (val): RowData => { - const newValue = val; + const newValue = { ...val }; Object.keys(val).forEach((k) => { if (columnUnits[k]) { newValue[k] = getYAxisFormattedValue(String(val[k]), columnUnits[k]); + newValue[`${k}_without_unit`] = val[k]; } }); return newValue; @@ -81,7 +82,6 @@ function GridTableComponent({ applyColumnUnits, originalDataSource, ]); - useEffect(() => { if (tableProcessedDataRef) { // eslint-disable-next-line no-param-reassign diff --git a/frontend/src/lib/query/createTableColumnsFromQuery.ts b/frontend/src/lib/query/createTableColumnsFromQuery.ts index 28b5b3dc67..c3a5865cd9 100644 --- a/frontend/src/lib/query/createTableColumnsFromQuery.ts +++ b/frontend/src/lib/query/createTableColumnsFromQuery.ts @@ -537,8 +537,12 @@ const generateTableColumns = ( width: QUERY_TABLE_CONFIG.width, render: renderColumnCell && renderColumnCell[item.dataIndex], sorter: (a: RowData, b: RowData): number => { - const valueA = Number(a[item.dataIndex]); - const valueB = Number(b[item.dataIndex]); + const valueA = Number( + a[`${item.dataIndex}_without_unit`] ?? a[item.dataIndex], + ); + const valueB = Number( + b[`${item.dataIndex}_without_unit`] ?? b[item.dataIndex], + ); if (!isNaN(valueA) && !isNaN(valueB)) { return valueA - valueB; From c104b758bab5f0e2d15581aec22bbfbec2f6d464 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 17 Jun 2024 22:59:28 +0530 Subject: [PATCH 004/281] chore: adjust the step interval for builder queries (#5253) --- .../migrate/0_47_alerts_custom_step/run.go | 70 +++++++++++++++++++ pkg/query-service/migrate/migate.go | 12 ++++ 2 files changed, 82 insertions(+) create mode 100644 pkg/query-service/migrate/0_47_alerts_custom_step/run.go diff --git a/pkg/query-service/migrate/0_47_alerts_custom_step/run.go b/pkg/query-service/migrate/0_47_alerts_custom_step/run.go new file mode 100644 index 0000000000..b25705aaf2 --- /dev/null +++ b/pkg/query-service/migrate/0_47_alerts_custom_step/run.go @@ -0,0 +1,70 @@ +package alertscustomstep + +import ( + "context" + "encoding/json" + "time" + + "github.com/jmoiron/sqlx" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "go.signoz.io/signoz/pkg/query-service/rules" + "go.uber.org/multierr" + "go.uber.org/zap" +) + +var Version = "0.47-alerts-custom-step" + +func Migrate(conn *sqlx.DB) error { + ruleDB := rules.NewRuleDB(conn) + storedRules, err := ruleDB.GetStoredRules(context.Background()) + if err != nil { + return err + } + + for _, storedRule := range storedRules { + parsedRule, errs := rules.ParsePostableRule([]byte(storedRule.Data)) + if len(errs) > 0 { + // this should not happen but if it does, we should not stop the migration + zap.L().Error("Error parsing rule", zap.Error(multierr.Combine(errs...)), zap.Int("rule", storedRule.Id)) + continue + } + zap.L().Info("Rule parsed", zap.Int("rule", storedRule.Id)) + updated := false + if parsedRule.RuleCondition != nil { + if parsedRule.RuleCondition.QueryType() == v3.QueryTypeBuilder { + if parsedRule.EvalWindow <= rules.Duration(6*time.Hour) { + for _, query := range parsedRule.RuleCondition.CompositeQuery.BuilderQueries { + if query.StepInterval > 60 { + updated = true + zap.L().Info("Updating step interval", zap.Int("rule", storedRule.Id), zap.Int64("old", query.StepInterval), zap.Int64("new", 60)) + query.StepInterval = 60 + } + } + } + } + } + + if !updated { + zap.L().Info("Rule not updated", zap.Int("rule", storedRule.Id)) + continue + } + + ruleJSON, jsonErr := json.Marshal(parsedRule) + if jsonErr != nil { + zap.L().Error("Error marshalling rule; skipping rule migration", zap.Error(jsonErr), zap.Int("rule", storedRule.Id)) + continue + } + + stmt, prepareError := conn.PrepareContext(context.Background(), `UPDATE rules SET data=$3 WHERE id=$4;`) + if prepareError != nil { + zap.L().Error("Error in preparing statement for UPDATE to rules", zap.Error(prepareError)) + continue + } + defer stmt.Close() + + if _, err := stmt.Exec(ruleJSON, storedRule.Id); err != nil { + zap.L().Error("Error in Executing prepared statement for UPDATE to rules", zap.Error(err)) + } + } + return nil +} diff --git a/pkg/query-service/migrate/migate.go b/pkg/query-service/migrate/migate.go index f9d15a1567..4fe61764bc 100644 --- a/pkg/query-service/migrate/migate.go +++ b/pkg/query-service/migrate/migate.go @@ -5,6 +5,7 @@ import ( "github.com/jmoiron/sqlx" alertstov4 "go.signoz.io/signoz/pkg/query-service/migrate/0_45_alerts_to_v4" + alertscustomstep "go.signoz.io/signoz/pkg/query-service/migrate/0_47_alerts_custom_step" "go.uber.org/zap" ) @@ -63,5 +64,16 @@ func Migrate(dsn string) error { } } + if m, err := getMigrationVersion(conn, "0.47_alerts_custom_step"); err == nil && m == nil { + if err := alertscustomstep.Migrate(conn); err != nil { + zap.L().Error("failed to migrate 0.47_alerts_custom_step", zap.Error(err)) + } else { + _, err := conn.Exec("INSERT INTO data_migrations (version, succeeded) VALUES ('0.47_alerts_custom_step', true)") + if err != nil { + return err + } + } + } + return nil } From 3b4b9e43b383536e94e89901dc269f9e855eeee2 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 18 Jun 2024 11:58:35 +0530 Subject: [PATCH 005/281] fix: trace explorer not highlighting in sidenav (#5263) --- frontend/src/container/SideNav/menuItems.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/SideNav/menuItems.tsx b/frontend/src/container/SideNav/menuItems.tsx index 6c9a87db02..31bd96333e 100644 --- a/frontend/src/container/SideNav/menuItems.tsx +++ b/frontend/src/container/SideNav/menuItems.tsx @@ -125,8 +125,8 @@ const menuItems: SidebarItem[] = [ /** Mapping of some newly added routes and their corresponding active sidebar menu key */ export const NEW_ROUTES_MENU_ITEM_KEY_MAP: Record = { - [ROUTES.TRACES_EXPLORER]: ROUTES.TRACE, - [ROUTES.TRACE_EXPLORER]: ROUTES.TRACE, + [ROUTES.TRACE]: ROUTES.TRACES_EXPLORER, + [ROUTES.TRACE_EXPLORER]: ROUTES.TRACES_EXPLORER, [ROUTES.LOGS_BASE]: ROUTES.LOGS_EXPLORER, }; From 0fade428ef6efd8d791eac5c15bee38eea0434c3 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 18 Jun 2024 12:25:10 +0530 Subject: [PATCH 006/281] fix: table row data doesn't align with the response (#5248) * fix: wrong values getting associated with the table rows * fix: table columns rendering * fix: remove console logs --- .../lib/query/createTableColumnsFromQuery.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/query/createTableColumnsFromQuery.ts b/frontend/src/lib/query/createTableColumnsFromQuery.ts index c3a5865cd9..18926f2f0c 100644 --- a/frontend/src/lib/query/createTableColumnsFromQuery.ts +++ b/frontend/src/lib/query/createTableColumnsFromQuery.ts @@ -365,6 +365,7 @@ const fillRestAggregationData = ( queryTableData: QueryDataV3[], seria: SeriesItem, equalQueriesByLabels: string[], + isEqualQuery: boolean, ): void => { const nextQueryData = queryTableData.find((q) => q.queryName === column.field) || null; @@ -374,13 +375,13 @@ const fillRestAggregationData = ( nextQueryData, ); + const isEqual = isEqualQueriesByLabel(equalQueriesByLabels, column.field); if (targetSeria) { - const isEqual = isEqualQueriesByLabel(equalQueriesByLabels, column.field); if (!isEqual) { // This line is crucial. It ensures that no additional rows are added to the table for similar labels across all formulas here is how this check is applied: signoz/frontend/src/lib/query/createTableColumnsFromQuery.ts line number 370 equalQueriesByLabels.push(column.field); } - } else { + } else if (!isEqualQuery) { column.data.push('N/A'); } }; @@ -435,6 +436,7 @@ const fillDataFromSeries = ( queryTableData, seria, equalQueriesByLabels, + isEqualQuery, ); return; @@ -570,6 +572,29 @@ export const createTableColumnsFromQuery: CreateTableDataFromQuery = ({ a.queryName < b.queryName ? -1 : 1, ); + // the reason we need this is because the filling of values in rows doesn't account for mismatch enteries + // in the response. Example : Series A -> [label1, label2] and Series B -> [label2,label1] this isn't accounted for + sortedQueryTableData.forEach((q) => { + q.series?.forEach((s) => { + s.labelsArray?.sort((a, b) => + Object.keys(a)[0] < Object.keys(b)[0] ? -1 : 1, + ); + }); + q.series?.sort((a, b) => { + let labelA = ''; + let labelB = ''; + a.labelsArray.forEach((lab) => { + labelA += Object.values(lab)[0]; + }); + + b.labelsArray.forEach((lab) => { + labelB += Object.values(lab)[0]; + }); + + return labelA < labelB ? -1 : 1; + }); + }); + const dynamicColumns = getDynamicColumns(sortedQueryTableData, query); const { filledDynamicColumns, rowsLength } = fillColumnsData( From a65d5095a0dc1aadbf6b66bae665d25ebddc8bb2 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:02:15 +0530 Subject: [PATCH 007/281] feat: added checkbox selection in dashboard variables (#5191) * feat: added checkbox selection in dashboard variables * feat: added checkbox selection - handling with only and all * feat: added checkbox selection - style changes * fix: fixed deselecting all options * feat: fixed all showing up in single select * feat: improve styles * feat: fixed single select getting all values and array issues * feat: updated test case * feat: added max tag shown logic with count length and info on hover for overflowed content --- .../DashboardVariableSelection.styles.scss | 38 ++- .../VariableItem.test.tsx | 2 + .../VariableItem.tsx | 222 ++++++++++++++++-- .../DashboardVariablesSelection/styles.ts | 1 + 4 files changed, 246 insertions(+), 17 deletions(-) diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss b/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss index f610198f4a..f7fcb83a53 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.styles.scss @@ -40,12 +40,46 @@ } .variable-select { - .ant-select-dropdown { - max-width: 300px; + .ant-select-item { + display: flex; + align-items: center; + } + + .all-label { + display: flex; + gap: 16px; + } + + .dropdown-checkbox-label { + display: grid; + grid-template-columns: 24px 1fr; + } + + .dropdown-value { + display: flex; + justify-content: space-between; + align-items: center; + + .option-text { + max-width: 180px; + padding: 0 8px; + } + + .toggle-tag-label { + padding-left: 8px; + right: 40px; + font-weight: normal; + position: absolute; + } } } } +.dropdown-styles { + min-width: 300px; + max-width: 350px; +} + .lightMode { .variable-item { .variable-name { diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx index cd8b23ea46..14f20347d0 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx @@ -123,6 +123,8 @@ describe('VariableItem', () => { const customVariableData = { ...mockCustomVariableData, allSelected: true, + showALLOption: true, + multiSelect: true, }; render( diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx index 2a14aa19e5..e0393ea163 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx @@ -1,15 +1,29 @@ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable no-nested-ternary */ import './DashboardVariableSelection.styles.scss'; import { orange } from '@ant-design/colors'; import { WarningOutlined } from '@ant-design/icons'; -import { Input, Popover, Select, Typography } from 'antd'; +import { + Checkbox, + Input, + Popover, + Select, + Tag, + Tooltip, + Typography, +} from 'antd'; +import { CheckboxChangeEvent } from 'antd/es/checkbox'; import dashboardVariablesQuery from 'api/dashboard/variables/dashboardVariablesQuery'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import { commaValuesParser } from 'lib/dashbaordVariables/customCommaValuesParser'; import sortValues from 'lib/dashbaordVariables/sortVariableValues'; import { debounce, isArray, isString } from 'lodash-es'; import map from 'lodash-es/map'; -import { memo, useEffect, useMemo, useState } from 'react'; +import { ChangeEvent, memo, useEffect, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; import { IDashboardVariable } from 'types/api/dashboard/getAll'; import { VariableResponseProps } from 'types/api/dashboard/variables/query'; @@ -23,6 +37,11 @@ const ALL_SELECT_VALUE = '__ALL__'; const variableRegexPattern = /\{\{\s*?\.([^\s}]+)\s*?\}\}/g; +enum ToggleTagValue { + Only = 'Only', + All = 'All', +} + interface VariableItemProps { variableData: IDashboardVariable; existingVariables: Record; @@ -37,8 +56,12 @@ interface VariableItemProps { const getSelectValue = ( selectedValue: IDashboardVariable['selectedValue'], + variableData: IDashboardVariable, ): string | string[] => { if (Array.isArray(selectedValue)) { + if (!variableData.multiSelect && selectedValue.length === 1) { + return selectedValue[0]?.toString() || ''; + } return selectedValue.map((item) => item.toString()); } return selectedValue?.toString() || ''; @@ -193,7 +216,7 @@ function VariableItem({ }); const handleChange = (value: string | string[]): void => { - if (variableData.name) + if (variableData.name) { if ( value === ALL_SELECT_VALUE || (Array.isArray(value) && value.includes(ALL_SELECT_VALUE)) || @@ -203,25 +226,29 @@ function VariableItem({ } else { onValueUpdate(variableData.name, variableData.id, value, false); } + } }; // do not debounce the above function as we do not need debounce in select variables const debouncedHandleChange = debounce(handleChange, 500); const { selectedValue } = variableData; - const selectedValueStringified = useMemo(() => getSelectValue(selectedValue), [ - selectedValue, - ]); + const selectedValueStringified = useMemo( + () => getSelectValue(selectedValue, variableData), + [selectedValue, variableData], + ); - const selectValue = variableData.allSelected - ? 'ALL' - : selectedValueStringified; + const enableSelectAll = variableData.multiSelect && variableData.showALLOption; + + const selectValue = + variableData.allSelected && enableSelectAll + ? 'ALL' + : selectedValueStringified; - const mode = + const mode: 'multiple' | undefined = variableData.multiSelect && !variableData.allSelected ? 'multiple' : undefined; - const enableSelectAll = variableData.multiSelect && variableData.showALLOption; useEffect(() => { // Fetch options for CUSTOM Type @@ -231,6 +258,117 @@ function VariableItem({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [variableData.type, variableData.customValue]); + const checkAll = (e: MouseEvent): void => { + e.stopPropagation(); + e.preventDefault(); + const isChecked = + variableData.allSelected || selectValue.includes(ALL_SELECT_VALUE); + + if (isChecked) { + handleChange([]); + } else { + handleChange(ALL_SELECT_VALUE); + } + }; + + const handleOptionSelect = ( + e: CheckboxChangeEvent, + option: string | number | boolean, + ): void => { + const newSelectedValue = Array.isArray(selectedValue) + ? ((selectedValue.filter( + (val) => val.toString() !== option.toString(), + ) as unknown) as string[]) + : []; + + if ( + !e.target.checked && + Array.isArray(selectedValueStringified) && + selectedValueStringified.includes(option.toString()) + ) { + if (newSelectedValue.length === 0) { + handleChange(ALL_SELECT_VALUE); + return; + } + if (newSelectedValue.length === 1) { + handleChange(newSelectedValue[0].toString()); + return; + } + handleChange(newSelectedValue); + } else if (!e.target.checked && selectedValue === option.toString()) { + handleChange(ALL_SELECT_VALUE); + } else if (newSelectedValue.length === optionsData.length - 1) { + handleChange(ALL_SELECT_VALUE); + } + }; + + const [optionState, setOptionState] = useState({ + tag: '', + visible: false, + }); + + function currentToggleTagValue({ + option, + }: { + option: string; + }): ToggleTagValue { + if ( + option.toString() === selectValue || + (Array.isArray(selectValue) && + selectValue?.includes(option.toString()) && + selectValue.length === 1) + ) { + return ToggleTagValue.All; + } + return ToggleTagValue.Only; + } + + function handleToggle(e: ChangeEvent, option: string): void { + e.stopPropagation(); + const mode = currentToggleTagValue({ option: option as string }); + const isChecked = + variableData.allSelected || + option.toString() === selectValue || + (Array.isArray(selectValue) && selectValue?.includes(option.toString())); + + if (isChecked) { + if (mode === ToggleTagValue.Only) { + handleChange(option.toString()); + } else if (!variableData.multiSelect) { + handleChange(option.toString()); + } else { + handleChange(ALL_SELECT_VALUE); + } + } else { + handleChange(option.toString()); + } + } + + function retProps( + option: string, + ): { + onMouseOver: () => void; + onMouseOut: () => void; + } { + return { + onMouseOver: (): void => + setOptionState({ + tag: option.toString(), + visible: true, + }), + onMouseOut: (): void => + setOptionState({ + tag: option.toString(), + visible: false, + }), + }; + } + + const ensureValidOption = (option: string): boolean => + !( + currentToggleTagValue({ option }) === ToggleTagValue.All && !enableSelectAll + ); + return (
@@ -264,19 +402,35 @@ function VariableItem({ onChange={handleChange} bordered={false} placeholder="Select value" - placement="bottomRight" + placement="bottomLeft" mode={mode} - dropdownMatchSelectWidth={false} style={SelectItemStyle} loading={isLoading} showSearch data-testid="variable-select" className="variable-select" + popupClassName="dropdown-styles" + maxTagCount={4} getPopupContainer={popupContainer} + // eslint-disable-next-line react/no-unstable-nested-components + tagRender={(props): JSX.Element => ( + + {props.value} + + )} + // eslint-disable-next-line react/no-unstable-nested-components + maxTagPlaceholder={(omittedValues): JSX.Element => ( + value).join(', ')}> + + {omittedValues.length} + + )} > {enableSelectAll && ( - ALL +
checkAll(e as any)}> + + ALL +
)} {map(optionsData, (option) => ( @@ -285,7 +439,45 @@ function VariableItem({ key={option.toString()} value={option} > - {option.toString()} +
+ {variableData.multiSelect && ( + { + e.stopPropagation(); + e.preventDefault(); + handleOptionSelect(e, option); + }} + checked={ + variableData.allSelected || + option.toString() === selectValue || + (Array.isArray(selectValue) && + selectValue?.includes(option.toString())) + } + /> + )} +
handleToggle(e as any, option as string)} + > + + + {option.toString()} + + + + {variableData.multiSelect && + optionState.tag === option.toString() && + optionState.visible && + ensureValidOption(option as string) && ( + + {currentToggleTagValue({ option: option as string })} + + )} +
+
))} diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/styles.ts b/frontend/src/container/NewDashboard/DashboardVariablesSelection/styles.ts index 5c5de3e97e..02530dbd0f 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/styles.ts +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/styles.ts @@ -42,4 +42,5 @@ export const VariableValue = styled(Typography)` export const SelectItemStyle = { minWidth: 120, fontSize: '0.8rem', + width: '100%', }; From 1695b4f06dbb456645ef686bdad27348003080c7 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:24:54 +0530 Subject: [PATCH 008/281] fix: convert timestamp in new trace explorer to user browser timezone and readable format (#5235) * fix: convert timestamp in new trace explorer to user browser timezone and readable format * fix: code refactor --- frontend/src/container/TracesExplorer/ListView/utils.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/container/TracesExplorer/ListView/utils.tsx b/frontend/src/container/TracesExplorer/ListView/utils.tsx index 732c8eacdf..254ff93296 100644 --- a/frontend/src/container/TracesExplorer/ListView/utils.tsx +++ b/frontend/src/container/TracesExplorer/ListView/utils.tsx @@ -3,6 +3,7 @@ import { ColumnsType } from 'antd/es/table'; import ROUTES from 'constants/routes'; import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util'; import { formUrlParams } from 'container/TraceDetail/utils'; +import dayjs from 'dayjs'; import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { ILog } from 'types/api/logs/log'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; @@ -30,6 +31,13 @@ export const getListColumns = ( key: 'date', title: 'Timestamp', width: 145, + render: (item): JSX.Element => { + const date = + typeof item === 'string' + ? dayjs(item).format('YYYY-MM-DD HH:mm:ss.SSS') + : dayjs(item / 1e6).format('YYYY-MM-DD HH:mm:ss.SSS'); + return {date}; + }, }, ]; From cf7bf32ac275da5a34e2589a758dc9aa01a04d92 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:38:30 +0530 Subject: [PATCH 009/281] fix: fixed lightMode style for histogram panel (#5236) --- .../RightContainer/RightContainer.styles.scss | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frontend/src/container/NewWidget/RightContainer/RightContainer.styles.scss b/frontend/src/container/NewWidget/RightContainer/RightContainer.styles.scss index 3ec5338e45..55d717926a 100644 --- a/frontend/src/container/NewWidget/RightContainer/RightContainer.styles.scss +++ b/frontend/src/container/NewWidget/RightContainer/RightContainer.styles.scss @@ -429,6 +429,21 @@ } } + .bucket-config { + .label { + color: var(--bg-ink-400); + } + + .bucket-input { + border: 1px solid var(--bg-vanilla-300); + background: var(--bg-vanilla-300); + + .ant-input { + background: var(--bg-vanilla-300); + } + } + } + .panel-time-text { color: var(--bg-ink-400); } From 45d6430ab3ff204a264b6b85deb475591283c902 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:48:28 +0530 Subject: [PATCH 010/281] fix: fixed panelType when creating alerts from histogram dashboard (#5251) * fix: fixed panelType when creating alerts from histogram dashboard * fix: added PANEL_TYPES.TIME_SERIES always for all type in case of alerts --- frontend/src/container/FormAlertRules/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/FormAlertRules/index.tsx b/frontend/src/container/FormAlertRules/index.tsx index 3e2325f5be..09fc618759 100644 --- a/frontend/src/container/FormAlertRules/index.tsx +++ b/frontend/src/container/FormAlertRules/index.tsx @@ -77,7 +77,8 @@ function FormAlertRules({ const urlQuery = useUrlQuery(); - const panelType = urlQuery.get(QueryParams.panelTypes) as PANEL_TYPES | null; + // In case of alert the panel types should always be "Graph" only + const panelType = PANEL_TYPES.TIME_SERIES; const { currentQuery, From 9a6db272c19b647091dc54e76f4bdecc6147ea88 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 18 Jun 2024 19:04:06 +0530 Subject: [PATCH 011/281] fix: update the error boundary components with sentry error boundary components (#5271) * fix: update the error boundary components with sentry error boundary components * fix: update fallback components --- frontend/src/components/Uplot/Uplot.tsx | 6 +++--- frontend/src/container/AppLayout/index.tsx | 6 +++--- frontend/src/container/ServiceApplication/index.tsx | 6 +++--- frontend/src/index.tsx | 5 ++--- frontend/src/pages/LogsExplorer/index.tsx | 6 +++--- frontend/src/pages/Pipelines/index.tsx | 6 +++--- frontend/src/pages/Trace/index.tsx | 6 +++--- frontend/src/pages/TracesExplorer/index.tsx | 6 +++--- 8 files changed, 23 insertions(+), 24 deletions(-) diff --git a/frontend/src/components/Uplot/Uplot.tsx b/frontend/src/components/Uplot/Uplot.tsx index 05f050a87c..af6e28ddf3 100644 --- a/frontend/src/components/Uplot/Uplot.tsx +++ b/frontend/src/components/Uplot/Uplot.tsx @@ -1,6 +1,7 @@ /* eslint-disable sonarjs/cognitive-complexity */ import './Uplot.styles.scss'; +import * as Sentry from '@sentry/react'; import { Typography } from 'antd'; import { ToggleGraphProps } from 'components/Graph/types'; import { LineChart } from 'lucide-react'; @@ -13,7 +14,6 @@ import { useImperativeHandle, useRef, } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import UPlot from 'uplot'; import { dataMatch, optionsUpdateState } from './utils'; @@ -139,7 +139,7 @@ const Uplot = forwardRef( } return ( - + }>
{data && data[0] && data[0]?.length === 0 ? (
@@ -147,7 +147,7 @@ const Uplot = forwardRef(
) : null}
-
+ ); }, ); diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index f3beabb2bc..ef7be7eef4 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -3,6 +3,7 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ import './AppLayout.styles.scss'; +import * as Sentry from '@sentry/react'; import { Flex } from 'antd'; import getLocalStorageKey from 'api/browser/localstorage/get'; import getDynamicConfigs from 'api/dynamicConfigs/getDynamicConfigs'; @@ -27,7 +28,6 @@ import { useRef, useState, } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import { Helmet } from 'react-helmet-async'; import { useTranslation } from 'react-i18next'; import { useQueries } from 'react-query'; @@ -342,7 +342,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element { /> )}
- + }> - +
diff --git a/frontend/src/container/ServiceApplication/index.tsx b/frontend/src/container/ServiceApplication/index.tsx index b7b22018e1..0afdc6950d 100644 --- a/frontend/src/container/ServiceApplication/index.tsx +++ b/frontend/src/container/ServiceApplication/index.tsx @@ -1,7 +1,7 @@ +import * as Sentry from '@sentry/react'; import { FeatureKeys } from 'constants/features'; import useFeatureFlag from 'hooks/useFeatureFlag'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; -import { ErrorBoundary } from 'react-error-boundary'; import ServiceMetrics from './ServiceMetrics'; import ServiceTraces from './ServiceTraces'; @@ -12,11 +12,11 @@ function Services(): JSX.Element { ?.active; return ( - + }> {isSpanMetricEnabled ? : } - + ); } diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 570db8c1da..45a1b6f11a 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -7,7 +7,6 @@ import { AxiosError } from 'axios'; import { ThemeProvider } from 'hooks/useDarkMode'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { createRoot } from 'react-dom/client'; -import { ErrorBoundary } from 'react-error-boundary'; import { HelmetProvider } from 'react-helmet-async'; import { QueryClient, QueryClientProvider } from 'react-query'; import { Provider } from 'react-redux'; @@ -58,7 +57,7 @@ if (container) { const root = createRoot(container); root.render( - + }> @@ -68,6 +67,6 @@ if (container) { - , + , ); } diff --git a/frontend/src/pages/LogsExplorer/index.tsx b/frontend/src/pages/LogsExplorer/index.tsx index 0cc2c07b4d..d8f5f38804 100644 --- a/frontend/src/pages/LogsExplorer/index.tsx +++ b/frontend/src/pages/LogsExplorer/index.tsx @@ -1,5 +1,6 @@ import './LogsExplorer.styles.scss'; +import * as Sentry from '@sentry/react'; import ExplorerCard from 'components/ExplorerCard/ExplorerCard'; import LogExplorerQuerySection from 'container/LogExplorerQuerySection'; import LogsExplorerViews from 'container/LogsExplorerViews'; @@ -9,7 +10,6 @@ import Toolbar from 'container/Toolbar/Toolbar'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useEffect, useMemo, useState } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import { DataSource } from 'types/common/queryBuilder'; import { WrapperStyled } from './styles'; @@ -70,7 +70,7 @@ function LogsExplorer(): JSX.Element { ); return ( - + }>
- + ); } diff --git a/frontend/src/pages/Pipelines/index.tsx b/frontend/src/pages/Pipelines/index.tsx index 81b3b503f9..a1ae05c30c 100644 --- a/frontend/src/pages/Pipelines/index.tsx +++ b/frontend/src/pages/Pipelines/index.tsx @@ -1,5 +1,6 @@ import './Pipelines.styles.scss'; +import * as Sentry from '@sentry/react'; import type { TabsProps } from 'antd'; import { Tabs } from 'antd'; import getPipeline from 'api/pipeline/get'; @@ -9,7 +10,6 @@ import PipelinePage from 'container/PipelinePage/Layouts/Pipeline'; import { useNotifications } from 'hooks/useNotifications'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useEffect, useMemo } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { SuccessResponse } from 'types/api'; @@ -82,13 +82,13 @@ function Pipelines(): JSX.Element { } return ( - + }> - + ); } diff --git a/frontend/src/pages/Trace/index.tsx b/frontend/src/pages/Trace/index.tsx index 75bf58f1bd..3d31271c18 100644 --- a/frontend/src/pages/Trace/index.tsx +++ b/frontend/src/pages/Trace/index.tsx @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/react'; import { Card } from 'antd'; import { NotificationInstance } from 'antd/es/notification/interface'; import ROUTES from 'constants/routes'; @@ -11,7 +12,6 @@ import getStep from 'lib/getStep'; import history from 'lib/history'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { MouseEventHandler, useCallback, useEffect, useState } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import { connect, useDispatch, useSelector } from 'react-redux'; import { bindActionCreators, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; @@ -146,7 +146,7 @@ function Trace({ ); return ( - + }>
@@ -169,7 +169,7 @@ function Trace({ - + ); } diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index 6c4057332f..30dc747345 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -1,6 +1,7 @@ import './TracesExplorer.styles.scss'; import { FilterOutlined } from '@ant-design/icons'; +import * as Sentry from '@sentry/react'; import { Button, Card, Tabs, Tooltip } from 'antd'; import axios from 'axios'; import ExplorerCard from 'components/ExplorerCard/ExplorerCard'; @@ -21,7 +22,6 @@ import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; import { Dashboard } from 'types/api/dashboard/getAll'; import { DataSource } from 'types/common/queryBuilder'; import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink'; @@ -185,7 +185,7 @@ function TracesExplorer(): JSX.Element { const [isOpen, setOpen] = useState(true); return ( - + }>
-
+ ); } From b69545a771d1f1faf1e4e45a6de223452e879923 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Wed, 19 Jun 2024 10:49:57 +0530 Subject: [PATCH 012/281] fix: update companyDomain in before firing every event (#5275) --- pkg/query-service/telemetry/telemetry.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index b160bbfe66..22be2a8648 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -460,6 +460,7 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma if userEmail != "" { a.SetUserEmail(userEmail) + a.SetCompanyDomain(userEmail) } if !a.isTelemetryEnabled() { From c0195e9dc90f896fa33bd970de18a369ef6b6700 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Wed, 19 Jun 2024 11:50:18 +0530 Subject: [PATCH 013/281] fix: added null checks for stacked bar chart with fallbacks (#5282) --- frontend/src/lib/uPlotLib/getUplotChartOptions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/uPlotLib/getUplotChartOptions.ts b/frontend/src/lib/uPlotLib/getUplotChartOptions.ts index daf21e1429..dd08cbfff1 100644 --- a/frontend/src/lib/uPlotLib/getUplotChartOptions.ts +++ b/frontend/src/lib/uPlotLib/getUplotChartOptions.ts @@ -68,7 +68,8 @@ function getStackedSeries(apiResponse: QueryData[]): QueryData[] { const { values } = series[i]; for (let j = 0; j < values.length; j++) { values[j][1] = String( - parseFloat(values[j]?.[1]) + parseFloat(series[i + 1].values[j]?.[1]), + parseFloat(values[j]?.[1] || '0') + + parseFloat(series[i + 1].values[j]?.[1] || '0'), ); } @@ -88,7 +89,8 @@ function getStackedSeriesQueryFormat(apiResponse: QueryData[]): QueryData[] { const { values } = series[i]; for (let j = 0; j < values.length; j++) { values[j].value = String( - parseFloat(values[j].value) + parseFloat(series[i + 1].values[j].value), + parseFloat(values[j]?.value || '0') + + parseFloat(series[i + 1].values[j]?.value || '0'), ); } From b69e97d7b0780e00956320ceb3aa22e0d8bdd6f8 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Wed, 19 Jun 2024 12:10:43 +0530 Subject: [PATCH 014/281] fix: fixed flakiness in alert list actions - delete, edit, clone & toggle (#5237) * fix: fixed flakiness in alert list actions - delete, edit, clone & toggle * fix: added onhover dropdown open and close --- frontend/src/components/DropDown/DropDown.tsx | 17 ++++- .../container/ListAlertRules/ListAlert.tsx | 74 ++++++++++++------- .../src/container/ListAlertRules/styles.ts | 3 + 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/frontend/src/components/DropDown/DropDown.tsx b/frontend/src/components/DropDown/DropDown.tsx index c29fbdd15b..df845b7084 100644 --- a/frontend/src/components/DropDown/DropDown.tsx +++ b/frontend/src/components/DropDown/DropDown.tsx @@ -3,6 +3,7 @@ import './DropDown.styles.scss'; import { EllipsisOutlined } from '@ant-design/icons'; import { Button, Dropdown, MenuProps } from 'antd'; import { useIsDarkMode } from 'hooks/useDarkMode'; +import { useState } from 'react'; function DropDown({ element }: { element: JSX.Element[] }): JSX.Element { const isDarkMode = useIsDarkMode(); @@ -14,12 +15,24 @@ function DropDown({ element }: { element: JSX.Element[] }): JSX.Element { }), ); + const [isDdOpen, setDdOpen] = useState(false); + return ( - + setDdOpen(true), + onMouseLeave: (): void => setDdOpen(false), + }} + open={isDdOpen} + > diff --git a/frontend/src/container/ListAlertRules/ListAlert.tsx b/frontend/src/container/ListAlertRules/ListAlert.tsx index 37a56ba921..91e0786e05 100644 --- a/frontend/src/container/ListAlertRules/ListAlert.tsx +++ b/frontend/src/container/ListAlertRules/ListAlert.tsx @@ -55,6 +55,9 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { role, ); + const [editLoader, setEditLoader] = useState(false); + const [cloneLoader, setCloneLoader] = useState(false); + const params = useUrlQuery(); const orderColumnParam = params.get('columnKey'); const orderQueryParam = params.get('order'); @@ -113,6 +116,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { }, [featureResponse, handleError]); const onEditHandler = (record: GettableAlert) => (): void => { + setEditLoader(true); featureResponse .refetch() .then(() => { @@ -129,9 +133,11 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { params.set(QueryParams.ruleId, record.id.toString()); + setEditLoader(false); history.push(`${ROUTES.EDIT_ALERTS}?${params.toString()}`); }) - .catch(handleError); + .catch(handleError) + .finally(() => setEditLoader(false)); }; const onCloneHandler = ( @@ -143,33 +149,41 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { }; const apiReq = { data: copyAlert }; - const response = await saveAlertApi(apiReq); + try { + setCloneLoader(true); + const response = await saveAlertApi(apiReq); - if (response.statusCode === 200) { - notificationsApi.success({ - message: 'Success', - description: 'Alert cloned successfully', - }); + if (response.statusCode === 200) { + notificationsApi.success({ + message: 'Success', + description: 'Alert cloned successfully', + }); - const { data: refetchData, status } = await refetch(); - if (status === 'success' && refetchData.payload) { - setData(refetchData.payload || []); - setTimeout(() => { - const clonedAlert = refetchData.payload[refetchData.payload.length - 1]; - params.set(QueryParams.ruleId, String(clonedAlert.id)); - history.push(`${ROUTES.EDIT_ALERTS}?${params.toString()}`); - }, 2000); - } - if (status === 'error') { + const { data: refetchData, status } = await refetch(); + if (status === 'success' && refetchData.payload) { + setData(refetchData.payload || []); + setTimeout(() => { + const clonedAlert = refetchData.payload[refetchData.payload.length - 1]; + params.set(QueryParams.ruleId, String(clonedAlert.id)); + history.push(`${ROUTES.EDIT_ALERTS}?${params.toString()}`); + }, 2000); + } + if (status === 'error') { + notificationsApi.error({ + message: t('something_went_wrong'), + }); + } + } else { notificationsApi.error({ - message: t('something_went_wrong'), + message: 'Error', + description: response.error || t('something_went_wrong'), }); } - } else { - notificationsApi.error({ - message: 'Error', - description: response.error || t('something_went_wrong'), - }); + } catch (error) { + handleError(); + console.error(error); + } finally { + setCloneLoader(false); } }; @@ -314,10 +328,20 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { setData={setData} id={id} />, - + Edit , - + Clone , Date: Wed, 19 Jun 2024 14:19:30 +0530 Subject: [PATCH 015/281] chore: threshold rule panel type to graph (#5284) --- pkg/query-service/rules/thresholdRule.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/query-service/rules/thresholdRule.go b/pkg/query-service/rules/thresholdRule.go index 2af4ce647f..051476e6b8 100644 --- a/pkg/query-service/rules/thresholdRule.go +++ b/pkg/query-service/rules/thresholdRule.go @@ -484,6 +484,10 @@ func (r *ThresholdRule) prepareQueryRange(ts time.Time) *v3.QueryRangeParamsV3 { } } + if r.ruleCondition.CompositeQuery.PanelType != v3.PanelTypeGraph { + r.ruleCondition.CompositeQuery.PanelType = v3.PanelTypeGraph + } + // default mode return &v3.QueryRangeParamsV3{ Start: start, From a4e98e565d3dd654432b03d3cc8e22bdf3bed2ab Mon Sep 17 00:00:00 2001 From: Nityananda Gohain Date: Wed, 19 Jun 2024 15:40:34 +0530 Subject: [PATCH 016/281] feat: sanitize query and remove groupBy for list panel query (#5285) --- pkg/query-service/app/parser.go | 3 +++ pkg/query-service/model/v3/v3.go | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index d976fd17b2..47da531d0b 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -958,6 +958,9 @@ func ParseQueryRangeParams(r *http.Request) (*v3.QueryRangeParamsV3, *model.ApiE return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("cannot parse the request body: %v", err)} } + // sanitize the request body + queryRangeParams.CompositeQuery.Sanitize() + // validate the request body if err := validateQueryRangeParamsV3(queryRangeParams); err != nil { return nil, &model.ApiError{Typ: model.ErrorBadData, Err: err} diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index 61272ddb2d..2a12c8e1fa 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -428,6 +428,16 @@ func (c *CompositeQuery) EnabledQueries() int { return count } +func (c *CompositeQuery) Sanitize() { + // remove groupBy for queries with list panel type + for _, query := range c.BuilderQueries { + if len(query.GroupBy) > 0 && c.PanelType == PanelTypeList { + query.GroupBy = []AttributeKey{} + } + } + +} + func (c *CompositeQuery) Validate() error { if c == nil { return fmt.Errorf("composite query is required") @@ -747,9 +757,9 @@ func (b *BuilderQuery) Validate(panelType PanelType) error { } } if b.GroupBy != nil { - if len(b.GroupBy) > 0 && panelType == PanelTypeList { - return fmt.Errorf("group by is not supported for list panel type") - } + // if len(b.GroupBy) > 0 && panelType == PanelTypeList { + // return fmt.Errorf("group by is not supported for list panel type") + // } for _, groupBy := range b.GroupBy { if err := groupBy.Validate(); err != nil { From f3c2fb0246a024f15efdc72754b649410aa2ac61 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Wed, 19 Jun 2024 20:31:36 +0530 Subject: [PATCH 017/281] fix: dashboard empty state learn more link not working (#5287) --- .../src/container/ListOfDashboard/DashboardsList.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/ListOfDashboard/DashboardsList.tsx b/frontend/src/container/ListOfDashboard/DashboardsList.tsx index 52a525d611..1c8e96b6f7 100644 --- a/frontend/src/container/ListOfDashboard/DashboardsList.tsx +++ b/frontend/src/container/ListOfDashboard/DashboardsList.tsx @@ -699,7 +699,16 @@ function DashboardsList(): JSX.Element { New Dashboard - From 537641000d0db7ea047cc40069458cdd09bea316 Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Wed, 19 Jun 2024 21:14:45 +0530 Subject: [PATCH 018/281] =?UTF-8?q?chore(signoz):=20=F0=9F=93=8C=20pin=20v?= =?UTF-8?q?ersions:=20SigNoz=200.48.0,=20SigNoz=20OtelCollector=200.102.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Prashant Shahi --- .../clickhouse-setup/docker-compose.yaml | 8 +- .../clickhouse-setup/docker-compose-core.yaml | 4 +- .../docker-compose.testing.yaml | 8 +- .../clickhouse-setup/docker-compose.yaml | 8 +- go.mod | 167 ++--- go.sum | 704 ++++++------------ .../tests/test-deploy/docker-compose.yaml | 4 +- 7 files changed, 334 insertions(+), 569 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 755d61c919..1f5f8e4229 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.47.0 + image: signoz/query-service:0.48.0 command: [ "-config=/root/config/prometheus.yml", @@ -186,7 +186,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:0.47.0 + image: signoz/frontend:0.48.0 deploy: restart_policy: condition: on-failure @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.88.26 + image: signoz/signoz-otel-collector:0.102.0 command: [ "--config=/etc/otel-collector-config.yaml", @@ -237,7 +237,7 @@ services: - query-service otel-collector-migrator: - image: signoz/signoz-schema-migrator:0.88.26 + image: signoz/signoz-schema-migrator:0.102.0 deploy: restart_policy: condition: on-failure diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index d18c10f913..bbafa71a1f 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -66,7 +66,7 @@ services: - --storage.path=/data otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -81,7 +81,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.88.26 + image: signoz/signoz-otel-collector:0.102.0 command: [ "--config=/etc/otel-collector-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index 48d77b98df..075bb3322c 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.47.0} + image: signoz/query-service:${DOCKER_TAG:-0.48.0} container_name: signoz-query-service command: [ @@ -204,7 +204,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.47.0} + image: signoz/frontend:${DOCKER_TAG:-0.48.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -216,7 +216,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -230,7 +230,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.26} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.0} container_name: signoz-otel-collector command: [ diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index d47b7acd46..72980c15a5 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.47.0} + image: signoz/query-service:${DOCKER_TAG:-0.48.0} container_name: signoz-query-service command: [ @@ -203,7 +203,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.47.0} + image: signoz/frontend:${DOCKER_TAG:-0.48.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -215,7 +215,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -229,7 +229,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.26} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.0} container_name: signoz-otel-collector command: [ diff --git a/go.mod b/go.mod index 54caf82679..c5ef326355 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.20.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd - github.com/SigNoz/signoz-otel-collector v0.88.26 + github.com/SigNoz/signoz-otel-collector v0.102.0 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 github.com/auth0/go-jwt-middleware v1.0.1 github.com/cespare/xxhash v1.1.0 - github.com/coreos/go-oidc/v3 v3.4.0 + github.com/coreos/go-oidc/v3 v3.10.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 @@ -21,7 +21,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.1 - github.com/gorilla/mux v1.8.0 + github.com/gorilla/mux v1.8.1 github.com/gosimple/slug v1.10.0 github.com/jmoiron/sqlx v1.3.4 github.com/json-iterator/go v1.1.12 @@ -29,18 +29,18 @@ require ( github.com/mailru/easyjson v0.7.7 github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/minio/minio-go/v6 v6.0.57 - github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 + github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c 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.88.0 - github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.102.0 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.102.0 github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f - github.com/prometheus/common v0.44.0 + github.com/prometheus/common v0.53.0 github.com/prometheus/prometheus v2.5.0+incompatible - github.com/rs/cors v1.10.1 + github.com/rs/cors v1.11.0 github.com/russellhaering/gosaml2 v0.9.0 github.com/russellhaering/goxmldsig v1.2.0 github.com/samber/lo v1.38.1 @@ -49,93 +49,89 @@ require ( github.com/soheilhy/cmux v0.1.5 github.com/srikanthccv/ClickHouse-go-mock v0.7.0 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/collector/component v0.88.0 - go.opentelemetry.io/collector/confmap v0.88.0 - go.opentelemetry.io/collector/connector v0.88.0 - go.opentelemetry.io/collector/consumer v0.88.0 - go.opentelemetry.io/collector/exporter v0.88.0 - go.opentelemetry.io/collector/extension v0.88.0 - go.opentelemetry.io/collector/otelcol v0.88.0 - go.opentelemetry.io/collector/pdata v1.3.0 - go.opentelemetry.io/collector/processor v0.88.0 - go.opentelemetry.io/collector/receiver v0.88.0 - go.opentelemetry.io/collector/service v0.88.0 - go.opentelemetry.io/otel v1.24.0 - go.opentelemetry.io/otel/sdk v1.23.1 + go.opentelemetry.io/collector/component v0.102.1 + go.opentelemetry.io/collector/confmap v0.102.1 + go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.0 + go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.0 + go.opentelemetry.io/collector/connector v0.102.0 + go.opentelemetry.io/collector/consumer v0.102.1 + go.opentelemetry.io/collector/exporter v0.102.0 + go.opentelemetry.io/collector/extension v0.102.1 + go.opentelemetry.io/collector/otelcol v0.102.0 + go.opentelemetry.io/collector/pdata v1.9.0 + go.opentelemetry.io/collector/processor v0.102.0 + go.opentelemetry.io/collector/receiver v0.102.0 + go.opentelemetry.io/collector/service v0.102.0 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.24.0 - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/net v0.26.0 - golang.org/x/oauth2 v0.16.0 + golang.org/x/oauth2 v0.20.0 golang.org/x/text v0.16.0 - google.golang.org/grpc v1.62.0 - google.golang.org/protobuf v1.33.0 + google.golang.org/grpc v1.64.0 + google.golang.org/protobuf v1.34.1 gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.28.2 + k8s.io/apimachinery v0.29.3 ) require ( - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect -) - -require ( - contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/ClickHouse/ch-go v0.61.3 // indirect - github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect github.com/andybalholm/brotli v1.1.0 // indirect - github.com/aws/aws-sdk-go v1.45.26 // indirect + github.com/aws/aws-sdk-go v1.53.11 // 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 - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/expr-lang/expr v1.16.9 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/form3tech-oss/jwt-go v3.2.5+incompatible // 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.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/gosimple/unidecode v1.0.0 // indirect github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect 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.7 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid v1.2.3 // indirect - github.com/knadh/koanf/v2 v2.0.1 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/leodido/go-syslog/v4 v4.1.0 // indirect + github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b // indirect + github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/md5-simd v1.1.0 // indirect github.com/minio/sha256-simd v0.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -146,67 +142,68 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect - github.com/onsi/gomega v1.19.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.102.0 // indirect github.com/paulmach/orb v0.11.1 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_golang v1.17.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect + github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect - github.com/prometheus/statsd_exporter v0.22.7 // indirect + github.com/prometheus/procfs v0.15.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/backo-go v1.0.1 // indirect - github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smarty/assertions v1.15.0 // indirect - github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/valyala/fastjson v1.6.4 // indirect github.com/vjeantet/grok v1.0.1 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/collector v0.88.0 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.88.0 // indirect - go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017 // indirect - go.opentelemetry.io/collector/semconv v0.88.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.20.0 // indirect - go.opentelemetry.io/otel/bridge/opencensus v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/collector v0.102.1 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.102.1 // indirect + go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.0 // indirect + go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.0 // indirect + go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.0 // indirect + go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.0 // indirect + go.opentelemetry.io/collector/featuregate v1.9.0 // indirect + go.opentelemetry.io/collector/semconv v0.102.0 // indirect + go.opentelemetry.io/contrib/config v0.7.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.27.0 // indirect + go.opentelemetry.io/otel/bridge/opencensus v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect - golang.org/x/time v0.3.0 // indirect - gonum.org/v1/gonum v0.14.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect + golang.org/x/time v0.5.0 // indirect + gonum.org/v1/gonum v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect ) replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.11.0 diff --git a/go.sum b/go.sum index 068478198f..948746aacd 100644 --- a/go.sum +++ b/go.sum @@ -13,36 +13,14 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -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 v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -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 v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= 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/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -52,10 +30,9 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= -contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= 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/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM= @@ -98,8 +75,8 @@ github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkb github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc= github.com/SigNoz/prometheus v1.11.0 h1:toX7fU2wqY1TnzvPzDglIYx6OxpqrZ0NNlM/H5S5+u8= github.com/SigNoz/prometheus v1.11.0/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww= -github.com/SigNoz/signoz-otel-collector v0.88.26 h1:+dbBOzIN6nKWD6DSAbsTtm9fcsUC5dSkhDLk6IfQuxg= -github.com/SigNoz/signoz-otel-collector v0.88.26/go.mod h1:sT1EM9PFDaOJLbAz5npWpgXK6OhpWJ9PpSwyhHWs9rU= +github.com/SigNoz/signoz-otel-collector v0.102.0 h1:v6ap+gdvrKklMwU+M9FJgrn28vN0YxrINl3kvdcLonA= +github.com/SigNoz/signoz-otel-collector v0.102.0/go.mod h1:kCx5BfzDujq6C0+kotiqLp5COG2ut4Cb039+55rbWE0= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= @@ -111,8 +88,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= +github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -127,8 +104,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/auth0/go-jwt-middleware v1.0.1 h1:/fsQ4vRr4zod1wKReUH+0A3ySRjGiT9G34kypO/EKwI= github.com/auth0/go-jwt-middleware v1.0.1/go.mod h1:YSeUX3z6+TF2H+7padiEqNJ73Zy9vXW72U//IgN0BIM= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.45.26 h1:PJ2NJNY5N/yeobLYe1Y+xLdavBi67ZI8gvph6ftwVCg= -github.com/aws/aws-sdk-go v1.45.26/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.53.11 h1:KcmduYvX15rRqt4ZU/7jKkmDxU/G87LJ9MUI0yQJh00= +github.com/aws/aws-sdk-go v1.53.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= @@ -148,36 +125,29 @@ 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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +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= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -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-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g= -github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw= +github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc= +github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= +github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= +github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -187,12 +157,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.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= -github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= +github.com/digitalocean/godo v1.109.0 h1:4W97RJLJSUQ3veRZDNbp1Ol3Rbn6Lmt9bKGvfqYI5SU= +github.com/digitalocean/godo v1.109.0/go.mod h1:R6EmmWI8CT1+fCtjWY9UCB+L5uufuZH13wk3YhxycCs= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= -github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE= +github.com/docker/docker v25.0.5+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= @@ -207,32 +177,29 @@ github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -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.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= +github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= 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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-co-op/gocron v1.30.1 h1:tjWUvJl5KrcwpkEkSXFSQFr4F9h5SfV/m4+RX0cV2fs= github.com/go-co-op/gocron v1.30.1/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y= @@ -243,45 +210,46 @@ github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7F github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= 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.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 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.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 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.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redis/redismock/v8 v8.11.5 h1:RJFIiua58hrBrSpXhnGX3on79AU3S271H4ZhRI1wyVo= github.com/go-redis/redismock/v8 v8.11.5/go.mod h1:UaAU9dEe1C+eGr+FHV5prCWIt0hafyPWbGMEWE0UWdA= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA= +github.com/go-resty/resty/v2 v2.12.0/go.mod h1:o0yGPrkS3lOe1+eFajk6kBW8ScXzwU3hD69/gt2yB/0= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -295,8 +263,6 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w 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/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= 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= @@ -309,8 +275,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -326,12 +290,10 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -351,7 +313,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -362,8 +323,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -371,29 +330,15 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -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/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/gophercloud/gophercloud v1.5.0 h1:cDN6XFCLKiiqvYpjQLq9AiM7RDRbIC9450WpPH+yvXo= -github.com/gophercloud/gophercloud v1.5.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.8.0 h1:TM3Jawprb2NrdOnvcHhWJalmKmAmOGgfZElM/3oBYCk= +github.com/gophercloud/gophercloud v1.8.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= @@ -401,8 +346,8 @@ github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfre github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.10.0 h1:3XbiQua1IpCdrvuntWvGBxVm+K99wCSxJjlxkP49GGQ= @@ -413,11 +358,11 @@ github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= 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.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= -github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= -github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= +github.com/hashicorp/consul/api v1.28.3 h1:IE06LST/knnCQ+cxcvzyXRF/DetkgGhJoaOFd4l9xkk= +github.com/hashicorp/consul/api v1.28.3/go.mod h1:7AGcUFu28HkgOKD/GmsIGIFzRTmN0L02AE9Thsr2OhU= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= @@ -431,8 +376,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -454,6 +399,8 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= @@ -463,8 +410,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e h1:sr4lujmn9heD030xx/Pd4B/JSmvRhFzuotNXaaV0WLs= -github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= +github.com/hashicorp/nomad/api v0.0.0-20240306004928-3e7191ccb702 h1:fI1LXuBaS1d9z1kmb++Og6YD8uMRwadXorCwE+xgOFA= +github.com/hashicorp/nomad/api v0.0.0-20240306004928-3e7191ccb702/go.mod h1:z71gkJdrkAt/Rl6C7Q79VE7AwJ5lUF+M+fzFTyIHYB0= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= @@ -472,8 +419,8 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hetznercloud/hcloud-go/v2 v2.0.0 h1:Sg1DJ+MAKvbYAqaBaq9tPbwXBS2ckPIaMtVdUjKu+4g= -github.com/hetznercloud/hcloud-go/v2 v2.0.0/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q= +github.com/hetznercloud/hcloud-go/v2 v2.6.0 h1:RJOA2hHZ7rD1pScA4O1NF6qhkHyUdbbxjHgFNot8928= +github.com/hetznercloud/hcloud-go/v2 v2.6.0/go.mod h1:4J1cSE57+g0WS93IiHLV7ubTHItcp+awzeBp5bM9mfA= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -483,10 +430,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6 h1:s9ZL6ZhFF8y6ebnm1FLvobkzoIu5xwDQUcRPk/IEhpM= -github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6/go.mod h1:aXdIdfn2OcGnMhOTojXmwZqXKgC3MU5riiNvzwwG9OY= -github.com/ionos-cloud/sdk-go/v6 v6.1.8 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0= -github.com/ionos-cloud/sdk-go/v6 v6.1.8/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/ionos-cloud/sdk-go/v6 v6.1.11 h1:J/uRN4UWO3wCyGOeDdMKv8LWRzKu6UIkLEaes38Kzh8= +github.com/ionos-cloud/sdk-go/v6 v6.1.11/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -517,14 +462,14 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= -github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g= -github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -542,15 +487,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 h1:bCiVCRCs1Heq84lurVinUPy19keqGEe4jh5vtK37jcg= -github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= +github.com/leodido/go-syslog/v4 v4.1.0 h1:Wsl194qyWXr7V6DrGWC3xmxA9Ra6XgWO+toNt2fmCaI= +github.com/leodido/go-syslog/v4 v4.1.0/go.mod h1:eJ8rUfDN5OS6dOkCOBYlg2a+hbAg6pJa99QXXgMrd98= +github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b h1:11UHH39z1RhZ5dc4y4r/4koJo6IYFgTRMe/LlwRTEw0= +github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= 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.19.0 h1:n4WJrcr9+30e9JGZ6DI0nZbm5SdAj1kSwvvt/998YUw= -github.com/linode/linodego v1.19.0/go.mod h1:XZFR+yJ9mm2kwf6itZ6SCpu+6w3KnIevV0Uu5HNWJgQ= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/linode/linodego v1.33.0 h1:cX2FYry7r6CA1ujBMsdqiM4VhvIQtnWsOuVblzfBhCw= +github.com/linode/linodego v1.33.0/go.mod h1:dSJJgIwqZCF5wnpuC6w5cyIbRtcexAm7uVvuJopGB40= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -565,18 +513,16 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/minio-go/v6 v6.0.57 h1:ixPkbKkyD7IhnluRgQpGSpHdpvNVaW6OD5R9IAO/9Tw= @@ -596,8 +542,8 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= -github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -635,30 +581,32 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g= github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.88.0 h1:9gjzrpUlzGC5BebgO1cxb/9KQ9yuIIE6B+6wLySKVCQ= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.88.0/go.mod h1:GXfK9q6RosmltLUcOdrQMS3hF1RYuwIgFTIa4RRR5J4= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0 h1:2HoGcjmHHIDMafd3Uj3flQJrV8TC2FAnUiTKD8FH0G8= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0/go.mod h1:JvXKcDtcOQRkz/Sw1m27K4QA3OwMbUvifoeEX2NQC6k= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.88.0 h1:jqdkgfHXcjvk6L2CyTUv3Rn+whX3TfFWd0Mz4QNAV1c= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.88.0/go.mod h1:5QXLdN4gdjAEcMHNEK/RrDdp+FObca0bS4/pRauyZs8= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.88.0 h1:S1FEVDH5GEMZQuHg8jfv47lCHHDFVjZBpO/Yrb/vKpE= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.88.0/go.mod h1:IJqzjDv6ZFeu7cYGCUzQ5/3CuTPVIo3UAGK3o2jK/Sw= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.88.0 h1:Ezi3FyxGbetZ12yAinyif/aabc9J7VyFdOvdufPCaUU= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.88.0/go.mod h1:6nO6NG5H5V5YGRp5onf9JnitXwhMfNXLSfZNyVwRPuw= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0 h1:meHyTMeWC3xYativnHwYdnT9XwHWtfjioPRqgzaDJXA= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0/go.mod h1:Vhb+pyxTKFjAoLaaJCiYHbJS6o56vQEvnJDhh/ws6yY= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.102.0 h1:7QHxeMnKzMXMw9oh5lnOHakfPpGSglxiZfbYUn6l6yc= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.102.0/go.mod h1:BtKaHa1yDHfhM9qjGUHweb0HgqFGxFSM7AMzwLXVR98= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.102.0 h1:PNLVcz8kJLE9V5kGnbBh277Bvl4WwiVZ+NbFbOB80WY= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.102.0/go.mod h1:cBbjwd8m4rBVgCQksUbAVQX1EoM5IuCyNQw2mzvibEM= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.102.0 h1:qsM5HhWpAfIMg8LdO4u+CHofu4UuCuJwg/M+ySO9uZA= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.102.0/go.mod h1:wBJlGy9Wx6s7AxIMcSne2sGw73e5ZUy1AQ/duYwpFf8= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.102.0 h1:vJL6lDaeI3pVA7ADnWKD3HMpI80BSrZ2UnGc+qkwqoY= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.102.0/go.mod h1:xtE7tds5j8PtI/wMuGb+Em5K9rJH8hm6t28Qe4QrpoU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.102.0 h1:TvJYcU/DLRFCgHr7nT98k5D+qkZ4syKVxc8OJjv+K4c= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.102.0/go.mod h1:WzD3Ox7tywAQHknxAFpAC1oZJGItMp5mbvgUGjvzNY8= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.102.0 h1:J8GFYxKLWG1360XRukc1tY5K9BF80MFXcO91UpCMgcQ= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.102.0/go.mod h1:GNxigQNap2jyOEPdOedAKqCbh61y576ND4BKn/7i8xY= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.102.0 h1:XOoV42CE0BJUsKJQ7+Fie2jusw0MBzzOc79IoQONJAk= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.102.0/go.mod h1:nCpPHY7XLM+zbJxKxP132IuV0xHCu5E6oa3ZLpmBPl4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= -github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM= -github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M= +github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= +github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -686,45 +634,36 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f h1:h0p1aZ9F5d6IXOygysob3g4B07b+HuVUQC0VJKD8wA4= github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= -github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= -github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= +github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= +github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= 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= @@ -735,8 +674,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4PfRmVOIk= github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU= github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg= @@ -748,8 +687,8 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20 h1:a9hSJdJcd16e0HoMsnFvaHvxB3pxSD+SC7+CISp7xY0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25 h1:/8rfZAdFfafRXOgz+ZpMZZWZ5pYggCY9t7e/BvjaBHM= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= @@ -757,8 +696,8 @@ github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N+ github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc= 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/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= -github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -785,8 +724,8 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/srikanthccv/ClickHouse-go-mock v0.7.0 h1:XhRMX2663xkDGq3DYavw8m75O94s9u76hOIjo9QBl8c= @@ -810,7 +749,6 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -819,6 +757,8 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/vjeantet/grok v1.0.1 h1:2rhIR7J4gThTgcZ1m2JY4TrJZNgjn985U28kT2wQrJ4= github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= @@ -834,9 +774,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= @@ -846,83 +785,94 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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/collector v0.88.0 h1:I0lerJK1h88vk7enriSgLV+h7dM099G9FgwkfmIZaf0= -go.opentelemetry.io/collector v0.88.0/go.mod h1:we0quZ+4txHS3Sfb0VdjFv95KYLGmto4ZAThCHiYgGA= -go.opentelemetry.io/collector/component v0.88.0 h1:LU/1ov5D/O/gv9D2Uv88EjNKHn7DHcUCZn1qQsb/zgw= -go.opentelemetry.io/collector/component v0.88.0/go.mod h1:4utKxz4Lilym3SPxNXJHosdaTjT1aQxI+TCmnJO54pU= -go.opentelemetry.io/collector/config/confignet v0.88.0 h1:CbVZQpWC8Bm/BKo3x2mnQZVdQKClU0gCa6SVbRCc930= -go.opentelemetry.io/collector/config/confignet v0.88.0/go.mod h1:cpO8JYWGONaViOygKVw+Hd2UoBcn2cUiyi0WWeFTwJY= -go.opentelemetry.io/collector/config/configtelemetry v0.88.0 h1:54Z9uoSTpbkq3esDwHvJMChoUH8p/nfesG2xJTOXayY= -go.opentelemetry.io/collector/config/configtelemetry v0.88.0/go.mod h1:+LAXM5WFMW/UbTlAuSs6L/W72WC+q8TBJt/6z39FPOU= -go.opentelemetry.io/collector/confmap v0.88.0 h1:tOgY6NXMXAL2hz2+zVDQ0jvBlCUHprSf90bw5ktbdaI= -go.opentelemetry.io/collector/confmap v0.88.0/go.mod h1:CSJlMk1KRZloXAygpiPeCLpuQiLVDEZYbGsGHIKHeUg= -go.opentelemetry.io/collector/connector v0.88.0 h1:hUTSMexixSx4rWExBfr5p3YzDdj9a9+cUveaRK0EdnQ= -go.opentelemetry.io/collector/connector v0.88.0/go.mod h1:vkOHpyWNlHQVFHKUB4Dp1yYCIpAFnouZ2REupkzL/PU= -go.opentelemetry.io/collector/consumer v0.88.0 h1:l8Ty5UHhZ2U6WCp4yHt97uW6vN1vMP0JbFeQEaVnEgY= -go.opentelemetry.io/collector/consumer v0.88.0/go.mod h1:VVoafgyhjpO6fuJu12GqspmuLrn91JCOou0sOtb9GOg= -go.opentelemetry.io/collector/exporter v0.88.0 h1:bDXltsjQslhT7tlObQzKJiHuP5LDPeZHrkpUh4cT6Kk= -go.opentelemetry.io/collector/exporter v0.88.0/go.mod h1:0KQKlbUlYBwNJ9Dfapn6mRLhdhtM3tUlDGgN88oDVug= -go.opentelemetry.io/collector/extension v0.88.0 h1:/WH97pQYypL7ZC5OEccoE0gFs6fjBC/Uh9NuVEYEoZ0= -go.opentelemetry.io/collector/extension v0.88.0/go.mod h1:5wPlOyWtVJcZS9CMhFUnuRvNQ0XIoV/iUSaZWtCjoHA= -go.opentelemetry.io/collector/extension/zpagesextension v0.88.0 h1:cpkwzjhq6jfkVq3ltUl9wdb/8RrWbn0utHTCU3K5Mhc= -go.opentelemetry.io/collector/extension/zpagesextension v0.88.0/go.mod h1:8LPmV8UkQgDAfNaAizQqLzYnYibzQv81eBGKv0Mk6wU= -go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017 h1:DtJQalPXMWQqT6jd2LZ1oKrOfLJJRCi+rh2LKnkj4Zo= -go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017/go.mod h1:fLmJMf1AoHttkF8p5oJAc4o5ZpHu8yO5XYJ7gbLCLzo= -go.opentelemetry.io/collector/otelcol v0.88.0 h1:f2eRVLJY66w9WFj5iT1Tg6Qxtlljagov9v8TPStuK2g= -go.opentelemetry.io/collector/otelcol v0.88.0/go.mod h1:F85TtMPt+ySe29HD6DOyvsMFCV3onaB3VJzky7qrtzQ= -go.opentelemetry.io/collector/pdata v1.3.0 h1:JRYN7tVHYFwmtQhIYbxWeiKSa2L1nCohyAs8sYqKFZo= -go.opentelemetry.io/collector/pdata v1.3.0/go.mod h1:t7W0Undtes53HODPdSujPLTnfSR5fzT+WpL+RTaaayo= -go.opentelemetry.io/collector/processor v0.88.0 h1:5BUZaH+RhTpgTVqBZCrBnN/vl0M1CtwQsZ8ek4iH1lc= -go.opentelemetry.io/collector/processor v0.88.0/go.mod h1:2T5KxgBQxXuuyMu9dh+PIBxQ/geCFYcdnjmlWZx8o3E= -go.opentelemetry.io/collector/receiver v0.88.0 h1:MPvVAFOfjl0+Ylka7so8QoK8T2Za2471rv5t3sqbbSY= -go.opentelemetry.io/collector/receiver v0.88.0/go.mod h1:MIZ6jPPZ+I8XibZm6I3RAn9h7Wcy2ZJsPmtXd2BLr60= -go.opentelemetry.io/collector/semconv v0.88.0 h1:8TVP4hYaUC87S6CCLKNoSxsUE0ChldE4vqotvNHHUnE= -go.opentelemetry.io/collector/semconv v0.88.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw= -go.opentelemetry.io/collector/service v0.88.0 h1:KSue2w94Tb2xjenlm+SC2y2g87hdhFJeHMT9pEshKAE= -go.opentelemetry.io/collector/service v0.88.0/go.mod h1:+Fov4arJzWl8SBuMonvM7gOrfK72G+d+2WotRjR5c2I= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0 h1:Yty9Vs4F3D6/liF1o6FNt0PvN85h/BJJ6DQKJ3nrcM0= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0/go.mod h1:On4VgbkqYL18kbJlWsa18+cMNe6rYpBnPi1ARI/BrsU= -go.opentelemetry.io/contrib/zpages v0.45.0 h1:jIwHHGoWzJoZdbIUtWdErjL85Gni6BignnAFqDtMRL4= -go.opentelemetry.io/contrib/zpages v0.45.0/go.mod h1:4mIdA5hqH6hEx9sZgV50qKfQO8aIYolUZboHmz+G7vw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/bridge/opencensus v0.42.0 h1:QvC+bcZkWMphWPiVqRQygMj6M0/3TOuJEO+erRA7kI8= -go.opentelemetry.io/otel/bridge/opencensus v0.42.0/go.mod h1:XJojP7g5DqYdiyArix/H9i1XzPPlIUc9dGLKtF9copI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 h1:wNMDy/LVGLj2h3p6zg4d0gypKfWKSWI14E1C4smOgl8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0/go.mod h1:YfbDdXAAkemWJK3H/DshvlrxqFB2rtW4rY6ky/3x/H0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= -go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0/go.mod h1:/MtYTE1SfC2QIcE0bDot6fIX+h+WvXjgTqgn9P0LNPE= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= -go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= -go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= -go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/collector v0.102.1 h1:M/ciCcReQsSDYG9bJ2Qwqk7pQILDJ2bM/l0MdeCAvJE= +go.opentelemetry.io/collector v0.102.1/go.mod h1:yF1lDRgL/Eksb4/LUnkMjvLvHHpi6wqBVlzp+dACnPM= +go.opentelemetry.io/collector/component v0.102.1 h1:66z+LN5dVCXhvuVKD1b56/3cYLK+mtYSLIwlskYA9IQ= +go.opentelemetry.io/collector/component v0.102.1/go.mod h1:XfkiSeImKYaewT2DavA80l0VZ3JjvGndZ8ayPXfp8d0= +go.opentelemetry.io/collector/config/confignet v0.102.1 h1:nSiAFQMzNCO4sDBztUxY73qFw4Vh0hVePq8+3wXUHtU= +go.opentelemetry.io/collector/config/confignet v0.102.1/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= +go.opentelemetry.io/collector/config/configtelemetry v0.102.1 h1:f/CYcrOkaHd+COIJ2lWnEgBCHfhEycpbow4ZhrGwAlA= +go.opentelemetry.io/collector/config/configtelemetry v0.102.1/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= +go.opentelemetry.io/collector/confmap v0.102.1 h1:wZuH+d/P11Suz8wbp+xQCJ0BPE9m5pybtUe74c+rU7E= +go.opentelemetry.io/collector/confmap v0.102.1/go.mod h1:KgpS7UxH5rkd69CzAzlY2I1heH8Z7eNCZlHmwQBMxNg= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.0 h1:8Ne/oL6M4kMWK0P3FKV9EduQa+1UOGyVAnFHfSo4c1A= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.0/go.mod h1:Xj4Ld/RriP/Bj+5oPpaYJsLNs2wWRDN2TvzX3Lbi6+M= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.0 h1:o1iKqN+oM+TZqHoGdnKw1Am2BQlIGCYbxCRzU8T3jbM= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.0/go.mod h1:JpCemLtL/sXQ2Rk3Bx7OPPA7Qt/9NVH91q0bR655gSo= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.0 h1:SfASE6lxXjrmYj/UibcWdOiFWvRG0zt4hJgenloEhlY= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.0/go.mod h1:+Ku0Fvdb5f6e9UkfqJXAV5FaUJVxxg6Ykfx7Js8y+V4= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.0 h1:GwJQTXs7pYPUv/fVf+0nBgsJdlrTuY/PfwQ/TRA/sIk= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.0/go.mod h1:PGE3DcRgqYWWC2cq2hYZoET1d3Q8JZyPNmgvqXPFWEU= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.0 h1:zdYZLiHHtDf4Kk9WU7mW9dW6WAXtBF54I5jmTMRJtiw= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.0/go.mod h1:yFsgUM0PbUJkPlbpJfOG6da+YiF0Z80tv7YcnL3qwv4= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.0 h1:Y4H+GaCQl2URp9mEJMV5CYOhw+erONqNyvtFoKQfIoA= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.0/go.mod h1:g1RjfVD0gHAf/mPOIs3zBoKBeuDsN+rc5x0lZtgA8tI= +go.opentelemetry.io/collector/connector v0.102.0 h1:IvAsVfYRxP0ajmKbUovF8qugkcUtHq6RuYNtjcMa63E= +go.opentelemetry.io/collector/connector v0.102.0/go.mod h1:f4M7wZ/9+XtgTE0fivBFH3WlwntaEd0qFFA0giFkdnY= +go.opentelemetry.io/collector/consumer v0.102.1 h1:0CkgHhxwx4lI/m+hWjh607xyjooW5CObZ8hFQy5vvo0= +go.opentelemetry.io/collector/consumer v0.102.1/go.mod h1:HoXqmrRV13jLnP3/Gg3fYNdRkDPoO7UW58hKiLyFF60= +go.opentelemetry.io/collector/exporter v0.102.0 h1:hvyTyyGVx5FIikA6HzlTeZHILJ62hrIBsoZCoKlpX3A= +go.opentelemetry.io/collector/exporter v0.102.0/go.mod h1:JWE+1qNoSVBSelzhI3Iao/VkYVssY+sXaTPK1JOmpQ0= +go.opentelemetry.io/collector/extension v0.102.1 h1:gAvE3w15q+Vv0Tj100jzcDpeMTyc8dAiemHRtJbspLg= +go.opentelemetry.io/collector/extension v0.102.1/go.mod h1:XBxUOXjZpwYLZYOK5u3GWlbBTOKmzStY5eU1R/aXkIo= +go.opentelemetry.io/collector/extension/zpagesextension v0.102.0 h1:BPq98py8nwzaV7KAsxt4ZZAF9LiSRu7ZjHNGavFNyKo= +go.opentelemetry.io/collector/extension/zpagesextension v0.102.0/go.mod h1:P86HW3x3epDS5F4yP0gAvsZiw4xxP1OupTEx2o6UqjY= +go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= +go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= +go.opentelemetry.io/collector/otelcol v0.102.0 h1:HuE+ok4iUjOrmYhQBSWpG5kBTVhcA24ljPL4pBERZ5E= +go.opentelemetry.io/collector/otelcol v0.102.0/go.mod h1:w8pCRu2nM/jAkLlEAS6ccKtJv5ylUQe6Ugl98zzTfyE= +go.opentelemetry.io/collector/pdata v1.9.0 h1:qyXe3HEVYYxerIYu0rzgo1Tx2d1Zs6iF+TCckbHLFOw= +go.opentelemetry.io/collector/pdata v1.9.0/go.mod h1:vk7LrfpyVpGZrRWcpjyy0DDZzL3SZiYMQxfap25551w= +go.opentelemetry.io/collector/pdata/testdata v0.102.1 h1:S3idZaJxy8M7mCC4PG4EegmtiSaOuh6wXWatKIui8xU= +go.opentelemetry.io/collector/pdata/testdata v0.102.1/go.mod h1:JEoSJTMgeTKyGxoMRy48RMYyhkA5vCCq/abJq9B6vXs= +go.opentelemetry.io/collector/processor v0.102.0 h1:JsjTlpBRmoSYxcu3cAbKBchOmL6aNUxLa03ZkWIqZr8= +go.opentelemetry.io/collector/processor v0.102.0/go.mod h1:IaCSDcfy75uQTaOM+LgR1bMf/bUw2eFfzn20uvWYfLQ= +go.opentelemetry.io/collector/receiver v0.102.0 h1:8rHNjWjV90bL0dgvKVc/7D10NCbM7bXCiqpcLRz5jBI= +go.opentelemetry.io/collector/receiver v0.102.0/go.mod h1:bYDwYItMrj7Drx0Pn4wZQ8Ii67lp9Nta62gbau93FhA= +go.opentelemetry.io/collector/semconv v0.102.0 h1:VEOdog9IbSfaGR7yg4AVmT54MwHAgH9lzITH6C33uyc= +go.opentelemetry.io/collector/semconv v0.102.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= +go.opentelemetry.io/collector/service v0.102.0 h1:B5nfyQZF7eB/y+yucl9G/7VsusbXixYXWingXn7VszM= +go.opentelemetry.io/collector/service v0.102.0/go.mod h1:c+0n0DfQeCjgrdplNHYwYbG/5aupTZVYU/50nMQraoc= +go.opentelemetry.io/contrib/config v0.7.0 h1:b1rK5tGTuhhPirJiMxOcyQfZs76j2VapY6ODn3b2Dbs= +go.opentelemetry.io/contrib/config v0.7.0/go.mod h1:8tdiFd8N5etOi3XzBmAoMxplEzI3TcL8dU5rM5/xcOQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= +go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= +go.opentelemetry.io/contrib/zpages v0.52.0 h1:MPgkMy0Cp3O5EdfVXP0ss3ujhEibysTM4eszx7E7d+E= +go.opentelemetry.io/contrib/zpages v0.52.0/go.mod h1:fqG5AFdoYru3A3DnhibVuaaEfQV2WKxE7fYE1jgDRwk= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs= +go.opentelemetry.io/otel/bridge/opencensus v1.27.0/go.mod h1:uRvWtAAXzyVOST0WMPX5JHGBaAvBws+2F8PcC5gMnTk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= +go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= +go.opentelemetry.io/otel/exporters/prometheus v0.49.0/go.mod h1:KfQ1wpjf3zsHjzP149P4LyAwWRupc6c7t1ZJ9eXpKQM= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 h1:/jlt1Y8gXWiHG9FBx6cJaIC5hYx5Fe64nC8w5Cylt/0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0/go.mod h1:bmToOGOBZ4hA9ghphIc1PAf66VA8KOtsuy3+ScStG20= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 h1:/0YaXu3755A/cFbtXp+21lkXgI0QE5avTWA2HjU9/WE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0/go.mod h1:m7SFxp0/7IxmJPLIY3JhOcU9CoFzDaCPL6xxQIxhA+o= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= 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= @@ -943,7 +893,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= @@ -957,8 +906,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -971,7 +920,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -981,10 +929,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1020,29 +965,14 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1050,24 +980,9 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1079,8 +994,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1129,59 +1042,27 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1190,19 +1071,16 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/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= @@ -1246,31 +1124,17 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= -gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= +gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= +gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1287,38 +1151,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -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.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= 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= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1350,60 +1188,11 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -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-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5 h1:Q2RxlXqh1cgzzUgV261vBO2jI5R/3DD1J2pM0nI4NhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= 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= @@ -1418,28 +1207,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -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.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1453,10 +1225,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 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.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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= @@ -1474,8 +1244,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U= gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1498,18 +1266,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= -k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= -k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= -k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= -k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= -k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= -k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= -k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 67d6f6d508..1fb37ce2ff 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -192,7 +192,7 @@ services: <<: *db-depend otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.26} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -205,7 +205,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:0.88.26 + image: signoz/signoz-otel-collector:0.102.0 container_name: signoz-otel-collector command: [ From 64e06ab3f9e675fbc130bac4edc0af447ee5f659 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Thu, 20 Jun 2024 11:34:27 +0530 Subject: [PATCH 019/281] fix: update from typography link to react router dom link component to maintain global state (#5279) --- .../container/TraceDetail/TraceDetails.styles.scss | 12 +++++++++++- frontend/src/container/TraceDetail/index.tsx | 3 ++- .../container/TracesExplorer/TracesView/configs.tsx | 8 ++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/frontend/src/container/TraceDetail/TraceDetails.styles.scss b/frontend/src/container/TraceDetail/TraceDetails.styles.scss index 676160aecc..0c309bc83d 100644 --- a/frontend/src/container/TraceDetail/TraceDetails.styles.scss +++ b/frontend/src/container/TraceDetail/TraceDetails.styles.scss @@ -1,7 +1,17 @@ .span-details-sider { + padding-top: 16px; + + ::-webkit-scrollbar { + width: 0.2em; + } + + ::-webkit-scrollbar-track { + box-shadow: inset 0 0 6px rgba(18, 18, 18, 0.3); + } + &.dark { .ant-layout-sider-trigger { - background-color: black !important; + background-color: #0b0c0e !important; } } diff --git a/frontend/src/container/TraceDetail/index.tsx b/frontend/src/container/TraceDetail/index.tsx index 67b55a1bca..4b333e0dad 100644 --- a/frontend/src/container/TraceDetail/index.tsx +++ b/frontend/src/container/TraceDetail/index.tsx @@ -246,13 +246,14 @@ function TraceDetail({ response }: TraceDetailProps): JSX.Element { setCollapsed(value)} > {!collapsed && ( diff --git a/frontend/src/container/TracesExplorer/TracesView/configs.tsx b/frontend/src/container/TracesExplorer/TracesView/configs.tsx index a6296e362a..f5980a044d 100644 --- a/frontend/src/container/TracesExplorer/TracesView/configs.tsx +++ b/frontend/src/container/TracesExplorer/TracesView/configs.tsx @@ -3,7 +3,7 @@ import { ColumnsType } from 'antd/es/table'; import ROUTES from 'constants/routes'; import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util'; import { DEFAULT_PER_PAGE_OPTIONS } from 'hooks/queryPagination'; -import { generatePath } from 'react-router-dom'; +import { generatePath, Link } from 'react-router-dom'; import { ListItem } from 'types/api/widgets/getQuery'; export const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS]; @@ -38,14 +38,14 @@ export const columns: ColumnsType = [ dataIndex: 'traceID', key: 'traceID', render: (traceID: string): JSX.Element => ( - {traceID} - + ), }, ]; From 38b1de5cccea597fae0bdba5f9ad77b759115c89 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Thu, 20 Jun 2024 17:05:18 +0530 Subject: [PATCH 020/281] feat: [5038] enable list view - add to dashboard (#5268) * feat: [5038] enable list view - add to dashboard * feat: pass page size for list view export --- frontend/src/constants/panelTypes.ts | 1 + .../src/container/LogsExplorerViews/index.tsx | 38 +++++--- .../LogsPanelComponent.styles.scss | 2 +- frontend/src/container/NewWidget/utils.ts | 44 ---------- .../TracesTableComponent.styles.scss | 2 +- frontend/src/hooks/dashboard/utils.ts | 88 +++++++++++-------- frontend/src/pages/TracesExplorer/index.tsx | 37 +++++++- .../api/queryBuilder/queryBuilderData.ts | 1 + 8 files changed, 119 insertions(+), 94 deletions(-) diff --git a/frontend/src/constants/panelTypes.ts b/frontend/src/constants/panelTypes.ts index 7476b20783..8892f0fcbe 100644 --- a/frontend/src/constants/panelTypes.ts +++ b/frontend/src/constants/panelTypes.ts @@ -40,4 +40,5 @@ export const getComponentForPanelType = ( export const AVAILABLE_EXPORT_PANEL_TYPES = [ PANEL_TYPES.TIME_SERIES, PANEL_TYPES.TABLE, + PANEL_TYPES.LIST, ]; diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx index 28e199066a..ced6556f44 100644 --- a/frontend/src/container/LogsExplorerViews/index.tsx +++ b/frontend/src/container/LogsExplorerViews/index.tsx @@ -37,7 +37,7 @@ import { useNotifications } from 'hooks/useNotifications'; import useUrlQueryData from 'hooks/useUrlQueryData'; import { FlatLogData } from 'lib/logs/flatLogData'; import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData'; -import { defaultTo, isEmpty, omit } from 'lodash-es'; +import { cloneDeep, defaultTo, isEmpty, omit, set } from 'lodash-es'; import { Sliders } from 'lucide-react'; import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -117,6 +117,12 @@ function LogsExplorerViews({ return stagedQuery.builder.queryData.find((item) => !item.disabled) || null; }, [stagedQuery]); + const { options, config } = useOptionsMenu({ + storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS, + dataSource: initialDataSource || DataSource.LOGS, + aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP, + }); + const orderByTimestamp: OrderByPayload | null = useMemo(() => { const timestampOrderBy = listQuery?.orderBy.find( (item) => item.columnName === 'timestamp', @@ -174,10 +180,10 @@ function LogsExplorerViews({ () => updateAllQueriesOperators( currentQuery || initialQueriesMap.logs, - PANEL_TYPES.TIME_SERIES, + selectedPanelType, DataSource.LOGS, ), - [currentQuery, updateAllQueriesOperators], + [currentQuery, selectedPanelType, updateAllQueriesOperators], ); const handleModeChange = (panelType: PANEL_TYPES): void => { @@ -309,6 +315,14 @@ function LogsExplorerViews({ isLoading: isUpdateDashboardLoading, } = useUpdateDashboard(); + const getUpdatedQueryForExport = useCallback((): Query => { + const updatedQuery = cloneDeep(currentQuery); + + set(updatedQuery, 'builder.queryData[0].pageSize', 10); + + return updatedQuery; + }, [currentQuery]); + const handleExport = useCallback( (dashboard: Dashboard | null): void => { if (!dashboard || !panelType) return; @@ -319,11 +333,17 @@ function LogsExplorerViews({ const widgetId = v4(); + const query = + panelType === PANEL_TYPES.LIST + ? getUpdatedQueryForExport() + : exportDefaultQuery; + const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery( dashboard, - exportDefaultQuery, + query, widgetId, panelTypeParam, + options.selectColumns, ); updateDashboard(updatedDashboard, { @@ -353,7 +373,7 @@ function LogsExplorerViews({ } const dashboardEditView = generateExportToDashboardLink({ - query: exportDefaultQuery, + query, panelType: panelTypeParam, dashboardId: data.payload?.uuid || '', widgetId, @@ -365,7 +385,9 @@ function LogsExplorerViews({ }); }, [ + getUpdatedQueryForExport, exportDefaultQuery, + options.selectColumns, history, notifications, panelType, @@ -460,12 +482,6 @@ function LogsExplorerViews({ selectedView, ]); - const { options, config } = useOptionsMenu({ - storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS, - dataSource: initialDataSource || DataSource.METRICS, - aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP, - }); - const chartData = useMemo(() => { if (!stagedQuery) return []; diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss b/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss index 5fa4ce0cda..6317ea2134 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.styles.scss @@ -5,7 +5,7 @@ height: 100%; .resize-table { - height: calc(100% - 40px); + height: calc(100% - 70px); overflow: scroll; overflow-x: hidden; diff --git a/frontend/src/container/NewWidget/utils.ts b/frontend/src/container/NewWidget/utils.ts index a8ac095d1e..b840d6e71a 100644 --- a/frontend/src/container/NewWidget/utils.ts +++ b/frontend/src/container/NewWidget/utils.ts @@ -189,50 +189,6 @@ export const panelTypeDataSourceFormValuesMap: Record< }, }, }, - [PANEL_TYPES.HISTOGRAM]: { - [DataSource.LOGS]: { - builder: { - queryData: [ - 'filters', - 'aggregateOperator', - 'aggregateAttribute', - 'groupBy', - 'limit', - 'having', - 'orderBy', - 'functions', - ], - }, - }, - [DataSource.METRICS]: { - builder: { - queryData: [ - 'filters', - 'aggregateOperator', - 'aggregateAttribute', - 'groupBy', - 'limit', - 'having', - 'orderBy', - 'functions', - 'spaceAggregation', - ], - }, - }, - [DataSource.TRACES]: { - builder: { - queryData: [ - 'filters', - 'aggregateOperator', - 'aggregateAttribute', - 'groupBy', - 'limit', - 'having', - 'orderBy', - ], - }, - }, - }, [PANEL_TYPES.TABLE]: { [DataSource.LOGS]: { builder: { diff --git a/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss b/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss index 0787972eec..74e80f8764 100644 --- a/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss +++ b/frontend/src/container/TracesTableComponent/TracesTableComponent.styles.scss @@ -5,7 +5,7 @@ height: 100%; .resize-table { - height: calc(100% - 40px); + height: calc(100% - 70px); overflow: scroll; overflow-x: hidden; diff --git a/frontend/src/hooks/dashboard/utils.ts b/frontend/src/hooks/dashboard/utils.ts index ad3fadd8b9..ed61e25581 100644 --- a/frontend/src/hooks/dashboard/utils.ts +++ b/frontend/src/hooks/dashboard/utils.ts @@ -1,43 +1,61 @@ import { PANEL_TYPES } from 'constants/queryBuilder'; +import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils'; import { Dashboard } from 'types/api/dashboard/getAll'; +import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; +const baseLogsSelectedColumns = { + dataType: 'string', + type: '', + name: 'timestamp', +}; + export const addEmptyWidgetInDashboardJSONWithQuery = ( dashboard: Dashboard, query: Query, widgetId: string, - panelTypes?: PANEL_TYPES, -): Dashboard => ({ - ...dashboard, - data: { - ...dashboard.data, - layout: [ - { - i: widgetId, - w: 6, - x: 0, - h: 6, - y: 0, - }, - ...(dashboard?.data?.layout || []), - ], - widgets: [ - ...(dashboard?.data?.widgets || []), - { - id: widgetId, - query, - description: '', - isStacked: false, - nullZeroValues: '', - opacity: '', - title: '', - timePreferance: 'GLOBAL_TIME', - panelTypes: panelTypes || PANEL_TYPES.TIME_SERIES, - softMax: null, - softMin: null, - selectedLogFields: [], - selectedTracesFields: [], - }, - ], - }, -}); + panelType?: PANEL_TYPES, + selectedColumns?: BaseAutocompleteData[] | null, +): Dashboard => { + const logsSelectedColumns = [ + baseLogsSelectedColumns, + ...convertKeysToColumnFields(selectedColumns || []), + ]; + + return { + ...dashboard, + data: { + ...dashboard.data, + layout: [ + { + i: widgetId, + w: 6, + x: 0, + h: 6, + y: 0, + }, + ...(dashboard?.data?.layout || []), + ], + widgets: [ + ...(dashboard?.data?.widgets || []), + { + id: widgetId, + query, + description: '', + isStacked: false, + nullZeroValues: '', + opacity: '', + title: '', + timePreferance: 'GLOBAL_TIME', + panelTypes: panelType || PANEL_TYPES.TIME_SERIES, + softMax: null, + softMin: null, + selectedLogFields: + panelType === PANEL_TYPES.LIST ? logsSelectedColumns : [], + selectedTracesFields: + panelType === PANEL_TYPES.LIST ? selectedColumns || [] : [], + }, + ], + }, + }; +}; diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index 30dc747345..ab022bfeee 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -5,12 +5,15 @@ import * as Sentry from '@sentry/react'; import { Button, Card, Tabs, Tooltip } from 'antd'; import axios from 'axios'; import ExplorerCard from 'components/ExplorerCard/ExplorerCard'; +import { LOCALSTORAGE } from 'constants/localStorage'; import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper'; import ExportPanel from 'container/ExportPanel'; +import { useOptionsMenu } from 'container/OptionsMenu'; import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions'; import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2'; +import { defaultSelectedColumns } from 'container/TracesExplorer/ListView/configs'; import QuerySection from 'container/TracesExplorer/QuerySection'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils'; @@ -20,9 +23,11 @@ import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; +import { cloneDeep, set } from 'lodash-es'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { Dashboard } from 'types/api/dashboard/getAll'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { DataSource } from 'types/common/queryBuilder'; import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink'; import { v4 } from 'uuid'; @@ -42,6 +47,15 @@ function TracesExplorer(): JSX.Element { stagedQuery, } = useQueryBuilder(); + const { options } = useOptionsMenu({ + storageKey: LOCALSTORAGE.TRACES_LIST_OPTIONS, + dataSource: DataSource.TRACES, + aggregateOperator: 'noop', + initialOptions: { + selectColumns: defaultSelectedColumns, + }, + }); + const currentPanelType = useGetPanelTypesQueryParam(); const { handleExplorerTabChange } = useHandleExplorerTabChange(); @@ -101,6 +115,18 @@ function TracesExplorer(): JSX.Element { const { mutate: updateDashboard, isLoading } = useUpdateDashboard(); + const getUpdatedQueryForExport = (): Query => { + const updatedQuery = cloneDeep(currentQuery); + + set( + updatedQuery, + 'builder.queryData[0].selectColumns', + options.selectColumns, + ); + + return updatedQuery; + }; + const handleExport = useCallback( (dashboard: Dashboard | null): void => { if (!dashboard || !panelType) return; @@ -111,11 +137,17 @@ function TracesExplorer(): JSX.Element { const widgetId = v4(); + const query = + panelType === PANEL_TYPES.LIST + ? getUpdatedQueryForExport() + : exportDefaultQuery; + const updatedDashboard = addEmptyWidgetInDashboardJSONWithQuery( dashboard, - exportDefaultQuery, + query, widgetId, panelTypeParam, + options.selectColumns, ); updateDashboard(updatedDashboard, { @@ -144,7 +176,7 @@ function TracesExplorer(): JSX.Element { return; } const dashboardEditView = generateExportToDashboardLink({ - query: exportDefaultQuery, + query, panelType: panelTypeParam, dashboardId: data.payload?.uuid || '', widgetId, @@ -161,6 +193,7 @@ function TracesExplorer(): JSX.Element { }, }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [exportDefaultQuery, notifications, panelType, updateDashboard], ); diff --git a/frontend/src/types/api/queryBuilder/queryBuilderData.ts b/frontend/src/types/api/queryBuilder/queryBuilderData.ts index 6a54254617..832bd09411 100644 --- a/frontend/src/types/api/queryBuilder/queryBuilderData.ts +++ b/frontend/src/types/api/queryBuilder/queryBuilderData.ts @@ -74,6 +74,7 @@ export type IBuilderQuery = { legend: string; pageSize?: number; offset?: number; + selectColumns?: BaseAutocompleteData[]; }; export interface IClickHouseQuery { From 67965c8e4d1ebc16090d10efdd1f6da52e66c0e1 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Thu, 20 Jun 2024 17:21:04 +0530 Subject: [PATCH 021/281] fix: dependent variable panel not updating (#5283) * fix: dependent variable panel not updating * fix: build issues --- .../DashboardVariableSelection.tsx | 1 + .../DashboardVariablesSelection/VariableItem.test.tsx | 6 ++++++ .../DashboardVariablesSelection/VariableItem.tsx | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.tsx index 0572196fbf..813185e0b6 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.tsx +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/DashboardVariableSelection.tsx @@ -138,6 +138,7 @@ function DashboardVariableSelection(): JSX.Element | null { }} onValueUpdate={onValueUpdate} variablesToGetUpdated={variablesToGetUpdated} + setVariablesToGetUpdated={setVariablesToGetUpdated} /> ))} diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx index 14f20347d0..0c8fbd51ae 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.test.tsx @@ -54,6 +54,7 @@ describe('VariableItem', () => { existingVariables={{}} onValueUpdate={mockOnValueUpdate} variablesToGetUpdated={[]} + setVariablesToGetUpdated={(): void => {}} /> , ); @@ -69,6 +70,7 @@ describe('VariableItem', () => { existingVariables={{}} onValueUpdate={mockOnValueUpdate} variablesToGetUpdated={[]} + setVariablesToGetUpdated={(): void => {}} /> , ); @@ -83,6 +85,7 @@ describe('VariableItem', () => { existingVariables={{}} onValueUpdate={mockOnValueUpdate} variablesToGetUpdated={[]} + setVariablesToGetUpdated={(): void => {}} /> , ); @@ -111,6 +114,7 @@ describe('VariableItem', () => { existingVariables={{}} onValueUpdate={mockOnValueUpdate} variablesToGetUpdated={[]} + setVariablesToGetUpdated={(): void => {}} /> , ); @@ -134,6 +138,7 @@ describe('VariableItem', () => { existingVariables={{}} onValueUpdate={mockOnValueUpdate} variablesToGetUpdated={[]} + setVariablesToGetUpdated={(): void => {}} /> , ); @@ -149,6 +154,7 @@ describe('VariableItem', () => { existingVariables={{}} onValueUpdate={mockOnValueUpdate} variablesToGetUpdated={[]} + setVariablesToGetUpdated={(): void => {}} /> , ); diff --git a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx index e0393ea163..baa8228b3c 100644 --- a/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx +++ b/frontend/src/container/NewDashboard/DashboardVariablesSelection/VariableItem.tsx @@ -52,6 +52,7 @@ interface VariableItemProps { allSelected: boolean, ) => void; variablesToGetUpdated: string[]; + setVariablesToGetUpdated: React.Dispatch>; } const getSelectValue = ( @@ -73,6 +74,7 @@ function VariableItem({ existingVariables, onValueUpdate, variablesToGetUpdated, + setVariablesToGetUpdated, }: VariableItemProps): JSX.Element { const [optionsData, setOptionsData] = useState<(string | number | boolean)[]>( [], @@ -171,6 +173,10 @@ function VariableItem({ } setOptionsData(newOptionsData); + } else { + setVariablesToGetUpdated((prev) => + prev.filter((name) => name !== variableData.name), + ); } } } catch (e) { From 6ee9705599b5b1766ed903e8badd082be4b36a85 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 20 Jun 2024 18:33:45 +0530 Subject: [PATCH 022/281] chore: bump SigNoz/signoz-otel-collector and SigNoz/prometheus (#5294) --- go.mod | 176 ++-- go.sum | 755 ++++++------------ .../app/clickhouseReader/reader.go | 62 -- pkg/query-service/app/integrations/builtin.go | 4 +- .../app/logparsingpipeline/preview.go | 11 +- .../collectorsimulator/collectorsimulator.go | 8 +- .../inmemoryexporter/factory.go | 2 +- .../inmemoryreceiver/factory.go | 2 +- pkg/query-service/collectorsimulator/logs.go | 11 +- 9 files changed, 372 insertions(+), 659 deletions(-) diff --git a/go.mod b/go.mod index 42b5e7a534..4a52cead08 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.20.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd - github.com/SigNoz/signoz-otel-collector v0.88.24 + github.com/SigNoz/signoz-otel-collector v0.102.0 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 github.com/auth0/go-jwt-middleware v1.0.1 github.com/cespare/xxhash v1.1.0 - github.com/coreos/go-oidc/v3 v3.4.0 + github.com/coreos/go-oidc/v3 v3.10.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 @@ -21,7 +21,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.1 - github.com/gorilla/mux v1.8.0 + github.com/gorilla/mux v1.8.1 github.com/gosimple/slug v1.10.0 github.com/jmoiron/sqlx v1.3.4 github.com/json-iterator/go v1.1.12 @@ -29,18 +29,18 @@ require ( github.com/mailru/easyjson v0.7.7 github.com/mattn/go-sqlite3 v2.0.3+incompatible github.com/minio/minio-go/v6 v6.0.57 - github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 + github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c 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.88.0 - github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.102.0 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.102.0 github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f - github.com/prometheus/common v0.44.0 + github.com/prometheus/common v0.54.0 github.com/prometheus/prometheus v2.5.0+incompatible - github.com/rs/cors v1.10.1 + github.com/rs/cors v1.11.0 github.com/russellhaering/gosaml2 v0.9.0 github.com/russellhaering/goxmldsig v1.2.0 github.com/samber/lo v1.38.1 @@ -49,93 +49,90 @@ require ( github.com/soheilhy/cmux v0.1.5 github.com/srikanthccv/ClickHouse-go-mock v0.7.0 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/collector/component v0.88.0 - go.opentelemetry.io/collector/confmap v0.88.0 - go.opentelemetry.io/collector/connector v0.88.0 - go.opentelemetry.io/collector/consumer v0.88.0 - go.opentelemetry.io/collector/exporter v0.88.0 - go.opentelemetry.io/collector/extension v0.88.0 - go.opentelemetry.io/collector/otelcol v0.88.0 - go.opentelemetry.io/collector/pdata v1.3.0 - go.opentelemetry.io/collector/processor v0.88.0 - go.opentelemetry.io/collector/receiver v0.88.0 - go.opentelemetry.io/collector/service v0.88.0 - go.opentelemetry.io/otel v1.24.0 - go.opentelemetry.io/otel/sdk v1.23.1 + go.opentelemetry.io/collector/component v0.102.1 + go.opentelemetry.io/collector/confmap v0.102.1 + go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.0 + go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.0 + go.opentelemetry.io/collector/connector v0.102.0 + go.opentelemetry.io/collector/consumer v0.102.1 + go.opentelemetry.io/collector/exporter v0.102.0 + go.opentelemetry.io/collector/extension v0.102.1 + go.opentelemetry.io/collector/otelcol v0.102.0 + go.opentelemetry.io/collector/pdata v1.9.0 + go.opentelemetry.io/collector/processor v0.102.0 + go.opentelemetry.io/collector/receiver v0.102.0 + go.opentelemetry.io/collector/service v0.102.0 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.24.0 - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/net v0.26.0 - golang.org/x/oauth2 v0.16.0 + golang.org/x/oauth2 v0.21.0 golang.org/x/text v0.16.0 - google.golang.org/grpc v1.62.0 - google.golang.org/protobuf v1.33.0 + google.golang.org/grpc v1.64.0 + google.golang.org/protobuf v1.34.1 gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.28.2 + k8s.io/apimachinery v0.29.3 ) require ( - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect -) - -require ( - contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect github.com/ClickHouse/ch-go v0.61.3 // indirect - github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect github.com/andybalholm/brotli v1.1.0 // indirect - github.com/aws/aws-sdk-go v1.45.26 // indirect + github.com/aws/aws-sdk-go v1.53.16 // 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 - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // 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/form3tech-oss/jwt-go v3.2.5+incompatible // 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.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/gosimple/unidecode v1.0.0 // indirect - github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect + github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect 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.7 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid v1.2.3 // indirect - github.com/knadh/koanf/v2 v2.0.1 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/leodido/go-syslog/v4 v4.1.0 // indirect + github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b // indirect + github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/minio/md5-simd v1.1.0 // indirect github.com/minio/sha256-simd v0.1.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -145,68 +142,67 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/oklog/run v1.1.0 // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/onsi/gomega v1.19.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.102.0 // indirect github.com/paulmach/orb v0.11.1 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/prometheus/client_golang v1.17.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect + github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect - github.com/prometheus/statsd_exporter v0.22.7 // indirect + github.com/prometheus/procfs v0.15.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/backo-go v1.0.1 // indirect - github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smarty/assertions v1.15.0 // indirect - github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/valyala/fastjson v1.6.4 // indirect github.com/vjeantet/grok v1.0.1 // indirect github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/collector v0.88.0 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.88.0 // indirect - go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017 // indirect - go.opentelemetry.io/collector/semconv v0.88.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.20.0 // indirect - go.opentelemetry.io/otel/bridge/opencensus v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/collector v0.102.1 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.102.1 // indirect + go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.0 // indirect + go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.0 // indirect + go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.0 // indirect + go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.0 // indirect + go.opentelemetry.io/collector/featuregate v1.9.0 // indirect + go.opentelemetry.io/collector/semconv v0.102.0 // indirect + go.opentelemetry.io/contrib/config v0.7.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.27.0 // indirect + go.opentelemetry.io/otel/bridge/opencensus v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/goleak v1.3.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect - golang.org/x/time v0.3.0 // indirect - gonum.org/v1/gonum v0.14.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect + golang.org/x/time v0.5.0 // indirect + gonum.org/v1/gonum v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect + k8s.io/client-go v0.29.3 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect ) -replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.11.0 +replace github.com/prometheus/prometheus => github.com/SigNoz/prometheus v1.11.1 diff --git a/go.sum b/go.sum index d755ee9438..cb3f0f902e 100644 --- a/go.sum +++ b/go.sum @@ -13,36 +13,14 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -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 v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -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 v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= 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/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -52,34 +30,20 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= -contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= 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/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo= github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= -github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +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-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/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -88,6 +52,8 @@ github.com/ClickHouse/ch-go v0.61.3 h1:MmBwUhXrAOBZK7n/sWBzq6FdIQ01cuF2SaaO8KlDR github.com/ClickHouse/ch-go v0.61.3/go.mod h1:1PqXjMz/7S1ZUaKvwPA3i35W2bz2mAMFeCi6DIXgGwQ= github.com/ClickHouse/clickhouse-go/v2 v2.20.0 h1:bvlLQ31XJfl7MxIqAq2l1G6JhHYzqEXdvfpMeU6bkKc= github.com/ClickHouse/clickhouse-go/v2 v2.20.0/go.mod h1:VQfyA+tCwCRw2G7ogfY8V0fq/r0yJWzy8UDrjiP/Lbs= +github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU= +github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= @@ -96,10 +62,10 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkbj57eGXx8H3ZJ4zhmQXBnrW523ktj8= github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc= -github.com/SigNoz/prometheus v1.11.0 h1:toX7fU2wqY1TnzvPzDglIYx6OxpqrZ0NNlM/H5S5+u8= -github.com/SigNoz/prometheus v1.11.0/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww= -github.com/SigNoz/signoz-otel-collector v0.88.24 h1:6ESLmQtYPHmik9ZZFSJSbfuj4VQ1/0IC3v1qV9hm5Nk= -github.com/SigNoz/signoz-otel-collector v0.88.24/go.mod h1:sT1EM9PFDaOJLbAz5npWpgXK6OhpWJ9PpSwyhHWs9rU= +github.com/SigNoz/prometheus v1.11.1 h1:roM8ugYf4UxaeKKujEeBvoX7ybq3IrS+TB26KiRtIJg= +github.com/SigNoz/prometheus v1.11.1/go.mod h1:uv4mQwZQtx7y4GQ6EdHOi8Wsk07uHNn2XHd1zM85m6I= +github.com/SigNoz/signoz-otel-collector v0.102.0 h1:v6ap+gdvrKklMwU+M9FJgrn28vN0YxrINl3kvdcLonA= +github.com/SigNoz/signoz-otel-collector v0.102.0/go.mod h1:kCx5BfzDujq6C0+kotiqLp5COG2ut4Cb039+55rbWE0= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= @@ -111,8 +77,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs= +github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -127,8 +93,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/auth0/go-jwt-middleware v1.0.1 h1:/fsQ4vRr4zod1wKReUH+0A3ySRjGiT9G34kypO/EKwI= github.com/auth0/go-jwt-middleware v1.0.1/go.mod h1:YSeUX3z6+TF2H+7padiEqNJ73Zy9vXW72U//IgN0BIM= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.45.26 h1:PJ2NJNY5N/yeobLYe1Y+xLdavBi67ZI8gvph6ftwVCg= -github.com/aws/aws-sdk-go v1.45.26/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.53.16 h1:8oZjKQO/ml1WLUZw5hvF7pvYjPf8o9f57Wldoy/q9Qc= +github.com/aws/aws-sdk-go v1.53.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= @@ -139,6 +105,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72H github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= +github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3/go.mod h1:CIWtjkly68+yqLPbvwwR/fjNJA/idrtULjZWh2v1ys0= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -148,36 +116,29 @@ 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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +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= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -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-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= -github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= -github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g= -github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw= +github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc= +github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= +github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= +github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -187,12 +148,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.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= -github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= +github.com/digitalocean/godo v1.117.0 h1:WVlTe09melDYTd7VCVyvHcNWbgB+uI1O115+5LOtdSw= +github.com/digitalocean/godo v1.117.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ= -github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v26.1.3+incompatible h1:lLCzRbrVZrljpVNobJu1J2FHk8V0s4BawoZippkc+xo= +github.com/docker/docker v26.1.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/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= @@ -207,32 +168,31 @@ github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -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.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= +github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= +github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.5+incompatible h1:/l4kBbb4/vGSsdtB5nUe8L7B9mImVMaBPw9L/0TBHU8= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= 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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-co-op/gocron v1.30.1 h1:tjWUvJl5KrcwpkEkSXFSQFr4F9h5SfV/m4+RX0cV2fs= github.com/go-co-op/gocron v1.30.1/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y= @@ -243,45 +203,46 @@ github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7F github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= 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.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 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.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 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.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= +github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4= +github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE= +github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redis/redismock/v8 v8.11.5 h1:RJFIiua58hrBrSpXhnGX3on79AU3S271H4ZhRI1wyVo= github.com/go-redis/redismock/v8 v8.11.5/go.mod h1:UaAU9dEe1C+eGr+FHV5prCWIt0hafyPWbGMEWE0UWdA= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g= +github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -290,13 +251,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 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/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= 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= @@ -309,8 +266,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -326,12 +281,10 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -351,7 +304,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -362,8 +314,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -371,29 +321,15 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -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/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/gophercloud/gophercloud v1.5.0 h1:cDN6XFCLKiiqvYpjQLq9AiM7RDRbIC9450WpPH+yvXo= -github.com/gophercloud/gophercloud v1.5.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.12.0 h1:Jrz16vPAL93l80q16fp8NplrTCp93y7rZh2P3Q4Yq7g= +github.com/gophercloud/gophercloud v1.12.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= @@ -401,23 +337,23 @@ github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfre github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.10.0 h1:3XbiQua1IpCdrvuntWvGBxVm+K99wCSxJjlxkP49GGQ= github.com/gosimple/slug v1.10.0/go.mod h1:MICb3w495l9KNdZm+Xn5b6T2Hn831f9DMxiJ1r+bAjw= github.com/gosimple/unidecode v1.0.0 h1:kPdvM+qy0tnk4/BrnkrbdJ82xe88xn7c9hcaipDz4dQ= github.com/gosimple/unidecode v1.0.0/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= -github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= -github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= +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.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= -github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= -github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= +github.com/hashicorp/consul/api v1.29.1 h1:UEwOjYJrd3lG1x5w7HxDRMGiAUPrb3f103EoeKuuEcc= +github.com/hashicorp/consul/api v1.29.1/go.mod h1:lumfRkY/coLuqMICkI7Fh3ylMG31mQSRZyef2c5YvJI= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= @@ -431,8 +367,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -454,6 +390,8 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= @@ -463,8 +401,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e h1:sr4lujmn9heD030xx/Pd4B/JSmvRhFzuotNXaaV0WLs= -github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= +github.com/hashicorp/nomad/api v0.0.0-20240604134157-e73d8bb1140d h1:KHq+mAzWSkumj4PDoXc5VZbycPGcmYu8tohgVLQ6SIc= +github.com/hashicorp/nomad/api v0.0.0-20240604134157-e73d8bb1140d/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= @@ -472,8 +410,8 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hetznercloud/hcloud-go/v2 v2.0.0 h1:Sg1DJ+MAKvbYAqaBaq9tPbwXBS2ckPIaMtVdUjKu+4g= -github.com/hetznercloud/hcloud-go/v2 v2.0.0/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q= +github.com/hetznercloud/hcloud-go/v2 v2.9.0 h1:s0N6R7Zoi2DPfMtUF5o9VeUBzTtHVY6MIkHOQnfu/AY= +github.com/hetznercloud/hcloud-go/v2 v2.9.0/go.mod h1:qtW/TuU7Bs16ibXl/ktJarWqU2LwHr7eGlwoilHxtgg= github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= @@ -483,10 +421,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6 h1:s9ZL6ZhFF8y6ebnm1FLvobkzoIu5xwDQUcRPk/IEhpM= -github.com/influxdata/go-syslog/v3 v3.0.1-0.20210608084020-ac565dc76ba6/go.mod h1:aXdIdfn2OcGnMhOTojXmwZqXKgC3MU5riiNvzwwG9OY= -github.com/ionos-cloud/sdk-go/v6 v6.1.8 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0= -github.com/ionos-cloud/sdk-go/v6 v6.1.8/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/ionos-cloud/sdk-go/v6 v6.1.11 h1:J/uRN4UWO3wCyGOeDdMKv8LWRzKu6UIkLEaes38Kzh8= +github.com/ionos-cloud/sdk-go/v6 v6.1.11/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -517,14 +453,14 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= -github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g= -github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -542,15 +478,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 h1:bCiVCRCs1Heq84lurVinUPy19keqGEe4jh5vtK37jcg= -github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= +github.com/leodido/go-syslog/v4 v4.1.0 h1:Wsl194qyWXr7V6DrGWC3xmxA9Ra6XgWO+toNt2fmCaI= +github.com/leodido/go-syslog/v4 v4.1.0/go.mod h1:eJ8rUfDN5OS6dOkCOBYlg2a+hbAg6pJa99QXXgMrd98= +github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b h1:11UHH39z1RhZ5dc4y4r/4koJo6IYFgTRMe/LlwRTEw0= +github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= 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.19.0 h1:n4WJrcr9+30e9JGZ6DI0nZbm5SdAj1kSwvvt/998YUw= -github.com/linode/linodego v1.19.0/go.mod h1:XZFR+yJ9mm2kwf6itZ6SCpu+6w3KnIevV0Uu5HNWJgQ= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/linode/linodego v1.35.0 h1:rIhUeCHBLEDlkoRnOTwzSGzljQ3ksXwLxacmXnrV+Do= +github.com/linode/linodego v1.35.0/go.mod h1:JxuhOEAMfSxun6RU5/MgTKH2GGTmFrhKRj3wL1NFin0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -565,18 +504,16 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= +github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/minio-go/v6 v6.0.57 h1:ixPkbKkyD7IhnluRgQpGSpHdpvNVaW6OD5R9IAO/9Tw= @@ -596,11 +533,13 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4 h1:BpfhmLKZf+SjVanKKhCgf3bg+511DmU9eDQTen7LLbY= -github.com/mitchellh/mapstructure v1.5.1-0.20220423185008-bf980b35cac4/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -635,30 +574,32 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g= github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.88.0 h1:9gjzrpUlzGC5BebgO1cxb/9KQ9yuIIE6B+6wLySKVCQ= -github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.88.0/go.mod h1:GXfK9q6RosmltLUcOdrQMS3hF1RYuwIgFTIa4RRR5J4= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0 h1:2HoGcjmHHIDMafd3Uj3flQJrV8TC2FAnUiTKD8FH0G8= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0/go.mod h1:JvXKcDtcOQRkz/Sw1m27K4QA3OwMbUvifoeEX2NQC6k= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.88.0 h1:jqdkgfHXcjvk6L2CyTUv3Rn+whX3TfFWd0Mz4QNAV1c= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.88.0/go.mod h1:5QXLdN4gdjAEcMHNEK/RrDdp+FObca0bS4/pRauyZs8= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.88.0 h1:S1FEVDH5GEMZQuHg8jfv47lCHHDFVjZBpO/Yrb/vKpE= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.88.0/go.mod h1:IJqzjDv6ZFeu7cYGCUzQ5/3CuTPVIo3UAGK3o2jK/Sw= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.88.0 h1:Ezi3FyxGbetZ12yAinyif/aabc9J7VyFdOvdufPCaUU= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.88.0/go.mod h1:6nO6NG5H5V5YGRp5onf9JnitXwhMfNXLSfZNyVwRPuw= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0 h1:meHyTMeWC3xYativnHwYdnT9XwHWtfjioPRqgzaDJXA= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.88.0/go.mod h1:Vhb+pyxTKFjAoLaaJCiYHbJS6o56vQEvnJDhh/ws6yY= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.102.0 h1:7QHxeMnKzMXMw9oh5lnOHakfPpGSglxiZfbYUn6l6yc= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.102.0/go.mod h1:BtKaHa1yDHfhM9qjGUHweb0HgqFGxFSM7AMzwLXVR98= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.102.0 h1:PNLVcz8kJLE9V5kGnbBh277Bvl4WwiVZ+NbFbOB80WY= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.102.0/go.mod h1:cBbjwd8m4rBVgCQksUbAVQX1EoM5IuCyNQw2mzvibEM= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.102.0 h1:qsM5HhWpAfIMg8LdO4u+CHofu4UuCuJwg/M+ySO9uZA= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.102.0/go.mod h1:wBJlGy9Wx6s7AxIMcSne2sGw73e5ZUy1AQ/duYwpFf8= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.102.0 h1:vJL6lDaeI3pVA7ADnWKD3HMpI80BSrZ2UnGc+qkwqoY= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.102.0/go.mod h1:xtE7tds5j8PtI/wMuGb+Em5K9rJH8hm6t28Qe4QrpoU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.102.0 h1:TvJYcU/DLRFCgHr7nT98k5D+qkZ4syKVxc8OJjv+K4c= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.102.0/go.mod h1:WzD3Ox7tywAQHknxAFpAC1oZJGItMp5mbvgUGjvzNY8= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.102.0 h1:J8GFYxKLWG1360XRukc1tY5K9BF80MFXcO91UpCMgcQ= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.102.0/go.mod h1:GNxigQNap2jyOEPdOedAKqCbh61y576ND4BKn/7i8xY= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.102.0 h1:XOoV42CE0BJUsKJQ7+Fie2jusw0MBzzOc79IoQONJAk= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/logstransformprocessor v0.102.0/go.mod h1:nCpPHY7XLM+zbJxKxP132IuV0xHCu5E6oa3ZLpmBPl4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= -github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM= -github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M= +github.com/ovh/go-ovh v1.5.1 h1:P8O+7H+NQuFK9P/j4sFW5C0fvSS2DnHYGPwdVCp45wI= +github.com/ovh/go-ovh v1.5.1/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= @@ -686,45 +627,36 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f h1:h0p1aZ9F5d6IXOygysob3g4B07b+HuVUQC0VJKD8wA4= github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8= +github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= -github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= -github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= +github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= +github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= 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= @@ -735,8 +667,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= -github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4PfRmVOIk= github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU= github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg= @@ -748,8 +680,8 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20 h1:a9hSJdJcd16e0HoMsnFvaHvxB3pxSD+SC7+CISp7xY0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 h1:yGAraK1uUjlhSXgNMIy8o/J4LFNcy7yeipBqt9N9mVg= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= @@ -757,8 +689,8 @@ github.com/segmentio/backo-go v1.0.1 h1:68RQccglxZeyURy93ASB/2kc9QudzgIDexJ927N+ github.com/segmentio/backo-go v1.0.1/go.mod h1:9/Rh6yILuLysoQnZ2oNooD2g7aBnvM7r/fNVxRNWfBc= 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/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= -github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -785,8 +717,8 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/srikanthccv/ClickHouse-go-mock v0.7.0 h1:XhRMX2663xkDGq3DYavw8m75O94s9u76hOIjo9QBl8c= @@ -810,7 +742,6 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -819,6 +750,8 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/vjeantet/grok v1.0.1 h1:2rhIR7J4gThTgcZ1m2JY4TrJZNgjn985U28kT2wQrJ4= github.com/vjeantet/grok v1.0.1/go.mod h1:ax1aAchzC6/QMXMcyzHQGZWaW1l195+uMYIkCWPCNIo= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= @@ -834,9 +767,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= @@ -846,83 +778,94 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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/collector v0.88.0 h1:I0lerJK1h88vk7enriSgLV+h7dM099G9FgwkfmIZaf0= -go.opentelemetry.io/collector v0.88.0/go.mod h1:we0quZ+4txHS3Sfb0VdjFv95KYLGmto4ZAThCHiYgGA= -go.opentelemetry.io/collector/component v0.88.0 h1:LU/1ov5D/O/gv9D2Uv88EjNKHn7DHcUCZn1qQsb/zgw= -go.opentelemetry.io/collector/component v0.88.0/go.mod h1:4utKxz4Lilym3SPxNXJHosdaTjT1aQxI+TCmnJO54pU= -go.opentelemetry.io/collector/config/confignet v0.88.0 h1:CbVZQpWC8Bm/BKo3x2mnQZVdQKClU0gCa6SVbRCc930= -go.opentelemetry.io/collector/config/confignet v0.88.0/go.mod h1:cpO8JYWGONaViOygKVw+Hd2UoBcn2cUiyi0WWeFTwJY= -go.opentelemetry.io/collector/config/configtelemetry v0.88.0 h1:54Z9uoSTpbkq3esDwHvJMChoUH8p/nfesG2xJTOXayY= -go.opentelemetry.io/collector/config/configtelemetry v0.88.0/go.mod h1:+LAXM5WFMW/UbTlAuSs6L/W72WC+q8TBJt/6z39FPOU= -go.opentelemetry.io/collector/confmap v0.88.0 h1:tOgY6NXMXAL2hz2+zVDQ0jvBlCUHprSf90bw5ktbdaI= -go.opentelemetry.io/collector/confmap v0.88.0/go.mod h1:CSJlMk1KRZloXAygpiPeCLpuQiLVDEZYbGsGHIKHeUg= -go.opentelemetry.io/collector/connector v0.88.0 h1:hUTSMexixSx4rWExBfr5p3YzDdj9a9+cUveaRK0EdnQ= -go.opentelemetry.io/collector/connector v0.88.0/go.mod h1:vkOHpyWNlHQVFHKUB4Dp1yYCIpAFnouZ2REupkzL/PU= -go.opentelemetry.io/collector/consumer v0.88.0 h1:l8Ty5UHhZ2U6WCp4yHt97uW6vN1vMP0JbFeQEaVnEgY= -go.opentelemetry.io/collector/consumer v0.88.0/go.mod h1:VVoafgyhjpO6fuJu12GqspmuLrn91JCOou0sOtb9GOg= -go.opentelemetry.io/collector/exporter v0.88.0 h1:bDXltsjQslhT7tlObQzKJiHuP5LDPeZHrkpUh4cT6Kk= -go.opentelemetry.io/collector/exporter v0.88.0/go.mod h1:0KQKlbUlYBwNJ9Dfapn6mRLhdhtM3tUlDGgN88oDVug= -go.opentelemetry.io/collector/extension v0.88.0 h1:/WH97pQYypL7ZC5OEccoE0gFs6fjBC/Uh9NuVEYEoZ0= -go.opentelemetry.io/collector/extension v0.88.0/go.mod h1:5wPlOyWtVJcZS9CMhFUnuRvNQ0XIoV/iUSaZWtCjoHA= -go.opentelemetry.io/collector/extension/zpagesextension v0.88.0 h1:cpkwzjhq6jfkVq3ltUl9wdb/8RrWbn0utHTCU3K5Mhc= -go.opentelemetry.io/collector/extension/zpagesextension v0.88.0/go.mod h1:8LPmV8UkQgDAfNaAizQqLzYnYibzQv81eBGKv0Mk6wU= -go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017 h1:DtJQalPXMWQqT6jd2LZ1oKrOfLJJRCi+rh2LKnkj4Zo= -go.opentelemetry.io/collector/featuregate v1.0.0-rcv0017/go.mod h1:fLmJMf1AoHttkF8p5oJAc4o5ZpHu8yO5XYJ7gbLCLzo= -go.opentelemetry.io/collector/otelcol v0.88.0 h1:f2eRVLJY66w9WFj5iT1Tg6Qxtlljagov9v8TPStuK2g= -go.opentelemetry.io/collector/otelcol v0.88.0/go.mod h1:F85TtMPt+ySe29HD6DOyvsMFCV3onaB3VJzky7qrtzQ= -go.opentelemetry.io/collector/pdata v1.3.0 h1:JRYN7tVHYFwmtQhIYbxWeiKSa2L1nCohyAs8sYqKFZo= -go.opentelemetry.io/collector/pdata v1.3.0/go.mod h1:t7W0Undtes53HODPdSujPLTnfSR5fzT+WpL+RTaaayo= -go.opentelemetry.io/collector/processor v0.88.0 h1:5BUZaH+RhTpgTVqBZCrBnN/vl0M1CtwQsZ8ek4iH1lc= -go.opentelemetry.io/collector/processor v0.88.0/go.mod h1:2T5KxgBQxXuuyMu9dh+PIBxQ/geCFYcdnjmlWZx8o3E= -go.opentelemetry.io/collector/receiver v0.88.0 h1:MPvVAFOfjl0+Ylka7so8QoK8T2Za2471rv5t3sqbbSY= -go.opentelemetry.io/collector/receiver v0.88.0/go.mod h1:MIZ6jPPZ+I8XibZm6I3RAn9h7Wcy2ZJsPmtXd2BLr60= -go.opentelemetry.io/collector/semconv v0.88.0 h1:8TVP4hYaUC87S6CCLKNoSxsUE0ChldE4vqotvNHHUnE= -go.opentelemetry.io/collector/semconv v0.88.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw= -go.opentelemetry.io/collector/service v0.88.0 h1:KSue2w94Tb2xjenlm+SC2y2g87hdhFJeHMT9pEshKAE= -go.opentelemetry.io/collector/service v0.88.0/go.mod h1:+Fov4arJzWl8SBuMonvM7gOrfK72G+d+2WotRjR5c2I= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0 h1:Yty9Vs4F3D6/liF1o6FNt0PvN85h/BJJ6DQKJ3nrcM0= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0/go.mod h1:On4VgbkqYL18kbJlWsa18+cMNe6rYpBnPi1ARI/BrsU= -go.opentelemetry.io/contrib/zpages v0.45.0 h1:jIwHHGoWzJoZdbIUtWdErjL85Gni6BignnAFqDtMRL4= -go.opentelemetry.io/contrib/zpages v0.45.0/go.mod h1:4mIdA5hqH6hEx9sZgV50qKfQO8aIYolUZboHmz+G7vw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/bridge/opencensus v0.42.0 h1:QvC+bcZkWMphWPiVqRQygMj6M0/3TOuJEO+erRA7kI8= -go.opentelemetry.io/otel/bridge/opencensus v0.42.0/go.mod h1:XJojP7g5DqYdiyArix/H9i1XzPPlIUc9dGLKtF9copI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 h1:ZtfnDL+tUrs1F0Pzfwbg2d59Gru9NCH3bgSHBM6LDwU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 h1:NmnYCiR0qNufkldjVvyQfZTHSdzeHoZ41zggMsdMcLM= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0/go.mod h1:UVAO61+umUsHLtYb8KXXRoHtxUkdOPkYidzW3gipRLQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 h1:wNMDy/LVGLj2h3p6zg4d0gypKfWKSWI14E1C4smOgl8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0/go.mod h1:YfbDdXAAkemWJK3H/DshvlrxqFB2rtW4rY6ky/3x/H0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/exporters/prometheus v0.42.0 h1:jwV9iQdvp38fxXi8ZC+lNpxjK16MRcZlpDYvbuO1FiA= -go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0/go.mod h1:/MtYTE1SfC2QIcE0bDot6fIX+h+WvXjgTqgn9P0LNPE= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= -go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= -go.opentelemetry.io/otel/sdk/metric v1.19.0 h1:EJoTO5qysMsYCa+w4UghwFV/ptQgqSL/8Ni+hx+8i1k= -go.opentelemetry.io/otel/sdk/metric v1.19.0/go.mod h1:XjG0jQyFJrv2PbMvwND7LwCEhsJzCzV5210euduKcKY= -go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= -go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/collector v0.102.1 h1:M/ciCcReQsSDYG9bJ2Qwqk7pQILDJ2bM/l0MdeCAvJE= +go.opentelemetry.io/collector v0.102.1/go.mod h1:yF1lDRgL/Eksb4/LUnkMjvLvHHpi6wqBVlzp+dACnPM= +go.opentelemetry.io/collector/component v0.102.1 h1:66z+LN5dVCXhvuVKD1b56/3cYLK+mtYSLIwlskYA9IQ= +go.opentelemetry.io/collector/component v0.102.1/go.mod h1:XfkiSeImKYaewT2DavA80l0VZ3JjvGndZ8ayPXfp8d0= +go.opentelemetry.io/collector/config/confignet v0.102.1 h1:nSiAFQMzNCO4sDBztUxY73qFw4Vh0hVePq8+3wXUHtU= +go.opentelemetry.io/collector/config/confignet v0.102.1/go.mod h1:pfOrCTfSZEB6H2rKtx41/3RN4dKs+X2EKQbw3MGRh0E= +go.opentelemetry.io/collector/config/configtelemetry v0.102.1 h1:f/CYcrOkaHd+COIJ2lWnEgBCHfhEycpbow4ZhrGwAlA= +go.opentelemetry.io/collector/config/configtelemetry v0.102.1/go.mod h1:WxWKNVAQJg/Io1nA3xLgn/DWLE/W1QOB2+/Js3ACi40= +go.opentelemetry.io/collector/confmap v0.102.1 h1:wZuH+d/P11Suz8wbp+xQCJ0BPE9m5pybtUe74c+rU7E= +go.opentelemetry.io/collector/confmap v0.102.1/go.mod h1:KgpS7UxH5rkd69CzAzlY2I1heH8Z7eNCZlHmwQBMxNg= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.0 h1:8Ne/oL6M4kMWK0P3FKV9EduQa+1UOGyVAnFHfSo4c1A= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.102.0/go.mod h1:Xj4Ld/RriP/Bj+5oPpaYJsLNs2wWRDN2TvzX3Lbi6+M= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.0 h1:o1iKqN+oM+TZqHoGdnKw1Am2BQlIGCYbxCRzU8T3jbM= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.102.0/go.mod h1:JpCemLtL/sXQ2Rk3Bx7OPPA7Qt/9NVH91q0bR655gSo= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.0 h1:SfASE6lxXjrmYj/UibcWdOiFWvRG0zt4hJgenloEhlY= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.102.0/go.mod h1:+Ku0Fvdb5f6e9UkfqJXAV5FaUJVxxg6Ykfx7Js8y+V4= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.0 h1:GwJQTXs7pYPUv/fVf+0nBgsJdlrTuY/PfwQ/TRA/sIk= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.102.0/go.mod h1:PGE3DcRgqYWWC2cq2hYZoET1d3Q8JZyPNmgvqXPFWEU= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.0 h1:zdYZLiHHtDf4Kk9WU7mW9dW6WAXtBF54I5jmTMRJtiw= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.102.0/go.mod h1:yFsgUM0PbUJkPlbpJfOG6da+YiF0Z80tv7YcnL3qwv4= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.0 h1:Y4H+GaCQl2URp9mEJMV5CYOhw+erONqNyvtFoKQfIoA= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.102.0/go.mod h1:g1RjfVD0gHAf/mPOIs3zBoKBeuDsN+rc5x0lZtgA8tI= +go.opentelemetry.io/collector/connector v0.102.0 h1:IvAsVfYRxP0ajmKbUovF8qugkcUtHq6RuYNtjcMa63E= +go.opentelemetry.io/collector/connector v0.102.0/go.mod h1:f4M7wZ/9+XtgTE0fivBFH3WlwntaEd0qFFA0giFkdnY= +go.opentelemetry.io/collector/consumer v0.102.1 h1:0CkgHhxwx4lI/m+hWjh607xyjooW5CObZ8hFQy5vvo0= +go.opentelemetry.io/collector/consumer v0.102.1/go.mod h1:HoXqmrRV13jLnP3/Gg3fYNdRkDPoO7UW58hKiLyFF60= +go.opentelemetry.io/collector/exporter v0.102.0 h1:hvyTyyGVx5FIikA6HzlTeZHILJ62hrIBsoZCoKlpX3A= +go.opentelemetry.io/collector/exporter v0.102.0/go.mod h1:JWE+1qNoSVBSelzhI3Iao/VkYVssY+sXaTPK1JOmpQ0= +go.opentelemetry.io/collector/extension v0.102.1 h1:gAvE3w15q+Vv0Tj100jzcDpeMTyc8dAiemHRtJbspLg= +go.opentelemetry.io/collector/extension v0.102.1/go.mod h1:XBxUOXjZpwYLZYOK5u3GWlbBTOKmzStY5eU1R/aXkIo= +go.opentelemetry.io/collector/extension/zpagesextension v0.102.0 h1:BPq98py8nwzaV7KAsxt4ZZAF9LiSRu7ZjHNGavFNyKo= +go.opentelemetry.io/collector/extension/zpagesextension v0.102.0/go.mod h1:P86HW3x3epDS5F4yP0gAvsZiw4xxP1OupTEx2o6UqjY= +go.opentelemetry.io/collector/featuregate v1.9.0 h1:mC4/HnR5cx/kkG1RKOQAvHxxg5Ktmd9gpFdttPEXQtA= +go.opentelemetry.io/collector/featuregate v1.9.0/go.mod h1:PsOINaGgTiFc+Tzu2K/X2jP+Ngmlp7YKGV1XrnBkH7U= +go.opentelemetry.io/collector/otelcol v0.102.0 h1:HuE+ok4iUjOrmYhQBSWpG5kBTVhcA24ljPL4pBERZ5E= +go.opentelemetry.io/collector/otelcol v0.102.0/go.mod h1:w8pCRu2nM/jAkLlEAS6ccKtJv5ylUQe6Ugl98zzTfyE= +go.opentelemetry.io/collector/pdata v1.9.0 h1:qyXe3HEVYYxerIYu0rzgo1Tx2d1Zs6iF+TCckbHLFOw= +go.opentelemetry.io/collector/pdata v1.9.0/go.mod h1:vk7LrfpyVpGZrRWcpjyy0DDZzL3SZiYMQxfap25551w= +go.opentelemetry.io/collector/pdata/testdata v0.102.1 h1:S3idZaJxy8M7mCC4PG4EegmtiSaOuh6wXWatKIui8xU= +go.opentelemetry.io/collector/pdata/testdata v0.102.1/go.mod h1:JEoSJTMgeTKyGxoMRy48RMYyhkA5vCCq/abJq9B6vXs= +go.opentelemetry.io/collector/processor v0.102.0 h1:JsjTlpBRmoSYxcu3cAbKBchOmL6aNUxLa03ZkWIqZr8= +go.opentelemetry.io/collector/processor v0.102.0/go.mod h1:IaCSDcfy75uQTaOM+LgR1bMf/bUw2eFfzn20uvWYfLQ= +go.opentelemetry.io/collector/receiver v0.102.0 h1:8rHNjWjV90bL0dgvKVc/7D10NCbM7bXCiqpcLRz5jBI= +go.opentelemetry.io/collector/receiver v0.102.0/go.mod h1:bYDwYItMrj7Drx0Pn4wZQ8Ii67lp9Nta62gbau93FhA= +go.opentelemetry.io/collector/semconv v0.102.0 h1:VEOdog9IbSfaGR7yg4AVmT54MwHAgH9lzITH6C33uyc= +go.opentelemetry.io/collector/semconv v0.102.0/go.mod h1:yMVUCNoQPZVq/IPfrHrnntZTWsLf5YGZ7qwKulIl5hw= +go.opentelemetry.io/collector/service v0.102.0 h1:B5nfyQZF7eB/y+yucl9G/7VsusbXixYXWingXn7VszM= +go.opentelemetry.io/collector/service v0.102.0/go.mod h1:c+0n0DfQeCjgrdplNHYwYbG/5aupTZVYU/50nMQraoc= +go.opentelemetry.io/contrib/config v0.7.0 h1:b1rK5tGTuhhPirJiMxOcyQfZs76j2VapY6ODn3b2Dbs= +go.opentelemetry.io/contrib/config v0.7.0/go.mod h1:8tdiFd8N5etOi3XzBmAoMxplEzI3TcL8dU5rM5/xcOQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0 h1:9l89oX4ba9kHbBol3Xin3leYJ+252h0zszDtBwyKe2A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.52.0/go.mod h1:XLZfZboOJWHNKUv7eH0inh0E9VV6eWDFB/9yJyTLPp0= +go.opentelemetry.io/contrib/propagators/b3 v1.27.0 h1:IjgxbomVrV9za6bRi8fWCNXENs0co37SZedQilP2hm0= +go.opentelemetry.io/contrib/propagators/b3 v1.27.0/go.mod h1:Dv9obQz25lCisDvvs4dy28UPh974CxkahRDUPsY7y9E= +go.opentelemetry.io/contrib/zpages v0.52.0 h1:MPgkMy0Cp3O5EdfVXP0ss3ujhEibysTM4eszx7E7d+E= +go.opentelemetry.io/contrib/zpages v0.52.0/go.mod h1:fqG5AFdoYru3A3DnhibVuaaEfQV2WKxE7fYE1jgDRwk= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/bridge/opencensus v1.27.0 h1:ao9aGGHd+G4YfjBpGs6vbkvt5hoC67STlJA9fCnOAcs= +go.opentelemetry.io/otel/bridge/opencensus v1.27.0/go.mod h1:uRvWtAAXzyVOST0WMPX5JHGBaAvBws+2F8PcC5gMnTk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= +go.opentelemetry.io/otel/exporters/prometheus v0.49.0 h1:Er5I1g/YhfYv9Affk9nJLfH/+qCCVVg1f2R9AbJfqDQ= +go.opentelemetry.io/otel/exporters/prometheus v0.49.0/go.mod h1:KfQ1wpjf3zsHjzP149P4LyAwWRupc6c7t1ZJ9eXpKQM= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0 h1:/jlt1Y8gXWiHG9FBx6cJaIC5hYx5Fe64nC8w5Cylt/0= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.27.0/go.mod h1:bmToOGOBZ4hA9ghphIc1PAf66VA8KOtsuy3+ScStG20= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0 h1:/0YaXu3755A/cFbtXp+21lkXgI0QE5avTWA2HjU9/WE= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.27.0/go.mod h1:m7SFxp0/7IxmJPLIY3JhOcU9CoFzDaCPL6xxQIxhA+o= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= 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= @@ -943,7 +886,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= @@ -957,8 +899,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -971,7 +913,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -981,12 +922,9 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1020,29 +958,14 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1050,24 +973,9 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1079,8 +987,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1129,59 +1035,27 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1190,19 +1064,16 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/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= @@ -1246,31 +1117,17 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= -gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= +gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= +gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1287,38 +1144,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -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.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= 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= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1350,60 +1181,11 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -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-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= -google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= -google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= 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= @@ -1418,28 +1200,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -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.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= -google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1453,10 +1218,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 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.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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= @@ -1474,8 +1237,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/segmentio/analytics-go.v3 v3.1.0 h1:UzxH1uaGZRpMKDhJyBz0pexz6yUoBU3x8bJsRk/HV6U= gopkg.in/segmentio/analytics-go.v3 v3.1.0/go.mod h1:4QqqlTlSSpVlWA9/9nDcPw+FkM2yv1NQoYjUbL9/JAw= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1498,18 +1259,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= -k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= -k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= -k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= -k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= -k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= -k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= -k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= +k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= +k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 4d2b369956..320f7fe5b3 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -27,10 +27,8 @@ import ( "github.com/pkg/errors" "github.com/prometheus/common/promlog" "github.com/prometheus/prometheus/config" - "github.com/prometheus/prometheus/discovery" "github.com/prometheus/prometheus/promql" - "github.com/prometheus/prometheus/scrape" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/storage/remote" "github.com/prometheus/prometheus/util/stats" @@ -262,14 +260,8 @@ func (r *ClickHouseReader) Start(readerReady chan bool) { configFile: r.promConfigFile, } - // fanoutStorage := remoteStorage fanoutStorage := storage.NewFanout(logger, remoteStorage) - ctxScrape, cancelScrape := context.WithCancel(context.Background()) - discoveryManagerScrape := discovery.NewManager(ctxScrape, log.With(logger, "component", "discovery manager scrape"), discovery.Name("scrape")) - - scrapeManager := scrape.NewManager(nil, log.With(logger, "component", "scrape manager"), fanoutStorage) - opts := promql.EngineOpts{ Logger: log.With(logger, "component", "query engine"), Reg: nil, @@ -286,16 +278,6 @@ func (r *ClickHouseReader) Start(readerReady chan bool) { reloaders := []func(cfg *config.Config) error{ remoteStorage.ApplyConfig, - // The Scrape managers need to reload before the Discovery manager as - // they need to read the most updated config when receiving the new targets list. - scrapeManager.ApplyConfig, - func(cfg *config.Config) error { - c := make(map[string]discovery.Configs) - for _, v := range cfg.ScrapeConfigs { - c[v.JobName] = v.ServiceDiscoveryConfigs - } - return discoveryManagerScrape.ApplyConfig(c) - }, } // sync.Once is used to make sure we can close the channel at different execution stages(SIGTERM or when the config is loaded). @@ -315,55 +297,11 @@ func (r *ClickHouseReader) Start(readerReady chan bool) { } var g group.Group - { - // Scrape discovery manager. - g.Add( - func() error { - err := discoveryManagerScrape.Run() - level.Info(logger).Log("msg", "Scrape discovery manager stopped") - return err - }, - func(err error) { - level.Info(logger).Log("msg", "Stopping scrape discovery manager...") - cancelScrape() - }, - ) - } - { - // Scrape manager. - g.Add( - func() error { - // When the scrape manager receives a new targets list - // it needs to read a valid config for each job. - // It depends on the config being in sync with the discovery manager so - // we wait until the config is fully loaded. - <-reloadReady.C - - err := scrapeManager.Run(discoveryManagerScrape.SyncCh()) - level.Info(logger).Log("msg", "Scrape manager stopped") - return err - }, - func(err error) { - // Scrape manager needs to be stopped before closing the local TSDB - // so that it doesn't try to write samples to a closed storage. - level.Info(logger).Log("msg", "Stopping scrape manager...") - scrapeManager.Stop() - }, - ) - } { // Initial configuration loading. cancel := make(chan struct{}) g.Add( func() error { - // select { - // case <-dbOpen: - // break - // // In case a shutdown is initiated before the dbOpen is released - // case <-cancel: - // reloadReady.Close() - // return nil - // } var err error r.promConfig, err = reloadConfig(cfg.configFile, logger, reloaders...) if err != nil { diff --git a/pkg/query-service/app/integrations/builtin.go b/pkg/query-service/app/integrations/builtin.go index 00810cacc1..d8099633ab 100644 --- a/pkg/query-service/app/integrations/builtin.go +++ b/pkg/query-service/app/integrations/builtin.go @@ -27,8 +27,8 @@ func (bi *BuiltInIntegrations) list(ctx context.Context) ( []IntegrationDetails, *model.ApiError, ) { integrations := maps.Values(builtInIntegrations) - slices.SortFunc(integrations, func(i1, i2 IntegrationDetails) bool { - return i1.Id < i2.Id + slices.SortFunc(integrations, func(i1, i2 IntegrationDetails) int { + return strings.Compare(i1.Id, i2.Id) }) return integrations, nil } diff --git a/pkg/query-service/app/logparsingpipeline/preview.go b/pkg/query-service/app/logparsingpipeline/preview.go index 0eed21aa18..b37295eb96 100644 --- a/pkg/query-service/app/logparsingpipeline/preview.go +++ b/pkg/query-service/app/logparsingpipeline/preview.go @@ -3,6 +3,7 @@ package logparsingpipeline import ( "context" "sort" + "strings" "time" _ "github.com/SigNoz/signoz-otel-collector/pkg/parser/grok" @@ -90,7 +91,15 @@ func SimulatePipelinesProcessing( delete(sigLog.Attributes_int64, inputOrderAttribute) } - return outputSignozLogs, collectorErrs, nil + for _, log := range collectorErrs { + // if log is empty or log comes from featuregate.go, then remove it + if log == "" || strings.Contains(log, "featuregate.go") { + continue + } + collectorWarnAndErrorLogs = append(collectorWarnAndErrorLogs, log) + } + + return outputSignozLogs, collectorWarnAndErrorLogs, nil } // plog doesn't contain an ID field. diff --git a/pkg/query-service/collectorsimulator/collectorsimulator.go b/pkg/query-service/collectorsimulator/collectorsimulator.go index e45c2d168a..cf9e6cbafd 100644 --- a/pkg/query-service/collectorsimulator/collectorsimulator.go +++ b/pkg/query-service/collectorsimulator/collectorsimulator.go @@ -119,12 +119,12 @@ func NewCollectorSimulator( return nil, cleanupFn, model.InternalError(errors.Wrap(err, "could not close tmp simulation config file")) } - fp := fileprovider.New() + fp := fileprovider.NewFactory() confProvider, err := otelcol.NewConfigProvider(otelcol.ConfigProviderSettings{ ResolverSettings: confmap.ResolverSettings{ - URIs: []string{simulationConfigPath}, - Providers: map[string]confmap.Provider{fp.Scheme(): fp}, - Converters: []confmap.Converter{expandconverter.New()}, + URIs: []string{simulationConfigPath}, + ProviderFactories: []confmap.ProviderFactory{fp}, + ConverterFactories: []confmap.ConverterFactory{expandconverter.NewFactory()}, }, }) if err != nil { diff --git a/pkg/query-service/collectorsimulator/inmemoryexporter/factory.go b/pkg/query-service/collectorsimulator/inmemoryexporter/factory.go index 7752693060..6bcc3a1226 100644 --- a/pkg/query-service/collectorsimulator/inmemoryexporter/factory.go +++ b/pkg/query-service/collectorsimulator/inmemoryexporter/factory.go @@ -28,7 +28,7 @@ func createLogsExporter( func NewFactory() exporter.Factory { return exporter.NewFactory( - "memory", + component.MustNewType("memory"), createDefaultConfig, exporter.WithLogs(createLogsExporter, component.StabilityLevelBeta)) } diff --git a/pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go b/pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go index 9db222cc43..584fbb28fb 100644 --- a/pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go +++ b/pkg/query-service/collectorsimulator/inmemoryreceiver/factory.go @@ -35,7 +35,7 @@ func createLogsReceiver( // NewFactory creates a new OTLP receiver factory. func NewFactory() receiver.Factory { return receiver.NewFactory( - "memory", + component.MustNewType("memory"), createDefaultConfig, receiver.WithLogs(createLogsReceiver, component.StabilityLevelBeta)) } diff --git a/pkg/query-service/collectorsimulator/logs.go b/pkg/query-service/collectorsimulator/logs.go index dc84e6fe56..d1b4f01abb 100644 --- a/pkg/query-service/collectorsimulator/logs.go +++ b/pkg/query-service/collectorsimulator/logs.go @@ -3,6 +3,7 @@ package collectorsimulator import ( "context" "fmt" + "strings" "time" "github.com/pkg/errors" @@ -68,7 +69,15 @@ func SimulateLogsProcessing( ) } - return result, simulationErrs, nil + for _, log := range simulationErrs { + // if log is empty or log comes from featuregate.go, then remove it + if log == "" || strings.Contains(log, "featuregate.go") { + continue + } + collectorErrs = append(collectorErrs, log) + } + + return result, collectorErrs, nil } func SendLogsToSimulator( From adfeaaa1f0eab9bd6e8c02432c841e40d792095a Mon Sep 17 00:00:00 2001 From: Yunus M Date: Thu, 20 Jun 2024 18:34:05 +0530 Subject: [PATCH 023/281] feat: pass fill gaps to query range api (#5276) --- .../container/GridCardLayout/GridCard/index.tsx | 3 +++ .../container/NewWidget/LeftContainer/index.tsx | 8 +++++++- frontend/src/lib/dashboard/getQueryResults.ts | 1 + .../lib/dashboard/prepareQueryRangePayload.ts | 2 ++ .../src/lib/uPlotLib/utils/getUplotChartData.ts | 16 ++++------------ frontend/src/types/api/metrics/getQueryRange.ts | 1 + 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 79003af7b8..28c67ae92e 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -108,6 +108,7 @@ function GridCardGraph({ query: updatedQuery, globalSelectedInterval, variables: getDashboardVariables(variables), + fillGaps: widget.fillSpans, }; } updatedQuery.builder.queryData[0].pageSize = 10; @@ -122,6 +123,7 @@ function GridCardGraph({ limit: updatedQuery.builder.queryData[0].limit || 0, }, }, + fillGaps: widget.fillSpans, }; }); @@ -152,6 +154,7 @@ function GridCardGraph({ widget?.query, widget?.panelTypes, widget.timePreferance, + widget.fillSpans, requestData, ], retry(failureCount, error): boolean { diff --git a/frontend/src/container/NewWidget/LeftContainer/index.tsx b/frontend/src/container/NewWidget/LeftContainer/index.tsx index e7c149c246..0363b4c6f8 100644 --- a/frontend/src/container/NewWidget/LeftContainer/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/index.tsx @@ -72,10 +72,16 @@ function LeftContainer({ globalSelectedInterval, graphType: getGraphType(selectedGraph || selectedWidget.panelTypes), query: stagedQuery, + fillGaps: selectedWidget.fillSpans || false, })); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [stagedQuery, selectedTime, globalSelectedInterval]); + }, [ + stagedQuery, + selectedTime, + selectedWidget.fillSpans, + globalSelectedInterval, + ]); const queryResponse = useGetQueryRange( requestData, diff --git a/frontend/src/lib/dashboard/getQueryResults.ts b/frontend/src/lib/dashboard/getQueryResults.ts index e3f956c32a..7b7276c378 100644 --- a/frontend/src/lib/dashboard/getQueryResults.ts +++ b/frontend/src/lib/dashboard/getQueryResults.ts @@ -75,6 +75,7 @@ export interface GetQueryResultsProps { globalSelectedInterval: Time | TimeV2 | CustomTimeType; variables?: Record; params?: Record; + fillGaps?: boolean; tableParams?: { pagination?: Pagination; selectColumns?: any; diff --git a/frontend/src/lib/dashboard/prepareQueryRangePayload.ts b/frontend/src/lib/dashboard/prepareQueryRangePayload.ts index 0b918bcb14..244e096079 100644 --- a/frontend/src/lib/dashboard/prepareQueryRangePayload.ts +++ b/frontend/src/lib/dashboard/prepareQueryRangePayload.ts @@ -20,6 +20,7 @@ export const prepareQueryRangePayload = ({ tableParams, variables = {}, params = {}, + fillGaps = false, }: GetQueryResultsProps): PrepareQueryRangePayload => { let legendMap: Record = {}; const { allowSelectedIntervalForStepGen, ...restParams } = params; @@ -27,6 +28,7 @@ export const prepareQueryRangePayload = ({ const compositeQuery: QueryRangePayload['compositeQuery'] = { queryType: query.queryType, panelType: graphType, + fillGaps, }; switch (query.queryType) { diff --git a/frontend/src/lib/uPlotLib/utils/getUplotChartData.ts b/frontend/src/lib/uPlotLib/utils/getUplotChartData.ts index 0effbbe390..3e88f8769e 100644 --- a/frontend/src/lib/uPlotLib/utils/getUplotChartData.ts +++ b/frontend/src/lib/uPlotLib/utils/getUplotChartData.ts @@ -17,11 +17,7 @@ function getXAxisTimestamps(seriesList: QueryData[]): number[] { return timestampsArr.sort((a, b) => a - b); } -function fillMissingXAxisTimestamps( - timestampArr: number[], - data: any[], - fillSpans: boolean, -): any { +function fillMissingXAxisTimestamps(timestampArr: number[], data: any[]): any { // Generate a set of all timestamps in the range const allTimestampsSet = new Set(timestampArr); const processedData = JSON.parse(JSON.stringify(data)); @@ -35,14 +31,14 @@ function fillMissingXAxisTimestamps( ); missingTimestamps.forEach((timestamp) => { - const value = fillSpans ? 0 : null; + const value = null; entry.values.push([timestamp, value]); }); entry.values.forEach((v) => { if (Number.isNaN(v[1])) { - const replaceValue = fillSpans ? 0 : null; + const replaceValue = null; // eslint-disable-next-line no-param-reassign v[1] = replaceValue; } else if (v[1] !== null) { @@ -85,11 +81,7 @@ export const getUPlotChartData = ( ): any[] => { const seriesList = apiResponse?.data?.result || []; const timestampArr = getXAxisTimestamps(seriesList); - const yAxisValuesArr = fillMissingXAxisTimestamps( - timestampArr, - seriesList, - fillSpans || false, - ); + const yAxisValuesArr = fillMissingXAxisTimestamps(timestampArr, seriesList); return [ timestampArr, diff --git a/frontend/src/types/api/metrics/getQueryRange.ts b/frontend/src/types/api/metrics/getQueryRange.ts index b5dcba2d77..5409eba346 100644 --- a/frontend/src/types/api/metrics/getQueryRange.ts +++ b/frontend/src/types/api/metrics/getQueryRange.ts @@ -18,6 +18,7 @@ export type QueryRangePayload = { promQueries?: Record; queryType: EQueryType; panelType: PANEL_TYPES; + fillGaps?: boolean; }; end: number; start: number; From 82a079e68783915fb53ac63f8729dda676bfb3fa Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Thu, 20 Jun 2024 19:19:42 +0530 Subject: [PATCH 024/281] fix: move date time picker to click rather than hover (#5296) --- frontend/src/components/CustomTimePicker/CustomTimePicker.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx b/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx index 114db17924..a3bb980175 100644 --- a/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx +++ b/frontend/src/components/CustomTimePicker/CustomTimePicker.tsx @@ -287,7 +287,7 @@ function CustomTimePicker({ ) } arrow={false} - trigger="hover" + trigger="click" open={open} onOpenChange={handleOpenChange} style={{ From afcee9cd02cfc048f77bb94f8adb842d17b94aee Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Fri, 21 Jun 2024 11:46:04 +0530 Subject: [PATCH 025/281] refactor: add to query should not open log detail drawer (#4732) Co-authored-by: Rajat-Dabade --- frontend/src/components/Logs/AddToQueryHOC.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Logs/AddToQueryHOC.tsx b/frontend/src/components/Logs/AddToQueryHOC.tsx index 609840477b..8391a23b81 100644 --- a/frontend/src/components/Logs/AddToQueryHOC.tsx +++ b/frontend/src/components/Logs/AddToQueryHOC.tsx @@ -2,7 +2,7 @@ import './AddToQueryHOC.styles.scss'; import { Popover } from 'antd'; import { OPERATORS } from 'constants/queryBuilder'; -import { memo, ReactNode, useCallback, useMemo } from 'react'; +import { memo, MouseEvent, ReactNode, useMemo } from 'react'; function AddToQueryHOC({ fieldKey, @@ -10,9 +10,10 @@ function AddToQueryHOC({ onAddToQuery, children, }: AddToQueryHOCProps): JSX.Element { - const handleQueryAdd = useCallback(() => { + const handleQueryAdd = (event: MouseEvent): void => { + event.stopPropagation(); onAddToQuery(fieldKey, fieldValue, OPERATORS.IN); - }, [fieldKey, fieldValue, onAddToQuery]); + }; const popOverContent = useMemo(() => Add to query: {fieldKey}, [ fieldKey, From b59d9c7b907017ca54d15ab2ce4beebe36ed1356 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:55:27 +0530 Subject: [PATCH 026/281] fix: handled value as string from queryParams for trace filters (#5274) * fix: handled value as string from queryParams for trace filters * fix: added isArray check --- .../TracesExplorer/Filter/SectionContent.tsx | 20 ++++++----- .../TracesExplorer/Filter/filterUtils.ts | 35 ++++++++++++++----- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx b/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx index 05fc7bb1f3..4cefaaeca0 100644 --- a/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx @@ -4,7 +4,7 @@ import { Button, Card, Checkbox, Input, Tooltip } from 'antd'; import { CheckboxChangeEvent } from 'antd/es/checkbox'; import { ParaGraph } from 'container/Trace/Filters/Panel/PanelBody/Common/styles'; import useDebouncedFn from 'hooks/useDebouncedFunction'; -import { defaultTo, isEmpty } from 'lodash-es'; +import { isArray, isEmpty } from 'lodash-es'; import { ChangeEvent, Dispatch, @@ -17,6 +17,7 @@ import { import { addFilter, AllTraceFilterKeys, + convertToStringArr, FilterType, HandleRunProps, removeFilter, @@ -37,15 +38,14 @@ export function SectionBody(props: SectionBodyProps): JSX.Element { const [searchFilter, setSearchFilter] = useState(''); const [searchText, setSearchText] = useState(''); const [checkedItems, setCheckedItems] = useState( - defaultTo(selectedFilters?.[type]?.values as string[], []), + convertToStringArr(selectedFilters?.[type]?.values), ); const [results, setResults] = useState([]); const [isFetching, setFetching] = useState(false); useEffect( - () => - setCheckedItems(defaultTo(selectedFilters?.[type]?.values as string[], [])), + () => setCheckedItems(convertToStringArr(selectedFilters?.[type]?.values)), [selectedFilters, type], ); @@ -92,17 +92,21 @@ export function SectionBody(props: SectionBodyProps): JSX.Element { if (checked) { addFilter(type, newValue, setSelectedFilters, keys); setCheckedItems((prev) => { - if (!prev.includes(newValue)) { - prev.push(newValue); + const arr = prev || []; + if (isArray(arr) && !arr.includes(newValue)) { + arr.push(newValue); } - return prev; + return convertToStringArr(arr); }); } else if (checkedItems.length === 1) { handleRun({ clearByType: type }); setCheckedItems([]); } else { removeFilter(type, newValue, setSelectedFilters, keys); - setCheckedItems((prev) => prev.filter((item) => item !== newValue)); + setCheckedItems((prev) => { + const prevValue = convertToStringArr(prev); + return prevValue.filter((item) => item !== newValue); + }); } }; diff --git a/frontend/src/pages/TracesExplorer/Filter/filterUtils.ts b/frontend/src/pages/TracesExplorer/Filter/filterUtils.ts index 86b52fdbb0..88f604a0dc 100644 --- a/frontend/src/pages/TracesExplorer/Filter/filterUtils.ts +++ b/frontend/src/pages/TracesExplorer/Filter/filterUtils.ts @@ -1,6 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { getAttributesValues } from 'api/queryBuilder/getAttributesValues'; -import { isArray } from 'lodash-es'; import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { BaseAutocompleteData, @@ -41,6 +40,18 @@ export type FilterType = Record< { values: string[] | string; keys: BaseAutocompleteData } >; +export function convertToStringArr( + value: string | string[] | undefined, +): string[] { + if (value) { + if (typeof value === 'string') { + return [value]; + } + return value; + } + return []; +} + export const addFilter = ( filterType: AllTraceFilterKeys, value: string, @@ -62,28 +73,36 @@ export const addFilter = ( 'durationNano', ].includes(filterType); + // Convert value to string array + const valueArray = convertToStringArr(value); + // If previous filters are undefined, initialize them if (!prevFilters) { return ({ - [filterType]: { values: isDuration ? value : [value], keys }, + [filterType]: { values: isDuration ? value : valueArray, keys }, } as unknown) as FilterType; } + // If the filter type doesn't exist, initialize it if (!prevFilters[filterType]?.values.length) { return { ...prevFilters, - [filterType]: { values: isDuration ? value : [value], keys }, + [filterType]: { values: isDuration ? value : valueArray, keys }, }; } + // If the value already exists, don't add it again - if (prevFilters[filterType].values.includes(value)) { + if (convertToStringArr(prevFilters[filterType].values).includes(value)) { return prevFilters; } + // Otherwise, add the value to the existing array return { ...prevFilters, [filterType]: { - values: isDuration ? value : [...prevFilters[filterType].values, value], + values: isDuration + ? value + : [...convertToStringArr(prevFilters[filterType].values), value], keys, }, }; @@ -110,10 +129,8 @@ export const removeFilter = ( return prevFilters; } - const prevValue = prevFilters[filterType]?.values; - const updatedValues = !isArray(prevValue) - ? prevValue - : prevValue?.filter((item: any) => item !== value); + const prevValue = convertToStringArr(prevFilters[filterType]?.values); + const updatedValues = prevValue.filter((item: any) => item !== value); if (updatedValues.length === 0) { const { [filterType]: item, ...remainingFilters } = prevFilters; From 8ff392bc96c0de975d6df0d648a2bdc3608c3d9c Mon Sep 17 00:00:00 2001 From: CheetoDa <31571545+Calm-Rock@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:05:37 +0530 Subject: [PATCH 027/281] feat: azure monitoring docs (#5159) * feat: azure monitoring docs * chore: mapped paths * chore: fixed instructions * fix: added central collector steps * fix: handle default azure steps, card alignment and reload issues * fix: removed return true --------- Co-authored-by: YounixM --- frontend/public/Logos/azure-aks.svg | 1 + frontend/public/Logos/azure-app-service.svg | 1 + frontend/public/Logos/azure-blob-storage.svg | 2 + .../public/Logos/azure-container-apps.svg | 1 + frontend/public/Logos/azure-functions.svg | 1 + .../Logos/azure-sql-database-metrics.svg | 1 + frontend/public/Logos/azure-vm.svg | 1 + frontend/public/locales/en/titles.json | 1 + frontend/src/constants/routes.ts | 1 + frontend/src/container/AppLayout/index.tsx | 3 +- .../AKS/aks-installCentralCollector.md | 111 +++++++++++++++ .../Modules/AzureMonitoring/AKS/aks-logs.md | 8 ++ .../AzureMonitoring/AKS/aks-metrics.md | 8 ++ .../AzureMonitoring/AKS/aks-setupEventsHub.md | 40 ++++++ .../AzureMonitoring/AKS/aks-tracing.md | 16 +++ .../appService-installCentralCollector.md | 129 +++++++++++++++++ .../AppService/appService-logs.md | 33 +++++ .../AppService/appService-metrics.md | 25 ++++ .../AppService/appService-setupEventsHub.md | 54 +++++++ .../AppService/appService-tracing.md | 29 ++++ .../blobStorage-installCentralCollector.md | 129 +++++++++++++++++ .../BlobStorage/blobStorage-logs.md | 23 +++ .../BlobStorage/blobStorage-metrics.md | 28 ++++ .../BlobStorage/blobStorage-setupEventsHub.md | 54 +++++++ .../containerApps-installCentralCollector.md | 129 +++++++++++++++++ .../ContainerApps/containerApps-logs.md | 28 ++++ .../ContainerApps/containerApps-metrics.md | 27 ++++ .../containerApps-setupEventsHub.md | 54 +++++++ .../ContainerApps/containerApps-tracing.md | 29 ++++ .../functions-installCentralCollector.md | 129 +++++++++++++++++ .../Functions/functions-logs.md | 21 +++ .../Functions/functions-metrics.md | 28 ++++ .../Functions/functions-setupEventsHub.md | 54 +++++++ .../Functions/functions-tracing.md | 29 ++++ ...DatabaseMetrics-installCentralCollector.md | 129 +++++++++++++++++ .../sqlDatabaseMetrics-metrics.md | 31 ++++ .../sqlDatabaseMetrics-setupEventsHub.md | 54 +++++++ .../Vm/vm-hostmetrics-and-logs.md | 134 ++++++++++++++++++ .../Vm/vm-installCentralCollector.md | 129 +++++++++++++++++ .../AzureMonitoring/Vm/vm-setupEventsHub.md | 54 +++++++ .../Onboarding.styles.scss | 5 +- .../OnboardingContainer.tsx | 33 ++++- .../Steps/MarkdownStep/MarkdownStep.tsx | 3 + .../constants/azureMonitoringDocFilePaths.ts | 82 +++++++++++ .../constants/stepsConfig.tsx | 36 +++++ .../utils/dataSourceUtils.ts | 53 ++++++- .../OnboardingContainer/utils/getSteps.ts | 74 ++++++++++ .../TopNav/DateTimeSelection/config.ts | 1 + .../TopNav/DateTimeSelectionV2/config.ts | 1 + frontend/src/utils/permission/index.ts | 1 + 50 files changed, 2039 insertions(+), 9 deletions(-) create mode 100644 frontend/public/Logos/azure-aks.svg create mode 100644 frontend/public/Logos/azure-app-service.svg create mode 100644 frontend/public/Logos/azure-blob-storage.svg create mode 100644 frontend/public/Logos/azure-container-apps.svg create mode 100644 frontend/public/Logos/azure-functions.svg create mode 100644 frontend/public/Logos/azure-sql-database-metrics.svg create mode 100644 frontend/public/Logos/azure-vm.svg create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-logs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-metrics.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-setupEventsHub.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-tracing.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-installCentralCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-logs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-metrics.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-setupEventsHub.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-tracing.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-installCentralCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-logs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-metrics.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-setupEventsHub.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-installCentralCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-logs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-metrics.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-setupEventsHub.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-tracing.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-installCentralCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-logs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-metrics.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-setupEventsHub.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-tracing.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-installCentralCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-metrics.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-setupEventsHub.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-hostmetrics-and-logs.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-installCentralCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-setupEventsHub.md create mode 100644 frontend/src/container/OnboardingContainer/constants/azureMonitoringDocFilePaths.ts diff --git a/frontend/public/Logos/azure-aks.svg b/frontend/public/Logos/azure-aks.svg new file mode 100644 index 0000000000..d45672703d --- /dev/null +++ b/frontend/public/Logos/azure-aks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/azure-app-service.svg b/frontend/public/Logos/azure-app-service.svg new file mode 100644 index 0000000000..54051fc58f --- /dev/null +++ b/frontend/public/Logos/azure-app-service.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/azure-blob-storage.svg b/frontend/public/Logos/azure-blob-storage.svg new file mode 100644 index 0000000000..1650133096 --- /dev/null +++ b/frontend/public/Logos/azure-blob-storage.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/public/Logos/azure-container-apps.svg b/frontend/public/Logos/azure-container-apps.svg new file mode 100644 index 0000000000..3dd3d4db91 --- /dev/null +++ b/frontend/public/Logos/azure-container-apps.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/azure-functions.svg b/frontend/public/Logos/azure-functions.svg new file mode 100644 index 0000000000..9face30fb9 --- /dev/null +++ b/frontend/public/Logos/azure-functions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/azure-sql-database-metrics.svg b/frontend/public/Logos/azure-sql-database-metrics.svg new file mode 100644 index 0000000000..fed69970bb --- /dev/null +++ b/frontend/public/Logos/azure-sql-database-metrics.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/Logos/azure-vm.svg b/frontend/public/Logos/azure-vm.svg new file mode 100644 index 0000000000..bde2b81881 --- /dev/null +++ b/frontend/public/Logos/azure-vm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/locales/en/titles.json b/frontend/public/locales/en/titles.json index 8aef9c9af6..f77bf0e85a 100644 --- a/frontend/public/locales/en/titles.json +++ b/frontend/public/locales/en/titles.json @@ -8,6 +8,7 @@ "GET_STARTED_LOGS_MANAGEMENT": "SigNoz | Get Started | Logs", "GET_STARTED_INFRASTRUCTURE_MONITORING": "SigNoz | Get Started | Infrastructure", "GET_STARTED_AWS_MONITORING": "SigNoz | Get Started | AWS", + "GET_STARTED_AZURE_MONITORING": "SigNoz | Get Started | AZURE", "TRACE": "SigNoz | Trace", "TRACE_DETAIL": "SigNoz | Trace Detail", "TRACES_EXPLORER": "SigNoz | Traces Explorer", diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts index 243bdd0bba..ef73184a86 100644 --- a/frontend/src/constants/routes.ts +++ b/frontend/src/constants/routes.ts @@ -13,6 +13,7 @@ const ROUTES = { GET_STARTED_INFRASTRUCTURE_MONITORING: '/get-started/infrastructure-monitoring', GET_STARTED_AWS_MONITORING: '/get-started/aws-monitoring', + GET_STARTED_AZURE_MONITORING: '/get-started/azure-monitoring', USAGE_EXPLORER: '/usage-explorer', APPLICATION: '/services', ALL_DASHBOARD: '/dashboard', diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index ef7be7eef4..88c6b8f8f6 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -236,7 +236,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element { pathname === ROUTES.GET_STARTED_APPLICATION_MONITORING || pathname === ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING || pathname === ROUTES.GET_STARTED_LOGS_MANAGEMENT || - pathname === ROUTES.GET_STARTED_AWS_MONITORING; + pathname === ROUTES.GET_STARTED_AWS_MONITORING || + pathname === ROUTES.GET_STARTED_AZURE_MONITORING; const [showTrialExpiryBanner, setShowTrialExpiryBanner] = useState(false); diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md new file mode 100644 index 0000000000..cb78ca5d27 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md @@ -0,0 +1,111 @@ +## Setup + +### Installing with OpenTelemetry Helm Charts + +Prior to installation, you must ensure your Kubernetes cluster is ready and that you have the necessary permissions to deploy applications. Follow these steps to use Helm for setting up the Collector: + +1. **Add the OpenTelemetry Helm repository:** + +```bash +helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts +``` + +2. **Prepare the `otel-collector-values.yaml` Configuration** + + #### Azure Event Hub Receiver Configuration + If you haven't created the logs Event Hub, you can create one by following the steps in the [Azure Event Hubs documentation](../../bootstrapping/data-ingestion). + + and replace the placeholders `` with the primary connection string for your Event Hub, it should look something like this: + + ```yaml + connection: Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName + ``` + The Event Hub docs have a step to create a SAS policy for the event hub and copy the connection string. + + #### Azure Monitor Receiver Configuration + + You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. + + 1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. + You can name it `signoz-central-collector-app` the redirect URI can be empty. + 2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read acess can be given to the full subscription. + 3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. + + 4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + +
+ Application Overview +
+ + Application Overview + +
+
+ + 5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + + 6. Ensure you replace the placeholders `` and `` with the appropriate values for your signoz cloud instance. + + + +Below is an example targeting the SigNoz backend with Azure Monitor receivers configured: + +```yaml +service: + pipelines: + metrics/am: + receivers: [azuremonitor] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, azureeventhub] + processors: [batch] + exporters: [otlp] +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + azureeventhub: + connection: Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName + format: "azure" + azuremonitor: + subscription_id: "" + tenant_id: "" + client_id: "" + client_secret: "" + resource_groups: [""] + collection_interval: 60s +processors: + batch: {} +exporters: + otlp: + endpoint: "ingest..signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "" +``` + +3. **Deploy the OpenTelemetry Collector to your Kubernetes cluster:** + +You'll need to prepare a custom configuration file, say `otel-collector-values.yaml`, that matches your environment's specific needs. Replace `` with the Kubernetes namespace where you wish to install the Collector. + +```bash +helm install -n --create-namespace otel-collector open-telemetry/opentelemetry-collector -f otel-collector-values.yaml + +``` + +For more detail, refer to the [official OpenTelemetry Helm Chart documentation](https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-collector), which offers comprehensive installation instructions and configuration options tailored to your environment's requirements. diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-logs.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-logs.md new file mode 100644 index 0000000000..17f80b964a --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-logs.md @@ -0,0 +1,8 @@ +## Prerequisite + +- An AKS cluster +- Central Collector Setup + +  + +Once you have setup the Central Collector, it will automatically start collecting your Logs. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-metrics.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-metrics.md new file mode 100644 index 0000000000..68b8c391b4 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-metrics.md @@ -0,0 +1,8 @@ +## Prerequisite + +- An AKS cluster +- Central Collector Setup + +  + +Once you have setup the Central Collector, it will automatically start sending your Metrics to SigNoz. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-setupEventsHub.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-setupEventsHub.md new file mode 100644 index 0000000000..976ef7c813 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-setupEventsHub.md @@ -0,0 +1,40 @@ +## Overview + +Azure Event Hubs is a big data streaming platform ideal for centralizing logging and real-time log streaming for applications on Azure or on-premises. + +Integrate SigNoz with Azure Event Hubs for a robust log management solution, leveraging SigNoz's log aggregation, querying, visualization, and alerting features. + +## Prerequisites + +- An active Azure subscription + +## Setup + +### 1. Create an Event Hubs Namespace + +1. In the [Azure portal](https://portal.azure.com), create an Event Hubs namespace. +2. Fill in the required details: + - **Resource group**: Choose or create a new one. + - **Namespace name**: Enter a unique name, e.g., `-obs-signoz`. + - **Pricing tier**: Based on your logging requirements. + - **Region**: Should match the region of the resources you want to monitor. + - **Throughput units**: Choose based on logging needs. +3. Click "Review + create" and then "Create". + +### 2. Create an Event Hub + +1. Navigate to the Event Hubs namespace you created in the Azure portal. +2. Click "+ Event Hub" to create a new event hub. +3. Enter a name, e.g., `logs`and click "Create" + +### 3. Create a SAS Policy and Copy Connection String + +1. Navigate to the Event Hub in the Azure portal. +2. Click "Shared access policies" in the left menu. +3. Click "Add" to create a new policy named `signozListen`. +4. Select the "Listen" permission and set the expiration time. +5. Click "Save". +6. Copy the *Connection string–primary key*. + + + diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-tracing.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-tracing.md new file mode 100644 index 0000000000..16a3d8cacb --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-tracing.md @@ -0,0 +1,16 @@ +## Application level Tracing + +For application-level tracing, you can use the OpenTelemetry SDKs integrated with your application. These SDKs will automatically collect and forward traces to the Central Collector. + +  + +To see how you can instrument your applications like FastAPI, NextJS, Node.js, Spring etc. you can check out the **Application Monitoring** section available at the start of this onboarding or you can checkout this [documentation](https://signoz.io/docs/instrumentation/). + +## Configure the OpenTelemetry SDK + +```bash +# Set env vars or config file +export OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector.kubelet-otel.svc.cluster.local:4318/" +``` + +For application-level traces and metrics, configure your application to use the `kube-dns` name of the **Central Collector** you set up earlier. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-installCentralCollector.md new file mode 100644 index 0000000000..7963cf9526 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-installCentralCollector.md @@ -0,0 +1,129 @@ +Set up the OpenTelemetry Collector on a Virtual Machine (VM). The setup is compatible with cloud VM instances, your own data center, or even a local VM on your development machine. Here's how to do it: + + +## Download and Install the OpenTelemetry Collector Binary + +Please visit [Documentation For VM](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) which provides further guidance on a VM installation. + +  + +## Configure OpenTelemetry Collector + +While following the documentation above for installing the OpenTelemetry Collector Binary, you must have created `config.yaml` file. Replace the content of the `config.yaml` with the below config file which includes the **Azure Monitor receiver**. + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + azureeventhub: + connection: + format: "azure" + azuremonitor: + subscription_id: "" + tenant_id: "" + client_id: "" + client_secret: "" + resource_groups: [""] + collection_interval: 60s +processors: + batch: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" +service: + pipelines: + metrics/am: + receivers: [azuremonitor] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, azureeventhub] + processors: [batch] + exporters: [otlp] + +``` +**NOTE:** +Replace the `` in the config file with the primary connection string for your Event Hub that you created in the previous section. It would look something like this: + +```bash +Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName +``` + +  + +## Azure Monitor Receiver Configuration + +You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. + +1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. +You can name it `signoz-central-collector-app` the redirect URI can be empty. + +2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read access can be given to the full subscription. + +3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. + +4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + +5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + +**NOTE:** +By following the above steps, you will get the values for ``, ``, `` and `` which you need to fill in the `config.yaml` file. + +  + +## Run the Collector + +With your configuration file ready, you can now start the Collector using the following command: + +```bash +# Runs in background with the configuration we just created +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +### Open Ports + +You will need to open the following ports on your Azure VM: +- 4317 for gRPC +- 4318 for HTTP + +You can do this by navigating to the Azure VM's Networking section and adding a new inbound rule for the ports. + +  + +### Validating the Deployment + +Once the Collector is running, ensure that telemetry data is being successfully sent and received. Use the logging exporter as defined in your configuration file, or check the logs for any startup errors. + +  + +## Configure DNS label For Collector + +To the IP address of the collector, you can add a DNS label to the Public IP address. This will make it easier to refer to the centralized collector from other services. You can do this by following these steps: + +1. Go to the Public IP address of the collector. This would be the IP address of the VM or Load Balancer in case of Kubernetes or Load Balanced collector. +2. Click on the "Configuration" tab. +3. Enter the DNS label you want to use for the collector. +4. Click on "Save". + +**NOTE:** Please take note of the DNS label you have entered. You will need this in the next steps. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/bootstrapping/collector-setup/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-logs.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-logs.md new file mode 100644 index 0000000000..e0b650d0b9 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-logs.md @@ -0,0 +1,33 @@ +Follow these steps if you want to setup logging for your Azure App Service. + +  + +## Prerequisites + +- EventHub Setup +- Central Collector Setup + + +## Setup + +1. Navigate to your App Service in the Azure portal + +2. Search for "Diagnostic settings" in the left navigation menu + +3. Click on "Add Diagnostic Setting" + +4. Select the desired log categories to export: +- HTTP logs +- App Service Console Logs +- App Service Application Logs +- Access Audit Logs +- IPSecurity Audit logs +- App Service Platform logs + + +5. Configure the destination details as **"Stream to an Event Hub"** and select the Event Hub namespace and Event Hub name created during the EventHub Setup in the earlier steps. + +6. Save the diagnostic settings + + +This will start sending your Azure App Service Logs to SigNoz! \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-metrics.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-metrics.md new file mode 100644 index 0000000000..06893fda79 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-metrics.md @@ -0,0 +1,25 @@ +Follow these steps if you want to monitor System metrics like CPU Percentage, Memory Percentage etc. of your Azure App Service. + +  + +## Prerequisites + +- EventHub Setup +- Central Collector Setup + +## Dashboard Example + +Once you have completed the prerequisites, you can start monitoring your Azure App Service's system metrics with SigNoz Cloud. Here's how you can do it: + +1. Log in to your SigNoz account +2. Navigate to the Dashboards section, and [add a dashboard](https://signoz.io/docs/userguide/manage-dashboards/) +3. Add a Timeseries Panel +4. In **Metrics**, select `azure_memorypercentage_total` and **Avg By** select tag `location` +5. In Filter say `name = ` +6. Hit “Save Changes” and you now have Memory Usage of your App Service in a Dashboard for reporting and alerting + +In this way, you can monitor system metrics of your Azure App Service in SigNoz Cloud. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/app-service/metrics/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-setupEventsHub.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-setupEventsHub.md new file mode 100644 index 0000000000..67e4ceffc1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-setupEventsHub.md @@ -0,0 +1,54 @@ +## Overview + +Azure Event Hubs is a big data streaming platform ideal for centralizing logging and real-time log streaming for applications on Azure or on-premises. + +Integrate SigNoz with Azure Event Hubs for a robust log management solution, leveraging SigNoz's log aggregation, querying, visualization, and alerting features. + +## Prerequisites + +- An active Azure subscription + +## Setup + +### 1. Create an Event Hubs Namespace + +1. In the [Azure portal](https://portal.azure.com), create an Event Hubs namespace. +2. Fill in the required details: + - **Resource group**: Choose or create a new one. + - **Namespace name**: Enter a unique name, e.g., `-obs-signoz`. + - **Pricing tier**: Based on your logging requirements. + - **Region**: Should match the region of the resources you want to monitor. + - **Throughput units**: Choose based on logging needs. +3. Click "Review + create" and then "Create". + +### 2. Create an Event Hub + +1. Navigate to the Event Hubs namespace you created in the Azure portal. +2. Click "+ Event Hub" to create a new event hub. +3. Enter a name, e.g., `logs`and click "Create" + +### 3. Create a SAS Policy and Copy Connection String + +1. Navigate to the Event Hub in the Azure portal. +2. Click "Shared access policies" in the left menu. +3. Click "Add" to create a new policy named `signozListen`. +4. Select the "Listen" permission and set the expiration time. +5. Click "Save". +6. Copy the *Connection string–primary key*. + + + + + + diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-tracing.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-tracing.md new file mode 100644 index 0000000000..35e1ba03e6 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AppService/appService-tracing.md @@ -0,0 +1,29 @@ +## Application level Tracing + +For application-level tracing, you can use the OpenTelemetry SDKs integrated with your application. These SDKs will automatically collect and forward traces to the Central Collector. + +  + +To see how you can instrument your applications like FastAPI, NextJS, Node.js, Spring etc. you can check out the **Application Monitoring** section available at the start of this onboarding or you can checkout this [documentation](https://signoz.io/docs/instrumentation/). + +  + +## Prerequisites + +1. **Azure Subscription & App Service**: You need an active Azure subscription with a running Azure App Service instance. +2. **Central Collector Setup**: Make sure you have set up the Central Collector + +  + +## Configure the OpenTelemetry SDK + +```bash +# Set env vars or config file +export OTEL_EXPORTER_OTLP_ENDPOINT="http://:4318/" +``` + +For application-level traces, configure your application to use the DNS name of the **Central Collector** you set up earlier. This Central Collector will automatically forward the collected data to SigNoz. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/app-service/tracing/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-installCentralCollector.md new file mode 100644 index 0000000000..7963cf9526 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-installCentralCollector.md @@ -0,0 +1,129 @@ +Set up the OpenTelemetry Collector on a Virtual Machine (VM). The setup is compatible with cloud VM instances, your own data center, or even a local VM on your development machine. Here's how to do it: + + +## Download and Install the OpenTelemetry Collector Binary + +Please visit [Documentation For VM](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) which provides further guidance on a VM installation. + +  + +## Configure OpenTelemetry Collector + +While following the documentation above for installing the OpenTelemetry Collector Binary, you must have created `config.yaml` file. Replace the content of the `config.yaml` with the below config file which includes the **Azure Monitor receiver**. + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + azureeventhub: + connection: + format: "azure" + azuremonitor: + subscription_id: "" + tenant_id: "" + client_id: "" + client_secret: "" + resource_groups: [""] + collection_interval: 60s +processors: + batch: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" +service: + pipelines: + metrics/am: + receivers: [azuremonitor] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, azureeventhub] + processors: [batch] + exporters: [otlp] + +``` +**NOTE:** +Replace the `` in the config file with the primary connection string for your Event Hub that you created in the previous section. It would look something like this: + +```bash +Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName +``` + +  + +## Azure Monitor Receiver Configuration + +You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. + +1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. +You can name it `signoz-central-collector-app` the redirect URI can be empty. + +2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read access can be given to the full subscription. + +3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. + +4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + +5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + +**NOTE:** +By following the above steps, you will get the values for ``, ``, `` and `` which you need to fill in the `config.yaml` file. + +  + +## Run the Collector + +With your configuration file ready, you can now start the Collector using the following command: + +```bash +# Runs in background with the configuration we just created +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +### Open Ports + +You will need to open the following ports on your Azure VM: +- 4317 for gRPC +- 4318 for HTTP + +You can do this by navigating to the Azure VM's Networking section and adding a new inbound rule for the ports. + +  + +### Validating the Deployment + +Once the Collector is running, ensure that telemetry data is being successfully sent and received. Use the logging exporter as defined in your configuration file, or check the logs for any startup errors. + +  + +## Configure DNS label For Collector + +To the IP address of the collector, you can add a DNS label to the Public IP address. This will make it easier to refer to the centralized collector from other services. You can do this by following these steps: + +1. Go to the Public IP address of the collector. This would be the IP address of the VM or Load Balancer in case of Kubernetes or Load Balanced collector. +2. Click on the "Configuration" tab. +3. Enter the DNS label you want to use for the collector. +4. Click on "Save". + +**NOTE:** Please take note of the DNS label you have entered. You will need this in the next steps. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/bootstrapping/collector-setup/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-logs.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-logs.md new file mode 100644 index 0000000000..17ea35ddd2 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-logs.md @@ -0,0 +1,23 @@ +Follow these steps if you want to setup logging for your Azure App Service. + +  + +## Prerequisites + +- EventHub Setup +- Central Collector Setup + +## Setup + +1. Navigate to the relevant Storage Account in the Azure portal +2. Search for "Diagnostic settings" in the left navigation menu +3. Click on `blob` under the storage account +4. Click on "Add Diagnostic Setting" +5. Select the desired log categories to export: + - Storage Read + - Storage Write + - Storage Delete +5. Configure the destination details as "**Stream to an Event Hub**" and select the Event Hub namespace and Event Hub name created during the EventHub Setup +6. Save the diagnostic settings + +That's it! You have successfully set up logging for your Azure Blob Storage. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-metrics.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-metrics.md new file mode 100644 index 0000000000..2e9ebeacfa --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-metrics.md @@ -0,0 +1,28 @@ +Follow these steps if you want to monitor system metrics like Total Requests, Total Ingress / Egress, and Total Errors etc., of your Azure Blob Storage. + +  + +## Prerequisites + +- Azure Subscription and Azure Blob storage instance running +- Central Collector Setup + +  + +## Dashboard Example + +Once you have completed the prerequisites, you can start monitoring your Azure Blob Storage's system metrics with SigNoz. + +1. Log in to your SigNoz account. +2. Navigate to the Dashboards, and [add a dashboard](https://signoz.io/docs/userguide/manage-dashboards/) +3. Add a Timeseries Panel +4. In **Metrics**, select `azure_ingress_total` and **Avg B*y* select tag `location` +5. In Filter say `name = ` +6. Hit “Save Changes”. You now have Total Ingress of your Azure Blob Storage in a Dashboard for reporting and alerting + + +That's it! You have successfully set up monitoring for your Azure Blob Storage's system metrics with SigNoz. You can now start creating other panels and dashboards to monitor other Azure Blob Storage's metrics. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/az-blob-storage/metrics/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-setupEventsHub.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-setupEventsHub.md new file mode 100644 index 0000000000..67e4ceffc1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/BlobStorage/blobStorage-setupEventsHub.md @@ -0,0 +1,54 @@ +## Overview + +Azure Event Hubs is a big data streaming platform ideal for centralizing logging and real-time log streaming for applications on Azure or on-premises. + +Integrate SigNoz with Azure Event Hubs for a robust log management solution, leveraging SigNoz's log aggregation, querying, visualization, and alerting features. + +## Prerequisites + +- An active Azure subscription + +## Setup + +### 1. Create an Event Hubs Namespace + +1. In the [Azure portal](https://portal.azure.com), create an Event Hubs namespace. +2. Fill in the required details: + - **Resource group**: Choose or create a new one. + - **Namespace name**: Enter a unique name, e.g., `-obs-signoz`. + - **Pricing tier**: Based on your logging requirements. + - **Region**: Should match the region of the resources you want to monitor. + - **Throughput units**: Choose based on logging needs. +3. Click "Review + create" and then "Create". + +### 2. Create an Event Hub + +1. Navigate to the Event Hubs namespace you created in the Azure portal. +2. Click "+ Event Hub" to create a new event hub. +3. Enter a name, e.g., `logs`and click "Create" + +### 3. Create a SAS Policy and Copy Connection String + +1. Navigate to the Event Hub in the Azure portal. +2. Click "Shared access policies" in the left menu. +3. Click "Add" to create a new policy named `signozListen`. +4. Select the "Listen" permission and set the expiration time. +5. Click "Save". +6. Copy the *Connection string–primary key*. + + + + + + diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-installCentralCollector.md new file mode 100644 index 0000000000..7963cf9526 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-installCentralCollector.md @@ -0,0 +1,129 @@ +Set up the OpenTelemetry Collector on a Virtual Machine (VM). The setup is compatible with cloud VM instances, your own data center, or even a local VM on your development machine. Here's how to do it: + + +## Download and Install the OpenTelemetry Collector Binary + +Please visit [Documentation For VM](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) which provides further guidance on a VM installation. + +  + +## Configure OpenTelemetry Collector + +While following the documentation above for installing the OpenTelemetry Collector Binary, you must have created `config.yaml` file. Replace the content of the `config.yaml` with the below config file which includes the **Azure Monitor receiver**. + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + azureeventhub: + connection: + format: "azure" + azuremonitor: + subscription_id: "" + tenant_id: "" + client_id: "" + client_secret: "" + resource_groups: [""] + collection_interval: 60s +processors: + batch: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" +service: + pipelines: + metrics/am: + receivers: [azuremonitor] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, azureeventhub] + processors: [batch] + exporters: [otlp] + +``` +**NOTE:** +Replace the `` in the config file with the primary connection string for your Event Hub that you created in the previous section. It would look something like this: + +```bash +Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName +``` + +  + +## Azure Monitor Receiver Configuration + +You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. + +1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. +You can name it `signoz-central-collector-app` the redirect URI can be empty. + +2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read access can be given to the full subscription. + +3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. + +4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + +5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + +**NOTE:** +By following the above steps, you will get the values for ``, ``, `` and `` which you need to fill in the `config.yaml` file. + +  + +## Run the Collector + +With your configuration file ready, you can now start the Collector using the following command: + +```bash +# Runs in background with the configuration we just created +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +### Open Ports + +You will need to open the following ports on your Azure VM: +- 4317 for gRPC +- 4318 for HTTP + +You can do this by navigating to the Azure VM's Networking section and adding a new inbound rule for the ports. + +  + +### Validating the Deployment + +Once the Collector is running, ensure that telemetry data is being successfully sent and received. Use the logging exporter as defined in your configuration file, or check the logs for any startup errors. + +  + +## Configure DNS label For Collector + +To the IP address of the collector, you can add a DNS label to the Public IP address. This will make it easier to refer to the centralized collector from other services. You can do this by following these steps: + +1. Go to the Public IP address of the collector. This would be the IP address of the VM or Load Balancer in case of Kubernetes or Load Balanced collector. +2. Click on the "Configuration" tab. +3. Enter the DNS label you want to use for the collector. +4. Click on "Save". + +**NOTE:** Please take note of the DNS label you have entered. You will need this in the next steps. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/bootstrapping/collector-setup/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-logs.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-logs.md new file mode 100644 index 0000000000..98f0c1cb23 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-logs.md @@ -0,0 +1,28 @@ +Follow these steps if you want to setup logging for your Azure Container App. + +  + +## Prerequisites + +- EventHub Setup +- Central Collector Setup + + +## Setup + +1. Navigate to your Container Apps in the Azure portal +2. Click on "Container Apps Environment" to open the Container Apps Environment +3. Search for "Diagnostic settings" in the left navigation menu +4. Click on "Add Diagnostic Setting" +5. Select the desired log categories to export: + - Container App console logs + - Container App system logs + - Spring App console logs + + +6. Configure the destination details as **"Stream to an Event Hub"** and select the Event Hub namespace and Event Hub name created during the EventHub Setup. + +7. Save the diagnostic settings + + +That's it! You have successfully set up logging for your Azure Container App. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-metrics.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-metrics.md new file mode 100644 index 0000000000..771e19321d --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-metrics.md @@ -0,0 +1,27 @@ +Follow these steps if you want to monitor System metrics like CPU Percentage, Memory Percentage etc. of your Azure Container App. + +  + +## Prerequisites + +- Azure subscription and an Azure Container App instance running +- Central Collector Setup + +  + +# Dashboard Example + +Once you have completed the prerequisites, you can start monitoring your Azure Container App's system metrics with SigNoz. Here's how you can do it: + +1. Log in to your SigNoz account. +2. Navigate to the Dashboards, and [add an dashboard](https://signoz.io/docs/userguide/manage-dashboards/) +3. Add a Timeseries Panel +4. In **Metrics**, select `azure_replicas_count` and **Avg By** select tag `name` +5. In Filter say `type = Microsoft.App/containerApps` +6. Hit “Save Changes”. You now have Memory Usage of your Container App in a Dashboard for reporting and alerting + +In this way, you can monitor system metrics of your Azure Container App in SigNoz! + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/az-container-apps/metrics/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-setupEventsHub.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-setupEventsHub.md new file mode 100644 index 0000000000..67e4ceffc1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-setupEventsHub.md @@ -0,0 +1,54 @@ +## Overview + +Azure Event Hubs is a big data streaming platform ideal for centralizing logging and real-time log streaming for applications on Azure or on-premises. + +Integrate SigNoz with Azure Event Hubs for a robust log management solution, leveraging SigNoz's log aggregation, querying, visualization, and alerting features. + +## Prerequisites + +- An active Azure subscription + +## Setup + +### 1. Create an Event Hubs Namespace + +1. In the [Azure portal](https://portal.azure.com), create an Event Hubs namespace. +2. Fill in the required details: + - **Resource group**: Choose or create a new one. + - **Namespace name**: Enter a unique name, e.g., `-obs-signoz`. + - **Pricing tier**: Based on your logging requirements. + - **Region**: Should match the region of the resources you want to monitor. + - **Throughput units**: Choose based on logging needs. +3. Click "Review + create" and then "Create". + +### 2. Create an Event Hub + +1. Navigate to the Event Hubs namespace you created in the Azure portal. +2. Click "+ Event Hub" to create a new event hub. +3. Enter a name, e.g., `logs`and click "Create" + +### 3. Create a SAS Policy and Copy Connection String + +1. Navigate to the Event Hub in the Azure portal. +2. Click "Shared access policies" in the left menu. +3. Click "Add" to create a new policy named `signozListen`. +4. Select the "Listen" permission and set the expiration time. +5. Click "Save". +6. Copy the *Connection string–primary key*. + + + + + + diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-tracing.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-tracing.md new file mode 100644 index 0000000000..3bcc892e08 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/ContainerApps/containerApps-tracing.md @@ -0,0 +1,29 @@ +## Application level Tracing + +For application-level tracing, you can use the OpenTelemetry SDKs integrated with your application. These SDKs will automatically collect and forward traces to the Central Collector. + +  + +To see how you can instrument your applications like FastAPI, NextJS, Node.js, Spring etc. you can check out the **Application Monitoring** section available at the start of this onboarding or you can checkout this [documentation](https://signoz.io/docs/instrumentation/). + +  + +## Prerequisites + +1. **Azure Subscription & App Service**: You need an active Azure subscription with a running Azure App Service instance. +2. **Central Collector Setup**: Make sure you have set up the Central Collector + +  + +## Configure the OpenTelemetry SDK + +```bash +# Set env vars or config file +export OTEL_EXPORTER_OTLP_ENDPOINT="http://:4318/" +``` + +For application-level traces, configure your application to use the DNS name of the **Central Collector** you set up earlier. This Central Collector will automatically forward the collected data to SigNoz. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/az-container-apps/tracing/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-installCentralCollector.md new file mode 100644 index 0000000000..7963cf9526 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-installCentralCollector.md @@ -0,0 +1,129 @@ +Set up the OpenTelemetry Collector on a Virtual Machine (VM). The setup is compatible with cloud VM instances, your own data center, or even a local VM on your development machine. Here's how to do it: + + +## Download and Install the OpenTelemetry Collector Binary + +Please visit [Documentation For VM](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) which provides further guidance on a VM installation. + +  + +## Configure OpenTelemetry Collector + +While following the documentation above for installing the OpenTelemetry Collector Binary, you must have created `config.yaml` file. Replace the content of the `config.yaml` with the below config file which includes the **Azure Monitor receiver**. + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + azureeventhub: + connection: + format: "azure" + azuremonitor: + subscription_id: "" + tenant_id: "" + client_id: "" + client_secret: "" + resource_groups: [""] + collection_interval: 60s +processors: + batch: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" +service: + pipelines: + metrics/am: + receivers: [azuremonitor] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, azureeventhub] + processors: [batch] + exporters: [otlp] + +``` +**NOTE:** +Replace the `` in the config file with the primary connection string for your Event Hub that you created in the previous section. It would look something like this: + +```bash +Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName +``` + +  + +## Azure Monitor Receiver Configuration + +You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. + +1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. +You can name it `signoz-central-collector-app` the redirect URI can be empty. + +2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read access can be given to the full subscription. + +3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. + +4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + +5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + +**NOTE:** +By following the above steps, you will get the values for ``, ``, `` and `` which you need to fill in the `config.yaml` file. + +  + +## Run the Collector + +With your configuration file ready, you can now start the Collector using the following command: + +```bash +# Runs in background with the configuration we just created +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +### Open Ports + +You will need to open the following ports on your Azure VM: +- 4317 for gRPC +- 4318 for HTTP + +You can do this by navigating to the Azure VM's Networking section and adding a new inbound rule for the ports. + +  + +### Validating the Deployment + +Once the Collector is running, ensure that telemetry data is being successfully sent and received. Use the logging exporter as defined in your configuration file, or check the logs for any startup errors. + +  + +## Configure DNS label For Collector + +To the IP address of the collector, you can add a DNS label to the Public IP address. This will make it easier to refer to the centralized collector from other services. You can do this by following these steps: + +1. Go to the Public IP address of the collector. This would be the IP address of the VM or Load Balancer in case of Kubernetes or Load Balanced collector. +2. Click on the "Configuration" tab. +3. Enter the DNS label you want to use for the collector. +4. Click on "Save". + +**NOTE:** Please take note of the DNS label you have entered. You will need this in the next steps. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/bootstrapping/collector-setup/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-logs.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-logs.md new file mode 100644 index 0000000000..73ae89d8b0 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-logs.md @@ -0,0 +1,21 @@ +Follow these steps if you want to setup logging for your Azure Functions. + +  + +## Prerequisites + +- EventHub Setup +- Central Collector Setup + + +## Setup + +1. Navigate to your Azure Function in the Azure portal +2. Search for "Diagnostic settings" in the left navigation menu +3. Click on "Add Diagnostic Setting" +4. Select the desired log categories to export: + - Function App logs +5. Configure the destination details as "**Stream to an Event Hub**" and select the Event Hub namespace and Event Hub name created during the EventHub Setup +6. Save the diagnostic settings + +That's it! You have successfully set up logging for your Azure Function. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-metrics.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-metrics.md new file mode 100644 index 0000000000..abb92f8303 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-metrics.md @@ -0,0 +1,28 @@ +Follow these steps if you want to monitor System metrics like CPU Percentage, Memory Percentage etc. of your Azure Functions. + +  + +## Prerequisites + +- Azure subscription and an Azure Container App instance running +- Central Collector Setup + +  + +## Dashboard Example + +Once you have completed the prerequisites, you can start monitoring your Azure Function's system metrics with SigNoz. Here's how you can do it: + +1. Log in to your SigNoz account. +2. Navigate to the Dashboards, and add an dashboard +3. Add a Timeseries Panel +4. In *Metrics*, select `azure_requests_total` and *Avg By* select tag `location` +5. In Filter say `name = ` +6. Hit “Save Changes” You now have Total Requests of your Azure Function in a Dashboard for reporting and alerting + + +That's it! You have successfully set up monitoring for your Azure Function's system metrics with SigNoz. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/az-fns/metrics/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-setupEventsHub.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-setupEventsHub.md new file mode 100644 index 0000000000..67e4ceffc1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-setupEventsHub.md @@ -0,0 +1,54 @@ +## Overview + +Azure Event Hubs is a big data streaming platform ideal for centralizing logging and real-time log streaming for applications on Azure or on-premises. + +Integrate SigNoz with Azure Event Hubs for a robust log management solution, leveraging SigNoz's log aggregation, querying, visualization, and alerting features. + +## Prerequisites + +- An active Azure subscription + +## Setup + +### 1. Create an Event Hubs Namespace + +1. In the [Azure portal](https://portal.azure.com), create an Event Hubs namespace. +2. Fill in the required details: + - **Resource group**: Choose or create a new one. + - **Namespace name**: Enter a unique name, e.g., `-obs-signoz`. + - **Pricing tier**: Based on your logging requirements. + - **Region**: Should match the region of the resources you want to monitor. + - **Throughput units**: Choose based on logging needs. +3. Click "Review + create" and then "Create". + +### 2. Create an Event Hub + +1. Navigate to the Event Hubs namespace you created in the Azure portal. +2. Click "+ Event Hub" to create a new event hub. +3. Enter a name, e.g., `logs`and click "Create" + +### 3. Create a SAS Policy and Copy Connection String + +1. Navigate to the Event Hub in the Azure portal. +2. Click "Shared access policies" in the left menu. +3. Click "Add" to create a new policy named `signozListen`. +4. Select the "Listen" permission and set the expiration time. +5. Click "Save". +6. Copy the *Connection string–primary key*. + + + + + + diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-tracing.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-tracing.md new file mode 100644 index 0000000000..c20488159c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Functions/functions-tracing.md @@ -0,0 +1,29 @@ +## Application level Tracing + +For application-level tracing, you can use the OpenTelemetry SDKs integrated with your application. These SDKs will automatically collect and forward traces to the Central Collector. + +  + +To see how you can instrument your applications like FastAPI, NextJS, Node.js, Spring etc. you can check out the **Application Monitoring** section available at the start of this onboarding or you can checkout this [documentation](https://signoz.io/docs/instrumentation/). + +  + +## Prerequisites + +1. **Azure Subscription & App Service**: You need an active Azure subscription with a running Azure Function App instance. +2. **Central Collector Setup**: Make sure you have set up the Central Collector + +  + +## Configure the OpenTelemetry SDK + +```bash +# Set env vars or config file +export OTEL_EXPORTER_OTLP_ENDPOINT="http://:4318/" +``` + +For application-level traces, configure your application to use the DNS name of the **Central Collector** you set up earlier. This Central Collector will automatically forward the collected data to SigNoz. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/az-fns/tracing/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-installCentralCollector.md new file mode 100644 index 0000000000..7963cf9526 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-installCentralCollector.md @@ -0,0 +1,129 @@ +Set up the OpenTelemetry Collector on a Virtual Machine (VM). The setup is compatible with cloud VM instances, your own data center, or even a local VM on your development machine. Here's how to do it: + + +## Download and Install the OpenTelemetry Collector Binary + +Please visit [Documentation For VM](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) which provides further guidance on a VM installation. + +  + +## Configure OpenTelemetry Collector + +While following the documentation above for installing the OpenTelemetry Collector Binary, you must have created `config.yaml` file. Replace the content of the `config.yaml` with the below config file which includes the **Azure Monitor receiver**. + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + azureeventhub: + connection: + format: "azure" + azuremonitor: + subscription_id: "" + tenant_id: "" + client_id: "" + client_secret: "" + resource_groups: [""] + collection_interval: 60s +processors: + batch: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" +service: + pipelines: + metrics/am: + receivers: [azuremonitor] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, azureeventhub] + processors: [batch] + exporters: [otlp] + +``` +**NOTE:** +Replace the `` in the config file with the primary connection string for your Event Hub that you created in the previous section. It would look something like this: + +```bash +Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName +``` + +  + +## Azure Monitor Receiver Configuration + +You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. + +1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. +You can name it `signoz-central-collector-app` the redirect URI can be empty. + +2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read access can be given to the full subscription. + +3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. + +4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + +5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + +**NOTE:** +By following the above steps, you will get the values for ``, ``, `` and `` which you need to fill in the `config.yaml` file. + +  + +## Run the Collector + +With your configuration file ready, you can now start the Collector using the following command: + +```bash +# Runs in background with the configuration we just created +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +### Open Ports + +You will need to open the following ports on your Azure VM: +- 4317 for gRPC +- 4318 for HTTP + +You can do this by navigating to the Azure VM's Networking section and adding a new inbound rule for the ports. + +  + +### Validating the Deployment + +Once the Collector is running, ensure that telemetry data is being successfully sent and received. Use the logging exporter as defined in your configuration file, or check the logs for any startup errors. + +  + +## Configure DNS label For Collector + +To the IP address of the collector, you can add a DNS label to the Public IP address. This will make it easier to refer to the centralized collector from other services. You can do this by following these steps: + +1. Go to the Public IP address of the collector. This would be the IP address of the VM or Load Balancer in case of Kubernetes or Load Balanced collector. +2. Click on the "Configuration" tab. +3. Enter the DNS label you want to use for the collector. +4. Click on "Save". + +**NOTE:** Please take note of the DNS label you have entered. You will need this in the next steps. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/bootstrapping/collector-setup/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-metrics.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-metrics.md new file mode 100644 index 0000000000..d6da5ffc2e --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-metrics.md @@ -0,0 +1,31 @@ +## Prerequisite + +- Azure subscription and Database instance running +- Central Collector Setup +- [SQL monitoring profile](https://learn.microsoft.com/en-us/azure/azure-sql/database/sql-insights-enable?view=azuresql#create-sql-monitoring-profile) created to monitor the databases in Azure Monitor + +  + + +## Setup + +Once you have completed the prerequisites, you can start monitoring your Database's system metrics with SigNoz. Here's how you can do it: + +1. Log in to your SigNoz account. +2. Navigate to the Dashboards Section, and [add an dashboard](https://signoz.io/docs/userguide/manage-dashboards/) +3. Add a Timeseries Panel +4. In **Metrics**, select `azure_storage_maximum` and **Avg By** select tag `location` +5. In Filter say `name = ` +6. Hit “Save Changes”. You now have Memory Usage of your Database in a Dashboard for reporting and alerting + +That's it! You have successfully set up monitoring for your Database's system metrics with SigNoz. + +  + +**NOTE:** +Make sure you have created a sql monitoring profile in Azure Monitor if not, follow this guide to [Create SQL Monitoring Profile](https://learn.microsoft.com/en-us/azure/azure-sql/database/sql-insights-enable?view=azuresql#create-sql-monitoring-profile). +You can monitor multiple databases in a single profile. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/db-metrics/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-setupEventsHub.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-setupEventsHub.md new file mode 100644 index 0000000000..67e4ceffc1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-setupEventsHub.md @@ -0,0 +1,54 @@ +## Overview + +Azure Event Hubs is a big data streaming platform ideal for centralizing logging and real-time log streaming for applications on Azure or on-premises. + +Integrate SigNoz with Azure Event Hubs for a robust log management solution, leveraging SigNoz's log aggregation, querying, visualization, and alerting features. + +## Prerequisites + +- An active Azure subscription + +## Setup + +### 1. Create an Event Hubs Namespace + +1. In the [Azure portal](https://portal.azure.com), create an Event Hubs namespace. +2. Fill in the required details: + - **Resource group**: Choose or create a new one. + - **Namespace name**: Enter a unique name, e.g., `-obs-signoz`. + - **Pricing tier**: Based on your logging requirements. + - **Region**: Should match the region of the resources you want to monitor. + - **Throughput units**: Choose based on logging needs. +3. Click "Review + create" and then "Create". + +### 2. Create an Event Hub + +1. Navigate to the Event Hubs namespace you created in the Azure portal. +2. Click "+ Event Hub" to create a new event hub. +3. Enter a name, e.g., `logs`and click "Create" + +### 3. Create a SAS Policy and Copy Connection String + +1. Navigate to the Event Hub in the Azure portal. +2. Click "Shared access policies" in the left menu. +3. Click "Add" to create a new policy named `signozListen`. +4. Select the "Listen" permission and set the expiration time. +5. Click "Save". +6. Copy the *Connection string–primary key*. + + + + + + diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-hostmetrics-and-logs.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-hostmetrics-and-logs.md new file mode 100644 index 0000000000..e3aba6a9c9 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-hostmetrics-and-logs.md @@ -0,0 +1,134 @@ +## Prerequisites + +- An Azure subscription with Azure VM and SSH access enabled +- Central Collector Setup + + +### Connect to the VM +The [SSH Keys Guide](https://learn.microsoft.com/en-us/azure/virtual-machines/ssh-keys-portal#connect-to-the-vm) has steps on how to connect to your VM via SSH. + +  + +### Install OpenTelemetry Collector + +Follow the [OpenTelemetry SigNoz documentation](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) to install the OpenTelemetry Collector. + +  + +### Configure Collector + + We send the logs, traces and metrics to the central collector that we set up in the previous step instead of SigNoz directly, in order to adopt a scalable architecture pattern. We recommend to our users to use the same pattern in your Azure subscription. + +Replace the content of the `config.yaml` file that you created while installing the collector. + +```yaml +receivers: + filelog: + include: [ ] # /var/log/myservice/*.json + operators: + - type: json_parser + timestamp: + parse_from: attributes.time + layout: '%Y-%m-%d %H:%M:%S' + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, azure, system] + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [dns, os] +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: ":4318" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, filelog] + processors: [batch] + exporters: [otlp] +``` +  + +#### OLTP Exporter Configuration +Make sure to replace `` with the DNS name of your central collector that you set up earlier. + +  + +#### File Logs Receiver Configuration +The file logs receiver needs to be configured with the paths to the log files that you want to stream to SigNoz. You can specify multiple paths by separating them as a array. + +You can also specify globed path patterns to match multiple log files. For example, `/var/log/myservice/*.json` will match all log files in the `/var/log/myservice` directory with a `.json` extension. + +  + +### Start the OpenTelemetry Collector + +Once we are done with the above configurations, we can now run the collector service with the following command: + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +### Hostmetrics Dashboard + +Once the collector is running, you can access the SigNoz dashboard to view the logs and metrics from your Azure VM. + +Please refer to the [Hostmetrics Dashboard](https://signoz.io/docs/userguide/hostmetrics/) for information on how to import and use the dashboard. + diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-installCentralCollector.md new file mode 100644 index 0000000000..7963cf9526 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-installCentralCollector.md @@ -0,0 +1,129 @@ +Set up the OpenTelemetry Collector on a Virtual Machine (VM). The setup is compatible with cloud VM instances, your own data center, or even a local VM on your development machine. Here's how to do it: + + +## Download and Install the OpenTelemetry Collector Binary + +Please visit [Documentation For VM](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) which provides further guidance on a VM installation. + +  + +## Configure OpenTelemetry Collector + +While following the documentation above for installing the OpenTelemetry Collector Binary, you must have created `config.yaml` file. Replace the content of the `config.yaml` with the below config file which includes the **Azure Monitor receiver**. + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + azureeventhub: + connection: + format: "azure" + azuremonitor: + subscription_id: "" + tenant_id: "" + client_id: "" + client_secret: "" + resource_groups: [""] + collection_interval: 60s +processors: + batch: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" +service: + pipelines: + metrics/am: + receivers: [azuremonitor] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp, azureeventhub] + processors: [batch] + exporters: [otlp] + +``` +**NOTE:** +Replace the `` in the config file with the primary connection string for your Event Hub that you created in the previous section. It would look something like this: + +```bash +Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName +``` + +  + +## Azure Monitor Receiver Configuration + +You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. + +1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. +You can name it `signoz-central-collector-app` the redirect URI can be empty. + +2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read access can be given to the full subscription. + +3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. + +4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + +5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + +**NOTE:** +By following the above steps, you will get the values for ``, ``, `` and `` which you need to fill in the `config.yaml` file. + +  + +## Run the Collector + +With your configuration file ready, you can now start the Collector using the following command: + +```bash +# Runs in background with the configuration we just created +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +### Open Ports + +You will need to open the following ports on your Azure VM: +- 4317 for gRPC +- 4318 for HTTP + +You can do this by navigating to the Azure VM's Networking section and adding a new inbound rule for the ports. + +  + +### Validating the Deployment + +Once the Collector is running, ensure that telemetry data is being successfully sent and received. Use the logging exporter as defined in your configuration file, or check the logs for any startup errors. + +  + +## Configure DNS label For Collector + +To the IP address of the collector, you can add a DNS label to the Public IP address. This will make it easier to refer to the centralized collector from other services. You can do this by following these steps: + +1. Go to the Public IP address of the collector. This would be the IP address of the VM or Load Balancer in case of Kubernetes or Load Balanced collector. +2. Click on the "Configuration" tab. +3. Enter the DNS label you want to use for the collector. +4. Click on "Save". + +**NOTE:** Please take note of the DNS label you have entered. You will need this in the next steps. + +  + +If you encounter any difficulties, please refer to this [troubleshooting section](https://signoz.io/docs/azure-monitoring/bootstrapping/collector-setup/#troubleshooting) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-setupEventsHub.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-setupEventsHub.md new file mode 100644 index 0000000000..67e4ceffc1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/Vm/vm-setupEventsHub.md @@ -0,0 +1,54 @@ +## Overview + +Azure Event Hubs is a big data streaming platform ideal for centralizing logging and real-time log streaming for applications on Azure or on-premises. + +Integrate SigNoz with Azure Event Hubs for a robust log management solution, leveraging SigNoz's log aggregation, querying, visualization, and alerting features. + +## Prerequisites + +- An active Azure subscription + +## Setup + +### 1. Create an Event Hubs Namespace + +1. In the [Azure portal](https://portal.azure.com), create an Event Hubs namespace. +2. Fill in the required details: + - **Resource group**: Choose or create a new one. + - **Namespace name**: Enter a unique name, e.g., `-obs-signoz`. + - **Pricing tier**: Based on your logging requirements. + - **Region**: Should match the region of the resources you want to monitor. + - **Throughput units**: Choose based on logging needs. +3. Click "Review + create" and then "Create". + +### 2. Create an Event Hub + +1. Navigate to the Event Hubs namespace you created in the Azure portal. +2. Click "+ Event Hub" to create a new event hub. +3. Enter a name, e.g., `logs`and click "Create" + +### 3. Create a SAS Policy and Copy Connection String + +1. Navigate to the Event Hub in the Azure portal. +2. Click "Shared access policies" in the left menu. +3. Click "Add" to create a new policy named `signozListen`. +4. Select the "Listen" permission and set the expiration time. +5. Click "Save". +6. Copy the *Connection string–primary key*. + + + + + + diff --git a/frontend/src/container/OnboardingContainer/Onboarding.styles.scss b/frontend/src/container/OnboardingContainer/Onboarding.styles.scss index 018c9af352..e81679d143 100644 --- a/frontend/src/container/OnboardingContainer/Onboarding.styles.scss +++ b/frontend/src/container/OnboardingContainer/Onboarding.styles.scss @@ -31,7 +31,7 @@ .onboardingHeader { text-align: center; - margin-top: 48px; + margin-top: 24px; } .onboardingHeader h1 { @@ -51,13 +51,14 @@ justify-content: center; gap: 36px; margin: 36px; + flex-wrap: wrap; } .moduleStyles { padding: 0; box-sizing: border-box; cursor: pointer; - width: 400px; + width: 300px; transition: 0.3s; .ant-card-body { diff --git a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx index 68e8f0edce..5383f459f9 100644 --- a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx +++ b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx @@ -25,6 +25,7 @@ import { DataSourceType } from './Steps/DataSource/DataSource'; import { defaultApplicationDataSource, defaultAwsServices, + defaultAzureServices, defaultInfraMetricsType, defaultLogsType, moduleRouteMap, @@ -32,6 +33,7 @@ import { import { APM_STEPS, AWS_MONITORING_STEPS, + AZURE_MONITORING_STEPS, getSteps, INFRASTRUCTURE_MONITORING_STEPS, LOGS_MANAGEMENT_STEPS, @@ -42,6 +44,7 @@ export enum ModulesMap { LogsManagement = 'LogsManagement', InfrastructureMonitoring = 'InfrastructureMonitoring', AwsMonitoring = 'AwsMonitoring', + AzureMonitoring = 'AzureMonitoring', } export interface ModuleProps { @@ -81,6 +84,12 @@ export const useCases = { desc: 'Monitor your traces, logs and metrics for AWS services like EC2, ECS, EKS etc.', }, + AzureMonitoring: { + id: ModulesMap.AzureMonitoring, + title: 'Azure Monitoring', + desc: + 'Monitor your traces, logs and metrics for Azure services like AKS, Container Apps, App Service etc.', + }, }; export default function Onboarding(): JSX.Element { @@ -172,6 +181,7 @@ export default function Onboarding(): JSX.Element { setSelectedModuleSteps(APM_STEPS); }; + // eslint-disable-next-line sonarjs/cognitive-complexity useEffect(() => { if (selectedModule?.id === ModulesMap.InfrastructureMonitoring) { if (selectedDataSource) { @@ -194,6 +204,13 @@ export default function Onboarding(): JSX.Element { setSelectedModuleSteps(AWS_MONITORING_STEPS); updateSelectedDataSource(defaultAwsServices); } + } else if (selectedModule?.id === ModulesMap.AzureMonitoring) { + if (selectedDataSource) { + setModuleStepsBasedOnSelectedDataSource(selectedDataSource); + } else { + setSelectedModuleSteps(AZURE_MONITORING_STEPS); + updateSelectedDataSource(defaultAzureServices); + } } else if (selectedModule?.id === ModulesMap.APM) { handleAPMSteps(); @@ -240,18 +257,24 @@ export default function Onboarding(): JSX.Element { }; useEffect(() => { - if (location.pathname === ROUTES.GET_STARTED_APPLICATION_MONITORING) { + const { pathname } = location; + + if (pathname === ROUTES.GET_STARTED_APPLICATION_MONITORING) { handleModuleSelect(useCases.APM); updateSelectedDataSource(defaultApplicationDataSource); handleNextStep(); - } else if ( - location.pathname === ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING - ) { + } else if (pathname === ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING) { handleModuleSelect(useCases.InfrastructureMonitoring); handleNextStep(); - } else if (location.pathname === ROUTES.GET_STARTED_LOGS_MANAGEMENT) { + } else if (pathname === ROUTES.GET_STARTED_LOGS_MANAGEMENT) { handleModuleSelect(useCases.LogsManagement); handleNextStep(); + } else if (pathname === ROUTES.GET_STARTED_AWS_MONITORING) { + handleModuleSelect(useCases.AwsMonitoring); + handleNextStep(); + } else if (pathname === ROUTES.GET_STARTED_AZURE_MONITORING) { + handleModuleSelect(useCases.AzureMonitoring); + handleNextStep(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/frontend/src/container/OnboardingContainer/Steps/MarkdownStep/MarkdownStep.tsx b/frontend/src/container/OnboardingContainer/Steps/MarkdownStep/MarkdownStep.tsx index ce38786dee..6954714342 100644 --- a/frontend/src/container/OnboardingContainer/Steps/MarkdownStep/MarkdownStep.tsx +++ b/frontend/src/container/OnboardingContainer/Steps/MarkdownStep/MarkdownStep.tsx @@ -2,6 +2,7 @@ import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; import { ApmDocFilePaths } from 'container/OnboardingContainer/constants/apmDocFilePaths'; import { AwsMonitoringDocFilePaths } from 'container/OnboardingContainer/constants/awsMonitoringDocFilePaths'; +import { AzureMonitoringDocFilePaths } from 'container/OnboardingContainer/constants/azureMonitoringDocFilePaths'; import { InfraMonitoringDocFilePaths } from 'container/OnboardingContainer/constants/infraMonitoringDocFilePaths'; import { LogsManagementDocFilePaths } from 'container/OnboardingContainer/constants/logsManagementDocFilePaths'; import { @@ -69,6 +70,8 @@ export default function MarkdownStep(): JSX.Element { docFilePaths = InfraMonitoringDocFilePaths; } else if (selectedModule?.id === ModulesMap.AwsMonitoring) { docFilePaths = AwsMonitoringDocFilePaths; + } else if (selectedModule?.id === ModulesMap.AzureMonitoring) { + docFilePaths = AzureMonitoringDocFilePaths; } // @ts-ignore if (docFilePaths && docFilePaths[path]) { diff --git a/frontend/src/container/OnboardingContainer/constants/azureMonitoringDocFilePaths.ts b/frontend/src/container/OnboardingContainer/constants/azureMonitoringDocFilePaths.ts new file mode 100644 index 0000000000..5e867ef6ee --- /dev/null +++ b/frontend/src/container/OnboardingContainer/constants/azureMonitoringDocFilePaths.ts @@ -0,0 +1,82 @@ +import AzureMonitoring_azureAks_setupCentralCollector from '../Modules/AzureMonitoring/AKS/aks-installCentralCollector.md'; +import AzureMonitoring_azureAks_sendLogs from '../Modules/AzureMonitoring/AKS/aks-logs.md'; +import AzureMonitoring_azureAks_sendMetrics from '../Modules/AzureMonitoring/AKS/aks-metrics.md'; +import AzureMonitoring_azureAks_setupAzureEventsHub from '../Modules/AzureMonitoring/AKS/aks-setupEventsHub.md'; +import AzureMonitoring_azureAks_sendTraces from '../Modules/AzureMonitoring/AKS/aks-tracing.md'; +// Azure App Service +import AzureMonitoring_azureAppService_setupCentralCollector from '../Modules/AzureMonitoring/AppService/appService-installCentralCollector.md'; +import AzureMonitoring_azureAppService_sendLogs from '../Modules/AzureMonitoring/AppService/appService-logs.md'; +import AzureMonitoring_azureAppService_sendMetrics from '../Modules/AzureMonitoring/AppService/appService-metrics.md'; +import AzureMonitoring_azureAppService_setupAzureEventsHub from '../Modules/AzureMonitoring/AppService/appService-setupEventsHub.md'; +import AzureMonitoring_azureAppService_sendTraces from '../Modules/AzureMonitoring/AppService/appService-tracing.md'; +// Azure Blob Storage +import AzureMonitoring_azureBlobStorage_setupCentralCollector from '../Modules/AzureMonitoring/BlobStorage/blobStorage-installCentralCollector.md'; +import AzureMonitoring_azureBlobStorage_sendLogs from '../Modules/AzureMonitoring/BlobStorage/blobStorage-logs.md'; +import AzureMonitoring_azureBlobStorage_sendMetrics from '../Modules/AzureMonitoring/BlobStorage/blobStorage-metrics.md'; +import AzureMonitoring_azureBlobStorage_setupAzureEventsHub from '../Modules/AzureMonitoring/BlobStorage/blobStorage-setupEventsHub.md'; +// Azure Container Apps +import AzureMonitoring_azureContainerApps_setupCentralCollector from '../Modules/AzureMonitoring/ContainerApps/containerApps-installCentralCollector.md'; +import AzureMonitoring_azureContainerApps_sendLogs from '../Modules/AzureMonitoring/ContainerApps/containerApps-logs.md'; +import AzureMonitoring_azureContainerApps_sendMetrics from '../Modules/AzureMonitoring/ContainerApps/containerApps-metrics.md'; +import AzureMonitoring_azureContainerApps_setupAzureEventsHub from '../Modules/AzureMonitoring/ContainerApps/containerApps-setupEventsHub.md'; +import AzureMonitoring_azureContainerApps_sendTraces from '../Modules/AzureMonitoring/ContainerApps/containerApps-tracing.md'; +// Azure Functions +import AzureMonitoring_azureFunctions_setupCentralCollector from '../Modules/AzureMonitoring/Functions/functions-installCentralCollector.md'; +import AzureMonitoring_azureFunctions_sendLogs from '../Modules/AzureMonitoring/Functions/functions-logs.md'; +import AzureMonitoring_azureFunctions_sendMetrics from '../Modules/AzureMonitoring/Functions/functions-metrics.md'; +import AzureMonitoring_azureFunctions_setupAzureEventsHub from '../Modules/AzureMonitoring/Functions/functions-setupEventsHub.md'; +import AzureMonitoring_azureFunctions_sendTraces from '../Modules/AzureMonitoring/Functions/functions-tracing.md'; +// Azure SQL Database Metrics +import AzureMonitoring_azureSQLDatabaseMetrics_setupCentralCollector from '../Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-installCentralCollector.md'; +import AzureMonitoring_azureSQLDatabaseMetrics_sendMetrics from '../Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-metrics.md'; +import AzureMonitoring_azureSQLDatabaseMetrics_setupAzureEventsHub from '../Modules/AzureMonitoring/SqlDatabaseMetrics/sqlDatabaseMetrics-setupEventsHub.md'; +import AzureMonitoring_azureVm_sendHostmetricsLogs from '../Modules/AzureMonitoring/Vm/vm-hostmetrics-and-logs.md'; +// Azure VM +import AzureMonitoring_azureVm_setupCentralCollector from '../Modules/AzureMonitoring/Vm/vm-installCentralCollector.md'; +import AzureMonitoring_azureVm_setupAzureEventsHub from '../Modules/AzureMonitoring/Vm/vm-setupEventsHub.md'; + +export const AzureMonitoringDocFilePaths = { + // Azure AKS + AzureMonitoring_azureAks_setupCentralCollector, + AzureMonitoring_azureAks_setupAzureEventsHub, + AzureMonitoring_azureAks_sendTraces, + AzureMonitoring_azureAks_sendLogs, + AzureMonitoring_azureAks_sendMetrics, + + // Azure App Service + AzureMonitoring_azureFunctions_setupCentralCollector, + AzureMonitoring_azureFunctions_setupAzureEventsHub, + AzureMonitoring_azureFunctions_sendTraces, + AzureMonitoring_azureFunctions_sendLogs, + AzureMonitoring_azureFunctions_sendMetrics, + + // Azure Functions + AzureMonitoring_azureAppService_setupCentralCollector, + AzureMonitoring_azureAppService_setupAzureEventsHub, + AzureMonitoring_azureAppService_sendTraces, + AzureMonitoring_azureAppService_sendLogs, + AzureMonitoring_azureAppService_sendMetrics, + + // Azure Container Apps + AzureMonitoring_azureContainerApps_setupCentralCollector, + AzureMonitoring_azureContainerApps_setupAzureEventsHub, + AzureMonitoring_azureContainerApps_sendTraces, + AzureMonitoring_azureContainerApps_sendLogs, + AzureMonitoring_azureContainerApps_sendMetrics, + + // Azure VM + AzureMonitoring_azureVm_setupCentralCollector, + AzureMonitoring_azureVm_setupAzureEventsHub, + AzureMonitoring_azureVm_sendHostmetricsLogs, + + // Azure SQL Database Metrics + AzureMonitoring_azureSQLDatabaseMetrics_setupCentralCollector, + AzureMonitoring_azureSQLDatabaseMetrics_setupAzureEventsHub, + AzureMonitoring_azureSQLDatabaseMetrics_sendMetrics, + + // Azure Blob Storage + AzureMonitoring_azureBlobStorage_setupCentralCollector, + AzureMonitoring_azureBlobStorage_setupAzureEventsHub, + AzureMonitoring_azureBlobStorage_sendLogs, + AzureMonitoring_azureBlobStorage_sendMetrics, +}; diff --git a/frontend/src/container/OnboardingContainer/constants/stepsConfig.tsx b/frontend/src/container/OnboardingContainer/constants/stepsConfig.tsx index 57b3a597d8..e4897cda50 100644 --- a/frontend/src/container/OnboardingContainer/constants/stepsConfig.tsx +++ b/frontend/src/container/OnboardingContainer/constants/stepsConfig.tsx @@ -35,6 +35,12 @@ export const stepsMap = { deployTaskDefinition: `deployTaskDefinition`, ecsSendLogsData: `ecsSendLogsData`, monitorDashboard: `monitorDashboard`, + setupCentralCollector: `setupCentralCollector`, + setupAzureEventsHub: `setupAzureEventsHub`, + sendTraces: `sendTraces`, + sendLogs: `sendLogs`, + sendMetrics: `sendMetrics`, + sendHostmetricsLogs: `sendHostmetricsLogs`, }; export const DataSourceStep: SelectedModuleStepProps = { @@ -201,3 +207,33 @@ export const MonitorDashboard: SelectedModuleStepProps = { title: 'Monitor using Dashboard ', component: , }; +export const SetupCentralCollectorStep: SelectedModuleStepProps = { + id: stepsMap.setupCentralCollector, + title: 'Setup Central Collector ', + component: , +}; +export const SetupAzureEventsHub: SelectedModuleStepProps = { + id: stepsMap.setupAzureEventsHub, + title: 'Setup EventsHub', + component: , +}; +export const SendTraces: SelectedModuleStepProps = { + id: stepsMap.sendTraces, + title: 'Send Traces', + component: , +}; +export const SendLogs: SelectedModuleStepProps = { + id: stepsMap.sendLogs, + title: 'Send Logs', + component: , +}; +export const SendMetrics: SelectedModuleStepProps = { + id: stepsMap.sendMetrics, + title: 'Send Metrics', + component: , +}; +export const SendHostmetricsLogs: SelectedModuleStepProps = { + id: stepsMap.sendHostmetricsLogs, + title: 'HostMetrics and Logging', + component: , +}; diff --git a/frontend/src/container/OnboardingContainer/utils/dataSourceUtils.ts b/frontend/src/container/OnboardingContainer/utils/dataSourceUtils.ts index 7e5ba3b319..03f92c2a39 100644 --- a/frontend/src/container/OnboardingContainer/utils/dataSourceUtils.ts +++ b/frontend/src/container/OnboardingContainer/utils/dataSourceUtils.ts @@ -8,6 +8,7 @@ export enum ModulesMap { LogsManagement = 'LogsManagement', InfrastructureMonitoring = 'InfrastructureMonitoring', AwsMonitoring = 'AwsMonitoring', + AzureMonitoring = 'AzureMonitoring', } export const frameworksMap = { @@ -82,6 +83,7 @@ export const frameworksMap = { LogsManagement: {}, InfrastructureMonitoring: {}, AwsMonitoring: {}, + AzureMonitoring: {}, }; export const defaultApplicationDataSource = { @@ -270,6 +272,50 @@ const supportedAwsServices = [ }, ]; +export const defaultAzureServices = { + name: 'VM', + id: 'azureVm', + imgURL: `/Logos/azure-vm.svg`, +}; + +const supportedAzureServices = [ + { + name: 'VM', + id: 'azureVm', + imgURL: `/Logos/azure-vm.svg`, + }, + { + name: 'App Service', + id: 'azureAppService', + imgURL: `/Logos/azure-app-service.svg`, + }, + { + name: 'AKS', + id: 'azureAks', + imgURL: `/Logos/azure-aks.svg`, + }, + { + name: 'Azure Functions', + id: 'azureFunctions', + imgURL: `/Logos/azure-functions.svg`, + }, + { + name: 'Azure Container Apps', + id: 'azureContainerApps', + imgURL: `/Logos/azure-container-apps.svg`, + }, + { + name: 'SQL Database Metrics', + id: 'azureSQLDatabaseMetrics', + imgURL: `/Logos/azure-sql-database-metrics.svg`, + }, + { + name: 'Azure Blob Storage', + id: 'azureBlobStorage', + imgURL: `/Logos/azure-blob-storage.svg`, + }, +]; + export const getDataSources = (module: ModuleProps): DataSourceType[] => { if (module.id === ModulesMap.APM) { return supportedLanguages; @@ -283,7 +329,11 @@ export const getDataSources = (module: ModuleProps): DataSourceType[] => { return supportedLogsTypes; } - return supportedAwsServices; + if (module.id === ModulesMap.AwsMonitoring) { + return supportedAwsServices; + } + + return supportedAzureServices; }; export const getSupportedFrameworks = ({ @@ -347,4 +397,5 @@ export const moduleRouteMap = { [ModulesMap.InfrastructureMonitoring]: ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING, [ModulesMap.AwsMonitoring]: ROUTES.GET_STARTED_AWS_MONITORING, + [ModulesMap.AzureMonitoring]: ROUTES.GET_STARTED_AZURE_MONITORING, }; diff --git a/frontend/src/container/OnboardingContainer/utils/getSteps.ts b/frontend/src/container/OnboardingContainer/utils/getSteps.ts index 4ad252fa89..94b2472b02 100644 --- a/frontend/src/container/OnboardingContainer/utils/getSteps.ts +++ b/frontend/src/container/OnboardingContainer/utils/getSteps.ts @@ -22,7 +22,13 @@ import { RestartOtelCollector, RunApplicationStep, SelectMethodStep, + SendHostmetricsLogs, + SendLogs, SendLogsCloudwatch, + SendMetrics, + SendTraces, + SetupAzureEventsHub, + SetupCentralCollectorStep, SetupDaemonService, SetupLogDrains, SetupOtelCollectorStep, @@ -57,6 +63,10 @@ export const INFRASTRUCTURE_MONITORING_STEPS: SelectedModuleStepProps[] = [ export const AWS_MONITORING_STEPS: SelectedModuleStepProps[] = [DataSourceStep]; +export const AZURE_MONITORING_STEPS: SelectedModuleStepProps[] = [ + DataSourceStep, +]; + export const getSteps = ({ selectedDataSource, }: GetStepsProps): SelectedModuleStepProps[] => { @@ -144,6 +154,70 @@ export const getSteps = ({ ]; case 'awsEks': return [DataSourceStep, SetupOtelCollectorStep, MonitorDashboard]; + case 'azureVm': + return [ + DataSourceStep, + SetupAzureEventsHub, + SetupCentralCollectorStep, + SendHostmetricsLogs, + ]; + // eslint-disable-next-line sonarjs/no-duplicated-branches + case 'azureAks': + return [ + DataSourceStep, + SetupAzureEventsHub, + SetupCentralCollectorStep, + SendTraces, + SendLogs, + SendMetrics, + ]; + // eslint-disable-next-line sonarjs/no-duplicated-branches + case 'azureAppService': + return [ + DataSourceStep, + SetupAzureEventsHub, + SetupCentralCollectorStep, + SendTraces, + SendLogs, + SendMetrics, + ]; + // eslint-disable-next-line sonarjs/no-duplicated-branches + case 'azureFunctions': + return [ + DataSourceStep, + SetupAzureEventsHub, + SetupCentralCollectorStep, + SendTraces, + SendLogs, + SendMetrics, + ]; + // eslint-disable-next-line sonarjs/no-duplicated-branches + case 'azureContainerApps': + return [ + DataSourceStep, + SetupAzureEventsHub, + SetupCentralCollectorStep, + SendTraces, + SendLogs, + SendMetrics, + ]; + // eslint-disable-next-line sonarjs/no-duplicated-branches + case 'azureBlobStorage': + return [ + DataSourceStep, + SetupAzureEventsHub, + SetupCentralCollectorStep, + SendLogs, + SendMetrics, + ]; + // eslint-disable-next-line sonarjs/no-duplicated-branches + case 'azureSQLDatabaseMetrics': + return [ + DataSourceStep, + SetupAzureEventsHub, + SetupCentralCollectorStep, + SendMetrics, + ]; default: return [DataSourceStep]; diff --git a/frontend/src/container/TopNav/DateTimeSelection/config.ts b/frontend/src/container/TopNav/DateTimeSelection/config.ts index 102fe00c43..b46c60bab0 100644 --- a/frontend/src/container/TopNav/DateTimeSelection/config.ts +++ b/frontend/src/container/TopNav/DateTimeSelection/config.ts @@ -112,6 +112,7 @@ export const routesToSkip = [ ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING, ROUTES.GET_STARTED_LOGS_MANAGEMENT, ROUTES.GET_STARTED_AWS_MONITORING, + ROUTES.GET_STARTED_AZURE_MONITORING, ROUTES.VERSION, ROUTES.ALL_DASHBOARD, ROUTES.ORG_SETTINGS, diff --git a/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts b/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts index 7543e02a47..19a3e8c431 100644 --- a/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts +++ b/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts @@ -181,6 +181,7 @@ export const routesToSkip = [ ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING, ROUTES.GET_STARTED_LOGS_MANAGEMENT, ROUTES.GET_STARTED_AWS_MONITORING, + ROUTES.GET_STARTED_AZURE_MONITORING, ROUTES.VERSION, ROUTES.ALL_DASHBOARD, ROUTES.ORG_SETTINGS, diff --git a/frontend/src/utils/permission/index.ts b/frontend/src/utils/permission/index.ts index 44757e3508..8af1c68f3f 100644 --- a/frontend/src/utils/permission/index.ts +++ b/frontend/src/utils/permission/index.ts @@ -86,6 +86,7 @@ export const routePermission: Record = { GET_STARTED_INFRASTRUCTURE_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'], GET_STARTED_LOGS_MANAGEMENT: ['ADMIN', 'EDITOR', 'VIEWER'], GET_STARTED_AWS_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'], + GET_STARTED_AZURE_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'], WORKSPACE_LOCKED: ['ADMIN', 'EDITOR', 'VIEWER'], BILLING: ['ADMIN', 'EDITOR', 'VIEWER'], SUPPORT: ['ADMIN', 'EDITOR', 'VIEWER'], From 8cd60b5c6080845611e28758349ca417d783695b Mon Sep 17 00:00:00 2001 From: Yunus M Date: Sat, 22 Jun 2024 15:41:42 +0530 Subject: [PATCH 028/281] fix: handle overflow for attribute tooltips in trace details page (#5313) --- .../SelectedSpanDetails/Tags/Tag.tsx | 9 ++++- .../SelectedSpanDetails/Tags/Tags.styles.scss | 37 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/Tags.styles.scss diff --git a/frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/Tag.tsx b/frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/Tag.tsx index 293b6fc993..f913dd6cbb 100644 --- a/frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/Tag.tsx +++ b/frontend/src/container/TraceDetail/SelectedSpanDetails/Tags/Tag.tsx @@ -1,3 +1,5 @@ +import './Tags.styles.scss'; + import { Tooltip } from 'antd'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { Fragment, useMemo } from 'react'; @@ -26,7 +28,12 @@ function Tag({ tags, onToggleHandler, setText }: TagProps): JSX.Element { {tags.key} - value}> + Date: Mon, 24 Jun 2024 09:23:18 +0530 Subject: [PATCH 029/281] fix: incorrect telemetry query for samples (#5314) --- pkg/query-service/app/clickhouseReader/reader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 320f7fe5b3..25b2792939 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -3219,7 +3219,7 @@ func (r *ClickHouseReader) GetSamplesInfoInLastHeartBeatInterval(ctx context.Con var totalSamples uint64 - queryStr := fmt.Sprintf("select count() from %s.%s where metric_name not like 'signoz_%%' and timestamp_ms > toUnixTimestamp(now()-toIntervalMinute(%d))*1000;", signozMetricDBName, signozSampleTableName, int(interval.Minutes())) + queryStr := fmt.Sprintf("select count() from %s.%s where metric_name not like 'signoz_%%' and unix_milli > toUnixTimestamp(now()-toIntervalMinute(%d))*1000;", signozMetricDBName, signozSampleTableName, int(interval.Minutes())) r.db.QueryRow(ctx, queryStr).Scan(&totalSamples) From 0375fc47a75546b27077a1a24e69d3f161223b4d Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 24 Jun 2024 14:45:26 +0530 Subject: [PATCH 030/281] chore: add start/end millis for trace details response (#5321) --- pkg/query-service/app/clickhouseReader/reader.go | 13 +++++++++++++ pkg/query-service/model/response.go | 8 +++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 25b2792939..d8c902055b 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1924,6 +1924,7 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_TRACE_DETAIL_API, data, userEmail, true, false) } + var startTime, endTime, durationNano uint64 var searchScanResponses []model.SearchSpanDBResponseItem query := fmt.Sprintf("SELECT timestamp, traceID, model FROM %s.%s WHERE traceID=$1", r.TraceDB, r.SpansTable) @@ -1954,6 +1955,15 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc easyjson.Unmarshal([]byte(item.Model), &jsonItem) jsonItem.TimeUnixNano = uint64(item.Timestamp.UnixNano() / 1000000) searchSpanResponses = append(searchSpanResponses, jsonItem) + if startTime == 0 || jsonItem.TimeUnixNano < startTime { + startTime = jsonItem.TimeUnixNano + } + if endTime == 0 || jsonItem.TimeUnixNano > endTime { + endTime = jsonItem.TimeUnixNano + } + if durationNano == 0 || uint64(jsonItem.DurationNano) > durationNano { + durationNano = uint64(jsonItem.DurationNano) + } } end = time.Now() zap.L().Debug("getTraceSQLQuery unmarshal took: ", zap.Duration("duration", end.Sub(start))) @@ -1983,6 +1993,9 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc } } + searchSpansResult[0].StartTimestampMillis = startTime - durationNano + searchSpansResult[0].EndTimestampMillis = endTime + durationNano + return &searchSpansResult, nil } diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index 7a3d948ebb..5ad5ea54ef 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -212,9 +212,11 @@ type ServiceOverviewItem struct { } type SearchSpansResult struct { - Columns []string `json:"columns"` - Events [][]interface{} `json:"events"` - IsSubTree bool `json:"isSubTree"` + StartTimestampMillis uint64 `json:"startTimestampMillis"` + EndTimestampMillis uint64 `json:"endTimestampMillis"` + Columns []string `json:"columns"` + Events [][]interface{} `json:"events"` + IsSubTree bool `json:"isSubTree"` } type GetFilterSpansResponseItem struct { From 878cb7c0a66b7261ed554da79fe8bba4521909cc Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Mon, 24 Jun 2024 16:07:42 +0530 Subject: [PATCH 031/281] fix: trace detail api start and end time (#5325) --- pkg/query-service/app/clickhouseReader/reader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index d8c902055b..33b8e7ec08 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1993,8 +1993,8 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc } } - searchSpansResult[0].StartTimestampMillis = startTime - durationNano - searchSpansResult[0].EndTimestampMillis = endTime + durationNano + searchSpansResult[0].StartTimestampMillis = startTime - (durationNano/1000000) + searchSpansResult[0].EndTimestampMillis = endTime + (durationNano/1000000) return &searchSpansResult, nil } From 89b67b8880d0dd26a68abe101a1e3064d45f120e Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Mon, 24 Jun 2024 16:48:25 +0530 Subject: [PATCH 032/281] chore: posthog js init (#5324) * chore: posthog js init * feat: posthog events --------- Co-authored-by: YounixM --- frontend/package.json | 1 + frontend/src/AppRoutes/index.tsx | 42 +++++++++++++++++++++++--------- frontend/src/index.tsx | 8 ++++++ frontend/webpack.config.js | 2 ++ frontend/webpack.config.prod.js | 2 ++ frontend/yarn.lock | 18 ++++++++++++++ 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 25d32f69df..d78064278a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -88,6 +88,7 @@ "lucide-react": "0.379.0", "mini-css-extract-plugin": "2.4.5", "papaparse": "5.4.1", + "posthog-js": "1.140.1", "rc-tween-one": "3.0.6", "react": "18.2.0", "react-addons-update": "15.6.3", diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx index 645974204c..bc2b02d842 100644 --- a/frontend/src/AppRoutes/index.tsx +++ b/frontend/src/AppRoutes/index.tsx @@ -17,6 +17,7 @@ import { NotificationProvider } from 'hooks/useNotifications'; import { ResourceProvider } from 'hooks/useResourceAttribute'; import history from 'lib/history'; import { identity, pick, pickBy } from 'lodash-es'; +import posthog from 'posthog-js'; import { DashboardProvider } from 'providers/Dashboard/Dashboard'; import { QueryBuilderProvider } from 'providers/QueryBuilder'; import { Suspense, useEffect, useState } from 'react'; @@ -38,7 +39,7 @@ import defaultRoutes, { function App(): JSX.Element { const themeConfig = useThemeConfig(); - const { data } = useLicense(); + const { data: licenseData } = useLicense(); const [routes, setRoutes] = useState(defaultRoutes); const { role, isLoggedIn: isLoggedInState, user, org } = useSelector< AppState, @@ -92,10 +93,10 @@ function App(): JSX.Element { }); const isOnBasicPlan = - data?.payload?.licenses?.some( + licenseData?.payload?.licenses?.some( (license) => license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN, - ) || data?.payload?.licenses === null; + ) || licenseData?.payload?.licenses === null; const enableAnalytics = (user: User): void => { const orgName = @@ -112,9 +113,7 @@ function App(): JSX.Element { }; const sanitizedIdentifyPayload = pickBy(identifyPayload, identity); - const domain = extractDomain(email); - const hostNameParts = hostname.split('.'); const groupTraits = { @@ -127,10 +126,30 @@ function App(): JSX.Element { }; window.analytics.identify(email, sanitizedIdentifyPayload); - window.analytics.group(domain, groupTraits); - window.clarity('identify', email, name); + + posthog?.identify(email, { + email, + name, + orgName, + tenant_id: hostNameParts[0], + data_region: hostNameParts[1], + tenant_url: hostname, + company_domain: domain, + source: 'signoz-ui', + isPaidUser: !!licenseData?.payload?.trialConvertedToSubscription, + }); + + posthog?.group('company', domain, { + name: orgName, + tenant_id: hostNameParts[0], + data_region: hostNameParts[1], + tenant_url: hostname, + company_domain: domain, + source: 'signoz-ui', + isPaidUser: !!licenseData?.payload?.trialConvertedToSubscription, + }); }; useEffect(() => { @@ -144,10 +163,6 @@ function App(): JSX.Element { !isIdentifiedUser ) { setLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER, 'true'); - - if (isCloudUserVal) { - enableAnalytics(user); - } } if ( @@ -195,6 +210,11 @@ function App(): JSX.Element { console.error('Failed to parse local storage theme analytics event'); } } + + if (isCloudUserVal && user && user.email) { + enableAnalytics(user); + } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [user]); diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 45a1b6f11a..b83ca56731 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -6,6 +6,7 @@ import AppRoutes from 'AppRoutes'; import { AxiosError } from 'axios'; import { ThemeProvider } from 'hooks/useDarkMode'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; +import posthog from 'posthog-js'; import { createRoot } from 'react-dom/client'; import { HelmetProvider } from 'react-helmet-async'; import { QueryClient, QueryClientProvider } from 'react-query'; @@ -33,6 +34,13 @@ const queryClient = new QueryClient({ const container = document.getElementById('root'); +if (process.env.POSTHOG_KEY) { + posthog.init(process.env.POSTHOG_KEY, { + api_host: 'https://us.i.posthog.com', + person_profiles: 'identified_only', // or 'always' to create profiles for anonymous users as well + }); +} + Sentry.init({ dsn: process.env.SENTRY_DSN, tunnel: process.env.TUNNEL_URL, diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 65883594bb..2e5c0d0f4e 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -22,6 +22,7 @@ const plugins = [ template: 'src/index.html.ejs', INTERCOM_APP_ID: process.env.INTERCOM_APP_ID, SEGMENT_ID: process.env.SEGMENT_ID, + POSTHOG_KEY: process.env.POSTHOG_KEY, CLARITY_PROJECT_ID: process.env.CLARITY_PROJECT_ID, SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN, SENTRY_ORG: process.env.SENTRY_ORG, @@ -39,6 +40,7 @@ const plugins = [ FRONTEND_API_ENDPOINT: process.env.FRONTEND_API_ENDPOINT, INTERCOM_APP_ID: process.env.INTERCOM_APP_ID, SEGMENT_ID: process.env.SEGMENT_ID, + POSTHOG_KEY: process.env.POSTHOG_KEY, CLARITY_PROJECT_ID: process.env.CLARITY_PROJECT_ID, SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN, SENTRY_ORG: process.env.SENTRY_ORG, diff --git a/frontend/webpack.config.prod.js b/frontend/webpack.config.prod.js index 9b17d345c9..87ef8b7143 100644 --- a/frontend/webpack.config.prod.js +++ b/frontend/webpack.config.prod.js @@ -27,6 +27,7 @@ const plugins = [ template: 'src/index.html.ejs', INTERCOM_APP_ID: process.env.INTERCOM_APP_ID, SEGMENT_ID: process.env.SEGMENT_ID, + POSTHOG_KEY: process.env.POSTHOG_KEY, CLARITY_PROJECT_ID: process.env.CLARITY_PROJECT_ID, SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN, SENTRY_ORG: process.env.SENTRY_ORG, @@ -49,6 +50,7 @@ const plugins = [ FRONTEND_API_ENDPOINT: process.env.FRONTEND_API_ENDPOINT, INTERCOM_APP_ID: process.env.INTERCOM_APP_ID, SEGMENT_ID: process.env.SEGMENT_ID, + POSTHOG_KEY: process.env.POSTHOG_KEY, CLARITY_PROJECT_ID: process.env.CLARITY_PROJECT_ID, SENTRY_AUTH_TOKEN: process.env.SENTRY_AUTH_TOKEN, SENTRY_ORG: process.env.SENTRY_ORG, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c717a16507..295ae66012 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8776,6 +8776,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fflate@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" + integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA== + figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -13700,6 +13705,19 @@ postcss@8.4.38, postcss@^8.0.0, postcss@^8.1.1, postcss@^8.3.7, postcss@^8.4.21, picocolors "^1.0.0" source-map-js "^1.2.0" +posthog-js@1.140.1: + version "1.140.1" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.140.1.tgz#34efc0d326fa5fcf7950106f350fb4f0e73b2da6" + integrity sha512-UeKuAtQSvbzmTCzNVaauku8F194EYwAP33WrRrWZlDlMNbMy7GKcZOgKbr7jZqnha7FlVlHrWk+Rpyr1zCFhPQ== + dependencies: + fflate "^0.4.8" + preact "^10.19.3" + +preact@^10.19.3: + version "10.22.0" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.22.0.tgz#a50f38006ae438d255e2631cbdaf7488e6dd4e16" + integrity sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" From 415057c260f0b961dabb22563b82f409df37b465 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Mon, 24 Jun 2024 16:57:05 +0530 Subject: [PATCH 033/281] feat: go to traces should use start time and end time from trace details (#5326) * feat: go to traces should use start time and endtime from trace details * chore: remove console log --------- Co-authored-by: Vishal Sharma --- .../TraceDetail/SelectedSpanDetails/index.tsx | 15 ++++++++++++--- frontend/src/container/TraceDetail/index.tsx | 8 ++++++++ frontend/src/types/api/trace/getTraceItem.ts | 2 ++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx b/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx index 4f12c47470..ce7f3372a3 100644 --- a/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx +++ b/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx @@ -19,12 +19,17 @@ import { CardContainer, CustomSubText, styles } from './styles'; import Tags from './Tags'; function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { - const { tree, firstSpanStartTime } = props; - const { maxTime, minTime } = useSelector( (state) => state.globalTime, ); + const { + tree, + firstSpanStartTime, + traceStartTime = minTime, + traceEndTime = maxTime, + } = props; + const { id: traceId } = useParams(); const isDarkMode = useIsDarkMode(); @@ -74,7 +79,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { ]; const onLogsHandler = (): void => { - const query = getTraceToLogsQuery(traceId, minTime, maxTime); + const query = getTraceToLogsQuery(traceId, traceStartTime, traceEndTime); history.push( `${ROUTES.LOGS_EXPLORER}?${createQueryParams({ @@ -140,10 +145,14 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { interface SelectedSpanDetailsProps { tree?: ITraceTree; firstSpanStartTime: number; + traceStartTime?: number; + traceEndTime?: number; } SelectedSpanDetails.defaultProps = { tree: undefined, + traceStartTime: undefined, + traceEndTime: undefined, }; export interface ModalText { diff --git a/frontend/src/container/TraceDetail/index.tsx b/frontend/src/container/TraceDetail/index.tsx index 4b333e0dad..568ed3c4f4 100644 --- a/frontend/src/container/TraceDetail/index.tsx +++ b/frontend/src/container/TraceDetail/index.tsx @@ -48,6 +48,12 @@ function TraceDetail({ response }: TraceDetailProps): JSX.Element { [response], ); + const traceStartTime = useMemo(() => response[0].startTimestampMillis, [ + response, + ]); + + const traceEndTime = useMemo(() => response[0].endTimestampMillis, [response]); + const urlQuery = useUrlQuery(); const [spanId] = useState(urlQuery.get('spanId')); @@ -260,6 +266,8 @@ function TraceDetail({ response }: TraceDetailProps): JSX.Element { Date: Mon, 24 Jun 2024 18:17:34 +0530 Subject: [PATCH 034/281] fix: stacked series no data case (#5328) --- frontend/src/lib/uPlotLib/getUplotChartOptions.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/uPlotLib/getUplotChartOptions.ts b/frontend/src/lib/uPlotLib/getUplotChartOptions.ts index dd08cbfff1..29904f0d6a 100644 --- a/frontend/src/lib/uPlotLib/getUplotChartOptions.ts +++ b/frontend/src/lib/uPlotLib/getUplotChartOptions.ts @@ -64,6 +64,10 @@ export interface GetUPlotChartOptions { function getStackedSeries(apiResponse: QueryData[]): QueryData[] { const series = cloneDeep(apiResponse); + if (!series) { + return series; + } + for (let i = series.length - 2; i >= 0; i--) { const { values } = series[i]; for (let j = 0; j < values.length; j++) { @@ -84,6 +88,9 @@ function getStackedSeries(apiResponse: QueryData[]): QueryData[] { */ function getStackedSeriesQueryFormat(apiResponse: QueryData[]): QueryData[] { const series = cloneDeep(apiResponse); + if (!series) { + return apiResponse; + } for (let i = series.length - 2; i >= 0; i--) { const { values } = series[i]; @@ -102,9 +109,12 @@ function getStackedSeriesQueryFormat(apiResponse: QueryData[]): QueryData[] { function getStackedSeriesYAxis(apiResponse: QueryDataV3[]): QueryDataV3[] { const series = cloneDeep(apiResponse); + if (!series) { + return apiResponse; + } for (let i = 0; i < series.length; i++) { - series[i].series = getStackedSeriesQueryFormat(series[i].series); + series[i].series = getStackedSeriesQueryFormat(series[i].series || []); } return series; From d5f156a6e9626a055d660ee4396a5b7a9ac15684 Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Mon, 24 Jun 2024 19:24:42 +0530 Subject: [PATCH 035/281] ci(push): include POSTHOG_KEY environment in frontend (#5327) Signed-off-by: Prashant Shahi --- .github/workflows/push.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml index 6869cf7fb7..7808f9d18e 100644 --- a/.github/workflows/push.yaml +++ b/.github/workflows/push.yaml @@ -158,6 +158,7 @@ jobs: 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 - name: Install dependencies working-directory: frontend run: yarn install From 8ccdc71eaf1247694780b2de415781ff78f84084 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 25 Jun 2024 10:10:33 +0530 Subject: [PATCH 036/281] chore: remove deprecated option (#5239) --- ee/query-service/app/api/api.go | 2 -- ee/query-service/app/server.go | 2 -- ee/query-service/main.go | 5 +---- pkg/query-service/app/http_handler.go | 3 --- pkg/query-service/app/server.go | 2 -- pkg/query-service/main.go | 5 +---- 6 files changed, 2 insertions(+), 17 deletions(-) diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index be0cf1ec36..66b462e167 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -24,7 +24,6 @@ import ( type APIHandlerOptions struct { DataConnector interfaces.DataConnector SkipConfig *basemodel.SkipConfig - PreferDelta bool PreferSpanMetrics bool MaxIdleConns int MaxOpenConns int @@ -53,7 +52,6 @@ func NewAPIHandler(opts APIHandlerOptions) (*APIHandler, error) { baseHandler, err := baseapp.NewAPIHandler(baseapp.APIHandlerOpts{ Reader: opts.DataConnector, SkipConfig: opts.SkipConfig, - PerferDelta: opts.PreferDelta, PreferSpanMetrics: opts.PreferSpanMetrics, MaxIdleConns: opts.MaxIdleConns, MaxOpenConns: opts.MaxOpenConns, diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index 75af1d7ebc..9a2c96734f 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -64,7 +64,6 @@ type ServerOptions struct { // alert specific params DisableRules bool RuleRepoURL string - PreferDelta bool PreferSpanMetrics bool MaxIdleConns int MaxOpenConns int @@ -256,7 +255,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { apiOpts := api.APIHandlerOptions{ DataConnector: reader, SkipConfig: skipConfig, - PreferDelta: serverOptions.PreferDelta, PreferSpanMetrics: serverOptions.PreferSpanMetrics, MaxIdleConns: serverOptions.MaxIdleConns, MaxOpenConns: serverOptions.MaxOpenConns, diff --git a/ee/query-service/main.go b/ee/query-service/main.go index 4a8a12af6e..c5a03f4c0f 100644 --- a/ee/query-service/main.go +++ b/ee/query-service/main.go @@ -89,7 +89,6 @@ func main() { var cacheConfigPath, fluxInterval string var enableQueryServiceLogOTLPExport bool - var preferDelta bool var preferSpanMetrics bool var maxIdleConns int @@ -100,14 +99,13 @@ func main() { flag.StringVar(&promConfigPath, "config", "./config/prometheus.yml", "(prometheus config to read metrics)") flag.StringVar(&skipTopLvlOpsPath, "skip-top-level-ops", "", "(config file to skip top level operations)") flag.BoolVar(&disableRules, "rules.disable", false, "(disable rule evaluation)") - flag.BoolVar(&preferDelta, "prefer-delta", false, "(prefer delta over cumulative metrics)") flag.BoolVar(&preferSpanMetrics, "prefer-span-metrics", false, "(prefer span metrics for service level metrics)") flag.IntVar(&maxIdleConns, "max-idle-conns", 50, "(number of connections to maintain in the pool.)") flag.IntVar(&maxOpenConns, "max-open-conns", 100, "(max connections for use at any time.)") flag.DurationVar(&dialTimeout, "dial-timeout", 5*time.Second, "(the maximum time to establish a connection.)") flag.StringVar(&ruleRepoURL, "rules.repo-url", baseconst.AlertHelpPage, "(host address used to build rule link in alert messages)") flag.StringVar(&cacheConfigPath, "experimental.cache-config", "", "(cache config to use)") - flag.StringVar(&fluxInterval, "flux-interval", "5m", "(cache config to use)") + flag.StringVar(&fluxInterval, "flux-interval", "5m", "(the interval to exclude data from being cached to avoid incorrect cache for data in motion)") flag.BoolVar(&enableQueryServiceLogOTLPExport, "enable.query.service.log.otlp.export", false, "(enable query service log otlp export)") flag.StringVar(&cluster, "cluster", "cluster", "(cluster name - defaults to 'cluster')") flag.StringVar(&gatewayUrl, "gateway-url", "", "(url to the gateway)") @@ -125,7 +123,6 @@ func main() { HTTPHostPort: baseconst.HTTPHostPort, PromConfigPath: promConfigPath, SkipTopLvlOpsPath: skipTopLvlOpsPath, - PreferDelta: preferDelta, PreferSpanMetrics: preferSpanMetrics, PrivateHostPort: baseconst.PrivateHostPort, DisableRules: disableRules, diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 1f0769bb08..70c8fd3dd3 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -76,7 +76,6 @@ type APIHandler struct { querier interfaces.Querier querierV2 interfaces.Querier queryBuilder *queryBuilder.QueryBuilder - preferDelta bool preferSpanMetrics bool // temporalityMap is a map of metric name to temporality @@ -106,7 +105,6 @@ type APIHandlerOpts struct { SkipConfig *model.SkipConfig - PerferDelta bool PreferSpanMetrics bool MaxIdleConns int @@ -166,7 +164,6 @@ func NewAPIHandler(opts APIHandlerOpts) (*APIHandler, error) { reader: opts.Reader, appDao: opts.AppDao, skipConfig: opts.SkipConfig, - preferDelta: opts.PerferDelta, preferSpanMetrics: opts.PreferSpanMetrics, temporalityMap: make(map[string]map[v3.Temporality]bool), maxIdleConns: opts.MaxIdleConns, diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index 92b879fcc9..2260045f4d 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -55,7 +55,6 @@ type ServerOptions struct { // alert specific params DisableRules bool RuleRepoURL string - PreferDelta bool PreferSpanMetrics bool MaxIdleConns int MaxOpenConns int @@ -172,7 +171,6 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { apiHandler, err := NewAPIHandler(APIHandlerOpts{ Reader: reader, SkipConfig: skipConfig, - PerferDelta: serverOptions.PreferDelta, PreferSpanMetrics: serverOptions.PreferSpanMetrics, MaxIdleConns: serverOptions.MaxIdleConns, MaxOpenConns: serverOptions.MaxOpenConns, diff --git a/pkg/query-service/main.go b/pkg/query-service/main.go index 793ce25bf2..3063e07b12 100644 --- a/pkg/query-service/main.go +++ b/pkg/query-service/main.go @@ -37,7 +37,6 @@ func main() { var ruleRepoURL, cacheConfigPath, fluxInterval string var cluster string - var preferDelta bool var preferSpanMetrics bool var maxIdleConns int @@ -47,11 +46,10 @@ func main() { flag.StringVar(&promConfigPath, "config", "./config/prometheus.yml", "(prometheus config to read metrics)") flag.StringVar(&skipTopLvlOpsPath, "skip-top-level-ops", "", "(config file to skip top level operations)") flag.BoolVar(&disableRules, "rules.disable", false, "(disable rule evaluation)") - flag.BoolVar(&preferDelta, "prefer-delta", false, "(prefer delta over cumulative metrics)") flag.BoolVar(&preferSpanMetrics, "prefer-span-metrics", false, "(prefer span metrics for service level metrics)") flag.StringVar(&ruleRepoURL, "rules.repo-url", constants.AlertHelpPage, "(host address used to build rule link in alert messages)") flag.StringVar(&cacheConfigPath, "experimental.cache-config", "", "(cache config to use)") - flag.StringVar(&fluxInterval, "flux-interval", "5m", "(cache config to use)") + flag.StringVar(&fluxInterval, "flux-interval", "5m", "(the interval to exclude data from being cached to avoid incorrect cache for data in motion)") flag.StringVar(&cluster, "cluster", "cluster", "(cluster name - defaults to 'cluster')") // Allow using the consistent naming with the signoz collector flag.StringVar(&cluster, "cluster-name", "cluster", "(cluster name - defaults to 'cluster')") @@ -71,7 +69,6 @@ func main() { HTTPHostPort: constants.HTTPHostPort, PromConfigPath: promConfigPath, SkipTopLvlOpsPath: skipTopLvlOpsPath, - PreferDelta: preferDelta, PreferSpanMetrics: preferSpanMetrics, PrivateHostPort: constants.PrivateHostPort, DisableRules: disableRules, From 873280abea632fb070dbfe59c0a6c9c7f26c8e2a Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 25 Jun 2024 10:32:44 +0530 Subject: [PATCH 037/281] chore: read double pointer numbers from result (#5300) --- .../app/clickhouseReader/reader.go | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 33b8e7ec08..20eb11d479 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -4444,6 +4444,21 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ } groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Float()) } + case **float64, **float32: + val := reflect.ValueOf(v) + if val.IsValid() && !val.IsNil() && !val.Elem().IsNil() { + isValidPoint = true + value := reflect.ValueOf(v).Elem().Elem().Float() + if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + point.Value = value + } else { + groupBy = append(groupBy, fmt.Sprintf("%v", value)) + if _, ok := groupAttributes[colName]; !ok { + groupAttributesArray = append(groupAttributesArray, map[string]string{colName: fmt.Sprintf("%v", value)}) + } + groupAttributes[colName] = fmt.Sprintf("%v", value) + } + } case *uint, *uint8, *uint64, *uint16, *uint32: isValidPoint = true if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { @@ -4455,6 +4470,21 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ } groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Uint()) } + case **uint, **uint8, **uint64, **uint16, **uint32: + val := reflect.ValueOf(v) + if val.IsValid() && !val.IsNil() && !val.Elem().IsNil() { + isValidPoint = true + value := reflect.ValueOf(v).Elem().Elem().Uint() + if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + point.Value = float64(value) + } else { + groupBy = append(groupBy, fmt.Sprintf("%v", value)) + if _, ok := groupAttributes[colName]; !ok { + groupAttributesArray = append(groupAttributesArray, map[string]string{colName: fmt.Sprintf("%v", value)}) + } + groupAttributes[colName] = fmt.Sprintf("%v", value) + } + } case *int, *int8, *int16, *int32, *int64: isValidPoint = true if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { @@ -4466,6 +4496,21 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ } groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Int()) } + case **int, **int8, **int16, **int32, **int64: + val := reflect.ValueOf(v) + if val.IsValid() && !val.IsNil() && !val.Elem().IsNil() { + isValidPoint = true + value := reflect.ValueOf(v).Elem().Elem().Int() + if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + point.Value = float64(value) + } else { + groupBy = append(groupBy, fmt.Sprintf("%v", value)) + if _, ok := groupAttributes[colName]; !ok { + groupAttributesArray = append(groupAttributesArray, map[string]string{colName: fmt.Sprintf("%v", value)}) + } + groupAttributes[colName] = fmt.Sprintf("%v", value) + } + } case *bool: groupBy = append(groupBy, fmt.Sprintf("%v", *v)) if _, ok := groupAttributes[colName]; !ok { From 5df25e83d1f43434d21d2721686b37e26720cb30 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 25 Jun 2024 10:56:52 +0530 Subject: [PATCH 038/281] fix: make the license key check case insensitive (#5331) * fix: make the license key check case insensitive * fix: added safety checks --- frontend/src/container/Header/index.tsx | 9 +++++++-- frontend/src/container/SideNav/SideNav.tsx | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/Header/index.tsx b/frontend/src/container/Header/index.tsx index 191f83e38b..af24bdc4eb 100644 --- a/frontend/src/container/Header/index.tsx +++ b/frontend/src/container/Header/index.tsx @@ -27,6 +27,7 @@ import { import { useSelector } from 'react-redux'; import { NavLink } from 'react-router-dom'; import { AppState } from 'store/reducers'; +import { License } from 'types/api/licenses/def'; import AppReducer from 'types/reducer/app'; import { getFormattedDate, getRemainingDays } from 'utils/timeUtils'; @@ -109,9 +110,13 @@ function HeaderContainer(): JSX.Element { const { data: licenseData, isFetching, status: licenseStatus } = useLicense(); + const licensesStatus: string = + licenseData?.payload?.licenses?.find((e: License) => e.isCurrent)?.status || + ''; + const isLicenseActive = - licenseData?.payload?.licenses?.find((e) => e.isCurrent)?.status === - LICENSE_PLAN_STATUS.VALID; + licensesStatus?.toLocaleLowerCase() === + LICENSE_PLAN_STATUS.VALID.toLocaleLowerCase(); useEffect(() => { if ( diff --git a/frontend/src/container/SideNav/SideNav.tsx b/frontend/src/container/SideNav/SideNav.tsx index 6cec0448b2..82697d78b0 100644 --- a/frontend/src/container/SideNav/SideNav.tsx +++ b/frontend/src/container/SideNav/SideNav.tsx @@ -152,9 +152,13 @@ function SideNav({ const { t } = useTranslation(''); + const licenseStatus: string = + licenseData?.payload?.licenses?.find((e: License) => e.isCurrent)?.status || + ''; + const isLicenseActive = - licenseData?.payload?.licenses?.find((e: License) => e.isCurrent)?.status === - LICENSE_PLAN_STATUS.VALID; + licenseStatus?.toLocaleLowerCase() === + LICENSE_PLAN_STATUS.VALID.toLocaleLowerCase(); const isEnterprise = licenseData?.payload?.licenses?.some( (license: License) => From 52e030399773ae8b362be5117f464c952d691387 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 25 Jun 2024 13:42:40 +0530 Subject: [PATCH 039/281] fix: table order by with builder queries (#5308) --- pkg/query-service/app/http_handler.go | 14 +- pkg/query-service/model/v3/v3.go | 22 +- pkg/query-service/postprocess/process.go | 7 + pkg/query-service/postprocess/table.go | 299 +++++++++ pkg/query-service/postprocess/table_test.go | 668 ++++++++++++++++++++ 5 files changed, 1006 insertions(+), 4 deletions(-) create mode 100644 pkg/query-service/postprocess/table.go create mode 100644 pkg/query-service/postprocess/table_test.go diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 70c8fd3dd3..d6c91558a5 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -3013,6 +3013,7 @@ func (aH *APIHandler) QueryRangeV3Format(w http.ResponseWriter, r *http.Request) RespondError(w, apiErrorObj, nil) return } + queryRangeParams.Version = "v3" aH.Respond(w, queryRangeParams) } @@ -3067,6 +3068,14 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que postprocess.FillGaps(result, queryRangeParams) } + if queryRangeParams.CompositeQuery.PanelType == v3.PanelTypeTable && queryRangeParams.FormatForWeb { + if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL { + result = postprocess.TransformToTableForClickHouseQueries(result) + } else if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder { + result = postprocess.TransformToTableForBuilderQueries(result, queryRangeParams) + } + } + resp := v3.QueryRangeResponse{ Result: result, } @@ -3315,8 +3324,10 @@ func (aH *APIHandler) queryRangeV4(ctx context.Context, queryRangeParams *v3.Que } if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder { - result, err = postprocess.PostProcessResult(result, queryRangeParams) + } else if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL && + queryRangeParams.CompositeQuery.PanelType == v3.PanelTypeTable && queryRangeParams.FormatForWeb { + result = postprocess.TransformToTableForClickHouseQueries(result) } if err != nil { @@ -3340,6 +3351,7 @@ func (aH *APIHandler) QueryRangeV4(w http.ResponseWriter, r *http.Request) { RespondError(w, apiErrorObj, nil) return } + queryRangeParams.Version = "v4" // add temporality for each metric temporalityErr := aH.populateTemporality(r.Context(), queryRangeParams) diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index 2a12c8e1fa..7e6daa3751 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -354,6 +354,8 @@ type QueryRangeParamsV3 struct { CompositeQuery *CompositeQuery `json:"compositeQuery"` Variables map[string]interface{} `json:"variables,omitempty"` NoCache bool `json:"noCache"` + Version string `json:"-"` + FormatForWeb bool `json:"formatForWeb,omitempty"` } type PromQuery struct { @@ -986,10 +988,24 @@ type QueryRangeResponse struct { Result []*Result `json:"result"` } +type TableColumn struct { + Name string `json:"name"` +} + +type TableRow struct { + Data []interface{} `json:"data"` +} + +type Table struct { + Columns []*TableColumn `json:"columns"` + Rows []*TableRow `json:"rows"` +} + type Result struct { - QueryName string `json:"queryName"` - Series []*Series `json:"series"` - List []*Row `json:"list"` + QueryName string `json:"queryName,omitempty"` + Series []*Series `json:"series,omitempty"` + List []*Row `json:"list,omitempty"` + Table *Table `json:"table,omitempty"` } type LogsLiveTailClient struct { diff --git a/pkg/query-service/postprocess/process.go b/pkg/query-service/postprocess/process.go index fc35b404de..1f9ace33eb 100644 --- a/pkg/query-service/postprocess/process.go +++ b/pkg/query-service/postprocess/process.go @@ -86,6 +86,13 @@ func PostProcessResult(result []*v3.Result, queryRangeParams *v3.QueryRangeParam if queryRangeParams.CompositeQuery.FillGaps { FillGaps(result, queryRangeParams) } + + if queryRangeParams.FormatForWeb && + queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder && + queryRangeParams.CompositeQuery.PanelType == v3.PanelTypeTable { + result = TransformToTableForBuilderQueries(result, queryRangeParams) + } + return result, nil } diff --git a/pkg/query-service/postprocess/table.go b/pkg/query-service/postprocess/table.go new file mode 100644 index 0000000000..1599bf37be --- /dev/null +++ b/pkg/query-service/postprocess/table.go @@ -0,0 +1,299 @@ +package postprocess + +import ( + "fmt" + "sort" + "strings" + + "go.signoz.io/signoz/pkg/query-service/constants" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" +) + +func getAutoColNameForQuery(queryName string, params *v3.QueryRangeParamsV3) string { + q := params.CompositeQuery.BuilderQueries[queryName] + if q.DataSource == v3.DataSourceTraces || q.DataSource == v3.DataSourceLogs { + if q.AggregateAttribute.Key != "" { + return fmt.Sprintf("%s(%s)", q.AggregateOperator, q.AggregateAttribute.Key) + } + return string(q.AggregateOperator) + } else if q.DataSource == v3.DataSourceMetrics { + if q.SpaceAggregation != "" && params.Version == "v4" { + return fmt.Sprintf("%s(%s)", q.SpaceAggregation, q.AggregateAttribute.Key) + } + return fmt.Sprintf("%s(%s)", q.AggregateOperator, q.AggregateAttribute.Key) + } + return queryName +} + +func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRangeParamsV3) []*v3.Result { + if len(results) == 0 { + return []*v3.Result{} + } + + // Sort results by QueryName + sort.Slice(results, func(i, j int) bool { + return results[i].QueryName < results[j].QueryName + }) + + // Create a map to store all unique labels + seen := make(map[string]struct{}) + labelKeys := []string{} + for _, result := range results { + for _, series := range result.Series { + for _, labels := range series.LabelsArray { + for key := range labels { + if _, ok := seen[key]; !ok { + seen[key] = struct{}{} + labelKeys = append(labelKeys, key) + } + } + } + } + } + + // Create columns + // There will be one column for each label key and one column for each query name + columns := make([]*v3.TableColumn, 0, len(labelKeys)+len(results)) + for _, key := range labelKeys { + columns = append(columns, &v3.TableColumn{Name: key}) + } + for _, result := range results { + columns = append(columns, &v3.TableColumn{Name: result.QueryName}) + } + + // Create a map to store unique rows + rowMap := make(map[string]*v3.TableRow) + + for _, result := range results { + for _, series := range result.Series { + if len(series.Points) == 0 { + continue + } + + // Create a key for the row based on labels + var keyParts []string + rowData := make([]interface{}, len(columns)) + for i, key := range labelKeys { + value := "n/a" + for _, labels := range series.LabelsArray { + if v, ok := labels[key]; ok { + value = v + break + } + } + keyParts = append(keyParts, fmt.Sprintf("%s=%s", key, value)) + rowData[i] = value + } + rowKey := strings.Join(keyParts, ",") + + // Get or create the row + row, ok := rowMap[rowKey] + if !ok { + row = &v3.TableRow{Data: rowData} + rowMap[rowKey] = row + } + + // Add the value for this query + for i, col := range columns { + if col.Name == result.QueryName { + row.Data[i] = series.Points[0].Value + break + } + } + } + } + + // Convert rowMap to a slice of TableRows + rows := make([]*v3.TableRow, 0, len(rowMap)) + for _, row := range rowMap { + for i, value := range row.Data { + if value == nil { + row.Data[i] = "n/a" + } + } + rows = append(rows, row) + } + + // Get sorted query names + queryNames := make([]string, 0, len(params.CompositeQuery.BuilderQueries)) + for queryName := range params.CompositeQuery.BuilderQueries { + queryNames = append(queryNames, queryName) + } + sort.Strings(queryNames) + + // Sort rows based on OrderBy from BuilderQueries + sortRows(rows, columns, params.CompositeQuery.BuilderQueries, queryNames) + + for _, column := range columns { + if _, exists := params.CompositeQuery.BuilderQueries[column.Name]; exists { + column.Name = getAutoColNameForQuery(column.Name, params) + } + } + + // Create the final result + tableResult := v3.Result{ + Table: &v3.Table{ + Columns: columns, + Rows: rows, + }, + } + + return []*v3.Result{&tableResult} +} + +func sortRows(rows []*v3.TableRow, columns []*v3.TableColumn, builderQueries map[string]*v3.BuilderQuery, queryNames []string) { + sort.SliceStable(rows, func(i, j int) bool { + for _, queryName := range queryNames { + query := builderQueries[queryName] + orderByList := query.OrderBy + if len(orderByList) == 0 { + // If no orderBy is specified, sort by value in descending order + orderByList = []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "desc"}} + } + for _, orderBy := range orderByList { + name := orderBy.ColumnName + if name == constants.SigNozOrderByValue { + name = queryName + } + colIndex := -1 + for k, col := range columns { + if col.Name == name { + colIndex = k + break + } + } + if colIndex == -1 { + continue + } + + valI := rows[i].Data[colIndex] + valJ := rows[j].Data[colIndex] + + // Handle "n/a" values + if valI == "n/a" && valJ == "n/a" { + continue + } + + // Compare based on the data type + switch v := valI.(type) { + case float64: + switch w := valJ.(type) { + case float64: + if v != w { + return (v < w) == (orderBy.Order == "asc") + } + default: + // For any other type, sort float64 first + return orderBy.Order == "asc" + } + case string: + switch w := valJ.(type) { + case float64: + // If types are different, sort numbers before strings + return orderBy.Order != "asc" + case string: + if v != w { + return (v < w) == (orderBy.Order == "asc") + } + default: + // For any other type, sort strings before bools + return orderBy.Order == "asc" + } + case bool: + switch w := valJ.(type) { + case float64, string: + // If types are different, sort bools after numbers and strings + return orderBy.Order != "asc" + case bool: + if v != w { + return (!v && w) == (orderBy.Order == "asc") + } + } + } + } + } + return false + }) +} + +func TransformToTableForClickHouseQueries(results []*v3.Result) []*v3.Result { + if len(results) == 0 { + return []*v3.Result{} + } + + // Sort results by QueryName + sort.Slice(results, func(i, j int) bool { + return results[i].QueryName < results[j].QueryName + }) + + // Create a map to store all unique labels + seen := make(map[string]struct{}) + labelKeys := []string{} + for _, result := range results { + for _, series := range result.Series { + for _, labels := range series.LabelsArray { + for key := range labels { + if _, ok := seen[key]; !ok { + seen[key] = struct{}{} + labelKeys = append(labelKeys, key) + } + } + } + } + } + + // Create columns + // Why don't we have a column for each query name? + // Because we don't know if the query is an aggregation query or a non-aggregation query + // So we create a column for each query name that has at least one point + columns := make([]*v3.TableColumn, 0) + for _, key := range labelKeys { + columns = append(columns, &v3.TableColumn{Name: key}) + } + for _, result := range results { + if len(result.Series) > 0 && len(result.Series[0].Points) > 0 { + columns = append(columns, &v3.TableColumn{Name: result.QueryName}) + } + } + + rows := make([]*v3.TableRow, 0) + for _, result := range results { + for _, series := range result.Series { + + // Create a key for the row based on labels + rowData := make([]interface{}, len(columns)) + for i, key := range labelKeys { + value := "n/a" + for _, labels := range series.LabelsArray { + if v, ok := labels[key]; ok { + value = v + break + } + } + rowData[i] = value + } + + // Get or create the row + row := &v3.TableRow{Data: rowData} + + // Add the value for this query + for i, col := range columns { + if col.Name == result.QueryName && len(series.Points) > 0 { + row.Data[i] = series.Points[0].Value + break + } + } + rows = append(rows, row) + } + } + + // Create the final result + tableResult := v3.Result{ + Table: &v3.Table{ + Columns: columns, + Rows: rows, + }, + } + + return []*v3.Result{&tableResult} +} diff --git a/pkg/query-service/postprocess/table_test.go b/pkg/query-service/postprocess/table_test.go new file mode 100644 index 0000000000..6e8f588a5f --- /dev/null +++ b/pkg/query-service/postprocess/table_test.go @@ -0,0 +1,668 @@ +package postprocess + +import ( + "encoding/json" + "reflect" + "testing" + + "go.signoz.io/signoz/pkg/query-service/constants" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" +) + +func TestSortRows(t *testing.T) { + tests := []struct { + name string + rows []*v3.TableRow + columns []*v3.TableColumn + builderQueries map[string]*v3.BuilderQuery + queryNames []string + expected []*v3.TableRow + }{ + { + name: "Sort by single numeric query, ascending order", + rows: []*v3.TableRow{ + {Data: []interface{}{"service2", 20.0}}, + {Data: []interface{}{"service1", 10.0}}, + {Data: []interface{}{"service3", 30.0}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "asc"}}}, + }, + queryNames: []string{"A"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service1", 10.0}}, + {Data: []interface{}{"service2", 20.0}}, + {Data: []interface{}{"service3", 30.0}}, + }, + }, + { + name: "Sort by single numeric query, descending order", + rows: []*v3.TableRow{ + {Data: []interface{}{"service2", 20.0}}, + {Data: []interface{}{"service1", 10.0}}, + {Data: []interface{}{"service3", 30.0}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "desc"}}}, + }, + queryNames: []string{"A"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service3", 30.0}}, + {Data: []interface{}{"service2", 20.0}}, + {Data: []interface{}{"service1", 10.0}}, + }, + }, + { + name: "Sort by single string query, ascending order", + rows: []*v3.TableRow{ + {Data: []interface{}{"service2", "b"}}, + {Data: []interface{}{"service1", "c"}}, + {Data: []interface{}{"service3", "a"}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, + }, + queryNames: []string{"A"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service3", "a"}}, + {Data: []interface{}{"service2", "b"}}, + {Data: []interface{}{"service1", "c"}}, + }, + }, + { + name: "Sort with n/a values", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", 10.0, "n/a"}}, + {Data: []interface{}{"service2", "n/a", 15.0}}, + {Data: []interface{}{"service3", 30.0, 25.0}}, + {Data: []interface{}{"service4", "n/a", "n/a"}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + {Name: "B"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "asc"}}}, + "B": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "desc"}}}, + }, + queryNames: []string{"A", "B"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service1", 10.0, "n/a"}}, + {Data: []interface{}{"service3", 30.0, 25.0}}, + {Data: []interface{}{"service4", "n/a", "n/a"}}, + {Data: []interface{}{"service2", "n/a", 15.0}}, + }, + }, + { + name: "Sort with different data types", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", "string", 10.0, true}}, + {Data: []interface{}{"service2", 20.0, "string", false}}, + {Data: []interface{}{"service3", true, 30.0, "string"}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + {Name: "B"}, + {Name: "C"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "asc"}}}, + "B": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "desc"}}}, + "C": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "asc"}}}, + }, + queryNames: []string{"A", "B", "C"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service2", 20.0, "string", false}}, + {Data: []interface{}{"service1", "string", 10.0, true}}, + {Data: []interface{}{"service3", true, 30.0, "string"}}, + }, + }, + { + name: "Sort with SigNozOrderByValue", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", 20.0}}, + {Data: []interface{}{"service2", 10.0}}, + {Data: []interface{}{"service3", 30.0}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "desc"}}}, + }, + queryNames: []string{"A"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service3", 30.0}}, + {Data: []interface{}{"service1", 20.0}}, + {Data: []interface{}{"service2", 10.0}}, + }, + }, + { + name: "Sort by multiple queries with mixed types", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", 10.0, "b", true}}, + {Data: []interface{}{"service2", 20.0, "a", false}}, + {Data: []interface{}{"service3", 10.0, "c", true}}, + {Data: []interface{}{"service4", 20.0, "b", false}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + {Name: "B"}, + {Name: "C"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, + "B": {OrderBy: []v3.OrderBy{{ColumnName: "B", Order: "desc"}}}, + "C": {OrderBy: []v3.OrderBy{{ColumnName: "C", Order: "asc"}}}, + }, + queryNames: []string{"A", "B", "C"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service3", 10.0, "c", true}}, + {Data: []interface{}{"service1", 10.0, "b", true}}, + {Data: []interface{}{"service4", 20.0, "b", false}}, + {Data: []interface{}{"service2", 20.0, "a", false}}, + }, + }, + { + name: "Sort with all n/a values", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", "n/a", "n/a"}}, + {Data: []interface{}{"service2", "n/a", "n/a"}}, + {Data: []interface{}{"service3", "n/a", "n/a"}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + {Name: "B"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, + "B": {OrderBy: []v3.OrderBy{{ColumnName: "B", Order: "desc"}}}, + }, + queryNames: []string{"A", "B"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service1", "n/a", "n/a"}}, + {Data: []interface{}{"service2", "n/a", "n/a"}}, + {Data: []interface{}{"service3", "n/a", "n/a"}}, + }, + }, + { + name: "Sort with negative numbers", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", -10.0}}, + {Data: []interface{}{"service2", 20.0}}, + {Data: []interface{}{"service3", -30.0}}, + {Data: []interface{}{"service4", 0.0}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, + }, + queryNames: []string{"A"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service3", -30.0}}, + {Data: []interface{}{"service1", -10.0}}, + {Data: []interface{}{"service4", 0.0}}, + {Data: []interface{}{"service2", 20.0}}, + }, + }, + { + name: "Sort with mixed case strings", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", "Apple"}}, + {Data: []interface{}{"service2", "banana"}}, + {Data: []interface{}{"service3", "Cherry"}}, + {Data: []interface{}{"service4", "date"}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, + }, + queryNames: []string{"A"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service1", "Apple"}}, + {Data: []interface{}{"service3", "Cherry"}}, + {Data: []interface{}{"service2", "banana"}}, + {Data: []interface{}{"service4", "date"}}, + }, + }, + { + name: "Sort with empty strings", + rows: []*v3.TableRow{ + {Data: []interface{}{"service1", ""}}, + {Data: []interface{}{"service2", "b"}}, + {Data: []interface{}{"service3", ""}}, + {Data: []interface{}{"service4", "a"}}, + }, + columns: []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + }, + builderQueries: map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, + }, + queryNames: []string{"A"}, + expected: []*v3.TableRow{ + {Data: []interface{}{"service1", ""}}, + {Data: []interface{}{"service3", ""}}, + {Data: []interface{}{"service4", "a"}}, + {Data: []interface{}{"service2", "b"}}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sortRows(tt.rows, tt.columns, tt.builderQueries, tt.queryNames) + if !reflect.DeepEqual(tt.rows, tt.expected) { + exp, _ := json.Marshal(tt.expected) + got, _ := json.Marshal(tt.rows) + t.Errorf("sortRows() = %v, want %v", string(got), string(exp)) + } + }) + } +} + +func TestSortRowsWithEmptyQueries(t *testing.T) { + rows := []*v3.TableRow{ + {Data: []interface{}{"service1", 20.0}}, + {Data: []interface{}{"service2", 10.0}}, + {Data: []interface{}{"service3", 30.0}}, + } + columns := []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + } + builderQueries := map[string]*v3.BuilderQuery{} + queryNames := []string{} + + sortRows(rows, columns, builderQueries, queryNames) + + // Expect the original order to be maintained + expected := []*v3.TableRow{ + {Data: []interface{}{"service1", 20.0}}, + {Data: []interface{}{"service2", 10.0}}, + {Data: []interface{}{"service3", 30.0}}, + } + + if !reflect.DeepEqual(rows, expected) { + t.Errorf("sortRows() with empty queries = %v, want %v", rows, expected) + } +} + +func TestSortRowsWithInvalidColumnName(t *testing.T) { + rows := []*v3.TableRow{ + {Data: []interface{}{"service1", 20.0}}, + {Data: []interface{}{"service2", 10.0}}, + {Data: []interface{}{"service3", 30.0}}, + } + columns := []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + } + builderQueries := map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "InvalidColumn", Order: "asc"}}}, + } + queryNames := []string{"A"} + + sortRows(rows, columns, builderQueries, queryNames) + + // Expect the original order to be maintained + expected := []*v3.TableRow{ + {Data: []interface{}{"service1", 20.0}}, + {Data: []interface{}{"service2", 10.0}}, + {Data: []interface{}{"service3", 30.0}}, + } + + if !reflect.DeepEqual(rows, expected) { + t.Errorf("sortRows() with invalid column name = %v, want %v", rows, expected) + } +} + +func TestSortRowsStability(t *testing.T) { + rows := []*v3.TableRow{ + {Data: []interface{}{"service1", 10.0, "a"}}, + {Data: []interface{}{"service2", 10.0, "b"}}, + {Data: []interface{}{"service3", 10.0, "c"}}, + } + columns := []*v3.TableColumn{ + {Name: "service_name"}, + {Name: "A"}, + {Name: "B"}, + } + builderQueries := map[string]*v3.BuilderQuery{ + "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, + } + queryNames := []string{"A"} + + sortRows(rows, columns, builderQueries, queryNames) + + // Expect the original order to be maintained for equal values + expected := []*v3.TableRow{ + {Data: []interface{}{"service1", 10.0, "a"}}, + {Data: []interface{}{"service2", 10.0, "b"}}, + {Data: []interface{}{"service3", 10.0, "c"}}, + } + + if !reflect.DeepEqual(rows, expected) { + t.Errorf("sortRows() stability test failed = %v, want %v", rows, expected) + } +} + +func TestTransformToTableForClickHouseQueries(t *testing.T) { + tests := []struct { + name string + input []*v3.Result + expected []*v3.Result + }{ + { + name: "Empty input", + input: []*v3.Result{}, + expected: []*v3.Result{}, + }, + { + name: "Single result with one series", + input: []*v3.Result{ + { + QueryName: "A", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "frontend"}, + }, + Points: []v3.Point{ + {Value: 10.0}, + }, + }, + }, + }, + }, + expected: []*v3.Result{ + { + Table: &v3.Table{ + Columns: []*v3.TableColumn{ + {Name: "service"}, + {Name: "A"}, + }, + Rows: []*v3.TableRow{ + {Data: []interface{}{"frontend", 10.0}}, + }, + }, + }, + }, + }, + { + name: "Multiple results with multiple series", + input: []*v3.Result{ + { + QueryName: "A", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "frontend", "env": "prod"}, + }, + Points: []v3.Point{ + {Value: 10.0}, + }, + }, + { + LabelsArray: []map[string]string{ + {"service": "backend", "env": "prod"}, + }, + Points: []v3.Point{ + {Value: 20.0}, + }, + }, + }, + }, + { + QueryName: "B", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "frontend", "env": "prod"}, + }, + Points: []v3.Point{ + {Value: 15.0}, + }, + }, + { + LabelsArray: []map[string]string{ + {"service": "backend", "env": "prod"}, + }, + Points: []v3.Point{ + {Value: 25.0}, + }, + }, + }, + }, + }, + expected: []*v3.Result{ + { + Table: &v3.Table{ + Columns: []*v3.TableColumn{ + {Name: "service"}, + {Name: "env"}, + {Name: "A"}, + {Name: "B"}, + }, + Rows: []*v3.TableRow{ + {Data: []interface{}{"frontend", "prod", 10.0, nil}}, + {Data: []interface{}{"backend", "prod", 20.0, nil}}, + {Data: []interface{}{"frontend", "prod", nil, 15.0}}, + {Data: []interface{}{"backend", "prod", nil, 25.0}}, + }, + }, + }, + }, + }, + { + name: "Results with missing labels", + input: []*v3.Result{ + { + QueryName: "A", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "frontend"}, + }, + Points: []v3.Point{ + {Value: 10.0}, + }, + }, + }, + }, + { + QueryName: "B", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"env": "prod"}, + }, + Points: []v3.Point{ + {Value: 20.0}, + }, + }, + }, + }, + }, + expected: []*v3.Result{ + { + Table: &v3.Table{ + Columns: []*v3.TableColumn{ + {Name: "service"}, + {Name: "env"}, + {Name: "A"}, + {Name: "B"}, + }, + Rows: []*v3.TableRow{ + {Data: []interface{}{"frontend", "n/a", 10.0, nil}}, + {Data: []interface{}{"n/a", "prod", nil, 20.0}}, + }, + }, + }, + }, + }, + { + name: "Results with empty series", + input: []*v3.Result{ + { + QueryName: "A", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "frontend"}, + }, + Points: []v3.Point{ + {Value: 10.0}, + }, + }, + }, + }, + { + QueryName: "B", + Series: []*v3.Series{}, + }, + }, + expected: []*v3.Result{ + { + Table: &v3.Table{ + Columns: []*v3.TableColumn{ + {Name: "service"}, + {Name: "A"}, + }, + Rows: []*v3.TableRow{ + {Data: []interface{}{"frontend", 10.0}}, + }, + }, + }, + }, + }, + { + name: "Results with empty points", + input: []*v3.Result{ + { + QueryName: "A", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "frontend"}, + }, + Points: []v3.Point{}, + }, + }, + }, + { + QueryName: "B", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "backend"}, + }, + Points: []v3.Point{ + {Value: 20.0}, + }, + }, + }, + }, + }, + expected: []*v3.Result{ + { + Table: &v3.Table{ + Columns: []*v3.TableColumn{ + {Name: "service"}, + {Name: "B"}, + }, + Rows: []*v3.TableRow{ + {Data: []interface{}{"frontend", nil}}, + {Data: []interface{}{"backend", 20.0}}, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := TransformToTableForClickHouseQueries(tt.input) + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("TransformToTableForClickHouseQueries() = %v, want %v", result, tt.expected) + } + }) + } +} + +func TestTransformToTableForClickHouseQueriesSorting(t *testing.T) { + input := []*v3.Result{ + { + QueryName: "B", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "frontend"}, + }, + Points: []v3.Point{ + {Value: 10.0}, + }, + }, + }, + }, + { + QueryName: "A", + Series: []*v3.Series{ + { + LabelsArray: []map[string]string{ + {"service": "backend"}, + }, + Points: []v3.Point{ + {Value: 20.0}, + }, + }, + }, + }, + } + + expected := []*v3.Result{ + { + Table: &v3.Table{ + Columns: []*v3.TableColumn{ + {Name: "service"}, + {Name: "A"}, + {Name: "B"}, + }, + Rows: []*v3.TableRow{ + {Data: []interface{}{"backend", 20.0, nil}}, + {Data: []interface{}{"frontend", nil, 10.0}}, + }, + }, + }, + } + + result := TransformToTableForClickHouseQueries(input) + if !reflect.DeepEqual(result, expected) { + t.Errorf("TransformToTableForClickHouseQueries() sorting test failed. Got %v, want %v", result, expected) + } +} From 41f91db622d05346c130bac6b35c2ae0fcf6855e Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 25 Jun 2024 14:01:49 +0530 Subject: [PATCH 040/281] fix: apdex tooltip not visible in service details page (#5346) --- frontend/src/components/TextToolTip/TextToolTip.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/components/TextToolTip/TextToolTip.tsx b/frontend/src/components/TextToolTip/TextToolTip.tsx index 6c8fad783e..13f3c72d73 100644 --- a/frontend/src/components/TextToolTip/TextToolTip.tsx +++ b/frontend/src/components/TextToolTip/TextToolTip.tsx @@ -9,7 +9,6 @@ import { Tooltip } from 'antd'; import { themeColors } from 'constants/theme'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useMemo } from 'react'; -import { popupContainer } from 'utils/selectPopupContainer'; import { style } from './constant'; @@ -64,7 +63,7 @@ function TextToolTip({ ); return ( - + {useFilledIcon ? ( ) : ( From 51032f6caae9c9ec2065cb3277c1ca3d76af3163 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Tue, 25 Jun 2024 15:50:09 +0530 Subject: [PATCH 041/281] chore: move posthog to segment (#5330) --- deploy/install.sh | 2 +- go.mod | 1 - go.sum | 6 -- pkg/query-service/telemetry/ignored.go | 2 +- pkg/query-service/telemetry/telemetry.go | 84 +++++++++++------------- 5 files changed, 40 insertions(+), 55 deletions(-) diff --git a/deploy/install.sh b/deploy/install.sh index 1d4905b6f6..85c63c248d 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -389,7 +389,7 @@ trap bye EXIT URL="https://api.segment.io/v1/track" HEADER_1="Content-Type: application/json" -HEADER_2="Authorization: Basic NEdtb2E0aXhKQVVIeDJCcEp4c2p3QTFiRWZud0VlUno6" +HEADER_2="Authorization: Basic OWtScko3b1BDR1BFSkxGNlFqTVBMdDVibGpGaFJRQnI=" send_event() { error="" diff --git a/go.mod b/go.mod index 4a52cead08..fbaea1ba45 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,6 @@ require ( github.com/opentracing/opentracing-go v1.2.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 - github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f github.com/prometheus/common v0.54.0 github.com/prometheus/prometheus v2.5.0+incompatible github.com/rs/cors v1.11.0 diff --git a/go.sum b/go.sum index cb3f0f902e..04162eb8bd 100644 --- a/go.sum +++ b/go.sum @@ -137,7 +137,6 @@ github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoE github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -625,8 +624,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f h1:h0p1aZ9F5d6IXOygysob3g4B07b+HuVUQC0VJKD8wA4= -github.com/posthog/posthog-go v0.0.0-20220817142604-0b0bbf0f9c0f/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI= github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= @@ -673,7 +670,6 @@ github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4P github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU= github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg= github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -697,7 +693,6 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= @@ -747,7 +742,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= diff --git a/pkg/query-service/telemetry/ignored.go b/pkg/query-service/telemetry/ignored.go index c0a739e9ee..f91ec7966c 100644 --- a/pkg/query-service/telemetry/ignored.go +++ b/pkg/query-service/telemetry/ignored.go @@ -10,7 +10,7 @@ func EnabledPaths() map[string]struct{} { func ignoreEvents(event string, attributes map[string]interface{}) bool { - if event == TELEMETRY_EVENT_ACTIVE_USER || event == TELEMETRY_EVENT_ACTIVE_USER_PH { + if event == TELEMETRY_EVENT_ACTIVE_USER { for attr_key, attr_val := range attributes { if attr_key == "any" && attr_val.(int8) == 0 { diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 22be2a8648..9b75259296 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -11,7 +11,6 @@ import ( "testing" "time" - ph "github.com/posthog/posthog-go" "gopkg.in/segmentio/analytics-go.v3" "go.signoz.io/signoz/pkg/query-service/constants" @@ -26,7 +25,6 @@ const ( TELEMETRY_EVENT_USER = "User" TELEMETRY_EVENT_INPRODUCT_FEEDBACK = "InProduct Feedback Submitted" TELEMETRY_EVENT_NUMBER_OF_SERVICES = "Number of Services" - TELEMETRY_EVENT_NUMBER_OF_SERVICES_PH = "Number of Services V2" TELEMETRY_EVENT_HEART_BEAT = "Heart Beat" TELEMETRY_EVENT_ORG_SETTINGS = "Org Settings" DEFAULT_SAMPLING = 0.1 @@ -44,7 +42,6 @@ const ( TELEMETRY_EVENT_QUERY_RANGE_API = "Query Range API" TELEMETRY_EVENT_DASHBOARDS_ALERTS = "Dashboards/Alerts Info" TELEMETRY_EVENT_ACTIVE_USER = "Active User" - TELEMETRY_EVENT_ACTIVE_USER_PH = "Active User V2" TELEMETRY_EVENT_USER_INVITATION_SENT = "User Invitation Sent" TELEMETRY_EVENT_USER_INVITATION_ACCEPTED = "User Invitation Accepted" TELEMETRY_EVENT_SUCCESSFUL_DASHBOARD_PANEL_QUERY = "Successful Dashboard Panel Query" @@ -69,8 +66,21 @@ var SAAS_EVENTS_LIST = map[string]struct{}{ TELEMETRY_EVENT_TRACE_DETAIL_API: {}, } -const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz" -const ph_api_key = "H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w" +var OSS_EVENTS_LIST = map[string]struct{}{ + TELEMETRY_EVENT_NUMBER_OF_SERVICES: {}, + TELEMETRY_EVENT_HEART_BEAT: {}, + TELEMETRY_EVENT_LANGUAGE: {}, + TELEMETRY_EVENT_ENVIRONMENT: {}, + TELEMETRY_EVENT_DASHBOARDS_ALERTS: {}, + TELEMETRY_EVENT_ACTIVE_USER: {}, + TELEMETRY_EVENT_PATH: {}, + TELEMETRY_EVENT_ORG_SETTINGS: {}, + TELEMETRY_LICENSE_CHECK_FAILED: {}, + TELEMETRY_LICENSE_UPDATED: {}, + TELEMETRY_LICENSE_ACT_FAILED: {}, +} + +const api_key = "9kRrJ7oPCGPEJLF6QjMPLt5bljFhRQBr" const IP_NOT_FOUND_PLACEHOLDER = "NA" const DEFAULT_NUMBER_OF_SERVICES = 6 @@ -110,13 +120,13 @@ func (telemetry *Telemetry) CheckSigNozSignals(postData *v3.QueryRangeParamsV3) if postData.CompositeQuery.QueryType == v3.QueryTypeBuilder { for _, query := range postData.CompositeQuery.BuilderQueries { - if query.DataSource == v3.DataSourceLogs && len(query.Filters.Items) > 0 { + if query.DataSource == v3.DataSourceLogs && query.Filters != nil && len(query.Filters.Items) > 0 { signozLogsUsed = true } else if query.DataSource == v3.DataSourceMetrics && !strings.Contains(query.AggregateAttribute.Key, "signoz_") && len(query.AggregateAttribute.Key) > 0 { signozMetricsUsed = true - } else if query.DataSource == v3.DataSourceTraces && len(query.Filters.Items) > 0 { + } else if query.DataSource == v3.DataSourceTraces && query.Filters != nil && len(query.Filters.Items) > 0 { signozTracesUsed = true } } @@ -159,9 +169,8 @@ func (telemetry *Telemetry) AddActiveLogsUser() { } type Telemetry struct { - operator analytics.Client + ossOperator analytics.Client saasOperator analytics.Client - phOperator ph.Client ipAddress string userEmail string isEnabled bool @@ -188,11 +197,10 @@ func createTelemetry() { } telemetry = &Telemetry{ - operator: analytics.New(api_key), - phOperator: ph.New(ph_api_key), - ipAddress: getOutboundIP(), - rateLimits: make(map[string]int8), - activeUser: make(map[string]int8), + ossOperator: analytics.New(api_key), + ipAddress: getOutboundIP(), + rateLimits: make(map[string]int8), + activeUser: make(map[string]int8), } telemetry.minRandInt = 0 telemetry.maxRandInt = int(1 / DEFAULT_SAMPLING) @@ -392,18 +400,16 @@ func (a *Telemetry) IdentifyUser(user *model.User) { }) } - a.operator.Enqueue(analytics.Identify{ + a.ossOperator.Enqueue(analytics.Identify{ UserId: a.ipAddress, Traits: analytics.NewTraits().SetName(user.Name).SetEmail(user.Email).Set("ip", a.ipAddress), }) // Updating a groups properties - a.phOperator.Enqueue(ph.GroupIdentify{ - Type: "companyDomain", - Key: a.getCompanyDomain(), - Properties: ph.NewProperties(). - Set("companyDomain", a.getCompanyDomain()), + a.ossOperator.Enqueue(analytics.Group{ + UserId: a.ipAddress, + GroupId: a.getCompanyDomain(), + Traits: analytics.NewTraits().Set("company_domain", a.getCompanyDomain()), }) - } func (a *Telemetry) SetCountUsers(countUsers int8) { @@ -520,33 +526,19 @@ func (a *Telemetry) SendEvent(event string, data map[string]interface{}, userEma }) } - a.operator.Enqueue(analytics.Track{ - Event: event, - UserId: userId, - Properties: properties, - }) - - if event == TELEMETRY_EVENT_NUMBER_OF_SERVICES { - - a.phOperator.Enqueue(ph.Capture{ - DistinctId: userId, - Event: TELEMETRY_EVENT_NUMBER_OF_SERVICES_PH, - Properties: ph.Properties(properties), - Groups: ph.NewGroups(). - Set("companyDomain", a.getCompanyDomain()), - }) + _, isOSSEvent := OSS_EVENTS_LIST[event] - } - if event == TELEMETRY_EVENT_ACTIVE_USER { - - a.phOperator.Enqueue(ph.Capture{ - DistinctId: userId, - Event: TELEMETRY_EVENT_ACTIVE_USER_PH, - Properties: ph.Properties(properties), - Groups: ph.NewGroups(). - Set("companyDomain", a.getCompanyDomain()), + if a.ossOperator != nil && isOSSEvent { + a.ossOperator.Enqueue(analytics.Track{ + Event: event, + UserId: userId, + Properties: properties, + Context: &analytics.Context{ + Extra: map[string]interface{}{ + "groupId": a.getCompanyDomain(), + }, + }, }) - } } From 97b66741a77d833a477e94cef81c8d1f411f4d8f Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Tue, 25 Jun 2024 17:59:37 +0530 Subject: [PATCH 042/281] =?UTF-8?q?chore(signoz):=20=F0=9F=93=8C=20pin=20v?= =?UTF-8?q?ersions:=20SigNoz=200.48.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Prashant Shahi --- deploy/docker-swarm/clickhouse-setup/docker-compose.yaml | 2 +- deploy/docker/clickhouse-setup/docker-compose.testing.yaml | 4 ++-- deploy/docker/clickhouse-setup/docker-compose.yaml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 1f5f8e4229..03f3cb92f1 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.48.0 + image: signoz/query-service:0.48.1 command: [ "-config=/root/config/prometheus.yml", diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index 075bb3322c..c5c13c5de8 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.48.0} + image: signoz/query-service:${DOCKER_TAG:-0.48.1} container_name: signoz-query-service command: [ @@ -204,7 +204,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.48.0} + image: signoz/frontend:${DOCKER_TAG:-0.48.1} container_name: signoz-frontend restart: on-failure depends_on: diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 72980c15a5..fb7757ba0d 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.48.0} + image: signoz/query-service:${DOCKER_TAG:-0.48.1} container_name: signoz-query-service command: [ @@ -203,7 +203,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.48.0} + image: signoz/frontend:${DOCKER_TAG:-0.48.1} container_name: signoz-frontend restart: on-failure depends_on: From 6c402d9e465fce408b659526af362ca9ba432b8f Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Tue, 25 Jun 2024 21:31:52 +0530 Subject: [PATCH 043/281] fix: added correct query for external metric service graph (#5318) --- .../MetricsApplication/Tabs/External.tsx | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index ff0bc1f682..564f0a657e 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -15,6 +15,7 @@ import { } from 'hooks/useResourceAttribute/utils'; import { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; +import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { EQueryType } from 'types/common/dashboard'; import { v4 as uuid } from 'uuid'; @@ -93,6 +94,26 @@ function External(): JSX.Element { [servicename, tagFilterItems], ); + const errorApmToTraceQuery = useGetAPMToTracesQueries({ + servicename, + isExternalCall: true, + filters: [ + { + id: uuid().slice(0, 8), + key: { + key: 'hasError', + dataType: DataTypes.bool, + type: 'tag', + isColumn: true, + isJSON: false, + id: 'hasError--bool--tag--true', + }, + op: 'in', + value: ['true'], + }, + ], + }); + const externalCallRPSWidget = useMemo( () => getWidgetQueryBuilder({ @@ -156,7 +177,7 @@ function External(): JSX.Element { servicename, selectedTraceTags, timestamp: selectedTimeStamp, - apmToTraceQuery, + apmToTraceQuery: errorApmToTraceQuery, })} > View Traces From 4f69996b9d8b3cfdf195051178ca0060ccaa9be9 Mon Sep 17 00:00:00 2001 From: CheetoDa <31571545+Calm-Rock@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:43:55 +0530 Subject: [PATCH 044/281] fix: aks collector instruction fix (#5350) --- .../AKS/aks-installCentralCollector.md | 57 +++++++++---------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md index cb78ca5d27..b2e3a12286 100644 --- a/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md +++ b/frontend/src/container/OnboardingContainer/Modules/AzureMonitoring/AKS/aks-installCentralCollector.md @@ -4,50 +4,45 @@ Prior to installation, you must ensure your Kubernetes cluster is ready and that you have the necessary permissions to deploy applications. Follow these steps to use Helm for setting up the Collector: +  + 1. **Add the OpenTelemetry Helm repository:** ```bash helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts ``` +  + 2. **Prepare the `otel-collector-values.yaml` Configuration** - #### Azure Event Hub Receiver Configuration - If you haven't created the logs Event Hub, you can create one by following the steps in the [Azure Event Hubs documentation](../../bootstrapping/data-ingestion). +  - and replace the placeholders `` with the primary connection string for your Event Hub, it should look something like this: +#### Azure Event Hub Receiver Configuration - ```yaml - connection: Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName - ``` - The Event Hub docs have a step to create a SAS policy for the event hub and copy the connection string. + Replace the placeholders `` with the primary connection string for your Event Hub, it should look something like this: - #### Azure Monitor Receiver Configuration + ```yaml + connection: Endpoint=sb://namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=superSecret1234=;EntityPath=hubName + ``` + The Event Hub setup have a step to create a SAS policy for the event hub and copy the connection string. - You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. +  - 1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. - You can name it `signoz-central-collector-app` the redirect URI can be empty. - 2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read acess can be given to the full subscription. - 3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. +#### Azure Monitor Receiver Configuration - 4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + You will need to set up a [service principal](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal) with Read permissions to receive data from Azure Monitor. -
- Application Overview -
- - Application Overview - -
-
+ 1. Follow the steps in the [Create a service principal Azure Doc](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#register-an-application-with-microsoft-entra-id-and-create-a-service-principal) documentation to create a service principal. + You can name it `signoz-central-collector-app` the redirect URI can be empty. + 2. To add read permissions to Azure Monitor, Follow the [Assign Role](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#assign-a-role-to-the-application) documentation. The read acess can be given to the full subscription. + 3. There are multiple ways to authenticate the service principal, we will use the client secret option, follow [Creating a client secret](https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal#option-3-create-a-new-client-secret) and don't forget to copy the client secret. The secret is used in the configuration file as `client_secret`. - 5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. - - 6. Ensure you replace the placeholders `` and `` with the appropriate values for your signoz cloud instance. + 4. To find `client_id` and `tenant_id`, go to the [Azure Portal](https://portal.azure.com/) and search for the `Application` you created. You would see the `Application (client) ID` and `Directory (tenant) ID` in the Overview section. + + 5. To find `subscription_id`, follow steps in [Find Your Subscription](https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription) and populate them in the configuration file. + + 6. Ensure you replace the placeholders `` and `` with the appropriate values for your signoz cloud instance. @@ -92,13 +87,15 @@ processors: batch: {} exporters: otlp: - endpoint: "ingest..signoz.cloud:443" + endpoint: "ingest.{{REGION}}.signoz.cloud:443" tls: insecure: false headers: - "signoz-access-token": "" + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" ``` +  + 3. **Deploy the OpenTelemetry Collector to your Kubernetes cluster:** You'll need to prepare a custom configuration file, say `otel-collector-values.yaml`, that matches your environment's specific needs. Replace `` with the Kubernetes namespace where you wish to install the Collector. From daa5a05677f1041264d7bad8935511312d5c5660 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 26 Jun 2024 14:34:27 +0530 Subject: [PATCH 045/281] chore: update table response format (#5349) --- Makefile | 1 + .../app/clickhouseReader/reader.go | 16 +- pkg/query-service/model/v3/v3.go | 8 +- pkg/query-service/postprocess/table.go | 120 ++++---- pkg/query-service/postprocess/table_test.go | 288 +++++++----------- 5 files changed, 191 insertions(+), 242 deletions(-) diff --git a/Makefile b/Makefile index 95cf7afdb9..5f4a3c1ac2 100644 --- a/Makefile +++ b/Makefile @@ -188,3 +188,4 @@ test: go test ./pkg/query-service/tests/integration/... go test ./pkg/query-service/rules/... go test ./pkg/query-service/collectorsimulator/... + go test ./pkg/query-service/postprocess/... diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 20eb11d479..c82afb9861 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1993,8 +1993,8 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc } } - searchSpansResult[0].StartTimestampMillis = startTime - (durationNano/1000000) - searchSpansResult[0].EndTimestampMillis = endTime + (durationNano/1000000) + searchSpansResult[0].StartTimestampMillis = startTime - (durationNano / 1000000) + searchSpansResult[0].EndTimestampMillis = endTime + (durationNano / 1000000) return &searchSpansResult, nil } @@ -4434,8 +4434,8 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ case *time.Time: point.Timestamp = v.UnixMilli() case *float64, *float32: - isValidPoint = true if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + isValidPoint = true point.Value = float64(reflect.ValueOf(v).Elem().Float()) } else { groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Float())) @@ -4447,9 +4447,9 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ case **float64, **float32: val := reflect.ValueOf(v) if val.IsValid() && !val.IsNil() && !val.Elem().IsNil() { - isValidPoint = true value := reflect.ValueOf(v).Elem().Elem().Float() if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + isValidPoint = true point.Value = value } else { groupBy = append(groupBy, fmt.Sprintf("%v", value)) @@ -4460,8 +4460,8 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ } } case *uint, *uint8, *uint64, *uint16, *uint32: - isValidPoint = true if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + isValidPoint = true point.Value = float64(reflect.ValueOf(v).Elem().Uint()) } else { groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Uint())) @@ -4473,9 +4473,9 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ case **uint, **uint8, **uint64, **uint16, **uint32: val := reflect.ValueOf(v) if val.IsValid() && !val.IsNil() && !val.Elem().IsNil() { - isValidPoint = true value := reflect.ValueOf(v).Elem().Elem().Uint() if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + isValidPoint = true point.Value = float64(value) } else { groupBy = append(groupBy, fmt.Sprintf("%v", value)) @@ -4486,8 +4486,8 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ } } case *int, *int8, *int16, *int32, *int64: - isValidPoint = true if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + isValidPoint = true point.Value = float64(reflect.ValueOf(v).Elem().Int()) } else { groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Int())) @@ -4499,9 +4499,9 @@ func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([ case **int, **int8, **int16, **int32, **int64: val := reflect.ValueOf(v) if val.IsValid() && !val.IsNil() && !val.Elem().IsNil() { - isValidPoint = true value := reflect.ValueOf(v).Elem().Elem().Int() if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { + isValidPoint = true point.Value = float64(value) } else { groupBy = append(groupBy, fmt.Sprintf("%v", value)) diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index 7e6daa3751..7b9fd8d989 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -990,10 +990,16 @@ type QueryRangeResponse struct { type TableColumn struct { Name string `json:"name"` + // QueryName is the name of the query that this column belongs to + QueryName string `json:"queryName"` + // IsValueColumn is true if this column is a value column + // i.e it is the column that contains the actual value that is being plotted + IsValueColumn bool `json:"isValueColumn"` } type TableRow struct { - Data []interface{} `json:"data"` + Data map[string]interface{} `json:"data"` + QueryName string `json:"-"` } type Table struct { diff --git a/pkg/query-service/postprocess/table.go b/pkg/query-service/postprocess/table.go index 1599bf37be..01be79c153 100644 --- a/pkg/query-service/postprocess/table.go +++ b/pkg/query-service/postprocess/table.go @@ -2,6 +2,7 @@ package postprocess import ( "fmt" + "math" "sort" "strings" @@ -9,20 +10,21 @@ import ( v3 "go.signoz.io/signoz/pkg/query-service/model/v3" ) -func getAutoColNameForQuery(queryName string, params *v3.QueryRangeParamsV3) string { - q := params.CompositeQuery.BuilderQueries[queryName] - if q.DataSource == v3.DataSourceTraces || q.DataSource == v3.DataSourceLogs { - if q.AggregateAttribute.Key != "" { - return fmt.Sprintf("%s(%s)", q.AggregateOperator, q.AggregateAttribute.Key) - } - return string(q.AggregateOperator) - } else if q.DataSource == v3.DataSourceMetrics { - if q.SpaceAggregation != "" && params.Version == "v4" { - return fmt.Sprintf("%s(%s)", q.SpaceAggregation, q.AggregateAttribute.Key) - } - return fmt.Sprintf("%s(%s)", q.AggregateOperator, q.AggregateAttribute.Key) +func roundToTwoDecimal(number float64) float64 { + // Handle very small numbers + if math.Abs(number) < 0.000001 { + return 0 + } + + // Determine the number of decimal places to round to + decimalPlaces := 2 + if math.Abs(number) < 0.01 { + decimalPlaces = int(math.Ceil(-math.Log10(math.Abs(number)))) + 1 } - return queryName + + // Round to the determined number of decimal places + scale := math.Pow(10, float64(decimalPlaces)) + return math.Round(number*scale) / scale } func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRangeParamsV3) []*v3.Result { @@ -55,10 +57,10 @@ func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRan // There will be one column for each label key and one column for each query name columns := make([]*v3.TableColumn, 0, len(labelKeys)+len(results)) for _, key := range labelKeys { - columns = append(columns, &v3.TableColumn{Name: key}) + columns = append(columns, &v3.TableColumn{Name: key, IsValueColumn: false}) } for _, result := range results { - columns = append(columns, &v3.TableColumn{Name: result.QueryName}) + columns = append(columns, &v3.TableColumn{Name: result.QueryName, QueryName: result.QueryName, IsValueColumn: true}) } // Create a map to store unique rows @@ -72,8 +74,8 @@ func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRan // Create a key for the row based on labels var keyParts []string - rowData := make([]interface{}, len(columns)) - for i, key := range labelKeys { + rowData := make(map[string]interface{}, len(columns)) + for _, key := range labelKeys { value := "n/a" for _, labels := range series.LabelsArray { if v, ok := labels[key]; ok { @@ -82,21 +84,21 @@ func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRan } } keyParts = append(keyParts, fmt.Sprintf("%s=%s", key, value)) - rowData[i] = value + rowData[key] = value } rowKey := strings.Join(keyParts, ",") // Get or create the row row, ok := rowMap[rowKey] if !ok { - row = &v3.TableRow{Data: rowData} + row = &v3.TableRow{Data: rowData, QueryName: result.QueryName} rowMap[rowKey] = row } // Add the value for this query - for i, col := range columns { + for _, col := range columns { if col.Name == result.QueryName { - row.Data[i] = series.Points[0].Value + row.Data[col.Name] = roundToTwoDecimal(series.Points[0].Value) break } } @@ -106,11 +108,6 @@ func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRan // Convert rowMap to a slice of TableRows rows := make([]*v3.TableRow, 0, len(rowMap)) for _, row := range rowMap { - for i, value := range row.Data { - if value == nil { - row.Data[i] = "n/a" - } - } rows = append(rows, row) } @@ -122,11 +119,15 @@ func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRan sort.Strings(queryNames) // Sort rows based on OrderBy from BuilderQueries - sortRows(rows, columns, params.CompositeQuery.BuilderQueries, queryNames) + sortRows(rows, params.CompositeQuery.BuilderQueries, queryNames) - for _, column := range columns { - if _, exists := params.CompositeQuery.BuilderQueries[column.Name]; exists { - column.Name = getAutoColNameForQuery(column.Name, params) + for _, row := range rows { + for _, col := range columns { + if col.IsValueColumn { + if row.Data[col.Name] == nil { + row.Data[col.Name] = "n/a" + } + } } } @@ -141,9 +142,11 @@ func TransformToTableForBuilderQueries(results []*v3.Result, params *v3.QueryRan return []*v3.Result{&tableResult} } -func sortRows(rows []*v3.TableRow, columns []*v3.TableColumn, builderQueries map[string]*v3.BuilderQuery, queryNames []string) { - sort.SliceStable(rows, func(i, j int) bool { - for _, queryName := range queryNames { +func sortRows(rows []*v3.TableRow, builderQueries map[string]*v3.BuilderQuery, queryNames []string) { + // use reverse order of queryNames + for i := len(queryNames) - 1; i >= 0; i-- { + queryName := queryNames[i] + sort.SliceStable(rows, func(i, j int) bool { query := builderQueries[queryName] orderByList := query.OrderBy if len(orderByList) == 0 { @@ -155,23 +158,12 @@ func sortRows(rows []*v3.TableRow, columns []*v3.TableColumn, builderQueries map if name == constants.SigNozOrderByValue { name = queryName } - colIndex := -1 - for k, col := range columns { - if col.Name == name { - colIndex = k - break - } - } - if colIndex == -1 { - continue - } - valI := rows[i].Data[colIndex] - valJ := rows[j].Data[colIndex] + valI := rows[i].Data[name] + valJ := rows[j].Data[name] - // Handle "n/a" values - if valI == "n/a" && valJ == "n/a" { - continue + if valI == nil || valJ == nil { + return rows[i].QueryName < rows[j].QueryName } // Compare based on the data type @@ -211,9 +203,9 @@ func sortRows(rows []*v3.TableRow, columns []*v3.TableColumn, builderQueries map } } } - } - return false - }) + return false + }) + } } func TransformToTableForClickHouseQueries(results []*v3.Result) []*v3.Result { @@ -248,11 +240,11 @@ func TransformToTableForClickHouseQueries(results []*v3.Result) []*v3.Result { // So we create a column for each query name that has at least one point columns := make([]*v3.TableColumn, 0) for _, key := range labelKeys { - columns = append(columns, &v3.TableColumn{Name: key}) + columns = append(columns, &v3.TableColumn{Name: key, IsValueColumn: false}) } for _, result := range results { if len(result.Series) > 0 && len(result.Series[0].Points) > 0 { - columns = append(columns, &v3.TableColumn{Name: result.QueryName}) + columns = append(columns, &v3.TableColumn{Name: result.QueryName, QueryName: result.QueryName, IsValueColumn: true}) } } @@ -261,8 +253,8 @@ func TransformToTableForClickHouseQueries(results []*v3.Result) []*v3.Result { for _, series := range result.Series { // Create a key for the row based on labels - rowData := make([]interface{}, len(columns)) - for i, key := range labelKeys { + rowData := make(map[string]interface{}, len(columns)) + for _, key := range labelKeys { value := "n/a" for _, labels := range series.LabelsArray { if v, ok := labels[key]; ok { @@ -270,16 +262,16 @@ func TransformToTableForClickHouseQueries(results []*v3.Result) []*v3.Result { break } } - rowData[i] = value + rowData[key] = value } // Get or create the row - row := &v3.TableRow{Data: rowData} + row := &v3.TableRow{Data: rowData, QueryName: result.QueryName} // Add the value for this query - for i, col := range columns { + for _, col := range columns { if col.Name == result.QueryName && len(series.Points) > 0 { - row.Data[i] = series.Points[0].Value + row.Data[col.Name] = roundToTwoDecimal(series.Points[0].Value) break } } @@ -287,6 +279,16 @@ func TransformToTableForClickHouseQueries(results []*v3.Result) []*v3.Result { } } + for _, row := range rows { + for _, col := range columns { + if col.IsValueColumn { + if row.Data[col.Name] == nil { + row.Data[col.Name] = "n/a" + } + } + } + } + // Create the final result tableResult := v3.Result{ Table: &v3.Table{ diff --git a/pkg/query-service/postprocess/table_test.go b/pkg/query-service/postprocess/table_test.go index 6e8f588a5f..04a835c2ff 100644 --- a/pkg/query-service/postprocess/table_test.go +++ b/pkg/query-service/postprocess/table_test.go @@ -1,6 +1,7 @@ package postprocess import ( + "bytes" "encoding/json" "reflect" "testing" @@ -21,9 +22,9 @@ func TestSortRows(t *testing.T) { { name: "Sort by single numeric query, ascending order", rows: []*v3.TableRow{ - {Data: []interface{}{"service2", 20.0}}, - {Data: []interface{}{"service1", 10.0}}, - {Data: []interface{}{"service3", 30.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -34,17 +35,17 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service1", 10.0}}, - {Data: []interface{}{"service2", 20.0}}, - {Data: []interface{}{"service3", 30.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, }, }, { name: "Sort by single numeric query, descending order", rows: []*v3.TableRow{ - {Data: []interface{}{"service2", 20.0}}, - {Data: []interface{}{"service1", 10.0}}, - {Data: []interface{}{"service3", 30.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -55,17 +56,17 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service3", 30.0}}, - {Data: []interface{}{"service2", 20.0}}, - {Data: []interface{}{"service1", 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0}}, }, }, { name: "Sort by single string query, ascending order", rows: []*v3.TableRow{ - {Data: []interface{}{"service2", "b"}}, - {Data: []interface{}{"service1", "c"}}, - {Data: []interface{}{"service3", "a"}}, + {Data: map[string]interface{}{"service": "service2", "A": "b"}}, + {Data: map[string]interface{}{"service": "service1", "A": "c"}}, + {Data: map[string]interface{}{"service": "service3", "A": "a"}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -76,18 +77,18 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service3", "a"}}, - {Data: []interface{}{"service2", "b"}}, - {Data: []interface{}{"service1", "c"}}, + {Data: map[string]interface{}{"service": "service3", "A": "a"}}, + {Data: map[string]interface{}{"service": "service2", "A": "b"}}, + {Data: map[string]interface{}{"service": "service1", "A": "c"}}, }, }, { name: "Sort with n/a values", rows: []*v3.TableRow{ - {Data: []interface{}{"service1", 10.0, "n/a"}}, - {Data: []interface{}{"service2", "n/a", 15.0}}, - {Data: []interface{}{"service3", 30.0, 25.0}}, - {Data: []interface{}{"service4", "n/a", "n/a"}}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service2", "B": 15.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0, "B": 25.0}}, + {Data: map[string]interface{}{"service": "service4"}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -100,43 +101,18 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A", "B"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service1", 10.0, "n/a"}}, - {Data: []interface{}{"service3", 30.0, 25.0}}, - {Data: []interface{}{"service4", "n/a", "n/a"}}, - {Data: []interface{}{"service2", "n/a", 15.0}}, - }, - }, - { - name: "Sort with different data types", - rows: []*v3.TableRow{ - {Data: []interface{}{"service1", "string", 10.0, true}}, - {Data: []interface{}{"service2", 20.0, "string", false}}, - {Data: []interface{}{"service3", true, 30.0, "string"}}, - }, - columns: []*v3.TableColumn{ - {Name: "service_name"}, - {Name: "A"}, - {Name: "B"}, - {Name: "C"}, - }, - builderQueries: map[string]*v3.BuilderQuery{ - "A": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "asc"}}}, - "B": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "desc"}}}, - "C": {OrderBy: []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "asc"}}}, - }, - queryNames: []string{"A", "B", "C"}, - expected: []*v3.TableRow{ - {Data: []interface{}{"service2", 20.0, "string", false}}, - {Data: []interface{}{"service1", "string", 10.0, true}}, - {Data: []interface{}{"service3", true, 30.0, "string"}}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0, "B": 25.0}}, + {Data: map[string]interface{}{"service": "service2", "B": 15.0}}, + {Data: map[string]interface{}{"service": "service4"}}, }, }, { name: "Sort with SigNozOrderByValue", rows: []*v3.TableRow{ - {Data: []interface{}{"service1", 20.0}}, - {Data: []interface{}{"service2", 10.0}}, - {Data: []interface{}{"service3", 30.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -147,44 +123,17 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service3", 30.0}}, - {Data: []interface{}{"service1", 20.0}}, - {Data: []interface{}{"service2", 10.0}}, - }, - }, - { - name: "Sort by multiple queries with mixed types", - rows: []*v3.TableRow{ - {Data: []interface{}{"service1", 10.0, "b", true}}, - {Data: []interface{}{"service2", 20.0, "a", false}}, - {Data: []interface{}{"service3", 10.0, "c", true}}, - {Data: []interface{}{"service4", 20.0, "b", false}}, - }, - columns: []*v3.TableColumn{ - {Name: "service_name"}, - {Name: "A"}, - {Name: "B"}, - {Name: "C"}, - }, - builderQueries: map[string]*v3.BuilderQuery{ - "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, - "B": {OrderBy: []v3.OrderBy{{ColumnName: "B", Order: "desc"}}}, - "C": {OrderBy: []v3.OrderBy{{ColumnName: "C", Order: "asc"}}}, - }, - queryNames: []string{"A", "B", "C"}, - expected: []*v3.TableRow{ - {Data: []interface{}{"service3", 10.0, "c", true}}, - {Data: []interface{}{"service1", 10.0, "b", true}}, - {Data: []interface{}{"service4", 20.0, "b", false}}, - {Data: []interface{}{"service2", 20.0, "a", false}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0}}, }, }, { name: "Sort with all n/a values", rows: []*v3.TableRow{ - {Data: []interface{}{"service1", "n/a", "n/a"}}, - {Data: []interface{}{"service2", "n/a", "n/a"}}, - {Data: []interface{}{"service3", "n/a", "n/a"}}, + {Data: map[string]interface{}{"service": "service1", "A": "n/a", "B": "n/a"}}, + {Data: map[string]interface{}{"service": "service2", "A": "n/a", "B": "n/a"}}, + {Data: map[string]interface{}{"service": "service3", "A": "n/a", "B": "n/a"}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -197,18 +146,18 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A", "B"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service1", "n/a", "n/a"}}, - {Data: []interface{}{"service2", "n/a", "n/a"}}, - {Data: []interface{}{"service3", "n/a", "n/a"}}, + {Data: map[string]interface{}{"service": "service1", "A": "n/a", "B": "n/a"}}, + {Data: map[string]interface{}{"service": "service2", "A": "n/a", "B": "n/a"}}, + {Data: map[string]interface{}{"service": "service3", "A": "n/a", "B": "n/a"}}, }, }, { name: "Sort with negative numbers", rows: []*v3.TableRow{ - {Data: []interface{}{"service1", -10.0}}, - {Data: []interface{}{"service2", 20.0}}, - {Data: []interface{}{"service3", -30.0}}, - {Data: []interface{}{"service4", 0.0}}, + {Data: map[string]interface{}{"service": "service1", "A": -10.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service3", "A": -30.0}}, + {Data: map[string]interface{}{"service": "service4", "A": 0.0}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -219,19 +168,19 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service3", -30.0}}, - {Data: []interface{}{"service1", -10.0}}, - {Data: []interface{}{"service4", 0.0}}, - {Data: []interface{}{"service2", 20.0}}, + {Data: map[string]interface{}{"service": "service3", "A": -30.0}}, + {Data: map[string]interface{}{"service": "service1", "A": -10.0}}, + {Data: map[string]interface{}{"service": "service4", "A": 0.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 20.0}}, }, }, { name: "Sort with mixed case strings", rows: []*v3.TableRow{ - {Data: []interface{}{"service1", "Apple"}}, - {Data: []interface{}{"service2", "banana"}}, - {Data: []interface{}{"service3", "Cherry"}}, - {Data: []interface{}{"service4", "date"}}, + {Data: map[string]interface{}{"service": "service1", "A": "Apple"}}, + {Data: map[string]interface{}{"service": "service2", "A": "banana"}}, + {Data: map[string]interface{}{"service": "service3", "A": "Cherry"}}, + {Data: map[string]interface{}{"service": "service4", "A": "date"}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -242,19 +191,19 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service1", "Apple"}}, - {Data: []interface{}{"service3", "Cherry"}}, - {Data: []interface{}{"service2", "banana"}}, - {Data: []interface{}{"service4", "date"}}, + {Data: map[string]interface{}{"service": "service1", "A": "Apple"}}, + {Data: map[string]interface{}{"service": "service3", "A": "Cherry"}}, + {Data: map[string]interface{}{"service": "service2", "A": "banana"}}, + {Data: map[string]interface{}{"service": "service4", "A": "date"}}, }, }, { name: "Sort with empty strings", rows: []*v3.TableRow{ - {Data: []interface{}{"service1", ""}}, - {Data: []interface{}{"service2", "b"}}, - {Data: []interface{}{"service3", ""}}, - {Data: []interface{}{"service4", "a"}}, + {Data: map[string]interface{}{"service": "service1", "A": ""}}, + {Data: map[string]interface{}{"service": "service2", "A": "b"}}, + {Data: map[string]interface{}{"service": "service3", "A": ""}}, + {Data: map[string]interface{}{"service": "service4", "A": "a"}}, }, columns: []*v3.TableColumn{ {Name: "service_name"}, @@ -265,17 +214,17 @@ func TestSortRows(t *testing.T) { }, queryNames: []string{"A"}, expected: []*v3.TableRow{ - {Data: []interface{}{"service1", ""}}, - {Data: []interface{}{"service3", ""}}, - {Data: []interface{}{"service4", "a"}}, - {Data: []interface{}{"service2", "b"}}, + {Data: map[string]interface{}{"service": "service1", "A": ""}}, + {Data: map[string]interface{}{"service": "service3", "A": ""}}, + {Data: map[string]interface{}{"service": "service4", "A": "a"}}, + {Data: map[string]interface{}{"service": "service2", "A": "b"}}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - sortRows(tt.rows, tt.columns, tt.builderQueries, tt.queryNames) + sortRows(tt.rows, tt.builderQueries, tt.queryNames) if !reflect.DeepEqual(tt.rows, tt.expected) { exp, _ := json.Marshal(tt.expected) got, _ := json.Marshal(tt.rows) @@ -287,24 +236,20 @@ func TestSortRows(t *testing.T) { func TestSortRowsWithEmptyQueries(t *testing.T) { rows := []*v3.TableRow{ - {Data: []interface{}{"service1", 20.0}}, - {Data: []interface{}{"service2", 10.0}}, - {Data: []interface{}{"service3", 30.0}}, - } - columns := []*v3.TableColumn{ - {Name: "service_name"}, - {Name: "A"}, + {Data: map[string]interface{}{"service": "service1", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, } builderQueries := map[string]*v3.BuilderQuery{} queryNames := []string{} - sortRows(rows, columns, builderQueries, queryNames) + sortRows(rows, builderQueries, queryNames) // Expect the original order to be maintained expected := []*v3.TableRow{ - {Data: []interface{}{"service1", 20.0}}, - {Data: []interface{}{"service2", 10.0}}, - {Data: []interface{}{"service3", 30.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, } if !reflect.DeepEqual(rows, expected) { @@ -314,26 +259,22 @@ func TestSortRowsWithEmptyQueries(t *testing.T) { func TestSortRowsWithInvalidColumnName(t *testing.T) { rows := []*v3.TableRow{ - {Data: []interface{}{"service1", 20.0}}, - {Data: []interface{}{"service2", 10.0}}, - {Data: []interface{}{"service3", 30.0}}, - } - columns := []*v3.TableColumn{ - {Name: "service_name"}, - {Name: "A"}, + {Data: map[string]interface{}{"service": "service1", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, } builderQueries := map[string]*v3.BuilderQuery{ "A": {OrderBy: []v3.OrderBy{{ColumnName: "InvalidColumn", Order: "asc"}}}, } queryNames := []string{"A"} - sortRows(rows, columns, builderQueries, queryNames) + sortRows(rows, builderQueries, queryNames) // Expect the original order to be maintained expected := []*v3.TableRow{ - {Data: []interface{}{"service1", 20.0}}, - {Data: []interface{}{"service2", 10.0}}, - {Data: []interface{}{"service3", 30.0}}, + {Data: map[string]interface{}{"service": "service1", "A": 20.0}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0}}, + {Data: map[string]interface{}{"service": "service3", "A": 30.0}}, } if !reflect.DeepEqual(rows, expected) { @@ -343,27 +284,22 @@ func TestSortRowsWithInvalidColumnName(t *testing.T) { func TestSortRowsStability(t *testing.T) { rows := []*v3.TableRow{ - {Data: []interface{}{"service1", 10.0, "a"}}, - {Data: []interface{}{"service2", 10.0, "b"}}, - {Data: []interface{}{"service3", 10.0, "c"}}, - } - columns := []*v3.TableColumn{ - {Name: "service_name"}, - {Name: "A"}, - {Name: "B"}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0, "B": "a"}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0, "B": "b"}}, + {Data: map[string]interface{}{"service": "service3", "A": 10.0, "B": "c"}}, } builderQueries := map[string]*v3.BuilderQuery{ "A": {OrderBy: []v3.OrderBy{{ColumnName: "A", Order: "asc"}}}, } queryNames := []string{"A"} - sortRows(rows, columns, builderQueries, queryNames) + sortRows(rows, builderQueries, queryNames) // Expect the original order to be maintained for equal values expected := []*v3.TableRow{ - {Data: []interface{}{"service1", 10.0, "a"}}, - {Data: []interface{}{"service2", 10.0, "b"}}, - {Data: []interface{}{"service3", 10.0, "c"}}, + {Data: map[string]interface{}{"service": "service1", "A": 10.0, "B": "a"}}, + {Data: map[string]interface{}{"service": "service2", "A": 10.0, "B": "b"}}, + {Data: map[string]interface{}{"service": "service3", "A": 10.0, "B": "c"}}, } if !reflect.DeepEqual(rows, expected) { @@ -404,10 +340,10 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { Table: &v3.Table{ Columns: []*v3.TableColumn{ {Name: "service"}, - {Name: "A"}, + {Name: "A", QueryName: "A", IsValueColumn: true}, }, Rows: []*v3.TableRow{ - {Data: []interface{}{"frontend", 10.0}}, + {Data: map[string]interface{}{"service": "frontend", "A": 10.0}}, }, }, }, @@ -465,14 +401,14 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { Columns: []*v3.TableColumn{ {Name: "service"}, {Name: "env"}, - {Name: "A"}, - {Name: "B"}, + {Name: "A", QueryName: "A", IsValueColumn: true}, + {Name: "B", QueryName: "B", IsValueColumn: true}, }, Rows: []*v3.TableRow{ - {Data: []interface{}{"frontend", "prod", 10.0, nil}}, - {Data: []interface{}{"backend", "prod", 20.0, nil}}, - {Data: []interface{}{"frontend", "prod", nil, 15.0}}, - {Data: []interface{}{"backend", "prod", nil, 25.0}}, + {Data: map[string]interface{}{"service": "frontend", "env": "prod", "A": 10.0, "B": "n/a"}}, + {Data: map[string]interface{}{"service": "backend", "env": "prod", "A": 20.0, "B": "n/a"}}, + {Data: map[string]interface{}{"service": "frontend", "env": "prod", "A": "n/a", "B": 15.0}}, + {Data: map[string]interface{}{"service": "backend", "env": "prod", "A": "n/a", "B": 25.0}}, }, }, }, @@ -514,12 +450,12 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { Columns: []*v3.TableColumn{ {Name: "service"}, {Name: "env"}, - {Name: "A"}, - {Name: "B"}, + {Name: "A", QueryName: "A", IsValueColumn: true}, + {Name: "B", QueryName: "B", IsValueColumn: true}, }, Rows: []*v3.TableRow{ - {Data: []interface{}{"frontend", "n/a", 10.0, nil}}, - {Data: []interface{}{"n/a", "prod", nil, 20.0}}, + {Data: map[string]interface{}{"service": "frontend", "env": "n/a", "A": 10.0, "B": "n/a"}}, + {Data: map[string]interface{}{"service": "n/a", "env": "prod", "A": "n/a", "B": 20.0}}, }, }, }, @@ -551,10 +487,10 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { Table: &v3.Table{ Columns: []*v3.TableColumn{ {Name: "service"}, - {Name: "A"}, + {Name: "A", QueryName: "A", IsValueColumn: true}, }, Rows: []*v3.TableRow{ - {Data: []interface{}{"frontend", 10.0}}, + {Data: map[string]interface{}{"service": "frontend", "A": 10.0}}, }, }, }, @@ -593,11 +529,11 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { Table: &v3.Table{ Columns: []*v3.TableColumn{ {Name: "service"}, - {Name: "B"}, + {Name: "B", QueryName: "B", IsValueColumn: true}, }, Rows: []*v3.TableRow{ - {Data: []interface{}{"frontend", nil}}, - {Data: []interface{}{"backend", 20.0}}, + {Data: map[string]interface{}{"service": "frontend", "B": "n/a"}}, + {Data: map[string]interface{}{"service": "backend", "B": 20.0}}, }, }, }, @@ -608,8 +544,10 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := TransformToTableForClickHouseQueries(tt.input) - if !reflect.DeepEqual(result, tt.expected) { - t.Errorf("TransformToTableForClickHouseQueries() = %v, want %v", result, tt.expected) + exp, _ := json.Marshal(tt.expected) + got, _ := json.Marshal(result) + if !bytes.Equal(got, exp) { + t.Errorf("TransformToTableForClickHouseQueries() = %v, want %v", string(got), string(exp)) } }) } @@ -650,19 +588,21 @@ func TestTransformToTableForClickHouseQueriesSorting(t *testing.T) { Table: &v3.Table{ Columns: []*v3.TableColumn{ {Name: "service"}, - {Name: "A"}, - {Name: "B"}, + {Name: "A", QueryName: "A", IsValueColumn: true}, + {Name: "B", QueryName: "B", IsValueColumn: true}, }, Rows: []*v3.TableRow{ - {Data: []interface{}{"backend", 20.0, nil}}, - {Data: []interface{}{"frontend", nil, 10.0}}, + {Data: map[string]interface{}{"service": "backend", "A": 20.0, "B": "n/a"}}, + {Data: map[string]interface{}{"service": "frontend", "A": "n/a", "B": 10.0}}, }, }, }, } result := TransformToTableForClickHouseQueries(input) - if !reflect.DeepEqual(result, expected) { - t.Errorf("TransformToTableForClickHouseQueries() sorting test failed. Got %v, want %v", result, expected) + exp, _ := json.Marshal(expected) + got, _ := json.Marshal(result) + if !bytes.Equal(got, exp) { + t.Errorf("TransformToTableForClickHouseQueries() sorting test failed. Got %v, want %v", string(got), string(exp)) } } From de571aa69a022e096c2beca0c67a37b3dd2114ae Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 26 Jun 2024 16:19:24 +0530 Subject: [PATCH 046/281] chore: flaky TestTransformToTableForClickHouseQueries (#5355) --- pkg/query-service/postprocess/table_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/query-service/postprocess/table_test.go b/pkg/query-service/postprocess/table_test.go index 04a835c2ff..317626766b 100644 --- a/pkg/query-service/postprocess/table_test.go +++ b/pkg/query-service/postprocess/table_test.go @@ -357,7 +357,8 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { Series: []*v3.Series{ { LabelsArray: []map[string]string{ - {"service": "frontend", "env": "prod"}, + {"service": "frontend"}, + {"env": "prod"}, }, Points: []v3.Point{ {Value: 10.0}, @@ -365,7 +366,8 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { }, { LabelsArray: []map[string]string{ - {"service": "backend", "env": "prod"}, + {"service": "backend"}, + {"env": "prod"}, }, Points: []v3.Point{ {Value: 20.0}, @@ -378,7 +380,8 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { Series: []*v3.Series{ { LabelsArray: []map[string]string{ - {"service": "frontend", "env": "prod"}, + {"service": "frontend"}, + {"env": "prod"}, }, Points: []v3.Point{ {Value: 15.0}, @@ -386,7 +389,8 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) { }, { LabelsArray: []map[string]string{ - {"service": "backend", "env": "prod"}, + {"service": "backend"}, + {"env": "prod"}, }, Points: []v3.Point{ {Value: 25.0}, From 2fb5b16840397541fdfffbaa6688fb7d7fe93bc3 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Wed, 26 Jun 2024 16:30:11 +0530 Subject: [PATCH 047/281] fix: search not working in services table (#5353) --- .../Columns/GetColumnSearchProps.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/ServiceApplication/Columns/GetColumnSearchProps.tsx b/frontend/src/container/ServiceApplication/Columns/GetColumnSearchProps.tsx index 372f301008..037748b3dc 100644 --- a/frontend/src/container/ServiceApplication/Columns/GetColumnSearchProps.tsx +++ b/frontend/src/container/ServiceApplication/Columns/GetColumnSearchProps.tsx @@ -33,10 +33,12 @@ export const getColumnSearchProps = ( record: ServicesList, ): boolean => { if (record[dataIndex]) { - record[dataIndex] - ?.toString() - .toLowerCase() - .includes(value.toString().toLowerCase()); + return ( + record[dataIndex] + ?.toString() + .toLowerCase() + .includes(value.toString().toLowerCase()) || false + ); } return false; From 580198ca7a30b51a978da53594ae5166035baec0 Mon Sep 17 00:00:00 2001 From: ahmadshaheer1 Date: Sun, 23 Jun 2024 14:33:16 +0430 Subject: [PATCH 048/281] feat: remove pagination for single-page lists --- .../components/ResizeTable/ResizeTable.tsx | 5 ++++- frontend/src/container/AllError/index.tsx | 1 + .../container/ListAlertRules/ListAlert.tsx | 1 + .../ListOfDashboard/DashboardsList.tsx | 21 ++++++++++--------- .../container/PipelinePage/Layouts/config.ts | 1 + .../PlannedDowntime/PlannedDowntimeList.tsx | 7 ++++++- .../ServiceMetrics/ServiceMetricTable.tsx | 12 ++++++----- .../ServiceTraces/ServiceTracesTable.tsx | 12 ++++++----- .../src/container/Trace/TraceTable/index.tsx | 1 + .../DataCollected.tsx | 6 ++++-- frontend/src/pages/SaveView/index.tsx | 4 +++- 11 files changed, 46 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/ResizeTable/ResizeTable.tsx b/frontend/src/components/ResizeTable/ResizeTable.tsx index 90cc588c47..0f3a53077a 100644 --- a/frontend/src/components/ResizeTable/ResizeTable.tsx +++ b/frontend/src/components/ResizeTable/ResizeTable.tsx @@ -20,6 +20,7 @@ import { ResizeTableProps } from './types'; function ResizeTable({ columns, onDragColumn, + pagination, ...restProps }: ResizeTableProps): JSX.Element { const [columnsData, setColumns] = useState([]); @@ -63,8 +64,10 @@ function ResizeTable({ ...restProps, components: { header: { cell: ResizableHeader } }, columns: mergedColumns, + hideOnSinglePage: true, + pagination: { ...pagination, hideOnSinglePage: true }, }), - [mergedColumns, restProps], + [mergedColumns, pagination, restProps], ); useEffect(() => { diff --git a/frontend/src/container/AllError/index.tsx b/frontend/src/container/AllError/index.tsx index e8c13d88cd..a112301bf0 100644 --- a/frontend/src/container/AllError/index.tsx +++ b/frontend/src/container/AllError/index.tsx @@ -423,6 +423,7 @@ function AllErrors(): JSX.Element { current: getUpdatedOffset / 10 + 1, position: ['bottomLeft'], total: errorCountResponse.data?.payload || 0, + hideOnSinglePage: true, }} onChange={onChangeHandler} /> diff --git a/frontend/src/container/ListAlertRules/ListAlert.tsx b/frontend/src/container/ListAlertRules/ListAlert.tsx index 91e0786e05..70039a5de9 100644 --- a/frontend/src/container/ListAlertRules/ListAlert.tsx +++ b/frontend/src/container/ListAlertRules/ListAlert.tsx @@ -389,6 +389,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { onChange={handleChange} pagination={{ defaultCurrent: Number(paginationParam) || 1, + hideOnSinglePage: true, }} facingIssueBtn={{ attributes: { diff --git a/frontend/src/container/ListOfDashboard/DashboardsList.tsx b/frontend/src/container/ListOfDashboard/DashboardsList.tsx index 1c8e96b6f7..39326a558a 100644 --- a/frontend/src/container/ListOfDashboard/DashboardsList.tsx +++ b/frontend/src/container/ListOfDashboard/DashboardsList.tsx @@ -609,6 +609,16 @@ function DashboardsList(): JSX.Element { ); + const paginationConfig = data.length > 20 && { + pageSize: 20, + showTotal: showPaginationItem, + showSizeChanger: false, + onChange: (page: any): void => handlePageSizeUpdate(page), + current: Number(sortOrder.pagination), + defaultCurrent: Number(sortOrder.pagination) || 1, + hideOnSinglePage: true, + }; + return (
@@ -822,16 +832,7 @@ function DashboardsList(): JSX.Element { showSorterTooltip loading={isDashboardListLoading || isFilteringDashboards} showHeader={false} - pagination={ - data.length > 20 && { - pageSize: 20, - showTotal: showPaginationItem, - showSizeChanger: false, - onChange: (page): void => handlePageSizeUpdate(page), - current: Number(sortOrder.pagination), - defaultCurrent: Number(sortOrder.pagination) || 1, - } - } + pagination={paginationConfig} /> )} diff --git a/frontend/src/container/PipelinePage/Layouts/config.ts b/frontend/src/container/PipelinePage/Layouts/config.ts index 46d2d2738e..6984fd7b57 100644 --- a/frontend/src/container/PipelinePage/Layouts/config.ts +++ b/frontend/src/container/PipelinePage/Layouts/config.ts @@ -1,3 +1,4 @@ export const historyPagination = { defaultPageSize: 5, + hideOnSinglePage: true, }; diff --git a/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx b/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx index 40c468bfb2..b8c1ec405a 100644 --- a/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx +++ b/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx @@ -334,6 +334,11 @@ export function PlannedDowntimeList({ } }, [downtimeSchedules.error, downtimeSchedules.isError, notifications]); + const paginationConfig = { + pageSize: 5, + showSizeChanger: false, + hideOnSinglePage: true, + }; return ( columns={columns} @@ -342,7 +347,7 @@ export function PlannedDowntimeList({ dataSource={tableData || []} loading={downtimeSchedules.isLoading || downtimeSchedules.isFetching} showHeader={false} - pagination={{ pageSize: 5, showSizeChanger: false }} + pagination={paginationConfig} /> ); } diff --git a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx index f592f6a540..919e88d07e 100644 --- a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx +++ b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx @@ -79,6 +79,12 @@ function ServiceMetricTable({ } }, [services, licenseData, isFetching, isCloudUserVal]); + const paginationConfig = { + defaultPageSize: 10, + showTotal: (total: number, range: number[]): string => + `${range[0]}-${range[1]} of ${total} items`, + hideOnSinglePage: true, + }; return ( <> {RPS > MAX_RPS_LIMIT && ( @@ -92,11 +98,7 @@ function ServiceMetricTable({ - `${range[0]}-${range[1]} of ${total} items`, - }} + pagination={paginationConfig} columns={tableColumns} loading={isLoading} dataSource={services} diff --git a/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx b/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx index 717060d901..2ca57f7125 100644 --- a/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx +++ b/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx @@ -36,6 +36,12 @@ function ServiceTraceTable({ } }, [services, licenseData, isFetching, isCloudUserVal]); + const paginationConfig = { + defaultPageSize: 10, + showTotal: (total: number, range: number[]): string => + `${range[0]}-${range[1]} of ${total} items`, + hideOnSinglePage: true, + }; return ( <> {RPS > MAX_RPS_LIMIT && ( @@ -49,11 +55,7 @@ function ServiceTraceTable({ - `${range[0]}-${range[1]} of ${total} items`, - }} + pagination={paginationConfig} columns={tableColumns} loading={loading} dataSource={services} diff --git a/frontend/src/container/Trace/TraceTable/index.tsx b/frontend/src/container/Trace/TraceTable/index.tsx index fc76b2bd79..a9f61f73fc 100644 --- a/frontend/src/container/Trace/TraceTable/index.tsx +++ b/frontend/src/container/Trace/TraceTable/index.tsx @@ -215,6 +215,7 @@ function TraceTable(): JSX.Element { responsive: true, position: ['bottomLeft'], total: totalCount, + hideOnSinglePage: true, }} /> ); diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/DataCollected.tsx b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/DataCollected.tsx index 1c605ec863..768a4a7941 100644 --- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/DataCollected.tsx +++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/DataCollected.tsx @@ -46,6 +46,8 @@ function DataCollected(props: DataCollectedProps): JSX.Element { }, ]; + const paginationConfig = { pageSize: 20, hideOnSinglePage: true }; + return (
@@ -59,7 +61,7 @@ function DataCollected(props: DataCollectedProps): JSX.Element { index % 2 === 0 ? 'table-row-dark' : '' } dataSource={logsData} - pagination={{ pageSize: 20 }} + pagination={paginationConfig} className="logs-section-table" />
@@ -74,7 +76,7 @@ function DataCollected(props: DataCollectedProps): JSX.Element { index % 2 === 0 ? 'table-row-dark' : '' } dataSource={metricsData} - pagination={{ pageSize: 20 }} + pagination={paginationConfig} className="metrics-section-table" />
diff --git a/frontend/src/pages/SaveView/index.tsx b/frontend/src/pages/SaveView/index.tsx index 86a511291e..02a2578f0a 100644 --- a/frontend/src/pages/SaveView/index.tsx +++ b/frontend/src/pages/SaveView/index.tsx @@ -277,6 +277,8 @@ function SaveView(): JSX.Element { }, ]; + const paginationConfig = { pageSize: 5, hideOnSinglePage: true }; + return (
@@ -303,7 +305,7 @@ function SaveView(): JSX.Element { dataSource={dataSource} loading={isLoading || isRefetching} showHeader={false} - pagination={{ pageSize: 5 }} + pagination={paginationConfig} />
From 46b4c8a004554f8bda4dc4fe50c3cfd77103a0b0 Mon Sep 17 00:00:00 2001 From: ahmadshaheer1 Date: Sun, 23 Jun 2024 14:43:51 +0430 Subject: [PATCH 049/281] chore: extract pagination config --- frontend/src/container/ListAlertRules/ListAlert.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/ListAlertRules/ListAlert.tsx b/frontend/src/container/ListAlertRules/ListAlert.tsx index 70039a5de9..e53a80dfdf 100644 --- a/frontend/src/container/ListAlertRules/ListAlert.tsx +++ b/frontend/src/container/ListAlertRules/ListAlert.tsx @@ -356,6 +356,10 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { }); } + const paginationConfig = { + defaultCurrent: Number(paginationParam) || 1, + hideOnSinglePage: true, + }; return ( <> @@ -387,10 +391,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { dataSource={data} dynamicColumns={dynamicColumns} onChange={handleChange} - pagination={{ - defaultCurrent: Number(paginationParam) || 1, - hideOnSinglePage: true, - }} + pagination={paginationConfig} facingIssueBtn={{ attributes: { screen: 'Alert list page', From f23ceea54e78a2782a36fccc36ea4915d9c74818 Mon Sep 17 00:00:00 2001 From: ahmadshaheer1 Date: Sun, 23 Jun 2024 15:12:56 +0430 Subject: [PATCH 050/281] chore: remove the unnecessary hideOnSinglePage prop --- frontend/src/components/ResizeTable/ResizeTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/ResizeTable/ResizeTable.tsx b/frontend/src/components/ResizeTable/ResizeTable.tsx index 0f3a53077a..d2bd98f568 100644 --- a/frontend/src/components/ResizeTable/ResizeTable.tsx +++ b/frontend/src/components/ResizeTable/ResizeTable.tsx @@ -64,7 +64,6 @@ function ResizeTable({ ...restProps, components: { header: { cell: ResizableHeader } }, columns: mergedColumns, - hideOnSinglePage: true, pagination: { ...pagination, hideOnSinglePage: true }, }), [mergedColumns, pagination, restProps], From 13df87ed69105f40efa8da70e567c718b60274f2 Mon Sep 17 00:00:00 2001 From: ahmadshaheer1 Date: Sun, 23 Jun 2024 15:14:24 +0430 Subject: [PATCH 051/281] chore: discard passing 'hideOnSinglePage' to diff --git a/frontend/src/container/ListAlertRules/ListAlert.tsx b/frontend/src/container/ListAlertRules/ListAlert.tsx index e53a80dfdf..2a0ccd3d79 100644 --- a/frontend/src/container/ListAlertRules/ListAlert.tsx +++ b/frontend/src/container/ListAlertRules/ListAlert.tsx @@ -358,7 +358,6 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { const paginationConfig = { defaultCurrent: Number(paginationParam) || 1, - hideOnSinglePage: true, }; return ( <> diff --git a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx index 919e88d07e..33a4dd729b 100644 --- a/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx +++ b/frontend/src/container/ServiceApplication/ServiceMetrics/ServiceMetricTable.tsx @@ -83,7 +83,6 @@ function ServiceMetricTable({ defaultPageSize: 10, showTotal: (total: number, range: number[]): string => `${range[0]}-${range[1]} of ${total} items`, - hideOnSinglePage: true, }; return ( <> diff --git a/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx b/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx index 2ca57f7125..8b03027a16 100644 --- a/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx +++ b/frontend/src/container/ServiceApplication/ServiceTraces/ServiceTracesTable.tsx @@ -40,7 +40,6 @@ function ServiceTraceTable({ defaultPageSize: 10, showTotal: (total: number, range: number[]): string => `${range[0]}-${range[1]} of ${total} items`, - hideOnSinglePage: true, }; return ( <> diff --git a/frontend/src/container/Trace/TraceTable/index.tsx b/frontend/src/container/Trace/TraceTable/index.tsx index a9f61f73fc..fc76b2bd79 100644 --- a/frontend/src/container/Trace/TraceTable/index.tsx +++ b/frontend/src/container/Trace/TraceTable/index.tsx @@ -215,7 +215,6 @@ function TraceTable(): JSX.Element { responsive: true, position: ['bottomLeft'], total: totalCount, - hideOnSinglePage: true, }} /> ); From da3f6fd7fddf9554175c1f42799ed3683cfce59e Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Wed, 26 Jun 2024 22:09:14 +0530 Subject: [PATCH 052/281] fix: labelsArray being returned null despite of labels being present (#5357) --- frontend/src/lib/query/createTableColumnsFromQuery.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/query/createTableColumnsFromQuery.ts b/frontend/src/lib/query/createTableColumnsFromQuery.ts index 18926f2f0c..190f7996aa 100644 --- a/frontend/src/lib/query/createTableColumnsFromQuery.ts +++ b/frontend/src/lib/query/createTableColumnsFromQuery.ts @@ -583,11 +583,11 @@ export const createTableColumnsFromQuery: CreateTableDataFromQuery = ({ q.series?.sort((a, b) => { let labelA = ''; let labelB = ''; - a.labelsArray.forEach((lab) => { + a.labelsArray?.forEach((lab) => { labelA += Object.values(lab)[0]; }); - b.labelsArray.forEach((lab) => { + b.labelsArray?.forEach((lab) => { labelB += Object.values(lab)[0]; }); From a7e02af8b0fabfcd5e5e86bd2c892cde94047764 Mon Sep 17 00:00:00 2001 From: KJ <38991076+KJ1010G@users.noreply.github.com> Date: Thu, 27 Jun 2024 00:07:34 +0530 Subject: [PATCH 053/281] Hot rod load command fix (#5352) * fix: added user_count and spawn_rate options to hotRod load data command * fix: removed locust_count and hatch_rate options * fix: updated user_count and spawn_rate values to the default values used in other places --------- Co-authored-by: Prashant Shahi --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6123bd747b..cc1c4399d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -347,7 +347,7 @@ curl -sL https://github.com/SigNoz/signoz/raw/develop/sample-apps/hotrod/hotrod- ```bash kubectl -n sample-application run strzal --image=djbingham/curl \ --restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \ - 'locust_count=6' -F 'hatch_rate=2' http://locust-master:8089/swarm + 'user_count=6' -F 'spawn_rate=2' http://locust-master:8089/swarm ``` **5.1.3 To stop the load generation:** From 7ddfadfb183c9908f9b48d2254f48c8560968cdf Mon Sep 17 00:00:00 2001 From: ahmadshaheer1 Date: Thu, 27 Jun 2024 09:18:39 +0430 Subject: [PATCH 054/281] fix: remove duplicate severityText in raw logs --- .../src/components/Logs/RawLogView/index.tsx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/Logs/RawLogView/index.tsx b/frontend/src/components/Logs/RawLogView/index.tsx index fcb8beeeec..d1ae19fe99 100644 --- a/frontend/src/components/Logs/RawLogView/index.tsx +++ b/frontend/src/components/Logs/RawLogView/index.tsx @@ -62,8 +62,6 @@ function RawLogView({ const isDarkMode = useIsDarkMode(); const isReadOnlyLog = !isLogsExplorerPage || isReadOnly; - const severityText = data.severity_text ? `${data.severity_text} |` : ''; - const logType = getLogIndicatorType(data); const updatedSelecedFields = useMemo( @@ -88,17 +86,16 @@ function RawLogView({ attributesText += ' | '; } - const text = useMemo( - () => + const text = useMemo(() => { + const date = typeof data.timestamp === 'string' - ? `${dayjs(data.timestamp).format( - 'YYYY-MM-DD HH:mm:ss.SSS', - )} | ${attributesText} ${severityText} ${data.body}` - : `${dayjs(data.timestamp / 1e6).format( - 'YYYY-MM-DD HH:mm:ss.SSS', - )} | ${attributesText} ${severityText} ${data.body}`, - [data.timestamp, data.body, severityText, attributesText], - ); + ? dayjs(data.timestamp) + : dayjs(data.timestamp / 1e6); + + return `${date.format('YYYY-MM-DD HH:mm:ss.SSS')} | ${attributesText} ${ + data.body + }`; + }, [data.timestamp, data.body, attributesText]); const handleClickExpand = useCallback(() => { if (activeContextLog || isReadOnly) return; From 4f2c314f395d04c1c151301b411dc2424013a51c Mon Sep 17 00:00:00 2001 From: Nityananda Gohain Date: Thu, 27 Jun 2024 12:34:23 +0530 Subject: [PATCH 055/281] feat: add spanKind and status in span response (#5120) * feat: add spanKind and errorMessage in span response * fix: add statusCodeString --- .../app/clickhouseReader/reader.go | 2 +- pkg/query-service/model/response.go | 31 ++++++++++--------- pkg/query-service/model/response_easyjson.go | 21 +++++++++++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index c82afb9861..4e5f342301 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -1942,7 +1942,7 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc end := time.Now() zap.L().Debug("getTraceSQLQuery took: ", zap.Duration("duration", end.Sub(start))) searchSpansResult := []model.SearchSpansResult{{ - Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError"}, + Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError", "StatusMessage", "StatusCodeString", "SpanKind"}, Events: make([][]interface{}, len(searchScanResponses)), IsSubTree: false, }, diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index 5ad5ea54ef..d13ebd0cdb 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -252,19 +252,22 @@ type Event struct { //easyjson:json type SearchSpanResponseItem struct { - TimeUnixNano uint64 `json:"timestamp"` - DurationNano int64 `json:"durationNano"` - SpanID string `json:"spanId"` - RootSpanID string `json:"rootSpanId"` - TraceID string `json:"traceId"` - HasError bool `json:"hasError"` - Kind int32 `json:"kind"` - ServiceName string `json:"serviceName"` - Name string `json:"name"` - References []OtelSpanRef `json:"references,omitempty"` - TagMap map[string]string `json:"tagMap"` - Events []string `json:"event"` - RootName string `json:"rootName"` + TimeUnixNano uint64 `json:"timestamp"` + DurationNano int64 `json:"durationNano"` + SpanID string `json:"spanId"` + RootSpanID string `json:"rootSpanId"` + TraceID string `json:"traceId"` + HasError bool `json:"hasError"` + Kind int32 `json:"kind"` + ServiceName string `json:"serviceName"` + Name string `json:"name"` + References []OtelSpanRef `json:"references,omitempty"` + TagMap map[string]string `json:"tagMap"` + Events []string `json:"event"` + RootName string `json:"rootName"` + StatusMessage string `json:"statusMessage"` + StatusCodeString string `json:"statusCodeString"` + SpanKind string `json:"spanKind"` } type OtelSpanRef struct { @@ -301,7 +304,7 @@ func (item *SearchSpanResponseItem) GetValues() []interface{} { keys = append(keys, k) values = append(values, v) } - returnArray := []interface{}{item.TimeUnixNano, item.SpanID, item.TraceID, item.ServiceName, item.Name, strconv.Itoa(int(item.Kind)), strconv.FormatInt(item.DurationNano, 10), keys, values, referencesStringArray, item.Events, item.HasError} + returnArray := []interface{}{item.TimeUnixNano, item.SpanID, item.TraceID, item.ServiceName, item.Name, strconv.Itoa(int(item.Kind)), strconv.FormatInt(item.DurationNano, 10), keys, values, referencesStringArray, item.Events, item.HasError, item.StatusMessage, item.StatusCodeString, item.SpanKind} return returnArray } diff --git a/pkg/query-service/model/response_easyjson.go b/pkg/query-service/model/response_easyjson.go index 8c00b2a80b..708bc55707 100644 --- a/pkg/query-service/model/response_easyjson.go +++ b/pkg/query-service/model/response_easyjson.go @@ -118,6 +118,12 @@ func easyjson6ff3ac1dDecodeGoSignozIoSignozPkgQueryServiceModel(in *jlexer.Lexer } case "rootName": out.RootName = string(in.String()) + case "statusMessage": + out.StatusMessage = string(in.String()) + case "statusCodeString": + out.StatusCodeString = string(in.String()) + case "spanKind": + out.SpanKind = string(in.String()) default: in.SkipRecursive() } @@ -233,6 +239,21 @@ func easyjson6ff3ac1dEncodeGoSignozIoSignozPkgQueryServiceModel(out *jwriter.Wri out.RawString(prefix) out.String(string(in.RootName)) } + { + const prefix string = ",\"statusMessage\":" + out.RawString(prefix) + out.String(string(in.StatusMessage)) + } + { + const prefix string = ",\"statusCodeString\":" + out.RawString(prefix) + out.String(string(in.StatusCodeString)) + } + { + const prefix string = ",\"spanKind\":" + out.RawString(prefix) + out.String(string(in.SpanKind)) + } out.RawByte('}') } From 53c6288025a513ea82926789047c11a2c58ff820 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:40:11 +0530 Subject: [PATCH 056/281] feat: added track event in Alerts - (multiple places) (#5354) * feat: added track event in Alerts - (multiple places) * feat: comment resolve and code refactor * feat: add Alert Channel: Channel list page visited event * feat: removed testSuccess variable and used responseStatus directly * feat: added save status in alert channel: save action * feat: added channel detail in save and test notification event * feat: code refactor * feat: added status message for save and test * feat: added status message for save channel events * feat: code refactor --- frontend/src/components/DropDown/DropDown.tsx | 13 +- .../ResizeTable/DynamicColumnTable.tsx | 20 ++- frontend/src/components/ResizeTable/types.ts | 1 + .../src/container/AllAlertChannels/index.tsx | 12 +- .../container/CreateAlertChannels/index.tsx | 150 +++++++++++++----- .../CreateAlertRule/SelectAlertType/index.tsx | 9 ++ .../src/container/CreateAlertRule/index.tsx | 3 + .../src/container/EditAlertChannels/index.tsx | 129 ++++++++++----- .../container/FormAlertRules/BasicInfo.tsx | 18 +++ .../container/FormAlertRules/QuerySection.tsx | 13 +- .../src/container/FormAlertRules/index.tsx | 83 +++++++++- .../AlertsEmptyState/AlertInfoCard.tsx | 3 + .../AlertsEmptyState/AlertsEmptyState.tsx | 73 +++++++-- .../AlertsEmptyState/InfoLinkText.tsx | 3 + .../AlertsEmptyState/alertLinks.ts | 9 ++ .../container/ListAlertRules/ListAlert.tsx | 9 +- .../src/container/ListAlertRules/index.tsx | 15 +- .../src/container/ListAlertRules/utils.ts | 32 ++++ .../src/container/TriggeredAlerts/index.tsx | 14 ++ 19 files changed, 495 insertions(+), 114 deletions(-) diff --git a/frontend/src/components/DropDown/DropDown.tsx b/frontend/src/components/DropDown/DropDown.tsx index df845b7084..e847e895be 100644 --- a/frontend/src/components/DropDown/DropDown.tsx +++ b/frontend/src/components/DropDown/DropDown.tsx @@ -5,7 +5,13 @@ import { Button, Dropdown, MenuProps } from 'antd'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useState } from 'react'; -function DropDown({ element }: { element: JSX.Element[] }): JSX.Element { +function DropDown({ + element, + onDropDownItemClick, +}: { + element: JSX.Element[]; + onDropDownItemClick?: MenuProps['onClick']; +}): JSX.Element { const isDarkMode = useIsDarkMode(); const items: MenuProps['items'] = element.map( @@ -23,6 +29,7 @@ function DropDown({ element }: { element: JSX.Element[] }): JSX.Element { items, onMouseEnter: (): void => setDdOpen(true), onMouseLeave: (): void => setDdOpen(false), + onClick: (item): void => onDropDownItemClick?.(item), }} open={isDdOpen} > @@ -40,4 +47,8 @@ function DropDown({ element }: { element: JSX.Element[] }): JSX.Element { ); } +DropDown.defaultProps = { + onDropDownItemClick: (): void => {}, +}; + export default DropDown; diff --git a/frontend/src/components/ResizeTable/DynamicColumnTable.tsx b/frontend/src/components/ResizeTable/DynamicColumnTable.tsx index fb5d734ee8..5a3dfd39dd 100644 --- a/frontend/src/components/ResizeTable/DynamicColumnTable.tsx +++ b/frontend/src/components/ResizeTable/DynamicColumnTable.tsx @@ -2,7 +2,9 @@ import './DynamicColumnTable.syles.scss'; import { Button, Dropdown, Flex, MenuProps, Switch } from 'antd'; +import { ColumnGroupType, ColumnType } from 'antd/es/table'; import { ColumnsType } from 'antd/lib/table'; +import logEvent from 'api/common/logEvent'; import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn'; import { SlidersHorizontal } from 'lucide-react'; import { memo, useEffect, useState } from 'react'; @@ -22,6 +24,7 @@ function DynamicColumnTable({ dynamicColumns, onDragColumn, facingIssueBtn, + shouldSendAlertsLogEvent, ...restProps }: DynamicColumnTableProps): JSX.Element { const [columnsData, setColumnsData] = useState( @@ -47,11 +50,18 @@ function DynamicColumnTable({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [columns, dynamicColumns]); - const onToggleHandler = (index: number) => ( - checked: boolean, - event: React.MouseEvent, - ): void => { + const onToggleHandler = ( + index: number, + column: ColumnGroupType | ColumnType, + ) => (checked: boolean, event: React.MouseEvent): void => { event.stopPropagation(); + + if (shouldSendAlertsLogEvent) { + logEvent('Alert: Column toggled', { + column: column?.title, + action: checked ? 'Enable' : 'Disable', + }); + } setVisibleColumns({ tablesource, dynamicColumns, @@ -75,7 +85,7 @@ function DynamicColumnTable({
{column.title?.toString()}
c.key === column.key) !== -1} - onChange={onToggleHandler(index)} + onChange={onToggleHandler(index, column)} />
), diff --git a/frontend/src/components/ResizeTable/types.ts b/frontend/src/components/ResizeTable/types.ts index 35a13127a8..693e5ffda1 100644 --- a/frontend/src/components/ResizeTable/types.ts +++ b/frontend/src/components/ResizeTable/types.ts @@ -14,6 +14,7 @@ export interface DynamicColumnTableProps extends TableProps { dynamicColumns: TableProps['columns']; onDragColumn?: (fromIndex: number, toIndex: number) => void; facingIssueBtn?: FacingIssueBtnProps; + shouldSendAlertsLogEvent?: boolean; } export type GetVisibleColumnsFunction = ( diff --git a/frontend/src/container/AllAlertChannels/index.tsx b/frontend/src/container/AllAlertChannels/index.tsx index 5f34264a60..85b42de094 100644 --- a/frontend/src/container/AllAlertChannels/index.tsx +++ b/frontend/src/container/AllAlertChannels/index.tsx @@ -1,13 +1,15 @@ import { PlusOutlined } from '@ant-design/icons'; import { Tooltip, Typography } from 'antd'; import getAll from 'api/channels/getAll'; +import logEvent from 'api/common/logEvent'; import Spinner from 'components/Spinner'; import TextToolTip from 'components/TextToolTip'; import ROUTES from 'constants/routes'; import useComponentPermission from 'hooks/useComponentPermission'; import useFetch from 'hooks/useFetch'; import history from 'lib/history'; -import { useCallback } from 'react'; +import { isUndefined } from 'lodash-es'; +import { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -31,6 +33,14 @@ function AlertChannels(): JSX.Element { const { loading, payload, error, errorMessage } = useFetch(getAll); + useEffect(() => { + if (!isUndefined(payload)) { + logEvent('Alert Channel: Channel list page visited', { + number: payload?.length, + }); + } + }, [payload]); + if (error) { return {errorMessage}; } diff --git a/frontend/src/container/CreateAlertChannels/index.tsx b/frontend/src/container/CreateAlertChannels/index.tsx index d10b6fb225..85d609c24c 100644 --- a/frontend/src/container/CreateAlertChannels/index.tsx +++ b/frontend/src/container/CreateAlertChannels/index.tsx @@ -11,11 +11,12 @@ import testOpsGenie from 'api/channels/testOpsgenie'; import testPagerApi from 'api/channels/testPager'; import testSlackApi from 'api/channels/testSlack'; import testWebhookApi from 'api/channels/testWebhook'; +import logEvent from 'api/common/logEvent'; import ROUTES from 'constants/routes'; import FormAlertChannels from 'container/FormAlertChannels'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -43,6 +44,10 @@ function CreateAlertChannels({ const [formInstance] = Form.useForm(); + useEffect(() => { + logEvent('Alert Channel: Create channel page visited', {}); + }, []); + const [selectedConfig, setSelectedConfig] = useState< Partial< SlackChannel & @@ -139,19 +144,25 @@ function CreateAlertChannels({ description: t('channel_creation_done'), }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), - }); + return { status: 'success', statusMessage: t('channel_creation_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + return { + status: 'failed', + statusMessage: response.error || t('channel_creation_failed'), + }; } catch (error) { notifications.error({ message: 'Error', description: t('channel_creation_failed'), }); + return { status: 'failed', statusMessage: t('channel_creation_failed') }; + } finally { + setSavingState(false); } - setSavingState(false); }, [prepareSlackRequest, t, notifications]); const prepareWebhookRequest = useCallback(() => { @@ -200,19 +211,25 @@ function CreateAlertChannels({ description: t('channel_creation_done'), }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), - }); + return { status: 'success', statusMessage: t('channel_creation_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + return { + status: 'failed', + statusMessage: response.error || t('channel_creation_failed'), + }; } catch (error) { notifications.error({ message: 'Error', description: t('channel_creation_failed'), }); + return { status: 'failed', statusMessage: t('channel_creation_failed') }; + } finally { + setSavingState(false); } - setSavingState(false); }, [prepareWebhookRequest, t, notifications]); const preparePagerRequest = useCallback(() => { @@ -245,8 +262,8 @@ function CreateAlertChannels({ setSavingState(true); const request = preparePagerRequest(); - if (request) { - try { + try { + if (request) { const response = await createPagerApi(request); if (response.statusCode === 200) { @@ -255,20 +272,31 @@ function CreateAlertChannels({ description: t('channel_creation_done'), }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), - }); + return { status: 'success', statusMessage: t('channel_creation_done') }; } - } catch (e) { notifications.error({ message: 'Error', - description: t('channel_creation_failed'), + description: response.error || t('channel_creation_failed'), }); + return { + status: 'failed', + statusMessage: response.error || t('channel_creation_failed'), + }; } + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + return { status: 'failed', statusMessage: t('channel_creation_failed') }; + } catch (error) { + notifications.error({ + message: 'Error', + description: t('channel_creation_failed'), + }); + return { status: 'failed', statusMessage: t('channel_creation_failed') }; + } finally { + setSavingState(false); } - setSavingState(false); }, [t, notifications, preparePagerRequest]); const prepareOpsgenieRequest = useCallback( @@ -295,19 +323,25 @@ function CreateAlertChannels({ description: t('channel_creation_done'), }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), - }); + return { status: 'success', statusMessage: t('channel_creation_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + return { + status: 'failed', + statusMessage: response.error || t('channel_creation_failed'), + }; } catch (error) { notifications.error({ message: 'Error', description: t('channel_creation_failed'), }); + return { status: 'failed', statusMessage: t('channel_creation_failed') }; + } finally { + setSavingState(false); } - setSavingState(false); }, [prepareOpsgenieRequest, t, notifications]); const prepareEmailRequest = useCallback( @@ -332,19 +366,25 @@ function CreateAlertChannels({ description: t('channel_creation_done'), }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), - }); + return { status: 'success', statusMessage: t('channel_creation_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + return { + status: 'failed', + statusMessage: response.error || t('channel_creation_failed'), + }; } catch (error) { notifications.error({ message: 'Error', description: t('channel_creation_failed'), }); + return { status: 'failed', statusMessage: t('channel_creation_failed') }; + } finally { + setSavingState(false); } - setSavingState(false); }, [prepareEmailRequest, t, notifications]); const prepareMsTeamsRequest = useCallback( @@ -370,19 +410,25 @@ function CreateAlertChannels({ description: t('channel_creation_done'), }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_creation_failed'), - }); + return { status: 'success', statusMessage: t('channel_creation_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_creation_failed'), + }); + return { + status: 'failed', + statusMessage: response.error || t('channel_creation_failed'), + }; } catch (error) { notifications.error({ message: 'Error', description: t('channel_creation_failed'), }); + return { status: 'failed', statusMessage: t('channel_creation_failed') }; + } finally { + setSavingState(false); } - setSavingState(false); }, [prepareMsTeamsRequest, t, notifications]); const onSaveHandler = useCallback( @@ -400,7 +446,15 @@ function CreateAlertChannels({ const functionToCall = functionMapper[value as keyof typeof functionMapper]; if (functionToCall) { - functionToCall(); + const result = await functionToCall(); + logEvent('Alert Channel: Save channel', { + type: value, + sendResolvedAlert: selectedConfig.send_resolved, + name: selectedConfig.name, + new: 'true', + status: result?.status, + statusMessage: result?.statusMessage, + }); } else { notifications.error({ message: 'Error', @@ -409,6 +463,7 @@ function CreateAlertChannels({ } } }, + // eslint-disable-next-line react-hooks/exhaustive-deps [ onSlackHandler, onWebhookHandler, @@ -472,14 +527,25 @@ function CreateAlertChannels({ description: t('channel_test_failed'), }); } + + logEvent('Alert Channel: Test notification', { + type: channelType, + sendResolvedAlert: selectedConfig.send_resolved, + name: selectedConfig.name, + new: 'true', + status: + response && response.statusCode === 200 ? 'Test success' : 'Test failed', + }); } catch (error) { notifications.error({ message: 'Error', description: t('channel_test_unexpected'), }); } + setTestingState(false); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [ prepareWebhookRequest, t, diff --git a/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx b/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx index cd837b666b..52f4d52215 100644 --- a/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx +++ b/frontend/src/container/CreateAlertRule/SelectAlertType/index.tsx @@ -1,4 +1,6 @@ import { Row, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; +import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { AlertTypes } from 'types/api/alerts/alertTypes'; @@ -34,6 +36,13 @@ function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element { default: break; } + + logEvent('Alert: Sample alert link clicked', { + dataSource: ALERTS_DATA_SOURCE_MAP[option], + link: url, + page: 'New alert data source selection page', + }); + window.open(url, '_blank'); } const renderOptions = useMemo( diff --git a/frontend/src/container/CreateAlertRule/index.tsx b/frontend/src/container/CreateAlertRule/index.tsx index e5a4772f30..f7e491cd70 100644 --- a/frontend/src/container/CreateAlertRule/index.tsx +++ b/frontend/src/container/CreateAlertRule/index.tsx @@ -1,4 +1,5 @@ import { Form, Row } from 'antd'; +import logEvent from 'api/common/logEvent'; import { ENTITY_VERSION_V4 } from 'constants/app'; import { QueryParams } from 'constants/query'; import FormAlertRules from 'container/FormAlertRules'; @@ -68,6 +69,8 @@ function CreateRules(): JSX.Element { useEffect(() => { if (alertType) { onSelectType(alertType); + } else { + logEvent('Alert: New alert data source selection page visited', {}); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [alertType]); diff --git a/frontend/src/container/EditAlertChannels/index.tsx b/frontend/src/container/EditAlertChannels/index.tsx index 3c2e956f14..b4fe30d557 100644 --- a/frontend/src/container/EditAlertChannels/index.tsx +++ b/frontend/src/container/EditAlertChannels/index.tsx @@ -11,6 +11,7 @@ import testOpsgenie from 'api/channels/testOpsgenie'; import testPagerApi from 'api/channels/testPager'; import testSlackApi from 'api/channels/testSlack'; import testWebhookApi from 'api/channels/testWebhook'; +import logEvent from 'api/common/logEvent'; import ROUTES from 'constants/routes'; import { ChannelType, @@ -89,7 +90,7 @@ function EditAlertChannels({ description: t('webhook_url_required'), }); setSavingState(false); - return; + return { status: 'failed', statusMessage: t('webhook_url_required') }; } const response = await editSlackApi(prepareSlackRequest()); @@ -101,13 +102,17 @@ function EditAlertChannels({ }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); + return { status: 'success', statusMessage: t('channel_edit_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); setSavingState(false); + return { + status: 'failed', + statusMessage: response.error || t('channel_edit_failed'), + }; }, [prepareSlackRequest, t, notifications, selectedConfig]); const prepareWebhookRequest = useCallback(() => { @@ -136,13 +141,13 @@ function EditAlertChannels({ if (selectedConfig?.api_url === '') { showError(t('webhook_url_required')); setSavingState(false); - return; + return { status: 'failed', statusMessage: t('webhook_url_required') }; } if (username && (!password || password === '')) { showError(t('username_no_password')); setSavingState(false); - return; + return { status: 'failed', statusMessage: t('username_no_password') }; } const response = await editWebhookApi(prepareWebhookRequest()); @@ -154,10 +159,15 @@ function EditAlertChannels({ }); history.replace(ROUTES.ALL_CHANNELS); - } else { - showError(response.error || t('channel_edit_failed')); + return { status: 'success', statusMessage: t('channel_edit_done') }; } + showError(response.error || t('channel_edit_failed')); + setSavingState(false); + return { + status: 'failed', + statusMessage: response.error || t('channel_edit_failed'), + }; }, [prepareWebhookRequest, t, notifications, selectedConfig]); const prepareEmailRequest = useCallback( @@ -181,13 +191,18 @@ function EditAlertChannels({ description: t('channel_edit_done'), }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); + return { status: 'success', statusMessage: t('channel_edit_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + setSavingState(false); + return { + status: 'failed', + statusMessage: response.error || t('channel_edit_failed'), + }; }, [prepareEmailRequest, t, notifications]); const preparePagerRequest = useCallback( @@ -218,7 +233,7 @@ function EditAlertChannels({ description: validationError, }); setSavingState(false); - return; + return { status: 'failed', statusMessage: validationError }; } const response = await editPagerApi(preparePagerRequest()); @@ -229,13 +244,18 @@ function EditAlertChannels({ }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); + return { status: 'success', statusMessage: t('channel_edit_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + setSavingState(false); + return { + status: 'failed', + statusMessage: response.error || t('channel_edit_failed'), + }; }, [preparePagerRequest, notifications, selectedConfig, t]); const prepareOpsgenieRequest = useCallback( @@ -259,7 +279,7 @@ function EditAlertChannels({ description: t('api_key_required'), }); setSavingState(false); - return; + return { status: 'failed', statusMessage: t('api_key_required') }; } const response = await editOpsgenie(prepareOpsgenieRequest()); @@ -271,13 +291,18 @@ function EditAlertChannels({ }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); + return { status: 'success', statusMessage: t('channel_edit_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + setSavingState(false); + return { + status: 'failed', + statusMessage: response.error || t('channel_edit_failed'), + }; }, [prepareOpsgenieRequest, t, notifications, selectedConfig]); const prepareMsTeamsRequest = useCallback( @@ -301,7 +326,7 @@ function EditAlertChannels({ description: t('webhook_url_required'), }); setSavingState(false); - return; + return { status: 'failed', statusMessage: t('webhook_url_required') }; } const response = await editMsTeamsApi(prepareMsTeamsRequest()); @@ -313,31 +338,46 @@ function EditAlertChannels({ }); history.replace(ROUTES.ALL_CHANNELS); - } else { - notifications.error({ - message: 'Error', - description: response.error || t('channel_edit_failed'), - }); + return { status: 'success', statusMessage: t('channel_edit_done') }; } + notifications.error({ + message: 'Error', + description: response.error || t('channel_edit_failed'), + }); + setSavingState(false); + return { + status: 'failed', + statusMessage: response.error || t('channel_edit_failed'), + }; }, [prepareMsTeamsRequest, t, notifications, selectedConfig]); const onSaveHandler = useCallback( - (value: ChannelType) => { + async (value: ChannelType) => { + let result; if (value === ChannelType.Slack) { - onSlackEditHandler(); + result = await onSlackEditHandler(); } else if (value === ChannelType.Webhook) { - onWebhookEditHandler(); + result = await onWebhookEditHandler(); } else if (value === ChannelType.Pagerduty) { - onPagerEditHandler(); + result = await onPagerEditHandler(); } else if (value === ChannelType.MsTeams) { - onMsTeamsEditHandler(); + result = await onMsTeamsEditHandler(); } else if (value === ChannelType.Opsgenie) { - onOpsgenieEditHandler(); + result = await onOpsgenieEditHandler(); } else if (value === ChannelType.Email) { - onEmailEditHandler(); + result = await onEmailEditHandler(); } + logEvent('Alert Channel: Save channel', { + type: value, + sendResolvedAlert: selectedConfig.send_resolved, + name: selectedConfig.name, + new: 'false', + status: result?.status, + statusMessage: result?.statusMessage, + }); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [ onSlackEditHandler, onWebhookEditHandler, @@ -399,6 +439,14 @@ function EditAlertChannels({ description: t('channel_test_failed'), }); } + logEvent('Alert Channel: Test notification', { + type: channelType, + sendResolvedAlert: selectedConfig.send_resolved, + name: selectedConfig.name, + new: 'false', + status: + response && response.statusCode === 200 ? 'Test success' : 'Test failed', + }); } catch (error) { notifications.error({ message: 'Error', @@ -407,6 +455,7 @@ function EditAlertChannels({ } setTestingState(false); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [ t, prepareWebhookRequest, diff --git a/frontend/src/container/FormAlertRules/BasicInfo.tsx b/frontend/src/container/FormAlertRules/BasicInfo.tsx index 5fae4a713d..40edb7977e 100644 --- a/frontend/src/container/FormAlertRules/BasicInfo.tsx +++ b/frontend/src/container/FormAlertRules/BasicInfo.tsx @@ -3,6 +3,8 @@ import './FormAlertRules.styles.scss'; import { PlusOutlined } from '@ant-design/icons'; import { Button, Form, Select, Switch, Tooltip } from 'antd'; import getChannels from 'api/channels/getAll'; +import logEvent from 'api/common/logEvent'; +import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts'; import ROUTES from 'constants/routes'; import useComponentPermission from 'hooks/useComponentPermission'; import useFetch from 'hooks/useFetch'; @@ -10,6 +12,7 @@ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; +import { AlertTypes } from 'types/api/alerts/alertTypes'; import { AlertDef, Labels } from 'types/api/alerts/def'; import AppReducer from 'types/reducer/app'; import { requireErrorMessage } from 'utils/form/requireErrorMessage'; @@ -73,9 +76,24 @@ function BasicInfo({ const noChannels = channels.payload?.length === 0; const handleCreateNewChannels = useCallback(() => { + logEvent('Alert: Create notification channel button clicked', { + dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes], + ruleId: isNewRule ? 0 : alertDef?.id, + }); window.open(ROUTES.CHANNELS_NEW, '_blank'); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + if (!channels.loading && isNewRule) { + logEvent('Alert: New alert creation page visited', { + dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes], + numberOfChannels: channels.payload?.length, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [channels.payload, channels.loading]); + return ( <> {t('alert_form_step3')} diff --git a/frontend/src/container/FormAlertRules/QuerySection.tsx b/frontend/src/container/FormAlertRules/QuerySection.tsx index a567288585..aa56c84571 100644 --- a/frontend/src/container/FormAlertRules/QuerySection.tsx +++ b/frontend/src/container/FormAlertRules/QuerySection.tsx @@ -2,6 +2,7 @@ import './QuerySection.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Tabs, Tooltip } from 'antd'; +import logEvent from 'api/common/logEvent'; import PromQLIcon from 'assets/Dashboard/PromQl'; import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts'; import { ENTITY_VERSION_V4 } from 'constants/app'; @@ -31,6 +32,7 @@ function QuerySection({ runQuery, alertDef, panelType, + ruleId, }: QuerySectionProps): JSX.Element { // init namespace for translations const { t } = useTranslation('alerts'); @@ -158,7 +160,15 @@ function QuerySection({
@@ -113,14 +143,23 @@ export function AlertsEmptyState(): JSX.Element {
- {ALERT_CARDS.map((card) => ( - - ))} + {ALERT_CARDS.map((card) => { + const logEventTriggered = (): void => + alertLogEvents( + 'Alert: Sample alert link clicked', + card.link, + card.dataSource, + ); + return ( + + ); + })}
); diff --git a/frontend/src/container/ListAlertRules/AlertsEmptyState/InfoLinkText.tsx b/frontend/src/container/ListAlertRules/AlertsEmptyState/InfoLinkText.tsx index 1f17cd3969..3eb2ad4915 100644 --- a/frontend/src/container/ListAlertRules/AlertsEmptyState/InfoLinkText.tsx +++ b/frontend/src/container/ListAlertRules/AlertsEmptyState/InfoLinkText.tsx @@ -6,6 +6,7 @@ interface InfoLinkTextProps { link: string; leftIconVisible: boolean; rightIconVisible: boolean; + onClick: () => void; } function InfoLinkText({ @@ -13,10 +14,12 @@ function InfoLinkText({ link, leftIconVisible, rightIconVisible, + onClick, }: InfoLinkTextProps): JSX.Element { return ( { + onClick(); window.open(link, '_blank'); }} className="info-link-container" diff --git a/frontend/src/container/ListAlertRules/AlertsEmptyState/alertLinks.ts b/frontend/src/container/ListAlertRules/AlertsEmptyState/alertLinks.ts index fac0ad6b12..f58868b886 100644 --- a/frontend/src/container/ListAlertRules/AlertsEmptyState/alertLinks.ts +++ b/frontend/src/container/ListAlertRules/AlertsEmptyState/alertLinks.ts @@ -1,3 +1,5 @@ +import { DataSource } from 'types/common/queryBuilder'; + export const ALERT_INFO_LINKS = [ { infoText: 'How to create Metrics-based alerts', @@ -5,6 +7,7 @@ export const ALERT_INFO_LINKS = [ 'https://signoz.io/docs/alerts-management/metrics-based-alerts/?utm_source=product&utm_medium=alert-empty-page', leftIconVisible: false, rightIconVisible: true, + dataSource: DataSource.METRICS, }, { infoText: 'How to create Log-based alerts', @@ -12,6 +15,7 @@ export const ALERT_INFO_LINKS = [ 'https://signoz.io/docs/alerts-management/log-based-alerts/?utm_source=product&utm_medium=alert-empty-page', leftIconVisible: false, rightIconVisible: true, + dataSource: DataSource.LOGS, }, { infoText: 'How to create Trace-based alerts', @@ -19,6 +23,7 @@ export const ALERT_INFO_LINKS = [ 'https://signoz.io/docs/alerts-management/trace-based-alerts/?utm_source=product&utm_medium=alert-empty-page', leftIconVisible: false, rightIconVisible: true, + dataSource: DataSource.TRACES, }, ]; @@ -26,24 +31,28 @@ export const ALERT_CARDS = [ { header: 'Alert on high memory usage', subheader: "Monitor your host's memory usage", + dataSource: DataSource.METRICS, link: 'https://signoz.io/docs/alerts-management/metrics-based-alerts/?utm_source=product&utm_medium=alert-empty-page#1-alert-when-memory-usage-for-host-goes-above-400-mb-or-any-fixed-memory', }, { header: 'Alert on slow external API calls', subheader: 'Monitor your external API calls', + dataSource: DataSource.TRACES, link: 'https://signoz.io/docs/alerts-management/trace-based-alerts/?utm_source=product&utm_medium=alert-empty-page#1-alert-when-external-api-latency-p90-is-over-1-second-for-last-5-mins', }, { header: 'Alert on high percentage of timeout errors in logs', subheader: 'Monitor your logs for errors', + dataSource: DataSource.LOGS, link: 'https://signoz.io/docs/alerts-management/log-based-alerts/?utm_source=product&utm_medium=alert-empty-page#1-alert-when-percentage-of-redis-timeout-error-logs-greater-than-7-in-last-5-mins', }, { header: 'Alert on high error percentage of an endpoint', subheader: 'Monitor your API endpoint', + dataSource: DataSource.METRICS, link: 'https://signoz.io/docs/alerts-management/metrics-based-alerts/?utm_source=product&utm_medium=alert-empty-page#3-alert-when-the-error-percentage-for-an-endpoint-exceeds-5', }, diff --git a/frontend/src/container/ListAlertRules/ListAlert.tsx b/frontend/src/container/ListAlertRules/ListAlert.tsx index 2a0ccd3d79..88069195c2 100644 --- a/frontend/src/container/ListAlertRules/ListAlert.tsx +++ b/frontend/src/container/ListAlertRules/ListAlert.tsx @@ -3,6 +3,7 @@ import { PlusOutlined } from '@ant-design/icons'; import { Input, Typography } from 'antd'; import type { ColumnsType } from 'antd/es/table/interface'; import saveAlertApi from 'api/alerts/save'; +import logEvent from 'api/common/logEvent'; import DropDown from 'components/DropDown/DropDown'; import { listAlertMessage } from 'components/facingIssueBtn/util'; import { @@ -41,7 +42,7 @@ import { } from './styles'; import Status from './TableComponents/Status'; import ToggleAlertState from './ToggleAlertState'; -import { filterAlerts } from './utils'; +import { alertActionLogEvent, filterAlerts } from './utils'; const { Search } = Input; @@ -107,12 +108,16 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { }, [notificationsApi, t]); const onClickNewAlertHandler = useCallback(() => { + logEvent('Alert: New alert button clicked', { + number: allAlertRules?.length, + }); featureResponse .refetch() .then(() => { history.push(ROUTES.ALERTS_NEW); }) .catch(handleError); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [featureResponse, handleError]); const onEditHandler = (record: GettableAlert) => (): void => { @@ -321,6 +326,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element { width: 10, render: (id: GettableAlert['id'], record): JSX.Element => ( alertActionLogEvent(item.key, record)} element={[ { + if (!logEventCalledRef.current && !isUndefined(data?.payload)) { + logEvent('Alert: List page visited', { + number: data?.payload?.length, + }); + logEventCalledRef.current = true; + } + }, [data?.payload]); + useEffect(() => { if (status === 'error' || (status === 'success' && data.statusCode >= 400)) { notifications.error({ diff --git a/frontend/src/container/ListAlertRules/utils.ts b/frontend/src/container/ListAlertRules/utils.ts index a1cef5add7..32da7eaad5 100644 --- a/frontend/src/container/ListAlertRules/utils.ts +++ b/frontend/src/container/ListAlertRules/utils.ts @@ -1,3 +1,6 @@ +import logEvent from 'api/common/logEvent'; +import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts'; +import { AlertTypes } from 'types/api/alerts/alertTypes'; import { GettableAlert } from 'types/api/alerts/get'; export const filterAlerts = ( @@ -23,3 +26,32 @@ export const filterAlerts = ( ); }); }; + +export const alertActionLogEvent = ( + action: string, + record: GettableAlert, +): void => { + let actionValue = ''; + switch (action) { + case '0': + actionValue = 'Enable/Disable'; + break; + case '1': + actionValue = 'Edit'; + break; + case '2': + actionValue = 'Clone'; + break; + case '3': + actionValue = 'Delete'; + break; + default: + break; + } + logEvent('Alert: Action', { + ruleId: record.id, + dataSource: ALERTS_DATA_SOURCE_MAP[record.alertType as AlertTypes], + name: record.alert, + action: actionValue, + }); +}; diff --git a/frontend/src/container/TriggeredAlerts/index.tsx b/frontend/src/container/TriggeredAlerts/index.tsx index 106be51fda..d76945edd5 100644 --- a/frontend/src/container/TriggeredAlerts/index.tsx +++ b/frontend/src/container/TriggeredAlerts/index.tsx @@ -1,7 +1,10 @@ import getTriggeredApi from 'api/alerts/getTriggered'; +import logEvent from 'api/common/logEvent'; import Spinner from 'components/Spinner'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import useAxiosError from 'hooks/useAxiosError'; +import { isUndefined } from 'lodash-es'; +import { useEffect, useRef } from 'react'; import { useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -13,6 +16,8 @@ function TriggeredAlerts(): JSX.Element { (state) => state.app.user?.userId, ); + const hasLoggedEvent = useRef(false); // Track if logEvent has been called + const handleError = useAxiosError(); const alertsResponse = useQuery( @@ -29,6 +34,15 @@ function TriggeredAlerts(): JSX.Element { }, ); + useEffect(() => { + if (!hasLoggedEvent.current && !isUndefined(alertsResponse.data?.payload)) { + logEvent('Alert: Triggered alert list page visited', { + number: alertsResponse.data?.payload?.length, + }); + hasLoggedEvent.current = true; + } + }, [alertsResponse.data?.payload]); + if (alertsResponse.error) { return ; } From c5d23336a7b690979696001fc4870f7016393aa4 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Thu, 27 Jun 2024 22:04:14 +0530 Subject: [PATCH 057/281] chore: move the table calculation to backend (#5351) --- .../GridCard/FullView/index.tsx | 2 + .../GridCardLayout/GridCard/index.tsx | 1 + .../GridTableComponent/__tests__/response.ts | 215 ++++++++++++++++++ .../__tests__/utils.test.tsx | 42 ++++ .../container/GridTableComponent/index.tsx | 45 ++-- .../src/container/GridTableComponent/utils.ts | 89 ++++++++ .../WidgetGraph/WidgetGraphContainer.tsx | 10 +- .../NewWidget/LeftContainer/index.tsx | 6 + .../PanelWrapper/TablePanelWrapper.tsx | 2 +- frontend/src/lib/dashboard/getQueryResults.ts | 7 +- .../lib/dashboard/prepareQueryRangePayload.ts | 2 + .../src/types/api/metrics/getQueryRange.ts | 1 + 12 files changed, 397 insertions(+), 25 deletions(-) create mode 100644 frontend/src/container/GridTableComponent/__tests__/response.ts create mode 100644 frontend/src/container/GridTableComponent/__tests__/utils.test.tsx diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx index 02d71ed9bb..83ba3b7d4b 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx @@ -80,6 +80,8 @@ function FullView({ query: updatedQuery, globalSelectedInterval: globalSelectedTime, variables: getDashboardVariables(selectedDashboard?.data.variables), + fillGaps: widget.fillSpans, + formatForWeb: getGraphType(widget.panelTypes) === PANEL_TYPES.TABLE, }; } updatedQuery.builder.queryData[0].pageSize = 10; diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 28c67ae92e..2daac8c9b4 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -109,6 +109,7 @@ function GridCardGraph({ globalSelectedInterval, variables: getDashboardVariables(variables), fillGaps: widget.fillSpans, + formatForWeb: getGraphType(widget.panelTypes) === PANEL_TYPES.TABLE, }; } updatedQuery.builder.queryData[0].pageSize = 10; diff --git a/frontend/src/container/GridTableComponent/__tests__/response.ts b/frontend/src/container/GridTableComponent/__tests__/response.ts new file mode 100644 index 0000000000..677a1b18e3 --- /dev/null +++ b/frontend/src/container/GridTableComponent/__tests__/response.ts @@ -0,0 +1,215 @@ +export const tableDataMultipleQueriesSuccessResponse = { + columns: [ + { + name: 'service_name', + queryName: '', + isValueColumn: false, + }, + { + name: 'A', + queryName: 'A', + isValueColumn: true, + }, + { + name: 'B', + queryName: 'B', + isValueColumn: true, + }, + ], + rows: [ + { + data: { + A: 4196.71, + B: 'n/a', + service_name: 'demo-app', + }, + }, + { + data: { + A: 500.83, + B: 'n/a', + service_name: 'customer', + }, + }, + { + data: { + A: 499.5, + B: 'n/a', + service_name: 'mysql', + }, + }, + { + data: { + A: 293.22, + B: 'n/a', + service_name: 'frontend', + }, + }, + { + data: { + A: 230.03, + B: 'n/a', + service_name: 'driver', + }, + }, + { + data: { + A: 67.09, + B: 'n/a', + service_name: 'route', + }, + }, + { + data: { + A: 30.96, + B: 'n/a', + service_name: 'redis', + }, + }, + { + data: { + A: 'n/a', + B: 112.27, + service_name: 'n/a', + }, + }, + ], +}; + +export const widgetQueryWithLegend = { + clickhouse_sql: [ + { + name: 'A', + legend: '', + disabled: false, + query: '', + }, + ], + promql: [ + { + name: 'A', + query: '', + legend: '', + disabled: false, + }, + ], + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + dataType: 'float64', + id: 'signoz_latency--float64--ExponentialHistogram--true', + isColumn: true, + isJSON: false, + key: 'signoz_latency', + type: 'ExponentialHistogram', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [ + { + dataType: 'string', + isColumn: false, + isJSON: false, + key: 'service_name', + type: 'tag', + id: 'service_name--string--tag--false', + }, + ], + legend: 'p99', + reduceTo: 'avg', + }, + { + dataSource: 'metrics', + queryName: 'B', + aggregateOperator: 'rate', + aggregateAttribute: { + dataType: 'float64', + id: 'system_disk_operations--float64--Sum--true', + isColumn: true, + isJSON: false, + key: 'system_disk_operations', + type: 'Sum', + }, + timeAggregation: 'rate', + spaceAggregation: 'sum', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'B', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + ], + queryFormulas: [], + }, + id: '48ad5a67-9a3c-49d4-a886-d7a34f8b875d', + queryType: 'builder', +}; + +export const expectedOutputWithLegends = { + dataSource: [ + { + A: 4196.71, + B: 'n/a', + service_name: 'demo-app', + }, + { + A: 500.83, + B: 'n/a', + service_name: 'customer', + }, + { + A: 499.5, + B: 'n/a', + service_name: 'mysql', + }, + { + A: 293.22, + B: 'n/a', + service_name: 'frontend', + }, + { + A: 230.03, + B: 'n/a', + service_name: 'driver', + }, + { + A: 67.09, + B: 'n/a', + service_name: 'route', + }, + { + A: 30.96, + B: 'n/a', + service_name: 'redis', + }, + { + A: 'n/a', + B: 112.27, + service_name: 'n/a', + }, + ], +}; diff --git a/frontend/src/container/GridTableComponent/__tests__/utils.test.tsx b/frontend/src/container/GridTableComponent/__tests__/utils.test.tsx new file mode 100644 index 0000000000..f0582a51fb --- /dev/null +++ b/frontend/src/container/GridTableComponent/__tests__/utils.test.tsx @@ -0,0 +1,42 @@ +import { Query } from 'types/api/queryBuilder/queryBuilderData'; + +import { createColumnsAndDataSource, getQueryLegend } from '../utils'; +import { + expectedOutputWithLegends, + tableDataMultipleQueriesSuccessResponse, + widgetQueryWithLegend, +} from './response'; + +describe('Table Panel utils', () => { + it('createColumnsAndDataSource function', () => { + const data = tableDataMultipleQueriesSuccessResponse; + const query = widgetQueryWithLegend as Query; + + const { columns, dataSource } = createColumnsAndDataSource(data, query); + + expect(dataSource).toStrictEqual(expectedOutputWithLegends.dataSource); + + // this makes sure that the columns are rendered in the same order as response + expect(columns[0].title).toBe('service_name'); + // the next specifically makes sure that the legends are properly applied in multiple queries + expect(columns[1].title).toBe('p99'); + // this makes sure that the query without a legend takes the title from the query response + expect(columns[2].title).toBe('B'); + + // this is to ensure that the rows properly map to the column data indexes as the dataIndex should be equal to name of the columns + // returned in the response as the rows will be mapped with them + expect((columns[0] as any).dataIndex).toBe('service_name'); + expect((columns[1] as any).dataIndex).toBe('A'); + expect((columns[2] as any).dataIndex).toBe('B'); + }); + + it('getQueryLegend function', () => { + const query = widgetQueryWithLegend as Query; + + // query A has a legend of p99 + expect(getQueryLegend(query, 'A')).toBe('p99'); + + // should return undefined when legend not present + expect(getQueryLegend(query, 'B')).toBe(undefined); + }); +}); diff --git a/frontend/src/container/GridTableComponent/index.tsx b/frontend/src/container/GridTableComponent/index.tsx index 171f09da07..fab4d85e8b 100644 --- a/frontend/src/container/GridTableComponent/index.tsx +++ b/frontend/src/container/GridTableComponent/index.tsx @@ -3,10 +3,7 @@ import { Space, Tooltip } from 'antd'; import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig'; import { Events } from 'constants/events'; import { QueryTable } from 'container/QueryTable'; -import { - createTableColumnsFromQuery, - RowData, -} from 'lib/query/createTableColumnsFromQuery'; +import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { cloneDeep, get, isEmpty, set } from 'lodash-es'; import { memo, ReactNode, useCallback, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -14,7 +11,11 @@ import { eventEmitter } from 'utils/getEventEmitter'; import { WrapperStyled } from './styles'; import { GridTableComponentProps } from './types'; -import { findMatchingThreshold } from './utils'; +import { + createColumnsAndDataSource, + findMatchingThreshold, + TableData, +} from './utils'; function GridTableComponent({ data, @@ -25,28 +26,26 @@ function GridTableComponent({ ...props }: GridTableComponentProps): JSX.Element { const { t } = useTranslation(['valueGraph']); + + // create columns and dataSource in the ui friendly structure + // use the query from the widget here to extract the legend information const { columns, dataSource: originalDataSource } = useMemo( - () => - createTableColumnsFromQuery({ - query, - queryTableData: data, - }), - [data, query], + () => createColumnsAndDataSource((data as unknown) as TableData, query), + [query, data], ); + const createDataInCorrectFormat = useCallback( (dataSource: RowData[]): RowData[] => dataSource.map((d) => { const finalObject = {}; - const keys = Object.keys(d); - keys.forEach((k) => { - const label = get( - columns.find((c) => get(c, 'dataIndex', '') === k) || {}, - 'title', - '', + + // we use the order of the columns here to have similar download as the user view + columns.forEach((k) => { + set( + finalObject, + get(k, 'title', '') as string, + get(d, get(k, 'dataIndex', ''), 'n/a'), ); - if (label) { - set(finalObject, label as string, d[k]); - } }); return finalObject as RowData; }), @@ -65,7 +64,11 @@ function GridTableComponent({ const newValue = { ...val }; Object.keys(val).forEach((k) => { if (columnUnits[k]) { - newValue[k] = getYAxisFormattedValue(String(val[k]), columnUnits[k]); + // the check below takes care of not adding units for rows that have n/a values + newValue[k] = + val[k] !== 'n/a' + ? getYAxisFormattedValue(String(val[k]), columnUnits[k]) + : val[k]; newValue[`${k}_without_unit`] = val[k]; } }); diff --git a/frontend/src/container/GridTableComponent/utils.ts b/frontend/src/container/GridTableComponent/utils.ts index e60cac6a24..089a35fe00 100644 --- a/frontend/src/container/GridTableComponent/utils.ts +++ b/frontend/src/container/GridTableComponent/utils.ts @@ -1,4 +1,11 @@ +import { ColumnsType, ColumnType } from 'antd/es/table'; import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; +import { QUERY_TABLE_CONFIG } from 'container/QueryTable/config'; +import { QueryTableProps } from 'container/QueryTable/QueryTable.intefaces'; +import { RowData } from 'lib/query/createTableColumnsFromQuery'; +import { isEmpty, isNaN } from 'lodash-es'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import { EQueryType } from 'types/common/dashboard'; // Helper function to evaluate the condition based on the operator function evaluateCondition( @@ -56,3 +63,85 @@ export function findMatchingThreshold( hasMultipleMatches, }; } + +export interface TableData { + columns: { name: string; queryName: string; isValueColumn: boolean }[]; + rows: { data: any }[]; +} + +export function getQueryLegend( + currentQuery: Query, + queryName: string, +): string | undefined { + let legend: string | undefined; + switch (currentQuery.queryType) { + case EQueryType.QUERY_BUILDER: + // check if the value is present in the queries + legend = currentQuery.builder.queryData.find( + (query) => query.queryName === queryName, + )?.legend; + + if (!legend) { + // check if the value is present in the formula + legend = currentQuery.builder.queryFormulas.find( + (query) => query.queryName === queryName, + )?.legend; + } + break; + case EQueryType.CLICKHOUSE: + legend = currentQuery.clickhouse_sql.find( + (query) => query.name === queryName, + )?.legend; + break; + case EQueryType.PROM: + legend = currentQuery.promql.find((query) => query.name === queryName) + ?.legend; + break; + default: + legend = undefined; + break; + } + + return legend; +} + +export function createColumnsAndDataSource( + data: TableData, + currentQuery: Query, + renderColumnCell?: QueryTableProps['renderColumnCell'], +): { columns: ColumnsType; dataSource: RowData[] } { + const columns: ColumnsType = + data.columns?.reduce>((acc, item) => { + // is the column is the value column then we need to check for the available legend + const legend = item.isValueColumn + ? getQueryLegend(currentQuery, item.queryName) + : undefined; + + const column: ColumnType = { + dataIndex: item.name, + // if no legend present then rely on the column name value + title: !isEmpty(legend) ? legend : item.name, + width: QUERY_TABLE_CONFIG.width, + render: renderColumnCell && renderColumnCell[item.name], + sorter: (a: RowData, b: RowData): number => { + const valueA = Number(a[`${item.name}_without_unit`] ?? a[item.name]); + const valueB = Number(b[`${item.name}_without_unit`] ?? b[item.name]); + + if (!isNaN(valueA) && !isNaN(valueB)) { + return valueA - valueB; + } + + return ((a[item.name] as string) || '').localeCompare( + (b[item.name] as string) || '', + ); + }, + }; + + return [...acc, column]; + }, []) || []; + + // the rows returned have data encapsulation hence removing the same here + const dataSource = data.rows?.map((d) => d.data) || []; + + return { columns, dataSource }; +} diff --git a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx index d0b69fcd8d..14a2419915 100644 --- a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx @@ -2,8 +2,6 @@ import { Card, Typography } from 'antd'; import Spinner from 'components/Spinner'; import { PANEL_TYPES } from 'constants/queryBuilder'; import { WidgetGraphContainerProps } from 'container/NewWidget/types'; -// import useUrlQuery from 'hooks/useUrlQuery'; -// import { useDashboard } from 'providers/Dashboard/Dashboard'; import { getSortedSeriesData } from 'utils/getSortedSeriesData'; import { NotFoundContainer } from './styles'; @@ -59,6 +57,14 @@ function WidgetGraphContainer({ ); } + if (queryResponse.isIdle) { + return ( + + No Data + + ); + } + return ( ; params?: Record; fillGaps?: boolean; + formatForWeb?: boolean; tableParams?: { pagination?: Pagination; selectColumns?: any; diff --git a/frontend/src/lib/dashboard/prepareQueryRangePayload.ts b/frontend/src/lib/dashboard/prepareQueryRangePayload.ts index 244e096079..181a83914b 100644 --- a/frontend/src/lib/dashboard/prepareQueryRangePayload.ts +++ b/frontend/src/lib/dashboard/prepareQueryRangePayload.ts @@ -16,6 +16,7 @@ export const prepareQueryRangePayload = ({ query, globalSelectedInterval, graphType, + formatForWeb, selectedTime, tableParams, variables = {}, @@ -102,6 +103,7 @@ export const prepareQueryRangePayload = ({ inputFormat: 'ns', }), variables, + formatForWeb, compositeQuery, ...restParams, }; diff --git a/frontend/src/types/api/metrics/getQueryRange.ts b/frontend/src/types/api/metrics/getQueryRange.ts index 5409eba346..47c8fcba4a 100644 --- a/frontend/src/types/api/metrics/getQueryRange.ts +++ b/frontend/src/types/api/metrics/getQueryRange.ts @@ -24,6 +24,7 @@ export type QueryRangePayload = { start: number; step: number; variables?: Record; + formatForWeb?: boolean; [param: string]: unknown; }; export interface MetricRangePayloadProps { From fd603b8fdf71ad124141c6010480286a79c04f8b Mon Sep 17 00:00:00 2001 From: Raj Kamal Singh <1133322+raj-k-singh@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:31:21 +0530 Subject: [PATCH 058/281] Fix: pipeline alias collisions shouldnt lead to duplicate log processors (#5372) * chore: add test validating pipeline alias collisions dont lead to bad config recommendations * chore: emit error log on detecting duplicate processors in generated config * chore: ensure collector config processor names for pipelines are unique * chore: minor cleanups --- .../logparsingpipeline/collector_config.go | 5 ++ .../collector_config_test.go | 89 +++++++++++++++++++ .../app/logparsingpipeline/pipelineBuilder.go | 8 +- 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/pkg/query-service/app/logparsingpipeline/collector_config.go b/pkg/query-service/app/logparsingpipeline/collector_config.go index 17b8d96c1e..49f697fbd3 100644 --- a/pkg/query-service/app/logparsingpipeline/collector_config.go +++ b/pkg/query-service/app/logparsingpipeline/collector_config.go @@ -142,6 +142,11 @@ func checkDuplicateString(pipeline []string) bool { for _, processor := range pipeline { name := processor if _, ok := exists[name]; ok { + zap.L().Error( + "duplicate processor name detected in generated collector config for log pipelines", + zap.String("processor", processor), + zap.Any("pipeline", pipeline), + ) return true } diff --git a/pkg/query-service/app/logparsingpipeline/collector_config_test.go b/pkg/query-service/app/logparsingpipeline/collector_config_test.go index 8ef79875d5..f5ba7a352b 100644 --- a/pkg/query-service/app/logparsingpipeline/collector_config_test.go +++ b/pkg/query-service/app/logparsingpipeline/collector_config_test.go @@ -5,7 +5,10 @@ import ( "testing" . "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/require" "go.signoz.io/signoz/pkg/query-service/constants" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "gopkg.in/yaml.v3" ) var buildProcessorTestData = []struct { @@ -204,3 +207,89 @@ func TestBuildLogsPipeline(t *testing.T) { }) } } + +func TestPipelineAliasCollisionsDontResultInDuplicateCollectorProcessors(t *testing.T) { + require := require.New(t) + + baseConf := []byte(` + receivers: + memory: + id: in-memory-receiver + exporters: + memory: + id: in-memory-exporter + service: + pipelines: + logs: + receivers: + - memory + processors: [] + exporters: + - memory + `) + + makeTestPipeline := func(name string, alias string) Pipeline { + return Pipeline{ + OrderId: 1, + Name: name, + Alias: alias, + Enabled: true, + Filter: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{ + Key: "method", + DataType: v3.AttributeKeyDataTypeString, + Type: v3.AttributeKeyTypeTag, + }, + Operator: "=", + Value: "GET", + }, + }, + }, + Config: []PipelineOperator{ + { + ID: "regex", + Type: "regex_parser", + Enabled: true, + Name: "regex parser", + ParseFrom: "attributes.test_regex_target", + ParseTo: "attributes", + Regex: `^\s*(?P{.*})\s*$`, + }, + }, + } + } + + testPipelines := []Pipeline{ + makeTestPipeline("test pipeline 1", "pipeline-alias"), + makeTestPipeline("test pipeline 2", "pipeline-alias"), + } + + recommendedConfYaml, apiErr := GenerateCollectorConfigWithPipelines( + baseConf, testPipelines, + ) + require.Nil(apiErr, fmt.Sprintf("couldn't generate config recommendation: %v", apiErr)) + + var recommendedConf map[string]interface{} + err := yaml.Unmarshal(recommendedConfYaml, &recommendedConf) + require.Nil(err, "couldn't unmarshal recommended config") + + logsProcessors := recommendedConf["service"].(map[string]any)["pipelines"].(map[string]any)["logs"].(map[string]any)["processors"].([]any) + + require.Equal( + len(logsProcessors), len(testPipelines), + "test pipelines not included in recommended config as expected", + ) + + recommendedConfYaml2, apiErr := GenerateCollectorConfigWithPipelines( + baseConf, testPipelines, + ) + require.Nil(apiErr, fmt.Sprintf("couldn't generate config recommendation again: %v", apiErr)) + require.Equal( + string(recommendedConfYaml), string(recommendedConfYaml2), + "collector config should not change across recommendations for same set of pipelines", + ) + +} diff --git a/pkg/query-service/app/logparsingpipeline/pipelineBuilder.go b/pkg/query-service/app/logparsingpipeline/pipelineBuilder.go index e569d88a82..5dd6406a79 100644 --- a/pkg/query-service/app/logparsingpipeline/pipelineBuilder.go +++ b/pkg/query-service/app/logparsingpipeline/pipelineBuilder.go @@ -24,7 +24,7 @@ func CollectorConfProcessorName(p Pipeline) string { func PreparePipelineProcessor(pipelines []Pipeline) (map[string]interface{}, []string, error) { processors := map[string]interface{}{} names := []string{} - for _, v := range pipelines { + for pipelineIdx, v := range pipelines { if !v.Enabled { continue } @@ -70,6 +70,12 @@ func PreparePipelineProcessor(pipelines []Pipeline) (map[string]interface{}, []s Operators: v.Config, } name := CollectorConfProcessorName(v) + + // Ensure name is unique + if _, nameExists := processors[name]; nameExists { + name = fmt.Sprintf("%s-%d", name, pipelineIdx) + } + processors[name] = processor names = append(names, name) } From b34509215e1a45b64e672f0e39838c8856bd8f6d Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Fri, 28 Jun 2024 12:10:57 +0530 Subject: [PATCH 059/281] fix: pie chart panels not rendering (#5376) * fix: pie chart panels not rendering * fix: restructure code --- .../container/GridCardLayout/GridCard/FullView/index.tsx | 2 +- frontend/src/container/GridCardLayout/GridCard/index.tsx | 2 +- frontend/src/container/NewWidget/LeftContainer/index.tsx | 6 +++--- frontend/src/utils/getGraphType.ts | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx index 83ba3b7d4b..7511a4d445 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx @@ -81,7 +81,7 @@ function FullView({ globalSelectedInterval: globalSelectedTime, variables: getDashboardVariables(selectedDashboard?.data.variables), fillGaps: widget.fillSpans, - formatForWeb: getGraphType(widget.panelTypes) === PANEL_TYPES.TABLE, + formatForWeb: widget.panelTypes === PANEL_TYPES.TABLE, }; } updatedQuery.builder.queryData[0].pageSize = 10; diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 2daac8c9b4..976f58b8f0 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -109,7 +109,7 @@ function GridCardGraph({ globalSelectedInterval, variables: getDashboardVariables(variables), fillGaps: widget.fillSpans, - formatForWeb: getGraphType(widget.panelTypes) === PANEL_TYPES.TABLE, + formatForWeb: widget.panelTypes === PANEL_TYPES.TABLE, }; } updatedQuery.builder.queryData[0].pageSize = 10; diff --git a/frontend/src/container/NewWidget/LeftContainer/index.tsx b/frontend/src/container/NewWidget/LeftContainer/index.tsx index 658fb564b4..80e893759e 100644 --- a/frontend/src/container/NewWidget/LeftContainer/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/index.tsx @@ -12,7 +12,7 @@ import { memo, useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { GlobalReducer } from 'types/reducer/globalTime'; -import { getGraphType } from 'utils/getGraphType'; +import { getGraphType, getGraphTypeForFormat } from 'utils/getGraphType'; import { WidgetGraphProps } from '../types'; import ExplorerColumnsRenderer from './ExplorerColumnsRenderer'; @@ -45,7 +45,7 @@ function LeftContainer({ query: stagedQuery || initialQueriesMap.metrics, globalSelectedInterval, formatForWeb: - getGraphType(selectedGraph || selectedWidget.panelTypes) === + getGraphTypeForFormat(selectedGraph || selectedWidget.panelTypes) === PANEL_TYPES.TABLE, variables: getDashboardVariables(selectedDashboard?.data.variables), }; @@ -77,7 +77,7 @@ function LeftContainer({ query: stagedQuery, fillGaps: selectedWidget.fillSpans || false, formatForWeb: - getGraphType(selectedGraph || selectedWidget.panelTypes) === + getGraphTypeForFormat(selectedGraph || selectedWidget.panelTypes) === PANEL_TYPES.TABLE, })); } diff --git a/frontend/src/utils/getGraphType.ts b/frontend/src/utils/getGraphType.ts index 9c3ded97e8..cbb6e6c216 100644 --- a/frontend/src/utils/getGraphType.ts +++ b/frontend/src/utils/getGraphType.ts @@ -10,3 +10,6 @@ export const getGraphType = (panelType: PANEL_TYPES): PANEL_TYPES => { } return panelType; }; + +export const getGraphTypeForFormat = (panelType: PANEL_TYPES): PANEL_TYPES => + panelType; From 02106277a62879a05df8a7f855544571fd7e0cac Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Fri, 28 Jun 2024 13:53:35 +0530 Subject: [PATCH 060/281] fix: restructure code to handle loading state for panel type change (#5378) * fix: restructure code to handle loading state for panel type change * fix: add inline comments --- .../WidgetGraph/WidgetGraphContainer.tsx | 5 ++ .../LeftContainer/WidgetGraph/index.tsx | 2 + .../NewWidget/LeftContainer/index.tsx | 67 ++--------------- frontend/src/container/NewWidget/index.tsx | 73 ++++++++++++++++++- frontend/src/container/NewWidget/types.ts | 4 + 5 files changed, 90 insertions(+), 61 deletions(-) diff --git a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx index 14a2419915..715169b10a 100644 --- a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphContainer.tsx @@ -12,6 +12,7 @@ function WidgetGraphContainer({ queryResponse, setRequestData, selectedWidget, + isLoadingPanelData, }: WidgetGraphContainerProps): JSX.Element { if (queryResponse.data && selectedGraph === PANEL_TYPES.BAR) { const sortedSeriesData = getSortedSeriesData( @@ -36,6 +37,10 @@ function WidgetGraphContainer({ return ; } + if (isLoadingPanelData) { + return ; + } + if ( selectedGraph !== PANEL_TYPES.LIST && queryResponse.data?.payload.data?.result?.length === 0 diff --git a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/index.tsx b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/index.tsx index 73ec06856a..8ff3fe03f7 100644 --- a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/index.tsx @@ -17,6 +17,7 @@ function WidgetGraph({ queryResponse, setRequestData, selectedWidget, + isLoadingPanelData, }: WidgetGraphContainerProps): JSX.Element { const { currentQuery } = useQueryBuilder(); @@ -43,6 +44,7 @@ function WidgetGraph({ )} ((state) => state.globalTime); - - const [requestData, setRequestData] = useState(() => { - if (selectedWidget && selectedGraph !== PANEL_TYPES.LIST) { - return { - selectedTime: selectedWidget?.timePreferance, - graphType: getGraphType(selectedGraph || selectedWidget.panelTypes), - query: stagedQuery || initialQueriesMap.metrics, - globalSelectedInterval, - formatForWeb: - getGraphTypeForFormat(selectedGraph || selectedWidget.panelTypes) === - PANEL_TYPES.TABLE, - variables: getDashboardVariables(selectedDashboard?.data.variables), - }; - } - const updatedQuery = { ...(stagedQuery || initialQueriesMap.metrics) }; - updatedQuery.builder.queryData[0].pageSize = 10; - redirectWithQueryBuilderData(updatedQuery); - return { - query: updatedQuery, - graphType: PANEL_TYPES.LIST, - selectedTime: selectedTime.enum || 'GLOBAL_TIME', - globalSelectedInterval, - tableParams: { - pagination: { - offset: 0, - limit: updatedQuery.builder.queryData[0].limit || 0, - }, - }, - }; - }); - - useEffect(() => { - if (stagedQuery) { - setRequestData((prev) => ({ - ...prev, - selectedTime: selectedTime.enum || prev.selectedTime, - globalSelectedInterval, - graphType: getGraphType(selectedGraph || selectedWidget.panelTypes), - query: stagedQuery, - fillGaps: selectedWidget.fillSpans || false, - formatForWeb: - getGraphTypeForFormat(selectedGraph || selectedWidget.panelTypes) === - PANEL_TYPES.TABLE, - })); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - stagedQuery, - selectedTime, - selectedWidget.fillSpans, - globalSelectedInterval, - ]); - const queryResponse = useGetQueryRange( requestData, selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION, @@ -110,6 +56,7 @@ function LeftContainer({ queryResponse={queryResponse} setRequestData={setRequestData} selectedWidget={selectedWidget} + isLoadingPanelData={isLoadingPanelData} /> diff --git a/frontend/src/container/NewWidget/index.tsx b/frontend/src/container/NewWidget/index.tsx index 79c9cb9028..fc0c5d06cd 100644 --- a/frontend/src/container/NewWidget/index.tsx +++ b/frontend/src/container/NewWidget/index.tsx @@ -7,7 +7,7 @@ import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn'; import { chartHelpMessage } from 'components/facingIssueBtn/util'; import { FeatureKeys } from 'constants/features'; import { QueryParams } from 'constants/query'; -import { PANEL_TYPES } from 'constants/queryBuilder'; +import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; import { DashboardShortcuts } from 'constants/shortcuts/DashboardShortcuts'; import { DEFAULT_BUCKET_COUNT } from 'container/PanelWrapper/constants'; @@ -18,6 +18,8 @@ import useAxiosError from 'hooks/useAxiosError'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { MESSAGE, useIsFeatureDisabled } from 'hooks/useFeatureFlag'; import useUrlQuery from 'hooks/useUrlQuery'; +import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; +import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults'; import history from 'lib/history'; import { defaultTo, isUndefined } from 'lodash-es'; import { Check, X } from 'lucide-react'; @@ -38,6 +40,8 @@ import { IField } from 'types/api/logs/fields'; import { EQueryType } from 'types/common/dashboard'; import { DataSource } from 'types/common/queryBuilder'; import AppReducer from 'types/reducer/app'; +import { GlobalReducer } from 'types/reducer/globalTime'; +import { getGraphType, getGraphTypeForFormat } from 'utils/getGraphType'; import LeftContainer from './LeftContainer'; import QueryTypeTag from './LeftContainer/QueryTypeTag'; @@ -83,6 +87,10 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { const { featureResponse } = useSelector( (state) => state.app, ); + const { selectedTime: globalSelectedInterval } = useSelector< + AppState, + GlobalReducer + >((state) => state.globalTime); const { widgets = [] } = selectedDashboard?.data || {}; @@ -278,6 +286,65 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { const handleError = useAxiosError(); + // this loading state is to take care of mismatch in the responses for table and other panels + // hence while changing the query contains the older value and the processing logic fails + const [isLoadingPanelData, setIsLoadingPanelData] = useState(false); + + // request data should be handled by the parent and the child components should consume the same + // this has been moved here from the left container + const [requestData, setRequestData] = useState(() => { + if (selectedWidget && selectedGraph !== PANEL_TYPES.LIST) { + return { + selectedTime: selectedWidget?.timePreferance, + graphType: getGraphType(selectedGraph || selectedWidget.panelTypes), + query: stagedQuery || initialQueriesMap.metrics, + globalSelectedInterval, + formatForWeb: + getGraphTypeForFormat(selectedGraph || selectedWidget.panelTypes) === + PANEL_TYPES.TABLE, + variables: getDashboardVariables(selectedDashboard?.data.variables), + }; + } + const updatedQuery = { ...(stagedQuery || initialQueriesMap.metrics) }; + updatedQuery.builder.queryData[0].pageSize = 10; + redirectWithQueryBuilderData(updatedQuery); + return { + query: updatedQuery, + graphType: PANEL_TYPES.LIST, + selectedTime: selectedTime.enum || 'GLOBAL_TIME', + globalSelectedInterval, + tableParams: { + pagination: { + offset: 0, + limit: updatedQuery.builder.queryData[0].limit || 0, + }, + }, + }; + }); + + useEffect(() => { + if (stagedQuery) { + setIsLoadingPanelData(false); + setRequestData((prev) => ({ + ...prev, + selectedTime: selectedTime.enum || prev.selectedTime, + globalSelectedInterval, + graphType: getGraphType(selectedGraph || selectedWidget.panelTypes), + query: stagedQuery, + fillGaps: selectedWidget.fillSpans || false, + formatForWeb: + getGraphTypeForFormat(selectedGraph || selectedWidget.panelTypes) === + PANEL_TYPES.TABLE, + })); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + stagedQuery, + selectedTime, + selectedWidget.fillSpans, + globalSelectedInterval, + ]); + const onClickSaveHandler = useCallback(() => { if (!selectedDashboard) { return; @@ -402,6 +469,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { }, [dashboardId]); const setGraphHandler = (type: PANEL_TYPES): void => { + setIsLoadingPanelData(true); const updatedQuery = handleQueryChange(type as any, supersetQuery); setGraphType(type); redirectWithQueryBuilderData( @@ -527,6 +595,9 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { setSelectedTracesFields={setSelectedTracesFields} selectedWidget={selectedWidget} selectedTime={selectedTime} + requestData={requestData} + setRequestData={setRequestData} + isLoadingPanelData={isLoadingPanelData} /> )} diff --git a/frontend/src/container/NewWidget/types.ts b/frontend/src/container/NewWidget/types.ts index b6a86ad483..c3952e935a 100644 --- a/frontend/src/container/NewWidget/types.ts +++ b/frontend/src/container/NewWidget/types.ts @@ -24,6 +24,9 @@ export interface WidgetGraphProps { selectedWidget: Widgets; selectedGraph: PANEL_TYPES; selectedTime: timePreferance; + requestData: GetQueryResultsProps; + setRequestData: Dispatch>; + isLoadingPanelData: boolean; } export type WidgetGraphContainerProps = { @@ -34,4 +37,5 @@ export type WidgetGraphContainerProps = { setRequestData: Dispatch>; selectedGraph: PANEL_TYPES; selectedWidget: Widgets; + isLoadingPanelData: boolean; }; From b0b69c83dbfa4758cb893f95dfc345308241b011 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 1 Jul 2024 14:06:28 +0530 Subject: [PATCH 061/281] fix: use fill gaps only for time series panel types (#5387) --- pkg/query-service/postprocess/gaps.go | 3 ++ pkg/query-service/postprocess/gaps_test.go | 36 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/pkg/query-service/postprocess/gaps.go b/pkg/query-service/postprocess/gaps.go index 170d77844d..0970fecb9f 100644 --- a/pkg/query-service/postprocess/gaps.go +++ b/pkg/query-service/postprocess/gaps.go @@ -46,6 +46,9 @@ func fillGap(series *v3.Series, start, end, step int64) *v3.Series { // TODO(srikanthccv): can WITH FILL be perfect substitute for all cases https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier func FillGaps(results []*v3.Result, params *v3.QueryRangeParamsV3) { + if params.CompositeQuery.PanelType != v3.PanelTypeGraph { + return + } for _, result := range results { // A `result` item in `results` contains the query result for individual query. // If there are no series in the result, we add empty series and `fillGap` adds all zeros diff --git a/pkg/query-service/postprocess/gaps_test.go b/pkg/query-service/postprocess/gaps_test.go index 999176a830..9e42fa240b 100644 --- a/pkg/query-service/postprocess/gaps_test.go +++ b/pkg/query-service/postprocess/gaps_test.go @@ -43,6 +43,7 @@ func TestFillGaps(t *testing.T) { Start: 1000, End: 5000, CompositeQuery: &v3.CompositeQuery{ + PanelType: v3.PanelTypeGraph, BuilderQueries: map[string]*v3.BuilderQuery{ "query1": { QueryName: "query1", @@ -82,6 +83,7 @@ func TestFillGaps(t *testing.T) { Start: 1000, End: 5000, CompositeQuery: &v3.CompositeQuery{ + PanelType: v3.PanelTypeGraph, BuilderQueries: map[string]*v3.BuilderQuery{ "query1": { QueryName: "query1", @@ -121,6 +123,7 @@ func TestFillGaps(t *testing.T) { Start: 1000, End: 5000, CompositeQuery: &v3.CompositeQuery{ + PanelType: v3.PanelTypeGraph, BuilderQueries: map[string]*v3.BuilderQuery{ "query1": { QueryName: "query1", @@ -142,6 +145,39 @@ func TestFillGaps(t *testing.T) { }), }, }, + { + name: "Single series with gaps and panel type is not graph", + results: []*v3.Result{ + createResult("query1", []*v3.Series{ + createSeries([]v3.Point{ + {Timestamp: 1000, Value: 1.0}, + {Timestamp: 3000, Value: 3.0}, + }), + }), + }, + params: &v3.QueryRangeParamsV3{ + Start: 1000, + End: 5000, + CompositeQuery: &v3.CompositeQuery{ + PanelType: v3.PanelTypeList, + BuilderQueries: map[string]*v3.BuilderQuery{ + "query1": { + QueryName: "query1", + Expression: "query1", + StepInterval: 1, + }, + }, + }, + }, + expected: []*v3.Result{ + createResult("query1", []*v3.Series{ + createSeries([]v3.Point{ + {Timestamp: 1000, Value: 1.0}, + {Timestamp: 3000, Value: 3.0}, + }), + }), + }, + }, } // Execute test cases From 326dec21fdbd184b60ddc10f2ec619bbe279fbf5 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 1 Jul 2024 18:34:02 +0530 Subject: [PATCH 062/281] fix: use the correct formatter for the description (#5388) --- pkg/query-service/model/v3/v3.go | 7 +++++-- pkg/query-service/rules/promRule.go | 14 +++++++++++--- pkg/query-service/rules/thresholdRule.go | 14 +++++++++++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index 7b9fd8d989..7facd2ff50 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -401,8 +401,11 @@ type CompositeQuery struct { PromQueries map[string]*PromQuery `json:"promQueries,omitempty"` PanelType PanelType `json:"panelType"` QueryType QueryType `json:"queryType"` - Unit string `json:"unit,omitempty"` - FillGaps bool `json:"fillGaps,omitempty"` + // Unit for the time series data shown in the graph + // This is used in alerts to format the value and threshold + Unit string `json:"unit,omitempty"` + // FillGaps is used to fill the gaps in the time series data + FillGaps bool `json:"fillGaps,omitempty"` } func (c *CompositeQuery) EnabledQueries() int { diff --git a/pkg/query-service/rules/promRule.go b/pkg/query-service/rules/promRule.go index a998de243e..fd00363abf 100644 --- a/pkg/query-service/rules/promRule.go +++ b/pkg/query-service/rules/promRule.go @@ -111,13 +111,22 @@ func (r *PromRule) Condition() *RuleCondition { return r.ruleCondition } +// targetVal returns the target value for the rule condition +// when the y-axis and target units are non-empty, it +// converts the target value to the y-axis unit func (r *PromRule) targetVal() float64 { if r.ruleCondition == nil || r.ruleCondition.Target == nil { return 0 } + // get the converter for the target unit unitConverter := converter.FromUnit(converter.Unit(r.ruleCondition.TargetUnit)) - value := unitConverter.Convert(converter.Value{F: *r.ruleCondition.Target, U: converter.Unit(r.ruleCondition.TargetUnit)}, converter.Unit(r.Unit())) + // convert the target value to the y-axis unit + value := unitConverter.Convert(converter.Value{ + F: *r.ruleCondition.Target, + U: converter.Unit(r.ruleCondition.TargetUnit), + }, converter.Unit(r.Unit())) + return value.F } @@ -370,8 +379,7 @@ func (r *PromRule) Eval(ctx context.Context, ts time.Time, queriers *Queriers) ( } zap.L().Debug("alerting for series", zap.String("name", r.Name()), zap.Any("series", series)) - thresholdFormatter := formatter.FromUnit(r.ruleCondition.TargetUnit) - threshold := thresholdFormatter.Format(r.targetVal(), r.ruleCondition.TargetUnit) + threshold := valueFormatter.Format(r.targetVal(), r.Unit()) tmplData := AlertTemplateData(l, valueFormatter.Format(alertSmpl.F, r.Unit()), threshold) // Inject some convenience variables that are easier to remember for users diff --git a/pkg/query-service/rules/thresholdRule.go b/pkg/query-service/rules/thresholdRule.go index 051476e6b8..e642fb3a0a 100644 --- a/pkg/query-service/rules/thresholdRule.go +++ b/pkg/query-service/rules/thresholdRule.go @@ -165,13 +165,22 @@ func (r *ThresholdRule) PreferredChannels() []string { return r.preferredChannels } +// targetVal returns the target value for the rule condition +// when the y-axis and target units are non-empty, it +// converts the target value to the y-axis unit func (r *ThresholdRule) targetVal() float64 { if r.ruleCondition == nil || r.ruleCondition.Target == nil { return 0 } + // get the converter for the target unit unitConverter := converter.FromUnit(converter.Unit(r.ruleCondition.TargetUnit)) - value := unitConverter.Convert(converter.Value{F: *r.ruleCondition.Target, U: converter.Unit(r.ruleCondition.TargetUnit)}, converter.Unit(r.Unit())) + // convert the target value to the y-axis unit + value := unitConverter.Convert(converter.Value{ + F: *r.ruleCondition.Target, + U: converter.Unit(r.ruleCondition.TargetUnit), + }, converter.Unit(r.Unit())) + return value.F } @@ -874,8 +883,7 @@ func (r *ThresholdRule) Eval(ctx context.Context, ts time.Time, queriers *Querie } value := valueFormatter.Format(smpl.V, r.Unit()) - thresholdFormatter := formatter.FromUnit(r.ruleCondition.TargetUnit) - threshold := thresholdFormatter.Format(r.targetVal(), r.ruleCondition.TargetUnit) + threshold := valueFormatter.Format(r.targetVal(), r.Unit()) zap.L().Debug("Alert template data for rule", zap.String("name", r.Name()), zap.String("formatter", valueFormatter.Name()), zap.String("value", value), zap.String("threshold", threshold)) tmplData := AlertTemplateData(l, value, threshold) From 6e466df89d676ceabb84f8693ea31885d53ab3b8 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Mon, 1 Jul 2024 21:11:31 +0530 Subject: [PATCH 063/281] chore: update posthog-js (#5382) --- frontend/package.json | 2 +- frontend/yarn.lock | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index d78064278a..2b2e803478 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -88,7 +88,7 @@ "lucide-react": "0.379.0", "mini-css-extract-plugin": "2.4.5", "papaparse": "5.4.1", - "posthog-js": "1.140.1", + "posthog-js": "1.142.1", "rc-tween-one": "3.0.6", "react": "18.2.0", "react-addons-update": "15.6.3", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 295ae66012..6120da5e98 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -13705,13 +13705,14 @@ postcss@8.4.38, postcss@^8.0.0, postcss@^8.1.1, postcss@^8.3.7, postcss@^8.4.21, picocolors "^1.0.0" source-map-js "^1.2.0" -posthog-js@1.140.1: - version "1.140.1" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.140.1.tgz#34efc0d326fa5fcf7950106f350fb4f0e73b2da6" - integrity sha512-UeKuAtQSvbzmTCzNVaauku8F194EYwAP33WrRrWZlDlMNbMy7GKcZOgKbr7jZqnha7FlVlHrWk+Rpyr1zCFhPQ== +posthog-js@1.142.1: + version "1.142.1" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.142.1.tgz#3b91229732938c5c76b5ee6d410698a267e073e9" + integrity sha512-yqeWTWitlb0sCaH5v6s7UJ+pPspzf/lkzPaSE5pMMXRM2i2KNsMoZEAZqbPCW8fQ8QL6lHs6d8PLjHrvbR288w== dependencies: fflate "^0.4.8" preact "^10.19.3" + web-vitals "^4.0.1" preact@^10.19.3: version "10.22.0" @@ -17218,6 +17219,11 @@ web-vitals@^0.2.4: resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz" integrity sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg== +web-vitals@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.0.tgz#008949ab79717a68ccaaa3c4371cbc7bbbd78a92" + integrity sha512-ohj72kbtVWCpKYMxcbJ+xaOBV3En76hW47j52dG+tEGG36LZQgfFw5yHl9xyjmosy3XUMn8d/GBUAy4YPM839w== + web-worker@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz" From 932b7ddc694bc731f47809e15e83a2ab65554f9c Mon Sep 17 00:00:00 2001 From: Nityananda Gohain Date: Tue, 2 Jul 2024 11:53:30 +0530 Subject: [PATCH 064/281] fix: orderby validation and correction in logs old QB (#5399) --- pkg/query-service/app/logs/parser.go | 4 +- pkg/query-service/app/logs/parser_test.go | 50 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/pkg/query-service/app/logs/parser.go b/pkg/query-service/app/logs/parser.go index 06ea6d2271..855a023528 100644 --- a/pkg/query-service/app/logs/parser.go +++ b/pkg/query-service/app/logs/parser.go @@ -58,7 +58,9 @@ func ParseLogFilterParams(r *http.Request) (*model.LogsFilterParams, error) { res.OrderBy = val[0] } if val, ok := params[ORDER]; ok { - res.Order = val[0] + if val[0] == ASC || val[0] == DESC { + res.Order = val[0] + } } if val, ok := params["q"]; ok { res.Query = val[0] diff --git a/pkg/query-service/app/logs/parser_test.go b/pkg/query-service/app/logs/parser_test.go index bb7dde6296..6397738437 100644 --- a/pkg/query-service/app/logs/parser_test.go +++ b/pkg/query-service/app/logs/parser_test.go @@ -1,6 +1,8 @@ package logs import ( + "net/http" + "net/http/httptest" "testing" . "github.com/smartystreets/goconvey/convey" @@ -432,3 +434,51 @@ func TestGenerateSQLQuery(t *testing.T) { }) } } + +var parseLogFilterParams = []struct { + Name string + ReqParams string + ExpectedLogFilterParams *model.LogsFilterParams +}{ + { + Name: "test with proper order by", + ReqParams: "order=desc&q=service.name='myservice'&limit=10", + ExpectedLogFilterParams: &model.LogsFilterParams{ + Limit: 10, + OrderBy: "timestamp", + Order: DESC, + Query: "service.name='myservice'", + }, + }, + { + Name: "test with proper order by asc", + ReqParams: "order=asc&q=service.name='myservice'&limit=10", + ExpectedLogFilterParams: &model.LogsFilterParams{ + Limit: 10, + OrderBy: "timestamp", + Order: ASC, + Query: "service.name='myservice'", + }, + }, + { + Name: "test with incorrect order by", + ReqParams: "order=undefined&q=service.name='myservice'&limit=10", + ExpectedLogFilterParams: &model.LogsFilterParams{ + Limit: 10, + OrderBy: "timestamp", + Order: DESC, + Query: "service.name='myservice'", + }, + }, +} + +func TestParseLogFilterParams(t *testing.T) { + for _, test := range parseLogFilterParams { + Convey(test.Name, t, func() { + req := httptest.NewRequest(http.MethodGet, "/logs?"+test.ReqParams, nil) + params, err := ParseLogFilterParams(req) + So(err, ShouldBeNil) + So(params, ShouldEqual, test.ExpectedLogFilterParams) + }) + } +} From 3ee51770fd02ba1342d5e4681fd5a551b35edbf7 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 2 Jul 2024 12:03:01 +0530 Subject: [PATCH 065/281] chore: remove rules dependency in CH reader (#5396) --- .../app/clickhouseReader/reader.go | 31 ---------------- pkg/query-service/interfaces/interface.go | 1 - pkg/query-service/rules/db.go | 35 +++++++++++++++++++ pkg/query-service/rules/manager.go | 3 ++ pkg/query-service/telemetry/telemetry.go | 8 ++++- 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 4e5f342301..ae8fb64c94 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -53,7 +53,6 @@ import ( "go.signoz.io/signoz/pkg/query-service/interfaces" "go.signoz.io/signoz/pkg/query-service/model" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" - "go.signoz.io/signoz/pkg/query-service/rules" "go.signoz.io/signoz/pkg/query-service/telemetry" "go.signoz.io/signoz/pkg/query-service/utils" ) @@ -3420,36 +3419,6 @@ func countPanelsInDashboard(data map[string]interface{}) model.DashboardsInfo { } } -func (r *ClickHouseReader) GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) { - alertsInfo := model.AlertsInfo{} - // fetch alerts from rules db - query := "SELECT data FROM rules" - var alertsData []string - err := r.localDB.Select(&alertsData, query) - if err != nil { - zap.L().Error("Error in processing sql query", zap.Error(err)) - return &alertsInfo, err - } - for _, alert := range alertsData { - var rule rules.GettableRule - err = json.Unmarshal([]byte(alert), &rule) - if err != nil { - zap.L().Error("invalid rule data", zap.Error(err)) - continue - } - if rule.AlertType == "LOGS_BASED_ALERT" { - alertsInfo.LogsBasedAlerts = alertsInfo.LogsBasedAlerts + 1 - } else if rule.AlertType == "METRIC_BASED_ALERT" { - alertsInfo.MetricBasedAlerts = alertsInfo.MetricBasedAlerts + 1 - } else if rule.AlertType == "TRACES_BASED_ALERT" { - alertsInfo.TracesBasedAlerts = alertsInfo.TracesBasedAlerts + 1 - } - alertsInfo.TotalAlerts = alertsInfo.TotalAlerts + 1 - } - - return &alertsInfo, nil -} - func (r *ClickHouseReader) GetSavedViewsInfo(ctx context.Context) (*model.SavedViewsInfo, error) { savedViewsInfo := model.SavedViewsInfo{} savedViews, err := explorer.GetViews() diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index 9a5e33e5ff..0ab20fed0e 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -73,7 +73,6 @@ type Reader interface { LiveTailLogsV3(ctx context.Context, query string, timestampStart uint64, idStart string, client *v3.LogsLiveTailClient) GetDashboardsInfo(ctx context.Context) (*model.DashboardsInfo, error) - GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) GetSavedViewsInfo(ctx context.Context) (*model.SavedViewsInfo, error) GetTotalSpans(ctx context.Context) (uint64, error) GetTotalLogs(ctx context.Context) (uint64, error) diff --git a/pkg/query-service/rules/db.go b/pkg/query-service/rules/db.go index abf584a375..4d51d462dd 100644 --- a/pkg/query-service/rules/db.go +++ b/pkg/query-service/rules/db.go @@ -2,6 +2,7 @@ package rules import ( "context" + "encoding/json" "fmt" "strconv" "time" @@ -9,6 +10,7 @@ import ( "github.com/jmoiron/sqlx" "go.signoz.io/signoz/pkg/query-service/auth" "go.signoz.io/signoz/pkg/query-service/common" + "go.signoz.io/signoz/pkg/query-service/model" "go.uber.org/zap" ) @@ -43,6 +45,9 @@ type RuleDB interface { // GetAllPlannedMaintenance fetches the maintenance definitions from db GetAllPlannedMaintenance(ctx context.Context) ([]PlannedMaintenance, error) + + // used for internal telemetry + GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) } type StoredRule struct { @@ -295,3 +300,33 @@ func (r *ruleDB) EditPlannedMaintenance(ctx context.Context, maintenance Planned return "", nil } + +func (r *ruleDB) GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) { + alertsInfo := model.AlertsInfo{} + // fetch alerts from rules db + query := "SELECT data FROM rules" + var alertsData []string + err := r.Select(&alertsData, query) + if err != nil { + zap.L().Error("Error in processing sql query", zap.Error(err)) + return &alertsInfo, err + } + for _, alert := range alertsData { + var rule GettableRule + err = json.Unmarshal([]byte(alert), &rule) + if err != nil { + zap.L().Error("invalid rule data", zap.Error(err)) + continue + } + if rule.AlertType == "LOGS_BASED_ALERT" { + alertsInfo.LogsBasedAlerts = alertsInfo.LogsBasedAlerts + 1 + } else if rule.AlertType == "METRIC_BASED_ALERT" { + alertsInfo.MetricBasedAlerts = alertsInfo.MetricBasedAlerts + 1 + } else if rule.AlertType == "TRACES_BASED_ALERT" { + alertsInfo.TracesBasedAlerts = alertsInfo.TracesBasedAlerts + 1 + } + alertsInfo.TotalAlerts = alertsInfo.TotalAlerts + 1 + } + + return &alertsInfo, nil +} diff --git a/pkg/query-service/rules/manager.go b/pkg/query-service/rules/manager.go index 20951f56a0..6050981de8 100644 --- a/pkg/query-service/rules/manager.go +++ b/pkg/query-service/rules/manager.go @@ -25,6 +25,7 @@ import ( "go.signoz.io/signoz/pkg/query-service/interfaces" "go.signoz.io/signoz/pkg/query-service/model" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "go.signoz.io/signoz/pkg/query-service/telemetry" "go.signoz.io/signoz/pkg/query-service/utils/labels" ) @@ -112,6 +113,8 @@ func NewManager(o *ManagerOptions) (*Manager, error) { db := NewRuleDB(o.DBConn) + telemetry.GetInstance().SetAlertsInfoCallback(db.GetAlertsInfo) + m := &Manager{ tasks: map[string]Task{}, rules: map[string]Rule{}, diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 9b75259296..555d4a5d6c 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -185,6 +185,12 @@ type Telemetry struct { patTokenUser bool countUsers int8 mutex sync.RWMutex + + alertsInfoCallback func(ctx context.Context) (*model.AlertsInfo, error) +} + +func (a *Telemetry) SetAlertsInfoCallback(callback func(ctx context.Context) (*model.AlertsInfo, error)) { + a.alertsInfoCallback = callback } func createTelemetry() { @@ -310,7 +316,7 @@ func createTelemetry() { telemetry.SendEvent(TELEMETRY_EVENT_HEART_BEAT, data, user.Email, false, false) } } - alertsInfo, err := telemetry.reader.GetAlertsInfo(context.Background()) + alertsInfo, err := telemetry.alertsInfoCallback(context.Background()) if err == nil { dashboardsInfo, err := telemetry.reader.GetDashboardsInfo(context.Background()) if err == nil { From 161a69fbe9c3ae4a0750a24247c0468544ce37bb Mon Sep 17 00:00:00 2001 From: Raj Kamal Singh <1133322+raj-k-singh@users.noreply.github.com> Date: Tue, 2 Jul 2024 17:14:08 +0530 Subject: [PATCH 066/281] chore: remove workaround for supporting pipeline filters using attribs with . replaced with _ (#5405) --- .../pipelineBuilder_test.go | 73 ------------------- .../queryBuilderToExpr/queryBuilderToExpr.go | 56 ++++++-------- 2 files changed, 21 insertions(+), 108 deletions(-) diff --git a/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go b/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go index 19ffd4780f..82bb2ab2bf 100644 --- a/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go +++ b/pkg/query-service/app/logparsingpipeline/pipelineBuilder_test.go @@ -803,76 +803,3 @@ func TestContainsFilterIsCaseInsensitive(t *testing.T) { _, test2Exists := result[0].Attributes_string["test2"] require.False(test2Exists) } - -func TestTemporaryWorkaroundForSupportingAttribsContainingDots(t *testing.T) { - // TODO(Raj): Remove this after dots are supported - - require := require.New(t) - - testPipeline := Pipeline{ - OrderId: 1, - Name: "pipeline1", - Alias: "pipeline1", - Enabled: true, - Filter: &v3.FilterSet{ - Operator: "AND", - Items: []v3.FilterItem{ - { - Key: v3.AttributeKey{ - Key: "k8s_deployment_name", - DataType: v3.AttributeKeyDataTypeString, - Type: v3.AttributeKeyTypeResource, - }, - Operator: "=", - Value: "ingress", - }, - }, - }, - Config: []PipelineOperator{ - { - ID: "add", - Type: "add", - Enabled: true, - Name: "add", - Field: "attributes.test", - Value: "test-value", - }, - }, - } - - testLogs := []model.SignozLog{{ - Timestamp: uint64(time.Now().UnixNano()), - Body: "test log", - Attributes_string: map[string]string{}, - Resources_string: map[string]string{ - "k8s_deployment_name": "ingress", - }, - SeverityText: entry.Info.String(), - SeverityNumber: uint8(entry.Info), - SpanID: "", - TraceID: "", - }, { - Timestamp: uint64(time.Now().UnixNano()), - Body: "test log", - Attributes_string: map[string]string{}, - Resources_string: map[string]string{ - "k8s.deployment.name": "ingress", - }, - SeverityText: entry.Info.String(), - SeverityNumber: uint8(entry.Info), - SpanID: "", - TraceID: "", - }} - - result, collectorWarnAndErrorLogs, err := SimulatePipelinesProcessing( - context.Background(), - []Pipeline{testPipeline}, - testLogs, - ) - require.Nil(err) - require.Equal(0, len(collectorWarnAndErrorLogs), strings.Join(collectorWarnAndErrorLogs, "\n")) - require.Equal(2, len(result)) - for _, processedLog := range result { - require.Equal(processedLog.Attributes_string["test"], "test-value") - } -} diff --git a/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go b/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go index e853a37685..bda8c446b1 100644 --- a/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go +++ b/pkg/query-service/queryBuilderToExpr/queryBuilderToExpr.go @@ -53,49 +53,35 @@ func Parse(filters *v3.FilterSet) (string, error) { return "", fmt.Errorf("operator not supported") } - // TODO(Raj): Remove the use of dot replaced alternative when key - // contains underscore after dots are supported in keys - names := []string{getName(v.Key)} - if strings.Contains(v.Key.Key, "_") { - dotKey := v.Key - dotKey.Key = strings.Replace(v.Key.Key, "_", ".", -1) - names = append(names, getName(dotKey)) - } - - filterParts := []string{} - for _, name := range names { - var filter string + name := getName(v.Key) - switch v.Operator { - // uncomment following lines when new version of expr is used - // case v3.FilterOperatorIn, v3.FilterOperatorNotIn: - // filter = fmt.Sprintf("%s %s list%s", name, logOperatorsToExpr[v.Operator], exprFormattedValue(v.Value)) + var filter string - case v3.FilterOperatorExists, v3.FilterOperatorNotExists: - filter = fmt.Sprintf("%s %s %s", exprFormattedValue(v.Key.Key), logOperatorsToExpr[v.Operator], getTypeName(v.Key.Type)) + switch v.Operator { + // uncomment following lines when new version of expr is used + // case v3.FilterOperatorIn, v3.FilterOperatorNotIn: + // filter = fmt.Sprintf("%s %s list%s", name, logOperatorsToExpr[v.Operator], exprFormattedValue(v.Value)) - default: - filter = fmt.Sprintf("%s %s %s", name, logOperatorsToExpr[v.Operator], exprFormattedValue(v.Value)) - - if v.Operator == v3.FilterOperatorContains || v.Operator == v3.FilterOperatorNotContains { - // `contains` and `ncontains` should be case insensitive to match how they work when querying logs. - filter = fmt.Sprintf( - "lower(%s) %s lower(%s)", - name, logOperatorsToExpr[v.Operator], exprFormattedValue(v.Value), - ) - } + case v3.FilterOperatorExists, v3.FilterOperatorNotExists: + filter = fmt.Sprintf("%s %s %s", exprFormattedValue(v.Key.Key), logOperatorsToExpr[v.Operator], getTypeName(v.Key.Type)) - // Avoid running operators on nil values - if v.Operator != v3.FilterOperatorEqual && v.Operator != v3.FilterOperatorNotEqual { - filter = fmt.Sprintf("%s != nil && %s", name, filter) - } + default: + filter = fmt.Sprintf("%s %s %s", name, logOperatorsToExpr[v.Operator], exprFormattedValue(v.Value)) + + if v.Operator == v3.FilterOperatorContains || v.Operator == v3.FilterOperatorNotContains { + // `contains` and `ncontains` should be case insensitive to match how they work when querying logs. + filter = fmt.Sprintf( + "lower(%s) %s lower(%s)", + name, logOperatorsToExpr[v.Operator], exprFormattedValue(v.Value), + ) } - filterParts = append(filterParts, filter) + // Avoid running operators on nil values + if v.Operator != v3.FilterOperatorEqual && v.Operator != v3.FilterOperatorNotEqual { + filter = fmt.Sprintf("%s != nil && %s", name, filter) + } } - filter := strings.Join(filterParts, " || ") - // check if the filter is a correct expression language _, err := expr.Compile(filter) if err != nil { From d215ce09b070654a6b563968f0c01fceb60332e9 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Wed, 3 Jul 2024 10:30:41 +0530 Subject: [PATCH 067/281] fix: remove pagination from members listing in org settings page (#5400) --- .../components/ResizeTable/ResizeTable.tsx | 19 +- .../tests/OrganizationSettings.test.tsx | 37 +++ .../src/mocks-server/__mockdata__/members.ts | 220 ++++++++++++++++++ frontend/src/mocks-server/handlers.ts | 4 + frontend/src/tests/test-utils.tsx | 9 + 5 files changed, 283 insertions(+), 6 deletions(-) create mode 100644 frontend/src/container/OrganizationSettings/tests/OrganizationSettings.test.tsx create mode 100644 frontend/src/mocks-server/__mockdata__/members.ts diff --git a/frontend/src/components/ResizeTable/ResizeTable.tsx b/frontend/src/components/ResizeTable/ResizeTable.tsx index d2bd98f568..5f8ac7a4a5 100644 --- a/frontend/src/components/ResizeTable/ResizeTable.tsx +++ b/frontend/src/components/ResizeTable/ResizeTable.tsx @@ -3,6 +3,7 @@ import { Table } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { dragColumnParams } from 'hooks/useDragColumns/configs'; +import { set } from 'lodash-es'; import { SyntheticEvent, useCallback, @@ -59,15 +60,21 @@ function ResizeTable({ [columnsData, onDragColumn, handleResize], ); - const tableParams = useMemo( - () => ({ + const tableParams = useMemo(() => { + const props = { ...restProps, components: { header: { cell: ResizableHeader } }, columns: mergedColumns, - pagination: { ...pagination, hideOnSinglePage: true }, - }), - [mergedColumns, pagination, restProps], - ); + }; + + set( + props, + 'pagination', + pagination ? { ...pagination, hideOnSinglePage: true } : false, + ); + + return props; + }, [mergedColumns, pagination, restProps]); useEffect(() => { if (columns) { diff --git a/frontend/src/container/OrganizationSettings/tests/OrganizationSettings.test.tsx b/frontend/src/container/OrganizationSettings/tests/OrganizationSettings.test.tsx new file mode 100644 index 0000000000..35517806cd --- /dev/null +++ b/frontend/src/container/OrganizationSettings/tests/OrganizationSettings.test.tsx @@ -0,0 +1,37 @@ +import { act, render, screen, waitFor } from 'tests/test-utils'; + +import Members from '../Members'; + +describe('Organization Settings Page', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('render list of members', async () => { + act(() => { + render(); + }); + + const title = await screen.findByText(/Members/i); + expect(title).toBeInTheDocument(); + + await waitFor(() => { + expect(screen.getByText('firstUser@test.io')).toBeInTheDocument(); // first item + expect(screen.getByText('lastUser@test.io')).toBeInTheDocument(); // last item + }); + }); + + // this is required as our edit/delete logic is dependent on the index and it will break with pagination enabled + it('render list of members without pagination', async () => { + render(); + + await waitFor(() => { + expect(screen.getByText('firstUser@test.io')).toBeInTheDocument(); // first item + expect(screen.getByText('lastUser@test.io')).toBeInTheDocument(); // last item + + expect( + document.querySelector('.ant-table-pagination'), + ).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/src/mocks-server/__mockdata__/members.ts b/frontend/src/mocks-server/__mockdata__/members.ts new file mode 100644 index 0000000000..355224735a --- /dev/null +++ b/frontend/src/mocks-server/__mockdata__/members.ts @@ -0,0 +1,220 @@ +/* eslint-disable sonarjs/no-duplicate-string */ + +export const membersResponse = [ + { + id: '3223a874-5678458745786', + name: 'John Doe', + email: 'firstUser@test.io', + createdAt: 1666357530, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '5e9681b1-5678458745786', + name: 'Jane Doe', + email: 'johndoe2@test.io', + createdAt: 1666365394, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '11e8c55d-5678458745786', + name: 'Alex', + email: 'blah@test.io', + createdAt: 1666366317, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: 'd878012367813286731aab62', + role: 'VIEWER', + organization: 'Test Inc', + flags: null, + }, + { + id: '2ad2e404-5678458745786', + name: 'Tom', + email: 'johndoe4@test.io', + createdAt: 1673441483, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '6f532456-5678458745786', + name: 'Harry', + email: 'harry@test.io', + createdAt: 1691551672, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: 'ae22fa73-5678458745786', + name: 'Ron', + email: 'ron@test.io', + createdAt: 1691668239, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '3223a874-5678458745786', + name: 'John Doe', + email: 'johndoe@test.io', + createdAt: 1666357530, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '5e9681b1-5678458745786', + name: 'Jane Doe', + email: 'johndoe2@test.io', + createdAt: 1666365394, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '11e8c55d-5678458745786', + name: 'Alex', + email: 'blah@test.io', + createdAt: 1666366317, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: 'd878012367813286731aab62', + role: 'VIEWER', + organization: 'Test Inc', + flags: null, + }, + { + id: '2ad2e404-5678458745786', + name: 'Tom', + email: 'johndoe4@test.io', + createdAt: 1673441483, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '6f532456-5678458745786', + name: 'Harry', + email: 'harry@test.io', + createdAt: 1691551672, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: 'ae22fa73-5678458745786', + name: 'Ron', + email: 'ron@test.io', + createdAt: 1691668239, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '3223a874-5678458745786', + name: 'John Doe', + email: 'johndoe@test.io', + createdAt: 1666357530, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '5e9681b1-5678458745786', + name: 'Jane Doe', + email: 'johndoe2@test.io', + createdAt: 1666365394, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '11e8c55d-5678458745786', + name: 'Alex', + email: 'blah@test.io', + createdAt: 1666366317, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: 'd878012367813286731aab62', + role: 'VIEWER', + organization: 'Test Inc', + flags: null, + }, + { + id: '2ad2e404-5678458745786', + name: 'Tom', + email: 'johndoe4@test.io', + createdAt: 1673441483, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: '6f532456-5678458745786', + name: 'Harry', + email: 'harry@test.io', + createdAt: 1691551672, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, + { + id: 'ae22fa73-5678458745786', + name: 'Ron', + email: 'lastUser@test.io', + createdAt: 1691668239, + profilePictureURL: '', + orgId: '1287612376312867312867', + groupId: '5678458745786', + role: 'ADMIN', + organization: 'Test Inc', + flags: null, + }, +]; diff --git a/frontend/src/mocks-server/handlers.ts b/frontend/src/mocks-server/handlers.ts index 445c4d8354..4e48a4a908 100644 --- a/frontend/src/mocks-server/handlers.ts +++ b/frontend/src/mocks-server/handlers.ts @@ -2,6 +2,7 @@ import { rest } from 'msw'; import { billingSuccessResponse } from './__mockdata__/billing'; import { licensesSuccessResponse } from './__mockdata__/licenses'; +import { membersResponse } from './__mockdata__/members'; import { queryRangeSuccessResponse } from './__mockdata__/query_range'; import { serviceSuccessResponse } from './__mockdata__/services'; import { topLevelOperationSuccessResponse } from './__mockdata__/top_level_operations'; @@ -25,6 +26,9 @@ export const handlers = [ res(ctx.status(200), ctx.json(topLevelOperationSuccessResponse)), ), + rest.get('http://localhost/api/v1/orgUsers/*', (req, res, ctx) => + res(ctx.status(200), ctx.json(membersResponse)), + ), rest.get( 'http://localhost/api/v3/autocomplete/attribute_keys', (req, res, ctx) => { diff --git a/frontend/src/tests/test-utils.tsx b/frontend/src/tests/test-utils.tsx index 45793dfa76..ff2d3c7e51 100644 --- a/frontend/src/tests/test-utils.tsx +++ b/frontend/src/tests/test-utils.tsx @@ -42,6 +42,15 @@ const mockStored = (role?: string): any => accessJwt: '', refreshJwt: '', }, + org: [ + { + createdAt: 0, + hasOptedUpdates: false, + id: 'xyz', + isAnonymous: false, + name: 'Test Inc. - India', + }, + ], }, }); From 81c3e6fa651ead56a9524697ffaca5ee4d9e7bce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:33:22 +0530 Subject: [PATCH 068/281] chore(deps): bump braces from 3.0.2 to 3.0.3 in /frontend (#5196) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 6120da5e98..18392136c0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -6195,11 +6195,11 @@ brace-expansion@^2.0.1: balanced-match "^1.0.0" braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" broadcast-channel@^3.4.1: version "3.7.0" @@ -8808,10 +8808,10 @@ file-saver@^2.0.2: resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38" integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA== -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" From 7c81270ed935ddfd8cfc7458fc6274d5ed25590a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 15:39:28 +0530 Subject: [PATCH 069/281] chore(deps): bump ws from 7.5.9 to 7.5.10 in /frontend (#5265) Bumps [ws](https://github.com/websockets/ws) from 7.5.9 to 7.5.10. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 18392136c0..73bf7ae208 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -17638,14 +17638,14 @@ write-file-atomic@^4.0.2: signal-exit "^3.0.7" ws@^7.3.1, ws@^7.4.6: - version "7.5.9" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== ws@^8.13.0: - version "8.13.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== xhr-request@^1.0.1: version "1.1.0" From 216ad362345f0e01bf295d35b23793335c8f4635 Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Wed, 3 Jul 2024 16:53:01 +0530 Subject: [PATCH 070/281] =?UTF-8?q?chore(signoz):=20=F0=9F=93=8C=20pin=20v?= =?UTF-8?q?ersions:=20SigNoz=200.49.0,=20SigNoz=20OtelCollector=200.102.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Prashant Shahi --- deploy/docker-swarm/clickhouse-setup/docker-compose.yaml | 6 +++--- deploy/docker/clickhouse-setup/docker-compose-core.yaml | 4 ++-- .../docker/clickhouse-setup/docker-compose.testing.yaml | 8 ++++---- deploy/docker/clickhouse-setup/docker-compose.yaml | 8 ++++---- go.mod | 2 +- go.sum | 8 ++++---- pkg/query-service/tests/test-deploy/docker-compose.yaml | 4 ++-- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 03f3cb92f1..99060b38d2 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.48.1 + image: signoz/query-service:0.49.0 command: [ "-config=/root/config/prometheus.yml", @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.102.0 + image: signoz/signoz-otel-collector:0.102.1 command: [ "--config=/etc/otel-collector-config.yaml", @@ -237,7 +237,7 @@ services: - query-service otel-collector-migrator: - image: signoz/signoz-schema-migrator:0.102.0 + image: signoz/signoz-schema-migrator:0.102.1 deploy: restart_policy: condition: on-failure diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index bbafa71a1f..6803c29c69 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -66,7 +66,7 @@ services: - --storage.path=/data otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -81,7 +81,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.102.0 + image: signoz/signoz-otel-collector:0.102.1 command: [ "--config=/etc/otel-collector-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index c5c13c5de8..23d8195850 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.48.1} + image: signoz/query-service:${DOCKER_TAG:-0.49.0} container_name: signoz-query-service command: [ @@ -204,7 +204,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.48.1} + image: signoz/frontend:${DOCKER_TAG:-0.49.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -216,7 +216,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -230,7 +230,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.0} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.1} container_name: signoz-otel-collector command: [ diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index fb7757ba0d..8621bbfe44 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.48.1} + image: signoz/query-service:${DOCKER_TAG:-0.49.0} container_name: signoz-query-service command: [ @@ -203,7 +203,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.48.1} + image: signoz/frontend:${DOCKER_TAG:-0.49.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -215,7 +215,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -229,7 +229,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.0} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.1} container_name: signoz-otel-collector command: [ diff --git a/go.mod b/go.mod index fbaea1ba45..bfa3c7eccf 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.20.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd - github.com/SigNoz/signoz-otel-collector v0.102.0 + github.com/SigNoz/signoz-otel-collector v0.102.1 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 diff --git a/go.sum b/go.sum index 04162eb8bd..0db4e91ca4 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkb github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc= github.com/SigNoz/prometheus v1.11.1 h1:roM8ugYf4UxaeKKujEeBvoX7ybq3IrS+TB26KiRtIJg= github.com/SigNoz/prometheus v1.11.1/go.mod h1:uv4mQwZQtx7y4GQ6EdHOi8Wsk07uHNn2XHd1zM85m6I= -github.com/SigNoz/signoz-otel-collector v0.102.0 h1:v6ap+gdvrKklMwU+M9FJgrn28vN0YxrINl3kvdcLonA= -github.com/SigNoz/signoz-otel-collector v0.102.0/go.mod h1:kCx5BfzDujq6C0+kotiqLp5COG2ut4Cb039+55rbWE0= +github.com/SigNoz/signoz-otel-collector v0.102.1 h1:RXzs/dA9IMFGi6mXecEFVvShWfilqx5cCEXmzzvVfK0= +github.com/SigNoz/signoz-otel-collector v0.102.1/go.mod h1:ISAXYhZenojCWg6CdDJtPMpfS6Zwc08+uoxH25tc6Y0= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= @@ -378,8 +378,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 1fb37ce2ff..6fea110b73 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -192,7 +192,7 @@ services: <<: *db-depend otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.0} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -205,7 +205,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:0.102.0 + image: signoz/signoz-otel-collector:0.102.1 container_name: signoz-otel-collector command: [ From 2e0ddc7c7fa622c814203796ebfde03e3e7caa72 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Thu, 4 Jul 2024 01:07:55 +0530 Subject: [PATCH 071/281] chore: remove dynamic config invocation (#5416) --- frontend/src/container/AppLayout/index.tsx | 43 +--------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 88c6b8f8f6..774e0e888d 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -6,7 +6,6 @@ import './AppLayout.styles.scss'; import * as Sentry from '@sentry/react'; import { Flex } from 'antd'; import getLocalStorageKey from 'api/browser/localstorage/get'; -import getDynamicConfigs from 'api/dynamicConfigs/getDynamicConfigs'; import getUserLatestVersion from 'api/user/getLatestVersion'; import getUserVersion from 'api/user/getVersion'; import cx from 'classnames'; @@ -38,7 +37,6 @@ import { sideBarCollapse } from 'store/actions'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; import { - UPDATE_CONFIGS, UPDATE_CURRENT_ERROR, UPDATE_CURRENT_VERSION, UPDATE_LATEST_VERSION, @@ -66,11 +64,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element { const { pathname } = useLocation(); const { t } = useTranslation(['titles']); - const [ - getUserVersionResponse, - getUserLatestVersionResponse, - getDynamicConfigsResponse, - ] = useQueries([ + const [getUserVersionResponse, getUserLatestVersionResponse] = useQueries([ { queryFn: getUserVersion, queryKey: ['getUserVersion', user?.accessJwt], @@ -81,10 +75,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element { queryKey: ['getUserLatestVersion', user?.accessJwt], enabled: isLoggedIn, }, - { - queryFn: getDynamicConfigs, - queryKey: ['getDynamicConfigs', user?.accessJwt], - }, ]); useEffect(() => { @@ -95,15 +85,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element { if (getUserVersionResponse.status === 'idle' && isLoggedIn) { getUserVersionResponse.refetch(); } - if (getDynamicConfigsResponse.status === 'idle') { - getDynamicConfigsResponse.refetch(); - } - }, [ - getUserLatestVersionResponse, - getUserVersionResponse, - isLoggedIn, - getDynamicConfigsResponse, - ]); + }, [getUserLatestVersionResponse, getUserVersionResponse, isLoggedIn]); const { children } = props; @@ -111,7 +93,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element { const latestCurrentCounter = useRef(0); const latestVersionCounter = useRef(0); - const latestConfigCounter = useRef(0); const { notifications } = useNotifications(); @@ -189,23 +170,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element { }, }); } - - if ( - getDynamicConfigsResponse.isFetched && - getDynamicConfigsResponse.isSuccess && - getDynamicConfigsResponse.data && - getDynamicConfigsResponse.data.payload && - latestConfigCounter.current === 0 - ) { - latestConfigCounter.current = 1; - - dispatch({ - type: UPDATE_CONFIGS, - payload: { - configs: getDynamicConfigsResponse.data.payload, - }, - }); - } }, [ dispatch, isLoggedIn, @@ -220,9 +184,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element { getUserLatestVersionResponse.isFetched, getUserVersionResponse.isFetched, getUserLatestVersionResponse.isSuccess, - getDynamicConfigsResponse.data, - getDynamicConfigsResponse.isFetched, - getDynamicConfigsResponse.isSuccess, notifications, ]); From 1b0ec8ac4332b49d12d20099fd986946293f58c6 Mon Sep 17 00:00:00 2001 From: Nityananda Gohain Date: Thu, 4 Jul 2024 12:08:42 +0530 Subject: [PATCH 072/281] fix: typecase support added for float to int (#5408) --- pkg/query-service/utils/format.go | 4 ++++ pkg/query-service/utils/format_test.go | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/pkg/query-service/utils/format.go b/pkg/query-service/utils/format.go index 481590e761..bd7db15e6b 100644 --- a/pkg/query-service/utils/format.go +++ b/pkg/query-service/utils/format.go @@ -91,6 +91,10 @@ func ValidateAndCastValue(v interface{}, dataType v3.AttributeKeyDataType) (inte return x, nil case int, int64: return x, nil + case float32: + return int64(x), nil + case float64: + return int64(x), nil case string: int64val, err := strconv.ParseInt(x, 10, 64) if err != nil { diff --git a/pkg/query-service/utils/format_test.go b/pkg/query-service/utils/format_test.go index 2c7aab1e6d..3a2a7f1265 100644 --- a/pkg/query-service/utils/format_test.go +++ b/pkg/query-service/utils/format_test.go @@ -275,6 +275,24 @@ var testValidateAndCastValueData = []struct { want: nil, wantErr: true, }, + { + name: "v3.AttributeKeyDataTypeInt64: valid float32", + args: args{ + v: float32(1000), + dataType: v3.AttributeKeyDataTypeInt64, + }, + want: int64(1000), + wantErr: false, + }, + { + name: "v3.AttributeKeyDataTypeInt64: valid float64", + args: args{ + v: float64(1000), + dataType: v3.AttributeKeyDataTypeInt64, + }, + want: int64(1000), + wantErr: false, + }, } // Test cases for ValidateAndCastValue function in pkg/query-service/utils/format.go From e542d2ee09dd9b95a5eafeaac67ea829bd984cfd Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Thu, 4 Jul 2024 22:24:57 +0530 Subject: [PATCH 073/281] =?UTF-8?q?chore(signoz):=20=F0=9F=93=8C=20pin=20v?= =?UTF-8?q?ersions:=20SigNoz=200.49.1,=20SigNoz=20OtelCollector=200.102.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Prashant Shahi --- deploy/docker-swarm/clickhouse-setup/docker-compose.yaml | 6 +++--- deploy/docker/clickhouse-setup/docker-compose-core.yaml | 4 ++-- .../docker/clickhouse-setup/docker-compose.testing.yaml | 8 ++++---- deploy/docker/clickhouse-setup/docker-compose.yaml | 8 ++++---- go.mod | 2 +- go.sum | 4 ++-- pkg/query-service/tests/test-deploy/docker-compose.yaml | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 99060b38d2..de7ab6655e 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.49.0 + image: signoz/query-service:0.49.1 command: [ "-config=/root/config/prometheus.yml", @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.102.1 + image: signoz/signoz-otel-collector:0.102.2 command: [ "--config=/etc/otel-collector-config.yaml", @@ -237,7 +237,7 @@ services: - query-service otel-collector-migrator: - image: signoz/signoz-schema-migrator:0.102.1 + image: signoz/signoz-schema-migrator:0.102.2 deploy: restart_policy: condition: on-failure diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index 6803c29c69..73b862ea6e 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -66,7 +66,7 @@ services: - --storage.path=/data otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.2} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -81,7 +81,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.102.1 + image: signoz/signoz-otel-collector:0.102.2 command: [ "--config=/etc/otel-collector-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index 23d8195850..849a536c27 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.49.0} + image: signoz/query-service:${DOCKER_TAG:-0.49.1} container_name: signoz-query-service command: [ @@ -204,7 +204,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.49.0} + image: signoz/frontend:${DOCKER_TAG:-0.49.1} container_name: signoz-frontend restart: on-failure depends_on: @@ -216,7 +216,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.2} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -230,7 +230,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.1} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.2} container_name: signoz-otel-collector command: [ diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 8621bbfe44..6528a1826f 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -164,7 +164,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.49.0} + image: signoz/query-service:${DOCKER_TAG:-0.49.1} container_name: signoz-query-service command: [ @@ -203,7 +203,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.49.0} + image: signoz/frontend:${DOCKER_TAG:-0.49.1} container_name: signoz-frontend restart: on-failure depends_on: @@ -215,7 +215,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.2} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -229,7 +229,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.1} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.2} container_name: signoz-otel-collector command: [ diff --git a/go.mod b/go.mod index bfa3c7eccf..10831ec475 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.20.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd - github.com/SigNoz/signoz-otel-collector v0.102.1 + github.com/SigNoz/signoz-otel-collector v0.102.2 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 diff --git a/go.sum b/go.sum index 0db4e91ca4..5c49916eb3 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkb github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc= github.com/SigNoz/prometheus v1.11.1 h1:roM8ugYf4UxaeKKujEeBvoX7ybq3IrS+TB26KiRtIJg= github.com/SigNoz/prometheus v1.11.1/go.mod h1:uv4mQwZQtx7y4GQ6EdHOi8Wsk07uHNn2XHd1zM85m6I= -github.com/SigNoz/signoz-otel-collector v0.102.1 h1:RXzs/dA9IMFGi6mXecEFVvShWfilqx5cCEXmzzvVfK0= -github.com/SigNoz/signoz-otel-collector v0.102.1/go.mod h1:ISAXYhZenojCWg6CdDJtPMpfS6Zwc08+uoxH25tc6Y0= +github.com/SigNoz/signoz-otel-collector v0.102.2 h1:SmjsBZjMjTVVpuOlfJXlsDJQbdefQP/9Wz3CyzSuZuU= +github.com/SigNoz/signoz-otel-collector v0.102.2/go.mod h1:ISAXYhZenojCWg6CdDJtPMpfS6Zwc08+uoxH25tc6Y0= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 6fea110b73..19d088dc7a 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -192,7 +192,7 @@ services: <<: *db-depend otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.1} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.2} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -205,7 +205,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:0.102.1 + image: signoz/signoz-otel-collector:0.102.2 container_name: signoz-otel-collector command: [ From 4264fc0f3aa50a26dfc6c961379b4942bfd4aebf Mon Sep 17 00:00:00 2001 From: ahmadshaheer1 Date: Sun, 7 Jul 2024 10:46:49 +0430 Subject: [PATCH 074/281] feat: add react-query devtools in development env --- frontend/src/index.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index b83ca56731..49497d62bb 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -10,6 +10,7 @@ import posthog from 'posthog-js'; import { createRoot } from 'react-dom/client'; import { HelmetProvider } from 'react-helmet-async'; import { QueryClient, QueryClientProvider } from 'react-query'; +import { ReactQueryDevtools } from 'react-query/devtools'; import { Provider } from 'react-redux'; import store from 'store'; @@ -72,6 +73,9 @@ if (container) { + {process.env.NODE_ENV === 'development' && ( + + )} From c9309eecaa3ac5c74422e2b52333880f0339f23a Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:19:07 +0530 Subject: [PATCH 075/281] feat: added empty states for list, trace and timeSeried view in traces (#5290) * feat: added empty states for list, trace and timeSeried view in traces * feat: test case skip * feat: fixed import order * feat: added utm parameter link * feat: added strings * feat: resovled comments * feat: added common doclinks util * feat: test case updated: --- frontend/public/locales/en/common.json | 3 +- .../tests/LogsExplorerViews.test.tsx | 6 +- .../src/container/LogsLoading/LogsLoading.tsx | 6 +- .../src/container/NoLogs/NoLogs.styles.scss | 1 + frontend/src/container/NoLogs/NoLogs.tsx | 5 +- .../tests/PipelinesSearchSection.test.tsx | 2 +- .../TimeSeriesView/TimeSeriesView.tsx | 4 +- .../src/container/TimeSeriesView/index.tsx | 5 +- .../TracesExplorer/ListView/index.tsx | 43 +++++++--- .../TraceLoading/TraceLoading.styles.scss | 19 +++++ .../TraceLoading/TraceLoading.tsx | 24 ++++++ .../TracesExplorer/TracesView/configs.tsx | 1 - .../TracesExplorer/TracesView/index.tsx | 78 +++++++++++++------ frontend/src/pages/TracesExplorer/index.tsx | 9 ++- frontend/src/pages/TracesExplorer/utils.tsx | 13 +++- frontend/src/utils/docLinks.ts | 9 +++ 16 files changed, 177 insertions(+), 51 deletions(-) create mode 100644 frontend/src/container/TracesExplorer/TraceLoading/TraceLoading.styles.scss create mode 100644 frontend/src/container/TracesExplorer/TraceLoading/TraceLoading.tsx create mode 100644 frontend/src/utils/docLinks.ts diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index f167aecffc..72d9f13810 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -6,5 +6,6 @@ "share": "Share", "save": "Save", "edit": "Edit", - "logged_in": "Logged In" + "logged_in": "Logged In", + "pending_data_placeholder": "Just a bit of patience, just a little bit’s enough ⎯ we’re getting your {{dataSource}}!" } diff --git a/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx b/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx index d55e9e8f1b..b88022d89b 100644 --- a/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx +++ b/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx @@ -120,11 +120,7 @@ describe('LogsExplorerViews -', () => { // switch to table view await userEvent.click(queryByTestId('table-view') as HTMLElement); - expect( - queryByText( - 'Just a bit of patience, just a little bit’s enough ⎯ we’re getting your logs!', - ), - ).toBeInTheDocument(); + expect(queryByText('pending_data_placeholder')).toBeInTheDocument(); }); it('check error state', async () => { diff --git a/frontend/src/container/LogsLoading/LogsLoading.tsx b/frontend/src/container/LogsLoading/LogsLoading.tsx index 1710cd9f57..deb4758b6e 100644 --- a/frontend/src/container/LogsLoading/LogsLoading.tsx +++ b/frontend/src/container/LogsLoading/LogsLoading.tsx @@ -1,8 +1,11 @@ import './LogsLoading.styles.scss'; import { Typography } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { DataSource } from 'types/common/queryBuilder'; export function LogsLoading(): JSX.Element { + const { t } = useTranslation('common'); return (
@@ -13,8 +16,7 @@ export function LogsLoading(): JSX.Element { /> - Just a bit of patience, just a little bit’s enough ⎯ we’re getting your - logs! + {t('pending_data_placeholder', { dataSource: DataSource.LOGS })}
diff --git a/frontend/src/container/NoLogs/NoLogs.styles.scss b/frontend/src/container/NoLogs/NoLogs.styles.scss index 32d7309b28..94086413fc 100644 --- a/frontend/src/container/NoLogs/NoLogs.styles.scss +++ b/frontend/src/container/NoLogs/NoLogs.styles.scss @@ -39,6 +39,7 @@ font-weight: 500; line-height: 18px; /* 128.571% */ letter-spacing: -0.07px; + cursor: pointer; } } } diff --git a/frontend/src/container/NoLogs/NoLogs.tsx b/frontend/src/container/NoLogs/NoLogs.tsx index d317da69ce..71e0d213e8 100644 --- a/frontend/src/container/NoLogs/NoLogs.tsx +++ b/frontend/src/container/NoLogs/NoLogs.tsx @@ -6,6 +6,7 @@ import history from 'lib/history'; import { ArrowUpRight } from 'lucide-react'; import { DataSource } from 'types/common/queryBuilder'; import { isCloudUser } from 'utils/app'; +import DOCLINKS from 'utils/docLinks'; export default function NoLogs({ dataSource, @@ -25,8 +26,10 @@ export default function NoLogs({ ? ROUTES.GET_STARTED_APPLICATION_MONITORING : ROUTES.GET_STARTED_LOGS_MANAGEMENT, ); + } else if (dataSource === 'traces') { + window.open(DOCLINKS.TRACES_EXPLORER_EMPTY_STATE, '_blank'); } else { - window.open(`https://signoz.io/docs/userguide/${dataSource}/`, '_blank'); + window.open(`${DOCLINKS.USER_GUIDE}${dataSource}/`, '_blank'); } }; return ( diff --git a/frontend/src/container/PipelinePage/tests/PipelinesSearchSection.test.tsx b/frontend/src/container/PipelinePage/tests/PipelinesSearchSection.test.tsx index 2ef069a8f5..8c5d499433 100644 --- a/frontend/src/container/PipelinePage/tests/PipelinesSearchSection.test.tsx +++ b/frontend/src/container/PipelinePage/tests/PipelinesSearchSection.test.tsx @@ -22,7 +22,7 @@ describe('PipelinePage container test', () => { expect(asFragment()).toMatchSnapshot(); }); - it('should handle search', async () => { + it.skip('should handle search', async () => { const setPipelineValue = jest.fn(); const { getByPlaceholderText, container } = render( diff --git a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx index 973ea3a5c0..4abd67de21 100644 --- a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx +++ b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx @@ -7,6 +7,7 @@ import LogsError from 'container/LogsError/LogsError'; import { LogsLoading } from 'container/LogsLoading/LogsLoading'; import NoLogs from 'container/NoLogs/NoLogs'; import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config'; +import { TracesLoading } from 'container/TracesExplorer/TraceLoading/TraceLoading'; import { useIsDarkMode } from 'hooks/useDarkMode'; import useUrlQuery from 'hooks/useUrlQuery'; import GetMinMax from 'lib/getMinMax'; @@ -146,7 +147,8 @@ function TimeSeriesView({ style={{ height: '100%', width: '100%' }} ref={graphRef} > - {isLoading && } + {isLoading && + (dataSource === DataSource.LOGS ? : )} {chartData && chartData[0] && diff --git a/frontend/src/container/TimeSeriesView/index.tsx b/frontend/src/container/TimeSeriesView/index.tsx index 2dd009746d..b619e64b02 100644 --- a/frontend/src/container/TimeSeriesView/index.tsx +++ b/frontend/src/container/TimeSeriesView/index.tsx @@ -14,6 +14,7 @@ import { convertDataValueToMs } from './utils'; function TimeSeriesViewContainer({ dataSource = DataSource.TRACES, + isFilterApplied, }: TimeSeriesViewProps): JSX.Element { const { stagedQuery, currentQuery, panelType } = useQueryBuilder(); @@ -70,8 +71,7 @@ function TimeSeriesViewContainer({ return ( - + {transformedQueryTableData.length !== 0 && ( + + )} {isError && {data?.error || 'Something went wrong'}} - {!isError && ( + {(isLoading || (isFetching && transformedQueryTableData.length === 0)) && ( + + )} + + {isDataPresent && !isFilterApplied && ( + + )} + + {isDataPresent && isFilterApplied && } + + {!isError && transformedQueryTableData.length !== 0 && ( +
+ wait-icon + + + {t('pending_data_placeholder', { dataSource: DataSource.TRACES })} + +
+ + ); +} diff --git a/frontend/src/container/TracesExplorer/TracesView/configs.tsx b/frontend/src/container/TracesExplorer/TracesView/configs.tsx index f5980a044d..202603b680 100644 --- a/frontend/src/container/TracesExplorer/TracesView/configs.tsx +++ b/frontend/src/container/TracesExplorer/TracesView/configs.tsx @@ -7,7 +7,6 @@ import { generatePath, Link } from 'react-router-dom'; import { ListItem } from 'types/api/widgets/getQuery'; export const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS]; -export const TRACES_DETAILS_LINK = 'https://signoz.io/docs/userguide/traces/'; export const columns: ColumnsType = [ { diff --git a/frontend/src/container/TracesExplorer/TracesView/index.tsx b/frontend/src/container/TracesExplorer/TracesView/index.tsx index 2093881e01..923289dd19 100644 --- a/frontend/src/container/TracesExplorer/TracesView/index.tsx +++ b/frontend/src/container/TracesExplorer/TracesView/index.tsx @@ -4,6 +4,8 @@ import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { QueryParams } from 'constants/query'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; +import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch'; +import NoLogs from 'container/NoLogs/NoLogs'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { Pagination } from 'hooks/queryPagination'; @@ -11,13 +13,20 @@ import useUrlQueryData from 'hooks/useUrlQueryData'; import { memo, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; +import { DataSource } from 'types/common/queryBuilder'; import { GlobalReducer } from 'types/reducer/globalTime'; +import DOCLINKS from 'utils/docLinks'; import TraceExplorerControls from '../Controls'; -import { columns, PER_PAGE_OPTIONS, TRACES_DETAILS_LINK } from './configs'; +import { TracesLoading } from '../TraceLoading/TraceLoading'; +import { columns, PER_PAGE_OPTIONS } from './configs'; import { ActionsContainer, Container } from './styles'; -function TracesView(): JSX.Element { +interface TracesViewProps { + isFilterApplied: boolean; +} + +function TracesView({ isFilterApplied }: TracesViewProps): JSX.Element { const { stagedQuery, panelType } = useQueryBuilder(); const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector< @@ -29,7 +38,7 @@ function TracesView(): JSX.Element { QueryParams.pagination, ); - const { data, isLoading } = useGetQueryRange( + const { data, isLoading, isFetching, isError } = useGetQueryRange( { query: stagedQuery || initialQueriesMap.traces, graphType: panelType || PANEL_TYPES.TRACE, @@ -65,28 +74,49 @@ function TracesView(): JSX.Element { return ( - - - This tab only shows Root Spans. More details - - {' '} - here - - - + + This tab only shows Root Spans. More details + + {' '} + here + + + + + )} + + {(isLoading || (isFetching && (tableData || []).length === 0)) && ( + + )} + + {!isLoading && + !isFetching && + !isError && + !isFilterApplied && + (tableData || []).length === 0 && } + + {!isLoading && + !isFetching && + (tableData || []).length === 0 && + !isError && + isFilterApplied && } + + {(tableData || []).length !== 0 && ( + - - + )} ); } diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index ab022bfeee..ba267d383f 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -23,7 +23,7 @@ import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; -import { cloneDeep, set } from 'lodash-es'; +import { cloneDeep, isEmpty, set } from 'lodash-es'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { Dashboard } from 'types/api/dashboard/getAll'; @@ -62,6 +62,12 @@ function TracesExplorer(): JSX.Element { const currentTab = panelType || PANEL_TYPES.LIST; + const listQuery = useMemo(() => { + if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null; + + return stagedQuery.builder.queryData.find((item) => !item.disabled) || null; + }, [stagedQuery]); + const isMultipleQueries = useMemo( () => currentQuery.builder.queryData.length > 1 || @@ -101,6 +107,7 @@ function TracesExplorer(): JSX.Element { const tabsItems = getTabsItems({ isListViewDisabled: isMultipleQueries || isGroupByExist, + isFilterApplied: !isEmpty(listQuery?.filters.items), }); const exportDefaultQuery = useMemo( diff --git a/frontend/src/pages/TracesExplorer/utils.tsx b/frontend/src/pages/TracesExplorer/utils.tsx index dc3f1197b3..3e0566f415 100644 --- a/frontend/src/pages/TracesExplorer/utils.tsx +++ b/frontend/src/pages/TracesExplorer/utils.tsx @@ -9,10 +9,12 @@ import { DataSource } from 'types/common/queryBuilder'; interface GetTabsItemsProps { isListViewDisabled: boolean; + isFilterApplied: boolean; } export const getTabsItems = ({ isListViewDisabled, + isFilterApplied, }: GetTabsItemsProps): TabsProps['items'] => [ { label: ( @@ -23,7 +25,7 @@ export const getTabsItems = ({ /> ), key: PANEL_TYPES.LIST, - children: , + children: , disabled: isListViewDisabled, }, { @@ -35,13 +37,18 @@ export const getTabsItems = ({ /> ), key: PANEL_TYPES.TRACE, - children: , + children: , disabled: isListViewDisabled, }, { label: , key: PANEL_TYPES.TIME_SERIES, - children: , + children: ( + + ), }, { label: 'Table View', diff --git a/frontend/src/utils/docLinks.ts b/frontend/src/utils/docLinks.ts new file mode 100644 index 0000000000..0a4be20a00 --- /dev/null +++ b/frontend/src/utils/docLinks.ts @@ -0,0 +1,9 @@ +const DOCLINKS = { + TRACES_EXPLORER_EMPTY_STATE: + 'https://signoz.io/docs/instrumentation/overview/?utm_source=product&utm_medium=traces-explorer-empty-state', + USER_GUIDE: 'https://signoz.io/docs/userguide/', + TRACES_DETAILS_LINK: + 'https://signoz.io/docs/product-features/trace-explorer/?utm_source=product&utm_medium=traces-explorer-trace-tab#traces-view', +}; + +export default DOCLINKS; From f6b29999c98dbc3bb8b8a441226140271cbbc42d Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:17:27 +0530 Subject: [PATCH 076/281] fix: added right margin to facing issues btn on dashboad detail page (#5365) * fix: added right padding to facing issues btn on dashboad detail page * fix: added right margin instead of padding --- .../NewDashboard/DashboardDescription/Description.styles.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss b/frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss index 39802eaba9..48023ecda5 100644 --- a/frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss +++ b/frontend/src/container/NewDashboard/DashboardDescription/Description.styles.scss @@ -58,6 +58,7 @@ display: flex; justify-content: space-between; align-items: center; + margin-right: 16px; .dashboard-breadcrumbs { height: 48px; From bf177882e61cd667d4e30f61adb80d8df9890c9b Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 8 Jul 2024 19:24:05 +0530 Subject: [PATCH 077/281] fix: resize observer charts issue in alerts builder (#5436) --- .../FormAlertRules/ChartPreview/index.tsx | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx index 9819690641..331a5a0fc7 100644 --- a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx +++ b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx @@ -246,17 +246,19 @@ function ChartPreview({ return ( {headline} - {(queryResponse?.isError || queryResponse?.error) && ( - - {' '} - {queryResponse.error.message || t('preview_chart_unexpected_error')} - - )} - {chartData && !queryResponse.isError && ( -
- {queryResponse.isLoading && ( - - )} + +
+ {queryResponse.isLoading && ( + + )} + {(queryResponse?.isError || queryResponse?.error) && ( + + {' '} + {queryResponse.error.message || t('preview_chart_unexpected_error')} + + )} + + {chartData && !queryResponse.isError && ( -
- )} + )} +
); } From 4d64f1dedecb588779a2cf6dabddbe1b2fc10aaa Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 8 Jul 2024 19:25:50 +0530 Subject: [PATCH 078/281] chore: better logging for duplicate keyboard shortcuts (#5425) * chore: better logging for duplicate keyboard shortcuts * chore: skip flaky test * fix: make the shortcut error silent in prod --- frontend/src/hooks/hotkeys/useKeyboardHotkeys.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/hooks/hotkeys/useKeyboardHotkeys.tsx b/frontend/src/hooks/hotkeys/useKeyboardHotkeys.tsx index 68e1bc7ae4..12b70fa68e 100644 --- a/frontend/src/hooks/hotkeys/useKeyboardHotkeys.tsx +++ b/frontend/src/hooks/hotkeys/useKeyboardHotkeys.tsx @@ -90,8 +90,14 @@ function KeyboardHotkeysProvider({ (keyCombination: string, callback: () => void): void => { if (!shortcuts.current[keyCombination]) { shortcuts.current[keyCombination] = callback; + } else if (process.env.NODE_ENV === 'development') { + throw new Error( + `This shortcut is already present in current scope :- ${keyCombination}`, + ); } else { - throw new Error('This shortcut is already present in current scope'); + console.error( + `This shortcut is already present in current scope :- ${keyCombination}`, + ); } }, [shortcuts], From 79eef5bb919249777e0eaf43daff1bbdabcd0fe5 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 8 Jul 2024 19:27:02 +0530 Subject: [PATCH 079/281] fix: clickhouse editor cursor sync issue (#5435) --- .../container/ListOfDashboard/ImportJSON/index.tsx | 10 +++++----- frontend/src/container/LogDetailedView/Overview.tsx | 12 +++++------- .../QuerySection/QueryBuilder/clickHouse/query.tsx | 8 +++++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx index 849305c7c2..5b95eb4a9a 100644 --- a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx +++ b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx @@ -141,11 +141,6 @@ function ImportJSON({ colors: { 'editor.background': Color.BG_INK_300, }, - fontFamily: 'Space Mono', - fontSize: 20, - fontWeight: 'normal', - lineHeight: 18, - letterSpacing: -0.06, }); } @@ -233,6 +228,11 @@ function ImportJSON({ fontFamily: 'Space Mono', }} theme={isDarkMode ? 'my-theme' : 'light'} + onMount={(_, monaco): void => { + document.fonts.ready.then(() => { + monaco.editor.remeasureFonts(); + }); + }} // eslint-disable-next-line react/jsx-no-bind beforeMount={setEditorTheme} /> diff --git a/frontend/src/container/LogDetailedView/Overview.tsx b/frontend/src/container/LogDetailedView/Overview.tsx index 957aac5bf7..bd54524931 100644 --- a/frontend/src/container/LogDetailedView/Overview.tsx +++ b/frontend/src/container/LogDetailedView/Overview.tsx @@ -53,7 +53,6 @@ function Overview({ enabled: false, }, fontWeight: 400, - // fontFamily: 'SF Mono', fontFamily: 'Space Mono', fontSize: 13, lineHeight: '18px', @@ -80,12 +79,6 @@ function Overview({ colors: { 'editor.background': Color.BG_INK_400, }, - // fontFamily: 'SF Mono', - fontFamily: 'Space Mono', - fontSize: 12, - fontWeight: 'normal', - lineHeight: 18, - letterSpacing: -0.06, }); } @@ -124,6 +117,11 @@ function Overview({ onChange={(): void => {}} height="20vh" theme={isDarkMode ? 'my-theme' : 'light'} + onMount={(_, monaco): void => { + document.fonts.ready.then(() => { + monaco.editor.remeasureFonts(); + }); + }} // eslint-disable-next-line react/jsx-no-bind beforeMount={setEditorTheme} /> diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/query.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/query.tsx index 7dd61595d6..bf6925fea5 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/query.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/query.tsx @@ -87,9 +87,6 @@ function ClickHouseQueryBuilder({ 'editor.background': Color.BG_INK_300, }, }); - document.fonts.ready.then(() => { - monaco.editor.remeasureFonts(); - }); } return ( @@ -105,6 +102,11 @@ function ClickHouseQueryBuilder({ height="200px" onChange={handleUpdateEditor} value={queryData.query} + onMount={(_, monaco): void => { + document.fonts.ready.then(() => { + monaco.editor.remeasureFonts(); + }); + }} options={{ scrollbar: { alwaysConsumeMouseWheel: false, From e6eaaa660a702d712085c3eef7e0e878afc2712f Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:50:29 +0530 Subject: [PATCH 080/281] feat: added invite team member from onboarding flow (#5410) * feat: added invite team member from onboarding flow * feat: removed commented code and added text to strings-translations * feat: added en-gb strings * feat: added more text to strings * feat: removed commented code and app.ts changes * feat: added test case for onboarding and invite flow * feat: added invite team member logEvents * feat: resovled comments * feat: cdoe refactor and test case changes --- frontend/jest.config.ts | 1 + frontend/public/locales/en-GB/onboarding.json | 8 + frontend/public/locales/en/onboarding.json | 8 + .../Onboarding.styles.scss | 119 +++++++++++- .../OnboardingContainer.tsx | 91 ++++++--- .../__test__/InviteUserFlow.test.tsx | 127 ++++++++++++ .../ModuleStepsContainer.styles.scss | 36 ++++ .../ModuleStepsContainer.tsx | 67 ++++--- .../InviteTeamMembers/index.tsx | 2 +- .../InviteUserModal/InviteUserModal.tsx | 182 ++++++++++++++++++ .../PendingInvitesContainer/index.tsx | 88 +-------- .../mocks-server/__mockdata__/invite_user.ts | 25 +++ frontend/src/mocks-server/handlers.ts | 8 + 13 files changed, 623 insertions(+), 139 deletions(-) create mode 100644 frontend/public/locales/en-GB/onboarding.json create mode 100644 frontend/public/locales/en/onboarding.json create mode 100644 frontend/src/container/OnboardingContainer/__test__/InviteUserFlow.test.tsx create mode 100644 frontend/src/container/OrganizationSettings/InviteUserModal/InviteUserModal.tsx create mode 100644 frontend/src/mocks-server/__mockdata__/invite_user.ts diff --git a/frontend/jest.config.ts b/frontend/jest.config.ts index d7776b0034..122b309dae 100644 --- a/frontend/jest.config.ts +++ b/frontend/jest.config.ts @@ -9,6 +9,7 @@ const config: Config.InitialOptions = { modulePathIgnorePatterns: ['dist'], moduleNameMapper: { '\\.(css|less|scss)$': '/__mocks__/cssMock.ts', + '\\.md$': '/__mocks__/cssMock.ts', }, globals: { extensionsToTreatAsEsm: ['.ts'], diff --git a/frontend/public/locales/en-GB/onboarding.json b/frontend/public/locales/en-GB/onboarding.json new file mode 100644 index 0000000000..573282687e --- /dev/null +++ b/frontend/public/locales/en-GB/onboarding.json @@ -0,0 +1,8 @@ +{ + "invite_user": "Invite your teammates", + "invite": "Invite", + "skip": "Skip", + "invite_user_helper_text": "Not the right person to get started? No worries! Invite someone who can.", + "select_use_case": "Select a use-case to get started", + "get_started": "Get Started" +} diff --git a/frontend/public/locales/en/onboarding.json b/frontend/public/locales/en/onboarding.json new file mode 100644 index 0000000000..573282687e --- /dev/null +++ b/frontend/public/locales/en/onboarding.json @@ -0,0 +1,8 @@ +{ + "invite_user": "Invite your teammates", + "invite": "Invite", + "skip": "Skip", + "invite_user_helper_text": "Not the right person to get started? No worries! Invite someone who can.", + "select_use_case": "Select a use-case to get started", + "get_started": "Get Started" +} diff --git a/frontend/src/container/OnboardingContainer/Onboarding.styles.scss b/frontend/src/container/OnboardingContainer/Onboarding.styles.scss index e81679d143..007c2d1f7f 100644 --- a/frontend/src/container/OnboardingContainer/Onboarding.styles.scss +++ b/frontend/src/container/OnboardingContainer/Onboarding.styles.scss @@ -1,16 +1,6 @@ .container { width: 100%; - // max-width: 1440px; margin: 0 auto; - - &.darkMode { - } - - &.lightMode { - .onboardingHeader { - color: #1d1d1d; - } - } } .moduleSelectContainer { @@ -61,6 +51,8 @@ width: 300px; transition: 0.3s; + background-color: #000; + .ant-card-body { padding: 0px; } @@ -80,6 +72,9 @@ overflow: hidden; text-overflow: ellipsis; text-align: center; + + border-bottom: 1px solid #303030; + background-color: var(--bg-ink-400); } .moduleStyles.selected { @@ -157,3 +152,107 @@ padding: 12px; margin: 24px 0; } + +.invite-member-wrapper { + display: flex; + justify-content: center; + align-items: center; + margin: 32px 0; + flex-direction: column; + gap: 12px; + + .invite-member { + display: flex; + width: 480px; + height: 64px; + padding: 16px; + justify-content: space-between; + align-items: center; + flex-shrink: 0; + border-radius: 4px; + border: 1px solid var(--bg-slate-500); + background: var(--bg-ink-400); + + .ant-typography { + color: var(--bg-vanilla-400); + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; + } + + > button { + display: flex; + align-items: center; + border-radius: 2px; + } + } +} + +.onboarding-page { + display: flex; + flex-direction: column; + height: 100%; + align-items: center; + justify-content: space-between; +} + +.skip-to-console { + color: var(--bg-vanilla-400); + font-family: Inter; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; + position: absolute; + top: 40px; + right: 40px; + cursor: pointer; + + &:hover { + color: var(--bg-vanilla-200); + } +} + +.lightMode { + .invite-member-wrapper { + .invite-member { + border: 1px solid var(--bg-vanilla-200); + background: var(--bg-vanilla-100); + + .ant-typography { + color: var(--bg-slate-200); + } + } + } + + .skip-to-console { + color: var(--bg-slate-200); + + &:hover { + color: var(--bg-slate-200); + } + } +} + +.lightMode { + .container { + .onboardingHeader { + color: var(--bg-slate-200); + } + } + + .moduleStyles { + background-color: var(--bg-vanilla-100); + } + + .moduleTitleStyle { + border-bottom: 1px solid var(--bg-vanilla-300); + background-color: var(--bg-vanilla-100); + } + + .moduleDesc { + background-color: var(--bg-vanilla-100); + } +} diff --git a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx index 5383f459f9..d1f89b0762 100644 --- a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx +++ b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx @@ -3,15 +3,19 @@ import './Onboarding.styles.scss'; import { ArrowRightOutlined } from '@ant-design/icons'; -import { Button, Card, Typography } from 'antd'; +import { Button, Card, Form, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import getIngestionData from 'api/settings/getIngestionData'; import cx from 'classnames'; import ROUTES from 'constants/routes'; import FullScreenHeader from 'container/FullScreenHeader/FullScreenHeader'; +import InviteUserModal from 'container/OrganizationSettings/InviteUserModal/InviteUserModal'; +import { InviteMemberFormValues } from 'container/OrganizationSettings/PendingInvitesContainer'; import useAnalytics from 'hooks/analytics/useAnalytics'; -import { useIsDarkMode } from 'hooks/useDarkMode'; import history from 'lib/history'; -import { useEffect, useState } from 'react'; +import { UserPlus } from 'lucide-react'; +import { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { useEffectOnce } from 'react-use'; @@ -100,9 +104,9 @@ export default function Onboarding(): JSX.Element { const [selectedModuleSteps, setSelectedModuleSteps] = useState(APM_STEPS); const [activeStep, setActiveStep] = useState(1); const [current, setCurrent] = useState(0); - const isDarkMode = useIsDarkMode(); const { trackEvent } = useAnalytics(); const { location } = history; + const { t } = useTranslation(['onboarding']); const { selectedDataSource, @@ -279,13 +283,38 @@ export default function Onboarding(): JSX.Element { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const [form] = Form.useForm(); + const [ + isInviteTeamMemberModalOpen, + setIsInviteTeamMemberModalOpen, + ] = useState(false); + + const toggleModal = useCallback( + (value: boolean): void => { + setIsInviteTeamMemberModalOpen(value); + if (!value) { + form.resetFields(); + } + }, + [form], + ); + return ( -
+
{activeStep === 1 && ( - <> +
+
{ + logEvent('Onboarding V2: Skip Button Clicked', {}); + history.push('/'); + }} + className="skip-to-console" + > + {t('skip')} +
-

Select a use-case to get started

+

{t('select_use_case')}

@@ -298,26 +327,13 @@ export default function Onboarding(): JSX.Element { 'moduleStyles', selectedModule.id === selectedUseCase.id ? 'selected' : '', )} - style={{ - backgroundColor: isDarkMode ? '#000' : '#FFF', - }} key={selectedUseCase.id} onClick={(): void => handleModuleSelect(selectedUseCase)} > - + {selectedUseCase.title} - + {selectedUseCase.desc} @@ -327,10 +343,31 @@ export default function Onboarding(): JSX.Element {
- +
+ + {t('invite_user_helper_text')} + +
+ {t('invite_user')} + +
+
+
)} {activeStep > 1 && ( @@ -345,9 +382,15 @@ export default function Onboarding(): JSX.Element { }} selectedModule={selectedModule} selectedModuleSteps={selectedModuleSteps} + setIsInviteTeamMemberModalOpen={setIsInviteTeamMemberModalOpen} />
)} +
); } diff --git a/frontend/src/container/OnboardingContainer/__test__/InviteUserFlow.test.tsx b/frontend/src/container/OnboardingContainer/__test__/InviteUserFlow.test.tsx new file mode 100644 index 0000000000..91d370e9ed --- /dev/null +++ b/frontend/src/container/OnboardingContainer/__test__/InviteUserFlow.test.tsx @@ -0,0 +1,127 @@ +/* eslint-disable sonarjs/no-identical-functions */ +import { queryByAttribute, waitFor } from '@testing-library/react'; +import { fireEvent, render, screen, within } from 'tests/test-utils'; + +import OnboardingContainer from '..'; +import { OnboardingContextProvider } from '../context/OnboardingContext'; + +jest.mock('react-markdown', () => jest.fn()); +jest.mock('rehype-raw', () => jest.fn()); + +const successNotification = jest.fn(); +jest.mock('hooks/useNotifications', () => ({ + __esModule: true, + useNotifications: jest.fn(() => ({ + notifications: { + success: successNotification, + error: jest.fn(), + }, + })), +})); + +window.analytics = { + track: jest.fn(), +}; + +describe('Onboarding invite team member flow', () => { + it('initial render and get started page', async () => { + const { findByText } = render( + + + , + ); + + await expect(findByText('SigNoz')).resolves.toBeInTheDocument(); + + // Check all the option present + const monitoringTexts = [ + { + title: 'Application Monitoring', + description: + 'Monitor application metrics like p99 latency, error rates, external API calls, and db calls.', + }, + { + title: 'Logs Management', + description: + 'Easily filter and query logs, build dashboards and alerts based on attributes in logs', + }, + { + title: 'Infrastructure Monitoring', + description: + 'Monitor Kubernetes infrastructure metrics, hostmetrics, or metrics of any third-party integration', + }, + { + title: 'AWS Monitoring', + description: + 'Monitor your traces, logs and metrics for AWS services like EC2, ECS, EKS etc.', + }, + { + title: 'Azure Monitoring', + description: + 'Monitor your traces, logs and metrics for Azure services like AKS, Container Apps, App Service etc.', + }, + ]; + + monitoringTexts.forEach(async ({ title, description }) => { + await expect(findByText(title)).resolves.toBeInTheDocument(); + await expect(findByText(description)).resolves.toBeInTheDocument(); + }); + + // Invite team member button + await expect(findByText('invite')).resolves.toBeInTheDocument(); + }); + + it('invite team member', async () => { + const { findByText } = render( + + + , + ); + + // Invite team member button + const inviteBtn = await findByText('invite'); + expect(inviteBtn).toBeInTheDocument(); + + fireEvent.click(inviteBtn); + const inviteModal = await screen.findByTestId('invite-team-members-modal'); + expect(inviteModal).toBeInTheDocument(); + + const inviteModalTitle = await within(inviteModal).findAllByText( + /invite_team_members/i, + ); + expect(inviteModalTitle[0]).toBeInTheDocument(); + + // Verify that the invite modal contains an input field for entering the email address + const emailInput = within(inviteModal).getByText('email_address'); + expect(emailInput).toBeInTheDocument(); + + // Verify that the invite modal contains a dropdown for selecting the role + const role = within(inviteModal).getByText('role'); + expect(role).toBeInTheDocument(); + + // Verify that the invite modal contains a button for sending the invitation + const sendButton = within(inviteModal).getByTestId( + 'invite-team-members-button', + ); + expect(sendButton).toBeInTheDocument(); + + // Verify that the invite modal sends the invitation + fireEvent.input(queryByAttribute('id', inviteModal, 'members_0_email')!, { + target: { value: 'test@example.com' }, + }); + expect( + queryByAttribute('value', inviteModal, 'test@example.com'), + ).toBeInTheDocument(); + + const roleDropdown = within(inviteModal).getByTestId('role-select'); + expect(roleDropdown).toBeInTheDocument(); + + fireEvent.click(sendButton); + + await waitFor(() => + expect(successNotification).toHaveBeenCalledWith({ + message: 'Invite sent successfully', + }), + ); + }); +}); diff --git a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.styles.scss b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.styles.scss index 02972209dd..dbbb6a9baf 100644 --- a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.styles.scss +++ b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.styles.scss @@ -39,6 +39,9 @@ .steps-container { width: 20%; height: 100%; + display: flex; + flex-direction: column; + justify-content: space-between; .steps-container-header { display: flex; @@ -69,6 +72,30 @@ } } } + + .invite-user-btn { + display: flex; + width: 170px; + height: 32px; + padding: 6px; + justify-content: center; + align-items: center; + border-radius: 2px; + margin-bottom: 31px; + border: 1px solid var(--bg-slate-400); + background: var(--bg-ink-300); + box-shadow: none; + + .ant-typography { + color: var(--bg-vanilla-400); + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 10px; + letter-spacing: 0.12px; + } + } } .selected-step-content { @@ -196,5 +223,14 @@ } } } + + .invite-user-btn { + border: 1px solid var(--bg-vanilla-300); + background: var(--bg-vanilla-100); + + .ant-typography { + color: var(--bg-slate-200); + } + } } } diff --git a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx index bc983c9ee8..28890e4d5a 100644 --- a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx +++ b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx @@ -18,8 +18,8 @@ import { hasFrameworks } from 'container/OnboardingContainer/utils/dataSourceUti import useAnalytics from 'hooks/analytics/useAnalytics'; import history from 'lib/history'; import { isEmpty, isNull } from 'lodash-es'; -import { HelpCircle } from 'lucide-react'; -import { useState } from 'react'; +import { HelpCircle, UserPlus } from 'lucide-react'; +import { SetStateAction, useState } from 'react'; import { useOnboardingContext } from '../../context/OnboardingContext'; import { @@ -33,6 +33,7 @@ interface ModuleStepsContainerProps { onReselectModule: any; selectedModule: ModuleProps; selectedModuleSteps: SelectedModuleStepProps[]; + setIsInviteTeamMemberModalOpen: (value: SetStateAction) => void; } interface MetaDataProps { @@ -63,6 +64,7 @@ export default function ModuleStepsContainer({ onReselectModule, selectedModule, selectedModuleSteps, + setIsInviteTeamMemberModalOpen, }: ModuleStepsContainerProps): JSX.Element { const { activeStep, @@ -409,32 +411,47 @@ Thanks return (
-
-
- SigNoz +
+
+
+ SigNoz -
SigNoz
+
SigNoz
+
-
- - - - - + + + + + +
+
diff --git a/frontend/src/container/OrganizationSettings/InviteTeamMembers/index.tsx b/frontend/src/container/OrganizationSettings/InviteTeamMembers/index.tsx index 778e792085..2ee7aca4c1 100644 --- a/frontend/src/container/OrganizationSettings/InviteTeamMembers/index.tsx +++ b/frontend/src/container/OrganizationSettings/InviteTeamMembers/index.tsx @@ -50,7 +50,7 @@ function InviteTeamMembers({ form, onFinish }: Props): JSX.Element { - + ADMIN VIEWER EDITOR diff --git a/frontend/src/container/OrganizationSettings/InviteUserModal/InviteUserModal.tsx b/frontend/src/container/OrganizationSettings/InviteUserModal/InviteUserModal.tsx new file mode 100644 index 0000000000..ad158adaae --- /dev/null +++ b/frontend/src/container/OrganizationSettings/InviteUserModal/InviteUserModal.tsx @@ -0,0 +1,182 @@ +import { Button, Form, Modal } from 'antd'; +import { FormInstance } from 'antd/lib'; +import getPendingInvites from 'api/user/getPendingInvites'; +import sendInvite from 'api/user/sendInvite'; +import ROUTES from 'constants/routes'; +import { useNotifications } from 'hooks/useNotifications'; +import { + Dispatch, + SetStateAction, + useCallback, + useEffect, + useState, +} from 'react'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { PayloadProps } from 'types/api/user/getPendingInvites'; +import AppReducer from 'types/reducer/app'; +import { ROLES } from 'types/roles'; + +import InviteTeamMembers from '../InviteTeamMembers'; +import { InviteMemberFormValues } from '../PendingInvitesContainer'; + +export interface InviteUserModalProps { + isInviteTeamMemberModalOpen: boolean; + toggleModal: (value: boolean) => void; + form: FormInstance; + setDataSource?: Dispatch>; + shouldCallApi?: boolean; +} + +interface DataProps { + key: number; + name: string; + email: string; + accessLevel: ROLES; + inviteLink: string; +} + +function InviteUserModal(props: InviteUserModalProps): JSX.Element { + const { + isInviteTeamMemberModalOpen, + toggleModal, + form, + setDataSource, + shouldCallApi = false, + } = props; + const { notifications } = useNotifications(); + const { t } = useTranslation(['organizationsettings', 'common']); + const { user } = useSelector((state) => state.app); + const [isInvitingMembers, setIsInvitingMembers] = useState(false); + const [modalForm] = Form.useForm(form); + + const getPendingInvitesResponse = useQuery({ + queryFn: getPendingInvites, + queryKey: ['getPendingInvites', user?.accessJwt], + enabled: shouldCallApi, + }); + + const getParsedInviteData = useCallback( + (payload: PayloadProps = []) => + payload?.map((data) => ({ + key: data.createdAt, + name: data?.name, + email: data.email, + accessLevel: data.role, + inviteLink: `${window.location.origin}${ROUTES.SIGN_UP}?token=${data.token}`, + })), + [], + ); + + useEffect(() => { + if ( + getPendingInvitesResponse.status === 'success' && + getPendingInvitesResponse?.data?.payload + ) { + const data = getParsedInviteData( + getPendingInvitesResponse?.data?.payload || [], + ); + setDataSource?.(data); + } + }, [ + getParsedInviteData, + getPendingInvitesResponse?.data?.payload, + getPendingInvitesResponse.status, + setDataSource, + ]); + + const onInviteClickHandler = useCallback( + async (values: InviteMemberFormValues): Promise => { + try { + setIsInvitingMembers?.(true); + values?.members?.forEach( + async (member): Promise => { + const { error, statusCode } = await sendInvite({ + email: member.email, + name: member?.name, + role: member.role, + frontendBaseUrl: window.location.origin, + }); + + if (statusCode !== 200) { + notifications.error({ + message: + error || + t('something_went_wrong', { + ns: 'common', + }), + }); + } else if (statusCode === 200) { + notifications.success({ + message: 'Invite sent successfully', + }); + } + }, + ); + + setTimeout(async () => { + const { data, status } = await getPendingInvitesResponse.refetch(); + if (status === 'success' && data.payload) { + setDataSource?.(getParsedInviteData(data?.payload || [])); + } + setIsInvitingMembers?.(false); + toggleModal(false); + }, 2000); + } catch (error) { + notifications.error({ + message: t('something_went_wrong', { + ns: 'common', + }), + }); + } + }, + [ + getParsedInviteData, + getPendingInvitesResponse, + notifications, + setDataSource, + setIsInvitingMembers, + t, + toggleModal, + ], + ); + + return ( + toggleModal(false)} + centered + data-testid="invite-team-members-modal" + destroyOnClose + footer={[ + , + , + ]} + > + + + ); +} + +InviteUserModal.defaultProps = { + setDataSource: (): void => {}, + shouldCallApi: false, +}; + +export default InviteUserModal; diff --git a/frontend/src/container/OrganizationSettings/PendingInvitesContainer/index.tsx b/frontend/src/container/OrganizationSettings/PendingInvitesContainer/index.tsx index 3e9276f596..7c4909ab8d 100644 --- a/frontend/src/container/OrganizationSettings/PendingInvitesContainer/index.tsx +++ b/frontend/src/container/OrganizationSettings/PendingInvitesContainer/index.tsx @@ -1,9 +1,8 @@ import { PlusOutlined } from '@ant-design/icons'; -import { Button, Form, Modal, Space, Typography } from 'antd'; +import { Button, Form, Space, Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import deleteInvite from 'api/user/deleteInvite'; import getPendingInvites from 'api/user/getPendingInvites'; -import sendInvite from 'api/user/sendInvite'; import { ResizeTable } from 'components/ResizeTable'; import { INVITE_MEMBERS_HASH } from 'constants/app'; import ROUTES from 'constants/routes'; @@ -19,7 +18,7 @@ import { PayloadProps } from 'types/api/user/getPendingInvites'; import AppReducer from 'types/reducer/app'; import { ROLES } from 'types/roles'; -import InviteTeamMembers from '../InviteTeamMembers'; +import InviteUserModal from '../InviteUserModal/InviteUserModal'; import { TitleWrapper } from './styles'; function PendingInvitesContainer(): JSX.Element { @@ -28,7 +27,6 @@ function PendingInvitesContainer(): JSX.Element { setIsInviteTeamMemberModalOpen, ] = useState(false); const [form] = Form.useForm(); - const [isInvitingMembers, setIsInvitingMembers] = useState(false); const { t } = useTranslation(['organizationsettings', 'common']); const [state, setText] = useCopyToClipboard(); const { notifications } = useNotifications(); @@ -191,83 +189,15 @@ function PendingInvitesContainer(): JSX.Element { }, ]; - const onInviteClickHandler = useCallback( - async (values: InviteMemberFormValues): Promise => { - try { - setIsInvitingMembers(true); - values.members.forEach( - async (member): Promise => { - const { error, statusCode } = await sendInvite({ - email: member.email, - name: member.name, - role: member.role, - frontendBaseUrl: window.location.origin, - }); - - if (statusCode !== 200) { - notifications.error({ - message: - error || - t('something_went_wrong', { - ns: 'common', - }), - }); - } - }, - ); - - setTimeout(async () => { - const { data, status } = await getPendingInvitesResponse.refetch(); - if (status === 'success' && data.payload) { - setDataSource(getParsedInviteData(data?.payload || [])); - } - setIsInvitingMembers(false); - toggleModal(false); - }, 2000); - } catch (error) { - notifications.error({ - message: t('something_went_wrong', { - ns: 'common', - }), - }); - } - }, - [ - getParsedInviteData, - getPendingInvitesResponse, - notifications, - t, - toggleModal, - ], - ); - return (
- toggleModal(false)} - centered - destroyOnClose - footer={[ - , - , - ]} - > - - + diff --git a/frontend/src/mocks-server/__mockdata__/invite_user.ts b/frontend/src/mocks-server/__mockdata__/invite_user.ts new file mode 100644 index 0000000000..3b736df977 --- /dev/null +++ b/frontend/src/mocks-server/__mockdata__/invite_user.ts @@ -0,0 +1,25 @@ +export const inviteUser = { + status: 'success', + data: { + statusCode: 200, + error: null, + payload: [ + { + email: 'jane@doe.com', + name: 'Jane', + token: 'testtoken', + createdAt: 1715741587, + role: 'VIEWER', + organization: 'test', + }, + { + email: 'test+in@singoz.io', + name: '', + token: 'testtoken1', + createdAt: 1720095913, + role: 'VIEWER', + organization: 'test', + }, + ], + }, +}; diff --git a/frontend/src/mocks-server/handlers.ts b/frontend/src/mocks-server/handlers.ts index 4e48a4a908..1814d215f7 100644 --- a/frontend/src/mocks-server/handlers.ts +++ b/frontend/src/mocks-server/handlers.ts @@ -1,6 +1,7 @@ import { rest } from 'msw'; import { billingSuccessResponse } from './__mockdata__/billing'; +import { inviteUser } from './__mockdata__/invite_user'; import { licensesSuccessResponse } from './__mockdata__/licenses'; import { membersResponse } from './__mockdata__/members'; import { queryRangeSuccessResponse } from './__mockdata__/query_range'; @@ -89,4 +90,11 @@ export const handlers = [ rest.get('http://localhost/api/v1/billing', (req, res, ctx) => res(ctx.status(200), ctx.json(billingSuccessResponse)), ), + + rest.get('http://localhost/api/v1/invite', (_, res, ctx) => + res(ctx.status(200), ctx.json(inviteUser)), + ), + rest.post('http://localhost/api/v1/invite', (_, res, ctx) => + res(ctx.status(200), ctx.json(inviteUser)), + ), ]; From 9c9ed741b2a29f6310c4b0559f745203455bd2ea Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:02:10 +0530 Subject: [PATCH 081/281] feat: changed name from 'Histogram' to 'Frequency chart' (#5369) * feat: changed name from 'Histogram' to 'Frequence chart' * feat: cdoe refactor and test case changes * feat: added test case for frequency chart --- .../src/container/LogsExplorerViews/index.tsx | 6 +-- .../tests/LogsExplorerViews.test.tsx | 5 ++- .../ToolbarActions/LeftToolbarActions.tsx | 10 ++--- .../ToolbarActions/ToolbarActions.styles.scss | 2 +- .../tests/ToolbarActions.test.tsx | 14 +++---- .../__tests__/LogsExplorer.test.tsx | 39 +++++++++++++++++-- frontend/src/pages/LogsExplorer/index.tsx | 12 +++--- 7 files changed, 61 insertions(+), 27 deletions(-) diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx index ced6556f44..b95fd6e6c4 100644 --- a/frontend/src/container/LogsExplorerViews/index.tsx +++ b/frontend/src/container/LogsExplorerViews/index.tsx @@ -63,10 +63,10 @@ import { v4 } from 'uuid'; function LogsExplorerViews({ selectedView, - showHistogram, + showFrequencyChart, }: { selectedView: SELECTED_VIEWS; - showHistogram: boolean; + showFrequencyChart: boolean; }): JSX.Element { const { notifications } = useNotifications(); const history = useHistory(); @@ -561,7 +561,7 @@ function LogsExplorerViews({ return (
- {showHistogram && ( + {showFrequencyChart && ( - + diff --git a/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx b/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx index 487e85b785..d02a546f1f 100644 --- a/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx +++ b/frontend/src/container/QueryBuilder/components/ToolbarActions/LeftToolbarActions.tsx @@ -10,7 +10,7 @@ interface LeftToolbarActionsProps { selectedView: string; onToggleHistrogramVisibility: () => void; onChangeSelectedView: (view: SELECTED_VIEWS) => void; - showHistogram: boolean; + showFrequencyChart: boolean; } const activeTab = 'active-tab'; @@ -22,7 +22,7 @@ export default function LeftToolbarActions({ selectedView, onToggleHistrogramVisibility, onChangeSelectedView, - showHistogram, + showFrequencyChart, }: LeftToolbarActionsProps): JSX.Element { const { clickhouse, search, queryBuilder: QB } = items; @@ -71,11 +71,11 @@ export default function LeftToolbarActions({ )}
-
- Histogram +
+ Frequency chart diff --git a/frontend/src/container/QueryBuilder/components/ToolbarActions/ToolbarActions.styles.scss b/frontend/src/container/QueryBuilder/components/ToolbarActions/ToolbarActions.styles.scss index a29f031e37..a848cf8680 100644 --- a/frontend/src/container/QueryBuilder/components/ToolbarActions/ToolbarActions.styles.scss +++ b/frontend/src/container/QueryBuilder/components/ToolbarActions/ToolbarActions.styles.scss @@ -37,7 +37,7 @@ } } - .histogram-view-controller { + .frequency-chart-view-controller { display: flex; align-items: center; padding-left: 8px; diff --git a/frontend/src/container/QueryBuilder/components/ToolbarActions/tests/ToolbarActions.test.tsx b/frontend/src/container/QueryBuilder/components/ToolbarActions/tests/ToolbarActions.test.tsx index 414df975df..751bdbf99d 100644 --- a/frontend/src/container/QueryBuilder/components/ToolbarActions/tests/ToolbarActions.test.tsx +++ b/frontend/src/container/QueryBuilder/components/ToolbarActions/tests/ToolbarActions.test.tsx @@ -8,7 +8,7 @@ import RightToolbarActions from '../RightToolbarActions'; describe('ToolbarActions', () => { it('LeftToolbarActions - renders correctly with default props', async () => { const handleChangeSelectedView = jest.fn(); - const handleToggleShowHistogram = jest.fn(); + const handleToggleShowFrequencyChart = jest.fn(); const { queryByTestId } = render( { }} selectedView={SELECTED_VIEWS.SEARCH} onChangeSelectedView={handleChangeSelectedView} - onToggleHistrogramVisibility={handleToggleShowHistogram} - showHistogram + onToggleHistrogramVisibility={handleToggleShowFrequencyChart} + showFrequencyChart />, ); expect(screen.getByTestId('search-view')).toBeInTheDocument(); @@ -51,7 +51,7 @@ describe('ToolbarActions', () => { it('renders - clickhouse view and test histogram toggle', async () => { const handleChangeSelectedView = jest.fn(); - const handleToggleShowHistogram = jest.fn(); + const handleToggleShowFrequencyChart = jest.fn(); const { queryByTestId, getByRole } = render( { }} selectedView={SELECTED_VIEWS.QUERY_BUILDER} onChangeSelectedView={handleChangeSelectedView} - onToggleHistrogramVisibility={handleToggleShowHistogram} - showHistogram + onToggleHistrogramVisibility={handleToggleShowFrequencyChart} + showFrequencyChart />, ); @@ -88,7 +88,7 @@ describe('ToolbarActions', () => { expect(handleChangeSelectedView).toBeCalled(); await userEvent.click(getByRole('switch')); - expect(handleToggleShowHistogram).toBeCalled(); + expect(handleToggleShowFrequencyChart).toBeCalled(); }); it('RightToolbarActions - render correctly with props', async () => { diff --git a/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx b/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx index 7100a5a5b1..ff0f891333 100644 --- a/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx +++ b/frontend/src/pages/LogsExplorer/__tests__/LogsExplorer.test.tsx @@ -35,12 +35,14 @@ jest.mock( return
Time Series Chart
; }, ); + +const frequencyChartContent = 'Frequency chart content'; jest.mock( 'container/LogsExplorerChart', () => // eslint-disable-next-line func-names, @typescript-eslint/explicit-function-return-type, react/display-name function () { - return
Histogram Chart
; + return
{frequencyChartContent}
; }, ); @@ -83,13 +85,13 @@ describe('Logs Explorer Tests', () => { , ); - // check the presence of histogram chart - expect(getByText('Histogram Chart')).toBeInTheDocument(); + // check the presence of frequency chart content + expect(getByText(frequencyChartContent)).toBeInTheDocument(); // toggle the chart and check it gets removed from the DOM const histogramToggle = getByRole('switch'); await userEvent.click(histogramToggle); - expect(queryByText('Histogram Chart')).not.toBeInTheDocument(); + expect(queryByText(frequencyChartContent)).not.toBeInTheDocument(); // check the presence of search bar and query builder and absence of clickhouse const searchView = getByTestId('search-view'); @@ -229,4 +231,33 @@ describe('Logs Explorer Tests', () => { const aggrInterval = queryAllByText('AGGREGATION INTERVAL'); expect(aggrInterval.length).toBe(2); }); + + test('frequency chart visibility and switch toggle', async () => { + const { getByRole, queryByText } = render( + + + + + + , + + + + + , + ); + + // check the presence of Frequency Chart + expect(queryByText('Frequency chart')).toBeInTheDocument(); + + // check the default state of the histogram toggle + const histogramToggle = getByRole('switch'); + expect(histogramToggle).toBeInTheDocument(); + expect(histogramToggle).toBeChecked(); + expect(queryByText(frequencyChartContent)).toBeInTheDocument(); + + // toggle the chart and check it gets removed from the DOM + await userEvent.click(histogramToggle); + expect(queryByText(frequencyChartContent)).not.toBeInTheDocument(); + }); }); diff --git a/frontend/src/pages/LogsExplorer/index.tsx b/frontend/src/pages/LogsExplorer/index.tsx index d8f5f38804..d4422f58fe 100644 --- a/frontend/src/pages/LogsExplorer/index.tsx +++ b/frontend/src/pages/LogsExplorer/index.tsx @@ -16,15 +16,15 @@ import { WrapperStyled } from './styles'; import { SELECTED_VIEWS } from './utils'; function LogsExplorer(): JSX.Element { - const [showHistogram, setShowHistogram] = useState(true); + const [showFrequencyChart, setShowFrequencyChart] = useState(true); const [selectedView, setSelectedView] = useState( SELECTED_VIEWS.SEARCH, ); const { handleRunQuery, currentQuery } = useQueryBuilder(); - const handleToggleShowHistogram = (): void => { - setShowHistogram(!showHistogram); + const handleToggleShowFrequencyChart = (): void => { + setShowFrequencyChart(!showFrequencyChart); }; const handleChangeSelectedView = (view: SELECTED_VIEWS): void => { @@ -78,8 +78,8 @@ function LogsExplorer(): JSX.Element { items={toolbarViews} selectedView={selectedView} onChangeSelectedView={handleChangeSelectedView} - onToggleHistrogramVisibility={handleToggleShowHistogram} - showHistogram={showHistogram} + onToggleHistrogramVisibility={handleToggleShowFrequencyChart} + showFrequencyChart={showFrequencyChart} /> } rightActions={} @@ -96,7 +96,7 @@ function LogsExplorer(): JSX.Element {
From b0e355eb6440369b89e32583779aa577da369cd4 Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 9 Jul 2024 08:11:46 +0430 Subject: [PATCH 082/281] fix: properly render \n and \t in log details + apply Geist Mono font to the logs (#5347) * fix: properly render newline and tab in log details * fix: change font family and add tab size to properly render \t * feat: apply Geist Mono font to the logs --- frontend/public/fonts/GeistMonoVF.woff2 | Bin 0 -> 58048 bytes .../components/LogDetail/LogDetails.styles.scss | 2 +- .../src/components/Logs/RawLogView/styles.ts | 2 +- .../LogDetailedView/FieldRenderer.styles.scss | 2 +- .../src/container/LogDetailedView/JsonView.tsx | 2 +- .../src/container/LogDetailedView/Overview.tsx | 2 +- .../LogDetailedView/TableView.styles.scss | 2 ++ .../src/container/LogDetailedView/TableView.tsx | 8 +++++++- .../LogsExplorerChart.styled.ts | 2 +- frontend/src/styles.scss | 7 +++++++ 10 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 frontend/public/fonts/GeistMonoVF.woff2 diff --git a/frontend/public/fonts/GeistMonoVF.woff2 b/frontend/public/fonts/GeistMonoVF.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..fb2f024aca0a7bfd14a88b7fa74129f107a7d7ee GIT binary patch literal 58048 zcmV(_K-9l?Pew8T0RR910OG&^5dZ)H0qz_C0OD2v0W&iI00000000000000000000 z0000Qi3}UHEF7Bz24Fu^R6$gMI43RukUB4Z3WC`@f{;!ygP=MAHUcCAnLZ1QbN~b( z1&d$@zc*VD!jS}@KknbvRtp}an+%{}x6M?4i|YWJ4s7Fp~Bq^E_qo`=?!VBe` zv51DOm&fIF>)>i=eR$g_Y_|F(DVKePDN%ei=}(nZNtH)x4x;d}WE#O2#f##cVM^v{ zwq{2TJc&zuEBod6kxVyzP8cVQPcd$>24-X?^HiB&$?xxqfUi7`jNd?^FMWx7w<*_Y z9@}5*t?mE=8an;OyyAKseQS0yeSMc zHOvT#e@UD$X{f@I`%X+C#*Z}OFl{ZSEfi19#im5UflKaY-L4CnaJYfe2nIzak@ZtI z5ndyz3O$z@$GlVR9_tpc(|M8z@dUf+J*)lOH?E~{;&byjQDs@2-o*0Hv1spNE$ z-93>Bc_D;ViZWdE2F3v3-I=D(ymP%d<@)?b`&}W2g+t2Ptj!fe2W;dNqnetUn&juF zCL|BNoAt~@357z@QZaT*AseVH)w{0t)TDR6PfggC8a1PiR3HhFNd_fkt(Oh(MgRX= z>UUpJw;BbOOkp`_8Sn$rHkm%QN ztvlK5{(8b;p7{EU>o9{>G)Nn>^*R;6{aSqyy#6h~M{;rYkn7C$1c2KWaIc(~v1 zn_+o6l!E*0j#RIRS{!5~PTNO=O(GrJ2-geGG0Re_W8 zX;1U^w%5(|e-#S&RVWZuBqdaV5>X&&p+Lz(6(t)5IshOj10blSD7i;XduC*Ja(-&b zoz$tSNLc`7tpLb@lsZUqPlsr4v^OerR8L6xv>~5n_cVJt?ro2IOzCjA?Crk((|*4y z%)upDBN6(<6YCf@h*))t_mKvO9^%g=)->l0aa)2wF43gVr>d?voli9lVOpbMxbzI0 zt(T`cFT<4QWy0No2Op1L2u;#{6a<$}({v$4mrf}KM!J+f-&cWRsW6bok#P`w=&QUnucyk$_I;URiqEmE?@1be0J~fek=USVB?!ueH?^rD}Tre22h7#mI5N;L@?#E4rR4(ZEPUkaT zb+;~{#QhTz=fW7qFM;zj*9%+U0HC<8r$! zB1W4ch6o{7_ps zXE-v-WF*ZjWVS3MTRu`~A9BD6q{=Tyt>;LGE+E#cLQ!WDiZ;0@?y-R_PR^O2v&277 z%9cpg3JI-~&LA0x3k{i|dGTmT7KN5&pw)S3LlN3qiT1{YJ~To5o6%=6K_^e3a9rq1 z7y70F^!*)#XiFa8^C*uIarv^JDh8{eoPZSR{j&NCCRQ zFd#Zp>oU)19qR0E$6ARAzK41k^_ameP=yKBpIU=jXD-xBF4W5!e6P0N1#STmXn`a= z#Cr%ds>#P896$vy0emoQ9A+YLr<+V742^Me0SqT+*tx{xKKbksG&>zIAndcfWY%iH z)mM2v6{MNr^Q^Qa2t;;Ed}Q7UJ4_swOVLuF_ykd4+4%!l0-Kg;=v$PM4{c>&dC1xW zfw*E8t3#2ZR@rQ%SS<&u3RX<2m1ZEY?l4lGU_qu4bBMBQmPvPuR;f8uY^U>AB!H#U z`jlRoDGGMyj%*2EsDP2>c7VeuE*z%SkXUhTU?0-Dt(>_xt1}H*TBEXpBKwIJl6P*L zld21)SUZM?Jf;Zq%$$leVrXX`N343o)r8B14C$A4HnqWm8MEdHZ_?KTMxqeJv1?z~Yy!oD?;#SP zmIw$XyT%=%g7|CIn@iwE2gJ&osKw=u*?Cjb=)9$4t-Q6`Twbs21YKRY>-`&`WAhKX z4@-dlA(L4MOyryq2d(`}*~(GEXr73e#OkwUWCV7!=^py-q+Rar+EcA>ojaXM8?4|<^zMk^L@+K=hX)0)vEUI1^t{wd%uoR0_tg-SA9yb;n2@wsy*IPX_Ky6CzZH#NDS z*`ID`3*lZkO3@KhpCmS~U*5^(wk6h1#XgmVt?YrU9xbKW`2SX=q(Aj;w)M9*=TuwT zmg>G9Ms?#bx%qM2hm*UQJ`e~50)apv5C{ZPqya$?G&KHRg#d*R0uZEB3&MlFA4EGo z~Ng_KIZ^e06>FQK)d%5 z06I0qez@wB^}6@~T|A1|Qg)J8B&Qj`#jiMmCvw8l>5^IPzxKsqjVW=;di~ix&JQf} zz{!iAGhL!L^op5qkO2_On_QQDi@_kvw=#b?`;P9@F%Yodls6}(4xI|rjhm-_Bkllf6wbLx4Lfj{vjJT?atTS!-!*N4tvrqC;5k+L z8!BFWRF+df?*xC~mqGF6qvq{132baEppU*weqQ?{IK8?=^TDH2$MJx@>>qp>JZRi% z2Y~#kXudSiUn@w)kRIgM)?Fl@16a_cij>y@m-tz&?(apMPXjykJIR{}|(VVJte;DD^Hv|wI&!@;2IMuHK(P41O;h3Y# zW6l=={hQs3J8C}FNh>yC`%N+oC^QYXK6|Rt9`8F`mZifB)K5v7fsPJ9-%)9nj#UDP z((ioH-|>=c>S58ZyhocTk6Vx1Ujrw|TwRqyGX4m_gtH^>Pc#q!DNk2`Pfu^gl>7U- z$8tvCQ_J;gXD$VTL(M?R3~LwRu-3NAn#L!x2Xm#1YhI1{{70>hl5) z|2FUZGpHL1UJ?$x*f%Zp6FZ2Gz+=<2s6!Db%_AyPKT!6}@JJyM6?HxXW;Z~uPo z3#6C$KBT!~XFw>R(1#(VWd8_xs=;O&>A(pkH{A2DjAk6{+c}B3p};(A{lYTgObwQbu8GHH#dRn$~#wBGYGZM@!t@>;*y7k=DSTRU1Zv6fsJDQ~Wu zhIUMY@0h3R;Qqi<)y$J=?>fRM^wxCAOObh%=UJ}~j&rhlX95a+F^OsU`)A6Hk;fMR zfJzMFbOlH+8j;-%#Or3Pkel93iH(qs949C{A7798p631|P7!B|c_ZNQuCZfc$H(Tx zl4HejzPO55$OJsfu?RdwJ>}SUbqSgzE+&+*T5D`gYzfiUc?V=WW$R_jmfyLeW3r2} zBXeJkuZnMueI~rZ6tUjQAMOPv)G=n81Hvi)Fe5j1=It-vmTtV3^x6VZE3ajb?cQzm zX0{98fhjhN_j_zFZJRdB5UvALZPvTa9_Pm+I==a_XLxS}?VeTuc9(6NoJVd8b*S2u zZdLjxZJ%x5;}S578nJU87ys!Z)B`Z|)KTXq9p2NMriQ68I-+L`9a)c<#pUm26zqP} zomByBYk|>%fwWM!*YjP>jkep1i;tECvJo)8`NpiK*~waiqO@m|0pJ0l6FXzV>nvu% zb_`%*c#t)-+J#jUNc8^o+bEneIh_(F*Fe*>A4X2DDp!vU2PNACXye{Cu9k(CvOPxn zd6#9(rUj5um6R7sE+8>?Mc0)a(H=bemdV-dEf~S>&nY{)wgxMe2aZk(qg75a`RNy( z?(~A)#pWRMEaE-CS~mlX`{0sg%qkr#V5;^HNA_<#Anc!q3NSqahQ&^}vQzt>FzOjc z^jre%UXU<>U=q{Q>n@B5gV^v`odY$TqJYZUV{<@IS8y_UDn`%@>G2Yq&0|sC@Z!!9br32VLb# ztIeq#IVp{&U*&riU0}Zj@QvlOt=;=a+Pl*Pi0oDwW>tXL2#AulC-!Q8+;3X#{kx=>DRLr(r>%^?6dX(%!U$+LJ0nRKy2n}#10YbwGJe(QTXq%<}s@IGWu<0;5>s z2Z6h`0KAGJu66QU4oJ%IbYB8^j-WZl)aN7va(kSR7bMJIK_OOSz*I-c^Ig@*AU1@? zjFE}dACP7$k;3-V3643z&OP@jLL`D?^71OQk)()jBJverL;x|0Uov$!2`-G1Voy?e zeuk0@=PEKKYJnq1)rITKl+&WvF99bC36vpB*kQHle=A$Hgy9>OZ0JyAB zs-;u;L#SoFssJI_7^0R>)k{Mqwd52n24Qfq1<6BirCL#49>vA-n^smF{;Wo#j6`Uh zF_Or$F$WREkU;^*l0g$~Uxm|JHK7TB%})oN*j8Ilbt;~puJpp$3eGRJAJt4{1jN>C zR#%hPhAnvoSOp`+^&bJ&%)AX)JCI$@dBFO3OlvHo8_Y(=tQ9!rfOV$SIjaGi=+^9F zK*?2NDpW!yJ+(7pkowD9k*sZafO`HUEx;Sa55T3L;l_|ToDve(QNmvUa!hhuG0L7B zGvep%FMvB38a4d{C;?BC^)&7X{yr38>}^z+RozuN2%o1m=du!9KLeJRckrU^@j zP}Za7T;n;Cm93`xZPJjh6B(S=u&)Pr7eM_^Ru=)-FP0HNzbdy91<Joxz>M zYkMp|u5YjZ-sfcj`?^iEpxrPxkj}rBrKl^Vx#GIp8Z~S6$Wwo7*Wta+FcOnMby1Ky zlaiLK;8PHC=wXK+apcj(97jAUNmc68nvV2lFw1@`N^VSP%HIdE%)uPZ$(+v5IhQH( zQtERv4QYDc<3CS%l4toh9r-xXkcAHD(VM*dEY{%gD5Gdro895m3!+Rcw?}YjPAn)x zk#)#=WCOAhDMnTztB}>0H8vq@krHGx5{eWeMaULpDT2QSEJs!#laZ;&R-_c!hLj=S z;W9LYFtK=rg{4Q>SiHl*(lgv^qZKa-EWY7m(|;`lSo|Zz5)cuVUJ;{2qr{-}M~TJU zNE}K$$^euEOk)P3grS6^^uyvFIZ7mo2g)#%M5GHC5hzJ0gHQ&eB%=&LNx@aZ9P%1F zb)W65GUkX?U>dA;%n7TUIb*eFE?5;zi&e>VSXE6!jYW+^jYl1Tnt(bGH4!xlbr9-n z)DF})sBclj6KW?K_X&GEMt4UOh<;=KYc~xeq2bENlNWD2`S8`7Rfre~ zk|eVkZH%!d$}rPBi{x9Q-1jPMx5G}m>{e-yy^cDgT3vVJ8+UDkds_TKR`)&8=5L?E z(}+{jliMi!8)?ZOY0dpS$ip|0-UoS7YxxyaSW(55R9abY^?o1pVITEzpY+c@ZQD2u zZnB0j1*d;MBmYu;QUFOnd=S8S_ql3Bd+&raygk%Smpe`z43H~(TSkF5*;HM`bG#9- zj_fF>`qGswihJ@6Mx{h#-tJaU#yB<(r(H1^fgwF6pQXlt%WnC4r3NjP_`w6OvtC6H zUQ9$KR*L(gvxvxP7-3f#E1CzAX1&#l`e5-KUYDGv~6b9pk zV&bX?zYfm8IvAlP5WPVgDVRk`{B1AV;^12kheu=}Ken{>XXL>u+7{qyeHo%}{kq;* zFPAqxS6^Z~m#QznT79sFmGa?hAOSjziZpDHlQ48NX%@06b(Dz|F?#1Qff>Gtgo>V- zk3hj9nuV_^`nKJxtA1NIr?0-WJ9Is&CM>YcSlt8bEv>{e{a7Z_rjI!fHk-_=AM532 z`PaJIIao(@2DYzfW4)SQ*D~?`n(R@ViEB&*3pJZXKEB!|nplomkWLr#8sCyLBQF80 zomCcvNwg>$+&R8>xp`P%2CZmy79dpY82aEzGJP#ej%C)`qGI4=POCZmrpB%!YKT>l zr3>C)6f2fg?4{mF=5jgnT?O&CUEVVo)?XwX{jTB}W^Wc?;;aG*d1on=+NHpxeV8jU~J4Er?=N z?k%dM%Bsj9ZLD5xeU4i>uCr$4!^Z5us~uXWb{+|sfMY_Th~O_}c!73EJV%cCJ#NFPItfntg| zmcT!)G*L~p)m2|ZjfHBe`D5P7Ev|>GLei1e*b1r|by8bi(Z<#Q^VCoY(_<-2DQP z1HiOLqbP^%nYYEnPp|~3CYdQq0Rn)6^h^3y8eG_dJ743E3t(8*0{bLCut%?m+pmX4 z@izYHhny*T%oXGrBA_?8i|LC&MD|(fymK38Hti7r`B$80D0MC}aPKGeZZC(4<0_=G z%n1#C?FF|h*22`hrgV~8caS)BE$Pg}cHP;XE4$6n~Ysye0DhxKKR zRHh0|(Z6ip&aQ^9ZwfE+7Vq&Xev9AcALkQ!8lS=E@C`QGW~UQ=a8?bkahglq;I8}e zRX1ayg&y*UJM(js-efeHO_WaX#o=+RrS%j+a?l@x;`*;gKiiwzF!slN_neBbWIz|y-xjwBO zSp(%Zm502|`+WMQ-r=9%Q@$ddXck|qM5!GPJEaX z1pquc@A@8xc`J41h}a|Y)=vvtUX9HW(=wWjy=X+iUx!DX}sHzjV6;X~h%@voknf8|`5OLJvz zPUAF9^R&#z;xPy`}4e?k6`zA1%8ERANMbq?BDq8{Sf!tdFp-C{mThy zxUO{`%p-{NsyOwJv!#Z#xypXgmhR~{b=29So@<|4%B|{@;4YlGB;3wn{@0R{6CDqp z{8@wu7b#YpBqNMsGftXJ-rT%wY-HkLVdG3sZzG4v(qQ*X4lyDW*=Pq~yYKC= z*G_xvbJ$s@oOZ@d7kuZUBmVT*Ly!E0DE{sx?Xe>X$w(lbSLt>$?#8PisC?jqgpc6= zHji>}h_mBdoM7@JqiSw`(#;RNo#*8oA9Z^Azg{j2a7nN`g51*AZB|!>`AvjoQCh@k z73~lG-52kX1Wyd`SfZzf`cH~~rFvnsH^zErjJL-7V3JM~eKNsE8HvW{i9&iJF)QPJ zm2sG!7<`i?e3=A%okYyd1Pha9k=%5bA2)e15!ueq1%*4G|KTcr$31k>MeA>>(=^3Y zrUrGWNmYV`NF$4EGDs$gOj1aHlXBPm=6J@Q-ER%QHJ!VHm#o`5W$Lu)v**lT@WyfN z`VE^lZr-wE`?lNJLqhWlDq33G+d4Y;@9REt^3+?JJEn6EzhV4erh$VeD$AR@!p}sU zjXW21erE2hyyy!t7h^BQU5>w!a5eE-()E;^skhQ@r{Cf0S6L&>8EW2eWf$QIp-L>cu=z#12?(9HEq|AHvbOHc(1pvg41n$ew<3d2tx4`iLKt8~Z z3<7{H&sTzQD5RF$eYUI&an$a1_@zroBWKB*Is;PEgG&yfV<=b#v&poWvS2hoTG4w` zhDgP6=9>z$Y#k%T;8300$mFDAA)Oz_wvAMGk0Z{#HKZGn&P3}=FbCWw3~VBO^%xkh z^q>qmwB3t42VMnlkooVxE^tI9_Tna~5=Nsr49FLJd28}lh?Hn9T>-bJUGl-=|H?Orc_#bwxAss#V{PXpIctfgdfVT}{ll+!WidN`xwkq}9J@u32G z2kUt=PFQilcOf4}csbr`(huv3s5wsbIO6azvWZqlFUQRpr^Hwwg`N(VN%72ZCv_`g z<5oCRJ&ifaX{;5+JX^sZib(1q!z8d;=?W8GbOEK@TXMMFbYYj*oN zw(10cFRR9WB<3~TL3RKnfllhK!;TRUT{(8EIby)&9x+EJ7B+OmRy`M18DA+9V9Fm6 zAMTfG#h=Mi!-#%@CKbto#OanL12Tn6eeg|g$OOf<0UmIWokB4P3W>pNS%L_oP2~3~ zV%1S&^QBnMqEX2_UdvRh4S zo|56oC4YDmvtOFSD;VYbWV>#PzUSunB9EB?2pF8hY7OFeLrZqhmoR0>H6Q= z37Z#1R$!)i95GSdZc_>$>YyX2dLNkNic{J{G-3GUQ2r;Y)#+Xl;s>SYCo}acpHhp* za!N_%v+s(KoLT+-|Nq%%Sw4qc0>9t?ZT+g7%=#&@mE)>bv8Iq@+||hBOk*mg>^(#@ zc}3-kY5IW=U^*J0$ZDY0n}`6!k;L(Kn;RI7h9Ql~c5vY-J?p}lJ>Z8=)RdvEb>bZG z8SVz28ysC}u>%oRi`MXh7}{N~%2}pL4iNiYFr=~m!-_@`9f}u{1+=>w#DaS@Ib1w| z$5kwFS~myb0~VQBc_NH`h8a#fY(o`f;X4ISClD;Ta5>6QYY;F%5e3GhXhJxw4|pgr0fE6#r}C(V6l5q+UviE;c8@GpE#dJT#1^ejrfJH+ z$LVJeG;%t75?wf#Fl%%Q^!l7g$74Oxwk5e*W}jeS8~d1H7j%Rf-|ni zCFVFxEhX!F{9Gxiad+E?e21fwrHUGo;tnKlIBQC?=<9z%9Fn~I92BWe6NM0ls}P9Y zlEpN!$78IC+y+7N<~uI!;S|nq6T}u#%t^!r$aL70fe2}~uC@x0)L$%;l&$EWkTTKH zqSoS$x;~Zg5dh?fLDwa!D2gYfmMP>84&w3BE5*O)r+h>eY(qt#<Dr0O`~ zIaAKY|F0U0tVfb2$Ok=H+xb;tOQe`mf;-m~Ej!06OGWlrKUSyEdKGaA;tvxHXZbYU$-HQwC zue2!v@0bsl|q^b=>p$FT~?n6;$1?`6kP6#Egl zH*1t^gZ&v)?$abyL6r7>-8iBoWshjMwu@Tz8 z=*S1JVXmHA=+^K=lXqa+tDC3NrZeo6!sB9p?dGgbN>2}}fX5jLMtFh00>m0dOL($? zArz;1{d|stTz)=V1(|F&!Wpka&1i)RqK2~Zv1vw^@$4ny%-IDZmX5%JcqtwW*CmbS z1)$_{c#MiiM_Li53zAnb={39enG8a{7m&cGM)8t-FKXrsvUyCrhN^8P1-aeJO%5S{G#jTHLWyOcXE>$V%UCDLBHKUz>2xSFo{i2d+VZ8Yh)W%HUXmO%k>A z?5e22nZn?ck|0gEaJei#>Tt0In2OW#mrdlgRyuIo`XJ-Q27!S&{$gYDX*MD&i)~qU zu)PTtoqjxA>V@Kx0m}%A@9q&XAxIghEi&jFBWE#*Uarvs{M}6gU)BxX6^NHa=kM0p zBIKHGKLuruZL;=b+`y=;RjZQd%3@|x~~Rr<5beFmI~SpP)kUbF!OWL$;){hwKOnP z*Z-ZmAYp;K7=ly^)C%F?>XtB2KPWIuZ(|#^B-vjxRIY2oH{9OK+vLmX8!6NBvfIQ!(akm|!chYas{E}i{CCUUqJrc2 z>V}Yl)esE9JXkZ29a86l0s1}ZbkFGf2g6fFkDR2fhtkd3g#kEKs zeB-)|xCE$@X+!VRU3`U8F!`b#xKTB@leY0*2jWE+mP<|u*97r26Av-LThQU<#jIwPU8#?tj zD^sJ|{16&hqPr9))a}cY>w`E&kFUlbW=rkQ)c6EoS@>OWPyqjVgrivM8jz5zd1*kO zt<0Gc#Oe$!3rN;5FYUG^wsLnK+m|GW$hf<2WQ|PZaB=tYC^>_{@y(+k zSH<0^rSjxw3hyPWYW`0ZkAIIvJ+|2G@1`$;m7z7nDKVx9xgK9l+K&P^TS>9ZdYFtU^g!~S4?q))+{9D=P89^he?7Q!zFJE8sD5N9d(W}u0CIR^6o|s8yAAT zOgk;6G*2MwBbZ4ZCq<#52od!6uDKQlQiX^mkgS+NQTiZ4tiJh6J3iW1*LeffRU0Yi z6z6YP+`TI>4tU?<3$j~^ts4sllbFaiP%CYtjtQ9I&d5X0vtGM7A6~7&*oAq}CG)$c zuhZOo8!Pr9*<^)4JikaZY{af$P!29$P14p-Bdwx(G|7j)38c)RSY_qDMFcH2ESfGx z&BgAJ4Q;YUL0S!!4+y}^1I)2|SPk;C13;PF&_GP$u19_*=Iqm?TH=J_GA@ZBHPbrU zanJG4b&I@y1lggiN3EmgY_S0iEs3pQ8ZwUrK-0$S{)Y9R?ZsNkCCm+j^l@|(4K*;3 zTQjCLrBRT&RJMs69kOO3enQ`QnH zS`rlDeruT@4aq*9s;(d_9d{Gq7q@5b>QWHyT^8P6SXW zKvM;X4+I})FCjfMX4BqO?gyTktl>1RImg=23obTCv9_(?hjC<+zzYX&2r-8(2q8tN z9qmrB!licP5NnWB>Us;!HfNBCg_4eWsgDzO%qv@zhu8#=K7#KMJRtqx#BHoJoa{#O z*`tddWYnO;q)h;^FlfUYFJJ=H@t}QrCTE`<;3xvIo@&o=iSzW zM8Eglo9y%4G+~9gDc4j;`3^%VkTocYJtx3r8oJ@oL5Tgv?tNF#M^1HSA6#bOyTK@k zfMw*tMx}%kC5Cfc)4Yvy%*=8hn4BEZh}#k&k2e!z+bG`eM`+##aajr2n5>J7w7S2Q zv^3zcquym8(Z0CZm|;PNua&s5r5-hRWdf5?x4kGu0!fyB45Y4;uFqRx+6qt;q_6pL z-G}|WD_xklIX7ih$4hgx4;j@C4K1w^d`|-m47j3w}mJT5A@)L|6v%lu;f#I9bX706{e2|!A<$FNbjj8nW zu370CU;G<5`RN-OKKYA5A z8>CAEnzT{Y#62HU4|k!Ya@p0|vXR2}fP<3{2vV04Qq2po`D$Mfo;XuA(gdBu0~15n z;!dgL?=-X?>#p4597nmc+LZlu9F8OTq_5=%`=xPfl)la?DtV(p&UMZ=KAAxH28rBR zi;%Jrl3g>37fK2yW9L%Iy(@Mg0se{9KTYS~67HlMSCizo$VGby%+V~Jp9kHkgXGq0YiySpoewVlhpN0IGLg?z4xiWNV2`zv~p(UcJ}vpS#dipXw$`7ZFkI5ztAopU zzF+jLI8q%Q>(SnbnFr$M?*lozt-h(6 z^NwiF4pf^e@6Fv%x?Y$q&y#1Ds#L2)l%1FTOZkQzxw?2pwhX~-Kx@{Y&sHg^XH}-C zs9D+Iukh@j!k!cD`Be4{rzB?DA@EOlFtYdVHpjO6`w`4n8;id5@-y8N8&Tj5pP$-M z8K4$wO+@Axe~ljw^w&1uCfm(Yf!JJRT<~N8t4J&23$$WG+plflN>ZCwx}$Zfbm!EG zl+5a!*x<*EhwN67(4r#t2mHi7vlbR1?nH+#I(gdc!HU9v3%WZn*8y#AZiY6^wFh>` z+gpZb54Fs%wcsrvc1{$e7@iaR?DU2YIDGeD2mCPTI1cV6P4}v{L{vwFqm8TbBB=!XY#D23%ATVnH5%_3pRtmti%Z|f$kwaP3 zJ}JN;gTLK?=-sjZA1UKOd6zij>j{&Ja2uzbNVHDuFgMj-#YyWO$<{sCl+ znxy>D^iW%c0H_H(IB z(ZA_}sPD=)O)UGGN9g|oXv04@&ulJdQehn}Xn~?s5{w%AwR`+Y<@%x(IcQD`tdhxL zwQ(SF0DmVBaa9 zRLXpY$|C3Bztm-GlijHmVwznlkfDtX1ITmLH3jkl*G|D;22)imQ_9L)1m+=Rh;7WN zBPoIg7pMfT(N7Nv_RIc7{{>*`6-SKY$}|*wlGL}b^8$Lo4F^mn$L^C^XWznRkP;4@ zS(sW?0@uRLfEe}(md9Du9t*_XsXldhz{JQPeE__!_@b<0Syp-lznF5SykaHZI|w3j zFK-KOD5?_tVH+L=EZ6+ZPRCWWn^@s^2Hwl&dghKhBL%wfsI|F%el-BWRR-K{a_N@L zF=P6TXkLp`DU&-@a6icDTu&KrINq8^QMboC2T^ZMyHK+v z8~2s($e!i zGO3wr7)E`W-T2j;qhbOfuDK^~E*FjM_%CY%1R=>8O_*jaF-MF>Lo3Ae-Oi zvN|B}Fup-8?na#Ao;tNb8&XW8?#TmDP8+J@sOVR`V!3gn3|2ja}n;A+|$r$@N`RnAk%*6#+>^}Vl;`wsK~+Ms%sSDX(p3sT9ra2^l;J< z+mqt1ng;SXHJM=LWAd#UGF@vaaX8=-4JeRjL)pBQc`UX(j1(^Y#Vc1Lfd;<|k(v3B zP-(InqzFh|`VOM9o;b^VEP_LRG~R-4#bW6=PoTl{N&2f?9D>toY|68-y9`hU@P?5~ z5gE-FZ(U_pefYe(VK|M(&B^J3X39fjWFx{KA7@Pkzqa14&QfU~d?NZCM#Neor!O6r zXlQhf8l#L`7@=n>MBb_r1kqm2ne=)x)qx;yg6o;?6NT=RxKr}BQ-uMkiD}8@g4ApdCV7CA${|=OwSGSy1q4dS=UXH}T|_eG z;fY2Jv;_>o!V&F9sR4!DTnVvy%x2S6LgqqU`VR)w7=(TWu9$Q$EnQ8`b1M{Ynkp&- zSi^7x1^;y&{vhVFoJd`Ip><4oKtVkk(GKta_=*Z|SWTsCv;Kb?a0ja!JNuhhl~N&U zFxL}H{zp+D^Nd5#5bij19K49Hpo?s5H>O3;mrMW7OATz_V@f~^O*A^);3E|k`KVlT zpBpnjhQk%E%TM1JB-s2+lTZBuU}bX#fu!So2Am2To!`AQ>A_oiH`|io8++ z)!2~M==Ics7fHt{IDBr-xr&dXPL2yMyKIZE0P{?;^v$ignPaKtwv8}LLhv3YSCNDyhYOET5wwG&5_h|%Ni>Vbw zOwATxHtLjeM4t0m_9kePBK)f971-M{LH0iS80e?Ocg+e8xj^}{dP3f>Ss5{IU37nf zNLZxSD?1X_*x{s0C*(P!W0jwfU!8YPb&anm$0ZM@fV<5|F>FDKwOUuP1@?4p(;hZ^ zH?d%MU(%ieU=4qQeByGZ4$Osi{dvSy8`t5yhke!~oJAU2QIL6N3i>D7?)2@9Ygvh` zwC%DM9q%k8^p4eV9@ASr57CtiR-*GBWX+>)(^LKh9|nte7WZ=LN}Xb{;o|kZLK0jI z_%*4Wt@9RoFuI@;;#zY)CDGnM8D&P`TJM{uzn4(51DaH-%)zepVr*(l4MnbLpv#?n zwbSWH1b=YxC^n8vQ`*5ZflraQ&W;fjA^4>6I)!ft6!K*O z0jEa$H~P0qW##c)1g47oSERT!{JPM{HzUVd(1+-T2YZpd;1ZFV<<5u5!;u5~rEeeL zSWv(?u}|W7um{-#kc}Qg$2bGU+;Ol!T)$_854$HY+XkmrR}Qwc&H2e`RVvk1wVfHq zaFCZ73cb{lB|Db`Q)D3+T^bD1n@~#|CljbTuXP84LG<8lA5cTZ5=&U z3+RWPD!I(5gv-t4*Bv9=zXf0MHRa5>$pIBLp6dym_3{O(0 zV75!LEi@=_&s{Y+81q;{X1;d^gDqy@hh(s?nZX6 zm98e($9VgC{%X@f6>wX$(Vg$rm%HF9a&@bL-*?07BwN1TMVN_{;rk%RA4D_R1*9z7 z7q{~Xz6fVTf<>Q0Q&!1E%?7i)rCz3`SnF@N0t=f(FsaBCs*X%Fu_6c-4TTE4*z18h ztC|2eK*+xXOVrU8pIWS~ws~I?jU9OfU5p(YcTBi{A`gFzJ$L0?un{{IGR%C4fP?$c zyJ&{d_r*ypHpH$gJXgs^Mz(AUqnd?7&Mz$_#OWcGjzLAPAh(jBq-#dx76Nv~l&y#T zZ-L2KPB9F&Y-O*I$J7bV$4_%b8QoC?ElniRQV0|U%T|`PFV^65j&SHprM~E@{rvIhy;cn2&rOmkL%Vl1-k?$UA`GJI=Z4cHUYGCromgGF&DjBShlW&u*2tjTBD z#OYO3ooP6SJJwia5X%4!+q7cu^1Y2X#5==N+g&`i7y&+8h@g&yf{A{F;=$YT>wNP$ z6-1o-0{Q{^;=Bu7P3@6s8}xdSGtFlAQtd8hh0AF4Uesk=02j&Q3b??YtWPOagSn6% zsP76bLKbO@Y&@nmpP;5M%wPNv`>+shJ8sQ6V%1SEu^`>8vRb>UoJ`o;}z zq8qVyjmSU_BhFpjyB&SLV6mVp{E-be7pdd!`rRT-}W$R{n(l| zQpH0Ti`@Wia2c~kd1tI3!M|;YFflI2PLZeh6D~$Q3r|2vR2ra(J{AE?DEF6WZ@|ol z^7pKZo^~&OUTtmkb=4&MefJ?L-)Of9dmVhE> zKvG#+`m_>&RZry>j-|m-bqR-j+uQL?u>aam;zK=>#}vwA2`C98g`d9&2xX44`hW`& zm<5nXVS)_;HP=g4!d#-ijT=aIHrD(DL%<${d(QJ0e_`4GhIGIj8@3oa~h zr4=DXpz*bmK-^8-=07?{%cKX|Z>7zge5?Hql$~>Lf>1nq`CQdESE|0d;{1+GUKA7s zp4Of|sEAwVj5}=G+!pD$nUh_t%G~#GbMrA*6tbSZW@$1X>sxoCSt)YLL@7`l{VW`3rWHZLBCT|i;dnPm zh6YpeAp8sL{||ld0Y5=zZ8=hoC*CN@bivn}hc;NJ+fj z{Tja5Swq*T`guly=2?(IqberTb118olbJTFZM->mA-Qma3D4%Xa0_|5hJ_*s+)Qfn zNO!ijN_RDT&E`ce=+(8e$zx$T^%_FI$3vKD(6U$tEfARW>ox51Y-?1?Tm7#)s2TTh zVp+jmD@iwk{$ju7xCK}iMtfUbjleX#IbUc{YazEe&fvww*Up{MYN+Ax12RFL*`YQO z1I)^r!eQhYIFi&;AMN#EitS>xp;)e_FEC^>gosdT;)w)Wp-QFYlzB0+_8DcBnkqUU z6{{RJTOtmPtE__6%F?N92+Ud&fL%8NKcfh`7JY*J;{?@|(WF^BS9kM2>uRd>)p;hu-m%|Y5K0gg zZ78BVIi}8R)YeEu4F-#>rCDk;I1w*ww4rLIseoc&vIS2X|?+LQya%+(95h(QV(_{3cwJ@CqEwSJ2Ap(LJa09&r z_%EFLpwhvOFg*Mo-Zav`58uIYJXSzWeYT~1QfgLewkCL})}R*flyX)cLwo(;52VO1 zLSoj%1OYXba)ZJcl%@PFk2}expr1~;ot^zbdghn8lSPx$Z4**hlhX{?GadtxhUS#j~ zQZr!>07Cg?aeJSrfh}3%;4aVBR9a?h*69>3D}AQu_R=jn z15OG>FzDRZ)tI_7ws-e9;B5!DbyKGnzUWF^#%3y7q<-IL@QT#{e*nGbpUQNyi?XVf zMQpN)`OS_BOv+@Ni}lZl0YC7rP+>BQV@e1FX>L|eRCI4Pc)phX7x@#u6gPtx{F1(8 zK3!4s+i0dhy+n$IF0V!@pb@@U6j>gZQ+AzT=9EM5A&li9d=MlZ435AfY6TolVY1~* z3uIH~=KvTQ=4>WKMTMfG!43ebBBWU5jaw$IUPmW@rd*eTqr@CiNm zlH*mYeZGj0%CGUUztV#f@YP9C0Y_ScE`sw5f}?ko3v)AI97whnTxdRBTXPb?fPd_Q zC%~Ejo5O}#m2ofbxSjU+LEx>xyftIU{(%1g_9ad5>-21GgsU2LugTtj_*UYE|9aUE z_wP~KlA{8)%RDB`1b@FOZU!*y-K{{Vd`~|}1~L5E-VqT{z~4h5`5yLpmCS$eY)m8Z z9(s>}X~w%E0M&V6g8QN;VVVk_T|`VHTLgb#E@gZY6=Aj`qd7c{N@6raV+~$ROvSA3 zIz@0MP)ky{LG|s7FL@{cBiAX^&&W4aN~eLNn^cwUiuSE%DMP9dOMOOCi>0bn)&lRX zV(&SnL!c!ThI0EfX(p8RPF65{a`OmV?|^nSthN+@_aKjQnS zS#+q}F6Dp)-(!d+mq+W<^CRIRh0JQdo%!QjT2xgfmTBwQ60bOKUFS_^8w zbHSTD7b88pdJN42W-{4Z4Pd3$BmbgfqF`o;KBhTY@w)T_&I7wYp5+8zLkPPfHHExD zIKVyAToJGk)Rw?Qex-8*8kIj3;Zvs&;Dr;B6>)R22fMw2T=>$-OE6d%{~gFZQ^4LS z$G*2!%CR48U`I>)&dkAac?WLj^4CM7AbAz1x{T|MmeIwdt?P%+JTFAeA}5pkfKb0QAY84;SU04kLL6 zNa5Aa*?2oxiZ5pWj;wzHeiF}V%d4Y50N zj}|IPyp-zdmyUYJ+x+p|a+5b_yrPBoG#Cqge)ojuFckTvA6U4V6hc2j_R zc<%lLccCL+g{ZF}dXbZZIOWC+V-SWe-nhJBbQA^yP79;@quSsW!d-9&Ojtv>28g^` z5PiIa+NkKw>Z<4VWyD#9hIUzbPlKIrToOV{^E_gc4%=cXlNTZx*u3Wt=@<@6=NQq^ zOf^<>PkEV!rg2=`6hF0X*Is6YDX7ublXo6$KX6drFCr8j)lR~d{bg8NDkR`@DMwVJ zT-qd($HL25$Bxv2FrV064;puwPeua+iPpE$Msmm2|zY4w>SoMg}*I`t)Lr(sUG@}2Z zYJ$Os@zW-i)CVXf&j^#uFo2)zSozf_+6SMuz0G$1A8bAjB<=B-IS*Ok0l?=fJXuPEO{RB@OiZ8 zttw+|he_;5X&xHF+;~k)Nw0fzKXyM(oB+S2ire211=P~Z0~BA9=)=Sant;6H zs>nlR_^eyib-DG&Rxre}Bop;5cGbGNM%~E5o=Sa#Pr9|nl-E_HBj!dm#oe4vWXpVz z06$%8Wudu6Kxh{d!L?*F*CMZ9GmE*xW`KH zO=pm*D(N*(1~=bw^q+lKZ*HOz+KicO%y3^tZeDQ&3q(!sTn-Mh2I`B8dFC}=8-Y4? zohCMTSzk$$+thAMlSL@vDT(;3Yg(*vJW8r8Ak!7kez={_;_CwWB3gi?HkZ3F-8!~7 zE8+{yM8BH~`XogeS>Bi|!#6oO-(+Vyb|B-|Vy{sJ z#&aUKV4CPKTn?{$+c zzoGj&AFp~)&x36oFfJ?|k^ZNV9+K)#{qaXtH7fX{{qf^=5LLFO;?T&UdW}@xozX-+i&Z+A0|?Vh^6yNs z@GtYyo|XA|JU^eWu%Fl2X0^X#dU`dzJ-4PVCFAkrrS9C4RwZo<{%<*_w?}jIsHUg) z4%EB{IP)JLFn${W>W3ynzaLGDy)ZS-SYs>Q->=A(S3m3ir|J_gto`Qm3Aq|o2AK^@ z+lyl!s$C^?k*SF(^-9MtUC4&0=aw|GWFArGQ6fZzcBG`|I@IYDfkz;pGWNgESZgRn zAm_}RrZ;BO&vvWjCnKkM`b-IpBGzZ{P9Gfn1Jd)Rkp!DLu$i7bgMv*kCA<%y1n_e* z`tz;~kjZm1wKkjf^@i}98n@O5=-P~kj9pXRpr%7qBLBYy{{a*m(+W*-9@;ZmAn=xP z#F{B|0n`;7^QDGP%Ux2KA5WzD{yYe6QB3lPbwBPvo<_QV->TVmeIvZ_CiDV)7JMBZ znXvUbkkvcWfBb4iJ3V#UnFOq7Rq>P;`*s2Rk@~``5 zj6HVV$%NrKNLMc{CLWuRv4izT&Djx2OpS5ux>30VSa`~I;{?g zbI};c%L`-~fE1569k}?qTdkm81Pv?3K!0t1OIA3yj1e`sc!Jw)$`p{;`lvY%MPL_ju;8y89(mzWsh>EKov+!JpR(l1iR!Q((* zPGZzQE;MdjQsRAbE8GfB^!GtfZy%tyfBbP(%>o4e>M07e+StzoeeLu#Lbq(Gozcnm zK<%Bfbz5y`Hyf{7(6==-d+?QaXy5<#?hVbF^(qkJDGRtSH_GGFq$gt+#$IvG(pdtN zUCC3mIcLc|dGpmolCGIc;~uyiocIUm5}n)(q6^F6A^5`w1MmRQUp=|`)aGlsL+}t| z9XtR7^}jHYsKA@@^UZBSB*H{lxjlA5&X*nP+qrZ5j=CM&ckTj5{w?Q+Mg=lr z3c}oKYU)qoj_q~ZckJ950*^je4&VL_zVd0MnIP-8+wk(0sl^q3VxXvP27JEP+R>>1 zn@<8!7~Q1bS&}gH(|z{O_v@C*STGAS0-uBb{Hl>NGF38_Ck{zMVEEbQgc;|$;1U=t zh|IF6&U4S(o&ySY^GPL`ak~UA0sWI+-g)qDdEdUOl7Var1&`1*1AYzzLHMQP*E|1v zhZH2Pl+xMGQ@I&CM>@gqvCg%fR_tWw`p%sWuER5RP_NY{*G#?58islsdZNyX{I2v+ zPoM`gs|YIQ@cd#4FEB2#xP;@9hf(mwH!0rX*9bs$!O9^+HV7%BPK)THW%>8Td1aHNgyCP2AP& zPY2`|c`x(;4!4Sb0TwEs6g(jNf|=)x+cUOh0Jshr##bFtBcMc?RN*tW8v$5>F35%5 zs2dPh^rQVimHZES1Vt~%$M7@s5&BIebyh6He|3-7Byi;9IH`I`LB>`AXDO&Xezdi41b~Q>;t+i;sJqxT52wY_hJu>$Kf=hmW?AJ>v2ngUrke3I99IX& z_1-*AVi>C7qy$c?9+Nn!-hN;^@n%EHkYbxQP+PlH$}dVy0he)-uqTgevvWU;#yuLg zT#u{jJ`F(943XPQ?K&-0r-?bUa?U)woM{7QVkTx{rVD-;P3H@7Ngo=-vsLiyvN#)O zb9M@5V|KUf;RACx$2QLC`hK^iL%9VYOOr!W|ItpNEo9p=Zk;5@aIF2uSz|rUIZD(%;yS>AJb23OV&aR-VW z9MLa_T095Q1m|&=MI7sf1H>Pql+aj_S>Q*G0Yr)wz||^162H3>lq8vu!0AYIUvJIR zAM3p}Eic_s8&m%fQ-7^dqS^FBR#O*CH zRI6{MzZVqo zDatdlsRWMbGRcXdMz*m@iH9tSv2H>(Gr%FhA;6&=^@QI6GDbi|cVp$?Cj2%~!rTbp zY_aYSeJCAdilxGyD3hMNBE6)SJgOSYK{UY*tSn-$yPz#r9pz&^ksV3kh<-cN;()!g=)|RlqbG76q88CAh|&F*TzM2>}8COdwqrLJL#^E3*1fOAC6Pv zp}WYZo-5D=Yj_f9vL}z9INk8y_<{jnDBK263-V*<7aH*e2PzsM2nVkaN_~>I)x!?0m&S*92_(l3i9Ise0cXbU2bMV>>j^H8JZX z$~yBne3x`QYEdV8ghFx1$3h(_L?H@Mh(Z*i&=mF*h<`gj;fH#BO2<$QbmE5d`1EiY zA498-vXs-q;9jn60OwH;fb&>Q0RCdy<&yeabz*%^{M)%+p2vJ>`fhn%jhwgq-(55S zHS?aUl41d@A!og{(A;6?0e?>U3c$l%3gCKH{o<&yUIo`ESGdk-6~sBIy5TZJ&s_EJ zgIN2>wcKR@zzp=!X?13fV-{9Vxu3-+jKa&6xtesi2y`DH&)yr3wrN&q!o`k4l21+S zBsCWBX197=J^evAXTLiMgEjkJ2|tB_nb=)^Mb%qZWZV=?`Eg4q$ zHa;I%zq}>b&NLe^pa+*~saFTq@2%Y1xe5WM_Fz3FKEmSRF1^CYTnFUJWj znK1lQS{G;be}PEZFW*$84{s&=@4(*kd40XyCjzgR)zn%#T-T+pgBno)x2~k8+$bIx z+?%e99y8(N05<=1!5?$Rddm1L{+wFqV%02v_R_Dk6qJqi|VjTeR#rds>mJAyQB%NNKS(XzH_&TrC;tL*EUCX%JH&(g-#C)Z+vtq+=KYH%djTB!+1~9xG;3uLM|~vo_Mx z8-bIsV-p^@GYBhE=^zAv_rC0PrlEQduMT&B>J4auY7KPFOhapD8mh#0<+4R8o%q|r zVH$D7pC#;7teI4d=uYlC5`rMcRl-L7tuYnSMquN?A{8U98Oo^~kye`(&b?c;oH7Z| zoGb_r^hF-U*ir(Uq%k*c%=kw%c|^^PJw9H^m*vAY?Q)_Am`_S`M?G(Gxt@QjGRmhW zo^u|dz;1Do0OK@*Lg*v%(Tad%uU4yWPWeJDfg>S^(6A-}b4iP$zDvgRho_OcFLC~J z2@J+*y*5#=_(zUebwt=Nf*wbdqI|-JvUg3? zvpOKZS72MgEUcx>BC3(uf*s3nRW3!sFBK7QDaKE|w!5KQsJ@Iqc&d^qlB1NRj_TmN z-HudYtT9wr%wa0klsYQHb*k)IuxdK{a<(8|SLw7HmUiJCO@U}Ii3VRrSvE;DVOkdV zc(hLd`dfd$jGBy{LdBej9rL&e#1Hj-m%veW8?bZ6Ub>NE6o)`(>q+UZ$<*d^oj*0s zn5>-;xU^r|qm=(wiSXW=mZSM!);R%ms|>0D{nl=+VmX{D)CH*Drqa7HI+;S5o6Gt{ z)~H&Go>|FtKzTQbJ@1#+g2(Gz$A&6fC z8q6-;5$!xML?sH03E9sb=Zj<&7 zP0e6%E|G^>R+M!@K5M;>5Fp_ZH0y$H2AQFoD>~977}4D{h4Fe5Gj|wPckx%)SY^M- zp`KB@Yv9`1yZPNbid^=f}ubpb+tfnP7%_ z3KXjfM{#kaG(!fLhqeB*6c011!cX-%pe@#<* zQ`?!AT!|@&sgIcvGbCLmeUndZTx?b>B~}nSC|fDpD%&GFivQWV9{V8nY5px88#gg7 zH;x`Bj?>4%ag}ikg|)&i;jr+9a5L^)@t48ag?*gB>0A#9i&_v>F$P;u)kZ+8bf-T9=Rblh*a zJ-^MxDc-O5oR-pmO6RASg6V!)avgLtA2urNOfb$W`#b{_aJ{WzF(s8z{hD@H)q~z( zt({)?inEppCV$DlyV)(c``q`ZF`Zc(RctxPZbZ^K>UnoeGcYqVHLDGI?{27V?df1~ zWpyjcz4mVYI+CaFAf8>aw80`5zy5Tix3sR>o?44`^E0L`8O(6TK$mEsMzK+AOgCnN z01gzez>Ou{kx`qGndx2h!TQN&gE_;TO$r5ca7_O{eHs_NsNd-}*e%I&Tjf@h)n*Nd zD6S+j=-1|UX0Q=&gVhjb7+^c?QoGJxu_fDp=j+&zHOP)|a#xe9pKGvdtYdeIoeHPU zX?1#?*@toj_HjQ7V7B*cQ?2%}-yVYp-H*L@bl-ff_ugd5X3ZIw!+|#K>c*U|oGp!v z=KP%-mrKjl=XU1q&wYUB<6EKgWL_djYs{UuKJQ9?PQEYyLjs8a6Iuuh2p_@c{zQ0N zAad&pRuycoLBtZh#3o`FaXImS#7EHRD^e_pMVe3AN&1j#(Bh$KR_vz#45_&PchrX76 zl763ohbNt%Rio-=^)Q~@O7#}?KJ`cH)9MTACz>ct zs)nn9G_y39HNR;U@E+D{o!S;{k9N8Cg!UKRBps@2)-BW>*Ny7O>1FyZ{c8PD{e1(@ zP;A(37%@CDVvP!8v2nn7)c7Y<0DTc&!_X0v%#1VNx1?IC;as>4e*c|To8AWFf1aM$ zTkMM*1&;NO*G{W*hI60uQ|Ec-sPlK{D+G%)B7?|rVE9W_007g@|^X2@A<`>?xlN8-YV}*?>g^M@7F%1uiv-Cx7#1@m-@Z_7XLE;zCd(< z7ua8HDGn7+FJ4i+sra+vYsIfhCYLBms!I-)Tq=25T38w^?JZpi^&cwzsw}Q7w~SV% zE3=o?mJO7xD?3v5L)l;X5*QQ}lkZOPmlrGRUa%6p6Z~v(ZgF*SFDwQV+#K8={3%!$ zq=I2dE$#i~|5zd|KU#ja{B}MQZ;hdN!@{v3==0oyxjARA{Rv~ z(_RZ4oGp!!QCoat=Pwg_w9OaM@X;Clcvm{P03VKF#!3+>fDfdsl88He)ho|eZ8bAK zsrMCBFM^9epEP8g8FLpiJSf)cT55jWtTcJzwJY_*{8aOz00Ywvo0L$?eLO-Mr0ue| zIN*9?2PyE7yKmXH>T5AL(7aFHr9nK;{~zXXy)k-HKNJ|kW5{^H{Y|_&9DT? zaE(V62izdZ?kguzL+l_XrOf0NAPmB4N$<^^RM*PxTY&WMg`LND z)cD@!E1Q?Gy)s@b(=;4?Sv_syhRbczhLyiurD0#U2U&D+*xSn!a03j1%kV7Qts}PY z>eZAytPvY*>LNBH)~lna+ql_&uf&Y!d<_VjZD`Zy`Z<@rVEASMDueKM+J@tBFDex{ zgTW#E{%6Q2%6qr2gbAeLn0jR-k2i1r??s<(YFRHe7_}fIo;31kwI~QSQ3MB7#WvfcaPmIn_FN2dwOhYiS9|zyh<%W^(?NWe8${UZ@=U`xNn_qGa zTw%Br0O^yaF_n$U^HRMxjqKL`?2EH0E)EzP_rN`kJF7RUHW_fiU3DDRfR ztT~%i8K>s5Hg~|B#fosO^*whg6MF3$VV6|X*y&caI@JwxoN*%}FGz~bmf929NmRxI zH=pyD6CU)`b+({gXUGd((+gBO(4;J5IG7F4<7yWmz;)W|09a(~&e@#hH0o)mMxLg9 zMiXu5%Z}@&=~=>Pd}<$8z89qsh~2PD1hnM0)h!eB>vQxM3}ZzaNE0u05iiJ51selK zN58B(Db)R_V>(c3yZY|^_%BXtNU_odiACugv>tZNzSN&5syG5Q+>4kM`QVyyn`~k} zeJzQ?pux5Q0L0b;hh4uA%#Foeqx)PQC#JUTMa0(~9=y%v z$PqtWJeC7r0Y_>jSgZjkrc+b#;Z*a^(nVbH*5Ul&l)bjKzm~3}67&Cm>wo`F*LhjV zwM36kJjAcR58Qsk^_!%z`p|~&egq!90-49HlwC|n`|DO`Jul(=Oa>CPR0{!sdRiNE zi}KBUEHN@M1}6(yGcKnNXDdM`xmib)(2q_#=~vZ&j0yAN=w2AsnmJ61FVMjv#NplG z0cB570YSA|4FCcaM*ZN)!{p=FG_Zo-B~W5)%xs27qu$y3827h40gfIT6DY>`pK81~ zoRbMhqxz_(b_)@vWAxcZV5ze0O&6?zUma(FT2ve2EahYf&Mr z3I%f9NACRTZy)2^H>P3SW6phuo?5r1*S`5-Htu&ewegV(&mz7i|q&7hzWRECk!I8k`|X0!CRQ zU#LK(vH+X^r4(2Y_nPSY(Yh#7B$WjeQXo_jN0=Dwfh@Cyjl+>CUda~NII3cB_}DU) z@*u0E7NH^zem=pSoHg+x2c@0t=f2nwl6fxnhk!W>;s^T0*M$s*-dIBb%(QKG%}-ye z^#hu%WW!@$eJ$7HFc*{XV!O*R(qnW;*Z=oD?vHY7Zm!Y}Y*%F*=6AjpcI15YLGryb6@4KWIzA(~&hw`h+y2 zsfJgJHN~id z7JZpR$bJ%rh`rO@(rO9o)U%!?ymGA70+0seLb1sLn#5+3)~R(t9#w|Lx)pUeHP=B; zMNcW(9A0i7o|I#vfM#=cE&Hq;zao#xf|OZ4pq? z26&+60{|S)2<@&{slWycZlsh9D2uFBG*((wlc%+Su0}*WXi@WMVV)C^g#8Zmx~irt{v#JuXrG#nnYf)fc#Dpr(?drhvqp4cGnxfT=|vB`Z! zwkinYmvK2PsFdE5Z?F<8OJ>(z7}{D)m9bIk@4j#Q&2pkT!a>FB?C6|o{go|jyj8r<(uG$h$CIhqbe4zo=rfE+&REg1!CWAvCwl_z4l%^ z-F&X$bv8B7{{*a`|9b-QOK;m`2OzizKL7|5{`Xk>a6h>{`5=RVN-&E9oF99D7-}*;x;}<#HZG?6@{ofKg0f1tF$Y^0x-%3lMOL9M( z7v((&rI<=16BY{NPt!`xS^|ZhGW}L$5q;_qD`^Xu(71fs?*(=8R_5;H3R6FNZse; zaHa_9ejuOKmCUq95bzkRXbgHplGdzyrubBZoy)MQglNOqWT+|T3{7^)2up#fDh}^u z0;7y^){J)Pap!q@LPJ-li4*9b0$R@LBJggm90e@M+>*^$XxEwW@@)ZzQ95{ge2FiY6)&uQ$>t~Kg<MTt` z=6b#=JhfxbrjMXBS;0QaoKi6tmU-qcSx`y#OvEx|qK9+&#BV^z-$*lft8zZ|E8i_Q z;sf<&zvBW41~umZm<-+&&ZAcQ)_@fGq*4$zfAA*@B`FLV(-MsZo2N1UPlABgi?p2UtXgFVEL4G=E6I(Dd#nJ~PRFSAQ6IB+*l!9>K<$Xr3EzPmw zBnG)0U=mGLs(mgb3oC&n&4d1X8j2ypxYTVfj=BZcvKpYgS)3{cBM-jQyFoAT6<~}T zw-6ISQlyv&%+v>D1q_QJk{?>D^NGY$-EJN|>rlLO-|NP$&gP4E!vRmy0WRe6Yf|OW zG)OIqkb&Os5N{gf>yqzK+FVifu5uphby6gBtg9A4 zNh3K^RgWtj=KsxCV|dypS{oFU?<$kUxbYs87T`iZgc>7Ra4T_NTeMKV(8cFjqjpkD z0{|c})(?1m&)OOj1vl?n^n7W)KFqHVEm-@D9@zTrgpVN9Vhst~TqFq9tlnQfvT1j_ z0?ru~U=75T1VRZ4O&r(cs9|hMB&=TZ$tg6#U?hr+-&E3ZmCexinC!qkWSawyTp`2d@;n2fQMkXSRR;vSri;P~{E21Y~NA#ijv zMR9@hAf8o&wFadUJfaD$Zgp+qMIE^XR&tUrGZp*PZMmL_;CT1Hc#Vle1cHS{dI{J9 zZ)92o`{t^P>)T4~^>8J2Ex7@QL*7GGN%L$1 zk2oHAc^;Q3$d>I@QF{)^S0l$61tl7;xm;H+_V2*mB^k~AGHQ=%TwL8PM9Q@DU&?LU znJqVX(?-|0uE~)JuB_w-*kF+c0W^LFLe-NnWyqgLzD?yxs4j0(#;&H56`UcU9rWcy+W4D- zl<+s7S3>1g$`}<{AdS4U7Sy>mwk782C^DYA8Vy2F*Y#06|KH#2*8di0`@Jnq^%1nu z|5{sjY4h3o8J^`sdQC~q+aK;s?=6czbohAs>G1>aA!3v;oFO@V`_!M$EAm?bfpy$` z4abT7Sju1oCWVp1-7bM9&}4`MO+ttn6|pl8Ei|z6m_4`+%%HQJQ0V>U+qf)iwv85Q zK8&weJC6pC=Y!+u&oEEowDh8xdu2ZO+L15QR=jQXAg{H7Hp^rlFE3B8mMIj1T*l(E z==F-!R0RjBVUx3RP$Wb7$3^r1eUqfQ>u@~h6}?#FmZWZg3Box;92ABd9HcQxHq?%JLq>*eF7gFK zRz26TeBCy5HAw{Kb)$*jwux1D=$XaPdjRX4QIR#Ov(}llxGfB9d`rqOShVi@gyciv znmJnh79(_3*Rh*`k}x0E?__3iB54x7sXX&q;VUiWwYdCqd0|}^nuylSuFW{Zjx&?O z70q@bQ$hya8P7woRZ9oE@4lvZX|KU>L9b4&- zk&XwB^Krl9?pwaIa>x$~{0-n=b#watu|bfo`T7X_k)41k9$5Jx+&GiB zV67r02#Y3<$T5A^48l$vAJ4lZ>z=<1b7wK9Z;&*t1_PP%u~Lj^%5`Xh$gmE@>9W{A z#M6&8PW1m<;Y=|a6p^sEcmGaOq^$Oj>XM)z-kQzitG!M?D(k$}#x0bq-G1C|k8JOR z351X^}^A#yF0u0c87D zHny~Neh@{q+K4vSLL=exa(v+17X@Cp|9(%IQ7Q**hO~ZrcUGjt35d~2Y%?3L6Xj~B zs|W2xt3j78*DCc4y}$L^)ALJvEp=ANYRC@^8`{Kx)^cM&C`T_c2nV9DCpkWDuvvGF z9bHDDn%Ge_7MaKF)7y52F6Bibp+-q2nBma6z{w_T7I?L`hgx#uVluLP*!A{GlExt8 zonfh9!!^jFo3v~yNX&#v_RW+^@=#r#ASSp2+-Xseh|J}(ff@d&R#39dq-;wogRJuW zwYmnfTr{O?P8p%vMChe7SXCU`yGWk<;bv{fb)&RI%jk( zhS%6#iBzIWEu=dvP~6kdf;bG8TuW9f6`fHbV}oT6FowM?b{5N~RmGLjRtj-8m(uvX zZ0@X{iM+M3m78xTGhxv3NDo$mp`^(S4a?Ihop+nS{mGF75Z#gjyoPZM@@^RB$X9q> zk||<4x`o@TP`?n<)5puZ!HeRM&JvZ5`zA~nVagYfVj8#WX>?cY6{Fqu^AKB9l6utE zOEqoVwPz8}MYyQC-!bSImm{t|UMB2WEw`~;>_eOhl3bZtZqBXuE1|R-)^uuBp+?godym7GvB1}vO)B^EnW-Nu>s3@j+_;%cEGM3v4+-0cl`z=&=l zbk>?aI<5xL;zMn9&qJ*)WK1#WZMRA-wPc#moN>)HJ3H*oVMO|o)(OP#`v|Chda#B9FwEV zZnm})h^tX6wcSz1>PEdM@idfN5vQ_C7?(GINSlIMV+~xK5@VxO0?evPuyR+TZ|aI% zP}Y@HK%(WaBIO7S>}gH_phwGK)x|x=3I6kCo&Oj-&KF-n#hEXsd(%RB?=%B|-2Z;= zX0YyP`!U#kf)oEIo%uP08w+!JO*^Z5?*b75Wld~*NDv)`ZBH=z~|d~zKu3mCD28fXCkb#mV& zxn9!e7s}gW0v8vttI3*+F#HEcqIsjffP|n0bk=-6*(?-oa&3q_i)sX{9%mG<0D@rAVMtmEbzTWy*}7K>9ksH{%4&1;AWFDXVT z50Y6uk_irnipZ0~AgVeQsCu)x#$2pbX(8BZ#oY^nwW;g8jF(_)iHa282v-H~GNK;8 zN=exE?{Q1=B%W6eY7e4`W# zo7jU>hs7x@xJ%<|zEww~77IRw+73c+aO@1Apr@axa4ZWLkC-qLC_BW|-T)#N5dcuq zX2P1;4W>T3Qp}vu%5k-VHM~*=h>=>3{y^_<;i2Hj#t=o}<-V~A^?L1KsR+c$hL9^k z3Ni^NH!*>N%2hCAeLUL?sH7?VO1w=|3X^65i!EY9%n?me>C8MzUa&shP^|)|!j(kA zwl9L$$(F$pEm$kk%XU&{J51eR%`&hq8RyKIj@og8nZ%Z>Q5W;E{K!}dMk>}_jl0bP zpc}#L$u`xUCg4-E@LR#{)RVwQ><&JnOf^~*g#f&&xzZLWldDNZgjfsCqIk zabBC~ZUI27C8e?0XmJMUswPLlyOXk13okLTUYCdqDzTvjGS>Y|tye9}7}eDHdY3gR zW3LFtj@N|D3qI4JrE&5T4N%dq_f-Yuy)`!PstD6#R1U}1$0vLC;nG7)08hR1@D5G@ zp@!6HM6gE^2)GyN$%skJRZ28f`%FMIF<$J`Z2Mb$%thkms6pbjF zL*iQJQGMysKWjAJ{d=V-7qW&UG3Fd3zi)bjy(z-B`KV{;`lcYnukL>6_Vv$NdtoUI zv5Jk@R4c*#ra7l$?=@bwTP>R8WTks}KQ>WQe#Q6Zf>mDI(Dpw(JUKP~^S>;-S@_EE z8_OU4_PZ_*TNysW@i(Ycd;J@A#}`-rrg~#Ka36TsN@^8t@fl@L>q|MH!DFWJL}{H! zIMxmmsskpkNn|jj{7e;eO*Uq7B=s4hFjgHz`9hR)k6NDZZ=2QtfdehQ1doj(v8lXJ*D4pGI6)@mqM5 zV0J#Uh#e2Un#xq;Cr010B9ka=wGwg0NKyhfDLeAv_;>A|g?>WrzyjCi!_RKIR5f1~ z)j@{#GU#O(UxRAx1=>MJQ8z_8O7u%JC8f>@`^qE2?kX;HLd z0)v!`ZPXiEAG0cQp)uCPcP_^kBv&me{Q7tP+ovrFyOr0!^K1CZ5c#{R51dbHyS)d0 z7rEz+9Q0b8pCqXBJEK};K>Jb8o6U}pDU;UR5OZQ0m^Y$6b}^56JGtC=R*f=83aiUJ zx=St{=Go3?#`lrc06eL^`*a@1qw;@$xcDf+39G<$)f+VDs~Yo*;ikxQ+;nHL1jD~^ zvb>>;VmYqH$z@ zps3W1$xkbDYYVDCU)w6<-pWZ3lJsa;G)DrTT^-(8Il&y;ZWNLnfB=LWcLQA!8O=qD zc+gyt9Cv)J$Czp-4wIT9+!UzR>9t6E{HFO=j~2ax>cq8ZQ6iPN+tsK69R3B_w#3np z^n8q)a2eS~DC2 zSAe}4v{N)`-6yJbKj>+_K3u~gd2W{X-+Q_7WM?OV*lbeZ`#c|jJYdr79XJUg6!y89rtcbWd+XZ*=Y zVNrnI*o=_fQHo9p)(N7$AHD8V3u)MFq72ms14^axcH7_Vk1H3?Zx@TX5acN=mA=~g zp16%zBJR^)yiZ}MOP|&p(aShx8<<#$No-O|!o`@pQT%c@|522|-MF#LHubl{kJ1SB zj)g};4l`0zGAOY?p?ZRUpRTliP|IEIovpJoqNq2|o@spMQo+43Ie4jj@M<-UGBzQR zcF-+yxm)5kag#YiuMlOXvNiJ;RF;3#Y>)!rf7A&WeNkA^=_R*q>gA)(B%;+=)aysw z=_c|TISJ~9bK^uucK`EuKU*%ACe!gg)CJ6IByZ-W^401aYT~`r4e-@? zK|-Kf6(;nR%IKqq#Yc}E-xi*2w~dG2Xe;bja=*5zhd~QjigC~KG#fS3tQ3e@c`4#S z+%1i=+#(Du7L0Vum{h?`F zp|DCVdIUNlly*FZ6%QH{r3sK(!B11sk>{cABAf>;aJuDFu9a6;wo}utmR!CUd5uJy z7~zR&!Nj<00;;hFDUXS1?5LU1ax9rQd{|(-L_eyZ?pogn-rb%}ES&@GN(OxdHSSWT8jNGk&!}T_Wm0IwrDBT{3C;3EH>3@-Q}qV52ofqVC!W?*`XR8 zt55-SfXrg?I#0R7$O1VO-b|4J4-0duF~h>~Rx*=}B4~_2Z#R;N?B>RF0F$ZKmf+s9 z#iFqZ#q)0ZCo=c?5WLA56b&FXQw1wZ30p~0+T?jgIygmk6flvz8heor$s>+jPd#n! z!p>L2hu?aGO`&Y;lSOH?{J~4BkAb;uI66ISvR&6b_;>HrAJ$J50}%#CXaCtKBQ(7CGI#;SJXv z;M|^+5_C6zC3;ht%TVuBAff3kmeN@JqoZCa4XuZo*`L?9Z=a{HvNKWhTAsq1i7O1# zE$WqQLHJ1OaVj<%F`G!Dxp}I>KJ9e6;O%=9M1;sc0`4|zO~#+N_{T3SeVW#yrC_}` zhBb#}-2CpdC9@Czk!WNrV>8jXp|}W_iu57~#R2DbX@J(L>$sz4PXz;0vsulTNfrD3 zo|_Gh@|K~gvdH)Qns-4_RKv18A8yMgAawpRbk6JQe6}s?EF&wf;tUQ~m=nzi@w}hs zFXtm7gGlyE<#KfE6RcQeDc@(=?&ZJTvCuz5jRs>q`uFxyaeVK)waUwRM9)RM?3Q8ThF5cf1`f)gyvfOKl^{fgdV+fziGgo=9nLhf*f9T}bWmu9?+~~S^Tck{ngUh;(H8W3@FE< zzQBWyN0)S5619voNt@4Ab+IpS(p{w~P~tdXHQrFI$MXogsuh$gPgoaAX;k;(9bGvK zM+KTCL_y&kxngdVI&C)CK!O!9)eFGw9IupkDAq@6;0b-CBO$rLfhvcRR8R#P_}#WH z9gMB4PWfHL1CWDh}3M zME{!%1{T?S&+{hziv7A?&wq+Me(pKl^A}Lp_-+cw!UOu5H)qU6+AX^Y0&=?414CpH|-8 z`)YO168?=|oE;xKtGPS5E7kNJcy)bv1(6a3dIzluBMK2`@kD@|GxzuvXH5 Oe&? z0oUhdbW!1)ZL2(@8V3#vk^0l0okx zVk=2VlUN1uEcf~G@)i4}3th^S98U0UMjk2D6c~vgo?`piM*ihKY5s|`d?#qyrh!UF zt>y3j;K!%>YktrF@DF}h9dq2OFS(wvdGw0h{!*0$i&%2`!c`>-e%8^{F_xA2yo3DZ zXo%cFwB{&TiUygFFN3c{@Kljyep9$XZtI4aN3Z)5iIv(8?lwa%P+vjAvbC@03ctC3 zioLnicz*KMa5Ti98h$4iUN6kEh0FbQ_G)V?Uu+y>#hK5ZegpVSf=_HLeLt?5z~ki$ z(s3}rF0Sf0?+rrpNCFv?Jg|PFayK`+(r(5 z^V2|QARngR4%UGG|Hks_!dscQ0=4|0!5kZ>lUxsuu&Ji~kwqFSyxZjv4rZ;Zhl7f& zA_$B`QUk^`k^%Z90OGh<>879%l2R@iazGFgeMHZyCK68%tyMVCmSFImux{LOXy%w1VK>B=Jwba`B(fZ!@(aJIn9iZ_>rv;%I!#;S# zq>qbxo|grc`$jfw39bGLvD+H#%pMN@7yR|zyR|JI&^5jRtmp89iO&v?MM#Ymo2+hi zckA)OA^}I?5575~u#gl)E1VZMq!PKKmto491O{yqJ8ezdfo^=-hUMX_)yRm{gc0qR z4>SoEs#OUa&LlD6#D})S&K+)L+IMw9q?Y#hHDa9+J!Z}6Pm~a1^+WN9uI2C@rPkUn zcaL7Ho#r`Jz}w;f{ZmK2AbeU|dL-#wGGb~H-)`l)@@;(G!Gbv3eZJGmf^QoM8EiD} z=+X@$gJvq9xil;i*~5YdlxhRlt&P0Q-gNvLP&fPC(7_#cFYW%7oITa=+cxy;uUB-SW<)upS$(RYc_St7KV`p&L||X&b9GM9hFm|zn0BKbFA$Y(I0TvS zaB^O{V?s&)+2Ekw3hd$YNnMAu-F}{bYb~nLFheeoh4bFdgwUQl+%8)nK?u$Tk#4oa zN6Pn99W!&4RsDQte`Wka>Mg0QFZqpJ3DZ~2^kH8*@wdC{JM=8;(^Hi=V?aM0qVRyoLd`004-g_Ku zmA39aA-X~SlrUCOuvWJH0UJQpC(sLQ(+{${i%8PQ;RWceoo?4*l`hWkE@$rN6`ANY zWNKot8t>002%}VOgji;o^oHW-D!#*#K^{C|+wZ6@1p%>yf-mAW7?5Iu$_zLwxqs?4 z>tl5{+{rY`a5!QZjp2gw%TEQmBwas}3JMo?OL#|DVf&jG=-T7@k) zLAO2@UXDdiZgiqpFjb`5tpMjDqOQB(?St0Q>}KWT zXW93Z)_{En&$LIKaeJLZ6I?4Ca4S6qPdq?>p3?E?Y3phAp(~v30Ngx{npg#tt|!`T zn`eL$O%pU_s&H-Joi!kefFz_-n;rWXhfZ~OrbFQZo*zYWaopYqiVIuJd@k-My*5HpfHlq|zmbuhSzsX8{`VB9-ZdW&hQKu70#X zlX>H(?IO@FZuHI=QkighXjc^x>-C8F$8KF>V^17oIf-H3Zc_(U6ihKZm5I>h&`f=N znh%4Lpif7OVhRs-+82Nw#^M+~L0>e6L>}eFB#wQJ<0l!{2U~hbM^l6wK2B2{E^s%A z=kvT#@XBH8qU%dh6E16yw=T6cu^RKZJ$w2XH5PeYNR8Iqk#V!Yi2~R4-HaiR!wi|w z7*%yX@xWc+iAGP3wmBz>Tvuajvw#pa8Z%vEAx##F?8n^%{S(VZZctLWmx}_|)-^?v z!W%^IhM&4m4!RoO8E5&BzHXygy*VYkW$NB1uO93NP8rX3)@wQGyic_Dp#ww2}YE=K$R{=a|!h@4+sxqxV; zg*^~*V`yu}+w|tzAO7T#u_WvlUjE_lt7AUlt%vSf?#orrxgBMp^We+wY-pp{0Yw4x zgrn#EYW_b4rvqlcn$KP>Jhjc;XUhbbomBq73j!KnegL9jNdg7JECm0AxUIhR*)PD) z3dTk&2N}BHukQkTZq7;g#r>G3q{)T8zrRQA!uL+SxeeJic!+jpJ6R^YqxsJ6YHJly znY+AaT-s6QyHz5IL67V-Khp^c$aE4WvwK_L4?8)PxOd z@8Z#Lfz*7EXsJ0}$8h#|bKS3aLQN$~mmssE9AOiP9iA=*BF52j1CNz>JtgWZv}$;PcXR_; zC$29Q$RdgZrqNM`Uhm2TlL$)AtBFEb_QE0kST6famu$ic3+VGDl7;ZN=D}~Kr3N={ z&}NLIy^r`rgF5C8k^D-Xd*9rUh2b)J6bAuV2cYgt(Wi#y;ue=I*q{-MrgPfoMj`{3LFJUZY( z0heaZ@^Ne~5q@nLnL5nO@n&mcCW=k7tNu5ji%1hmpv^-+q#4x*CxuTfZ#Plko z&*iEyBC|15Q``Q$E~gIerw#+;hry=xtfbmVmyo8RNRVN6pbiaq`PxJ?XIt1PZW!m2 z%~gu_{9vgF3kBRE9g-1P<|hM=-&X;v`n|j<;lw1L;0~S0!y)OC^ae4r_QHx)ZdgJQ zErdnfKY2(7>{z8;HAOL^%$6uuJP@ggcR)B)JinpXHBlzqr1V*5y;8FsS!hAWWI=&G zRGe`*=pG`u`N~=s9Y-=MWs?AeIA~N?mON-{Y+MN9I7?zXq}aAk8e@C_J!j{i=(cDx zruD-jBm`67*n~PhMt|t@Vxttc$X5oC74k8HV_N@GI`A}j+I2c5jk4hTXc`p(#@J9M zf?yz7S{Q&%mOvVA7J#(sRKdv7IhR0C0|J#gwJ|Xalrd5&6kz&PbVsyty=|v4SN6+g z7qBCLGP*1rxbU<%0e2*2dF)|CV7vBeUP_z0SjZ7B9q6%30TagM06E0aB~xrHqTnrN(!8Hm_?|ilcGTAgTJf zs5uG^5|yA~EV^aa3tzVo#6_J|(fT4LqN7q&$*$sX^aRjgSB!*cB?0QxKx|CEA|^bx zANg@HfC0?0d2+D3f%kbG^R98QjK7TwD0z%~96whUn6iglchZS31_w&3;RXUE@%r{) zzbVUE6Gi)3*C*CDB@SqEoiipO9Y$6Ds|MzhMDHI-cvkCVtvdVavE5Z|)LT}L-Gq?T zGcyHoOPx3rJZX)pBx$J%B}3{smJ^mOy9f7cbp8qlonh8TwK0h^l>tV=Wnz-$ti*%* zN+);@JQ=!3xglrFwsYTDyoRaK=rlF`j-^p)I6#>X96^a8cjk|+0fMJW$^+q?iPQwl zxGWwmm#2f2ICR-Y+c`eFnPCXlm>5mRQTsH2i1$@i{GK^g`-n8pG1FeuRVHy43T=`g z2LK_bdt)PeMv-0&!7nO@ryD}RpGcKcXc`E=AUgcmrDlCH{rB2;F} z2{UTKV#_>TZaDs}f32@zS$)PDm+bkFY>$pft}SzeYhx-$hgtgrTPO*xoR{Fz#1jbA zgqv+YHcycsn1x5z@tTZ1BPE0?hd-{7L`gE?T$+Ro<%VP`!5kP)6*NuoYXb~__dhd- z`XD_Un>R-2$NiX_quJP8&K5o^vf*stuDOa$$6N)IFUc{u+j__KT<0uVYW{>7c+?`> zPZ$E9^%cWIF$SlM%Tp}=#jlcQCjNLa&3QXq4PMGsOj96OP-|nqpDo7f&j1nYYdEmM zFMywGx6OrwY7-5q2DYLJN6%8w*QYFRCA&lFA=hyHdJ)*>WrjUy}Iq_PaeIEZ4oB*>biKzf}5)J6#$v&aq; zEj;`pki}zwlPrewM*P-Gb(zn^Y%FQWJ4;Z8fAD)<|DCR#GD54Jk4n>C<`px-Q2!}> zR4ufOIi6@n218=Pc&T)$U+9VPy|H=rp)wZR&MvhiC}S-J_{KM?cCM}Ulrkc~x4?HY;8EN@luPKprb!^o zpzS_kf7qW$7+gZ9OeTnIL(UfM+x3)~S0-3xJJBnRV-?kN)TmTJ3rtS1uxbzWOYt}s z`@5nG%qJenj6RwH-pbE`C<=mL7F{J^cDDJABk=m30%Kx7zM@^a z)O}InquOMj8&ZLEsq3>f`R}jp+6YggN36wI@1hE=$Qs9EV@%J7as-8Jk+?aDDsxG%(Gg;1RYFUZ zWJK6VGKdV^b$py2&6rWe1)89#W9;#)S>w0F_zU7^O+a5$iXc9(*p3U48ZW%96c4w) zJZL76;>^FAXEd^-1h9;+A->wICFWy-)(b`Aiys&p;Q@SLKefyNBA~U+eSAMmwM3%;-lwxeXHHr2s<;*3G4O6% zrzXuf7($_1zD5%;u}vwZ56$|V7{I;=U{ z=5-^;)YPnS1y6IjTz{%amV;IaU6Q=>xS-HdBq%HB0^w-^yEcGOE{dwir-mr*C}-1z z(XwJnTdmO!oDNnNu>~y2Ef(d3p3^gjD?erkiPZw+J92On*>S+3G{mnLd25VypA8J* znbM9U0M-O)w8l<#=n{xeamlB!MALxEbKO`|&42;XFnn>3!h@;Yunwu(dhs{T)IT-a-CC$htjdbO^XNeAZd(NN>{f7 zVS^H+{ROeJ^m;#{+;`F~xECsdtLT~n5G78<=u>CO<~EO6^ZY(Q0`htr2+VF^0|Q}U ztbw=)aIDc9d9bblzKy8(^%#?4A)fB4`#(X*3bU*4>G6*#=EAAD{&ln`g>l2h{-?E5 z2Bm^d9glXT!Y@*sn}GCMJg|@Km%Vk?KdB4o&-|9rA3y0oY)sOn^tJu#cK%!gMeCoo zRDq8Xs?+61K%B2+xJL~Mzt$U1+xxiU zAsx;DX9cvQJgUEyX7}gam!FtAIofHC#K(F^gEqkfPEwgZk)R2hyUt4Ps5~mZCt%m? zFdft6e@eB|)!-*rTHii(?$UF&#@pXnUyaT^_q#D^AQRD`ZR|)LlAX-{KP31qKu@^8 zzgs`rI#iy1h&wmvefXF>FrPtUK#fTSwaFU|!h zuRjKiUC`j+p1%nsqWoWot_4%Z;mrCI*0Bv0H2BfB62P7HS5A9-uh4$!qfkAUxSc0% z0ird)Dr8p{9M778Jzm18$;op3tcSHcvGa&KFdTNcl4>j-oyk!#;|eUNpkn&Ud9i-l zUonLl{TVmhQSvgUwZ{s?zOi|DpH~d?YuF9zq5&pb!jDl6j0w@VIXv)h_6Utw7rq zS=EhWyyqyeq+0HX?jrNlQiY8B&VzX$iJi1mVZk#$%<62Xzps+D%=#*(DQV78hmlbO zBkIR(l?W$k>A*MH6G=~VELq~y)K3mdRVA70f~hlJ49f%Cm(4E9XiZZM+g?9LlUjYj zR#^$9Z9Z`uq{uq}h6V02gs{x7w+5zK17@tZ+9$xAiO~QEk)!PNdJz27UJ6pT|NEod zOdq1SBj84)2^(Qj^wgXKRd&F7!pL#47#9*95Jq7BHNe%b*8mAO%w^@MY)totjgzvh zT+i1;evstZy_qE~D9yFg)XpD)ts3gY%H-!^hn&xuH})ty^llhK!)W|(gstX&^a14} zsIx`t_LyzKXG5Wm23)4V_!V$?2WR~H{CDuS2USq6$Y~3=;cM*z)dQeeS zhcHEN-pFU!eQHLE*43V4D+pHw#deG{yDy^gsv|mYd(-V|%?TRY4YoBcd9l})Do8Er zcG9fX4XU0j_YnMK3o6f{{i$UtRa{k|XsY(akh8SW2Og?nBLZ_v4@OqyjHz?$R7KPFxwstTaD1$7jC`~Psl3Q6hunS)QX%BS7NJa>t{6z@K=6R5 zdn*)OsVlOtTbR~t?Rk|B;|y7@wX444ECg1?0#om4nX7S(Iy#roq<8Em)0`8Wd>b)s zYaO{DFP>J+ql4c;z!<`~Rwvfbc)Rlubo7d?jhVIK=C3wx;QcIN#!MMX-RBi9%lq?^ z{n`*^*ZC0Q18-o)!X^7c0c@w+d>p)4mCv?BAj~u%5Nak<_H%}m-wr@oHm0>9Rx$`n z#YRXA4x4Ug8n$&EZ%ec7FI6Mby`s|B7mJ=nz+gTggJ#DaVfIg@o2+?$h zt^=8k5XQT##a|87DJ87rP#+Nm+-^4;zD92QSWNnd=-4EC?ARR(msyq@*9^$HL%Il^~LBMbw z0`{UzGp!BX3WVwCLGcDxJBgNryXcRwfl;|?M{At@2Z6yZ*u@jTUsxUH(zI;ncz#I!Z_ zSzb|E<)1GYG0q2gg@`=H72@2+WPe9{rU41mVytV2rY7cD05L`E`d1{~IcvjOi{_Ie zI?;l`fYZ7Mz0xXBhnHD3CmY5&%g1qQ>bKXGN={imZcE0j5Tpn>@4;ua7>; zJbFX%N3Yl4pEXJkY4p3tfBbQv@zuY)A}+m3c&e9siS^#b?^ht}=?UiY?BS;~aW_|i z3-D>U4<;a(5TX1f3KV+Go_sDlp^$J_#O&Z%u|U?MfBKEHGx`z|an6Mt{-TcKCmE0U z?UY&pn=^BO!UKS@NBkTxw3zfqDBDi||NpGV|JKm2elmV}<2TE{;dlkOu;Cxx{#`Gh zu47PR)r8iWDK=t7=Gt;@gjbx4THV>i4yRHInOx~%5n*0Z^MwRebV_CB44T%)p1thx z_{(r4%ejdsFw9IVb@}85Lq#~Pp_&1z5Y2j>zB*k1#XuvG>A=(lk{jdZVR3PKx~}UP z=eXZww((FtWpQ#ZhLFZ~iU4Q%n^HT=Uk?gB(5KR2cz3L%*|h^Yv0UjsVw%g+i6As1 zQEey=Ys%f0%%9itM|MVXb&P_W6m8|1X_7m48oa`6VOFa-SL|pHdp&ofQb`*|=674A z-Mv|qhv1kB`mnCYL#?2oZ~M!7=n_8Bbt1QYbx!B3$Afygoh-9Q zEqC}WZohp)#2>wxi{2_JTmoB3FeH0F2G>UtMm&XyH9}O362X}Vohoywo4PlTflfeg zMr8CS3w3fO#dErZ-mHx<n1EG=UF#VhiM>An+YxCvl}?ji%*) z>e#wR@fTl`!XL-i>7W@lWOyK5f9kMEbme!6%`8{&uKrK&snB;1c)>j6*QVz2wY$kK zPQ(dM&*SUjW)@)+>;V-tlr^CT^<{5I_8@LRXsm~2_H=!ScUkq0f}r zfMvhIg@2rQyG}x9N4w|{>(rchFW7zj!_Wia&6^0_y!hyi3#_~4-8&G@e?_sD-Obg6>NRf*%WGjhH}qi1H*{xrb5K24bULz4~PQW7SLz5aSGb(4sKT)wwRCGVY8b}3m|?d>VMH#eADs~FEV zR$iWnv2fjWhF$c3<}8==G4jva}r7F=_+xODdV?$C+P4(M*?rL`3dvEv)47crbGE*bzi zXhb5%&qnNAMGu!^n@joO@@Ys`pj-5{lLGefSj`Qh3HbZvyQ$uAl9W$8zbtNH(Fi^p z@sT=b&HgNP}= zC|=OEJAd;j-2eS={>ncozK%D-_US{?tMw_Aa~j#AGX4Wau2(r!*ha%)CgHY#7-Rhu zVnQ6`50@-^zL-5B1W;A**VQ+|XQVfDsi*Rb;B@X9?*W(-OD5!E+>92D#%8R^rHw`r z-k9Gea2kOHpaTrNEQ3qLS2Y<(|H(T4qG+LiZstAcPggnbdvB#we77vMW{;-7)5t*6 z{);c~-)#8wr%5PUUh)@xLKsM2TVk`IWYO8iyk+r(7Ls0ONB;6k3#R`3*&&7;hA^}# z_$Xkun61(Fk5?B96uq@J&%l@!FEYfhRbRxPgO#?y!`K>`&gbWGqvohQZ&>b_Gw8x1 zpT<$ttn9~w%SHiA+px*iwBOIF-GaJn>Z&4fqIE4(-+}R=$dp`RP};wR1oGEXrrxyW zCE`jn6Qwu(*3l)6qVulXmvtNfBq2XmOdu695 z_KtakD}?OpfqIuYnBD$taB;JEjL8T-C5*8wj@|pVC}O7KW0W!28Wy#u0GCkk5E%KU z>>x^WE6Mcylj3~r3kL?vKoz4&>F;DL&21(p=O!8lu`fgzDzz!CMd|N~{5VaM^Qx~~ z`RAW~Qk{?Us{cWi&Fjs9l2Ql6%}xDa&g93jyL|easV#zS`Plx%jb8A>NU;2hJ@w;0 z+|~#O9|+09<)Rn2v$y@--~JQ)>nGswN^8%Z55P8@fdg<1{4$tvJ5#X1OO#B3h#)ay^chlT`ZH6|iSF)PQXE+Ya_S;6KdNktqm?(?a4KqN?5ln3`LwJ} zqV_>vewmw2&#Sr)7UDZlg27zWGD)S0ia{Q3CS`BKDFhcBHWm36Uj2NQRQ_LYf^(|}m%up~gHI-hw(UEO$YKk0PDPCWf;8p#d zGPWFltms01YE47V*A!in^pu55k!2++37rVQnxfFw41BfvgI;I>pH52Aaa7h4~kJOss^5muL23CUX0_S$zes)<*$#<0x?n57zlv8e?3rY;4YYOjVLZfv(CQ1>tIwV0d1LndY@4gzSBh6|-(@X`#upA4y@rQe~ zx-Ir3*&eR(lLZ*89FL@}DDg;Z`3vcpA@SM!RI^U>WCO)qu~cGs#f`Y*ij`B%@)htr zZS!)Pl?=(eTs|yjgDCAnDKBx7uMR`dc_bT2MvACM8X8rmCf_9)!zV?mNh|L*gC+Iu z9s(*$b-7syFSxW9omxtgkgwWQv`KP{wSjjHoKw8q(xhh}>A!L3?%i8c{oWrOI8RZj zT!}*8^?c8ig*JjBo5VJjHDk2Or>eTA(Pj)!0YEi4Vy1jO* zee0g2fAI0d%@SUoDamEk3o8oZFbC4*;@8g1F6}Ec6{oc~{mK4obRG?qJ$4=w<;-xp zi<#?yH!F=5X-`#Y^GxaV(K+*k42>AWRK84QY=g_~19Q!?qH6xaW{#Na`O==$rW$6d z<$MSXYp!dkkWp+}%pw%`>sS&(B$5mYs;s!JC$CMi3IItR?}t&oLOVZ)c!~qgxQJ|G zBGbveu^ze_6r6P~0t`f9$!+edI()}FP}d9ErX^*-PVfuxD`{z(cUnz_cD>n(_hQ@W zuRK`ubzveUv1Z&W=@HmEqJ_BJ84~IpmvOl+lQmbMLfqFJQ=tXUXvti7if=SszL{9Y zq~nDXC@sN#>8vB}lzRlNu>Rv_oE@btYsWGrYnT{P0C?mjve>C$ax0Da4(niJE#E}I z9hQYl`v0oZSXGrpBo*N9T}>ba+KYN9D5@xFC_zyt;d)z4Z38(bT=LX1P0LUOV|T}% zVK>wx&xl}sSD>PPCXHSYvLeTig>BXmpkujZ^C5{c|vhb5FOXk)gILA4cmQ)o-VRAWl8Ahkmzm9))XvB?zUrhTwA385adP>2|aDRN(pmCvlKg zlrAn0Ye=k%7Vo=Lg-NT$Xf1fq%DRSRS)Qeq#`8ItJ{U)&$_pZ-lG16HX0l@V{t%v` z^r0*Z4|Ic~rlb#l8mm4Ov+6@$cP*xK_Ov1KaoP`SWnYyz!GO#cTuWtSbgbf#lp)zg zvlLmroNM#$PS+m0D9LhVG4m^ZXGW!615#UPq;1RcLr2OB9d|Sgp4M(wn)^li0<01j zUxT6WMM0-ag-?u5qjHJqPH)R@@X?a|6#-s&C zB9>!Yp)n{}6Gc>#ij>vr{uDX>OyxNwk!QZ8SM1SdQtfi-=JN9D?&@9d*sxy0*DNdzEe*cgz+t3`i8pXWj}J)RYFfy5S9J_)bnbG;B(-MI){t}|4FyW zs#wxqQ=*=tx`i2&MLyOuk}_Ee3UFu4PkJ66CIjUGZ;*W_GceaPCWKHPGsLYKw)j?a z#z17l0CCoI0h4;#W{2!(32LX6_9^LXChgGUPOiU{w6Y)_U&B!WucYvz$)Q*ZQ{n5R zELEfU0g}FD#?iO!1e`M&Wvmf67XichX;n>W464FI0xMcPj@DBc91o4mQbOJ6(J7TB zZ_SaJL(GI>U}DjRWDZRn8y@HS3g5-N53%xI8EK`7jjgpcdDM9Ms+y~`^2UiD_QG!> z{3eQB$}hNjl-do?V$Z0VSt7+blj3kow!>ZB_IppIe8X#Jrs;-+;tPJtGw-SE?3kt~ zl)^HE4P{QUHkD~oCir^L>{U(S8u|V|KlY7)#aPW3ok=;mXcFVeZw+d8o=s2Ox-nHV zgr)~C6|>ZFT*PqRgY&|pfdtdkgYZfD`sQ9+8~vKM)LIH5jZw7t(Knh|WxFWOBUPiLR+_hxs9Wpz=+Wq_{||Y`m>$Z( zI^Y+I<)^Zdo$~*?usmzLY=(sp@aK!Pw>rti`U$D3fBi&-PWBtXPy;>&K4G>T*NnN= zwv*@`$m*y@(U;bH{=}NiC{JZ+7Xh8`eiv7~@6mKXa%uC9krgIr`)Juoi>HKyx)UdR zkifhKV$;&dMHv?iNL$M=RXE&23aE8$R|V$r*oHCMPr$Sa4M3RKn?z<&Lw-6F4Y<_u z$K;JRBzEOw3_W%{)7#}5Y!4^}8m)>@E7xV;`24YB-n z`eBYCoS-?CTZo!YaW&J%4#UpCX>Kg-p-B)IH0Ia=`aaMD*EMDv7og0?i;x!)4LzHd z1FSy8a(em-cRWHl9d{?NfOHtU$ zL$N39FM_NG6qKR*PH{a?JsSzbC`n{lma_AHo2sMbd!k;bR&8BmV*vbfzc8eXCqqFP z%&mD^9z*jAt&KWv)~B0Z2AI9U$#!sh_t*2B-DF-zS@;4le12&u#H2xvS{d)NLzYSI z$0WwPTgGk-QGETxTZ)*dh#h5z;7_=>>34>}ryo5GU*f30GZu`01eRET%kRw7DR^c9 z@Z|l&%EF`PIG?iX?h^m-kBT0aD1LZlhKKxE;$!`U`7{xfW|R=x zA(b10=BaC_oo*^LAeq}AjqboW ze)E;ngWBv4If3AX5qLkNRsa#CyOaw~p4dsdGe9D-;SRVH)H?+tNP>#T&{tC*!x%3V zA9o|6!eb_RrPn`~d4og8S%n%O(5vQYFSDMU!&R8q8?@nhdg-*krdVdpf_fn%IS<8L z!afwqs><0SS%rBT6lh3_hA2wiNLevGed=uP?RcGARRore2;jKDBXeD4!g$cccrr1E z7G5TxO5-9r)C*bo_Ow!52TPw`KcQ#zzOr&!e@ej3{?>VT*3WDV&NDtm0N^jtih45F zC0ZJ}+!XEeO%BZh)w(Kb1pv+vrv{f1K&t^KBG}zFMpp;PTs$o7M9~rpJKIa!`z;h6 zp~I!wonq}E`an5>qoeJ;V|wR|T;MW-fax-K<rZ zTA4RT#rF7!vA~>V9xM}n8sUQT0L}nckxUzYAKfs~Ea!w-J>>}?Oc4fm!Kj0ydA!x8US77{ z_lgIL_?6?Md+ye+gm`#Jt8>cD{l)J7hhDdJFyFzG2JomH0+AR;hEsag+}dT9sW21C z5=q=*SIuJ4%zurSPKG%6?C->R_Osd;@2CTFw3&%$`7sk5seannbN5pu?bR~rX|Go? zS=1zve`_A36vgOq#m|+4CLSGLaiZ+OOhb*=lWD7d8r5}`mr`Bt2Kni*dBe7{oFbd2 zshY`T9A9qr!+=>l+H^8ij?8Mli`_k;HvX#73*I<8bnP2c`DQAuY@sSBt`tnEHMxU| zLPvs|6(1~xW4R(e<3?V$e$)x-O6ur-EyFqHJtefo{wtVbD|i?@rkteh0xM-}kmaC7 z-i`Mtt}-`-pzB~Q!_m2y0tQ+gDak6@FTZ(=S=eN z$J0H$?@Y6dKh9PvT=RH_UKgI~AG<$sxI&)p-~HyC)$dRFFN=1epi|iqG$p=dOFZoV zYA1`v;2(bQ80|XwSi-^Wzv|EjcGf{GZN_LET7J<{35(V91@v0#MB!r4yhUp2qh&;T znz1U%?%g}%?Ie!jaP%A=IyD*5jT1qQp=L#q$86fN>~7~LhCA9)3Okl>I)y5Q6#x19 z#Z-XU#Z;j*aSW{GcF1vkjF!0bK;Di06KzY0jV4DX%vg*iR&&f2`&&3; z*WV?cAAFkW>^4Gv)sD8SA?#D0Xc*uz{=k&n%$d!YQKAr-0r*NCsl7#FC=8u3MFQMe6 z+#sLy#XyCPU!okOfOp|-rZ-^|Y-Y{uuxb-426Ro;}SyIYfwp?Vsh6o zZQ4%H8rT{JL~SAbSNEMBol#Oys0M7ai4j%d8c(IaCgPW%V~2H-#Am4Kv`0+Bo=;Gz zJ4`aWY6b~xLLsLUu@g6%PrSL?<2}EIW&@G*lt)y48}35{SY8qXZsHG-4fqZe1Qe@; z2XNo%WX|o!i=gDUv%hg)!^SE}@{xJ$q-UOzQpageU4ebZVeIR-TP}lzeJV%c=|7GF zgw*1efkP&B&_#qOaG$;zt_ZnIH9f%0>NYH76PD+>;RFq?@Y%-QJ0K+{1i#EC;@Pj>}{^-PFg^1He2$aopu8O z*l9Hon6#R3UqQrR-x?bB@z(s)PnSje>UwPP>76Zr2Hi9pP2s77NCRiEmNXSkdRfk6 z9YLl+<|7WoJ&yHZV16`gEz30$$hYqZ_$cmATk;O*t^p0h!ve~ors*1j!czp{;0Sra z9SE%D1WmnO&U3aV%boru@^Ybk)NKU;X9b-Yc}yK4O61?B#kb+;uAjH;2b3wS4ls3r4{$Zn09}Rh zU``hb`JL`Mcel5}PI=2eT~k{J`)4loj9x45J*Le`<7~+yoP+_(tip*~Hm7g&r>h!M zs=4`lu~(g%zS}dI>bOm`gh|s2fP{vLJ=FdsBF%da*4y)`Ya0br(J?UM|(7t89;^H*wN98wk^Kjxk6N(G||M z#gaqMjvO8&Csa%yfQS+;ea}&0>Jq2=_`OSu*U+3k1ulDZWwfa=8rw!uW$!?F` zE-*cIT=xru?NHA_`4$Ud`@v@}4Hm;v>1&m8STs$1>6hRCwSJ1D5iZ3FM97j0R+E?$ zwo9B9m4wM;t)y`}sHLhHjxY+T>u`>5TAMQseY7$JO>2iNiW+0xSL)bGN(9)RzQsDw zYoX7x0VXH+xa^1X4w9f<1lcuR-Nwzo^?b20DuhV#sDp&Li<}qxu0XXM5{N|BVN8RS zcriZ(PkcbJrHPDdjeUCVpbW&K#V`z~=_tgM0k)J7*C^`cCO9^=ijf~ZQN$&Ek3nM-@ZrK_S zxp76@-!7L@{HK$<)d^nuvjhKWw=zgi*)i-PycyK6C6Hu#a9FH61n=R^pb9NQjLiye zO{H6UlXs5@*@M?E-CzTf65wl5p78FsXB5VP~5PQsEX!!My;Hsq&w>EzyP zW*bVbtFuPjPRi$MzKmd=8s5{ZL9A5THlLRXU$m_L`Ae3cP9GJ%_TArJLZXoA6v0Br zQA+AfgoPX1{OOW8bkFOT6 z)1HnE4etD_BI9?rCtds!jn2KA>gDB>$&2u|Z4Vp}o|HR*l9%ESeH8vd z2d^Rra7KNi?*{FhhzoH446Qi>1f}p+=50G=E42lhiDCLn!1uwy1L@xD5h7>g{AsA= z9FBTK&kTG`hmxtw^`?ZauL?Y$D0}kxRJ{kTz<}H+3lh^kH9I{r2IMW!SDwZXcZhzr zumTt^P}EPz#uQ{;o>6PeO{Z6iODg{)`?R^3mshrj%d);^1E@;W&Wem74!qbBcV!11 zGbxs3Qi#bJE?_&nr--RRk1yC$18h^qZojgUp+yk4SOius( z^S^+z1^@hd-L|} zWSrHt)kS?1l)>g=?$hZX0v{)zAAozHW6`yq$^}bFPkfoKDP}EW@9QPFA;q6% z{wqC9b^8&+NJatZ1#$QEGN@MYauCdE&ry{vR=zU*e?LBH3qalsZeaiw(0b8l`Td3n zdVaM!e#RYRy|Pdp*as%o$@CkO|G1c7&0oF0qb!U175uZE^Yed1@$AcUWlmA^G+5xy zB^!HPtsbtZzT4mn{qyu}iu2fZCh)}X&4uYB_v*>|^YtFM0wueWDI9;S3MckdTdXK9 zq!r@+C#E+^wCMKqg9oITOKWe6a^9KNU0XF$k4Cnozz<55+&)_E%V=l!F1vA9+F$?U z|9+d(x3buIw>$#T7lIDnvn(bR8AgO10ub2rO9eY`RLXm=N@}h=wkUO>X=TvJ8|aak zqfWfDMIL#Mt#1ygL>ioRUS(6C4RY`k=sn~cm2U~HG$Rz^RGZtX1M=q^ts-}MiOez_ zx1}B3UoXT3^6igkf1{8e_%Hb1qL*)WveY5^NP5B;_DqxqS?ln&jiRyH>#jW~xeads z)09C&+8F7|n?{TSqQD6y!Xuww~ivjsyznl#EsnRdT8O z7HYPO6*KziT}I$;4zJI}uC9}N5D;h~Hc`q4LB$$=ETg9gAXbOtq;7;SX1>X2Si*{~ zu$-(`l>XK9r1ST9>4D+@dV=o+RLB)x@duqhILxmA-@5Zva}-?AzZP(9~s}f#ayV(8yf^k!o6r&!fN-G zbW|DdGBG$KshZ@jEAAHxMD5Hv9(*ULQ6`_3#z-+TwOZSbT$^h`G*IxB(fSfLFd}?F zr5fj88-nJvsn*&wWp5lQGu9eLJ8eKjAE?{WNkwVUFvJ~8qcEwxR*Pk^`ru@9v7pRo z{0qt!+C{SP13QzFGn5HVWIuya+&h)`x)WAsdtF;aPFI9zI68%VPb!I=XZ11Rz!V3o zW$rV&ts30Z>M8%mjENRY%sXARwsx~jvFGGtK3VZ1H&gQs*&npa&gzb;p{qQR)Q{Jz z?s4_(JKhmom5-_;{u=5(1%ylcPT#!mCXB?DL2+6sYdw9dV=gK4)Mhl%7I)h+%>3*U z6o}jf3uEPgxg_zb9;g8qb-qW(eZdwN$N08sN%GykU*=omTv%&97m*KjTZw8sJS-n| zNGN*X(N7+u*+5B)QnJ9_g5vg~WWj16vK39YE5l*4pk8d-XAgyXycyI&w_EXqT9>vQ zeOp>DWDH@<1tGS;#+^L#u?+?HTT$G;A2uKHUBxiMSWdYEk6IWhPW*Y&oZwxwND;7$ z(6Vnxfu(t)lrl)56bGtU!T>z7-3U)WDey7x^6hIK;$;vZQ&61CiclD5K_8S+?~9W+ z{lRDG83GMx?0WreaQxTbr2Wcj0skeeTNUAsNCO6H*xq>xWNhddfkj2d)?XR%^Z^?e z%4wa(0|I~_-<#)~S0_)Bf8Ye@|DNv9Z_dqHYhinV$+11*Kb8mp20U0{%$1J%b0-eF z-~Hp9c`8Tt6Z0SNK1#WF+3Epyd-@p&U`$uMTE{$hk(qE}0ESA_b0Po@h~>3@swr68 zd+qS`r1ve`TqW`N+IkgBYMIO{y%Ikv=b>?2trMb3wCR-wmMjxUeF0Y7TYNnRpevd( z`j%6~*p4^~l*U^b)c%Q6Z}fayf_macw=-oJRRXj;QIqPCjG+n| zw{8rfwD^9Qu#E_HNsV?@y0NA<>y7|R=@=`gbF7EluIZB6xIwiNn(~OHWc+bzc-O-^ zCv|OVgx8~QA1JBV8pe8v^1&~C690}w6+^(*P}YWIP-tve>d_}1zU5Sza!N-wi-*+? zWmR(1jGkpWjvwsOlM@^pTh?_XCdcMdBOuworEu|xiz9dz8;0sk3UxlelzQ~5M?`y} zi(%*nq5ei_Jv?<{jFyg`5+#uigzHBr|NHgzkkuxv9;$luZkK9Z(mU+n{!7ZaZ0Q(< zH=yxp4to_Iy{R@QYE$9dg4*nUc~#ct``YZ4yf*mvTDTX3rTddacpc%!g}ypxSMEuv zSTiC!R^H{VQN(qo7ddppX89D!3NrI_GwablC;JlmL*VHD)h4et+gpU0bfeO$QT1HL zeB7m^;{Z0UH|)j0VAEBe`(+UnDSy^|LScY6NwKSKOT(^-Tazxk8`k5owtb$b*@tpEa;Qna$dPr;I zJi6dxFZkXSGaT$Gw76oT$i*<58DfK%MNp(c>R}xnBpAJpwAEE@t7b3 zlL;|IltJ=nAm2hVPN39^cn@=bz+MY+g_ehBh>%nvCyeCWMcp$M4zt+92q;3rP+Ysp z$BH4;4Cx-K8B9CpDoZkkO`iE#y^D!JVGjO$8GwpK2!Q5kC4g7F#BaY2SPiHIwBJ6c z7H}Jo4k&~Eu2lqReq~Bje75UWt5oZ@G98|44xqe!NdoQ)(y3&TBaD&6=QzXtv+A9D z>sngL{%_;*QDqc&A1)`DImyRekqBe7c)}LAQfpPaGLr(ib}ZSOl)I!VU(%uEEFE>O zwhN))tk+;&~(?oC%|nR}7rc#YbFN6sk%I@6P=! zI97!rd?=A4QLKMpawmx@bQ1{)UM(DoVT+Fn7@SXm7V)Lln4p#kmM%EA6cDd9Gu#zM z<-t;uotwvXb|feZZZ5Y^Bo;F~Zap|rzS~S0S30g_0*@?2Em-aCOYtKo#x*LH2<_wc zBc3#GykJ=t9-0W^3hzuLtHue4$CXGW0@?`Ss()~C%Q?Hrh{fbSqL@NW%UPGzFLM;@ zC78WNQVTpMC;JQI*{K*w361ibxq#t1dpeZ<6vlJz>-~s8o){Gpod6!bJcz@uf?i<3 z9!c}kT0ri)w?Z$(^+1B{ZbMS3;DWD+{q(;P1q0iiC_ z9_gn{d(`&Rr%u1|re1w-ZI3(e>i5Gb_liIM^an2b5!dkEDd_DCu5Ows8~yjf>SrK` zyLCd58N%)-RU&pZg%p6O%1O%!LHvhD;ryDY~EvRx5yjAF@ZncR`U=R z9${pp$3DIpZMjig-eA{ok1;`xTuTy@l2cNRc1MP9?3l*7vwz7_&GyY0<8%F)hvpjr z_&!iv!u!pMRMj=Lb@dI69N-{)-`J)i!Y(D+w;$B^j-kUx_`$)G>d(jYnX~84`%d|V ziV`b<9Wh*lVf$>=z<}$~w8Y_31ZY(2!vxMqPOCgG*Pg-MDq9sH6-f{})e9T|-j~ zp^el*p>_2zSe!oF1JAnL9m^CcwxV~fl;3++6Y1qS$A&mup8t~E*H0wRdZcY+HS2GcKQy}UclZ3JNsHEr9K|R{HR{pK zhC`vIjAxY6(lxd&)dCbdD^;hR#Zz5Z#3~l=2LIRe%mq*iZ4icnE=)4X9(E9vt$3JLvVBQ9q4hEoLfoozx3M_KjZ=?F&P2eN+&%4|!5d(|S=ED2<|^bV`&0 znGEaFTR{pi;GwLIk4TTRB@|#VYitFraoL*HGZ!r;t3_*u$3I~gIPCoejh;f(cOi!srw^%Jr-pHsir+qM3Z-*|nBx-OG*=bwL+`Sbd{x=Qq% zs9#=PUCUt+gwoBBqk-eXaOP+#By)Bs%$Y0Yv-R(4e8*!%6=K~Ix?9-I!O`TR&YLxs zupO)3T}Ld?WuR@a9kG-;^_2v40!yos>0mFg_J`wpHAmwHUkUAn`H)B>>JnFZYn(J{ zc-3ujzb@YW(}6|AzCv}V`3aV;`| zN<1J_&Uds83gTjI20GP`3s?a@3>J08#Ym?jfJi4qV? z3{e6CB19>n6qS64OgTR^DK-mp(y;Fh_}nu7u2|hefd{u@aA5VTFNySlWWVew)TNLdm=2;WZS7QATU%)LK_t;y7Pf!~p(0hf zL{J^wMn{XSJ)$b&#Uui zUOoQy7_!>-M)}$MhM8U?>T#ZS3fvouAke^g+cG%CWivij&6ciMB_X> z|E2f-KPWW#9+DK8r=S1*#N@K%8$o4)yn+8B7^~h5{IK)kk8##oGWc|_3>3*j=mR;M zg+l}=#%~fejEq3)`Z_ES_ur4dZ;BMzleWo;8bXHeMVna{$}xiZhx?Ke7_G#JQJnus zkdH28*sD@nx&z>YJa1fGUB_NXc=~SfSkU=nCp3o2SxJ8o3NM<_JIdS#mgN_~@Z4wP z`j=I`q0k0o%w{W_cQ8BHz5_qC>J9}IToD9^ZUI)HxGG>cO$|_&dduG*Jm0>5$Ept| zD&!3&od?5Bf(%u{p%)@`EQ(WU#aBCV^^U5^3mG;w(L_Wpr)N668H8FVg#^`T=%uZu6G6IGx+p#v_TXU#JAZhPCm-&H?{;2p zC|9J_{S!Dcdb1wfTkZVrX9$jI!=SZ|veR#wu@}Q5_7T|%%uW`gyc` margin-bottom: 0; font-family: 'SF Mono', monospace; - font-family: 'Space Mono', monospace; + font-family: 'Geist Mono'; font-size: 13px; font-weight: 400; text-align: left; diff --git a/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss b/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss index bd24601b18..7e43e1caf3 100644 --- a/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss +++ b/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss @@ -8,7 +8,7 @@ .label { color: var(--text-robin-400); font-family: SF Mono; - font-family: 'Space Mono', monospace; + font-family: 'Geist Mono'; font-size: 13px; font-weight: var(--font-weight-normal); line-height: 18px; diff --git a/frontend/src/container/LogDetailedView/JsonView.tsx b/frontend/src/container/LogDetailedView/JsonView.tsx index 1f22b1a134..9766b8db6e 100644 --- a/frontend/src/container/LogDetailedView/JsonView.tsx +++ b/frontend/src/container/LogDetailedView/JsonView.tsx @@ -28,7 +28,7 @@ function JSONView({ logData }: JSONViewProps): JSX.Element { }, fontWeight: 400, // fontFamily: 'SF Mono', - fontFamily: 'Space Mono', + fontFamily: 'Geist Mono', fontSize: 13, lineHeight: '18px', colorDecorators: true, diff --git a/frontend/src/container/LogDetailedView/Overview.tsx b/frontend/src/container/LogDetailedView/Overview.tsx index bd54524931..cf98ac11a9 100644 --- a/frontend/src/container/LogDetailedView/Overview.tsx +++ b/frontend/src/container/LogDetailedView/Overview.tsx @@ -53,7 +53,7 @@ function Overview({ enabled: false, }, fontWeight: 400, - fontFamily: 'Space Mono', + fontFamily: 'Geist Mono', fontSize: 13, lineHeight: '18px', colorDecorators: true, diff --git a/frontend/src/container/LogDetailedView/TableView.styles.scss b/frontend/src/container/LogDetailedView/TableView.styles.scss index d9cbdcabbb..2f092dc04d 100644 --- a/frontend/src/container/LogDetailedView/TableView.styles.scss +++ b/frontend/src/container/LogDetailedView/TableView.styles.scss @@ -57,6 +57,8 @@ background: rgba(22, 25, 34, 0.4); .value-field { + font-family: 'Geist Mono'; + position: relative; } diff --git a/frontend/src/container/LogDetailedView/TableView.tsx b/frontend/src/container/LogDetailedView/TableView.tsx index a69fba6441..810c946fb0 100644 --- a/frontend/src/container/LogDetailedView/TableView.tsx +++ b/frontend/src/container/LogDetailedView/TableView.tsx @@ -289,7 +289,13 @@ function TableView({ return (
- + {removeEscapeCharacters(fieldData.value)} diff --git a/frontend/src/container/LogsExplorerChart/LogsExplorerChart.styled.ts b/frontend/src/container/LogsExplorerChart/LogsExplorerChart.styled.ts index fec5dc1f0c..69299bf0d0 100644 --- a/frontend/src/container/LogsExplorerChart/LogsExplorerChart.styled.ts +++ b/frontend/src/container/LogsExplorerChart/LogsExplorerChart.styled.ts @@ -9,6 +9,6 @@ export const CardStyled = styled(Card)` height: 200px; min-height: 200px; padding: 0 16px 16px 16px; - font-family: 'Space Mono', monospace; + font-family: 'Geist Mono'; } `; diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 1684d7de2a..b5aca15ddf 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -266,3 +266,10 @@ modal - 1000 notifications - 2050 */ + +@font-face { + font-family: 'Geist Mono'; + src: local('Geist Mono'), + url('../public/fonts/GeistMonoVF.woff2') format('woff'); + /* Add other formats if needed (e.g., woff2, truetype, opentype, svg) */ +} From 916663b4d541c97b973489ccf83c78eab770ce39 Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 9 Jul 2024 08:12:25 +0430 Subject: [PATCH 083/281] fix: fix the explorer toolbar buttons padding (#5443) --- .../src/container/ExplorerOptions/ExplorerOptions.styles.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss b/frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss index 2076b858f9..54e87fa458 100644 --- a/frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.styles.scss @@ -91,8 +91,7 @@ box-shadow: none !important; &.ant-btn-round { - padding-inline-start: 10px; - padding-inline-end: 8px; + padding: 8px 12px 8px 10px; font-weight: 500; } From 87f1597d4e4984df0a477dc7e2ca5d79790c6b07 Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 9 Jul 2024 08:13:35 +0430 Subject: [PATCH 084/281] fix: prevent overwriting query expression and queryName on switching between panel types (#5430) --- frontend/src/container/NewWidget/utils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/NewWidget/utils.ts b/frontend/src/container/NewWidget/utils.ts index b840d6e71a..7e651ff2ea 100644 --- a/frontend/src/container/NewWidget/utils.ts +++ b/frontend/src/container/NewWidget/utils.ts @@ -372,8 +372,12 @@ export function handleQueryChange( builder: { ...supersetQuery.builder, queryData: supersetQuery.builder.queryData.map((query, index) => { - const { dataSource } = query; - const tempQuery = { ...initialQueryBuilderFormValuesMap[dataSource] }; + const { dataSource, expression, queryName } = query; + const tempQuery = { + ...initialQueryBuilderFormValuesMap[dataSource], + expression, + queryName, + }; const fieldsToSelect = panelTypeDataSourceFormValuesMap[newPanelType][dataSource].builder From 2c7a5126fd56a02f4efd00adf446f73ff1c6f40a Mon Sep 17 00:00:00 2001 From: Yunus M Date: Wed, 10 Jul 2024 00:30:25 +0530 Subject: [PATCH 085/281] update project maintainers (#5460) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a17c67e498..852b44f240 100644 --- a/README.md +++ b/README.md @@ -198,14 +198,14 @@ Not sure how to get started? Just ping us on `#contributing` in our [slack commu #### Frontend -- [Palash Gupta](https://github.com/palashgdev) - [Yunus M](https://github.com/YounixM) -- [Rajat Dabade](https://github.com/Rajat-Dabade) +- [Vikrant Gupta](https://github.com/vikrantgupta25) +- [Sagar Rajput](https://github.com/SagarRajput-7) #### DevOps - [Prashant Shahi](https://github.com/prashant-shahi) -- [Dhawal Sanghvi](https://github.com/dhawal1248) +- [Vibhu Pandey](https://github.com/grandwizard28)

From 3b2a811f7bc6cdd1275830665c8f13d675dd5110 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 08:11:53 +0530 Subject: [PATCH 086/281] chore(deps): bump google.golang.org/grpc from 1.64.0 to 1.64.1 (#5463) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.64.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.64.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 10831ec475..b62ebab95f 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( golang.org/x/net v0.26.0 golang.org/x/oauth2 v0.21.0 golang.org/x/text v0.16.0 - google.golang.org/grpc v1.64.0 + google.golang.org/grpc v1.64.1 google.golang.org/protobuf v1.34.1 gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 5c49916eb3..6638d7b40d 100644 --- a/go.sum +++ b/go.sum @@ -1197,8 +1197,8 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= +google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 831de18464c98eef0720391aa257ca34182e605e Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 10 Jul 2024 11:00:28 +0530 Subject: [PATCH 087/281] fix: concurrent map writes to temporalityMap (#5432) --- pkg/query-service/app/http_handler.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index d6c91558a5..42879123ec 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -83,6 +83,7 @@ type APIHandler struct { // querying the v4 table on low cardinal temporality column // should be fast but we can still avoid the query if we have the data in memory temporalityMap map[string]map[v3.Temporality]bool + temporalityMux sync.Mutex maxIdleConns int maxOpenConns int @@ -455,6 +456,9 @@ func (aH *APIHandler) getRule(w http.ResponseWriter, r *http.Request) { // populateTemporality adds the temporality to the query if it is not present func (aH *APIHandler) populateTemporality(ctx context.Context, qp *v3.QueryRangeParamsV3) error { + aH.temporalityMux.Lock() + defer aH.temporalityMux.Unlock() + missingTemporality := make([]string, 0) metricNameToTemporality := make(map[string]map[v3.Temporality]bool) if qp.CompositeQuery != nil && len(qp.CompositeQuery.BuilderQueries) > 0 { From 83455e614e157b35d617d0b5186fbe0abad229ba Mon Sep 17 00:00:00 2001 From: Nityananda Gohain Date: Wed, 10 Jul 2024 11:23:29 +0530 Subject: [PATCH 088/281] fix: disable removing a selected field (#5457) * fix: disable removing a selected field * fix: comment updated with issue link * fix: remove local db --- .../app/clickhouseReader/reader.go | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index ae8fb64c94..ccdffd88bd 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -3575,38 +3575,42 @@ func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.Upda } } else { - // Delete the index first - query := fmt.Sprintf("ALTER TABLE %s.%s ON CLUSTER %s DROP INDEX IF EXISTS %s_idx`", r.logsDB, r.logsLocalTable, r.cluster, strings.TrimSuffix(colname, "`")) - err := r.db.Exec(ctx, query) - if err != nil { - return &model.ApiError{Err: err, Typ: model.ErrorInternal} - } + // We are not allowing to delete a materialized column + // For more details please check https://github.com/SigNoz/signoz/issues/4566 + return model.ForbiddenError(errors.New("Removing a selected field is not allowed, please reach out to support.")) - for _, table := range []string{r.logsTable, r.logsLocalTable} { - // drop materialized column from logs table - query := "ALTER TABLE %s.%s ON CLUSTER %s DROP COLUMN IF EXISTS %s " - err := r.db.Exec(ctx, fmt.Sprintf(query, - r.logsDB, table, - r.cluster, - colname, - ), - ) - if err != nil { - return &model.ApiError{Err: err, Typ: model.ErrorInternal} - } - - // drop exists column on logs table - query = "ALTER TABLE %s.%s ON CLUSTER %s DROP COLUMN IF EXISTS %s_exists` " - err = r.db.Exec(ctx, fmt.Sprintf(query, - r.logsDB, table, - r.cluster, - strings.TrimSuffix(colname, "`"), - ), - ) - if err != nil { - return &model.ApiError{Err: err, Typ: model.ErrorInternal} - } - } + // Delete the index first + // query := fmt.Sprintf("ALTER TABLE %s.%s ON CLUSTER %s DROP INDEX IF EXISTS %s_idx`", r.logsDB, r.logsLocalTable, r.cluster, strings.TrimSuffix(colname, "`")) + // err := r.db.Exec(ctx, query) + // if err != nil { + // return &model.ApiError{Err: err, Typ: model.ErrorInternal} + // } + + // for _, table := range []string{r.logsTable, r.logsLocalTable} { + // // drop materialized column from logs table + // query := "ALTER TABLE %s.%s ON CLUSTER %s DROP COLUMN IF EXISTS %s " + // err := r.db.Exec(ctx, fmt.Sprintf(query, + // r.logsDB, table, + // r.cluster, + // colname, + // ), + // ) + // if err != nil { + // return &model.ApiError{Err: err, Typ: model.ErrorInternal} + // } + + // // drop exists column on logs table + // query = "ALTER TABLE %s.%s ON CLUSTER %s DROP COLUMN IF EXISTS %s_exists` " + // err = r.db.Exec(ctx, fmt.Sprintf(query, + // r.logsDB, table, + // r.cluster, + // strings.TrimSuffix(colname, "`"), + // ), + // ) + // if err != nil { + // return &model.ApiError{Err: err, Typ: model.ErrorInternal} + // } + // } } return nil } From ddf5569ce91264a52a86dcc1c38ca9fe99ea21e4 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:56:11 +0530 Subject: [PATCH 089/281] fix: added null check on filters obj (#5419) * fix: added null check on filters obj * feat: added test cases of undefined filters and items * feat: added comments --- .../pages/TracesExplorer/Filter/Filter.tsx | 2 +- .../__test__/TracesExplorer.test.tsx | 132 ++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx diff --git a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx index 7eadc3d5cd..1a3fbc785c 100644 --- a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx @@ -56,7 +56,7 @@ export function Filter(props: FilterProps): JSX.Element { return {} as FilterType; } - return filters.items + return (filters.items || []) .filter((item) => Object.keys(AllTraceFilterKeyValue).includes(item.key?.key as string), ) diff --git a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx new file mode 100644 index 0000000000..1250a3f3cc --- /dev/null +++ b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx @@ -0,0 +1,132 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +/* eslint-disable no-restricted-syntax */ +/* eslint-disable no-await-in-loop */ +import { + initialQueriesMap, + initialQueryBuilderFormValues, +} from 'constants/queryBuilder'; +import ROUTES from 'constants/routes'; +import * as compositeQueryHook from 'hooks/queryBuilder/useGetCompositeQueryParam'; +import { render } from 'tests/test-utils'; +import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; + +import { Filter } from '../Filter/Filter'; +import { AllTraceFilterKeyValue } from '../Filter/filterUtils'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: (): { pathname: string } => ({ + pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.TRACES_EXPLORER}/`, + }), +})); + +jest.mock('uplot', () => { + const paths = { + spline: jest.fn(), + bars: jest.fn(), + }; + + const uplotMock = jest.fn(() => ({ + paths, + })); + + return { + paths, + default: uplotMock, + }; +}); + +const compositeQuery: Query = { + ...initialQueriesMap.traces, + builder: { + ...initialQueriesMap.traces.builder, + queryData: [ + { + ...initialQueryBuilderFormValues, + filters: { + items: [ + { + id: '95564eb1', + key: { + key: 'name', + dataType: DataTypes.String, + type: 'tag', + isColumn: true, + isJSON: false, + id: 'name--string--tag--true', + }, + op: 'in', + value: ['HTTP GET /customer'], + }, + { + id: '3337951c', + key: { + key: 'serviceName', + dataType: DataTypes.String, + type: 'tag', + isColumn: true, + isJSON: false, + id: 'serviceName--string--tag--true', + }, + op: 'in', + value: ['demo-app'], + }, + ], + op: 'AND', + }, + }, + ], + }, +}; + +describe('TracesExplorer - ', () => { + it('test edge cases of undefined filters', async () => { + jest.spyOn(compositeQueryHook, 'useGetCompositeQueryParam').mockReturnValue({ + ...compositeQuery, + builder: { + ...compositeQuery.builder, + queryData: compositeQuery.builder.queryData.map( + (item) => + ({ + ...item, + filters: undefined, + } as any), + ), + }, + }); + + const { getByText } = render(); + + // we should have all the filters + Object.values(AllTraceFilterKeyValue).forEach((filter) => { + expect(getByText(filter)).toBeInTheDocument(); + }); + }); + + it('test edge cases of undefined filters - items', async () => { + jest.spyOn(compositeQueryHook, 'useGetCompositeQueryParam').mockReturnValue({ + ...compositeQuery, + builder: { + ...compositeQuery.builder, + queryData: compositeQuery.builder.queryData.map( + (item) => + ({ + ...item, + filters: { + ...item.filters, + items: undefined, + }, + } as any), + ), + }, + }); + + const { getByText } = render(); + + // we should have all the filters + Object.values(AllTraceFilterKeyValue).forEach((filter) => { + expect(getByText(filter)).toBeInTheDocument(); + }); + }); +}); From 9844dcdfb785a50daaec4fef42330ee4079b3056 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:43:39 +0530 Subject: [PATCH 090/281] fix: added logic to keep sections uncollapsed for all filtered items (#5371) --- .../pages/TracesExplorer/Filter/Section.tsx | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/TracesExplorer/Filter/Section.tsx b/frontend/src/pages/TracesExplorer/Filter/Section.tsx index c95f8a77e0..8ce2007ef7 100644 --- a/frontend/src/pages/TracesExplorer/Filter/Section.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/Section.tsx @@ -1,7 +1,14 @@ import './Filter.styles.scss'; import { Button, Collapse, Divider } from 'antd'; -import { Dispatch, MouseEvent, SetStateAction } from 'react'; +import { + Dispatch, + MouseEvent, + SetStateAction, + useEffect, + useMemo, + useState, +} from 'react'; import { DurationSection } from './DurationSection'; import { @@ -18,9 +25,29 @@ interface SectionProps { setSelectedFilters: Dispatch>; handleRun: (props?: HandleRunProps) => void; } + export function Section(props: SectionProps): JSX.Element { const { panelName, setSelectedFilters, selectedFilters, handleRun } = props; + const defaultOpenPanes = useMemo( + () => + Array.from( + new Set([ + ...Object.keys(selectedFilters || {}), + 'hasError', + 'durationNano', + 'serviceName', + ]), + ), + [selectedFilters], + ); + + const [activeKeys, setActiveKeys] = useState(defaultOpenPanes); + + useEffect(() => { + setActiveKeys(defaultOpenPanes); + }, [defaultOpenPanes]); + const onClearHandler = (e: MouseEvent): void => { e.stopPropagation(); e.preventDefault(); @@ -41,11 +68,8 @@ export function Section(props: SectionProps): JSX.Element { setActiveKeys(keys as string[])} items={[ panelName === 'durationNano' ? { From 3ecb2e35ef3d0755bf56e2b378f5e8e4d2951c1b Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Fri, 12 Jul 2024 18:49:24 +0530 Subject: [PATCH 091/281] chore: use version v4 for export panel from explorer pages (#5438) --- frontend/src/container/ExportPanel/ExportPanelContainer.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/container/ExportPanel/ExportPanelContainer.tsx b/frontend/src/container/ExportPanel/ExportPanelContainer.tsx index df2d4f8720..9e4a658273 100644 --- a/frontend/src/container/ExportPanel/ExportPanelContainer.tsx +++ b/frontend/src/container/ExportPanel/ExportPanelContainer.tsx @@ -1,5 +1,6 @@ import { Button, Typography } from 'antd'; import createDashboard from 'api/dashboard/create'; +import { ENTITY_VERSION_V4 } from 'constants/app'; import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard'; import useAxiosError from 'hooks/useAxiosError'; import { useCallback, useMemo, useState } from 'react'; @@ -70,6 +71,7 @@ function ExportPanelContainer({ ns: 'dashboard', }), uploadedGrafana: false, + version: ENTITY_VERSION_V4, }); }, [t, createNewDashboard]); From 9194ab08b63773b0eef7aeca041bd6e15abfecfb Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 15 Jul 2024 18:06:39 +0530 Subject: [PATCH 092/281] fix: incorrect response for promql value type panels (#5497) --- pkg/query-service/app/querier/helper.go | 2 +- pkg/query-service/app/querier/querier.go | 17 +- pkg/query-service/app/querier/querier_test.go | 99 ++ pkg/query-service/app/querier/v2/helper.go | 2 +- pkg/query-service/app/querier/v2/querier.go | 17 +- .../app/querier/v2/querier_test.go | 1060 +++++++++++++++++ pkg/query-service/interfaces/interface.go | 1 + 7 files changed, 1186 insertions(+), 12 deletions(-) create mode 100644 pkg/query-service/app/querier/v2/querier_test.go diff --git a/pkg/query-service/app/querier/helper.go b/pkg/query-service/app/querier/helper.go index d65627bd92..1da4a5a46a 100644 --- a/pkg/query-service/app/querier/helper.go +++ b/pkg/query-service/app/querier/helper.go @@ -18,7 +18,7 @@ import ( "go.uber.org/zap" ) -func prepareLogsQuery(ctx context.Context, +func prepareLogsQuery(_ context.Context, start, end int64, builderQuery *v3.BuilderQuery, diff --git a/pkg/query-service/app/querier/querier.go b/pkg/query-service/app/querier/querier.go index 7668d401cd..95cfc7cc73 100644 --- a/pkg/query-service/app/querier/querier.go +++ b/pkg/query-service/app/querier/querier.go @@ -50,8 +50,10 @@ type querier struct { // TODO(srikanthccv): remove this once we have a proper mock testingMode bool queriesExecuted []string - returnedSeries []*v3.Series - returnedErr error + // tuple of start and end time in milliseconds + timeRanges [][]int + returnedSeries []*v3.Series + returnedErr error } type QuerierOptions struct { @@ -117,6 +119,7 @@ func (q *querier) execClickHouseQuery(ctx context.Context, query string) ([]*v3. func (q *querier) execPromQuery(ctx context.Context, params *model.QueryRangeParams) ([]*v3.Series, error) { q.queriesExecuted = append(q.queriesExecuted, params.Query) if q.testingMode && q.reader == nil { + q.timeRanges = append(q.timeRanges, []int{int(params.Start.UnixMilli()), int(params.End.UnixMilli())}) return q.returnedSeries, q.returnedErr } promResult, _, err := q.reader.GetQueryRangeResult(ctx, params) @@ -342,10 +345,10 @@ func (q *querier) runPromQueries(ctx context.Context, params *v3.QueryRangeParam wg.Add(1) go func(queryName string, promQuery *v3.PromQuery) { defer wg.Done() - cacheKey := cacheKeys[queryName] + cacheKey, ok := cacheKeys[queryName] var cachedData []byte // Ensure NoCache is not set and cache is not nil - if !params.NoCache && q.cache != nil { + if !params.NoCache && q.cache != nil && ok { data, retrieveStatus, err := q.cache.Retrieve(cacheKey, true) zap.L().Info("cache retrieve status", zap.String("status", retrieveStatus.String())) if err == nil { @@ -373,7 +376,7 @@ func (q *querier) runPromQueries(ctx context.Context, params *v3.QueryRangeParam channelResults <- channelResult{Err: nil, Name: queryName, Query: promQuery.Query, Series: mergedSeries} // Cache the seriesList for future queries - if len(missedSeries) > 0 && !params.NoCache && q.cache != nil { + if len(missedSeries) > 0 && !params.NoCache && q.cache != nil && ok { mergedSeriesData, err := json.Marshal(mergedSeries) if err != nil { zap.L().Error("error marshalling merged series", zap.Error(err)) @@ -546,3 +549,7 @@ func (q *querier) QueryRange(ctx context.Context, params *v3.QueryRangeParamsV3, func (q *querier) QueriesExecuted() []string { return q.queriesExecuted } + +func (q *querier) TimeRanges() [][]int { + return q.timeRanges +} diff --git a/pkg/query-service/app/querier/querier_test.go b/pkg/query-service/app/querier/querier_test.go index a9f8cc4030..5160c564da 100644 --- a/pkg/query-service/app/querier/querier_test.go +++ b/pkg/query-service/app/querier/querier_test.go @@ -951,3 +951,102 @@ func TestQueryRangeTimeShiftWithLimitAndCache(t *testing.T) { } } } + +func TestQueryRangeValueTypePromQL(t *testing.T) { + // There shouldn't be any caching for value panel type + params := []*v3.QueryRangeParamsV3{ + { + Start: 1675115596722, + End: 1675115596722 + 120*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypePromQL, + PanelType: v3.PanelTypeValue, + PromQueries: map[string]*v3.PromQuery{ + "A": { + Query: "signoz_calls_total", + }, + }, + }, + }, + { + Start: 1675115596722 + 60*60*1000, + End: 1675115596722 + 180*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypePromQL, + PanelType: v3.PanelTypeValue, + PromQueries: map[string]*v3.PromQuery{ + "A": { + Query: "signoz_latency_bucket", + }, + }, + }, + }, + } + cache := inmemory.New(&inmemory.Options{TTL: 60 * time.Minute, CleanupInterval: 10 * time.Minute}) + opts := QuerierOptions{ + Cache: cache, + Reader: nil, + FluxInterval: 5 * time.Minute, + KeyGenerator: queryBuilder.NewKeyGenerator(), + + TestingMode: true, + ReturnedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "method": "GET", + "service_name": "test", + "__name__": "doesn't matter", + }, + Points: []v3.Point{ + {Timestamp: 1675115596722, Value: 1}, + {Timestamp: 1675115596722 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 120*60*1000, Value: 3}, + }, + }, + }, + } + q := NewQuerier(opts) + + expectedQueryAndTimeRanges := []struct { + query string + ranges []missInterval + }{ + { + query: "signoz_calls_total", + ranges: []missInterval{ + {start: 1675115596722, end: 1675115596722 + 120*60*1000}, + }, + }, + { + query: "signoz_latency_bucket", + ranges: []missInterval{ + {start: 1675115596722 + 60*60*1000, end: 1675115596722 + 180*60*1000}, + }, + }, + } + + for i, param := range params { + _, errByName, err := q.QueryRange(context.Background(), param, nil) + if err != nil { + t.Errorf("expected no error, got %s", err) + } + if len(errByName) > 0 { + t.Errorf("expected no error, got %v", errByName) + } + + if !strings.Contains(q.QueriesExecuted()[i], expectedQueryAndTimeRanges[i].query) { + t.Errorf("expected query to contain %s, got %s", expectedQueryAndTimeRanges[i].query, q.QueriesExecuted()[i]) + } + if len(q.TimeRanges()[i]) != 2 { + t.Errorf("expected time ranges to be %v, got %v", expectedQueryAndTimeRanges[i].ranges, q.TimeRanges()[i]) + } + if q.TimeRanges()[i][0] != int(expectedQueryAndTimeRanges[i].ranges[0].start) { + t.Errorf("expected time ranges to be %v, got %v", expectedQueryAndTimeRanges[i].ranges, q.TimeRanges()[i]) + } + if q.TimeRanges()[i][1] != int(expectedQueryAndTimeRanges[i].ranges[0].end) { + t.Errorf("expected time ranges to be %v, got %v", expectedQueryAndTimeRanges[i].ranges, q.TimeRanges()[i]) + } + } +} diff --git a/pkg/query-service/app/querier/v2/helper.go b/pkg/query-service/app/querier/v2/helper.go index 04f798ad1b..9df9965b5c 100644 --- a/pkg/query-service/app/querier/v2/helper.go +++ b/pkg/query-service/app/querier/v2/helper.go @@ -18,7 +18,7 @@ import ( "go.uber.org/zap" ) -func prepareLogsQuery(ctx context.Context, +func prepareLogsQuery(_ context.Context, start, end int64, builderQuery *v3.BuilderQuery, diff --git a/pkg/query-service/app/querier/v2/querier.go b/pkg/query-service/app/querier/v2/querier.go index e6915ef078..b8a8a9e92e 100644 --- a/pkg/query-service/app/querier/v2/querier.go +++ b/pkg/query-service/app/querier/v2/querier.go @@ -50,8 +50,10 @@ type querier struct { // TODO(srikanthccv): remove this once we have a proper mock testingMode bool queriesExecuted []string - returnedSeries []*v3.Series - returnedErr error + // tuple of start and end time in milliseconds + timeRanges [][]int + returnedSeries []*v3.Series + returnedErr error } type QuerierOptions struct { @@ -117,6 +119,7 @@ func (q *querier) execClickHouseQuery(ctx context.Context, query string) ([]*v3. func (q *querier) execPromQuery(ctx context.Context, params *model.QueryRangeParams) ([]*v3.Series, error) { q.queriesExecuted = append(q.queriesExecuted, params.Query) if q.testingMode && q.reader == nil { + q.timeRanges = append(q.timeRanges, []int{int(params.Start.UnixMilli()), int(params.End.UnixMilli())}) return q.returnedSeries, q.returnedErr } promResult, _, err := q.reader.GetQueryRangeResult(ctx, params) @@ -335,10 +338,10 @@ func (q *querier) runPromQueries(ctx context.Context, params *v3.QueryRangeParam wg.Add(1) go func(queryName string, promQuery *v3.PromQuery) { defer wg.Done() - cacheKey := cacheKeys[queryName] + cacheKey, ok := cacheKeys[queryName] var cachedData []byte // Ensure NoCache is not set and cache is not nil - if !params.NoCache && q.cache != nil { + if !params.NoCache && q.cache != nil && ok { data, retrieveStatus, err := q.cache.Retrieve(cacheKey, true) zap.L().Info("cache retrieve status", zap.String("status", retrieveStatus.String())) if err == nil { @@ -366,7 +369,7 @@ func (q *querier) runPromQueries(ctx context.Context, params *v3.QueryRangeParam channelResults <- channelResult{Err: nil, Name: queryName, Query: promQuery.Query, Series: mergedSeries} // Cache the seriesList for future queries - if len(missedSeries) > 0 && !params.NoCache && q.cache != nil { + if len(missedSeries) > 0 && !params.NoCache && q.cache != nil && ok { mergedSeriesData, err := json.Marshal(mergedSeries) if err != nil { zap.L().Error("error marshalling merged series", zap.Error(err)) @@ -539,3 +542,7 @@ func (q *querier) QueryRange(ctx context.Context, params *v3.QueryRangeParamsV3, func (q *querier) QueriesExecuted() []string { return q.queriesExecuted } + +func (q *querier) TimeRanges() [][]int { + return q.timeRanges +} diff --git a/pkg/query-service/app/querier/v2/querier_test.go b/pkg/query-service/app/querier/v2/querier_test.go new file mode 100644 index 0000000000..d29785b310 --- /dev/null +++ b/pkg/query-service/app/querier/v2/querier_test.go @@ -0,0 +1,1060 @@ +package v2 + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "go.signoz.io/signoz/pkg/query-service/app/queryBuilder" + "go.signoz.io/signoz/pkg/query-service/cache/inmemory" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" +) + +func TestV2FindMissingTimeRangesZeroFreshNess(t *testing.T) { + // There are five scenarios: + // 1. Cached time range is a subset of the requested time range + // 2. Cached time range is a superset of the requested time range + // 3. Cached time range is a left overlap of the requested time range + // 4. Cached time range is a right overlap of the requested time range + // 5. Cached time range is a disjoint of the requested time range + testCases := []struct { + name string + requestedStart int64 // in milliseconds + requestedEnd int64 // in milliseconds + requestedStep int64 // in seconds + cachedSeries []*v3.Series + expectedMiss []missInterval + }{ + { + name: "cached time range is a subset of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + }, + }, + }, + expectedMiss: []missInterval{ + { + start: 1675115596722, + end: 1675115596722 + 60*60*1000 - 1, + }, + { + start: 1675115596722 + 120*60*1000 + 1, + end: 1675115596722 + 180*60*1000, + }, + }, + }, + { + name: "cached time range is a superset of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722, + Value: 1, + }, + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 180*60*1000, + Value: 1, + }, + }, + }, + }, + expectedMiss: []missInterval{}, + }, + { + name: "cached time range is a left overlap of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722, + Value: 1, + }, + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + }, + }, + }, + expectedMiss: []missInterval{ + { + start: 1675115596722 + 120*60*1000 + 1, + end: 1675115596722 + 180*60*1000, + }, + }, + }, + { + name: "cached time range is a right overlap of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 180*60*1000, + Value: 1, + }, + }, + }, + }, + expectedMiss: []missInterval{ + { + start: 1675115596722, + end: 1675115596722 + 60*60*1000 - 1, + }, + }, + }, + { + name: "cached time range is a disjoint of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722 + 240*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 300*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 360*60*1000, + Value: 1, + }, + }, + }, + }, + expectedMiss: []missInterval{ + { + start: 1675115596722, + end: 1675115596722 + 180*60*1000, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + misses := findMissingTimeRanges(tc.requestedStart, tc.requestedEnd, tc.requestedStep, tc.cachedSeries, 0*time.Minute) + if len(misses) != len(tc.expectedMiss) { + t.Errorf("expected %d misses, got %d", len(tc.expectedMiss), len(misses)) + } + for i, miss := range misses { + if miss.start != tc.expectedMiss[i].start { + t.Errorf("expected start %d, got %d", tc.expectedMiss[i].start, miss.start) + } + if miss.end != tc.expectedMiss[i].end { + t.Errorf("expected end %d, got %d", tc.expectedMiss[i].end, miss.end) + } + } + }) + } +} + +func TestV2FindMissingTimeRangesWithFluxInterval(t *testing.T) { + + testCases := []struct { + name string + requestedStart int64 + requestedEnd int64 + requestedStep int64 + cachedSeries []*v3.Series + fluxInterval time.Duration + expectedMiss []missInterval + }{ + { + name: "cached time range is a subset of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + }, + }, + }, + fluxInterval: 5 * time.Minute, + expectedMiss: []missInterval{ + { + start: 1675115596722, + end: 1675115596722 + 60*60*1000 - 1, + }, + { + start: 1675115596722 + 120*60*1000 + 1, + end: 1675115596722 + 180*60*1000, + }, + }, + }, + { + name: "cached time range is a superset of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722, + Value: 1, + }, + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 180*60*1000, + Value: 1, + }, + }, + }, + }, + fluxInterval: 5 * time.Minute, + expectedMiss: []missInterval{}, + }, + { + name: "cache time range is a left overlap of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722, + Value: 1, + }, + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + }, + }, + }, + fluxInterval: 5 * time.Minute, + expectedMiss: []missInterval{ + { + start: 1675115596722 + 120*60*1000 + 1, + end: 1675115596722 + 180*60*1000, + }, + }, + }, + { + name: "cache time range is a right overlap of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722 + 60*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 120*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 180*60*1000, + Value: 1, + }, + }, + }, + }, + fluxInterval: 5 * time.Minute, + expectedMiss: []missInterval{ + { + start: 1675115596722, + end: 1675115596722 + 60*60*1000 - 1, + }, + }, + }, + { + name: "cache time range is a disjoint of the requested time range", + requestedStart: 1675115596722, + requestedEnd: 1675115596722 + 180*60*1000, + requestedStep: 60, + cachedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + { + Timestamp: 1675115596722 + 240*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 300*60*1000, + Value: 1, + }, + { + Timestamp: 1675115596722 + 360*60*1000, + Value: 1, + }, + }, + }, + }, + fluxInterval: 5 * time.Minute, + expectedMiss: []missInterval{ + { + start: 1675115596722, + end: 1675115596722 + 180*60*1000, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + misses := findMissingTimeRanges(tc.requestedStart, tc.requestedEnd, tc.requestedStep, tc.cachedSeries, tc.fluxInterval) + if len(misses) != len(tc.expectedMiss) { + t.Errorf("expected %d misses, got %d", len(tc.expectedMiss), len(misses)) + } + for i, miss := range misses { + if miss.start != tc.expectedMiss[i].start { + t.Errorf("expected start %d, got %d", tc.expectedMiss[i].start, miss.start) + } + if miss.end != tc.expectedMiss[i].end { + t.Errorf("expected end %d, got %d", tc.expectedMiss[i].end, miss.end) + } + } + }) + } +} + +func TestV2QueryRange(t *testing.T) { + params := []*v3.QueryRangeParamsV3{ + { + Start: 1675115596722, + End: 1675115596722 + 120*60*1000, + Step: 60, + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + DataSource: v3.DataSourceMetrics, + Temporality: v3.Delta, + StepInterval: 60, + AggregateAttribute: v3.AttributeKey{Key: "http_server_requests_seconds_count", Type: v3.AttributeKeyTypeUnspecified, DataType: "float64", IsColumn: true}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{Key: "method", IsColumn: false}, + Operator: "=", + Value: "GET", + }, + }, + }, + GroupBy: []v3.AttributeKey{ + {Key: "service_name", IsColumn: false}, + {Key: "method", IsColumn: false}, + }, + AggregateOperator: v3.AggregateOperatorSumRate, + TimeAggregation: v3.TimeAggregationRate, + SpaceAggregation: v3.SpaceAggregationSum, + Expression: "A", + }, + }, + }, + }, + { + Start: 1675115596722 + 60*60*1000, + End: 1675115596722 + 180*60*1000, + Step: 60, + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + Temporality: v3.Delta, + StepInterval: 60, + AggregateAttribute: v3.AttributeKey{Key: "http_server_requests_seconds_count", Type: v3.AttributeKeyTypeUnspecified, DataType: "float64", IsColumn: true}, + DataSource: v3.DataSourceMetrics, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{Key: "method", IsColumn: false}, + Operator: "=", + Value: "GET", + }, + }, + }, + GroupBy: []v3.AttributeKey{ + {Key: "service_name", IsColumn: false}, + {Key: "method", IsColumn: false}, + }, + AggregateOperator: v3.AggregateOperatorSumRate, + TimeAggregation: v3.TimeAggregationRate, + SpaceAggregation: v3.SpaceAggregationSum, + Expression: "A", + }, + }, + }, + }, + // No caching for traces yet + { + Start: 1675115596722, + End: 1675115596722 + 120*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + AggregateAttribute: v3.AttributeKey{Key: "durationNano", Type: v3.AttributeKeyTypeUnspecified, DataType: "float64", IsColumn: true}, + StepInterval: 60, + DataSource: v3.DataSourceTraces, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{Key: "method", IsColumn: false}, + Operator: "=", + Value: "GET", + }, + }, + }, + GroupBy: []v3.AttributeKey{ + {Key: "serviceName", IsColumn: false}, + {Key: "name", IsColumn: false}, + }, + AggregateOperator: v3.AggregateOperatorP95, + Expression: "A", + }, + }, + }, + }, + { + Start: 1675115596722 + 60*60*1000, + End: 1675115596722 + 180*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + AggregateAttribute: v3.AttributeKey{Key: "durationNano", Type: v3.AttributeKeyTypeUnspecified, DataType: "float64", IsColumn: true}, + StepInterval: 60, + DataSource: v3.DataSourceTraces, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{Key: "method", IsColumn: false}, + Operator: "=", + Value: "GET", + }, + }, + }, + GroupBy: []v3.AttributeKey{ + {Key: "serviceName", IsColumn: false}, + {Key: "name", IsColumn: false}, + }, + AggregateOperator: v3.AggregateOperatorP95, + Expression: "A", + }, + }, + }, + }, + } + cache := inmemory.New(&inmemory.Options{TTL: 5 * time.Minute, CleanupInterval: 10 * time.Minute}) + opts := QuerierOptions{ + Cache: cache, + Reader: nil, + FluxInterval: 5 * time.Minute, + KeyGenerator: queryBuilder.NewKeyGenerator(), + + TestingMode: true, + ReturnedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "method": "GET", + "service_name": "test", + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + {Timestamp: 1675115596722, Value: 1}, + {Timestamp: 1675115596722 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 120*60*1000, Value: 3}, + }, + }, + }, + } + q := NewQuerier(opts) + expectedTimeRangeInQueryString := []string{ + fmt.Sprintf("unix_milli >= %d AND unix_milli < %d", 1675115580000, 1675115580000+120*60*1000), + fmt.Sprintf("unix_milli >= %d AND unix_milli < %d", 1675115580000+120*60*1000, 1675115580000+180*60*1000), + fmt.Sprintf("timestamp >= '%d' AND timestamp <= '%d'", 1675115580000*1000000, (1675115580000+120*60*1000)*int64(1000000)), + fmt.Sprintf("timestamp >= '%d' AND timestamp <= '%d'", (1675115580000+60*60*1000)*int64(1000000), (1675115580000+180*60*1000)*int64(1000000)), + } + + for i, param := range params { + _, errByName, err := q.QueryRange(context.Background(), param, nil) + if err != nil { + t.Errorf("expected no error, got %s", err) + } + if len(errByName) > 0 { + t.Errorf("expected no error, got %v", errByName) + } + + if !strings.Contains(q.QueriesExecuted()[i], expectedTimeRangeInQueryString[i]) { + t.Errorf("expected query to contain %s, got %s", expectedTimeRangeInQueryString[i], q.QueriesExecuted()[i]) + } + } +} + +func TestV2QueryRangeValueType(t *testing.T) { + // There shouldn't be any caching for value panel type + params := []*v3.QueryRangeParamsV3{ + { + Start: 1675115596722, + End: 1675115596722 + 120*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeValue, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + StepInterval: 60, + DataSource: v3.DataSourceMetrics, + AggregateAttribute: v3.AttributeKey{Key: "http_server_requests_seconds_count", Type: v3.AttributeKeyTypeUnspecified, DataType: "float64", IsColumn: true}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{Key: "method", IsColumn: false}, + Operator: "=", + Value: "GET", + }, + }, + }, + AggregateOperator: v3.AggregateOperatorSumRate, + TimeAggregation: v3.TimeAggregationRate, + SpaceAggregation: v3.SpaceAggregationSum, + Expression: "A", + ReduceTo: v3.ReduceToOperatorLast, + }, + }, + }, + }, + { + Start: 1675115596722 + 60*60*1000, + End: 1675115596722 + 180*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeValue, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + StepInterval: 60, + DataSource: v3.DataSourceTraces, + AggregateAttribute: v3.AttributeKey{Key: "durationNano", Type: v3.AttributeKeyTypeUnspecified, DataType: "float64", IsColumn: true}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: v3.AttributeKey{Key: "method", IsColumn: false}, + Operator: "=", + Value: "GET", + }, + }, + }, + AggregateOperator: v3.AggregateOperatorP95, + Expression: "A", + ReduceTo: v3.ReduceToOperatorLast, + }, + }, + }, + }, + } + cache := inmemory.New(&inmemory.Options{TTL: 60 * time.Minute, CleanupInterval: 10 * time.Minute}) + opts := QuerierOptions{ + Cache: cache, + Reader: nil, + FluxInterval: 5 * time.Minute, + KeyGenerator: queryBuilder.NewKeyGenerator(), + + TestingMode: true, + ReturnedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "method": "GET", + "service_name": "test", + "__name__": "http_server_requests_seconds_count", + }, + Points: []v3.Point{ + {Timestamp: 1675115596722, Value: 1}, + {Timestamp: 1675115596722 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 120*60*1000, Value: 3}, + }, + }, + }, + } + q := NewQuerier(opts) + // No caching + expectedTimeRangeInQueryString := []string{ + fmt.Sprintf("unix_milli >= %d AND unix_milli < %d", 1675115520000, 1675115580000+120*60*1000), + fmt.Sprintf("timestamp >= '%d' AND timestamp <= '%d'", (1675115580000+60*60*1000)*int64(1000000), (1675115580000+180*60*1000)*int64(1000000)), + } + + for i, param := range params { + _, errByName, err := q.QueryRange(context.Background(), param, nil) + if err != nil { + t.Errorf("expected no error, got %s", err) + } + if len(errByName) > 0 { + t.Errorf("expected no error, got %v", errByName) + } + + if !strings.Contains(q.QueriesExecuted()[i], expectedTimeRangeInQueryString[i]) { + t.Errorf("expected query to contain %s, got %s", expectedTimeRangeInQueryString[i], q.QueriesExecuted()[i]) + } + } +} + +// test timeshift +func TestV2QueryRangeTimeShift(t *testing.T) { + params := []*v3.QueryRangeParamsV3{ + { + Start: 1675115596722, //31, 3:23 + End: 1675115596722 + 120*60*1000, //31, 5:23 + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + StepInterval: 60, + DataSource: v3.DataSourceLogs, + AggregateAttribute: v3.AttributeKey{}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{}, + }, + AggregateOperator: v3.AggregateOperatorCount, + Expression: "A", + ShiftBy: 86400, + }, + }, + }, + }, + } + opts := QuerierOptions{ + Reader: nil, + FluxInterval: 5 * time.Minute, + KeyGenerator: queryBuilder.NewKeyGenerator(), + TestingMode: true, + } + q := NewQuerier(opts) + // logs queries are generates in ns + expectedTimeRangeInQueryString := fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+120*60*1000)-86400*1000)*1000000) + + for i, param := range params { + _, errByName, err := q.QueryRange(context.Background(), param, nil) + if err != nil { + t.Errorf("expected no error, got %s", err) + } + if len(errByName) > 0 { + t.Errorf("expected no error, got %v", errByName) + } + if !strings.Contains(q.QueriesExecuted()[i], expectedTimeRangeInQueryString) { + t.Errorf("expected query to contain %s, got %s", expectedTimeRangeInQueryString, q.QueriesExecuted()[i]) + } + } +} + +// timeshift works with caching +func TestV2QueryRangeTimeShiftWithCache(t *testing.T) { + params := []*v3.QueryRangeParamsV3{ + { + Start: 1675115596722 + 60*60*1000 - 86400*1000, //30, 4:23 + End: 1675115596722 + 120*60*1000 - 86400*1000, //30, 5:23 + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + StepInterval: 60, + DataSource: v3.DataSourceLogs, + AggregateAttribute: v3.AttributeKey{}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{}, + }, + AggregateOperator: v3.AggregateOperatorCount, + Expression: "A", + GroupBy: []v3.AttributeKey{ + {Key: "service_name", IsColumn: false}, + {Key: "method", IsColumn: false}, + }, + }, + }, + }, + }, + { + Start: 1675115596722, //31, 3:23 + End: 1675115596722 + 120*60*1000, //31, 5:23 + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + StepInterval: 60, + DataSource: v3.DataSourceLogs, + AggregateAttribute: v3.AttributeKey{}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{}, + }, + AggregateOperator: v3.AggregateOperatorCount, + Expression: "A", + ShiftBy: 86400, + GroupBy: []v3.AttributeKey{ + {Key: "service_name", IsColumn: false}, + {Key: "method", IsColumn: false}, + }, + }, + }, + }, + }, + } + cache := inmemory.New(&inmemory.Options{TTL: 60 * time.Minute, CleanupInterval: 10 * time.Minute}) + opts := QuerierOptions{ + Cache: cache, + Reader: nil, + FluxInterval: 5 * time.Minute, + KeyGenerator: queryBuilder.NewKeyGenerator(), + TestingMode: true, + ReturnedSeries: []*v3.Series{ + { + Labels: map[string]string{}, + Points: []v3.Point{ + {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, + {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, + }, + }, + }, + } + q := NewQuerier(opts) + + // logs queries are generates in ns + expectedTimeRangeInQueryString := []string{ + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722+60*60*1000-86400*1000)*1000000, (1675115596722+120*60*1000-86400*1000)*1000000), + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+60*60*1000)-86400*1000-1)*1000000), + } + + for i, param := range params { + _, errByName, err := q.QueryRange(context.Background(), param, nil) + if err != nil { + t.Errorf("expected no error, got %s", err) + } + if len(errByName) > 0 { + t.Errorf("expected no error, got %v", errByName) + } + if !strings.Contains(q.QueriesExecuted()[i], expectedTimeRangeInQueryString[i]) { + t.Errorf("expected query to contain %s, got %s", expectedTimeRangeInQueryString[i], q.QueriesExecuted()[i]) + } + } +} + +// timeshift with limit queries +func TestV2QueryRangeTimeShiftWithLimitAndCache(t *testing.T) { + params := []*v3.QueryRangeParamsV3{ + { + Start: 1675115596722 + 60*60*1000 - 86400*1000, //30, 4:23 + End: 1675115596722 + 120*60*1000 - 86400*1000, //30, 5:23 + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + StepInterval: 60, + DataSource: v3.DataSourceLogs, + AggregateAttribute: v3.AttributeKey{}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{}, + }, + AggregateOperator: v3.AggregateOperatorCount, + Expression: "A", + GroupBy: []v3.AttributeKey{ + {Key: "service_name", IsColumn: false}, + {Key: "method", IsColumn: false}, + }, + Limit: 5, + }, + }, + }, + }, + { + Start: 1675115596722, //31, 3:23 + End: 1675115596722 + 120*60*1000, //31, 5:23 + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypeBuilder, + PanelType: v3.PanelTypeGraph, + BuilderQueries: map[string]*v3.BuilderQuery{ + "A": { + QueryName: "A", + StepInterval: 60, + DataSource: v3.DataSourceLogs, + AggregateAttribute: v3.AttributeKey{}, + Filters: &v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{}, + }, + AggregateOperator: v3.AggregateOperatorCount, + Expression: "A", + ShiftBy: 86400, + GroupBy: []v3.AttributeKey{ + {Key: "service_name", IsColumn: false}, + {Key: "method", IsColumn: false}, + }, + Limit: 5, + }, + }, + }, + }, + } + cache := inmemory.New(&inmemory.Options{TTL: 60 * time.Minute, CleanupInterval: 10 * time.Minute}) + opts := QuerierOptions{ + Cache: cache, + Reader: nil, + FluxInterval: 5 * time.Minute, + KeyGenerator: queryBuilder.NewKeyGenerator(), + TestingMode: true, + ReturnedSeries: []*v3.Series{ + { + Labels: map[string]string{}, + Points: []v3.Point{ + {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, + {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, + }, + }, + }, + } + q := NewQuerier(opts) + + // logs queries are generates in ns + expectedTimeRangeInQueryString := []string{ + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722+60*60*1000-86400*1000)*1000000, (1675115596722+120*60*1000-86400*1000)*1000000), + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+60*60*1000)-86400*1000-1)*1000000), + } + + for i, param := range params { + _, errByName, err := q.QueryRange(context.Background(), param, nil) + if err != nil { + t.Errorf("expected no error, got %s", err) + } + if len(errByName) > 0 { + t.Errorf("expected no error, got %v", errByName) + } + if !strings.Contains(q.QueriesExecuted()[i], expectedTimeRangeInQueryString[i]) { + t.Errorf("expected query to contain %s, got %s", expectedTimeRangeInQueryString[i], q.QueriesExecuted()[i]) + } + } +} + +func TestV2QueryRangeValueTypePromQL(t *testing.T) { + // There shouldn't be any caching for value panel type + params := []*v3.QueryRangeParamsV3{ + { + Start: 1675115596722, + End: 1675115596722 + 120*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypePromQL, + PanelType: v3.PanelTypeValue, + PromQueries: map[string]*v3.PromQuery{ + "A": { + Query: "signoz_calls_total", + }, + }, + }, + }, + { + Start: 1675115596722 + 60*60*1000, + End: 1675115596722 + 180*60*1000, + Step: 5 * time.Minute.Milliseconds(), + CompositeQuery: &v3.CompositeQuery{ + QueryType: v3.QueryTypePromQL, + PanelType: v3.PanelTypeValue, + PromQueries: map[string]*v3.PromQuery{ + "A": { + Query: "signoz_latency_bucket", + }, + }, + }, + }, + } + cache := inmemory.New(&inmemory.Options{TTL: 60 * time.Minute, CleanupInterval: 10 * time.Minute}) + opts := QuerierOptions{ + Cache: cache, + Reader: nil, + FluxInterval: 5 * time.Minute, + KeyGenerator: queryBuilder.NewKeyGenerator(), + + TestingMode: true, + ReturnedSeries: []*v3.Series{ + { + Labels: map[string]string{ + "method": "GET", + "service_name": "test", + "__name__": "doesn't matter", + }, + Points: []v3.Point{ + {Timestamp: 1675115596722, Value: 1}, + {Timestamp: 1675115596722 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 120*60*1000, Value: 3}, + }, + }, + }, + } + q := NewQuerier(opts) + + expectedQueryAndTimeRanges := []struct { + query string + ranges []missInterval + }{ + { + query: "signoz_calls_total", + ranges: []missInterval{ + {start: 1675115596722, end: 1675115596722 + 120*60*1000}, + }, + }, + { + query: "signoz_latency_bucket", + ranges: []missInterval{ + {start: 1675115596722 + 60*60*1000, end: 1675115596722 + 180*60*1000}, + }, + }, + } + + for i, param := range params { + _, errByName, err := q.QueryRange(context.Background(), param, nil) + if err != nil { + t.Errorf("expected no error, got %s", err) + } + if len(errByName) > 0 { + t.Errorf("expected no error, got %v", errByName) + } + + if !strings.Contains(q.QueriesExecuted()[i], expectedQueryAndTimeRanges[i].query) { + t.Errorf("expected query to contain %s, got %s", expectedQueryAndTimeRanges[i].query, q.QueriesExecuted()[i]) + } + if len(q.TimeRanges()[i]) != 2 { + t.Errorf("expected time ranges to be %v, got %v", expectedQueryAndTimeRanges[i].ranges, q.TimeRanges()[i]) + } + if q.TimeRanges()[i][0] != int(expectedQueryAndTimeRanges[i].ranges[0].start) { + t.Errorf("expected time ranges to be %v, got %v", expectedQueryAndTimeRanges[i].ranges, q.TimeRanges()[i]) + } + if q.TimeRanges()[i][1] != int(expectedQueryAndTimeRanges[i].ranges[0].end) { + t.Errorf("expected time ranges to be %v, got %v", expectedQueryAndTimeRanges[i].ranges, q.TimeRanges()[i]) + } + } +} diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index 0ab20fed0e..385d48173b 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -110,4 +110,5 @@ type Querier interface { // test helpers QueriesExecuted() []string + TimeRanges() [][]int } From c7e3e6dc4e295a7a5e365ce671c21b8db635dc9b Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 15 Jul 2024 21:04:49 +0530 Subject: [PATCH 093/281] fix: retain legends while changing panel types (#5447) --- frontend/src/container/NewWidget/utils.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/frontend/src/container/NewWidget/utils.ts b/frontend/src/container/NewWidget/utils.ts index 7e651ff2ea..562a989529 100644 --- a/frontend/src/container/NewWidget/utils.ts +++ b/frontend/src/container/NewWidget/utils.ts @@ -54,6 +54,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -72,6 +73,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -88,6 +90,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -107,6 +110,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -125,6 +129,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -141,6 +146,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -157,6 +163,8 @@ export const panelTypeDataSourceFormValuesMap: Record< 'having', 'orderBy', 'functions', + 'disabled', + 'legend', ], }, }, @@ -172,6 +180,8 @@ export const panelTypeDataSourceFormValuesMap: Record< 'orderBy', 'functions', 'spaceAggregation', + 'disabled', + 'legend', ], }, }, @@ -185,6 +195,8 @@ export const panelTypeDataSourceFormValuesMap: Record< 'limit', 'having', 'orderBy', + 'disabled', + 'legend', ], }, }, @@ -204,6 +216,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -222,6 +235,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -238,6 +252,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -257,6 +272,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -275,6 +291,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -291,6 +308,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -325,6 +343,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -341,6 +360,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, @@ -357,6 +377,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'queryName', 'expression', 'disabled', + 'legend', ], }, }, From a6e68c65193ba448c48a1a5bb81773dd1e57444b Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 15 Jul 2024 21:15:37 +0530 Subject: [PATCH 094/281] fix: issue with table sorting when column contains both string and numbers (#5458) --- .../__tests__/utils.test.tsx | 90 ++++++++++++++++++- .../src/container/GridTableComponent/utils.ts | 47 +++++++--- 2 files changed, 124 insertions(+), 13 deletions(-) diff --git a/frontend/src/container/GridTableComponent/__tests__/utils.test.tsx b/frontend/src/container/GridTableComponent/__tests__/utils.test.tsx index f0582a51fb..e8f7a631bd 100644 --- a/frontend/src/container/GridTableComponent/__tests__/utils.test.tsx +++ b/frontend/src/container/GridTableComponent/__tests__/utils.test.tsx @@ -1,6 +1,10 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData'; -import { createColumnsAndDataSource, getQueryLegend } from '../utils'; +import { + createColumnsAndDataSource, + getQueryLegend, + sortFunction, +} from '../utils'; import { expectedOutputWithLegends, tableDataMultipleQueriesSuccessResponse, @@ -39,4 +43,88 @@ describe('Table Panel utils', () => { // should return undefined when legend not present expect(getQueryLegend(query, 'B')).toBe(undefined); }); + + it('sorter function for table sorting', () => { + let rowA: { + A: string | number; + timestamp: number; + key: string; + } = { + A: 22.4, + timestamp: 111111, + key: '1111', + }; + let rowB: { + A: string | number; + timestamp: number; + key: string; + } = { + A: 'n/a', + timestamp: 111112, + key: '1112', + }; + const item = { + isValueColumn: true, + name: 'A', + queryName: 'A', + }; + // A has value and value is considered bigger than n/a hence 1 + expect(sortFunction(rowA, rowB, item)).toBe(1); + + rowA = { + A: 'n/a', + timestamp: 111111, + key: '1111', + }; + rowB = { + A: 22.4, + timestamp: 111112, + key: '1112', + }; + + // B has value and value is considered bigger than n/a hence -1 + expect(sortFunction(rowA, rowB, item)).toBe(-1); + + rowA = { + A: 11, + timestamp: 111111, + key: '1111', + }; + rowB = { + A: 22, + timestamp: 111112, + key: '1112', + }; + + // A and B has value , since B > A hence A-B + expect(sortFunction(rowA, rowB, item)).toBe(-11); + + rowA = { + A: 'read', + timestamp: 111111, + key: '1111', + }; + rowB = { + A: 'write', + timestamp: 111112, + key: '1112', + }; + + // A and B are strings so A is smaller than B because r comes before w hence -1 + expect(sortFunction(rowA, rowB, item)).toBe(-1); + + rowA = { + A: 'n/a', + timestamp: 111111, + key: '1111', + }; + rowB = { + A: 'n/a', + timestamp: 111112, + key: '1112', + }; + + // A and B are strings n/a , since both of them are same hence 0 + expect(sortFunction(rowA, rowB, item)).toBe(0); + }); }); diff --git a/frontend/src/container/GridTableComponent/utils.ts b/frontend/src/container/GridTableComponent/utils.ts index 089a35fe00..acd58af62d 100644 --- a/frontend/src/container/GridTableComponent/utils.ts +++ b/frontend/src/container/GridTableComponent/utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/cognitive-complexity */ import { ColumnsType, ColumnType } from 'antd/es/table'; import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; import { QUERY_TABLE_CONFIG } from 'container/QueryTable/config'; @@ -105,6 +106,39 @@ export function getQueryLegend( return legend; } +export function sortFunction( + a: RowData, + b: RowData, + item: { + name: string; + queryName: string; + isValueColumn: boolean; + }, +): number { + // assumption :- number values is bigger than 'n/a' + const valueA = Number(a[`${item.name}_without_unit`] ?? a[item.name]); + const valueB = Number(b[`${item.name}_without_unit`] ?? b[item.name]); + + // if both the values are numbers then return the difference here + if (!isNaN(valueA) && !isNaN(valueB)) { + return valueA - valueB; + } + + // if valueB is a number then make it bigger value + if (isNaN(valueA) && !isNaN(valueB)) { + return -1; + } + + // if valueA is number make it the bigger value + if (!isNaN(valueA) && isNaN(valueB)) { + return 1; + } + + // if both of them are strings do the localecompare + return ((a[item.name] as string) || '').localeCompare( + (b[item.name] as string) || '', + ); +} export function createColumnsAndDataSource( data: TableData, currentQuery: Query, @@ -123,18 +157,7 @@ export function createColumnsAndDataSource( title: !isEmpty(legend) ? legend : item.name, width: QUERY_TABLE_CONFIG.width, render: renderColumnCell && renderColumnCell[item.name], - sorter: (a: RowData, b: RowData): number => { - const valueA = Number(a[`${item.name}_without_unit`] ?? a[item.name]); - const valueB = Number(b[`${item.name}_without_unit`] ?? b[item.name]); - - if (!isNaN(valueA) && !isNaN(valueB)) { - return valueA - valueB; - } - - return ((a[item.name] as string) || '').localeCompare( - (b[item.name] as string) || '', - ); - }, + sorter: (a: RowData, b: RowData): number => sortFunction(a, b, item), }; return [...acc, column]; From 42f7905b3bf02ccf77acea97c3a5c31c428614a5 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Tue, 16 Jul 2024 11:00:29 +0530 Subject: [PATCH 095/281] =?UTF-8?q?feat:=20show=20status=20message,=20stat?= =?UTF-8?q?us=20code=20string,=20span=20kind=20in=20trace=20det=E2=80=A6?= =?UTF-8?q?=20(#5428)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: show status message, status code string, span kind in trace details * chore: update tests * chore: update snapshots --- frontend/src/container/GantChart/utils.ts | 3 ++ .../TraceDetail/SelectedSpanDetails/index.tsx | 29 +++++++++++++++++-- .../src/container/TraceDetail/utils.test.ts | 3 ++ .../__tests__/TraceFlameGraph.test.tsx | 3 ++ .../src/lib/__fixtures__/getRandomColor.ts | 9 ++++++ frontend/src/types/api/trace/getTraceItem.ts | 6 ++++ .../__snapshots__/spanToTree.test.ts.snap | 18 ++++++++++++ frontend/src/utils/fixtures/TraceData.ts | 9 ++++++ frontend/src/utils/spanToTree.ts | 6 ++++ 9 files changed, 83 insertions(+), 3 deletions(-) diff --git a/frontend/src/container/GantChart/utils.ts b/frontend/src/container/GantChart/utils.ts index 0421fd40bd..7b3e1115e7 100644 --- a/frontend/src/container/GantChart/utils.ts +++ b/frontend/src/container/GantChart/utils.ts @@ -124,6 +124,9 @@ const getSpanWithoutChildren = ( value: span.value, event: span.event, hasError: span.hasError, + spanKind: span.spanKind, + statusCodeString: span.statusCodeString, + statusMessage: span.statusMessage, }); export const isSpanPresentInSearchString = ( diff --git a/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx b/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx index ce7f3372a3..39e180ee12 100644 --- a/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx +++ b/frontend/src/container/TraceDetail/SelectedSpanDetails/index.tsx @@ -1,4 +1,4 @@ -import { Button, Modal, Tabs, Typography } from 'antd'; +import { Button, Modal, Tabs, Tooltip, Typography } from 'antd'; import Editor from 'components/Editor'; import { StyledSpace } from 'components/Styled'; import { QueryParams } from 'constants/query'; @@ -102,8 +102,7 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { marginTop: '16px', }} > - {' '} - Details for selected Span{' '} + Details for selected Span Service @@ -114,6 +113,30 @@ function SelectedSpanDetails(props: SelectedSpanDetailsProps): JSX.Element { {tree.name} + SpanKind + + {tree.spanKind} + + + StatusCodeString + + + + {tree.statusCodeString} + + + {tree.statusMessage && ( + <> + + StatusMessage + + + + {tree.statusMessage} + + + )} + diff --git a/frontend/src/container/TraceDetail/utils.test.ts b/frontend/src/container/TraceDetail/utils.test.ts index 4cb20055bf..b8359916f4 100644 --- a/frontend/src/container/TraceDetail/utils.test.ts +++ b/frontend/src/container/TraceDetail/utils.test.ts @@ -13,6 +13,9 @@ describe('traces/getTreeLevelsCount', () => { children, serviceName: '', serviceColour: '', + spanKind: '', + statusCodeString: '', + statusMessage: '', }); test('should return 0 for empty tree', () => { diff --git a/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx b/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx index adcaa3f003..c2eb3db911 100644 --- a/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx +++ b/frontend/src/container/TraceFlameGraph/__tests__/TraceFlameGraph.test.tsx @@ -37,6 +37,9 @@ test('loads and displays greeting', () => { event: [], hasError: false, parent: undefined, + spanKind: '', + statusCodeString: '', + statusMessage: '', }, }} /> diff --git a/frontend/src/lib/__fixtures__/getRandomColor.ts b/frontend/src/lib/__fixtures__/getRandomColor.ts index 2ebbecae15..0ffe3fe1fd 100644 --- a/frontend/src/lib/__fixtures__/getRandomColor.ts +++ b/frontend/src/lib/__fixtures__/getRandomColor.ts @@ -16,6 +16,9 @@ const spans: Span[] = [ [''], [''], false, + 'Server', + 'Unset', + 'Lorem Ipsum', ], [ 2, @@ -30,6 +33,9 @@ const spans: Span[] = [ [''], [''], false, + 'Server2', + 'Unset2', + 'Lorem Ipsum2', ], [ 3, @@ -44,6 +50,9 @@ const spans: Span[] = [ [''], [''], false, + 'Server3', + 'Unset3', + 'Lorem Ipsum3', ], ]; diff --git a/frontend/src/types/api/trace/getTraceItem.ts b/frontend/src/types/api/trace/getTraceItem.ts index 5f94e0f6d8..4d968b5000 100644 --- a/frontend/src/types/api/trace/getTraceItem.ts +++ b/frontend/src/types/api/trace/getTraceItem.ts @@ -33,6 +33,9 @@ export type Span = [ string[], string[], boolean, + string, + string, + string, ]; export interface ITraceTree { @@ -49,6 +52,9 @@ export interface ITraceTree { hasError?: boolean; event?: ITraceEvents[]; isMissing?: boolean; + spanKind: string; + statusCodeString: string; + statusMessage: string; childReferences?: Record[]; nonChildReferences?: Record[]; // For internal use diff --git a/frontend/src/utils/__tests__/__snapshots__/spanToTree.test.ts.snap b/frontend/src/utils/__tests__/__snapshots__/spanToTree.test.ts.snap index c3149cf21a..49004ffc56 100644 --- a/frontend/src/utils/__tests__/__snapshots__/spanToTree.test.ts.snap +++ b/frontend/src/utils/__tests__/__snapshots__/spanToTree.test.ts.snap @@ -49,7 +49,10 @@ Object { "nonChildReferences": Array [], "serviceColour": "", "serviceName": "frontend", + "spanKind": "Lorem Ipsum2", "startTime": 1657275433246, + "statusCodeString": "Unset2", + "statusMessage": "Server2", "tags": Array [ Object { "key": "host.name.span3", @@ -78,7 +81,10 @@ Object { "nonChildReferences": Array [], "serviceColour": "", "serviceName": "frontend", + "spanKind": "Lorem Ipsum1", "startTime": 1657275433246, + "statusCodeString": "Unset1", + "statusMessage": "Server1", "tags": Array [ Object { "key": "host.name.span2", @@ -106,7 +112,10 @@ Object { "nonChildReferences": Array [], "serviceColour": "", "serviceName": "frontend", + "spanKind": "Lorem Ipsum", "startTime": 1657275433246, + "statusCodeString": "Unset", + "statusMessage": "Server", "tags": Array [ Object { "key": "host.name.span1", @@ -152,7 +161,10 @@ Object { "nonChildReferences": Array [], "serviceColour": "", "serviceName": "frontend", + "spanKind": "Lorem Ipsum2", "startTime": 1657275433246, + "statusCodeString": "Unset2", + "statusMessage": "Server2", "tags": Array [ Object { "key": "host.name.span3", @@ -168,7 +180,10 @@ Object { "name": "Missing Span (span_2)", "serviceColour": "", "serviceName": "", + "spanKind": "", "startTime": null, + "statusCodeString": "", + "statusMessage": "", "tags": Array [], "time": null, "value": null, @@ -201,7 +216,10 @@ Object { "nonChildReferences": Array [], "serviceColour": "", "serviceName": "frontend", + "spanKind": "Lorem Ipsum", "startTime": 1657275433246, + "statusCodeString": "Unset", + "statusMessage": "Server", "tags": Array [ Object { "key": "host.name.span1", diff --git a/frontend/src/utils/fixtures/TraceData.ts b/frontend/src/utils/fixtures/TraceData.ts index 289e91e949..b24643dbba 100644 --- a/frontend/src/utils/fixtures/TraceData.ts +++ b/frontend/src/utils/fixtures/TraceData.ts @@ -16,6 +16,9 @@ export const TraceData: Span[] = [ '{"timeUnixNano":1657275433246142000,"attributeMap":{"event":"HTTP request received S1","level":"info","method":"GET","url":"/dispatch?customer=392\\u0026nonse=0.015296363321630757"}}', ], false, + 'Server', + 'Unset', + 'Lorem Ipsum', ], [ 1657275433246, @@ -32,6 +35,9 @@ export const TraceData: Span[] = [ '{"timeUnixNano":1657275433246142000,"attributeMap":{"event":"HTTP request received S2","level":"info","method":"GET","url":"/dispatch?customer=392\\u0026nonse=0.015296363321630757"}}', ], false, + 'Server1', + 'Unset1', + 'Lorem Ipsum1', ], [ 1657275433246, @@ -48,5 +54,8 @@ export const TraceData: Span[] = [ '{"timeUnixNano":1657275433246142000,"attributeMap":{"event":"HTTP request received S3","level":"info","method":"GET","url":"/dispatch?customer=392\\u0026nonse=0.015296363321630757"}}', ], false, + 'Server2', + 'Unset2', + 'Lorem Ipsum2', ], ]; diff --git a/frontend/src/utils/spanToTree.ts b/frontend/src/utils/spanToTree.ts index 35d17f0681..155f0e61c4 100644 --- a/frontend/src/utils/spanToTree.ts +++ b/frontend/src/utils/spanToTree.ts @@ -71,6 +71,9 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => { time: null as never, value: null as never, isMissing: true, + statusMessage: '', + statusCodeString: '', + spanKind: '', }; } }); @@ -93,6 +96,9 @@ export const spanToTreeUtil = (inputSpanList: Span[]): ITraceForest => { event: span[10]?.map((e) => JSON.parse(e || '{}') || {}), childReferences, nonChildReferences, + statusMessage: span[12], + statusCodeString: span[13], + spanKind: span[14], }; spanMap[span[1]] = spanObject; }); From 46e6c34e5158c4afdc217282fb1c4de4a676adae Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 16 Jul 2024 13:13:25 +0430 Subject: [PATCH 096/281] fix: block alert creation if query_range API fails (#5441) --- .../container/FormAlertRules/ChartPreview/index.tsx | 7 +++++-- frontend/src/container/FormAlertRules/index.tsx | 12 ++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx index 331a5a0fc7..03db8a362e 100644 --- a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx +++ b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx @@ -48,6 +48,7 @@ export interface ChartPreviewProps { userQueryKey?: string; allowSelectedIntervalForStepGen?: boolean; yAxisUnit: string; + setQueryStatus?: (status: string) => void; } // eslint-disable-next-line sonarjs/cognitive-complexity @@ -62,6 +63,7 @@ function ChartPreview({ allowSelectedIntervalForStepGen = false, alertDef, yAxisUnit, + setQueryStatus, }: ChartPreviewProps): JSX.Element | null { const { t } = useTranslation('alerts'); const dispatch = useDispatch(); @@ -149,10 +151,10 @@ function ChartPreview({ useEffect((): void => { const { startTime, endTime } = getTimeRange(queryResponse); - + if (setQueryStatus) setQueryStatus(queryResponse.status); setMinTimeScale(startTime); setMaxTimeScale(endTime); - }, [maxTime, minTime, globalSelectedInterval, queryResponse]); + }, [maxTime, minTime, globalSelectedInterval, queryResponse, setQueryStatus]); if (queryResponse.data && graphType === PANEL_TYPES.BAR) { const sortedSeriesData = getSortedSeriesData( @@ -284,6 +286,7 @@ ChartPreview.defaultProps = { userQueryKey: '', allowSelectedIntervalForStepGen: false, alertDef: undefined, + setQueryStatus: (): void => {}, }; export default ChartPreview; diff --git a/frontend/src/container/FormAlertRules/index.tsx b/frontend/src/container/FormAlertRules/index.tsx index 40abbb3583..4b383eb272 100644 --- a/frontend/src/container/FormAlertRules/index.tsx +++ b/frontend/src/container/FormAlertRules/index.tsx @@ -101,6 +101,7 @@ function FormAlertRules({ const isNewRule = ruleId === 0; const [loading, setLoading] = useState(false); + const [queryStatus, setQueryStatus] = useState(''); // alertDef holds the form values to be posted const [alertDef, setAlertDef] = useState(initialValue); @@ -523,6 +524,7 @@ function FormAlertRules({ alertDef={alertDef} yAxisUnit={yAxisUnit || ''} graphType={panelType || PANEL_TYPES.TIME_SERIES} + setQueryStatus={setQueryStatus} /> ); @@ -540,6 +542,7 @@ function FormAlertRules({ selectedInterval={globalSelectedInterval} yAxisUnit={yAxisUnit || ''} graphType={panelType || PANEL_TYPES.TIME_SERIES} + setQueryStatus={setQueryStatus} /> ); @@ -665,7 +668,8 @@ function FormAlertRules({ disabled={ isAlertNameMissing || isAlertAvailableToSave || - !isChannelConfigurationValid + !isChannelConfigurationValid || + queryStatus === 'error' } > {isNewRule ? t('button_createrule') : t('button_savechanges')} @@ -674,7 +678,11 @@ function FormAlertRules({ From cd07c743b604209555118f004b77140041e76d20 Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Tue, 16 Jul 2024 13:16:13 +0430 Subject: [PATCH 097/281] Implement OverlayScrollbars throughout the app for MacOS-like scrolling experience (#5423) * feat: build overlay scrollbar component for Virtuoso elements * feat: apply overlay scroll to Virtuoso components * feat: build overlay scrollbar component for normal scrollable sections * feat: apply overlay scrollbar to normal scrollable sections * feat: add dark mode UI support to overlay scrollbars * chore: rename OverlayScrollbar to OverlayScrollbarForTypicalChildren * chore: move inline style to scss file * chore: rename VirtuosoOverlayScrollbar to OverlayScrollbarForVirtuosoChildren * chore: move OverlayScrollbarForTypicalChildren to components folder * chore: create a common component for handling Virtuoso and Typical scroll sections * chore: rename Virtuoso and Typical Overlay Scrollbar components * fix: fix the overlay scrollbar initialization flickering * fix: remove calculated height from typical overlay scrollbar + remove the explicit height: 100% --- frontend/package.json | 2 + .../OverlayScrollbar/OverlayScrollbar.tsx | 54 +++++++++ .../TypicalOverlayScrollbar.tsx | 31 +++++ .../typicalOverlayScrollbar.scss | 3 + .../VirtuosoOverlayScrollbar.tsx | 37 ++++++ .../virtuosoOverlayScrollbar.scss | 5 + .../container/AppLayout/AppLayout.styles.scss | 1 - frontend/src/container/AppLayout/index.tsx | 40 ++++--- frontend/src/container/AppLayout/styles.ts | 1 - .../GridCardLayout/GridCardLayout.styles.scss | 1 + .../GridCardLayout/GridCardLayout.tsx | 6 +- .../container/LiveLogs/LiveLogsList/index.tsx | 17 +-- .../ContextView/ContextLogRenderer.tsx | 17 +-- .../src/container/LogsContextList/index.tsx | 18 +-- .../src/container/LogsExplorerList/index.tsx | 21 ++-- .../LogsPanelTable/LogsPanelComponent.tsx | 25 ++-- frontend/src/container/LogsTable/index.tsx | 5 +- .../DashboardDescription/SettingsDrawer.tsx | 5 +- frontend/src/container/NewDashboard/index.tsx | 2 +- frontend/src/container/NewWidget/index.tsx | 107 +++++++++--------- .../TracesTableComponent.tsx | 25 ++-- .../useInitializeOverlayScrollbar.tsx | 43 +++++++ frontend/src/styles.scss | 1 + frontend/yarn.lock | 12 +- 24 files changed, 351 insertions(+), 128 deletions(-) create mode 100644 frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx create mode 100644 frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx create mode 100644 frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss create mode 100644 frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx create mode 100644 frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss create mode 100644 frontend/src/hooks/useInitializeOverlayScrollbar/useInitializeOverlayScrollbar.tsx diff --git a/frontend/package.json b/frontend/package.json index 2b2e803478..34e08ea263 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -110,6 +110,8 @@ "react-syntax-highlighter": "15.5.0", "react-use": "^17.3.2", "react-virtuoso": "4.0.3", + "overlayscrollbars-react": "^0.5.6", + "overlayscrollbars": "^2.8.1", "redux": "^4.0.5", "redux-thunk": "^2.3.0", "rehype-raw": "7.0.0", diff --git a/frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx b/frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx new file mode 100644 index 0000000000..73c95ce27d --- /dev/null +++ b/frontend/src/components/OverlayScrollbar/OverlayScrollbar.tsx @@ -0,0 +1,54 @@ +import TypicalOverlayScrollbar from 'components/TypicalOverlayScrollbar/TypicalOverlayScrollbar'; +import VirtuosoOverlayScrollbar from 'components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar'; +import { useIsDarkMode } from 'hooks/useDarkMode'; +import { PartialOptions } from 'overlayscrollbars'; +import { CSSProperties, ReactElement, useMemo } from 'react'; + +type Props = { + children: ReactElement; + isVirtuoso?: boolean; + style?: CSSProperties; + options?: PartialOptions; +}; + +function OverlayScrollbar({ + children, + isVirtuoso, + style, + options: customOptions, +}: Props): any { + const isDarkMode = useIsDarkMode(); + const options = useMemo( + () => + ({ + scrollbars: { + autoHide: 'scroll', + theme: isDarkMode ? 'os-theme-light' : 'os-theme-dark', + }, + ...(customOptions || {}), + } as PartialOptions), + [customOptions, isDarkMode], + ); + + if (isVirtuoso) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +} + +OverlayScrollbar.defaultProps = { + isVirtuoso: false, + style: {}, + options: {}, +}; + +export default OverlayScrollbar; diff --git a/frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx b/frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx new file mode 100644 index 0000000000..1ed1717d6d --- /dev/null +++ b/frontend/src/components/TypicalOverlayScrollbar/TypicalOverlayScrollbar.tsx @@ -0,0 +1,31 @@ +import './typicalOverlayScrollbar.scss'; + +import { PartialOptions } from 'overlayscrollbars'; +import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; +import { CSSProperties, ReactElement } from 'react'; + +interface Props { + children: ReactElement; + style?: CSSProperties; + options?: PartialOptions; +} + +export default function TypicalOverlayScrollbar({ + children, + style, + options, +}: Props): ReturnType { + return ( + + {children} + + ); +} + +TypicalOverlayScrollbar.defaultProps = { style: {}, options: {} }; diff --git a/frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss b/frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss new file mode 100644 index 0000000000..9dd4c3b381 --- /dev/null +++ b/frontend/src/components/TypicalOverlayScrollbar/typicalOverlayScrollbar.scss @@ -0,0 +1,3 @@ +.overlay-scrollbar { + height: 100%; +} diff --git a/frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx b/frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx new file mode 100644 index 0000000000..4f0e905fc2 --- /dev/null +++ b/frontend/src/components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar.tsx @@ -0,0 +1,37 @@ +import './virtuosoOverlayScrollbar.scss'; + +import useInitializeOverlayScrollbar from 'hooks/useInitializeOverlayScrollbar/useInitializeOverlayScrollbar'; +import { PartialOptions } from 'overlayscrollbars'; +import React, { CSSProperties, ReactElement } from 'react'; + +interface VirtuosoOverlayScrollbarProps { + children: ReactElement; + style?: CSSProperties; + options: PartialOptions; +} + +export default function VirtuosoOverlayScrollbar({ + children, + style, + options, +}: VirtuosoOverlayScrollbarProps): JSX.Element { + const { rootRef, setScroller } = useInitializeOverlayScrollbar(options); + + const enhancedChild = React.cloneElement(children, { + scrollerRef: setScroller, + 'data-overlayscrollbars-initialize': true, + }); + + return ( +
+ {enhancedChild} +
+ ); +} + +VirtuosoOverlayScrollbar.defaultProps = { style: {} }; diff --git a/frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss b/frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss new file mode 100644 index 0000000000..728bd1e8c3 --- /dev/null +++ b/frontend/src/components/VirtuosoOverlayScrollbar/virtuosoOverlayScrollbar.scss @@ -0,0 +1,5 @@ +.overlay-scroll-wrapper { + height: 100%; + width: 100%; + overflow: auto; +} diff --git a/frontend/src/container/AppLayout/AppLayout.styles.scss b/frontend/src/container/AppLayout/AppLayout.styles.scss index 6a0b223edf..c789ba5e0f 100644 --- a/frontend/src/container/AppLayout/AppLayout.styles.scss +++ b/frontend/src/container/AppLayout/AppLayout.styles.scss @@ -5,7 +5,6 @@ .app-content { width: calc(100% - 64px); - overflow: auto; z-index: 0; .content-container { diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 774e0e888d..ba64e9af45 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -9,6 +9,7 @@ import getLocalStorageKey from 'api/browser/localstorage/get'; import getUserLatestVersion from 'api/user/getLatestVersion'; import getUserVersion from 'api/user/getVersion'; import cx from 'classnames'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; import ROUTES from 'constants/routes'; import SideNav from 'container/SideNav'; @@ -303,24 +304,29 @@ function AppLayout(props: AppLayoutProps): JSX.Element { collapsed={collapsed} /> )} -
+
}> - - - {isToDisplayLayout && !renderFullScreen && } - {children} - + + + + {isToDisplayLayout && !renderFullScreen && } + {children} + +
diff --git a/frontend/src/container/AppLayout/styles.ts b/frontend/src/container/AppLayout/styles.ts index 5edddcac40..c66d2ee4d8 100644 --- a/frontend/src/container/AppLayout/styles.ts +++ b/frontend/src/container/AppLayout/styles.ts @@ -13,7 +13,6 @@ export const Layout = styled(LayoutComponent)` `; export const LayoutContent = styled(LayoutComponent.Content)` - overflow-y: auto; height: 100%; &::-webkit-scrollbar { width: 0.1rem; diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss index 77e3a0822e..1b2bd2a7ba 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss +++ b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss @@ -1,6 +1,7 @@ .fullscreen-grid-container { overflow: auto; margin: 8px -8px; + margin-right: 0; .react-grid-layout { border: none !important; diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx index 11bdf492ee..f0d178866c 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx +++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx @@ -428,7 +428,11 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element { return isDashboardEmpty ? ( ) : ( - + ) : ( - + + + )} diff --git a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx index f80e706d30..d5c9f68547 100644 --- a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx +++ b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx @@ -2,6 +2,7 @@ import './ContextLogRenderer.styles.scss'; import { Skeleton } from 'antd'; import RawLogView from 'components/Logs/RawLogView'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import ShowButton from 'container/LogsContextList/ShowButton'; import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config'; import { useCallback, useEffect, useState } from 'react'; @@ -94,13 +95,15 @@ function ContextLogRenderer({ }} /> )} - + + + {isAfterLogsFetching && ( No Data )} {isFetching && } - - + + + {order === ORDERBY_FILTERS.DESC && ( diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx index fc5a1f6800..0727d7100f 100644 --- a/frontend/src/container/LogsExplorerList/index.tsx +++ b/frontend/src/container/LogsExplorerList/index.tsx @@ -6,6 +6,7 @@ import { VIEW_TYPES } from 'components/LogDetail/constants'; // components import ListLogView from 'components/Logs/ListLogView'; import RawLogView from 'components/Logs/RawLogView'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import Spinner from 'components/Spinner'; import { CARD_BODY_STYLE } from 'constants/card'; import { LOCALSTORAGE } from 'constants/localStorage'; @@ -133,15 +134,17 @@ function LogsExplorerList({ style={{ width: '100%', marginTop: '20px' }} bodyStyle={CARD_BODY_STYLE} > - + + + ); }, [ diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx index f1d6e3642c..fef68ee60f 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx @@ -3,6 +3,7 @@ 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 { SOMETHING_WENT_WRONG } from 'constants/api'; import { PANEL_TYPES } from 'constants/queryBuilder'; import Controls from 'container/Controls'; @@ -207,17 +208,19 @@ function LogsPanelComponent({ <>
- + +
+ {!widget.query.builder.queryData[0].limit && (
diff --git a/frontend/src/container/LogsTable/index.tsx b/frontend/src/container/LogsTable/index.tsx index b10c3503dd..6b20986d1f 100644 --- a/frontend/src/container/LogsTable/index.tsx +++ b/frontend/src/container/LogsTable/index.tsx @@ -7,6 +7,7 @@ import { VIEW_TYPES } from 'components/LogDetail/constants'; import ListLogView from 'components/Logs/ListLogView'; import RawLogView from 'components/Logs/RawLogView'; import LogsTableView from 'components/Logs/TableView'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import Spinner from 'components/Spinner'; import { CARD_BODY_STYLE } from 'constants/card'; import { useActiveLog } from 'hooks/logs/useActiveLog'; @@ -97,7 +98,9 @@ function LogsTable(props: LogsTableProps): JSX.Element { return ( - + + + ); }, [getItemContent, linesPerRow, logs, onSetActiveLog, selected, viewMode]); diff --git a/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx b/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx index bbf0b12b45..e06332e562 100644 --- a/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/SettingsDrawer.tsx @@ -2,6 +2,7 @@ import './Description.styles.scss'; import { Button } from 'antd'; import ConfigureIcon from 'assets/Integrations/ConfigureIcon'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { useRef, useState } from 'react'; import DashboardSettingsContent from '../DashboardSettings'; @@ -41,7 +42,9 @@ function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element { open={visible} rootClassName="settings-container-root" > - + + + ); diff --git a/frontend/src/container/NewDashboard/index.tsx b/frontend/src/container/NewDashboard/index.tsx index 2042d77e16..d5daf1c3cb 100644 --- a/frontend/src/container/NewDashboard/index.tsx +++ b/frontend/src/container/NewDashboard/index.tsx @@ -6,7 +6,7 @@ import GridGraphs from './GridGraphs'; function NewDashboard(): JSX.Element { const handle = useFullScreenHandle(); return ( -
+
diff --git a/frontend/src/container/NewWidget/index.tsx b/frontend/src/container/NewWidget/index.tsx index fc0c5d06cd..811855b26a 100644 --- a/frontend/src/container/NewWidget/index.tsx +++ b/frontend/src/container/NewWidget/index.tsx @@ -5,6 +5,7 @@ import { WarningOutlined } from '@ant-design/icons'; import { Button, Flex, Modal, Space, Tooltip, Typography } from 'antd'; import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn'; import { chartHelpMessage } from 'components/facingIssueBtn/util'; +import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; import { FeatureKeys } from 'constants/features'; import { QueryParams } from 'constants/query'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; @@ -586,60 +587,64 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { - {selectedWidget && ( - - )} + + {selectedWidget && ( + + )} + - + + +
-
+ +
+
>; + rootRef: RefObject; +} => { + const rootRef = useRef(null); + const [scroller, setScroller] = useState(null); + const [initialize, osInstance] = useOverlayScrollbars({ + defer: true, + options, + }); + + useEffect(() => { + const { current: root } = rootRef; + + if (scroller && root) { + initialize({ + target: root, + elements: { + viewport: scroller, + }, + }); + } + + return (): void => osInstance()?.destroy(); + }, [scroller, initialize, osInstance]); + + return { setScroller, rootRef }; +}; + +export default useInitializeOverlayScrollbar; diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index b5aca15ddf..7e50e4e38c 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -1,4 +1,5 @@ @import '@signozhq/design-tokens/dist/style.css'; +@import 'overlayscrollbars/overlayscrollbars.css'; @import './periscope.scss'; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 73bf7ae208..78c76e6be5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -13028,6 +13028,16 @@ outvariant@^1.2.1, outvariant@^1.4.0: resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.0.tgz#e742e4bda77692da3eca698ef5bfac62d9fba06e" integrity sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw== +overlayscrollbars-react@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/overlayscrollbars-react/-/overlayscrollbars-react-0.5.6.tgz#e9779f9fc2c1a3288570a45c83f8e42518bfb8c1" + integrity sha512-E5To04bL5brn9GVCZ36SnfGanxa2I2MDkWoa4Cjo5wol7l+diAgi4DBc983V7l2nOk/OLJ6Feg4kySspQEGDBw== + +overlayscrollbars@^2.8.1: + version "2.9.2" + resolved "https://registry.yarnpkg.com/overlayscrollbars/-/overlayscrollbars-2.9.2.tgz#056020a3811742b58b754fab6f775d49bd109be9" + integrity sha512-iDT84r39i7oWP72diZN2mbJUsn/taCq568aQaIrc84S87PunBT7qtsVltAF2esk7ORTRjQDnfjVYoqqTzgs8QA== + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -14678,7 +14688,7 @@ react-use@17.4.0, react-use@^17.3.2: react-virtuoso@4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.0.3.tgz#0dc8b10978095852d985b064157639b9fb9d9b1e" integrity sha512-tyqt8FBWxO+smve/kUgJbhCI2MEOvH2hHgFYPKWBMA2cJmV+cHIDDh1BL/6w4pg/dcCdlHCNVwi6aiztPxWttw== react@18.2.0: From df751c7f380ccbf7ee4f93e92bebbdf258eb4a49 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Tue, 16 Jul 2024 14:18:59 +0530 Subject: [PATCH 098/281] chore: add activation events (#5474) --- frontend/src/container/AllError/index.tsx | 24 ++++++- .../EmptyLogsSearch/EmptyLogsSearch.tsx | 28 +++++++- frontend/src/container/ErrorDetails/index.tsx | 24 ++++++- .../ExplorerOptions/ExplorerOptions.tsx | 67 ++++++++++++++++--- .../ExportPanel/ExportPanelContainer.tsx | 4 +- frontend/src/container/ExportPanel/index.tsx | 2 +- .../DashboardEmptyState.tsx | 7 ++ .../GridCardLayout/GridCardLayout.tsx | 17 ++++- .../GridCardLayout/WidgetHeader/index.tsx | 2 +- .../ListOfDashboard/DashboardsList.tsx | 32 ++++++++- .../ListOfDashboard/ImportJSON/index.tsx | 10 +++ .../src/container/LogsExplorerList/index.tsx | 4 +- .../src/container/LogsExplorerViews/index.tsx | 31 ++++++++- .../MetricsApplication/Tabs/DBCall.tsx | 21 +++++- .../MetricsApplication/Tabs/External.tsx | 20 +++++- .../MetricsApplication/Tabs/Overview.tsx | 20 +++++- .../NewDashboard/ComponentsSlider/index.tsx | 9 ++- .../DashboardDescription/index.tsx | 7 ++ .../LeftContainer/QuerySection/index.tsx | 15 ++++- .../NewWidget/RightContainer/index.tsx | 2 +- frontend/src/container/NewWidget/index.tsx | 30 ++++++++- frontend/src/container/NoLogs/NoLogs.tsx | 6 ++ .../PipelineListsView/PipelineListsView.tsx | 20 +++++- .../ServiceTraces/index.tsx | 24 ++++++- frontend/src/container/SideNav/SideNav.tsx | 26 +++++++ .../TimeSeriesView/TimeSeriesView.tsx | 4 +- .../TracesExplorer/ListView/index.tsx | 4 +- .../TracesExplorer/TracesView/index.tsx | 4 +- .../hooks/queryBuilder/useCreateAlerts.tsx | 23 ++++++- frontend/src/pages/SaveView/index.tsx | 19 +++++- .../pages/TracesExplorer/Filter/Filter.tsx | 6 ++ frontend/src/pages/TracesExplorer/index.tsx | 18 ++++- 32 files changed, 493 insertions(+), 37 deletions(-) diff --git a/frontend/src/container/AllError/index.tsx b/frontend/src/container/AllError/index.tsx index e8c13d88cd..d571c3dba7 100644 --- a/frontend/src/container/AllError/index.tsx +++ b/frontend/src/container/AllError/index.tsx @@ -12,6 +12,7 @@ import { ColumnType, TablePaginationConfig } from 'antd/es/table'; import { FilterValue, SorterResult } from 'antd/es/table/interface'; import { ColumnsType } from 'antd/lib/table'; import { FilterConfirmProps } from 'antd/lib/table/interface'; +import logEvent from 'api/common/logEvent'; import getAll from 'api/errors/getAll'; import getErrorCounts from 'api/errors/getErrorCounts'; import { ResizeTable } from 'components/ResizeTable'; @@ -23,7 +24,8 @@ import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute import useUrlQuery from 'hooks/useUrlQuery'; import createQueryParams from 'lib/createQueryParams'; import history from 'lib/history'; -import { useCallback, useEffect, useMemo } from 'react'; +import { isUndefined } from 'lodash-es'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useQueries } from 'react-query'; import { useSelector } from 'react-redux'; @@ -410,6 +412,26 @@ function AllErrors(): JSX.Element { [pathname], ); + const logEventCalledRef = useRef(false); + useEffect(() => { + if ( + !logEventCalledRef.current && + !isUndefined(errorCountResponse.data?.payload) + ) { + const selectedEnvironments = queries.find( + (val) => val.tagKey === 'resource_deployment_environment', + )?.tagValue; + + logEvent('Exception: List page visited', { + numberOfExceptions: errorCountResponse.data?.payload, + selectedEnvironments, + resourceAttributeUsed: !!queries.length, + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [errorCountResponse.data?.payload]); + return ( { + if (!logEventCalledRef.current) { + if (dataSource === DataSource.TRACES) { + logEvent('Traces Explorer: No results', { + panelType, + }); + } else if (dataSource === DataSource.LOGS) { + logEvent('Logs Explorer: No results', { + panelType, + }); + } + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); -export default function EmptyLogsSearch(): JSX.Element { return (
diff --git a/frontend/src/container/ErrorDetails/index.tsx b/frontend/src/container/ErrorDetails/index.tsx index 33a86f9f50..2c87279e3d 100644 --- a/frontend/src/container/ErrorDetails/index.tsx +++ b/frontend/src/container/ErrorDetails/index.tsx @@ -1,6 +1,7 @@ import './styles.scss'; import { Button, Divider, Space, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import getNextPrevId from 'api/errors/getNextPrevId'; import Editor from 'components/Editor'; import { ResizeTable } from 'components/ResizeTable'; @@ -9,8 +10,9 @@ import dayjs from 'dayjs'; import { useNotifications } from 'hooks/useNotifications'; import createQueryParams from 'lib/createQueryParams'; import history from 'lib/history'; +import { isUndefined } from 'lodash-es'; import { urlKey } from 'pages/ErrorDetails/utils'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { useLocation } from 'react-router-dom'; @@ -111,9 +113,29 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element { })); const onClickTraceHandler = (): void => { + logEvent('Exception: Navigate to trace detail page', { + groupId: errorDetail.groupID, + spanId: errorDetail.spanID, + traceId: errorDetail.traceID, + exceptionId: errorDetail.errorId, + }); history.push(`/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`); }; + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current && !isUndefined(data)) { + logEvent('Exception: Detail page visited', { + groupId: errorDetail.groupID, + spanId: errorDetail.spanID, + traceId: errorDetail.traceID, + exceptionId: errorDetail.errorId, + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data]); + return ( <> {errorDetail.exceptionType} diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx index 1aaf22f796..e925a60a8a 100644 --- a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx @@ -14,6 +14,7 @@ import { Tooltip, Typography, } from 'antd'; +import logEvent from 'api/common/logEvent'; import axios from 'axios'; import cx from 'classnames'; import { getViewDetailsUsingViewKey } from 'components/ExplorerCard/utils'; @@ -93,7 +94,23 @@ function ExplorerOptions({ setIsExport(value); }, []); + const { + currentQuery, + panelType, + isStagedQueryUpdated, + redirectWithQueryBuilderData, + } = useQueryBuilder(); + const handleSaveViewModalToggle = (): void => { + if (sourcepage === DataSource.TRACES) { + logEvent('Traces Explorer: Save view clicked', { + panelType, + }); + } else if (sourcepage === DataSource.LOGS) { + logEvent('Logs Explorer: Save view clicked', { + panelType, + }); + } setIsSaveModalOpen(!isSaveModalOpen); }; @@ -104,11 +121,21 @@ function ExplorerOptions({ const { role } = useSelector((state) => state.app); const onCreateAlertsHandler = useCallback(() => { + if (sourcepage === DataSource.TRACES) { + logEvent('Traces Explorer: Create alert', { + panelType, + }); + } else if (sourcepage === DataSource.LOGS) { + logEvent('Logs Explorer: Create alert', { + panelType, + }); + } history.push( `${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent( JSON.stringify(query), )}`, ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [history, query]); const onCancel = (value: boolean) => (): void => { @@ -116,6 +143,15 @@ function ExplorerOptions({ }; const onAddToDashboard = (): void => { + if (sourcepage === DataSource.TRACES) { + logEvent('Traces Explorer: Add to dashboard clicked', { + panelType, + }); + } else if (sourcepage === DataSource.LOGS) { + logEvent('Logs Explorer: Add to dashboard clicked', { + panelType, + }); + } setIsExport(true); }; @@ -127,13 +163,6 @@ function ExplorerOptions({ refetch: refetchAllView, } = useGetAllViews(sourcepage); - const { - currentQuery, - panelType, - isStagedQueryUpdated, - redirectWithQueryBuilderData, - } = useQueryBuilder(); - const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType); const viewName = useGetSearchQueryParam(QueryParams.viewName) || ''; @@ -224,6 +253,17 @@ function ExplorerOptions({ onMenuItemSelectHandler({ key: option.key, }); + if (sourcepage === DataSource.TRACES) { + logEvent('Traces Explorer: Select view', { + panelType, + viewName: option.value, + }); + } else if (sourcepage === DataSource.LOGS) { + logEvent('Logs Explorer: Select view', { + panelType, + viewName: option.value, + }); + } if (ref.current) { ref.current.blur(); } @@ -259,6 +299,17 @@ function ExplorerOptions({ viewName: newViewName, setNewViewName, }); + if (sourcepage === DataSource.TRACES) { + logEvent('Traces Explorer: Save view successful', { + panelType, + viewName: newViewName, + }); + } else if (sourcepage === DataSource.LOGS) { + logEvent('Logs Explorer: Save view successful', { + panelType, + viewName: newViewName, + }); + } }; // TODO: Remove this and move this to scss file @@ -499,7 +550,7 @@ function ExplorerOptions({ export interface ExplorerOptionsProps { isLoading?: boolean; - onExport: (dashboard: Dashboard | null) => void; + onExport: (dashboard: Dashboard | null, isNewDashboard?: boolean) => void; query: Query | null; disabled: boolean; sourcepage: DataSource; diff --git a/frontend/src/container/ExportPanel/ExportPanelContainer.tsx b/frontend/src/container/ExportPanel/ExportPanelContainer.tsx index 9e4a658273..a0927e3692 100644 --- a/frontend/src/container/ExportPanel/ExportPanelContainer.tsx +++ b/frontend/src/container/ExportPanel/ExportPanelContainer.tsx @@ -41,7 +41,7 @@ function ExportPanelContainer({ } = useMutation(createDashboard, { onSuccess: (data) => { if (data.payload) { - onExport(data?.payload); + onExport(data?.payload, true); } refetch(); }, @@ -55,7 +55,7 @@ function ExportPanelContainer({ ({ uuid }) => uuid === selectedDashboardId, ); - onExport(currentSelectedDashboard || null); + onExport(currentSelectedDashboard || null, false); }, [data, selectedDashboardId, onExport]); const handleSelect = useCallback( diff --git a/frontend/src/container/ExportPanel/index.tsx b/frontend/src/container/ExportPanel/index.tsx index f302d83212..86d5f44139 100644 --- a/frontend/src/container/ExportPanel/index.tsx +++ b/frontend/src/container/ExportPanel/index.tsx @@ -40,7 +40,7 @@ function ExportPanel({ export interface ExportPanelProps { isLoading?: boolean; - onExport: (dashboard: Dashboard | null) => void; + onExport: (dashboard: Dashboard | null, isNewDashboard?: boolean) => void; query: Query | null; } diff --git a/frontend/src/container/GridCardLayout/DashboardEmptyState/DashboardEmptyState.tsx b/frontend/src/container/GridCardLayout/DashboardEmptyState/DashboardEmptyState.tsx index 0bfbd5ac28..99e3194d5a 100644 --- a/frontend/src/container/GridCardLayout/DashboardEmptyState/DashboardEmptyState.tsx +++ b/frontend/src/container/GridCardLayout/DashboardEmptyState/DashboardEmptyState.tsx @@ -3,6 +3,7 @@ import './DashboardEmptyState.styles.scss'; import { PlusOutlined } from '@ant-design/icons'; import { Button, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import SettingsDrawer from 'container/NewDashboard/DashboardDescription/SettingsDrawer'; import useComponentPermission from 'hooks/useComponentPermission'; import { useDashboard } from 'providers/Dashboard/Dashboard'; @@ -36,6 +37,12 @@ export default function DashboardEmptyState(): JSX.Element { const onEmptyWidgetHandler = useCallback(() => { handleToggleDashboardSlider(true); + logEvent('Dashboard Detail: Add new panel clicked', { + dashboardId: selectedDashboard?.uuid, + dashboardName: selectedDashboard?.data.title, + numberOfPanels: selectedDashboard?.data.widgets?.length, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [handleToggleDashboardSlider]); return (
diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx index f0d178866c..75fde3ce7a 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx +++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx @@ -3,6 +3,7 @@ import './GridCardLayout.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Form, Input, Modal, Typography } from 'antd'; import { useForm } from 'antd/es/form/Form'; +import logEvent from 'api/common/logEvent'; import cx from 'classnames'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { QueryParams } from 'constants/query'; @@ -15,7 +16,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode'; import { useNotifications } from 'hooks/useNotifications'; import useUrlQuery from 'hooks/useUrlQuery'; import history from 'lib/history'; -import { defaultTo } from 'lodash-es'; +import { defaultTo, isUndefined } from 'lodash-es'; import isEqual from 'lodash-es/isEqual'; import { Check, @@ -27,7 +28,7 @@ import { } from 'lucide-react'; import { useDashboard } from 'providers/Dashboard/Dashboard'; import { sortLayout } from 'providers/Dashboard/util'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { FullScreen, FullScreenHandle } from 'react-full-screen'; import { ItemCallback, Layout } from 'react-grid-layout'; import { useDispatch, useSelector } from 'react-redux'; @@ -126,6 +127,18 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element { setDashboardLayout(sortLayout(layouts)); }, [layouts]); + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current && !isUndefined(data)) { + logEvent('Dashboard Detail: Opened', { + dashboardId: data.uuid, + dashboardName: data.title, + numberOfPanels: data.widgets?.length, + numberOfVariables: Object.keys(data?.variables || {}).length || 0, + }); + logEventCalledRef.current = true; + } + }, [data]); const onSaveHandler = (): void => { if (!selectedDashboard) return; diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx index 1401576ca9..1954b1c458 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx +++ b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx @@ -79,7 +79,7 @@ function WidgetHeader({ ); }, [widget.id, widget.panelTypes, widget.query]); - const onCreateAlertsHandler = useCreateAlerts(widget); + const onCreateAlertsHandler = useCreateAlerts(widget, 'dashboardView'); const onDownloadHandler = useCallback((): void => { const csv = unparse(tableProcessedDataRef.current); diff --git a/frontend/src/container/ListOfDashboard/DashboardsList.tsx b/frontend/src/container/ListOfDashboard/DashboardsList.tsx index 39326a558a..f3dc600f9b 100644 --- a/frontend/src/container/ListOfDashboard/DashboardsList.tsx +++ b/frontend/src/container/ListOfDashboard/DashboardsList.tsx @@ -21,6 +21,7 @@ import { Typography, } from 'antd'; import { TableProps } from 'antd/lib'; +import logEvent from 'api/common/logEvent'; import createDashboard from 'api/dashboard/create'; import { AxiosError } from 'axios'; import cx from 'classnames'; @@ -34,7 +35,7 @@ import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard'; import useComponentPermission from 'hooks/useComponentPermission'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; -import { get, isEmpty } from 'lodash-es'; +import { get, isEmpty, isUndefined } from 'lodash-es'; import { ArrowDownWideNarrow, ArrowUpRight, @@ -60,6 +61,7 @@ import { useCallback, useEffect, useMemo, + useRef, useState, } from 'react'; import { useTranslation } from 'react-i18next'; @@ -269,6 +271,7 @@ function DashboardsList(): JSX.Element { const onNewDashboardHandler = useCallback(async () => { try { + logEvent('Dashboard List: Create dashboard clicked', {}); setNewDashboardState({ ...newDashboardState, loading: true, @@ -305,6 +308,8 @@ function DashboardsList(): JSX.Element { }, [newDashboardState, t]); const onModalHandler = (uploadedGrafana: boolean): void => { + logEvent('Dashboard List: Import JSON clicked', {}); + setIsImportJSONModalVisible((state) => !state); setUploadedGrafana(uploadedGrafana); }; @@ -441,6 +446,10 @@ function DashboardsList(): JSX.Element { } else { history.push(getLink()); } + logEvent('Dashboard List: Clicked on dashboard', { + dashboardId: dashboard.id, + dashboardName: dashboard.name, + }); }; return ( @@ -619,6 +628,21 @@ function DashboardsList(): JSX.Element { hideOnSinglePage: true, }; + const logEventCalledRef = useRef(false); + useEffect(() => { + if ( + !logEventCalledRef.current && + !isDashboardListLoading && + !isUndefined(dashboardListResponse) + ) { + logEvent('Dashboard List: Page visited', { + number: dashboardListResponse?.length, + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isDashboardListLoading]); + return (
@@ -705,6 +729,9 @@ function DashboardsList(): JSX.Element { type="text" className="new-dashboard" icon={} + onClick={(): void => { + logEvent('Dashboard List: New dashboard clicked', {}); + }} > New Dashboard @@ -745,6 +772,9 @@ function DashboardsList(): JSX.Element { type="primary" className="periscope-btn primary btn" icon={} + onClick={(): void => { + logEvent('Dashboard List: New dashboard clicked', {}); + }} > New dashboard diff --git a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx index 5b95eb4a9a..9bf1db2051 100644 --- a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx +++ b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx @@ -5,6 +5,7 @@ import { ExclamationCircleTwoTone } from '@ant-design/icons'; import MEditor, { Monaco } from '@monaco-editor/react'; import { Color } from '@signozhq/design-tokens'; import { Button, Modal, Space, Typography, Upload, UploadProps } from 'antd'; +import logEvent from 'api/common/logEvent'; import createDashboard from 'api/dashboard/create'; import ROUTES from 'constants/routes'; import { useIsDarkMode } from 'hooks/useDarkMode'; @@ -67,6 +68,8 @@ function ImportJSON({ const onClickLoadJsonHandler = async (): Promise => { try { setDashboardCreating(true); + logEvent('Dashboard List: Import and next clicked', {}); + const dashboardData = JSON.parse(editorValue) as DashboardData; if (dashboardData?.layout) { @@ -86,6 +89,10 @@ function ImportJSON({ dashboardId: response.payload.uuid, }), ); + logEvent('Dashboard List: New dashboard imported successfully', { + dashboardId: response.payload?.uuid, + dashboardName: response.payload?.data?.title, + }); } else if (response.error === 'feature usage exceeded') { setIsFeatureAlert(true); notifications.error({ @@ -180,6 +187,9 @@ function ImportJSON({ type="default" className="periscope-btn" icon={} + onClick={(): void => { + logEvent('Dashboard List: Upload JSON file clicked', {}); + }} > {' '} {t('upload_json_file')} diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx index 0727d7100f..760f3fea30 100644 --- a/frontend/src/container/LogsExplorerList/index.tsx +++ b/frontend/src/container/LogsExplorerList/index.tsx @@ -172,7 +172,9 @@ function LogsExplorerList({ !isFetching && logs.length === 0 && !isError && - isFilterApplied && } + isFilterApplied && ( + + )} {isError && !isLoading && !isFetching && } diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx index b95fd6e6c4..6318e1da71 100644 --- a/frontend/src/container/LogsExplorerViews/index.tsx +++ b/frontend/src/container/LogsExplorerViews/index.tsx @@ -2,6 +2,7 @@ import './LogsExplorerViews.styles.scss'; import { Button } from 'antd'; +import logEvent from 'api/common/logEvent'; import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu'; import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { LOCALSTORAGE } from 'constants/localStorage'; @@ -37,7 +38,14 @@ import { useNotifications } from 'hooks/useNotifications'; import useUrlQueryData from 'hooks/useUrlQueryData'; import { FlatLogData } from 'lib/logs/flatLogData'; import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData'; -import { cloneDeep, defaultTo, isEmpty, omit, set } from 'lodash-es'; +import { + cloneDeep, + defaultTo, + isEmpty, + isUndefined, + omit, + set, +} from 'lodash-es'; import { Sliders } from 'lucide-react'; import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -310,6 +318,19 @@ function LogsExplorerViews({ ], ); + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current && !isUndefined(data?.payload)) { + const currentData = data?.payload?.data?.newResult?.data?.result || []; + logEvent('Logs Explorer: Page visited', { + panelType, + isEmpty: !currentData?.[0]?.list, + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data?.payload]); + const { mutate: updateDashboard, isLoading: isUpdateDashboardLoading, @@ -324,7 +345,7 @@ function LogsExplorerViews({ }, [currentQuery]); const handleExport = useCallback( - (dashboard: Dashboard | null): void => { + (dashboard: Dashboard | null, isNewDashboard?: boolean): void => { if (!dashboard || !panelType) return; const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType) @@ -346,6 +367,12 @@ function LogsExplorerViews({ options.selectColumns, ); + logEvent('Logs Explorer: Add to dashboard successful', { + panelType, + isNewDashboard, + dashboardName: dashboard?.data?.title, + }); + updateDashboard(updatedDashboard, { onSuccess: (data) => { if (data.error) { diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index d45476b6f4..a520d98936 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -1,4 +1,5 @@ import { Col } from 'antd'; +import logEvent from 'api/common/logEvent'; import { ENTITY_VERSION_V4 } from 'constants/app'; import { PANEL_TYPES } from 'constants/queryBuilder'; import Graph from 'container/GridCardLayout/GridCard'; @@ -11,7 +12,7 @@ import { convertRawQueriesToTraceSelectedTags, resourceAttributesToTagFilterItems, } from 'hooks/useResourceAttribute/utils'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; @@ -97,6 +98,24 @@ function DBCall(): JSX.Element { [servicename, tagFilterItems], ); + const logEventCalledRef = useRef(false); + + useEffect(() => { + if (!logEventCalledRef.current) { + const selectedEnvironments = queries.find( + (val) => val.tagKey === 'resource_deployment_environment', + )?.tagValue; + + logEvent('APM: Service detail page visited', { + selectedEnvironments, + resourceAttributeUsed: !!queries.length, + section: 'dbMetrics', + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const apmToTraceQuery = useGetAPMToTracesQueries({ servicename, isDBCall: true, diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index 564f0a657e..d224135175 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -1,4 +1,5 @@ import { Col } from 'antd'; +import logEvent from 'api/common/logEvent'; import { ENTITY_VERSION_V4 } from 'constants/app'; import { PANEL_TYPES } from 'constants/queryBuilder'; import Graph from 'container/GridCardLayout/GridCard'; @@ -13,7 +14,7 @@ import { convertRawQueriesToTraceSelectedTags, resourceAttributesToTagFilterItems, } from 'hooks/useResourceAttribute/utils'; -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { EQueryType } from 'types/common/dashboard'; @@ -114,6 +115,23 @@ function External(): JSX.Element { ], }); + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current) { + const selectedEnvironments = queries.find( + (val) => val.tagKey === 'resource_deployment_environment', + )?.tagValue; + + logEvent('APM: Service detail page visited', { + selectedEnvironments, + resourceAttributeUsed: !!queries.length, + section: 'externalMetrics', + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const externalCallRPSWidget = useMemo( () => getWidgetQueryBuilder({ diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 7410e4ccb3..96cc821fb8 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -1,3 +1,4 @@ +import logEvent from 'api/common/logEvent'; import getTopLevelOperations, { ServiceDataProps, } from 'api/metrics/getTopLevelOperations'; @@ -17,7 +18,7 @@ import useUrlQuery from 'hooks/useUrlQuery'; import history from 'lib/history'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { defaultTo } from 'lodash-es'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useQuery } from 'react-query'; import { useDispatch } from 'react-redux'; import { useLocation, useParams } from 'react-router-dom'; @@ -81,6 +82,23 @@ function Application(): JSX.Element { [handleSetTimeStamp], ); + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current) { + const selectedEnvironments = queries.find( + (val) => val.tagKey === 'resource_deployment_environment', + )?.tagValue; + + logEvent('APM: Service detail page visited', { + selectedEnvironments, + resourceAttributeUsed: !!queries.length, + section: 'overview', + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const { data: topLevelOperations, error: topLevelOperationsError, diff --git a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx index 2859b87305..18cecc9184 100644 --- a/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx +++ b/frontend/src/container/NewDashboard/ComponentsSlider/index.tsx @@ -1,6 +1,7 @@ import './ComponentSlider.styles.scss'; import { Card, Modal } from 'antd'; +import logEvent from 'api/common/logEvent'; import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; import createQueryParams from 'lib/createQueryParams'; @@ -20,6 +21,13 @@ function DashboardGraphSlider(): JSX.Element { const onClickHandler = (name: PANEL_TYPES) => (): void => { const id = uuid(); handleToggleDashboardSlider(false); + logEvent('Dashboard Detail: New panel type selected', { + // dashboardId: '', + // dashboardName: '', + // numberOfPanels: 0, // todo - at this point we don't know these attributes + panelType: name, + widgetId: id, + }); const queryParamsLog = { graphType: name, widgetId: id, @@ -47,7 +55,6 @@ function DashboardGraphSlider(): JSX.Element { PANEL_TYPES_INITIAL_QUERY[name], ), }; - if (name === PANEL_TYPES.LIST) { history.push( `${history.location.pathname}/new?${createQueryParams(queryParamsLog)}`, diff --git a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx index f1288493bd..1c851176c1 100644 --- a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx @@ -2,6 +2,7 @@ import './Description.styles.scss'; import { PlusOutlined } from '@ant-design/icons'; import { Button, Card, Input, Modal, Popover, Tag, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn'; import { dashboardHelpMessage } from 'components/facingIssueBtn/util'; import { SOMETHING_WENT_WRONG } from 'constants/api'; @@ -126,6 +127,12 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element { const onEmptyWidgetHandler = useCallback(() => { handleToggleDashboardSlider(true); + logEvent('Dashboard Detail: Add new panel clicked', { + dashboardId: selectedDashboard?.uuid, + dashboardName: selectedDashboard?.data.title, + numberOfPanels: selectedDashboard?.data.widgets?.length, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [handleToggleDashboardSlider]); const handleLockDashboardToggle = (): void => { diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx index 3f6deab6f8..2d682b1f06 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx @@ -2,6 +2,7 @@ import './QuerySection.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Tabs, Tooltip, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import PromQLIcon from 'assets/Dashboard/PromQl'; import TextToolTip from 'components/TextToolTip'; import { PANEL_TYPES } from 'constants/queryBuilder'; @@ -14,7 +15,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useIsDarkMode } from 'hooks/useDarkMode'; import useUrlQuery from 'hooks/useUrlQuery'; -import { defaultTo } from 'lodash-es'; +import { defaultTo, isUndefined } from 'lodash-es'; import { Atom, Play, Terminal } from 'lucide-react'; import { useDashboard } from 'providers/Dashboard/Dashboard'; import { @@ -122,6 +123,18 @@ function QuerySection({ }; const handleRunQuery = (): void => { + const widgetId = urlQuery.get('widgetId'); + const isNewPanel = isUndefined(widgets?.find((e) => e.id === widgetId)); + + logEvent('Panel Edit: Stage and run query', { + dataSource: currentQuery.builder?.queryData?.[0]?.dataSource, + panelType: selectedWidget.panelTypes, + queryType: currentQuery.queryType, + widgetId: selectedWidget.id, + dashboardId: selectedDashboard?.uuid, + dashboardName: selectedDashboard?.data.title, + isNewPanel, + }); handleStageQuery(currentQuery); }; diff --git a/frontend/src/container/NewWidget/RightContainer/index.tsx b/frontend/src/container/NewWidget/RightContainer/index.tsx index 08387cd069..3cc27a5f16 100644 --- a/frontend/src/container/NewWidget/RightContainer/index.tsx +++ b/frontend/src/container/NewWidget/RightContainer/index.tsx @@ -82,7 +82,7 @@ function RightContainer({ const selectedGraphType = GraphTypes.find((e) => e.name === selectedGraph)?.display || ''; - const onCreateAlertsHandler = useCreateAlerts(selectedWidget); + const onCreateAlertsHandler = useCreateAlerts(selectedWidget, 'panelView'); const allowThreshold = panelTypeVsThreshold[selectedGraph]; const allowSoftMinMax = panelTypeVsSoftMinMax[selectedGraph]; diff --git a/frontend/src/container/NewWidget/index.tsx b/frontend/src/container/NewWidget/index.tsx index 811855b26a..c042269280 100644 --- a/frontend/src/container/NewWidget/index.tsx +++ b/frontend/src/container/NewWidget/index.tsx @@ -3,6 +3,7 @@ import './NewWidget.styles.scss'; import { WarningOutlined } from '@ant-design/icons'; import { Button, Flex, Modal, Space, Tooltip, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn'; import { chartHelpMessage } from 'components/facingIssueBtn/util'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; @@ -31,7 +32,7 @@ import { getPreviousWidgets, getSelectedWidgetIndex, } from 'providers/Dashboard/util'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { generatePath, useParams } from 'react-router-dom'; @@ -101,6 +102,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { const [isNewDashboard, setIsNewDashboard] = useState(false); + const logEventCalledRef = useRef(false); + useEffect(() => { const widgetId = query.get('widgetId'); const selectedWidget = widgets?.find((e) => e.id === widgetId); @@ -108,6 +111,18 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { if (isWidgetNotPresent) { setIsNewDashboard(true); } + + if (!logEventCalledRef.current) { + logEvent('Panel Edit: Page visited', { + panelType: selectedWidget?.panelTypes, + dashboardId: selectedDashboard?.uuid, + widgetId: selectedWidget?.id, + dashboardName: selectedDashboard?.data.title, + isNewPanel: !!isWidgetNotPresent, + dataSource: currentQuery.builder.queryData?.[0]?.dataSource, + }); + logEventCalledRef.current = true; + } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -482,7 +497,20 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { }; const onSaveDashboard = useCallback((): void => { + const widgetId = query.get('widgetId'); + const selectWidget = widgets?.find((e) => e.id === widgetId); + + logEvent('Panel Edit: Save changes', { + panelType: selectedWidget.panelTypes, + dashboardId: selectedDashboard?.uuid, + widgetId: selectedWidget.id, + dashboardName: selectedDashboard?.data.title, + queryType: currentQuery.queryType, + isNewPanel: isUndefined(selectWidget), + dataSource: currentQuery.builder.queryData?.[0]?.dataSource, + }); setSaveModal(true); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const isQueryBuilderActive = useIsFeatureDisabled( diff --git a/frontend/src/container/NoLogs/NoLogs.tsx b/frontend/src/container/NoLogs/NoLogs.tsx index 71e0d213e8..c4832e8a0e 100644 --- a/frontend/src/container/NoLogs/NoLogs.tsx +++ b/frontend/src/container/NoLogs/NoLogs.tsx @@ -1,6 +1,7 @@ import './NoLogs.styles.scss'; import { Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import ROUTES from 'constants/routes'; import history from 'lib/history'; import { ArrowUpRight } from 'lucide-react'; @@ -21,6 +22,11 @@ export default function NoLogs({ e.stopPropagation(); if (cloudUser) { + if (dataSource === DataSource.TRACES) { + logEvent('Traces Explorer: Navigate to onboarding', {}); + } else if (dataSource === DataSource.LOGS) { + logEvent('Logs Explorer: Navigate to onboarding', {}); + } history.push( dataSource === 'traces' ? ROUTES.GET_STARTED_APPLICATION_MONITORING diff --git a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx index 6f846e21cb..4e8def2e0c 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx @@ -3,11 +3,19 @@ import './styles.scss'; import { ExclamationCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { Card, Modal, Table, Typography } from 'antd'; import { ExpandableConfig } from 'antd/es/table/interface'; +import logEvent from 'api/common/logEvent'; import savePipeline from 'api/pipeline/post'; import useAnalytics from 'hooks/analytics/useAnalytics'; import { useNotifications } from 'hooks/useNotifications'; +import { isUndefined } from 'lodash-es'; import cloneDeep from 'lodash-es/cloneDeep'; -import React, { useCallback, useMemo, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { useTranslation } from 'react-i18next'; @@ -466,6 +474,16 @@ function PipelineListsView({ getExpandIcon(expanded, onExpand, record), }; + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current && !isUndefined(currPipelineData)) { + logEvent('Logs Pipelines: List page visited', { + number: currPipelineData?.length, + }); + logEventCalledRef.current = true; + } + }, [currPipelineData]); + return ( <> {contextHolder} diff --git a/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx b/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx index 370697af00..8d6238c68a 100644 --- a/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx +++ b/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx @@ -1,11 +1,13 @@ import localStorageGet from 'api/browser/localstorage/get'; import localStorageSet from 'api/browser/localstorage/set'; +import logEvent from 'api/common/logEvent'; import { SKIP_ONBOARDING } from 'constants/onboarding'; import useErrorNotification from 'hooks/useErrorNotification'; import { useQueryService } from 'hooks/useQueryService'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; -import { useMemo, useState } from 'react'; +import { isUndefined } from 'lodash-es'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { GlobalReducer } from 'types/reducer/globalTime'; @@ -45,6 +47,26 @@ function ServiceTraces(): JSX.Element { setSkipOnboarding(true); }; + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current && !isUndefined(data)) { + const selectedEnvironments = queries.find( + (val) => val.tagKey === 'resource_deployment_environment', + )?.tagValue; + + const rps = data.reduce((total, service) => total + service.callRate, 0); + + logEvent('APM: List page visited', { + numberOfServices: data?.length, + selectedEnvironments, + resourceAttributeUsed: !!queries.length, + rps, + }); + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data]); + if ( services.length === 0 && isLoading === false && diff --git a/frontend/src/container/SideNav/SideNav.tsx b/frontend/src/container/SideNav/SideNav.tsx index 82697d78b0..b5eb240af8 100644 --- a/frontend/src/container/SideNav/SideNav.tsx +++ b/frontend/src/container/SideNav/SideNav.tsx @@ -4,6 +4,7 @@ import './SideNav.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Tooltip } from 'antd'; +import logEvent from 'api/common/logEvent'; import cx from 'classnames'; import { FeatureKeys } from 'constants/features'; import ROUTES from 'constants/routes'; @@ -179,6 +180,11 @@ function SideNav({ }; const onClickShortcuts = (e: MouseEvent): void => { + // eslint-disable-next-line sonarjs/no-duplicate-string + logEvent('Sidebar: Menu clicked', { + menuRoute: '/shortcuts', + menuLabel: 'Keyboard Shortcuts', + }); if (isCtrlMetaKey(e)) { openInNewTab('/shortcuts'); } else { @@ -187,6 +193,10 @@ function SideNav({ }; const onClickGetStarted = (event: MouseEvent): void => { + logEvent('Sidebar: Menu clicked', { + menuRoute: '/get-started', + menuLabel: 'Get Started', + }); if (isCtrlMetaKey(event)) { openInNewTab('/get-started'); } else { @@ -313,6 +323,10 @@ function SideNav({ } else if (item) { onClickHandler(item?.key as string, event); } + logEvent('Sidebar: Menu clicked', { + menuRoute: item.key, + menuLabel: item.label, + }); }; useEffect(() => { @@ -440,6 +454,10 @@ function SideNav({ isActive={activeMenuKey === item?.key} onClick={(event: MouseEvent): void => { handleUserManagentMenuItemClick(item?.key as string, event); + logEvent('Sidebar: Menu clicked', { + menuRoute: item.key, + menuLabel: item.label, + }); }} /> ), @@ -456,6 +474,10 @@ function SideNav({ } else { history.push(`${inviteMemberMenuItem.key}`); } + logEvent('Sidebar: Menu clicked', { + menuRoute: inviteMemberMenuItem.key, + menuLabel: inviteMemberMenuItem.label, + }); }} /> )} @@ -470,6 +492,10 @@ function SideNav({ userSettingsMenuItem?.key as string, event, ); + logEvent('Sidebar: Menu clicked', { + menuRoute: userSettingsMenuItem.key, + menuLabel: 'User', + }); }} /> )} diff --git a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx index 4abd67de21..49059eddf5 100644 --- a/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx +++ b/frontend/src/container/TimeSeriesView/TimeSeriesView.tsx @@ -155,7 +155,9 @@ function TimeSeriesView({ chartData[0]?.length === 0 && !isLoading && !isError && - isFilterApplied && } + isFilterApplied && ( + + )} {chartData && chartData[0] && diff --git a/frontend/src/container/TracesExplorer/ListView/index.tsx b/frontend/src/container/TracesExplorer/ListView/index.tsx index 2bd7ac7e72..810ffb8241 100644 --- a/frontend/src/container/TracesExplorer/ListView/index.tsx +++ b/frontend/src/container/TracesExplorer/ListView/index.tsx @@ -156,7 +156,9 @@ function ListView({ isFilterApplied }: ListViewProps): JSX.Element { )} - {isDataPresent && isFilterApplied && } + {isDataPresent && isFilterApplied && ( + + )} {!isError && transformedQueryTableData.length !== 0 && ( } + isFilterApplied && ( + + )} {(tableData || []).length !== 0 && ( { +const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => { const queryRangeMutation = useMutation(getQueryRangeFormat); const { selectedTime: globalSelectedInterval } = useSelector< @@ -32,6 +34,24 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => { return useCallback(() => { if (!widget) return; + if (caller === 'panelView') { + logEvent('Panel Edit: Create alert', { + panelType: widget.panelTypes, + dashboardName: selectedDashboard?.data?.title, + dashboardId: selectedDashboard?.uuid, + widgetId: widget.id, + queryType: widget.query.queryType, + }); + } else if (caller === 'dashboardView') { + logEvent('Dashboard Detail: Panel action', { + action: MenuItemKeys.CreateAlerts, + panelType: widget.panelTypes, + dashboardName: selectedDashboard?.data?.title, + dashboardId: selectedDashboard?.uuid, + widgetId: widget.id, + queryType: widget.query.queryType, + }); + } const { queryPayload } = prepareQueryRangePayload({ query: widget.query, globalSelectedInterval, @@ -57,6 +77,7 @@ const useCreateAlerts = (widget?: Widgets): VoidFunction => { }); }, }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ globalSelectedInterval, notifications, diff --git a/frontend/src/pages/SaveView/index.tsx b/frontend/src/pages/SaveView/index.tsx index 02a2578f0a..efcd3f2a4b 100644 --- a/frontend/src/pages/SaveView/index.tsx +++ b/frontend/src/pages/SaveView/index.tsx @@ -10,6 +10,7 @@ import { TableProps, Typography, } from 'antd'; +import logEvent from 'api/common/logEvent'; import { getViewDetailsUsingViewKey, showErrorNotification, @@ -30,7 +31,7 @@ import { Trash2, X, } from 'lucide-react'; -import { ChangeEvent, useEffect, useState } from 'react'; +import { ChangeEvent, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; @@ -143,6 +144,22 @@ function SaveView(): JSX.Element { viewName: newViewName, }); + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current && !isLoading) { + if (sourcepage === DataSource.TRACES) { + logEvent('Traces Views: Views visited', { + number: viewsData?.data.data.length, + }); + } else if (sourcepage === DataSource.LOGS) { + logEvent('Logs Views: Views visited', { + number: viewsData?.data.data.length, + }); + } + logEventCalledRef.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [viewsData?.data.data, isLoading]); const onUpdateQueryHandler = (): void => { updateViewAsync( { diff --git a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx index 1a3fbc785c..3d3895e047 100644 --- a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx @@ -7,6 +7,7 @@ import { VerticalAlignTopOutlined, } from '@ant-design/icons'; import { Button, Flex, Tooltip, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util'; import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; @@ -197,6 +198,11 @@ export function Filter(props: FilterProps): JSX.Element { })), }, }; + if (selectedFilters) { + logEvent('Traces Explorer: Sidebar filter used', { + selectedFilters, + }); + } redirectWithQueryBuilderData(preparedQuery); }, [currentQuery, redirectWithQueryBuilderData, selectedFilters], diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index ba267d383f..e598673c28 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -3,6 +3,7 @@ import './TracesExplorer.styles.scss'; import { FilterOutlined } from '@ant-design/icons'; import * as Sentry from '@sentry/react'; import { Button, Card, Tabs, Tooltip } from 'antd'; +import logEvent from 'api/common/logEvent'; import axios from 'axios'; import ExplorerCard from 'components/ExplorerCard/ExplorerCard'; import { LOCALSTORAGE } from 'constants/localStorage'; @@ -25,7 +26,7 @@ import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; import { cloneDeep, isEmpty, set } from 'lodash-es'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Dashboard } from 'types/api/dashboard/getAll'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { DataSource } from 'types/common/queryBuilder'; @@ -135,7 +136,7 @@ function TracesExplorer(): JSX.Element { }; const handleExport = useCallback( - (dashboard: Dashboard | null): void => { + (dashboard: Dashboard | null, isNewDashboard?: boolean): void => { if (!dashboard || !panelType) return; const panelTypeParam = AVAILABLE_EXPORT_PANEL_TYPES.includes(panelType) @@ -157,6 +158,12 @@ function TracesExplorer(): JSX.Element { options.selectColumns, ); + logEvent('Traces Explorer: Add to dashboard successful', { + panelType, + isNewDashboard, + dashboardName: dashboard?.data?.title, + }); + updateDashboard(updatedDashboard, { onSuccess: (data) => { if (data.error) { @@ -223,6 +230,13 @@ function TracesExplorer(): JSX.Element { currentPanelType, ]); const [isOpen, setOpen] = useState(true); + const logEventCalledRef = useRef(false); + useEffect(() => { + if (!logEventCalledRef.current) { + logEvent('Traces Explorer: Page visited', {}); + logEventCalledRef.current = true; + } + }, []); return ( }> From 840d8b2e49fad788807b9b151a02ae48eb44ed28 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 16 Jul 2024 14:30:03 +0530 Subject: [PATCH 099/281] fix: 404 not found when intercepting the ingestion key calls (#5490) --- frontend/src/api/apiV1.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/api/apiV1.ts b/frontend/src/api/apiV1.ts index 05b4e62e78..613ed27a17 100644 --- a/frontend/src/api/apiV1.ts +++ b/frontend/src/api/apiV1.ts @@ -3,7 +3,7 @@ const apiV1 = '/api/v1/'; export const apiV2 = '/api/v2/'; export const apiV3 = '/api/v3/'; export const apiV4 = '/api/v4/'; -export const gatewayApiV1 = '/api/gateway/v1'; -export const apiAlertManager = '/api/alertmanager'; +export const gatewayApiV1 = '/api/gateway/v1/'; +export const apiAlertManager = '/api/alertmanager/'; export default apiV1; From 43e73e06fe8a1eb3ba814c6428a57ac83e17f965 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 16 Jul 2024 18:35:59 +0530 Subject: [PATCH 100/281] chore: helpers required for dashboards e2e test cases (#5496) * chore: helpers required for dashboards e2e test cases * chore: helpers required for dashboards e2e test cases * chore: helpers required for dashboards e2e test cases --- .../src/container/GridCardLayout/WidgetHeader/index.tsx | 1 + .../NewDashboard/DashboardDescription/index.tsx | 9 +++++++-- .../src/container/NewWidget/RightContainer/index.tsx | 1 + frontend/src/container/NewWidget/index.tsx | 7 ++++++- frontend/src/container/NewWidget/utils.ts | 3 +++ .../QueryBuilder/filters/GroupByFilter/GroupByFilter.tsx | 1 + .../filters/ReduceToFilter/ReduceToFilter.tsx | 1 + 7 files changed, 20 insertions(+), 3 deletions(-) diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx index 1954b1c458..7daa4e553d 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx +++ b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx @@ -234,6 +234,7 @@ function WidgetHeader({ )} - diff --git a/frontend/src/container/NewWidget/RightContainer/index.tsx b/frontend/src/container/NewWidget/RightContainer/index.tsx index 3cc27a5f16..84400737d4 100644 --- a/frontend/src/container/NewWidget/RightContainer/index.tsx +++ b/frontend/src/container/NewWidget/RightContainer/index.tsx @@ -163,6 +163,7 @@ function RightContainer({ value={selectedGraph} style={{ width: '100%' }} className="panel-type-select" + data-testid="panel-change-select" > {graphTypes.map((item) => (
- ) : dashboards?.length === 0 && !searchValue ? ( + ) : dashboards?.length === 0 && !searchString ? (
{ window.open( 'https://signoz.io/docs/userguide/manage-dashboards?utm_source=product&utm_medium=dashboard-list-empty-state', @@ -758,7 +754,7 @@ function DashboardsList(): JSX.Element { } - value={searchValue} + value={searchString} onChange={handleSearch} /> {createNewDashboard && ( @@ -786,7 +782,7 @@ function DashboardsList(): JSX.Element {
img - No dashboards found for {searchValue}. Create a new dashboard? + No dashboards found for {searchString}. Create a new dashboard?
) : ( @@ -808,6 +804,7 @@ function DashboardsList(): JSX.Element { type="text" className={cx('sort-btns')} onClick={(): void => sortHandle('createdAt')} + data-testid="sort-by-last-created" > Last created {sortOrder.columnKey === 'createdAt' && } @@ -816,6 +813,7 @@ function DashboardsList(): JSX.Element { type="text" className={cx('sort-btns')} onClick={(): void => sortHandle('updatedAt')} + data-testid="sort-by-last-updated" > Last updated {sortOrder.columnKey === 'updatedAt' && } @@ -826,7 +824,7 @@ function DashboardsList(): JSX.Element { placement="bottomRight" arrow={false} > - + + res(ctx.status(200), ctx.json(dashboardSuccessResponse)), + ), + rest.get('http://localhost/api/v1/invite', (_, res, ctx) => res(ctx.status(200), ctx.json(inviteUser)), ), diff --git a/frontend/src/pages/DashboardsListPage/__tests__/DashboardListPage.test.tsx b/frontend/src/pages/DashboardsListPage/__tests__/DashboardListPage.test.tsx new file mode 100644 index 0000000000..98bd40ef62 --- /dev/null +++ b/frontend/src/pages/DashboardsListPage/__tests__/DashboardListPage.test.tsx @@ -0,0 +1,207 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +import ROUTES from 'constants/routes'; +import DashboardsList from 'container/ListOfDashboard'; +import { dashboardEmptyState } from 'mocks-server/__mockdata__/dashboards'; +import { server } from 'mocks-server/server'; +import { rest } from 'msw'; +import { DashboardProvider } from 'providers/Dashboard/Dashboard'; +import { MemoryRouter, useLocation } from 'react-router-dom'; +import { fireEvent, render, waitFor } from 'tests/test-utils'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn(), + useRouteMatch: jest.fn().mockReturnValue({ + params: { + dashboardId: 4, + }, + }), +})); + +const mockWindowOpen = jest.fn(); +window.open = mockWindowOpen; + +describe('dashboard list page', () => { + // should render on updatedAt and descend when the column key and order is messed up + it('should render the list even when the columnKey or the order is mismatched', async () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`, + search: `columnKey=asgard&order=stones&page=1`, + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + const { getByText, getByTestId } = render( + + + + + , + ); + + await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument()); + const firstElement = getByTestId('dashboard-title-0'); + expect(firstElement.textContent).toBe('captain america'); + const secondElement = getByTestId('dashboard-title-1'); + expect(secondElement.textContent).toBe('thor'); + }); + + // should render correctly when the column key is createdAt and order is descend + it('should render the list even when the columnKey and the order are given', async () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`, + search: `columnKey=createdAt&order=descend&page=1`, + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + const { getByText, getByTestId } = render( + + + + + , + ); + + await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument()); + const firstElement = getByTestId('dashboard-title-0'); + expect(firstElement.textContent).toBe('thor'); + const secondElement = getByTestId('dashboard-title-1'); + expect(secondElement.textContent).toBe('captain america'); + }); + + // change the sort by order and dashboards list ot be updated accordingly + it('dashboards list should be correctly updated on choosing the different sortBy from dropdown values', async () => { + const { getByText, getByTestId } = render( + + + + + , + ); + + await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument()); + + const firstElement = getByTestId('dashboard-title-0'); + expect(firstElement.textContent).toBe('thor'); + const secondElement = getByTestId('dashboard-title-1'); + expect(secondElement.textContent).toBe('captain america'); + + // click on the sort button + const sortByButton = getByTestId('sort-by'); + expect(sortByButton).toBeInTheDocument(); + fireEvent.click(sortByButton!); + + // change the sort order + const sortByUpdatedBy = getByTestId('sort-by-last-updated'); + await waitFor(() => expect(sortByUpdatedBy).toBeInTheDocument()); + fireEvent.click(sortByUpdatedBy!); + + // expect the new order + const updatedFirstElement = getByTestId('dashboard-title-0'); + expect(updatedFirstElement.textContent).toBe('captain america'); + const updatedSecondElement = getByTestId('dashboard-title-1'); + expect(updatedSecondElement.textContent).toBe('thor'); + }); + + // should filter correctly on search string + it('should filter dashboards based on search string', async () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`, + search: `columnKey=createdAt&order=descend&page=1&search=tho`, + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + const { getByText, getByTestId, queryByText } = render( + + + + + , + ); + + await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument()); + const firstElement = getByTestId('dashboard-title-0'); + expect(firstElement.textContent).toBe('thor'); + expect(queryByText('captain america')).not.toBeInTheDocument(); + + // the pagination item should not be present in the list when number of items are less than one page size + expect( + document.querySelector('.ant-table-pagination'), + ).not.toBeInTheDocument(); + }); + + it('dashboard empty search state', async () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`, + search: `columnKey=createdAt&order=descend&page=1&search=someRandomString`, + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + const { getByText } = render( + + + + + , + ); + + await waitFor(() => + expect( + getByText( + 'No dashboards found for someRandomString. Create a new dashboard?', + ), + ).toBeInTheDocument(), + ); + }); + + it('dashboard empty state', async () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`, + search: `columnKey=createdAt&order=descend&page=1`, + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + server.use( + rest.get('http://localhost/api/v1/dashboards', (_, res, ctx) => + res(ctx.status(200), ctx.json(dashboardEmptyState)), + ), + ); + const { getByText, getByTestId } = render( + + + + + , + ); + + await waitFor(() => + expect(getByText('No dashboards yet.')).toBeInTheDocument(), + ); + + const learnMoreButton = getByTestId('learn-more'); + expect(learnMoreButton).toBeInTheDocument(); + fireEvent.click(learnMoreButton); + + // test the correct link to be added for the dashboards empty state + await waitFor(() => + expect(mockWindowOpen).toHaveBeenCalledWith( + 'https://signoz.io/docs/userguide/manage-dashboards?utm_source=product&utm_medium=dashboard-list-empty-state', + '_blank', + ), + ); + }); +}); diff --git a/frontend/src/providers/Dashboard/Dashboard.tsx b/frontend/src/providers/Dashboard/Dashboard.tsx index 6e15b9e3b2..364fbd1944 100644 --- a/frontend/src/providers/Dashboard/Dashboard.tsx +++ b/frontend/src/providers/Dashboard/Dashboard.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-nested-ternary */ import { Modal } from 'antd'; import getDashboard from 'api/dashboard/get'; import lockDashboardApi from 'api/dashboard/lockDashboard'; @@ -11,6 +12,7 @@ import useAxiosError from 'hooks/useAxiosError'; import useTabVisibility from 'hooks/useTabFocus'; import useUrlQuery from 'hooks/useUrlQuery'; import { getUpdatedLayout } from 'lib/dashboard/getUpdatedLayout'; +import history from 'lib/history'; import { defaultTo } from 'lodash-es'; import isEqual from 'lodash-es/isEqual'; import isUndefined from 'lodash-es/isUndefined'; @@ -38,7 +40,7 @@ import AppReducer from 'types/reducer/app'; import { GlobalReducer } from 'types/reducer/globalTime'; import { v4 as generateUUID } from 'uuid'; -import { IDashboardContext } from './types'; +import { DashboardSortOrder, IDashboardContext } from './types'; import { sortLayout } from './util'; const DashboardContext = createContext({ @@ -52,7 +54,12 @@ const DashboardContext = createContext({ layouts: [], panelMap: {}, setPanelMap: () => {}, - listSortOrder: { columnKey: 'createdAt', order: 'descend', pagination: '1' }, + listSortOrder: { + columnKey: 'createdAt', + order: 'descend', + pagination: '1', + search: '', + }, setListSortOrder: () => {}, setLayouts: () => {}, setSelectedDashboard: () => {}, @@ -68,6 +75,7 @@ interface Props { dashboardId: string; } +// eslint-disable-next-line sonarjs/cognitive-complexity export function DashboardProvider({ children, }: PropsWithChildren): JSX.Element { @@ -82,17 +90,50 @@ export function DashboardProvider({ exact: true, }); - const params = useUrlQuery(); - const orderColumnParam = params.get('columnKey'); - const orderQueryParam = params.get('order'); - const paginationParam = params.get('page'); + const isDashboardListPage = useRouteMatch({ + path: ROUTES.ALL_DASHBOARD, + exact: true, + }); + + // added extra checks here in case wrong values appear use the default values rather than empty dashboards + const supportedOrderColumnKeys = ['createdAt', 'updatedAt']; + + const supportedOrderKeys = ['ascend', 'descend']; - const [listSortOrder, setListSortOrder] = useState({ - columnKey: orderColumnParam || 'updatedAt', - order: orderQueryParam || 'descend', + const params = useUrlQuery(); + // since the dashboard provider is wrapped at the very top of the application hence it initialises these values from other pages as well. + // pick the below params from URL only if the user is on the dashboards list page. + const orderColumnParam = isDashboardListPage && params.get('columnKey'); + const orderQueryParam = isDashboardListPage && params.get('order'); + const paginationParam = isDashboardListPage && params.get('page'); + const searchParam = isDashboardListPage && params.get('search'); + + const [listSortOrder, setListOrder] = useState({ + columnKey: orderColumnParam + ? supportedOrderColumnKeys.includes(orderColumnParam) + ? orderColumnParam + : 'updatedAt' + : 'updatedAt', + order: orderQueryParam + ? supportedOrderKeys.includes(orderQueryParam) + ? orderQueryParam + : 'descend' + : 'descend', pagination: paginationParam || '1', + search: searchParam || '', }); + function setListSortOrder(sortOrder: DashboardSortOrder): void { + if (!isEqual(sortOrder, listSortOrder)) { + setListOrder(sortOrder); + } + params.set('columnKey', sortOrder.columnKey as string); + params.set('order', sortOrder.order as string); + params.set('page', sortOrder.pagination || '1'); + params.set('search', sortOrder.search || ''); + history.replace({ search: params.toString() }); + } + const dispatch = useDispatch>(); const globalTime = useSelector( diff --git a/frontend/src/providers/Dashboard/types.ts b/frontend/src/providers/Dashboard/types.ts index d72c1839f5..e19c00e422 100644 --- a/frontend/src/providers/Dashboard/types.ts +++ b/frontend/src/providers/Dashboard/types.ts @@ -1,9 +1,15 @@ import dayjs from 'dayjs'; -import { Dispatch, SetStateAction } from 'react'; import { Layout } from 'react-grid-layout'; import { UseQueryResult } from 'react-query'; import { Dashboard } from 'types/api/dashboard/getAll'; +export interface DashboardSortOrder { + columnKey: string; + order: string; + pagination: string; + search: string; +} + export interface IDashboardContext { isDashboardSliderOpen: boolean; isDashboardLocked: boolean; @@ -15,18 +21,8 @@ export interface IDashboardContext { layouts: Layout[]; panelMap: Record; setPanelMap: React.Dispatch>>; - listSortOrder: { - columnKey: string; - order: string; - pagination: string; - }; - setListSortOrder: Dispatch< - SetStateAction<{ - columnKey: string; - order: string; - pagination: string; - }> - >; + listSortOrder: DashboardSortOrder; + setListSortOrder: (sortOrder: DashboardSortOrder) => void; setLayouts: React.Dispatch>; setSelectedDashboard: React.Dispatch< React.SetStateAction diff --git a/frontend/src/store/reducers/logs.ts b/frontend/src/store/reducers/logs.ts index 0d0a69a1a4..4e46d00e69 100644 --- a/frontend/src/store/reducers/logs.ts +++ b/frontend/src/store/reducers/logs.ts @@ -1,3 +1,4 @@ +import ROUTES from 'constants/routes'; import { parseQuery } from 'lib/logql'; import { OrderPreferenceItems } from 'pages/Logs/config'; import { @@ -29,6 +30,30 @@ import { } from 'types/actions/logs'; import { ILogsReducer } from 'types/reducer/logs'; +const supportedLogsOrder = [ + OrderPreferenceItems.ASC, + OrderPreferenceItems.DESC, +]; + +function getLogsOrder(): OrderPreferenceItems { + // set the value of order from the URL only when order query param is present and the user is landing on the old logs explorer page + if (window.location.pathname === ROUTES.OLD_LOGS_EXPLORER) { + const orderParam = new URLSearchParams(window.location.search).get('order'); + + if (orderParam) { + // check if the order passed is supported else pass the default order + if (supportedLogsOrder.includes(orderParam as OrderPreferenceItems)) { + return orderParam as OrderPreferenceItems; + } + + return OrderPreferenceItems.DESC; + } + return OrderPreferenceItems.DESC; + } + + return OrderPreferenceItems.DESC; +} + const initialState: ILogsReducer = { fields: { interesting: [], @@ -51,10 +76,7 @@ const initialState: ILogsReducer = { liveTailStartRange: 15, selectedLogId: null, detailedLog: null, - order: - (new URLSearchParams(window.location.search).get( - 'order', - ) as ILogsReducer['order']) ?? OrderPreferenceItems.DESC, + order: getLogsOrder(), }; export const LogsReducer = ( From add2d196144cb80a61132b38382da17403d45b06 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Thu, 18 Jul 2024 13:54:05 +0530 Subject: [PATCH 104/281] fix: fixed logEvent breaking page due to lack of null checks (#5511) * fix: fixed logEvent breaking page due to lack of null checks * fix: fixed logEvent breaking page due to lack of null checks --- frontend/src/container/AllError/index.tsx | 4 ++-- .../src/container/CreateAlertChannels/index.tsx | 8 ++++---- frontend/src/container/EditAlertChannels/index.tsx | 8 ++++---- frontend/src/container/ErrorDetails/index.tsx | 8 ++++---- .../container/ExplorerOptions/ExplorerOptions.tsx | 4 ++-- .../src/container/FormAlertRules/BasicInfo.tsx | 2 +- frontend/src/container/ListAlertRules/utils.ts | 4 ++-- .../container/MetricsApplication/Tabs/DBCall.tsx | 2 +- .../container/MetricsApplication/Tabs/External.tsx | 2 +- .../container/MetricsApplication/Tabs/Overview.tsx | 2 +- .../ServiceApplication/ServiceTraces/index.tsx | 2 +- frontend/src/container/SideNav/SideNav.tsx | 14 +++++++------- frontend/src/pages/SaveView/index.tsx | 4 ++-- 13 files changed, 32 insertions(+), 32 deletions(-) diff --git a/frontend/src/container/AllError/index.tsx b/frontend/src/container/AllError/index.tsx index d571c3dba7..0dd46c0a64 100644 --- a/frontend/src/container/AllError/index.tsx +++ b/frontend/src/container/AllError/index.tsx @@ -423,9 +423,9 @@ function AllErrors(): JSX.Element { )?.tagValue; logEvent('Exception: List page visited', { - numberOfExceptions: errorCountResponse.data?.payload, + numberOfExceptions: errorCountResponse?.data?.payload, selectedEnvironments, - resourceAttributeUsed: !!queries.length, + resourceAttributeUsed: !!queries?.length, }); logEventCalledRef.current = true; } diff --git a/frontend/src/container/CreateAlertChannels/index.tsx b/frontend/src/container/CreateAlertChannels/index.tsx index 85d609c24c..7345fa4ef9 100644 --- a/frontend/src/container/CreateAlertChannels/index.tsx +++ b/frontend/src/container/CreateAlertChannels/index.tsx @@ -449,8 +449,8 @@ function CreateAlertChannels({ const result = await functionToCall(); logEvent('Alert Channel: Save channel', { type: value, - sendResolvedAlert: selectedConfig.send_resolved, - name: selectedConfig.name, + sendResolvedAlert: selectedConfig?.send_resolved, + name: selectedConfig?.name, new: 'true', status: result?.status, statusMessage: result?.statusMessage, @@ -530,8 +530,8 @@ function CreateAlertChannels({ logEvent('Alert Channel: Test notification', { type: channelType, - sendResolvedAlert: selectedConfig.send_resolved, - name: selectedConfig.name, + sendResolvedAlert: selectedConfig?.send_resolved, + name: selectedConfig?.name, new: 'true', status: response && response.statusCode === 200 ? 'Test success' : 'Test failed', diff --git a/frontend/src/container/EditAlertChannels/index.tsx b/frontend/src/container/EditAlertChannels/index.tsx index b4fe30d557..0fc46beb33 100644 --- a/frontend/src/container/EditAlertChannels/index.tsx +++ b/frontend/src/container/EditAlertChannels/index.tsx @@ -370,8 +370,8 @@ function EditAlertChannels({ } logEvent('Alert Channel: Save channel', { type: value, - sendResolvedAlert: selectedConfig.send_resolved, - name: selectedConfig.name, + sendResolvedAlert: selectedConfig?.send_resolved, + name: selectedConfig?.name, new: 'false', status: result?.status, statusMessage: result?.statusMessage, @@ -441,8 +441,8 @@ function EditAlertChannels({ } logEvent('Alert Channel: Test notification', { type: channelType, - sendResolvedAlert: selectedConfig.send_resolved, - name: selectedConfig.name, + sendResolvedAlert: selectedConfig?.send_resolved, + name: selectedConfig?.name, new: 'false', status: response && response.statusCode === 200 ? 'Test success' : 'Test failed', diff --git a/frontend/src/container/ErrorDetails/index.tsx b/frontend/src/container/ErrorDetails/index.tsx index 2c87279e3d..c6b0d5fa22 100644 --- a/frontend/src/container/ErrorDetails/index.tsx +++ b/frontend/src/container/ErrorDetails/index.tsx @@ -114,10 +114,10 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element { const onClickTraceHandler = (): void => { logEvent('Exception: Navigate to trace detail page', { - groupId: errorDetail.groupID, + groupId: errorDetail?.groupID, spanId: errorDetail.spanID, traceId: errorDetail.traceID, - exceptionId: errorDetail.errorId, + exceptionId: errorDetail?.errorId, }); history.push(`/trace/${errorDetail.traceID}?spanId=${errorDetail.spanID}`); }; @@ -126,10 +126,10 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element { useEffect(() => { if (!logEventCalledRef.current && !isUndefined(data)) { logEvent('Exception: Detail page visited', { - groupId: errorDetail.groupID, + groupId: errorDetail?.groupID, spanId: errorDetail.spanID, traceId: errorDetail.traceID, - exceptionId: errorDetail.errorId, + exceptionId: errorDetail?.errorId, }); logEventCalledRef.current = true; } diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx index e925a60a8a..138694058e 100644 --- a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx @@ -256,12 +256,12 @@ function ExplorerOptions({ if (sourcepage === DataSource.TRACES) { logEvent('Traces Explorer: Select view', { panelType, - viewName: option.value, + viewName: option?.value, }); } else if (sourcepage === DataSource.LOGS) { logEvent('Logs Explorer: Select view', { panelType, - viewName: option.value, + viewName: option?.value, }); } if (ref.current) { diff --git a/frontend/src/container/FormAlertRules/BasicInfo.tsx b/frontend/src/container/FormAlertRules/BasicInfo.tsx index 40edb7977e..d047ed617b 100644 --- a/frontend/src/container/FormAlertRules/BasicInfo.tsx +++ b/frontend/src/container/FormAlertRules/BasicInfo.tsx @@ -88,7 +88,7 @@ function BasicInfo({ if (!channels.loading && isNewRule) { logEvent('Alert: New alert creation page visited', { dataSource: ALERTS_DATA_SOURCE_MAP[alertDef?.alertType as AlertTypes], - numberOfChannels: channels.payload?.length, + numberOfChannels: channels?.payload?.length, }); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/ListAlertRules/utils.ts b/frontend/src/container/ListAlertRules/utils.ts index 32da7eaad5..1556b28274 100644 --- a/frontend/src/container/ListAlertRules/utils.ts +++ b/frontend/src/container/ListAlertRules/utils.ts @@ -49,9 +49,9 @@ export const alertActionLogEvent = ( break; } logEvent('Alert: Action', { - ruleId: record.id, + ruleId: record?.id, dataSource: ALERTS_DATA_SOURCE_MAP[record.alertType as AlertTypes], - name: record.alert, + name: record?.alert, action: actionValue, }); }; diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index a520d98936..3ee49ddc9a 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -108,7 +108,7 @@ function DBCall(): JSX.Element { logEvent('APM: Service detail page visited', { selectedEnvironments, - resourceAttributeUsed: !!queries.length, + resourceAttributeUsed: !!queries?.length, section: 'dbMetrics', }); logEventCalledRef.current = true; diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index d224135175..69abd6696d 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -124,7 +124,7 @@ function External(): JSX.Element { logEvent('APM: Service detail page visited', { selectedEnvironments, - resourceAttributeUsed: !!queries.length, + resourceAttributeUsed: !!queries?.length, section: 'externalMetrics', }); logEventCalledRef.current = true; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 96cc821fb8..4fb9a2ccf3 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -91,7 +91,7 @@ function Application(): JSX.Element { logEvent('APM: Service detail page visited', { selectedEnvironments, - resourceAttributeUsed: !!queries.length, + resourceAttributeUsed: !!queries?.length, section: 'overview', }); logEventCalledRef.current = true; diff --git a/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx b/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx index 8d6238c68a..59c7a294bb 100644 --- a/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx +++ b/frontend/src/container/ServiceApplication/ServiceTraces/index.tsx @@ -59,7 +59,7 @@ function ServiceTraces(): JSX.Element { logEvent('APM: List page visited', { numberOfServices: data?.length, selectedEnvironments, - resourceAttributeUsed: !!queries.length, + resourceAttributeUsed: !!queries?.length, rps, }); logEventCalledRef.current = true; diff --git a/frontend/src/container/SideNav/SideNav.tsx b/frontend/src/container/SideNav/SideNav.tsx index b5eb240af8..d4ad27908c 100644 --- a/frontend/src/container/SideNav/SideNav.tsx +++ b/frontend/src/container/SideNav/SideNav.tsx @@ -324,8 +324,8 @@ function SideNav({ onClickHandler(item?.key as string, event); } logEvent('Sidebar: Menu clicked', { - menuRoute: item.key, - menuLabel: item.label, + menuRoute: item?.key, + menuLabel: item?.label, }); }; @@ -455,8 +455,8 @@ function SideNav({ onClick={(event: MouseEvent): void => { handleUserManagentMenuItemClick(item?.key as string, event); logEvent('Sidebar: Menu clicked', { - menuRoute: item.key, - menuLabel: item.label, + menuRoute: item?.key, + menuLabel: item?.label, }); }} /> @@ -475,8 +475,8 @@ function SideNav({ history.push(`${inviteMemberMenuItem.key}`); } logEvent('Sidebar: Menu clicked', { - menuRoute: inviteMemberMenuItem.key, - menuLabel: inviteMemberMenuItem.label, + menuRoute: inviteMemberMenuItem?.key, + menuLabel: inviteMemberMenuItem?.label, }); }} /> @@ -493,7 +493,7 @@ function SideNav({ event, ); logEvent('Sidebar: Menu clicked', { - menuRoute: userSettingsMenuItem.key, + menuRoute: userSettingsMenuItem?.key, menuLabel: 'User', }); }} diff --git a/frontend/src/pages/SaveView/index.tsx b/frontend/src/pages/SaveView/index.tsx index efcd3f2a4b..7088c4a78c 100644 --- a/frontend/src/pages/SaveView/index.tsx +++ b/frontend/src/pages/SaveView/index.tsx @@ -149,11 +149,11 @@ function SaveView(): JSX.Element { if (!logEventCalledRef.current && !isLoading) { if (sourcepage === DataSource.TRACES) { logEvent('Traces Views: Views visited', { - number: viewsData?.data.data.length, + number: viewsData?.data?.data?.length, }); } else if (sourcepage === DataSource.LOGS) { logEvent('Logs Views: Views visited', { - number: viewsData?.data.data.length, + number: viewsData?.data?.data?.length, }); } logEventCalledRef.current = true; From 7d7d112f4005f5ea2932d62229abb7ff1ef705cc Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Thu, 18 Jul 2024 13:56:18 +0530 Subject: [PATCH 105/281] fix: the dashboard locked bar should be sticky at the bottom (#5512) --- .../src/container/GridCardLayout/GridCardLayout.styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss index 1b2bd2a7ba..762fcdbca8 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss +++ b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss @@ -50,7 +50,7 @@ .footer { display: flex; flex-direction: column; - position: absolute; + position: fixed; bottom: 0; width: -webkit-fill-available; From 80133240ca01bce9d228af7648f74af25f14433c Mon Sep 17 00:00:00 2001 From: Pranay Prateek Date: Thu, 18 Jul 2024 17:25:32 +0530 Subject: [PATCH 106/281] fix: update community link (#5516) * update community link * Update copyright year --- pkg/query-service/version/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/query-service/version/version.go b/pkg/query-service/version/version.go index 68c37a4e0e..3e2f984d4c 100644 --- a/pkg/query-service/version/version.go +++ b/pkg/query-service/version/version.go @@ -25,12 +25,12 @@ Commit timestamp : %v Branch : %v Go version : %v -For SigNoz Official Documentation, visit https://signoz.io/docs -For SigNoz Community Slack, visit http://signoz.io/slack -For discussions about SigNoz, visit https://community.signoz.io +For SigNoz Official Documentation, visit https://signoz.io/docs/ +For SigNoz Community Slack, visit http://signoz.io/slack/ +For archive of discussions about SigNoz, visit https://knowledgebase.signoz.io/ %s. -Copyright 2022 SigNoz +Copyright 2024 SigNoz `, buildVersion, buildHash, buildTime, gitBranch, runtime.Version(), licenseInfo) From eb8ca5a7cab2216b1a37b882cadba278388b31f0 Mon Sep 17 00:00:00 2001 From: Nityananda Gohain Date: Thu, 18 Jul 2024 18:03:39 +0530 Subject: [PATCH 107/281] fix: ignore offset if timestamp is selected in order by (#5520) * fix: ignore offset if timestamp is selected in order by * fix: tests updated --- .../app/logs/v3/query_builder.go | 2 +- .../app/logs/v3/query_builder_test.go | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/pkg/query-service/app/logs/v3/query_builder.go b/pkg/query-service/app/logs/v3/query_builder.go index 06cacc8f60..8319f96384 100644 --- a/pkg/query-service/app/logs/v3/query_builder.go +++ b/pkg/query-service/app/logs/v3/query_builder.go @@ -477,7 +477,7 @@ type Options struct { } func isOrderByTs(orderBy []v3.OrderBy) bool { - if len(orderBy) == 1 && orderBy[0].Key == constants.TIMESTAMP { + if len(orderBy) == 1 && (orderBy[0].Key == constants.TIMESTAMP || orderBy[0].ColumnName == constants.TIMESTAMP) { return true } return false diff --git a/pkg/query-service/app/logs/v3/query_builder_test.go b/pkg/query-service/app/logs/v3/query_builder_test.go index 606ccffeef..dc41ce8c66 100644 --- a/pkg/query-service/app/logs/v3/query_builder_test.go +++ b/pkg/query-service/app/logs/v3/query_builder_test.go @@ -1380,6 +1380,66 @@ var testPrepLogsQueryData = []struct { ExpectedQuery: "SELECT now() as ts, toFloat64(count(*)) as value from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) order by value DESC LIMIT 10", Options: Options{}, }, + { + Name: "Ignore offset if order by is timestamp in list queries", + PanelType: v3.PanelTypeList, + Start: 1680066360726, + End: 1680066458000, + BuilderQuery: &v3.BuilderQuery{ + QueryName: "A", + StepInterval: 60, + AggregateOperator: v3.AggregateOperatorNoOp, + Expression: "A", + Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{ + {Key: v3.AttributeKey{Key: "id", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeUnspecified, IsColumn: true}, Value: "logid", Operator: "<"}, + }, + }, + OrderBy: []v3.OrderBy{ + { + ColumnName: "timestamp", + Order: "DESC", + }, + }, + Offset: 100, + PageSize: 100, + }, + TableName: "logs", + ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as " + + "attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as " + + "attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string " + + "from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND id < 'logid' order by " + + "timestamp DESC LIMIT 100", + }, + { + Name: "Don't ignore offset if order by is not timestamp", + PanelType: v3.PanelTypeList, + Start: 1680066360726, + End: 1680066458000, + BuilderQuery: &v3.BuilderQuery{ + QueryName: "A", + StepInterval: 60, + AggregateOperator: v3.AggregateOperatorNoOp, + Expression: "A", + Filters: &v3.FilterSet{Operator: "AND", Items: []v3.FilterItem{ + {Key: v3.AttributeKey{Key: "method", DataType: v3.AttributeKeyDataTypeString, Type: v3.AttributeKeyTypeTag}, Value: "GET", Operator: "="}, + }, + }, + OrderBy: []v3.OrderBy{ + { + ColumnName: "mycolumn", + Order: "DESC", + }, + }, + Offset: 100, + PageSize: 100, + }, + TableName: "logs", + ExpectedQuery: "SELECT timestamp, id, trace_id, span_id, trace_flags, severity_text, severity_number, body,CAST((attributes_string_key, attributes_string_value), 'Map(String, String)') as " + + "attributes_string,CAST((attributes_int64_key, attributes_int64_value), 'Map(String, Int64)') as attributes_int64,CAST((attributes_float64_key, attributes_float64_value), 'Map(String, Float64)') as " + + "attributes_float64,CAST((attributes_bool_key, attributes_bool_value), 'Map(String, Bool)') as attributes_bool,CAST((resources_string_key, resources_string_value), 'Map(String, String)') as resources_string " + + "from signoz_logs.distributed_logs where (timestamp >= 1680066360726000000 AND timestamp <= 1680066458000000000) AND attributes_string_value[indexOf(attributes_string_key, 'method')] = 'GET' order by " + + "resources_string_value[indexOf(resources_string_key, 'mycolumn')] DESC LIMIT 100 OFFSET 100", + }, } func TestPrepareLogsQuery(t *testing.T) { From a2492b01350eec8755fe2b70de33ffa2d3657fa6 Mon Sep 17 00:00:00 2001 From: Vibhu Pandey Date: Fri, 19 Jul 2024 11:59:40 +0530 Subject: [PATCH 108/281] ci(github): change to beta (#5524) * ci(github): change to beta * Update testing-deployment.yaml * ci(staging): bump to beta --- .github/workflows/staging-deployment.yaml | 3 ++- .github/workflows/testing-deployment.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/staging-deployment.yaml b/.github/workflows/staging-deployment.yaml index 455ecbce8c..a2ff80354f 100644 --- a/.github/workflows/staging-deployment.yaml +++ b/.github/workflows/staging-deployment.yaml @@ -30,6 +30,7 @@ jobs: GCP_PROJECT: ${{ secrets.GCP_PROJECT }} GCP_ZONE: ${{ secrets.GCP_ZONE }} GCP_INSTANCE: ${{ secrets.GCP_INSTANCE }} + CLOUDSDK_CORE_DISABLE_PROMPTS: 1 run: | read -r -d '' COMMAND < Date: Mon, 22 Jul 2024 11:05:20 +0530 Subject: [PATCH 109/281] chore: added trace filter test cases (#5451) * feat: added trace filter test cases * feat: added trace filter test cases - initial render * feat: added test cases - query sync, filter section behaviour etc * feat: deleted mock-data files * feat: added test cases of undefined filters and items * feat: deleted tsconfig * feat: added clear and rest btn test cases for traces filters * feat: added collapse and uncollapse test for traces filters --- .../src/container/TimeSeriesView/styles.ts | 3 +- frontend/src/container/TraceDetail/index.tsx | 5 +- .../TracesExplorer/QuerySection/styles.ts | 3 +- .../__mockdata__/explorer_views.ts | 81 +++++ frontend/src/mocks-server/handlers.ts | 73 ++++ .../TracesExplorer/Filter/DurationSection.tsx | 2 + .../pages/TracesExplorer/Filter/Filter.tsx | 7 +- .../pages/TracesExplorer/Filter/Section.tsx | 8 +- .../TracesExplorer/Filter/SectionContent.tsx | 1 + .../__test__/TracesExplorer.test.tsx | 333 +++++++++++++++++- frontend/src/pages/TracesExplorer/index.tsx | 1 + 11 files changed, 505 insertions(+), 12 deletions(-) create mode 100644 frontend/src/mocks-server/__mockdata__/explorer_views.ts diff --git a/frontend/src/container/TimeSeriesView/styles.ts b/frontend/src/container/TimeSeriesView/styles.ts index d73f11c38a..41a730161d 100644 --- a/frontend/src/container/TimeSeriesView/styles.ts +++ b/frontend/src/container/TimeSeriesView/styles.ts @@ -1,5 +1,4 @@ -import { Typography } from 'antd'; -import Card from 'antd/es/card/Card'; +import { Card, Typography } from 'antd'; import styled from 'styled-components'; export const Container = styled(Card)` diff --git a/frontend/src/container/TraceDetail/index.tsx b/frontend/src/container/TraceDetail/index.tsx index 568ed3c4f4..38f6db7c08 100644 --- a/frontend/src/container/TraceDetail/index.tsx +++ b/frontend/src/container/TraceDetail/index.tsx @@ -1,8 +1,7 @@ import './TraceDetails.styles.scss'; import { FilterOutlined } from '@ant-design/icons'; -import { Button, Col, Typography } from 'antd'; -import Sider from 'antd/es/layout/Sider'; +import { Button, Col, Layout, Typography } from 'antd'; import cx from 'classnames'; import { StyledCol, @@ -42,6 +41,8 @@ import { INTERVAL_UNITS, } from './utils'; +const { Sider } = Layout; + function TraceDetail({ response }: TraceDetailProps): JSX.Element { const spanServiceColors = useMemo( () => spanServiceNameToColorMapping(response[0].events), diff --git a/frontend/src/container/TracesExplorer/QuerySection/styles.ts b/frontend/src/container/TracesExplorer/QuerySection/styles.ts index cdb46bd580..a688b0dbcb 100644 --- a/frontend/src/container/TracesExplorer/QuerySection/styles.ts +++ b/frontend/src/container/TracesExplorer/QuerySection/styles.ts @@ -1,5 +1,4 @@ -import { Col } from 'antd'; -import Card from 'antd/es/card/Card'; +import { Card, Col } from 'antd'; import styled from 'styled-components'; export const Container = styled(Card)` diff --git a/frontend/src/mocks-server/__mockdata__/explorer_views.ts b/frontend/src/mocks-server/__mockdata__/explorer_views.ts new file mode 100644 index 0000000000..ae88071e55 --- /dev/null +++ b/frontend/src/mocks-server/__mockdata__/explorer_views.ts @@ -0,0 +1,81 @@ +export const explorerView = { + status: 'success', + data: [ + { + uuid: 'test-uuid-1', + name: 'Table View', + category: '', + createdAt: '2023-08-29T18:04:10.906310033Z', + createdBy: 'test-user-1', + updatedAt: '2024-01-29T10:42:47.346331133Z', + updatedBy: 'test-user-1', + sourcePage: 'traces', + tags: [''], + compositeQuery: { + builderQueries: { + A: { + queryName: 'A', + stepInterval: 60, + dataSource: 'traces', + aggregateOperator: 'count', + aggregateAttribute: { + key: 'component', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + }, + filters: { + op: 'AND', + items: [ + { + key: { + key: 'component', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + }, + value: 'test-component', + op: '!=', + }, + ], + }, + groupBy: [ + { + key: 'component', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + }, + { + key: 'client-uuid', + dataType: 'string', + type: 'resource', + isColumn: false, + isJSON: false, + }, + ], + expression: 'A', + disabled: false, + limit: 0, + offset: 0, + pageSize: 0, + orderBy: [ + { + columnName: 'timestamp', + order: 'desc', + }, + ], + reduceTo: 'sum', + ShiftBy: 0, + }, + }, + panelType: 'table', + queryType: 'builder', + }, + extraData: '{"color":"#00ffd0"}', + }, + ], +}; diff --git a/frontend/src/mocks-server/handlers.ts b/frontend/src/mocks-server/handlers.ts index 35ede82e83..8381818981 100644 --- a/frontend/src/mocks-server/handlers.ts +++ b/frontend/src/mocks-server/handlers.ts @@ -2,6 +2,7 @@ import { rest } from 'msw'; import { billingSuccessResponse } from './__mockdata__/billing'; import { dashboardSuccessResponse } from './__mockdata__/dashboards'; +import { explorerView } from './__mockdata__/explorer_views'; import { inviteUser } from './__mockdata__/invite_user'; import { licensesSuccessResponse } from './__mockdata__/licenses'; import { membersResponse } from './__mockdata__/members'; @@ -55,6 +56,51 @@ export const handlers = [ const metricName = req.url.searchParams.get('metricName'); const tagKey = req.url.searchParams.get('tagKey'); + const attributeKey = req.url.searchParams.get('attributeKey'); + + if (attributeKey === 'serviceName') { + return res( + ctx.status(200), + ctx.json({ + status: 'success', + data: { + stringAttributeValues: [ + 'customer', + 'demo-app', + 'driver', + 'frontend', + 'mysql', + 'redis', + 'route', + 'go-grpc-otel-server', + 'test', + ], + numberAttributeValues: null, + boolAttributeValues: null, + }, + }), + ); + } + + if (attributeKey === 'name') { + return res( + ctx.status(200), + ctx.json({ + status: 'success', + data: { + stringAttributeValues: [ + 'HTTP GET', + 'HTTP GET /customer', + 'HTTP GET /dispatch', + 'HTTP GET /route', + ], + numberAttributeValues: null, + boolAttributeValues: null, + }, + }), + ); + } + if ( metricName === 'signoz_calls_total' && tagKey === 'resource_signoz_collector_id' @@ -102,4 +148,31 @@ export const handlers = [ rest.post('http://localhost/api/v1/invite', (_, res, ctx) => res(ctx.status(200), ctx.json(inviteUser)), ), + + rest.get( + 'http://localhost/api/v3/autocomplete/aggregate_attributes', + (req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + status: 'success', + data: { attributeKeys: null }, + }), + ), + ), + + rest.get('http://localhost/api/v1/explorer/views', (req, res, ctx) => + res(ctx.status(200), ctx.json(explorerView)), + ), + + rest.post('http://localhost/api/v1/event', (req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + statusCode: 200, + error: null, + payload: 'Event Processed Successfully', + }), + ), + ), ]; diff --git a/frontend/src/pages/TracesExplorer/Filter/DurationSection.tsx b/frontend/src/pages/TracesExplorer/Filter/DurationSection.tsx index ce124f623e..7cf2441a49 100644 --- a/frontend/src/pages/TracesExplorer/Filter/DurationSection.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/DurationSection.tsx @@ -109,6 +109,7 @@ export function DurationSection(props: DurationProps): JSX.Element { className="min-max-input" onChange={onChangeMinHandler} value={preMin} + data-testid="min-input" addonAfter="ms" />
diff --git a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx index 3d3895e047..2893fca2ba 100644 --- a/frontend/src/pages/TracesExplorer/Filter/Filter.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/Filter.tsx @@ -224,13 +224,18 @@ export function Filter(props: FilterProps): JSX.Element { - diff --git a/frontend/src/pages/TracesExplorer/Filter/Section.tsx b/frontend/src/pages/TracesExplorer/Filter/Section.tsx index 8ce2007ef7..9212f610b0 100644 --- a/frontend/src/pages/TracesExplorer/Filter/Section.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/Section.tsx @@ -64,7 +64,7 @@ export function Section(props: SectionProps): JSX.Element { return (
-
+
-
diff --git a/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx b/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx index 4cefaaeca0..2bae1dfe16 100644 --- a/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx +++ b/frontend/src/pages/TracesExplorer/Filter/SectionContent.tsx @@ -145,6 +145,7 @@ export function SectionBody(props: SectionBodyProps): JSX.Element { key={`${type}-${item}`} onChange={(e): void => onCheckHandler(e, item)} checked={checkboxMatcher(item)} + data-testid={`${type}-${item}`} >
diff --git a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx index 1250a3f3cc..1dcaaaa4cf 100644 --- a/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx +++ b/frontend/src/pages/TracesExplorer/__test__/TracesExplorer.test.tsx @@ -1,16 +1,19 @@ /* eslint-disable sonarjs/no-duplicate-string */ /* eslint-disable no-restricted-syntax */ /* eslint-disable no-await-in-loop */ +import userEvent from '@testing-library/user-event'; import { initialQueriesMap, initialQueryBuilderFormValues, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; import * as compositeQueryHook from 'hooks/queryBuilder/useGetCompositeQueryParam'; -import { render } from 'tests/test-utils'; +import { QueryBuilderContext } from 'providers/QueryBuilder'; +import { fireEvent, render, screen, waitFor, within } from 'tests/test-utils'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import TracesExplorer from '..'; import { Filter } from '../Filter/Filter'; import { AllTraceFilterKeyValue } from '../Filter/filterUtils'; @@ -37,6 +40,48 @@ jest.mock('uplot', () => { }; }); +jest.mock( + 'container/TopNav/DateTimeSelectionV2/index.tsx', + () => + function MockDateTimeSelection(): JSX.Element { + return
MockDateTimeSelection
; + }, +); + +function checkIfSectionIsOpen( + getByTestId: (testId: string) => HTMLElement, + panelName: string, +): void { + const section = getByTestId(`collapse-${panelName}`); + expect(section.querySelector('.ant-collapse-item-active')).not.toBeNull(); +} + +function checkIfSectionIsNotOpen( + getByTestId: (testId: string) => HTMLElement, + panelName: string, +): void { + const section = getByTestId(`collapse-${panelName}`); + expect(section.querySelector('.ant-collapse-item-active')).toBeNull(); +} + +const defaultOpenSections = ['hasError', 'durationNano', 'serviceName']; + +const defaultClosedSections = Object.keys(AllTraceFilterKeyValue).filter( + (section) => + ![...defaultOpenSections, 'durationNanoMin', 'durationNanoMax'].includes( + section, + ), +); + +async function checkForSectionContent(values: string[]): Promise { + for (const val of values) { + const sectionContent = await screen.findByText(val); + await waitFor(() => expect(sectionContent).toBeInTheDocument()); + } +} + +const redirectWithQueryBuilderData = jest.fn(); + const compositeQuery: Query = { ...initialQueriesMap.traces, builder: { @@ -81,6 +126,157 @@ const compositeQuery: Query = { }; describe('TracesExplorer - ', () => { + // Initial filter panel rendering + // Test the initial state like which filters section are opened, default state of duration slider, etc. + it('should render the Trace filter', async () => { + const { getByText, getByTestId } = render(); + + Object.values(AllTraceFilterKeyValue).forEach((filter) => { + expect(getByText(filter)).toBeInTheDocument(); + }); + + // Check default state of duration slider + const minDuration = getByTestId('min-input') as HTMLInputElement; + const maxDuration = getByTestId('max-input') as HTMLInputElement; + expect(minDuration).toHaveValue(null); + expect(minDuration).toHaveProperty('placeholder', '0'); + expect(maxDuration).toHaveValue(null); + expect(maxDuration).toHaveProperty('placeholder', '100000000'); + + // Check which all filter section are opened by default + defaultOpenSections.forEach((section) => + checkIfSectionIsOpen(getByTestId, section), + ); + + // Check which all filter section are closed by default + defaultClosedSections.forEach((section) => + checkIfSectionIsNotOpen(getByTestId, section), + ); + + // check for the status section content + await checkForSectionContent(['Ok', 'Error']); + + // check for the service name section content from API response + await checkForSectionContent([ + 'customer', + 'demo-app', + 'driver', + 'frontend', + 'mysql', + 'redis', + 'route', + 'go-grpc-otel-server', + 'test', + ]); + }); + + // test the filter panel actions like opening and closing the sections, etc. + it('filter panel actions', async () => { + const { getByTestId } = render(); + + // Check if the section is closed + checkIfSectionIsNotOpen(getByTestId, 'name'); + // Open the section + const name = getByTestId('collapse-name'); + expect(name).toBeInTheDocument(); + + userEvent.click(within(name).getByText(AllTraceFilterKeyValue.name)); + await waitFor(() => checkIfSectionIsOpen(getByTestId, 'name')); + + await checkForSectionContent([ + 'HTTP GET', + 'HTTP GET /customer', + 'HTTP GET /dispatch', + 'HTTP GET /route', + ]); + + // Close the section + userEvent.click(within(name).getByText(AllTraceFilterKeyValue.name)); + await waitFor(() => checkIfSectionIsNotOpen(getByTestId, 'name')); + }); + + it('checking filters should update the query', async () => { + const { getByText } = render( + + + , + ); + + const okCheckbox = getByText('Ok'); + fireEvent.click(okCheckbox); + expect( + redirectWithQueryBuilderData.mock.calls[ + redirectWithQueryBuilderData.mock.calls.length - 1 + ][0].builder.queryData[0].filters.items, + ).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: { + id: expect.any(String), + key: 'hasError', + type: 'tag', + dataType: 'bool', + isColumn: true, + isJSON: false, + }, + op: 'in', + value: ['false'], + }), + ]), + ); + + // Check if the query is updated when the error checkbox is clicked + const errorCheckbox = getByText('Error'); + fireEvent.click(errorCheckbox); + expect( + redirectWithQueryBuilderData.mock.calls[ + redirectWithQueryBuilderData.mock.calls.length - 1 + ][0].builder.queryData[0].filters.items, + ).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: { + id: expect.any(String), + key: 'hasError', + type: 'tag', + dataType: 'bool', + isColumn: true, + isJSON: false, + }, + op: 'in', + value: ['false', 'true'], + }), + ]), + ); + }); + + it('should render the trace filter with the given query', async () => { + jest + .spyOn(compositeQueryHook, 'useGetCompositeQueryParam') + .mockReturnValue(compositeQuery); + + const { findByText, getByTestId } = render(); + + // check if the default query is applied - composite query has filters - serviceName : demo-app and name : HTTP GET /customer + expect(await findByText('demo-app')).toBeInTheDocument(); + expect(getByTestId('serviceName-demo-app')).toBeChecked(); + expect(await findByText('HTTP GET /customer')).toBeInTheDocument(); + expect(getByTestId('name-HTTP GET /customer')).toBeChecked(); + }); + it('test edge cases of undefined filters', async () => { jest.spyOn(compositeQueryHook, 'useGetCompositeQueryParam').mockReturnValue({ ...compositeQuery, @@ -98,7 +294,6 @@ describe('TracesExplorer - ', () => { const { getByText } = render(); - // we should have all the filters Object.values(AllTraceFilterKeyValue).forEach((filter) => { expect(getByText(filter)).toBeInTheDocument(); }); @@ -124,9 +319,141 @@ describe('TracesExplorer - ', () => { const { getByText } = render(); - // we should have all the filters Object.values(AllTraceFilterKeyValue).forEach((filter) => { expect(getByText(filter)).toBeInTheDocument(); }); }); + + it('should clear filter on clear & reset button click', async () => { + const { getByText, getByTestId } = render( + + + , + ); + + // check for the status section content + await checkForSectionContent(['Ok', 'Error']); + + // check for the service name section content from API response + await checkForSectionContent([ + 'customer', + 'demo-app', + 'driver', + 'frontend', + 'mysql', + 'redis', + 'route', + 'go-grpc-otel-server', + 'test', + ]); + + const okCheckbox = getByText('Ok'); + fireEvent.click(okCheckbox); + + const frontendCheckbox = getByText('frontend'); + fireEvent.click(frontendCheckbox); + + // check if checked and present in query + expect( + redirectWithQueryBuilderData.mock.calls[ + redirectWithQueryBuilderData.mock.calls.length - 1 + ][0].builder.queryData[0].filters.items, + ).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: { + id: expect.any(String), + key: 'hasError', + type: 'tag', + dataType: 'bool', + isColumn: true, + isJSON: false, + }, + op: 'in', + value: ['false'], + }), + expect.objectContaining({ + key: { + key: 'serviceName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: expect.any(String), + }, + op: 'in', + value: ['frontend'], + }), + ]), + ); + + const clearButton = getByTestId('collapse-serviceName-clearBtn'); + expect(clearButton).toBeInTheDocument(); + fireEvent.click(clearButton); + + // check if cleared and not present in query + expect( + redirectWithQueryBuilderData.mock.calls[ + redirectWithQueryBuilderData.mock.calls.length - 1 + ][0].builder.queryData[0].filters.items, + ).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: { + key: 'serviceName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: expect.any(String), + }, + op: 'in', + value: ['frontend'], + }), + ]), + ); + + // check if reset button is present + const resetButton = getByTestId('reset-filters'); + expect(resetButton).toBeInTheDocument(); + fireEvent.click(resetButton); + + // check if reset id done + expect( + redirectWithQueryBuilderData.mock.calls[ + redirectWithQueryBuilderData.mock.calls.length - 1 + ][0].builder.queryData[0].filters.items, + ).toEqual([]); + }); + + it('filter panel should collapse & uncollapsed', async () => { + const { getByText, getByTestId } = render(); + + Object.values(AllTraceFilterKeyValue).forEach((filter) => { + expect(getByText(filter)).toBeInTheDocument(); + }); + + // Filter panel should collapse + const collapseButton = getByTestId('toggle-filter-panel'); + expect(collapseButton).toBeInTheDocument(); + fireEvent.click(collapseButton); + + // uncollapse btn should be present + expect( + await screen.findByTestId('filter-uncollapse-btn'), + ).toBeInTheDocument(); + }); }); diff --git a/frontend/src/pages/TracesExplorer/index.tsx b/frontend/src/pages/TracesExplorer/index.tsx index e598673c28..bb25a37f86 100644 --- a/frontend/src/pages/TracesExplorer/index.tsx +++ b/frontend/src/pages/TracesExplorer/index.tsx @@ -251,6 +251,7 @@ function TracesExplorer(): JSX.Element { From b08e859426e786c69c15fd49f6a0d886913b77b7 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 22 Jul 2024 13:43:47 +0530 Subject: [PATCH 110/281] fix: do not add select columns when the datasource is logs (#5515) * fix: do not add select columns when the datasource is logs * chore: added data test id --- frontend/src/container/GridCardLayout/GridCard/index.tsx | 5 ++--- .../NewWidget/LeftContainer/ExplorerColumnsRenderer.tsx | 1 + frontend/src/container/NewWidget/index.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 0f30a189cf..8e34e32879 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -125,10 +125,9 @@ function GridCardGraph({ offset: 0, limit: updatedQuery.builder.queryData[0].limit || 0, }, + // we do not need select columns in case of logs selectColumns: - initialDataSource === DataSource.LOGS - ? widget.selectedLogFields - : widget.selectedTracesFields, + initialDataSource === DataSource.TRACES && widget.selectedTracesFields, }, fillGaps: widget.fillSpans, }; diff --git a/frontend/src/container/NewWidget/LeftContainer/ExplorerColumnsRenderer.tsx b/frontend/src/container/NewWidget/LeftContainer/ExplorerColumnsRenderer.tsx index a9a8d9ceb2..0ead9a3765 100644 --- a/frontend/src/container/NewWidget/LeftContainer/ExplorerColumnsRenderer.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/ExplorerColumnsRenderer.tsx @@ -309,6 +309,7 @@ function ExplorerColumnsRenderer({ >
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + service_name + + + + +
+ +
+
+ + latency-per-service + + + + +
+ +
+
+ demo-app +
+
+
+ 4.35 s +
+
+
+ customer +
+
+
+ 431 ms +
+
+
+ mysql +
+
+
+ 431 ms +
+
+
+ frontend +
+
+
+ 287 ms +
+
+
+ driver +
+
+
+ 230 ms +
+
+
+ route +
+
+
+ 66.4 ms +
+
+
+ redis +
+
+
+ 31.3 ms +
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/frontend/src/container/PanelWrapper/__tests__/__snapshots__/ValuePanelWrapper.test.tsx.snap b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/ValuePanelWrapper.test.tsx.snap new file mode 100644 index 0000000000..435a7cb08d --- /dev/null +++ b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/ValuePanelWrapper.test.tsx.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Value panel wrappper tests should render tooltip when there are conflicting thresholds 1`] = ` +.c1 { + height: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; +} + +.c0 { + text-align: center; + padding-top: 1rem; +} + +
+ +
+
+
+
+
+ + 295 ms + +
+ + + +
+
+
+
+`; diff --git a/frontend/src/container/PanelWrapper/__tests__/tablePanelWrapperHelper.ts b/frontend/src/container/PanelWrapper/__tests__/tablePanelWrapperHelper.ts new file mode 100644 index 0000000000..c6be13c200 --- /dev/null +++ b/frontend/src/container/PanelWrapper/__tests__/tablePanelWrapperHelper.ts @@ -0,0 +1,286 @@ +export const tablePanelWidgetQuery = { + id: '727533b0-7718-4f99-a1db-a1875649325c', + title: '', + description: '', + isStacked: false, + nullZeroValues: 'zero', + opacity: '1', + panelTypes: 'table', + query: { + clickhouse_sql: [ + { + name: 'A', + legend: '', + disabled: false, + query: '', + }, + ], + promql: [ + { + name: 'A', + query: '', + legend: '', + disabled: false, + }, + ], + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + key: 'signoz_latency', + dataType: 'float64', + type: 'ExponentialHistogram', + isColumn: true, + isJSON: false, + id: 'signoz_latency--float64--ExponentialHistogram--true', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [ + { + key: 'service_name', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + id: 'service_name--string--tag--false', + }, + ], + legend: 'latency-per-service', + reduceTo: 'avg', + }, + ], + queryFormulas: [], + }, + id: '7feafec2-a450-4b5a-8897-260c1a9fe1e4', + queryType: 'builder', + }, + timePreferance: 'GLOBAL_TIME', + softMax: null, + softMin: null, + selectedLogFields: [ + { + dataType: 'string', + type: '', + name: 'body', + }, + { + dataType: 'string', + type: '', + name: 'timestamp', + }, + ], + selectedTracesFields: [ + { + key: 'serviceName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'serviceName--string--tag--true', + }, + { + key: 'name', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'name--string--tag--true', + }, + { + key: 'durationNano', + dataType: 'float64', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'durationNano--float64--tag--true', + }, + { + key: 'httpMethod', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'httpMethod--string--tag--true', + }, + { + key: 'responseStatusCode', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'responseStatusCode--string--tag--true', + }, + ], + yAxisUnit: 'none', + thresholds: [], + fillSpans: false, + columnUnits: { + A: 'ms', + }, + bucketCount: 30, + stackedBarChart: false, + bucketWidth: 0, + mergeAllActiveQueries: false, +}; + +export const tablePanelQueryResponse = { + status: 'success', + isLoading: false, + isSuccess: true, + isError: false, + isIdle: false, + data: { + statusCode: 200, + error: null, + message: 'success', + payload: { + status: 'success', + data: { + resultType: '', + result: [ + { + table: { + columns: [ + { + name: 'service_name', + queryName: '', + isValueColumn: false, + }, + { + name: 'A', + queryName: 'A', + isValueColumn: true, + }, + ], + rows: [ + { + data: { + A: 4353.81, + service_name: 'demo-app', + }, + }, + { + data: { + A: 431.25, + service_name: 'customer', + }, + }, + { + data: { + A: 431.25, + service_name: 'mysql', + }, + }, + { + data: { + A: 287.11, + service_name: 'frontend', + }, + }, + { + data: { + A: 230.02, + service_name: 'driver', + }, + }, + { + data: { + A: 66.37, + service_name: 'route', + }, + }, + { + data: { + A: 31.3, + service_name: 'redis', + }, + }, + ], + }, + }, + ], + }, + }, + params: { + start: 1721207225000, + end: 1721207525000, + step: 60, + variables: {}, + formatForWeb: true, + compositeQuery: { + queryType: 'builder', + panelType: 'table', + fillGaps: false, + builderQueries: { + A: { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + key: 'signoz_latency', + dataType: 'float64', + type: 'ExponentialHistogram', + isColumn: true, + isJSON: false, + id: 'signoz_latency--float64--ExponentialHistogram--true', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [ + { + key: 'service_name', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + id: 'service_name--string--tag--false', + }, + ], + legend: '', + reduceTo: 'avg', + }, + }, + }, + }, + }, + dataUpdatedAt: 1721207526018, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, +}; diff --git a/frontend/src/container/PanelWrapper/__tests__/valuePanelWrapperHelper.ts b/frontend/src/container/PanelWrapper/__tests__/valuePanelWrapperHelper.ts new file mode 100644 index 0000000000..1376feac06 --- /dev/null +++ b/frontend/src/container/PanelWrapper/__tests__/valuePanelWrapperHelper.ts @@ -0,0 +1,267 @@ +export const valuePanelWidget = { + id: 'b8b93086-ef01-47bf-9044-1e7abd583be4', + title: 'signoz latency in ms', + description: '', + isStacked: false, + nullZeroValues: 'zero', + opacity: '1', + panelTypes: 'value', + query: { + clickhouse_sql: [ + { + name: 'A', + legend: '', + disabled: false, + query: '', + }, + ], + promql: [ + { + name: 'A', + query: '', + legend: '', + disabled: false, + }, + ], + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + key: 'signoz_latency', + dataType: 'float64', + type: 'ExponentialHistogram', + isColumn: true, + isJSON: false, + id: 'signoz_latency--float64--ExponentialHistogram--true', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + ], + queryFormulas: [], + }, + id: '3bec289c-49c3-4d7e-98bb-84d47c79909c', + queryType: 'builder', + }, + timePreferance: 'GLOBAL_TIME', + softMax: null, + softMin: null, + selectedLogFields: [ + { + dataType: 'string', + type: '', + name: 'body', + }, + { + dataType: 'string', + type: '', + name: 'timestamp', + }, + ], + selectedTracesFields: [ + { + key: 'serviceName', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'serviceName--string--tag--true', + }, + { + key: 'name', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'name--string--tag--true', + }, + { + key: 'durationNano', + dataType: 'float64', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'durationNano--float64--tag--true', + }, + { + key: 'httpMethod', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'httpMethod--string--tag--true', + }, + { + key: 'responseStatusCode', + dataType: 'string', + type: 'tag', + isColumn: true, + isJSON: false, + id: 'responseStatusCode--string--tag--true', + }, + ], + yAxisUnit: 'ms', + thresholds: [], + fillSpans: false, + columnUnits: {}, + bucketCount: 30, + stackedBarChart: false, + bucketWidth: 0, + mergeAllActiveQueries: false, +}; + +export const thresholds = [ + { + index: '8eb16a3a-b4f1-47c8-943a-4b1786884583', + isEditEnabled: false, + thresholdColor: 'Blue', + thresholdFormat: 'Text', + thresholdOperator: '>', + thresholdUnit: 'none', + thresholdValue: 100, + keyIndex: 1, + selectedGraph: 'value', + thresholdTableOptions: '', + thresholdLabel: '', + }, + { + index: 'eb9c1186-ad7d-42dd-8e7f-3913a321d7cf', + isEditEnabled: false, + thresholdColor: 'Red', + thresholdFormat: 'Text', + thresholdOperator: '>', + thresholdUnit: 'none', + thresholdValue: 0, + keyIndex: 0, + selectedGraph: 'value', + thresholdTableOptions: '', + thresholdLabel: '', + }, +]; + +export const valuePanelQueryResponse = { + status: 'success', + isLoading: false, + isSuccess: true, + isError: false, + isIdle: false, + data: { + statusCode: 200, + error: null, + message: 'success', + payload: { + data: { + result: [ + { + metric: { + A: 'A', + }, + values: [[0, '295.4299833508185']], + queryName: 'A', + legend: 'A', + }, + ], + resultType: '', + newResult: { + status: 'success', + data: { + resultType: '', + result: [ + { + queryName: 'A', + series: [ + { + labels: { + A: 'A', + }, + labelsArray: null, + values: [ + { + timestamp: 0, + value: '295.4299833508185', + }, + ], + }, + ], + }, + ], + }, + }, + }, + }, + params: { + start: 1721203451000, + end: 1721203751000, + step: 60, + variables: {}, + formatForWeb: false, + compositeQuery: { + queryType: 'builder', + panelType: 'value', + fillGaps: false, + builderQueries: { + A: { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + key: 'signoz_latency', + dataType: 'float64', + type: 'ExponentialHistogram', + isColumn: true, + isJSON: false, + id: 'signoz_latency--float64--ExponentialHistogram--true', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + }, + }, + }, + }, + dataUpdatedAt: 1721203751775, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isRefetching: false, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isStale: true, +}; From 6957bd71cae970fbfcef95be6e546429350a2afd Mon Sep 17 00:00:00 2001 From: Yunus M Date: Tue, 23 Jul 2024 16:32:45 +0530 Subject: [PATCH 114/281] chore: move from trackEvent to logEvent (#5530) * chore: move from trackEvent to logEvent * feat: update test cases --- frontend/src/AppRoutes/index.tsx | 5 +- .../BillingContainer/BillingContainer.tsx | 8 ++-- .../OnboardingContainer.tsx | 6 +-- .../ConnectionStatus/ConnectionStatus.tsx | 8 ++-- .../LogsConnectionStatus.tsx | 7 ++- .../ModuleStepsContainer.tsx | 46 +++++++++---------- .../Layouts/Pipeline/CreatePipelineButton.tsx | 7 ++- .../PipelineListsView/PipelineExpandView.tsx | 5 +- .../PipelineListsView/PipelineListsView.tsx | 6 +-- .../components/PreviewAction.tsx | 6 +-- .../tests/CreatePipelineButton.test.tsx | 21 +++------ .../Configure.tsx | 5 +- .../IntegrationDetailHeader.tsx | 8 ++-- .../IntegrationsUninstallBar.tsx | 6 +-- .../src/pages/Integrations/Integrations.tsx | 10 ++-- frontend/src/pages/SignUp/SignUp.tsx | 9 ++-- frontend/src/pages/Support/Support.tsx | 7 ++- .../pages/WorkspaceLocked/WorkspaceLocked.tsx | 7 ++- 18 files changed, 71 insertions(+), 106 deletions(-) diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx index bc2b02d842..ac225db25a 100644 --- a/frontend/src/AppRoutes/index.tsx +++ b/frontend/src/AppRoutes/index.tsx @@ -1,6 +1,7 @@ import { ConfigProvider } from 'antd'; import getLocalStorageApi from 'api/browser/localstorage/get'; import setLocalStorageApi from 'api/browser/localstorage/set'; +import logEvent from 'api/common/logEvent'; import NotFound from 'components/NotFound'; import Spinner from 'components/Spinner'; import { FeatureKeys } from 'constants/features'; @@ -48,7 +49,7 @@ function App(): JSX.Element { const dispatch = useDispatch>(); - const { trackPageView, trackEvent } = useAnalytics(); + const { trackPageView } = useAnalytics(); const { hostname, pathname } = window.location; @@ -199,7 +200,7 @@ function App(): JSX.Element { LOCALSTORAGE.THEME_ANALYTICS_V1, ); if (!isThemeAnalyticsSent) { - trackEvent('Theme Analytics', { + logEvent('Theme Analytics', { theme: isDarkMode ? THEME_MODE.DARK : THEME_MODE.LIGHT, user: pick(user, ['email', 'userId', 'name']), org, diff --git a/frontend/src/container/BillingContainer/BillingContainer.tsx b/frontend/src/container/BillingContainer/BillingContainer.tsx index 248819723c..e366f068b2 100644 --- a/frontend/src/container/BillingContainer/BillingContainer.tsx +++ b/frontend/src/container/BillingContainer/BillingContainer.tsx @@ -19,10 +19,10 @@ import { ColumnsType } from 'antd/es/table'; import updateCreditCardApi from 'api/billing/checkout'; import getUsage, { UsageResponsePayloadProps } from 'api/billing/getUsage'; import manageCreditCardApi from 'api/billing/manage'; +import logEvent from 'api/common/logEvent'; import Spinner from 'components/Spinner'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import useAxiosError from 'hooks/useAxiosError'; import useLicense from 'hooks/useLicense'; import { useNotifications } from 'hooks/useNotifications'; @@ -137,8 +137,6 @@ export default function BillingContainer(): JSX.Element { Partial >({}); - const { trackEvent } = useAnalytics(); - const { isFetching, data: licensesData, error: licenseError } = useLicense(); const { user, org } = useSelector((state) => state.app); @@ -316,7 +314,7 @@ export default function BillingContainer(): JSX.Element { const handleBilling = useCallback(async () => { if (isFreeTrial && !licensesData?.payload?.trialConvertedToSubscription) { - trackEvent('Billing : Upgrade Plan', { + logEvent('Billing : Upgrade Plan', { user: pick(user, ['email', 'userId', 'name']), org, }); @@ -327,7 +325,7 @@ export default function BillingContainer(): JSX.Element { cancelURL: window.location.href, }); } else { - trackEvent('Billing : Manage Billing', { + logEvent('Billing : Manage Billing', { user: pick(user, ['email', 'userId', 'name']), org, }); diff --git a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx index d1f89b0762..3129d76c8e 100644 --- a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx +++ b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx @@ -11,7 +11,6 @@ import ROUTES from 'constants/routes'; import FullScreenHeader from 'container/FullScreenHeader/FullScreenHeader'; import InviteUserModal from 'container/OrganizationSettings/InviteUserModal/InviteUserModal'; import { InviteMemberFormValues } from 'container/OrganizationSettings/PendingInvitesContainer'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import history from 'lib/history'; import { UserPlus } from 'lucide-react'; import { useCallback, useEffect, useState } from 'react'; @@ -104,7 +103,6 @@ export default function Onboarding(): JSX.Element { const [selectedModuleSteps, setSelectedModuleSteps] = useState(APM_STEPS); const [activeStep, setActiveStep] = useState(1); const [current, setCurrent] = useState(0); - const { trackEvent } = useAnalytics(); const { location } = history; const { t } = useTranslation(['onboarding']); @@ -120,7 +118,7 @@ export default function Onboarding(): JSX.Element { } = useOnboardingContext(); useEffectOnce(() => { - trackEvent('Onboarding V2 Started'); + logEvent('Onboarding V2 Started', {}); }); const { status, data: ingestionData } = useQuery({ @@ -231,7 +229,7 @@ export default function Onboarding(): JSX.Element { const nextStep = activeStep + 1; // on next - trackEvent('Onboarding V2: Get Started', { + logEvent('Onboarding V2: Get Started', { selectedModule: selectedModule.id, nextStepId: nextStep, }); diff --git a/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx b/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx index 785e73d610..4cbdb39414 100644 --- a/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx +++ b/frontend/src/container/OnboardingContainer/Steps/ConnectionStatus/ConnectionStatus.tsx @@ -5,9 +5,9 @@ import { CloseCircleTwoTone, LoadingOutlined, } from '@ant-design/icons'; +import logEvent from 'api/common/logEvent'; import Header from 'container/OnboardingContainer/common/Header/Header'; import { useOnboardingContext } from 'container/OnboardingContainer/context/OnboardingContext'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { useQueryService } from 'hooks/useQueryService'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; @@ -41,8 +41,6 @@ export default function ConnectionStatus(): JSX.Element { [queries], ); - const { trackEvent } = useAnalytics(); - const [retryCount, setRetryCount] = useState(20); // Retry for 3 mins 20s const [loading, setLoading] = useState(true); const [isReceivingData, setIsReceivingData] = useState(false); @@ -155,7 +153,7 @@ export default function ConnectionStatus(): JSX.Element { if (data || isError) { setRetryCount(retryCount - 1); if (retryCount < 0) { - trackEvent('Onboarding V2: Connection Status', { + logEvent('Onboarding V2: Connection Status', { dataSource: selectedDataSource?.id, framework: selectedFramework, environment: selectedEnvironment, @@ -174,7 +172,7 @@ export default function ConnectionStatus(): JSX.Element { setLoading(false); setIsReceivingData(true); - trackEvent('Onboarding V2: Connection Status', { + logEvent('Onboarding V2: Connection Status', { dataSource: selectedDataSource?.id, framework: selectedFramework, environment: selectedEnvironment, diff --git a/frontend/src/container/OnboardingContainer/Steps/LogsConnectionStatus/LogsConnectionStatus.tsx b/frontend/src/container/OnboardingContainer/Steps/LogsConnectionStatus/LogsConnectionStatus.tsx index 9695721ef1..f418c4bb41 100644 --- a/frontend/src/container/OnboardingContainer/Steps/LogsConnectionStatus/LogsConnectionStatus.tsx +++ b/frontend/src/container/OnboardingContainer/Steps/LogsConnectionStatus/LogsConnectionStatus.tsx @@ -5,11 +5,11 @@ import { CloseCircleTwoTone, LoadingOutlined, } from '@ant-design/icons'; +import logEvent from 'api/common/logEvent'; import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { PANEL_TYPES } from 'constants/queryBuilder'; import Header from 'container/OnboardingContainer/common/Header/Header'; import { useOnboardingContext } from 'container/OnboardingContainer/context/OnboardingContext'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange'; import { useEffect, useState } from 'react'; import { SuccessResponse } from 'types/api'; @@ -32,7 +32,6 @@ export default function LogsConnectionStatus(): JSX.Element { activeStep, selectedEnvironment, } = useOnboardingContext(); - const { trackEvent } = useAnalytics(); const [isReceivingData, setIsReceivingData] = useState(false); const [pollingInterval, setPollingInterval] = useState(15000); // initial Polling interval of 15 secs , Set to false after 5 mins const [retryCount, setRetryCount] = useState(20); // Retry for 5 mins @@ -105,7 +104,7 @@ export default function LogsConnectionStatus(): JSX.Element { setRetryCount(retryCount - 1); if (retryCount < 0) { - trackEvent('Onboarding V2: Connection Status', { + logEvent('Onboarding V2: Connection Status', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, @@ -141,7 +140,7 @@ export default function LogsConnectionStatus(): JSX.Element { setRetryCount(-1); setPollingInterval(false); - trackEvent('Onboarding V2: Connection Status', { + logEvent('Onboarding V2: Connection Status', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, diff --git a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx index 28890e4d5a..ae74930d57 100644 --- a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx +++ b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx @@ -15,7 +15,6 @@ import ROUTES from 'constants/routes'; import { stepsMap } from 'container/OnboardingContainer/constants/stepsConfig'; import { DataSourceType } from 'container/OnboardingContainer/Steps/DataSource/DataSource'; import { hasFrameworks } from 'container/OnboardingContainer/utils/dataSourceUtils'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import history from 'lib/history'; import { isEmpty, isNull } from 'lodash-es'; import { HelpCircle, UserPlus } from 'lucide-react'; @@ -79,7 +78,6 @@ export default function ModuleStepsContainer({ } = useOnboardingContext(); const [current, setCurrent] = useState(0); - const { trackEvent } = useAnalytics(); const [metaData, setMetaData] = useState(defaultMetaData); const lastStepIndex = selectedModuleSteps.length - 1; @@ -143,7 +141,7 @@ export default function ModuleStepsContainer({ }; const redirectToModules = (): void => { - trackEvent('Onboarding V2 Complete', { + logEvent('Onboarding V2 Complete', { module: selectedModule.id, dataSource: selectedDataSource?.id, framework: selectedFramework, @@ -186,14 +184,14 @@ export default function ModuleStepsContainer({ // on next step click track events switch (selectedModuleSteps[current].id) { case stepsMap.dataSource: - trackEvent('Onboarding V2: Data Source Selected', { + logEvent('Onboarding V2: Data Source Selected', { dataSource: selectedDataSource?.id, framework: selectedFramework, module: activeStep?.module?.id, }); break; case stepsMap.environmentDetails: - trackEvent('Onboarding V2: Environment Selected', { + logEvent('Onboarding V2: Environment Selected', { dataSource: selectedDataSource?.id, framework: selectedFramework, environment: selectedEnvironment, @@ -201,7 +199,7 @@ export default function ModuleStepsContainer({ }); break; case stepsMap.selectMethod: - trackEvent('Onboarding V2: Method Selected', { + logEvent('Onboarding V2: Method Selected', { dataSource: selectedDataSource?.id, framework: selectedFramework, environment: selectedEnvironment, @@ -211,7 +209,7 @@ export default function ModuleStepsContainer({ break; case stepsMap.setupOtelCollector: - trackEvent('Onboarding V2: Setup Otel Collector', { + logEvent('Onboarding V2: Setup Otel Collector', { dataSource: selectedDataSource?.id, framework: selectedFramework, environment: selectedEnvironment, @@ -220,7 +218,7 @@ export default function ModuleStepsContainer({ }); break; case stepsMap.instrumentApplication: - trackEvent('Onboarding V2: Instrument Application', { + logEvent('Onboarding V2: Instrument Application', { dataSource: selectedDataSource?.id, framework: selectedFramework, environment: selectedEnvironment, @@ -229,13 +227,13 @@ export default function ModuleStepsContainer({ }); break; case stepsMap.cloneRepository: - trackEvent('Onboarding V2: Clone Repository', { + logEvent('Onboarding V2: Clone Repository', { dataSource: selectedDataSource?.id, module: activeStep?.module?.id, }); break; case stepsMap.runApplication: - trackEvent('Onboarding V2: Run Application', { + logEvent('Onboarding V2: Run Application', { dataSource: selectedDataSource?.id, framework: selectedFramework, environment: selectedEnvironment, @@ -244,95 +242,95 @@ export default function ModuleStepsContainer({ }); break; case stepsMap.addHttpDrain: - trackEvent('Onboarding V2: Add HTTP Drain', { + logEvent('Onboarding V2: Add HTTP Drain', { dataSource: selectedDataSource?.id, module: activeStep?.module?.id, }); break; case stepsMap.startContainer: - trackEvent('Onboarding V2: Start Container', { + logEvent('Onboarding V2: Start Container', { dataSource: selectedDataSource?.id, module: activeStep?.module?.id, }); break; case stepsMap.setupLogDrains: - trackEvent('Onboarding V2: Setup Log Drains', { + logEvent('Onboarding V2: Setup Log Drains', { dataSource: selectedDataSource?.id, module: activeStep?.module?.id, }); break; case stepsMap.configureReceiver: - trackEvent('Onboarding V2: Configure Receiver', { + logEvent('Onboarding V2: Configure Receiver', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.configureAws: - trackEvent('Onboarding V2: Configure AWS', { + logEvent('Onboarding V2: Configure AWS', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.sendLogsCloudwatch: - trackEvent('Onboarding V2: Send Logs Cloudwatch', { + logEvent('Onboarding V2: Send Logs Cloudwatch', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.setupDaemonService: - trackEvent('Onboarding V2: Setup ECS Daemon Service', { + logEvent('Onboarding V2: Setup ECS Daemon Service', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.createOtelConfig: - trackEvent('Onboarding V2: Create ECS OTel Config', { + logEvent('Onboarding V2: Create ECS OTel Config', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.createDaemonService: - trackEvent('Onboarding V2: Create ECS Daemon Service', { + logEvent('Onboarding V2: Create ECS Daemon Service', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.ecsSendData: - trackEvent('Onboarding V2: ECS send traces data', { + logEvent('Onboarding V2: ECS send traces data', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.createSidecarCollectorContainer: - trackEvent('Onboarding V2: ECS create Sidecar Container', { + logEvent('Onboarding V2: ECS create Sidecar Container', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.deployTaskDefinition: - trackEvent('Onboarding V2: ECS deploy task definition', { + logEvent('Onboarding V2: ECS deploy task definition', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.ecsSendLogsData: - trackEvent('Onboarding V2: ECS Fargate send logs data', { + logEvent('Onboarding V2: ECS Fargate send logs data', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, }); break; case stepsMap.monitorDashboard: - trackEvent('Onboarding V2: EKS monitor dashboard', { + logEvent('Onboarding V2: EKS monitor dashboard', { dataSource: selectedDataSource?.id, environment: selectedEnvironment, module: activeStep?.module?.id, diff --git a/frontend/src/container/PipelinePage/Layouts/Pipeline/CreatePipelineButton.tsx b/frontend/src/container/PipelinePage/Layouts/Pipeline/CreatePipelineButton.tsx index 23eaf335fe..e703e46a7e 100644 --- a/frontend/src/container/PipelinePage/Layouts/Pipeline/CreatePipelineButton.tsx +++ b/frontend/src/container/PipelinePage/Layouts/Pipeline/CreatePipelineButton.tsx @@ -1,6 +1,6 @@ import { EditFilled, PlusOutlined } from '@ant-design/icons'; +import logEvent from 'api/common/logEvent'; import TextToolTip from 'components/TextToolTip'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { ActionMode, ActionType, Pipeline } from 'types/api/pipeline/def'; @@ -15,7 +15,6 @@ function CreatePipelineButton({ pipelineData, }: CreatePipelineButtonProps): JSX.Element { const { t } = useTranslation(['pipeline']); - const { trackEvent } = useAnalytics(); const isAddNewPipelineVisible = useMemo( () => checkDataLength(pipelineData?.pipelines), @@ -26,7 +25,7 @@ function CreatePipelineButton({ const onEnterEditMode = (): void => { setActionMode(ActionMode.Editing); - trackEvent('Logs: Pipelines: Entered Edit Mode', { + logEvent('Logs: Pipelines: Entered Edit Mode', { source: 'signoz-ui', }); }; @@ -34,7 +33,7 @@ function CreatePipelineButton({ setActionMode(ActionMode.Editing); setActionType(ActionType.AddPipeline); - trackEvent('Logs: Pipelines: Clicked Add New Pipeline', { + logEvent('Logs: Pipelines: Clicked Add New Pipeline', { source: 'signoz-ui', }); }; diff --git a/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx b/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx index 71a2ed9014..17761ad99f 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/PipelineExpandView.tsx @@ -1,6 +1,6 @@ import { PlusCircleOutlined } from '@ant-design/icons'; import { TableLocale } from 'antd/es/table/interface'; -import useAnalytics from 'hooks/analytics/useAnalytics'; +import logEvent from 'api/common/logEvent'; import { useIsDarkMode } from 'hooks/useDarkMode'; import React, { useCallback, useMemo } from 'react'; import { DndProvider } from 'react-dnd'; @@ -39,7 +39,6 @@ function PipelineExpandView({ }: PipelineExpandViewProps): JSX.Element { const { t } = useTranslation(['pipeline']); const isDarkMode = useIsDarkMode(); - const { trackEvent } = useAnalytics(); const isEditingActionMode = isActionMode === ActionMode.Editing; const deleteProcessorHandler = useCallback( @@ -192,7 +191,7 @@ function PipelineExpandView({ const addNewProcessorHandler = useCallback((): void => { setActionType(ActionType.AddProcessor); - trackEvent('Logs: Pipelines: Clicked Add New Processor', { + logEvent('Logs: Pipelines: Clicked Add New Processor', { source: 'signoz-ui', }); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx index 4e8def2e0c..aa39c9e2aa 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/PipelineListsView.tsx @@ -5,7 +5,6 @@ import { Card, Modal, Table, Typography } from 'antd'; import { ExpandableConfig } from 'antd/es/table/interface'; import logEvent from 'api/common/logEvent'; import savePipeline from 'api/pipeline/post'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { useNotifications } from 'hooks/useNotifications'; import { isUndefined } from 'lodash-es'; import cloneDeep from 'lodash-es/cloneDeep'; @@ -100,7 +99,6 @@ function PipelineListsView({ const [modal, contextHolder] = Modal.useModal(); const { notifications } = useNotifications(); const [pipelineSearchValue, setPipelineSearchValue] = useState(''); - const { trackEvent } = useAnalytics(); const [prevPipelineData, setPrevPipelineData] = useState>( cloneDeep(pipelineData?.pipelines || []), ); @@ -376,7 +374,7 @@ function PipelineListsView({ const addNewPipelineHandler = useCallback((): void => { setActionType(ActionType.AddPipeline); - trackEvent('Logs: Pipelines: Clicked Add New Pipeline', { + logEvent('Logs: Pipelines: Clicked Add New Pipeline', { source: 'signoz-ui', }); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -415,7 +413,7 @@ function PipelineListsView({ setCurrPipelineData(pipelinesInDB); setPrevPipelineData(pipelinesInDB); - trackEvent('Logs: Pipelines: Saved Pipelines', { + logEvent('Logs: Pipelines: Saved Pipelines', { count: pipelinesInDB.length, enabled: pipelinesInDB.filter((p) => p.enabled).length, source: 'signoz-ui', diff --git a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx index d4796b3609..8aaa4a092b 100644 --- a/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx +++ b/frontend/src/container/PipelinePage/PipelineListsView/TableComponents/PipelineActions/components/PreviewAction.tsx @@ -1,15 +1,13 @@ import { EyeFilled } from '@ant-design/icons'; import { Divider, Modal } from 'antd'; +import logEvent from 'api/common/logEvent'; import PipelineProcessingPreview from 'container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { useState } from 'react'; import { PipelineData } from 'types/api/pipeline/def'; import { iconStyle } from '../../../config'; function PreviewAction({ pipeline }: PreviewActionProps): JSX.Element | null { - const { trackEvent } = useAnalytics(); - const [previewKey, setPreviewKey] = useState(null); const isModalOpen = Boolean(previewKey); @@ -23,7 +21,7 @@ function PreviewAction({ pipeline }: PreviewActionProps): JSX.Element | null { const onOpenPreview = (): void => { openModal(); - trackEvent('Logs: Pipelines: Clicked Preview Pipeline', { + logEvent('Logs: Pipelines: Clicked Preview Pipeline', { source: 'signoz-ui', }); }; diff --git a/frontend/src/container/PipelinePage/tests/CreatePipelineButton.test.tsx b/frontend/src/container/PipelinePage/tests/CreatePipelineButton.test.tsx index 49d0a132ea..33ec54e3a0 100644 --- a/frontend/src/container/PipelinePage/tests/CreatePipelineButton.test.tsx +++ b/frontend/src/container/PipelinePage/tests/CreatePipelineButton.test.tsx @@ -1,5 +1,6 @@ import { render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import logEvent from 'api/common/logEvent'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; import { MemoryRouter } from 'react-router-dom'; @@ -9,14 +10,7 @@ import store from 'store'; import CreatePipelineButton from '../Layouts/Pipeline/CreatePipelineButton'; import { pipelineApiResponseMockData } from '../mocks/pipeline'; -const trackEventVar = jest.fn(); -jest.mock('hooks/analytics/useAnalytics', () => ({ - __esModule: true, - default: jest.fn().mockImplementation(() => ({ - trackEvent: trackEventVar, - trackPageView: jest.fn(), - })), -})); +jest.mock('api/common/logEvent'); describe('PipelinePage container test', () => { it('should render CreatePipelineButton section', async () => { @@ -58,7 +52,7 @@ describe('PipelinePage container test', () => { expect(editButton).toBeInTheDocument(); await userEvent.click(editButton); - expect(trackEventVar).toBeCalledWith('Logs: Pipelines: Entered Edit Mode', { + expect(logEvent).toBeCalledWith('Logs: Pipelines: Entered Edit Mode', { source: 'signoz-ui', }); }); @@ -83,11 +77,8 @@ describe('PipelinePage container test', () => { expect(editButton).toBeInTheDocument(); await userEvent.click(editButton); - expect(trackEventVar).toBeCalledWith( - 'Logs: Pipelines: Clicked Add New Pipeline', - { - source: 'signoz-ui', - }, - ); + expect(logEvent).toBeCalledWith('Logs: Pipelines: Clicked Add New Pipeline', { + source: 'signoz-ui', + }); }); }); diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/Configure.tsx b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/Configure.tsx index f645653883..07955018bf 100644 --- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/Configure.tsx +++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailContentTabs/Configure.tsx @@ -4,7 +4,6 @@ import { Button, Typography } from 'antd'; import logEvent from 'api/common/logEvent'; import cx from 'classnames'; import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { INTEGRATION_TELEMETRY_EVENTS } from 'pages/Integrations/utils'; import { useEffect, useState } from 'react'; @@ -18,8 +17,6 @@ function Configure(props: ConfigurationProps): JSX.Element { const { configuration, integrationId } = props; const [selectedConfigStep, setSelectedConfigStep] = useState(0); - const { trackEvent } = useAnalytics(); - const handleMenuClick = (index: number, config: any): void => { setSelectedConfigStep(index); logEvent('Integrations Detail Page: Configure tab', { @@ -29,7 +26,7 @@ function Configure(props: ConfigurationProps): JSX.Element { }; useEffect(() => { - trackEvent( + logEvent( INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_CONFIGURE_INSTRUCTION, { integration: integrationId, diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx index 6d15fc8694..97ac3e11c8 100644 --- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx +++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailHeader.tsx @@ -2,12 +2,12 @@ import './IntegrationDetailPage.styles.scss'; import { Button, Modal, Tooltip, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import installIntegration from 'api/Integrations/installIntegration'; import ConfigureIcon from 'assets/Integrations/ConfigureIcon'; import cx from 'classnames'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import dayjs from 'dayjs'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { useNotifications } from 'hooks/useNotifications'; import { ArrowLeftRight, Check } from 'lucide-react'; import { useState } from 'react'; @@ -43,8 +43,6 @@ function IntegrationDetailHeader( } = props; const [isModalOpen, setIsModalOpen] = useState(false); - const { trackEvent } = useAnalytics(); - const { notifications } = useNotifications(); const showModal = (): void => { @@ -137,11 +135,11 @@ function IntegrationDetailHeader( disabled={isInstallLoading} onClick={(): void => { if (connectionState === ConnectionStates.NotInstalled) { - trackEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_CONNECT, { + logEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_CONNECT, { integration: id, }); } else { - trackEvent( + logEvent( INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_TEST_CONNECTION, { integration: id, diff --git a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx index a1ad762ec6..557327a19b 100644 --- a/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx +++ b/frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationsUninstallBar.tsx @@ -1,9 +1,9 @@ import './IntegrationDetailPage.styles.scss'; import { Button, Modal, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import unInstallIntegration from 'api/Integrations/uninstallIntegration'; import { SOMETHING_WENT_WRONG } from 'constants/api'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import { useNotifications } from 'hooks/useNotifications'; import { X } from 'lucide-react'; import { useState } from 'react'; @@ -30,8 +30,6 @@ function IntergrationsUninstallBar( const { notifications } = useNotifications(); const [isModalOpen, setIsModalOpen] = useState(false); - const { trackEvent } = useAnalytics(); - const { mutate: uninstallIntegration, isLoading: isUninstallLoading, @@ -52,7 +50,7 @@ function IntergrationsUninstallBar( }; const handleOk = (): void => { - trackEvent( + logEvent( INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_DETAIL_REMOVE_INTEGRATION, { integration: integrationId, diff --git a/frontend/src/pages/Integrations/Integrations.tsx b/frontend/src/pages/Integrations/Integrations.tsx index 8fdc943f77..d8bd318ae5 100644 --- a/frontend/src/pages/Integrations/Integrations.tsx +++ b/frontend/src/pages/Integrations/Integrations.tsx @@ -1,6 +1,6 @@ import './Integrations.styles.scss'; -import useAnalytics from 'hooks/analytics/useAnalytics'; +import logEvent from 'api/common/logEvent'; import useUrlQuery from 'hooks/useUrlQuery'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; @@ -16,8 +16,6 @@ function Integrations(): JSX.Element { const history = useHistory(); const location = useLocation(); - const { trackEvent } = useAnalytics(); - const selectedIntegration = useMemo(() => urlQuery.get('integration'), [ urlQuery, ]); @@ -25,7 +23,7 @@ function Integrations(): JSX.Element { const setSelectedIntegration = useCallback( (integration: string | null) => { if (integration) { - trackEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_ITEM_LIST_CLICKED, { + logEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_ITEM_LIST_CLICKED, { integration, }); urlQuery.set('integration', integration); @@ -35,7 +33,7 @@ function Integrations(): JSX.Element { const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; history.push(generatedUrl); }, - [history, location.pathname, trackEvent, urlQuery], + [history, location.pathname, urlQuery], ); const [activeDetailTab, setActiveDetailTab] = useState( @@ -43,7 +41,7 @@ function Integrations(): JSX.Element { ); useEffect(() => { - trackEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_LIST_VISITED); + logEvent(INTEGRATION_TELEMETRY_EVENTS.INTEGRATIONS_LIST_VISITED, {}); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/frontend/src/pages/SignUp/SignUp.tsx b/frontend/src/pages/SignUp/SignUp.tsx index 35cca11603..84329a1626 100644 --- a/frontend/src/pages/SignUp/SignUp.tsx +++ b/frontend/src/pages/SignUp/SignUp.tsx @@ -1,4 +1,5 @@ import { Button, Form, Input, Space, Switch, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; import editOrg from 'api/user/editOrg'; import getInviteDetails from 'api/user/getInviteDetails'; import loginApi from 'api/user/login'; @@ -7,7 +8,6 @@ import afterLogin from 'AppRoutes/utils'; import WelcomeLeftContainer from 'components/WelcomeLeftContainer'; import { FeatureKeys } from 'constants/features'; import ROUTES from 'constants/routes'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import useFeatureFlag from 'hooks/useFeatureFlag'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; @@ -57,7 +57,6 @@ function SignUp({ version }: SignUpProps): JSX.Element { false, ); const { search } = useLocation(); - const { trackEvent } = useAnalytics(); const params = new URLSearchParams(search); const token = params.get('token'); const [isDetailsDisable, setIsDetailsDisable] = useState(false); @@ -88,7 +87,7 @@ function SignUp({ version }: SignUpProps): JSX.Element { form.setFieldValue('organizationName', responseDetails.organization); setIsDetailsDisable(true); - trackEvent('Account Creation Page Visited', { + logEvent('Account Creation Page Visited', { email: responseDetails.email, name: responseDetails.name, company_name: responseDetails.organization, @@ -241,7 +240,7 @@ function SignUp({ version }: SignUpProps): JSX.Element { setLoading(true); if (!isPasswordValid(values.password)) { - trackEvent('Account Creation Page - Invalid Password', { + logEvent('Account Creation Page - Invalid Password', { email: values.email, name: values.firstName, }); @@ -253,7 +252,7 @@ function SignUp({ version }: SignUpProps): JSX.Element { if (isPreferenceVisible) { await commonHandler(values, onAdminAfterLogin); } else { - trackEvent('Account Created Successfully', { + logEvent('Account Created Successfully', { email: values.email, name: values.firstName, }); diff --git a/frontend/src/pages/Support/Support.tsx b/frontend/src/pages/Support/Support.tsx index 2d156a3816..08a8b4602a 100644 --- a/frontend/src/pages/Support/Support.tsx +++ b/frontend/src/pages/Support/Support.tsx @@ -1,7 +1,7 @@ import './Support.styles.scss'; import { Button, Card, Typography } from 'antd'; -import useAnalytics from 'hooks/analytics/useAnalytics'; +import logEvent from 'api/common/logEvent'; import { Book, Cable, @@ -85,7 +85,6 @@ const supportChannels = [ ]; export default function Support(): JSX.Element { - const { trackEvent } = useAnalytics(); const history = useHistory(); const handleChannelWithRedirects = (url: string): void => { @@ -97,7 +96,7 @@ export default function Support(): JSX.Element { const histroyState = history?.location?.state as any; if (histroyState && histroyState?.from) { - trackEvent(`Support : From URL : ${histroyState.from}`); + logEvent(`Support : From URL : ${histroyState.from}`, {}); } } @@ -129,7 +128,7 @@ export default function Support(): JSX.Element { }; const handleChannelClick = (channel: Channel): void => { - trackEvent(`Support : ${channel.name}`); + logEvent(`Support : ${channel.name}`, {}); switch (channel.key) { case channelsMap.documentation: diff --git a/frontend/src/pages/WorkspaceLocked/WorkspaceLocked.tsx b/frontend/src/pages/WorkspaceLocked/WorkspaceLocked.tsx index 4dcc055235..0cc3990af7 100644 --- a/frontend/src/pages/WorkspaceLocked/WorkspaceLocked.tsx +++ b/frontend/src/pages/WorkspaceLocked/WorkspaceLocked.tsx @@ -8,10 +8,10 @@ import { } from '@ant-design/icons'; import { Button, Card, Skeleton, Typography } from 'antd'; import updateCreditCardApi from 'api/billing/checkout'; +import logEvent from 'api/common/logEvent'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import ROUTES from 'constants/routes'; import FullScreenHeader from 'container/FullScreenHeader/FullScreenHeader'; -import useAnalytics from 'hooks/analytics/useAnalytics'; import useLicense from 'hooks/useLicense'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; @@ -27,7 +27,6 @@ export default function WorkspaceBlocked(): JSX.Element { const { role } = useSelector((state) => state.app); const isAdmin = role === 'ADMIN'; const [activeLicense, setActiveLicense] = useState(null); - const { trackEvent } = useAnalytics(); const { notifications } = useNotifications(); @@ -74,7 +73,7 @@ export default function WorkspaceBlocked(): JSX.Element { ); const handleUpdateCreditCard = useCallback(async () => { - trackEvent('Workspace Blocked: User Clicked Update Credit Card'); + logEvent('Workspace Blocked: User Clicked Update Credit Card', {}); updateCreditCard({ licenseKey: activeLicense?.key || '', @@ -85,7 +84,7 @@ export default function WorkspaceBlocked(): JSX.Element { }, [activeLicense?.key, updateCreditCard]); const handleExtendTrial = (): void => { - trackEvent('Workspace Blocked: User Clicked Extend Trial'); + logEvent('Workspace Blocked: User Clicked Extend Trial', {}); notifications.info({ message: 'Extend Trial', From d50530f58c500dfa22e8cc4d644ebf136c24e7a9 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 23 Jul 2024 17:20:31 +0530 Subject: [PATCH 115/281] fix: retain the step interval while creating alerts from the dashboard panel (#5455) * fix: use the same step interval as in the dashboard query while creating alerts from panel * chore: added extra safety checks * chore: add test cases for the mapQueryDataFromAPI utils * chore: added functions test cases as well --------- Co-authored-by: Srikanth Chekuri --- .../hooks/queryBuilder/useCreateAlerts.tsx | 5 +- .../__tests__/mapQueryDataFromApi.test.tsx | 56 ++ .../__tests__/mapQueryDataFromApiInputs.ts | 741 ++++++++++++++++++ .../mapQueryDataFromApi.ts | 6 +- .../transformQueryBuilderDataModel.ts | 35 +- 5 files changed, 838 insertions(+), 5 deletions(-) create mode 100644 frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApi.test.tsx create mode 100644 frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApiInputs.ts diff --git a/frontend/src/hooks/queryBuilder/useCreateAlerts.tsx b/frontend/src/hooks/queryBuilder/useCreateAlerts.tsx index 0c0d2b0425..c1a87bfc46 100644 --- a/frontend/src/hooks/queryBuilder/useCreateAlerts.tsx +++ b/frontend/src/hooks/queryBuilder/useCreateAlerts.tsx @@ -61,7 +61,10 @@ const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => { }); queryRangeMutation.mutate(queryPayload, { onSuccess: (data) => { - const updatedQuery = mapQueryDataFromApi(data.compositeQuery); + const updatedQuery = mapQueryDataFromApi( + data.compositeQuery, + widget?.query, + ); history.push( `${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent( diff --git a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApi.test.tsx b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApi.test.tsx new file mode 100644 index 0000000000..2a72ab5d10 --- /dev/null +++ b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApi.test.tsx @@ -0,0 +1,56 @@ +import { mapQueryDataFromApi } from '../mapQueryDataFromApi'; +import { + compositeQueriesWithFunctions, + compositeQueryWithoutVariables, + compositeQueryWithVariables, + defaultOutput, + outputWithFunctions, + replaceVariables, + stepIntervalUnchanged, + widgetQueriesWithFunctions, + widgetQueryWithoutVariables, + widgetQueryWithVariables, +} from './mapQueryDataFromApiInputs'; + +jest.mock('uuid', () => ({ + v4: (): string => 'test-id', +})); + +describe('mapQueryDataFromApi function tests', () => { + it('should not update the step interval when query is passed', () => { + const output = mapQueryDataFromApi( + compositeQueryWithoutVariables, + widgetQueryWithoutVariables, + ); + + // composite query is the response from the `v3/query_range/format` API call. + // even if the composite query returns stepInterval updated do not modify it + expect(output).toStrictEqual(stepIntervalUnchanged); + }); + + it('should update filter from the composite query', () => { + const output = mapQueryDataFromApi( + compositeQueryWithVariables, + widgetQueryWithVariables, + ); + + // replace the variables in the widget query and leave the rest items untouched + expect(output).toStrictEqual(replaceVariables); + }); + + it('should not update the step intervals with multiple queries and functions', () => { + const output = mapQueryDataFromApi( + compositeQueriesWithFunctions, + widgetQueriesWithFunctions, + ); + + expect(output).toStrictEqual(outputWithFunctions); + }); + + it('should use the default query values and the compositeQuery object when query is not passed', () => { + const output = mapQueryDataFromApi(compositeQueryWithoutVariables); + + // when the query object is not passed take the initial values and merge the composite query on top of it + expect(output).toStrictEqual(defaultOutput); + }); +}); diff --git a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApiInputs.ts b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApiInputs.ts new file mode 100644 index 0000000000..00d054e9bf --- /dev/null +++ b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/__tests__/mapQueryDataFromApiInputs.ts @@ -0,0 +1,741 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +import { PANEL_TYPES } from 'constants/queryBuilder'; +import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery'; +import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import { EQueryType } from 'types/common/dashboard'; +import { DataSource } from 'types/common/queryBuilder'; + +export const compositeQueryWithoutVariables = ({ + builderQueries: { + A: { + queryName: 'A', + stepInterval: 240, + dataSource: DataSource.METRICS, + aggregateOperator: 'rate', + aggregateAttribute: { + key: 'system_disk_operations', + dataType: DataTypes.Float64, + type: 'Sum', + isColumn: true, + isJSON: false, + }, + filters: { + op: 'AND', + items: [], + }, + expression: 'A', + disabled: false, + limit: 0, + offset: 0, + pageSize: 0, + reduceTo: 'avg', + timeAggregation: 'rate', + spaceAggregation: 'sum', + ShiftBy: 0, + }, + }, + panelType: PANEL_TYPES.TIME_SERIES, + queryType: EQueryType.QUERY_BUILDER, +} as unknown) as ICompositeMetricQuery; + +export const widgetQueryWithoutVariables = ({ + clickhouse_sql: [ + { + name: 'A', + legend: '', + disabled: false, + query: '', + }, + ], + promql: [ + { + name: 'A', + query: '', + legend: '', + disabled: false, + }, + ], + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'rate', + aggregateAttribute: { + key: 'system_disk_operations', + dataType: 'float64', + type: 'Sum', + isColumn: true, + isJSON: false, + id: 'system_disk_operations--float64--Sum--true', + }, + timeAggregation: 'rate', + spaceAggregation: 'sum', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + ], + queryFormulas: [], + }, + id: '2bbbd8d8-db99-40be-b9c6-9e197c5bc537', + queryType: 'builder', +} as unknown) as Query; + +export const stepIntervalUnchanged = { + builder: { + queryData: [ + { + aggregateAttribute: { + dataType: 'float64', + id: 'system_disk_operations--float64--Sum--true', + isColumn: true, + isJSON: false, + key: 'system_disk_operations', + type: 'Sum', + }, + aggregateOperator: 'rate', + dataSource: 'metrics', + disabled: false, + expression: 'A', + 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: 'test-id', + promql: [ + { + disabled: false, + legend: '', + name: 'A', + query: '', + }, + ], + queryType: 'builder', + unit: undefined, +}; + +export const compositeQueryWithVariables = ({ + builderQueries: { + A: { + queryName: 'A', + stepInterval: 240, + dataSource: 'metrics', + aggregateOperator: 'sum_rate', + aggregateAttribute: { + key: 'signoz_calls_total', + dataType: 'float64', + type: '', + isColumn: true, + isJSON: false, + }, + filters: { + op: 'AND', + items: [ + { + key: { + key: 'deployment_environment', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + }, + value: 'default', + op: 'in', + }, + { + key: { + key: 'service_name', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + }, + value: 'frontend', + op: 'in', + }, + { + key: { + key: 'operation', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + }, + value: 'HTTP GET /dispatch', + op: 'in', + }, + ], + }, + groupBy: [ + { + key: 'service_name', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + }, + { + key: 'operation', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + }, + ], + expression: 'A', + disabled: false, + legend: '{{service_name}}-{{operation}}', + limit: 0, + offset: 0, + pageSize: 0, + reduceTo: 'sum', + timeAggregation: 'rate', + spaceAggregation: 'sum', + ShiftBy: 0, + }, + }, + panelType: 'graph', + queryType: 'builder', +} as unknown) as ICompositeMetricQuery; + +export const widgetQueryWithVariables = ({ + clickhouse_sql: [ + { + name: 'A', + legend: '', + disabled: false, + query: '', + }, + ], + promql: [ + { + name: 'A', + query: '', + legend: '', + disabled: false, + }, + ], + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'sum_rate', + aggregateAttribute: { + dataType: 'float64', + id: 'signoz_calls_total--float64----true', + isColumn: true, + isJSON: false, + key: 'signoz_calls_total', + type: '', + }, + timeAggregation: 'rate', + spaceAggregation: 'sum', + functions: [], + filters: { + items: [ + { + id: 'aa56621e', + key: { + dataType: 'string', + id: 'deployment_environment--string--tag--false', + isColumn: false, + isJSON: false, + key: 'deployment_environment', + type: 'tag', + }, + op: 'in', + value: ['{{.deployment_environment}}'], + }, + { + id: '97055a02', + key: { + dataType: 'string', + id: 'service_name--string--tag--false', + isColumn: false, + isJSON: false, + key: 'service_name', + type: 'tag', + }, + op: 'in', + value: ['{{.service_name}}'], + }, + { + id: '8c4599f2', + key: { + dataType: 'string', + id: 'operation--string--tag--false', + isColumn: false, + isJSON: false, + key: 'operation', + type: 'tag', + }, + op: 'in', + value: ['{{.endpoint}}'], + }, + ], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [ + { + dataType: 'string', + isColumn: false, + isJSON: false, + key: 'service_name', + type: 'tag', + id: 'service_name--string--tag--false', + }, + { + dataType: 'string', + isColumn: false, + isJSON: false, + key: 'operation', + type: 'tag', + id: 'operation--string--tag--false', + }, + ], + legend: '{{service_name}}-{{operation}}', + reduceTo: 'sum', + }, + ], + queryFormulas: [], + }, + id: '64fcd7be-61d0-4f92-bbb2-1449b089f766', + queryType: 'builder', +} as unknown) as Query; + +export const replaceVariables = { + builder: { + queryData: [ + { + aggregateAttribute: { + dataType: 'float64', + id: 'signoz_calls_total--float64----true', + isColumn: true, + isJSON: false, + key: 'signoz_calls_total', + type: '', + }, + aggregateOperator: 'sum_rate', + dataSource: 'metrics', + disabled: false, + expression: 'A', + filters: { + items: [ + { + key: { + dataType: 'string', + isColumn: false, + isJSON: false, + key: 'deployment_environment', + type: 'tag', + }, + op: 'in', + value: 'default', + }, + { + key: { + dataType: 'string', + isColumn: false, + isJSON: false, + key: 'service_name', + type: 'tag', + }, + op: 'in', + value: 'frontend', + }, + { + key: { + dataType: 'string', + isColumn: false, + isJSON: false, + key: 'operation', + type: 'tag', + }, + op: 'in', + value: 'HTTP GET /dispatch', + }, + ], + op: 'AND', + }, + functions: [], + groupBy: [ + { + dataType: 'string', + id: 'service_name--string--tag--false', + isColumn: false, + isJSON: false, + key: 'service_name', + type: 'tag', + }, + { + dataType: 'string', + id: 'operation--string--tag--false', + isColumn: false, + isJSON: false, + key: 'operation', + type: 'tag', + }, + ], + having: [], + legend: '{{service_name}}-{{operation}}', + limit: null, + orderBy: [], + queryName: 'A', + reduceTo: 'sum', + spaceAggregation: 'sum', + stepInterval: 60, + timeAggregation: 'rate', + }, + ], + queryFormulas: [], + }, + clickhouse_sql: [ + { + disabled: false, + legend: '', + name: 'A', + query: '', + }, + ], + id: 'test-id', + promql: [ + { + disabled: false, + legend: '', + name: 'A', + query: '', + }, + ], + queryType: 'builder', + unit: undefined, +}; + +export const defaultOutput = { + builder: { + queryData: [ + { + ShiftBy: 0, + aggregateAttribute: { + dataType: 'float64', + isColumn: true, + isJSON: false, + key: 'system_disk_operations', + type: 'Sum', + }, + aggregateOperator: 'rate', + dataSource: 'metrics', + disabled: false, + expression: 'A', + filters: { items: [], op: 'AND' }, + functions: [], + groupBy: [], + having: [], + legend: '', + limit: 0, + offset: 0, + orderBy: [], + pageSize: 0, + queryName: 'A', + reduceTo: 'avg', + spaceAggregation: 'sum', + stepInterval: 240, + timeAggregation: 'rate', + }, + ], + queryFormulas: [], + }, + clickhouse_sql: [{ disabled: false, legend: '', name: 'A', query: '' }], + id: 'test-id', + promql: [{ disabled: false, legend: '', name: 'A', query: '' }], + queryType: 'builder', + unit: undefined, +}; + +export const compositeQueriesWithFunctions = ({ + builderQueries: { + A: { + queryName: 'A', + stepInterval: 60, + dataSource: 'metrics', + aggregateOperator: 'count', + aggregateAttribute: { + key: 'signoz_latency_bucket', + dataType: 'float64', + type: 'Histogram', + isColumn: true, + isJSON: false, + }, + filters: { + op: 'AND', + items: [], + }, + expression: 'A', + disabled: false, + limit: 0, + offset: 0, + pageSize: 0, + reduceTo: 'avg', + spaceAggregation: 'p90', + ShiftBy: 0, + }, + B: { + queryName: 'B', + stepInterval: 120, + dataSource: 'metrics', + aggregateOperator: 'rate', + aggregateAttribute: { + key: 'system_disk_io', + dataType: 'float64', + type: 'Sum', + isColumn: true, + isJSON: false, + }, + filters: { + op: 'AND', + items: [], + }, + expression: 'B', + disabled: false, + limit: 0, + offset: 0, + pageSize: 0, + reduceTo: 'avg', + timeAggregation: 'rate', + spaceAggregation: 'sum', + ShiftBy: 0, + }, + F1: { + queryName: 'F1', + stepInterval: 1, + dataSource: '', + aggregateOperator: '', + aggregateAttribute: { + key: '', + dataType: '', + type: '', + isColumn: false, + isJSON: false, + }, + expression: 'A / B ', + disabled: false, + limit: 0, + offset: 0, + pageSize: 0, + ShiftBy: 0, + }, + }, + panelType: 'graph', + queryType: 'builder', +} as unknown) as ICompositeMetricQuery; + +export const widgetQueriesWithFunctions = ({ + clickhouse_sql: [ + { + name: 'A', + legend: '', + disabled: false, + query: '', + }, + ], + promql: [ + { + name: 'A', + query: '', + legend: '', + disabled: false, + }, + ], + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + dataType: 'float64', + id: 'signoz_latency_bucket--float64--Histogram--true', + isColumn: true, + isJSON: false, + key: 'signoz_latency_bucket', + type: 'Histogram', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 120, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + { + dataSource: 'metrics', + queryName: 'B', + aggregateOperator: 'rate', + aggregateAttribute: { + key: 'system_disk_io', + dataType: 'float64', + type: 'Sum', + isColumn: true, + isJSON: false, + id: 'system_disk_io--float64--Sum--true', + }, + timeAggregation: 'rate', + spaceAggregation: 'sum', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'B', + disabled: false, + stepInterval: 120, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + ], + queryFormulas: [ + { + queryName: 'F1', + expression: 'A / B ', + disabled: false, + legend: '', + }, + ], + }, + id: '5d1844fe-9b44-4f15-b6fe-f1b843550b77', + queryType: 'builder', +} as unknown) as Query; + +export const outputWithFunctions = { + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + dataType: 'float64', + id: 'signoz_latency_bucket--float64--Histogram--true', + isColumn: true, + isJSON: false, + key: 'signoz_latency_bucket', + type: 'Histogram', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + op: 'AND', + items: [], + }, + expression: 'A', + disabled: false, + stepInterval: 120, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + { + dataSource: 'metrics', + queryName: 'B', + aggregateOperator: 'rate', + aggregateAttribute: { + key: 'system_disk_io', + dataType: 'float64', + type: 'Sum', + isColumn: true, + isJSON: false, + id: 'system_disk_io--float64--Sum--true', + }, + timeAggregation: 'rate', + spaceAggregation: 'sum', + functions: [], + filters: { + op: 'AND', + items: [], + }, + expression: 'B', + disabled: false, + stepInterval: 120, + having: [], + limit: null, + orderBy: [], + groupBy: [], + legend: '', + reduceTo: 'avg', + }, + ], + queryFormulas: [ + { + queryName: 'F1', + expression: 'A / B ', + disabled: false, + legend: '', + }, + ], + }, + clickhouse_sql: [{ disabled: false, legend: '', name: 'A', query: '' }], + id: 'test-id', + promql: [{ disabled: false, legend: '', name: 'A', query: '' }], + queryType: 'builder', + unit: undefined, +}; diff --git a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts index 733513b33b..d95127b969 100644 --- a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts +++ b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts @@ -7,9 +7,13 @@ import { transformQueryBuilderDataModel } from '../transformQueryBuilderDataMode export const mapQueryDataFromApi = ( compositeQuery: ICompositeMetricQuery, + query?: Query, ): Query => { const builder = compositeQuery.builderQueries - ? transformQueryBuilderDataModel(compositeQuery.builderQueries) + ? transformQueryBuilderDataModel( + compositeQuery.builderQueries, + query?.builder, + ) : initialQueryState.builder; const promql = compositeQuery.promQueries diff --git a/frontend/src/lib/newQueryBuilder/transformQueryBuilderDataModel.ts b/frontend/src/lib/newQueryBuilder/transformQueryBuilderDataModel.ts index 3cf545d49b..f2a24eef84 100644 --- a/frontend/src/lib/newQueryBuilder/transformQueryBuilderDataModel.ts +++ b/frontend/src/lib/newQueryBuilder/transformQueryBuilderDataModel.ts @@ -3,6 +3,7 @@ import { initialQueryBuilderFormValuesMap, } from 'constants/queryBuilder'; import { FORMULA_REGEXP } from 'constants/regExp'; +import { isUndefined } from 'lodash-es'; import { BuilderQueryDataResourse, IBuilderFormula, @@ -12,6 +13,7 @@ import { QueryBuilderData } from 'types/common/queryBuilder'; export const transformQueryBuilderDataModel = ( data: BuilderQueryDataResourse, + query?: QueryBuilderData, ): QueryBuilderData => { const queryData: QueryBuilderData['queryData'] = []; const queryFormulas: QueryBuilderData['queryFormulas'] = []; @@ -19,10 +21,37 @@ export const transformQueryBuilderDataModel = ( Object.entries(data).forEach(([, value]) => { if (FORMULA_REGEXP.test(value.queryName)) { const formula = value as IBuilderFormula; - queryFormulas.push({ ...initialFormulaBuilderFormValues, ...formula }); + const baseFormula = query?.queryFormulas?.find( + (f) => f.queryName === value.queryName, + ); + if (!isUndefined(baseFormula)) { + // this is part of the flow where we create alerts from dashboard. + // we pass the formula as is from the widget query as we do not want anything to update in formula from the format api call + queryFormulas.push({ ...baseFormula }); + } else { + queryFormulas.push({ ...initialFormulaBuilderFormValues, ...formula }); + } } else { - const query = value as IBuilderQuery; - queryData.push({ ...initialQueryBuilderFormValuesMap.metrics, ...query }); + const queryFromData = value as IBuilderQuery; + const baseQuery = query?.queryData?.find( + (q) => q.queryName === queryFromData.queryName, + ); + + if (!isUndefined(baseQuery)) { + // this is part of the flow where we create alerts from dashboard. + // we pass the widget query as the base query and accept the filters from the format API response. + // which fills the variable values inside the same and is used to create alerts + // do not accept the full object as the stepInterval field is subject to changes + queryData.push({ + ...baseQuery, + filters: queryFromData.filters, + }); + } else { + queryData.push({ + ...initialQueryBuilderFormValuesMap.metrics, + ...queryFromData, + }); + } } }); From d60daef171f1365cd7f82dbeba991e97324af167 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 23 Jul 2024 20:21:25 +0530 Subject: [PATCH 116/281] fix: handle the super set query reset state when changing widgets (#5539) --- frontend/src/container/NewWidget/index.tsx | 12 ++++++++++++ frontend/src/providers/QueryBuilder.tsx | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/NewWidget/index.tsx b/frontend/src/container/NewWidget/index.tsx index ce7bd59cb8..70d667452e 100644 --- a/frontend/src/container/NewWidget/index.tsx +++ b/frontend/src/container/NewWidget/index.tsx @@ -79,6 +79,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { stagedQuery, redirectWithQueryBuilderData, supersetQuery, + setSupersetQuery, } = useQueryBuilder(); const isQueryModified = useMemo( @@ -548,6 +549,17 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { isNewTraceLogsAvailable, ]); + useEffect(() => { + /** + * we need this extra handling for superset query because we cannot keep this in sync with current query + * always.we do not sync superset query in the initQueryBuilderData because that function is called on all stage and run + * actions. we do not want that as we loose out on superset functionalities if we do the same. hence initialising the superset query + * on mount here with the currentQuery in the begining itself + */ + setSupersetQuery(currentQuery); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { registerShortcut(DashboardShortcuts.SaveChanges, onSaveDashboard); registerShortcut(DashboardShortcuts.DiscardChanges, onClickDiscardHandler); diff --git a/frontend/src/providers/QueryBuilder.tsx b/frontend/src/providers/QueryBuilder.tsx index 5e1eda5714..3654ce3ef3 100644 --- a/frontend/src/providers/QueryBuilder.tsx +++ b/frontend/src/providers/QueryBuilder.tsx @@ -829,7 +829,7 @@ export function QueryBuilderProvider({ unit, })); }, - [setCurrentQuery], + [setCurrentQuery, setSupersetQuery], ); const query: Query = useMemo( From 05bd6d52f1b48b15a61fb3437bdeb9e291bf9db9 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Thu, 25 Jul 2024 23:23:01 +0530 Subject: [PATCH 117/281] fix: relative time param from the url not respected (#5545) * fix: relative time param from the url not respected * chore: added code comments and the priorities of the params * fix: added validity checks for the relativeTime in the url --- .../TopNav/DateTimeSelectionV2/index.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/TopNav/DateTimeSelectionV2/index.tsx b/frontend/src/container/TopNav/DateTimeSelectionV2/index.tsx index 427b57198c..2b553f8017 100644 --- a/frontend/src/container/TopNav/DateTimeSelectionV2/index.tsx +++ b/frontend/src/container/TopNav/DateTimeSelectionV2/index.tsx @@ -23,7 +23,7 @@ import NewExplorerCTA from 'container/NewExplorerCTA'; import dayjs, { Dayjs } from 'dayjs'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import useUrlQuery from 'hooks/useUrlQuery'; -import GetMinMax from 'lib/getMinMax'; +import GetMinMax, { isValidTimeFormat } from 'lib/getMinMax'; import getTimeString from 'lib/getTimeString'; import history from 'lib/history'; import { isObject } from 'lodash-es'; @@ -73,6 +73,7 @@ function DateTimeSelection({ const urlQuery = useUrlQuery(); const searchStartTime = urlQuery.get('startTime'); const searchEndTime = urlQuery.get('endTime'); + const relativeTimeFromUrl = urlQuery.get(QueryParams.relativeTime); const queryClient = useQueryClient(); const [enableAbsoluteTime, setEnableAbsoluteTime] = useState(false); const [isValidteRelativeTime, setIsValidteRelativeTime] = useState(false); @@ -404,9 +405,18 @@ function DateTimeSelection({ time: Time, currentRoute: string, ): Time | CustomTimeType => { + // if the relativeTime param is present in the url give top most preference to the same + // if the relativeTime param is not valid then move to next preference + if (relativeTimeFromUrl != null && isValidTimeFormat(relativeTimeFromUrl)) { + return relativeTimeFromUrl as Time; + } + + // if the startTime and endTime params are present in the url give next preference to the them. if (searchEndTime !== null && searchStartTime !== null) { return 'custom'; } + + // if nothing is present in the url for time range then rely on the local storage values if ( (localstorageEndTime === null || localstorageStartTime === null) && time === 'custom' @@ -414,6 +424,7 @@ function DateTimeSelection({ return getDefaultOption(currentRoute); } + // if not present in the local storage as well then rely on the defaults set for the page if (OLD_RELATIVE_TIME_VALUES.indexOf(time) > -1) { return convertOldTimeToNewValidCustomTimeFormat(time); } @@ -448,7 +459,11 @@ function DateTimeSelection({ setRefreshButtonHidden(updatedTime === 'custom'); - updateTimeInterval(updatedTime, [preStartTime, preEndTime]); + if (updatedTime !== 'custom') { + updateTimeInterval(updatedTime); + } else { + updateTimeInterval(updatedTime, [preStartTime, preEndTime]); + } if (updatedTime !== 'custom') { urlQuery.delete('startTime'); From eefccafa5b957492eccfa53d23c0eec4f0325ed5 Mon Sep 17 00:00:00 2001 From: Vibhu Pandey Date: Fri, 26 Jul 2024 12:31:33 +0530 Subject: [PATCH 118/281] feat(gateway): remove feature flag (#5561) --- ee/query-service/app/server.go | 29 +++---------------- ee/query-service/integrations/gateway/noop.go | 2 +- ee/query-service/model/plans.go | 22 ++++++++++++++ 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index 9a2c96734f..ac941cec37 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -8,7 +8,6 @@ import ( "io" "net" "net/http" - "net/http/httputil" _ "net/http/pprof" // http profiler "os" "regexp" @@ -118,33 +117,13 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { localDB.SetMaxOpenConns(10) - gatewayFeature := basemodel.Feature{ - Name: "GATEWAY", - Active: false, - Usage: 0, - UsageLimit: -1, - Route: "", - } - - //Activate this feature if the url is not empty - var gatewayProxy *httputil.ReverseProxy - if serverOptions.GatewayUrl == "" { - gatewayFeature.Active = false - gatewayProxy, err = gateway.NewNoopProxy() - if err != nil { - return nil, err - } - } else { - zap.L().Info("Enabling gateway feature flag ...") - gatewayFeature.Active = true - gatewayProxy, err = gateway.NewProxy(serverOptions.GatewayUrl, gateway.RoutePrefix) - if err != nil { - return nil, err - } + gatewayProxy, err := gateway.NewProxy(serverOptions.GatewayUrl, gateway.RoutePrefix) + if err != nil { + return nil, err } // initiate license manager - lm, err := licensepkg.StartManager("sqlite", localDB, gatewayFeature) + lm, err := licensepkg.StartManager("sqlite", localDB) if err != nil { return nil, err } diff --git a/ee/query-service/integrations/gateway/noop.go b/ee/query-service/integrations/gateway/noop.go index bbe930e2f9..ccb5d3269a 100644 --- a/ee/query-service/integrations/gateway/noop.go +++ b/ee/query-service/integrations/gateway/noop.go @@ -5,5 +5,5 @@ import ( ) func NewNoopProxy() (*httputil.ReverseProxy, error) { - return nil, nil + return &httputil.ReverseProxy{}, nil } diff --git a/ee/query-service/model/plans.go b/ee/query-service/model/plans.go index 41bca047d5..135d276cad 100644 --- a/ee/query-service/model/plans.go +++ b/ee/query-service/model/plans.go @@ -11,6 +11,7 @@ const Enterprise = "ENTERPRISE_PLAN" const DisableUpsell = "DISABLE_UPSELL" const Onboarding = "ONBOARDING" const ChatSupport = "CHAT_SUPPORT" +const Gateway = "GATEWAY" var BasicPlan = basemodel.FeatureSet{ basemodel.Feature{ @@ -111,6 +112,13 @@ var BasicPlan = basemodel.FeatureSet{ UsageLimit: -1, Route: "", }, + basemodel.Feature{ + Name: Gateway, + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + }, } var ProPlan = basemodel.FeatureSet{ @@ -205,6 +213,13 @@ var ProPlan = basemodel.FeatureSet{ UsageLimit: -1, Route: "", }, + basemodel.Feature{ + Name: Gateway, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, } var EnterprisePlan = basemodel.FeatureSet{ @@ -313,4 +328,11 @@ var EnterprisePlan = basemodel.FeatureSet{ UsageLimit: -1, Route: "", }, + basemodel.Feature{ + Name: Gateway, + Active: true, + Usage: 0, + UsageLimit: -1, + Route: "", + }, } From ba7e6fcf235db0a81edd8f3ac9a44df94bc6f4f1 Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Fri, 26 Jul 2024 18:41:39 +0530 Subject: [PATCH 119/281] fix(saml): handle invalid email domain (#5564) ### Summary Handle the scenario when email with domain is used for SSO Login which does not match authenticated domains. Signed-off-by: Prashant Shahi --- ee/query-service/dao/sqlite/auth.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ee/query-service/dao/sqlite/auth.go b/ee/query-service/dao/sqlite/auth.go index 4418b04cbf..b8bc5e0fa0 100644 --- a/ee/query-service/dao/sqlite/auth.go +++ b/ee/query-service/dao/sqlite/auth.go @@ -20,11 +20,14 @@ import ( func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*basemodel.User, basemodel.BaseApiError) { // get auth domain from email domain domain, apierr := m.GetDomainByEmail(ctx, email) - if apierr != nil { zap.L().Error("failed to get domain from email", zap.Error(apierr)) return nil, model.InternalErrorStr("failed to get domain from email") } + if domain == nil { + zap.L().Error("email domain does not match any authenticated domain", zap.String("email", email)) + return nil, model.InternalErrorStr("email domain does not match any authenticated domain") + } hash, err := baseauth.PasswordHash(utils.GeneratePassowrd()) if err != nil { From 522e73b48e60bb77b15f4f7f2c77f055441035d1 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Fri, 26 Jul 2024 18:51:48 +0530 Subject: [PATCH 120/281] chore: move facing issues button in dashboards and disable intercom ping (#5571) * chore: move facing issues button in dashboards and disable intercom ping * chore: review comment --- .../facingIssueBtn/FacingIssueBtn.tsx | 5 +++- .../ListOfDashboard/DashboardsList.tsx | 3 ++- .../DashboardDescription/index.tsx | 23 ++++++++++--------- .../LeftContainer/QuerySection/index.tsx | 16 +++++++++++++ frontend/src/container/NewWidget/index.tsx | 16 ------------- 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx b/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx index 2a4b07aa22..093c2b8f02 100644 --- a/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx +++ b/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx @@ -16,6 +16,7 @@ export interface FacingIssueBtnProps { buttonText?: string; className?: string; onHoverText?: string; + intercomMessageDisabled?: boolean; } function FacingIssueBtn({ @@ -25,11 +26,12 @@ function FacingIssueBtn({ buttonText = '', className = '', onHoverText = '', + intercomMessageDisabled = false, }: FacingIssueBtnProps): JSX.Element | null { const handleFacingIssuesClick = (): void => { logEvent(eventName, attributes); - if (window.Intercom) { + if (window.Intercom && !intercomMessageDisabled) { window.Intercom('showNewMessage', defaultTo(message, '')); } }; @@ -62,6 +64,7 @@ FacingIssueBtn.defaultProps = { buttonText: '', className: '', onHoverText: '', + intercomMessageDisabled: false, }; export default FacingIssueBtn; diff --git a/frontend/src/container/ListOfDashboard/DashboardsList.tsx b/frontend/src/container/ListOfDashboard/DashboardsList.tsx index 05847cebde..17b6fe0863 100644 --- a/frontend/src/container/ListOfDashboard/DashboardsList.tsx +++ b/frontend/src/container/ListOfDashboard/DashboardsList.tsx @@ -653,8 +653,9 @@ function DashboardsList(): JSX.Element { }} eventName="Dashboard: Facing Issues in dashboard" message={dashboardListMessage} - buttonText="Facing issues with dashboards?" + buttonText="Need help with dashboards?" onHoverText="Click here to get help with dashboards" + intercomMessageDisabled />
diff --git a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx index 60e9e90db8..d3c959ca8b 100644 --- a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx @@ -300,17 +300,6 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element { {title} -
@@ -323,6 +312,18 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element { {isDashboardLocked && }
+ + Configure panel -
{isSaveDisabled && ( From a688b6c60e6ba994f01515ca3ae8aea91c3538c7 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Sat, 27 Jul 2024 08:47:44 +0530 Subject: [PATCH 121/281] Revert "fix(saml): handle invalid email domain (#5564)" (#5579) This reverts commit ba7e6fcf235db0a81edd8f3ac9a44df94bc6f4f1. --- ee/query-service/dao/sqlite/auth.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ee/query-service/dao/sqlite/auth.go b/ee/query-service/dao/sqlite/auth.go index b8bc5e0fa0..4418b04cbf 100644 --- a/ee/query-service/dao/sqlite/auth.go +++ b/ee/query-service/dao/sqlite/auth.go @@ -20,14 +20,11 @@ import ( func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*basemodel.User, basemodel.BaseApiError) { // get auth domain from email domain domain, apierr := m.GetDomainByEmail(ctx, email) + if apierr != nil { zap.L().Error("failed to get domain from email", zap.Error(apierr)) return nil, model.InternalErrorStr("failed to get domain from email") } - if domain == nil { - zap.L().Error("email domain does not match any authenticated domain", zap.String("email", email)) - return nil, model.InternalErrorStr("email domain does not match any authenticated domain") - } hash, err := baseauth.PasswordHash(utils.GeneratePassowrd()) if err != nil { From 4360cd039730ec627c948ed1d8b651661216290c Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Sat, 27 Jul 2024 09:52:53 +0530 Subject: [PATCH 122/281] fix(saml): handle invalid email domain (#5580) ### Summary Handle the scenario when email with domain is used for SSO Login which does not match authenticated domains. Signed-off-by: Prashant Shahi --- ee/query-service/dao/sqlite/auth.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ee/query-service/dao/sqlite/auth.go b/ee/query-service/dao/sqlite/auth.go index 4418b04cbf..b8bc5e0fa0 100644 --- a/ee/query-service/dao/sqlite/auth.go +++ b/ee/query-service/dao/sqlite/auth.go @@ -20,11 +20,14 @@ import ( func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*basemodel.User, basemodel.BaseApiError) { // get auth domain from email domain domain, apierr := m.GetDomainByEmail(ctx, email) - if apierr != nil { zap.L().Error("failed to get domain from email", zap.Error(apierr)) return nil, model.InternalErrorStr("failed to get domain from email") } + if domain == nil { + zap.L().Error("email domain does not match any authenticated domain", zap.String("email", email)) + return nil, model.InternalErrorStr("email domain does not match any authenticated domain") + } hash, err := baseauth.PasswordHash(utils.GeneratePassowrd()) if err != nil { From d00024b64aa21d440bfb3545ed88cb70b3be856b Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 29 Jul 2024 09:51:18 +0530 Subject: [PATCH 123/281] feat: preference framework qs changes (#5527) * feat: query service changes base setup for preferences * feat: added handlers for user and org preferences * chore: added base for all user and all org preferences * feat: added handlers for all user and all org preferences * feat: register the preference routes and initDB in pkg/query-service * feat: code refactor * chore: too much fun code refactor * chore: little little missing attributes * fix: handle range queries better * fix: handle range queries better * chore: address review comments * chore: use struct inheritance for the all preferences struct * chore: address review comments * chore: address review comments * chore: correct preference routes * chore: low hanging optimisations * chore: address review comments * chore: address review comments * chore: added extra validations for the check in allowed values * fix: better handling for the jwt claims * fix: better handling for the jwt claims * chore: move the error to preference apis * chore: move the error to preference apis * fix: move the 401 logic to the auth middleware --- ee/query-service/app/server.go | 19 +- pkg/query-service/app/http_handler.go | 127 +++++ pkg/query-service/app/preferences/map.go | 37 ++ pkg/query-service/app/preferences/model.go | 544 +++++++++++++++++++++ pkg/query-service/app/server.go | 22 +- pkg/query-service/auth/auth.go | 6 + pkg/query-service/auth/jwt.go | 10 + 7 files changed, 763 insertions(+), 2 deletions(-) create mode 100644 pkg/query-service/app/preferences/map.go create mode 100644 pkg/query-service/app/preferences/model.go diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index ac941cec37..c41788e971 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net" @@ -27,6 +28,7 @@ import ( "go.signoz.io/signoz/ee/query-service/integrations/gateway" "go.signoz.io/signoz/ee/query-service/interfaces" baseauth "go.signoz.io/signoz/pkg/query-service/auth" + "go.signoz.io/signoz/pkg/query-service/model" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" licensepkg "go.signoz.io/signoz/ee/query-service/license" @@ -40,6 +42,7 @@ import ( "go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline" "go.signoz.io/signoz/pkg/query-service/app/opamp" opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model" + "go.signoz.io/signoz/pkg/query-service/app/preferences" "go.signoz.io/signoz/pkg/query-service/cache" baseconst "go.signoz.io/signoz/pkg/query-service/constants" "go.signoz.io/signoz/pkg/query-service/healthcheck" @@ -109,6 +112,10 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { baseexplorer.InitWithDSN(baseconst.RELATIONAL_DATASOURCE_PATH) + if err := preferences.InitDB(baseconst.RELATIONAL_DATASOURCE_PATH); err != nil { + return nil, err + } + localDB, err := dashboards.InitDB(baseconst.RELATIONAL_DATASOURCE_PATH) if err != nil { @@ -319,7 +326,17 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, e // add auth middleware getUserFromRequest := func(r *http.Request) (*basemodel.UserPayload, error) { - return auth.GetUserFromRequest(r, apiHandler) + user, err := auth.GetUserFromRequest(r, apiHandler) + + if err != nil { + return nil, err + } + + if user.User.OrgId == "" { + return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims")) + } + + return user, nil } am := baseapp.NewAuthMiddleware(getUserFromRequest) diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 42879123ec..3e4ddfbfcf 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -29,12 +29,14 @@ import ( logsv3 "go.signoz.io/signoz/pkg/query-service/app/logs/v3" "go.signoz.io/signoz/pkg/query-service/app/metrics" metricsv3 "go.signoz.io/signoz/pkg/query-service/app/metrics/v3" + "go.signoz.io/signoz/pkg/query-service/app/preferences" "go.signoz.io/signoz/pkg/query-service/app/querier" querierV2 "go.signoz.io/signoz/pkg/query-service/app/querier/v2" "go.signoz.io/signoz/pkg/query-service/app/queryBuilder" tracesV3 "go.signoz.io/signoz/pkg/query-service/app/traces/v3" "go.signoz.io/signoz/pkg/query-service/auth" "go.signoz.io/signoz/pkg/query-service/cache" + "go.signoz.io/signoz/pkg/query-service/common" "go.signoz.io/signoz/pkg/query-service/constants" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" "go.signoz.io/signoz/pkg/query-service/postprocess" @@ -398,6 +400,22 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) { router.HandleFunc("/api/v1/disks", am.ViewAccess(aH.getDisks)).Methods(http.MethodGet) + // === Preference APIs === + + // user actions + router.HandleFunc("/api/v1/user/preferences", am.ViewAccess(aH.getAllUserPreferences)).Methods(http.MethodGet) + + router.HandleFunc("/api/v1/user/preferences/{preferenceId}", am.ViewAccess(aH.getUserPreference)).Methods(http.MethodGet) + + router.HandleFunc("/api/v1/user/preferences/{preferenceId}", am.ViewAccess(aH.updateUserPreference)).Methods(http.MethodPut) + + // org actions + router.HandleFunc("/api/v1/org/preferences", am.AdminAccess(aH.getAllOrgPreferences)).Methods(http.MethodGet) + + router.HandleFunc("/api/v1/org/preferences/{preferenceId}", am.AdminAccess(aH.getOrgPreference)).Methods(http.MethodGet) + + router.HandleFunc("/api/v1/org/preferences/{preferenceId}", am.AdminAccess(aH.updateOrgPreference)).Methods(http.MethodPut) + // === Authentication APIs === router.HandleFunc("/api/v1/invite", am.AdminAccess(aH.inviteUser)).Methods(http.MethodPost) router.HandleFunc("/api/v1/invite/{token}", am.OpenAccess(aH.getInvite)).Methods(http.MethodGet) @@ -2192,6 +2210,115 @@ func (aH *APIHandler) WriteJSON(w http.ResponseWriter, r *http.Request, response w.Write(resp) } +// Preferences + +func (ah *APIHandler) getUserPreference( + w http.ResponseWriter, r *http.Request, +) { + preferenceId := mux.Vars(r)["preferenceId"] + user := common.GetUserFromContext(r.Context()) + + preference, apiErr := preferences.GetUserPreference( + r.Context(), preferenceId, user.User.OrgId, user.User.Id, + ) + if apiErr != nil { + RespondError(w, apiErr, nil) + return + } + + ah.Respond(w, preference) +} + +func (ah *APIHandler) updateUserPreference( + w http.ResponseWriter, r *http.Request, +) { + preferenceId := mux.Vars(r)["preferenceId"] + user := common.GetUserFromContext(r.Context()) + req := preferences.UpdatePreference{} + + err := json.NewDecoder(r.Body).Decode(&req) + + if err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + preference, apiErr := preferences.UpdateUserPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.Id) + if apiErr != nil { + RespondError(w, apiErr, nil) + return + } + + ah.Respond(w, preference) +} + +func (ah *APIHandler) getAllUserPreferences( + w http.ResponseWriter, r *http.Request, +) { + user := common.GetUserFromContext(r.Context()) + preference, apiErr := preferences.GetAllUserPreferences( + r.Context(), user.User.OrgId, user.User.Id, + ) + if apiErr != nil { + RespondError(w, apiErr, nil) + return + } + + ah.Respond(w, preference) +} + +func (ah *APIHandler) getOrgPreference( + w http.ResponseWriter, r *http.Request, +) { + preferenceId := mux.Vars(r)["preferenceId"] + user := common.GetUserFromContext(r.Context()) + preference, apiErr := preferences.GetOrgPreference( + r.Context(), preferenceId, user.User.OrgId, + ) + if apiErr != nil { + RespondError(w, apiErr, nil) + return + } + + ah.Respond(w, preference) +} + +func (ah *APIHandler) updateOrgPreference( + w http.ResponseWriter, r *http.Request, +) { + preferenceId := mux.Vars(r)["preferenceId"] + req := preferences.UpdatePreference{} + user := common.GetUserFromContext(r.Context()) + + err := json.NewDecoder(r.Body).Decode(&req) + + if err != nil { + RespondError(w, model.BadRequest(err), nil) + return + } + preference, apiErr := preferences.UpdateOrgPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.OrgId) + if apiErr != nil { + RespondError(w, apiErr, nil) + return + } + + ah.Respond(w, preference) +} + +func (ah *APIHandler) getAllOrgPreferences( + w http.ResponseWriter, r *http.Request, +) { + user := common.GetUserFromContext(r.Context()) + preference, apiErr := preferences.GetAllOrgPreferences( + r.Context(), user.User.OrgId, + ) + if apiErr != nil { + RespondError(w, apiErr, nil) + return + } + + ah.Respond(w, preference) +} + // Integrations func (ah *APIHandler) RegisterIntegrationRoutes(router *mux.Router, am *AuthMiddleware) { subRouter := router.PathPrefix("/api/v1/integrations").Subrouter() diff --git a/pkg/query-service/app/preferences/map.go b/pkg/query-service/app/preferences/map.go new file mode 100644 index 0000000000..219fb6c595 --- /dev/null +++ b/pkg/query-service/app/preferences/map.go @@ -0,0 +1,37 @@ +package preferences + +var preferenceMap = map[string]Preference{ + "DASHBOARDS_LIST_VIEW": { + Key: "DASHBOARDS_LIST_VIEW", + Name: "Dashboards List View", + Description: "", + ValueType: "string", + DefaultValue: "grid", + AllowedValues: []interface{}{"grid", "list"}, + IsDiscreteValues: true, + AllowedScopes: []string{"user", "org"}, + }, + "LOGS_TOOLBAR_COLLAPSED": { + Key: "LOGS_TOOLBAR_COLLAPSED", + Name: "Logs toolbar", + Description: "", + ValueType: "boolean", + DefaultValue: false, + AllowedValues: []interface{}{true, false}, + IsDiscreteValues: true, + AllowedScopes: []string{"user", "org"}, + }, + "MAX_DEPTH_ALLOWED": { + Key: "MAX_DEPTH_ALLOWED", + Name: "Max Depth Allowed", + Description: "", + ValueType: "integer", + DefaultValue: 10, + IsDiscreteValues: false, + Range: Range{ + Min: 0, + Max: 100, + }, + AllowedScopes: []string{"user", "org"}, + }, +} diff --git a/pkg/query-service/app/preferences/model.go b/pkg/query-service/app/preferences/model.go new file mode 100644 index 0000000000..82b8e9c9f6 --- /dev/null +++ b/pkg/query-service/app/preferences/model.go @@ -0,0 +1,544 @@ +package preferences + +import ( + "context" + "database/sql" + "fmt" + "strings" + + "github.com/jmoiron/sqlx" + "go.signoz.io/signoz/ee/query-service/model" +) + +type Range struct { + Min int64 `json:"min"` + Max int64 `json:"max"` +} + +type Preference struct { + Key string `json:"key"` + Name string `json:"name"` + Description string `json:"description"` + ValueType string `json:"valueType"` + DefaultValue interface{} `json:"defaultValue"` + AllowedValues []interface{} `json:"allowedValues"` + IsDiscreteValues bool `json:"isDiscreteValues"` + Range Range `json:"range"` + AllowedScopes []string `json:"allowedScopes"` +} + +func (p *Preference) ErrorValueTypeMismatch() *model.ApiError { + return &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("the preference value is not of expected type: %s", p.ValueType)} +} + +const ( + PreferenceValueTypeInteger string = "integer" + PreferenceValueTypeFloat string = "float" + PreferenceValueTypeString string = "string" + PreferenceValueTypeBoolean string = "boolean" +) + +const ( + OrgAllowedScope string = "org" + UserAllowedScope string = "user" +) + +func (p *Preference) checkIfInAllowedValues(preferenceValue interface{}) (bool, *model.ApiError) { + + switch p.ValueType { + case PreferenceValueTypeInteger: + _, ok := preferenceValue.(int64) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + case PreferenceValueTypeFloat: + _, ok := preferenceValue.(float64) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + case PreferenceValueTypeString: + _, ok := preferenceValue.(string) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + case PreferenceValueTypeBoolean: + _, ok := preferenceValue.(bool) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + } + isInAllowedValues := false + for _, value := range p.AllowedValues { + switch p.ValueType { + case PreferenceValueTypeInteger: + allowedValue, ok := value.(int64) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + + if allowedValue == preferenceValue { + isInAllowedValues = true + } + case PreferenceValueTypeFloat: + allowedValue, ok := value.(float64) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + + if allowedValue == preferenceValue { + isInAllowedValues = true + } + case PreferenceValueTypeString: + allowedValue, ok := value.(string) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + + if allowedValue == preferenceValue { + isInAllowedValues = true + } + case PreferenceValueTypeBoolean: + allowedValue, ok := value.(bool) + if !ok { + return false, p.ErrorValueTypeMismatch() + } + + if allowedValue == preferenceValue { + isInAllowedValues = true + } + } + } + return isInAllowedValues, nil +} + +func (p *Preference) IsValidValue(preferenceValue interface{}) *model.ApiError { + typeSafeValue := preferenceValue + switch p.ValueType { + case PreferenceValueTypeInteger: + val, ok := preferenceValue.(int64) + if !ok { + floatVal, ok := preferenceValue.(float64) + if !ok || floatVal != float64(int64(floatVal)) { + return p.ErrorValueTypeMismatch() + } + val = int64(floatVal) + typeSafeValue = val + } + if !p.IsDiscreteValues { + if val < p.Range.Min || val > p.Range.Max { + return &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("the preference value is not in the range specified, min: %v , max:%v", p.Range.Min, p.Range.Max)} + } + } + case PreferenceValueTypeString: + _, ok := preferenceValue.(string) + if !ok { + return p.ErrorValueTypeMismatch() + } + case PreferenceValueTypeFloat: + _, ok := preferenceValue.(float64) + if !ok { + return p.ErrorValueTypeMismatch() + } + case PreferenceValueTypeBoolean: + _, ok := preferenceValue.(bool) + if !ok { + return p.ErrorValueTypeMismatch() + } + } + + // check the validity of the value being part of allowed values or the range specified if any + if p.IsDiscreteValues { + if p.AllowedValues != nil { + isInAllowedValues, valueMisMatchErr := p.checkIfInAllowedValues(typeSafeValue) + + if valueMisMatchErr != nil { + return valueMisMatchErr + } + if !isInAllowedValues { + return &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("the preference value is not in the list of allowedValues: %v", p.AllowedValues)} + } + } + } + return nil +} + +func (p *Preference) IsEnabledForScope(scope string) bool { + isPreferenceEnabledForGivenScope := false + if p.AllowedScopes != nil { + for _, allowedScope := range p.AllowedScopes { + if allowedScope == strings.ToLower(scope) { + isPreferenceEnabledForGivenScope = true + } + } + } + return isPreferenceEnabledForGivenScope +} + +func (p *Preference) SanitizeValue(preferenceValue interface{}) interface{} { + switch p.ValueType { + case PreferenceValueTypeBoolean: + if preferenceValue == "1" || preferenceValue == true { + return true + } else { + return false + } + default: + return preferenceValue + } +} + +type AllPreferences struct { + Preference + Value interface{} `json:"value"` +} + +type PreferenceKV struct { + PreferenceId string `json:"preference_id" db:"preference_id"` + PreferenceValue interface{} `json:"preference_value" db:"preference_value"` +} + +type UpdatePreference struct { + PreferenceValue interface{} `json:"preference_value"` +} + +var db *sqlx.DB + +func InitDB(datasourceName string) error { + var err error + db, err = sqlx.Open("sqlite3", datasourceName) + + if err != nil { + return err + } + + // create the user preference table + tableSchema := ` + PRAGMA foreign_keys = ON; + CREATE TABLE IF NOT EXISTS user_preference( + preference_id TEXT NOT NULL, + preference_value TEXT, + user_id TEXT NOT NULL, + PRIMARY KEY (preference_id,user_id), + FOREIGN KEY (user_id) + REFERENCES users(id) + ON UPDATE CASCADE + ON DELETE CASCADE + );` + + _, err = db.Exec(tableSchema) + if err != nil { + return fmt.Errorf("error in creating user_preference table: %s", err.Error()) + } + + // create the org preference table + tableSchema = ` + PRAGMA foreign_keys = ON; + CREATE TABLE IF NOT EXISTS org_preference( + preference_id TEXT NOT NULL, + preference_value TEXT, + org_id TEXT NOT NULL, + PRIMARY KEY (preference_id,org_id), + FOREIGN KEY (org_id) + REFERENCES organizations(id) + ON UPDATE CASCADE + ON DELETE CASCADE + );` + + _, err = db.Exec(tableSchema) + if err != nil { + return fmt.Errorf("error in creating org_preference table: %s", err.Error()) + } + + return nil +} + +// org preference functions +func GetOrgPreference(ctx context.Context, preferenceId string, orgId string) (*PreferenceKV, *model.ApiError) { + // check if the preference key exists or not + preference, seen := preferenceMap[preferenceId] + if !seen { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)} + } + + // check if the preference is enabled for org scope or not + isPreferenceEnabled := preference.IsEnabledForScope(OrgAllowedScope) + if !isPreferenceEnabled { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at org scope: %s", preferenceId)} + } + + // fetch the value from the database + var orgPreference PreferenceKV + query := `SELECT preference_id , preference_value FROM org_preference WHERE preference_id=$1 AND org_id=$2;` + err := db.Get(&orgPreference, query, preferenceId, orgId) + + // if the value doesn't exist in db then return the default value + if err != nil { + if err == sql.ErrNoRows { + return &PreferenceKV{ + PreferenceId: preferenceId, + PreferenceValue: preference.DefaultValue, + }, nil + } + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in fetching the org preference: %s", err.Error())} + + } + + // else return the value fetched from the org_preference table + return &PreferenceKV{ + PreferenceId: preferenceId, + PreferenceValue: preference.SanitizeValue(orgPreference.PreferenceValue), + }, nil +} + +func UpdateOrgPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, orgId string) (*PreferenceKV, *model.ApiError) { + // check if the preference key exists or not + preference, seen := preferenceMap[preferenceId] + if !seen { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)} + } + + // check if the preference is enabled at org scope or not + isPreferenceEnabled := preference.IsEnabledForScope(OrgAllowedScope) + if !isPreferenceEnabled { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at org scope: %s", preferenceId)} + } + + err := preference.IsValidValue(preferenceValue) + if err != nil { + return nil, err + } + + // update the values in the org_preference table and return the key and the value + query := `INSERT INTO org_preference(preference_id,preference_value,org_id) VALUES($1,$2,$3) + ON CONFLICT(preference_id,org_id) DO + UPDATE SET preference_value= $2 WHERE preference_id=$1 AND org_id=$3;` + + _, dberr := db.Exec(query, preferenceId, preferenceValue, orgId) + + if dberr != nil { + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in setting the preference value: %s", dberr.Error())} + } + + return &PreferenceKV{ + PreferenceId: preferenceId, + PreferenceValue: preferenceValue, + }, nil +} + +func GetAllOrgPreferences(ctx context.Context, orgId string) (*[]AllPreferences, *model.ApiError) { + // filter out all the org enabled preferences from the preference variable + allOrgPreferences := []AllPreferences{} + + // fetch all the org preference values stored in org_preference table + orgPreferenceValues := []PreferenceKV{} + + query := `SELECT preference_id,preference_value FROM org_preference WHERE org_id=$1;` + err := db.Select(&orgPreferenceValues, query, orgId) + + if err != nil { + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting all org preference values: %s", err)} + } + + // create a map of key vs values from the above response + preferenceValueMap := map[string]interface{}{} + + for _, preferenceValue := range orgPreferenceValues { + preferenceValueMap[preferenceValue.PreferenceId] = preferenceValue.PreferenceValue + } + + // update in the above filtered list wherver value present in the map + for _, preference := range preferenceMap { + isEnabledForOrgScope := preference.IsEnabledForScope(OrgAllowedScope) + if isEnabledForOrgScope { + preferenceWithValue := AllPreferences{} + preferenceWithValue.Key = preference.Key + preferenceWithValue.Name = preference.Name + preferenceWithValue.Description = preference.Description + preferenceWithValue.AllowedScopes = preference.AllowedScopes + preferenceWithValue.AllowedValues = preference.AllowedValues + preferenceWithValue.DefaultValue = preference.DefaultValue + preferenceWithValue.Range = preference.Range + preferenceWithValue.ValueType = preference.ValueType + preferenceWithValue.IsDiscreteValues = preference.IsDiscreteValues + value, seen := preferenceValueMap[preference.Key] + + if seen { + preferenceWithValue.Value = value + } else { + preferenceWithValue.Value = preference.DefaultValue + } + + preferenceWithValue.Value = preference.SanitizeValue(preferenceWithValue.Value) + allOrgPreferences = append(allOrgPreferences, preferenceWithValue) + } + } + return &allOrgPreferences, nil +} + +// user preference functions +func GetUserPreference(ctx context.Context, preferenceId string, orgId string, userId string) (*PreferenceKV, *model.ApiError) { + // check if the preference key exists + preference, seen := preferenceMap[preferenceId] + if !seen { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)} + } + + preferenceValue := PreferenceKV{ + PreferenceId: preferenceId, + PreferenceValue: preference.DefaultValue, + } + + // check if the preference is enabled at user scope + isPreferenceEnabledAtUserScope := preference.IsEnabledForScope(UserAllowedScope) + if !isPreferenceEnabledAtUserScope { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at user scope: %s", preferenceId)} + } + + isPreferenceEnabledAtOrgScope := preference.IsEnabledForScope(OrgAllowedScope) + // get the value from the org scope if enabled at org scope + if isPreferenceEnabledAtOrgScope { + orgPreference := PreferenceKV{} + + query := `SELECT preference_id , preference_value FROM org_preference WHERE preference_id=$1 AND org_id=$2;` + + err := db.Get(&orgPreference, query, preferenceId, orgId) + + // if there is error in getting values and its not an empty rows error return from here + if err != nil && err != sql.ErrNoRows { + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting org preference values: %s", err.Error())} + } + + // if there is no error update the preference value with value from org preference + if err == nil { + preferenceValue.PreferenceValue = orgPreference.PreferenceValue + } + } + + // get the value from the user_preference table, if exists return this value else the one calculated in the above step + userPreference := PreferenceKV{} + + query := `SELECT preference_id, preference_value FROM user_preference WHERE preference_id=$1 AND user_id=$2;` + err := db.Get(&userPreference, query, preferenceId, userId) + + if err != nil && err != sql.ErrNoRows { + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting user preference values: %s", err.Error())} + } + + if err == nil { + preferenceValue.PreferenceValue = userPreference.PreferenceValue + } + + return &PreferenceKV{ + PreferenceId: preferenceValue.PreferenceId, + PreferenceValue: preference.SanitizeValue(preferenceValue.PreferenceValue), + }, nil +} + +func UpdateUserPreference(ctx context.Context, preferenceId string, preferenceValue interface{}, userId string) (*PreferenceKV, *model.ApiError) { + // check if the preference id is valid + preference, seen := preferenceMap[preferenceId] + if !seen { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no such preferenceId exists: %s", preferenceId)} + } + + // check if the preference is enabled at user scope + isPreferenceEnabledAtUserScope := preference.IsEnabledForScope(UserAllowedScope) + if !isPreferenceEnabledAtUserScope { + return nil, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("preference is not enabled at user scope: %s", preferenceId)} + } + + err := preference.IsValidValue(preferenceValue) + if err != nil { + return nil, err + } + // update the user preference values + query := `INSERT INTO user_preference(preference_id,preference_value,user_id) VALUES($1,$2,$3) + ON CONFLICT(preference_id,user_id) DO + UPDATE SET preference_value= $2 WHERE preference_id=$1 AND user_id=$3;` + + _, dberrr := db.Exec(query, preferenceId, preferenceValue, userId) + + if dberrr != nil { + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in setting the preference value: %s", dberrr.Error())} + } + + return &PreferenceKV{ + PreferenceId: preferenceId, + PreferenceValue: preferenceValue, + }, nil +} + +func GetAllUserPreferences(ctx context.Context, orgId string, userId string) (*[]AllPreferences, *model.ApiError) { + allUserPreferences := []AllPreferences{} + + // fetch all the org preference values stored in org_preference table + orgPreferenceValues := []PreferenceKV{} + + query := `SELECT preference_id,preference_value FROM org_preference WHERE org_id=$1;` + err := db.Select(&orgPreferenceValues, query, orgId) + + if err != nil { + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting all org preference values: %s", err)} + } + + // create a map of key vs values from the above response + preferenceOrgValueMap := map[string]interface{}{} + + for _, preferenceValue := range orgPreferenceValues { + preferenceOrgValueMap[preferenceValue.PreferenceId] = preferenceValue.PreferenceValue + } + + // fetch all the user preference values stored in user_preference table + userPreferenceValues := []PreferenceKV{} + + query = `SELECT preference_id,preference_value FROM user_preference WHERE user_id=$1;` + err = db.Select(&userPreferenceValues, query, userId) + + if err != nil { + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in getting all user preference values: %s", err)} + } + + // create a map of key vs values from the above response + preferenceUserValueMap := map[string]interface{}{} + + for _, preferenceValue := range userPreferenceValues { + preferenceUserValueMap[preferenceValue.PreferenceId] = preferenceValue.PreferenceValue + } + + // update in the above filtered list wherver value present in the map + for _, preference := range preferenceMap { + isEnabledForUserScope := preference.IsEnabledForScope(UserAllowedScope) + + if isEnabledForUserScope { + preferenceWithValue := AllPreferences{} + preferenceWithValue.Key = preference.Key + preferenceWithValue.Name = preference.Name + preferenceWithValue.Description = preference.Description + preferenceWithValue.AllowedScopes = preference.AllowedScopes + preferenceWithValue.AllowedValues = preference.AllowedValues + preferenceWithValue.DefaultValue = preference.DefaultValue + preferenceWithValue.Range = preference.Range + preferenceWithValue.ValueType = preference.ValueType + preferenceWithValue.IsDiscreteValues = preference.IsDiscreteValues + preferenceWithValue.Value = preference.DefaultValue + + isEnabledForOrgScope := preference.IsEnabledForScope(OrgAllowedScope) + if isEnabledForOrgScope { + value, seen := preferenceOrgValueMap[preference.Key] + if seen { + preferenceWithValue.Value = value + } + } + + value, seen := preferenceUserValueMap[preference.Key] + + if seen { + preferenceWithValue.Value = value + } + + preferenceWithValue.Value = preference.SanitizeValue(preferenceWithValue.Value) + allUserPreferences = append(allUserPreferences, preferenceWithValue) + } + } + return &allUserPreferences, nil +} diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index 2260045f4d..5120cd0039 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net" @@ -27,6 +28,7 @@ import ( "go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline" "go.signoz.io/signoz/pkg/query-service/app/opamp" opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model" + "go.signoz.io/signoz/pkg/query-service/app/preferences" "go.signoz.io/signoz/pkg/query-service/common" v3 "go.signoz.io/signoz/pkg/query-service/model/v3" @@ -94,6 +96,10 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { return nil, err } + if err := preferences.InitDB(constants.RELATIONAL_DATASOURCE_PATH); err != nil { + return nil, err + } + localDB, err := dashboards.InitDB(constants.RELATIONAL_DATASOURCE_PATH) explorer.InitWithDSN(constants.RELATIONAL_DATASOURCE_PATH) @@ -268,7 +274,21 @@ func (s *Server) createPublicServer(api *APIHandler) (*http.Server, error) { r.Use(s.analyticsMiddleware) r.Use(loggingMiddleware) - am := NewAuthMiddleware(auth.GetUserFromRequest) + // add auth middleware + getUserFromRequest := func(r *http.Request) (*model.UserPayload, error) { + user, err := auth.GetUserFromRequest(r) + + if err != nil { + return nil, err + } + + if user.User.OrgId == "" { + return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims")) + } + + return user, nil + } + am := NewAuthMiddleware(getUserFromRequest) api.RegisterRoutes(r, am) api.RegisterLogsRoutes(r, am) diff --git a/pkg/query-service/auth/auth.go b/pkg/query-service/auth/auth.go index 6041b3c1af..16eea6a5f3 100644 --- a/pkg/query-service/auth/auth.go +++ b/pkg/query-service/auth/auth.go @@ -467,6 +467,10 @@ func authenticateLogin(ctx context.Context, req *model.LoginRequest) (*model.Use return nil, errors.Wrap(err, "failed to validate refresh token") } + if user.OrgId == "" { + return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims")) + } + return user, nil } @@ -505,6 +509,7 @@ func GenerateJWTForUser(user *model.User) (model.UserJwtObject, error) { "gid": user.GroupId, "email": user.Email, "exp": j.AccessJwtExpiry, + "orgId": user.OrgId, }) j.AccessJwt, err = token.SignedString([]byte(JwtSecret)) @@ -518,6 +523,7 @@ func GenerateJWTForUser(user *model.User) (model.UserJwtObject, error) { "gid": user.GroupId, "email": user.Email, "exp": j.RefreshJwtExpiry, + "orgId": user.OrgId, }) j.RefreshJwt, err = token.SignedString([]byte(JwtSecret)) diff --git a/pkg/query-service/auth/jwt.go b/pkg/query-service/auth/jwt.go index 7fe70e2c71..f57bb2ae18 100644 --- a/pkg/query-service/auth/jwt.go +++ b/pkg/query-service/auth/jwt.go @@ -20,6 +20,8 @@ var ( ) func ParseJWT(jwtStr string) (jwt.MapClaims, error) { + // TODO[@vikrantgupta25] : to update this to the claims check function for better integrity of JWT + // reference - https://pkg.go.dev/github.com/golang-jwt/jwt/v5#Parser.ParseWithClaims token, err := jwt.Parse(jwtStr, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, errors.Errorf("unknown signing algo: %v", token.Header["alg"]) @@ -35,6 +37,7 @@ func ParseJWT(jwtStr string) (jwt.MapClaims, error) { if !ok || !token.Valid { return nil, errors.Errorf("Not a valid jwt claim") } + return claims, nil } @@ -47,11 +50,18 @@ func validateUser(tok string) (*model.UserPayload, error) { if !claims.VerifyExpiresAt(now, true) { return nil, model.ErrorTokenExpired } + + var orgId string + if claims["orgId"] != nil { + orgId = claims["orgId"].(string) + } + return &model.UserPayload{ User: model.User{ Id: claims["id"].(string), GroupId: claims["gid"].(string), Email: claims["email"].(string), + OrgId: orgId, }, }, nil } From 3134e8c1cf3f9ca39212065cad952d9e47baadc5 Mon Sep 17 00:00:00 2001 From: rahulkeswani101 Date: Mon, 29 Jul 2024 11:42:55 +0530 Subject: [PATCH 124/281] feat: removed top nav from new alerts landing page (#5538) * feat: removed top nav from new alerts landing page * feat: added new function to check new alerts landing page --------- Co-authored-by: Vikrant Gupta --- frontend/src/container/TopNav/index.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/TopNav/index.tsx b/frontend/src/container/TopNav/index.tsx index 5277908240..8219395568 100644 --- a/frontend/src/container/TopNav/index.tsx +++ b/frontend/src/container/TopNav/index.tsx @@ -31,7 +31,14 @@ function TopNav(): JSX.Element | null { [location.pathname], ); - if (isSignUpPage || isDisabled || isRouteToSkip) { + const isNewAlertsLandingPage = useMemo( + () => + matchPath(location.pathname, { path: ROUTES.ALERTS_NEW, exact: true }) && + !location.search, + [location.pathname, location.search], + ); + + if (isSignUpPage || isDisabled || isRouteToSkip || isNewAlertsLandingPage) { return null; } From 7b7cca7db7c44c6ce1a98278acb7713272c21f64 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Mon, 29 Jul 2024 11:45:03 +0530 Subject: [PATCH 125/281] chore: remove commented code (#5445) --- frontend/src/mocks-server/handlers.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/mocks-server/handlers.ts b/frontend/src/mocks-server/handlers.ts index 8381818981..f96309380a 100644 --- a/frontend/src/mocks-server/handlers.ts +++ b/frontend/src/mocks-server/handlers.ts @@ -133,7 +133,6 @@ export const handlers = [ res(ctx.status(200), ctx.json(licensesSuccessResponse)), ), - // ?licenseKey=58707e3d-3bdb-44e7-8c89-a9be237939f4 rest.get('http://localhost/api/v1/billing', (req, res, ctx) => res(ctx.status(200), ctx.json(billingSuccessResponse)), ), From 1281330c52877ca893f3f87b6e7cf488875e0f98 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 29 Jul 2024 15:47:09 +0530 Subject: [PATCH 126/281] fix: disable the unlock dashboard btn for integration dashboards (#5573) * fix: disable the unlock dashboard btn for integration dashboards * chore: added test cases for the integration / non integration dashboards --- ee/query-service/app/api/dashboard.go | 6 ++ .../__tests__/DashboardDescription.test.tsx | 100 ++++++++++++++++++ .../DashboardDescription/index.tsx | 36 +++++-- .../mocks-server/__mockdata__/dashboards.ts | 51 +++++++++ frontend/src/mocks-server/handlers.ts | 9 +- frontend/src/tests/test-utils.tsx | 1 + 6 files changed, 194 insertions(+), 9 deletions(-) create mode 100644 frontend/src/container/NewDashboard/DashboardDescription/__tests__/DashboardDescription.test.tsx diff --git a/ee/query-service/app/api/dashboard.go b/ee/query-service/app/api/dashboard.go index 0628ae18f6..51fe6c2ded 100644 --- a/ee/query-service/app/api/dashboard.go +++ b/ee/query-service/app/api/dashboard.go @@ -1,7 +1,9 @@ package api import ( + "errors" "net/http" + "strings" "github.com/gorilla/mux" "go.signoz.io/signoz/pkg/query-service/app/dashboards" @@ -29,6 +31,10 @@ func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request // Get the dashboard UUID from the request uuid := mux.Vars(r)["uuid"] + if strings.HasPrefix(uuid,"integration") { + RespondError(w, &model.ApiError{Typ: model.ErrorForbidden, Err: errors.New("dashboards created by integrations cannot be unlocked")}, "You are not authorized to lock/unlock this dashboard") + return + } dashboard, err := dashboards.GetDashboard(r.Context(), uuid) if err != nil { RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error()) diff --git a/frontend/src/container/NewDashboard/DashboardDescription/__tests__/DashboardDescription.test.tsx b/frontend/src/container/NewDashboard/DashboardDescription/__tests__/DashboardDescription.test.tsx new file mode 100644 index 0000000000..8e302042a3 --- /dev/null +++ b/frontend/src/container/NewDashboard/DashboardDescription/__tests__/DashboardDescription.test.tsx @@ -0,0 +1,100 @@ +import { getNonIntegrationDashboardById } from 'mocks-server/__mockdata__/dashboards'; +import { server } from 'mocks-server/server'; +import { rest } from 'msw'; +import { DashboardProvider } from 'providers/Dashboard/Dashboard'; +import { MemoryRouter, useLocation } from 'react-router-dom'; +import { fireEvent, render, screen, waitFor } from 'tests/test-utils'; + +import DashboardDescription from '..'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn(), + useRouteMatch: jest.fn().mockReturnValue({ + params: { + dashboardId: 4, + }, + }), +})); + +jest.mock( + 'container/TopNav/DateTimeSelectionV2/index.tsx', + () => + function MockDateTimeSelection(): JSX.Element { + return
MockDateTimeSelection
; + }, +); + +describe('Dashboard landing page actions header tests', () => { + it('unlock dashboard should be disabled for integrations created dashboards', async () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/dashboard/4`, + search: '', + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + const { getByTestId } = render( + + + => Promise.resolve(), + exit: (): Promise => Promise.resolve(), + node: { current: null }, + }} + /> + + , + ); + + await waitFor(() => + expect(getByTestId('dashboard-title')).toHaveTextContent('thor'), + ); + + const dashboardSettingsTrigger = getByTestId('options'); + + await fireEvent.click(dashboardSettingsTrigger); + + const lockUnlockButton = screen.getByTestId('lock-unlock-dashboard'); + + await waitFor(() => expect(lockUnlockButton).toBeDisabled()); + }); + it('unlock dashboard should not be disabled for non integration created dashboards', async () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/dashboard/4`, + search: '', + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + server.use( + rest.get('http://localhost/api/v1/dashboards/4', (_, res, ctx) => + res(ctx.status(200), ctx.json(getNonIntegrationDashboardById)), + ), + ); + const { getByTestId } = render( + + + => Promise.resolve(), + exit: (): Promise => Promise.resolve(), + node: { current: null }, + }} + /> + + , + ); + + await waitFor(() => + expect(getByTestId('dashboard-title')).toHaveTextContent('thor'), + ); + + const dashboardSettingsTrigger = getByTestId('options'); + + await fireEvent.click(dashboardSettingsTrigger); + + const lockUnlockButton = screen.getByTestId('lock-unlock-dashboard'); + + await waitFor(() => expect(lockUnlockButton).not.toBeDisabled()); + }); +}); diff --git a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx index d3c959ca8b..1a60e74de9 100644 --- a/frontend/src/container/NewDashboard/DashboardDescription/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardDescription/index.tsx @@ -1,7 +1,16 @@ import './Description.styles.scss'; import { PlusOutlined } from '@ant-design/icons'; -import { Button, Card, Input, Modal, Popover, Tag, Typography } from 'antd'; +import { + Button, + Card, + Input, + Modal, + Popover, + Tag, + Tooltip, + Typography, +} from 'antd'; import logEvent from 'api/common/logEvent'; import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn'; import { dashboardHelpMessage } from 'components/facingIssueBtn/util'; @@ -308,7 +317,9 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element { alt="dashboard-img" style={{ width: '16px', height: '16px' }} /> - {title} + + {title} + {isDashboardLocked && }
@@ -334,13 +345,22 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
{(isAuthor || role === USER_ROLES.ADMIN) && ( - + + )} {!isDashboardLocked && editDashboard && ( diff --git a/frontend/src/mocks-server/__mockdata__/dashboards.ts b/frontend/src/mocks-server/__mockdata__/dashboards.ts index 4f1a6b78e7..40d9cb48d9 100644 --- a/frontend/src/mocks-server/__mockdata__/dashboards.ts +++ b/frontend/src/mocks-server/__mockdata__/dashboards.ts @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/no-duplicate-string */ export const dashboardSuccessResponse = { status: 'success', data: [ @@ -48,3 +49,53 @@ export const dashboardEmptyState = { status: 'sucsess', data: [], }; + +export const getDashboardById = { + status: 'success', + data: { + id: 1, + uuid: '1', + created_at: '2022-11-16T13:29:47.064874419Z', + created_by: 'integration', + updated_at: '2024-05-21T06:41:30.546630961Z', + updated_by: 'thor@avengers.io', + isLocked: true, + data: { + collapsableRowsMigrated: true, + description: '', + name: '', + panelMap: {}, + tags: ['linux'], + title: 'thor', + uploadedGrafana: false, + uuid: '', + version: '', + variables: {}, + }, + }, +}; + +export const getNonIntegrationDashboardById = { + status: 'success', + data: { + id: 1, + uuid: '1', + created_at: '2022-11-16T13:29:47.064874419Z', + created_by: 'thor', + updated_at: '2024-05-21T06:41:30.546630961Z', + updated_by: 'thor@avengers.io', + isLocked: true, + data: { + collapsableRowsMigrated: true, + description: '', + name: '', + panelMap: {}, + tags: ['linux'], + title: 'thor', + uploadedGrafana: false, + uuid: '', + version: '', + variables: {}, + }, + }, +}; diff --git a/frontend/src/mocks-server/handlers.ts b/frontend/src/mocks-server/handlers.ts index f96309380a..d46aa52420 100644 --- a/frontend/src/mocks-server/handlers.ts +++ b/frontend/src/mocks-server/handlers.ts @@ -1,7 +1,10 @@ import { rest } from 'msw'; import { billingSuccessResponse } from './__mockdata__/billing'; -import { dashboardSuccessResponse } from './__mockdata__/dashboards'; +import { + dashboardSuccessResponse, + getDashboardById, +} from './__mockdata__/dashboards'; import { explorerView } from './__mockdata__/explorer_views'; import { inviteUser } from './__mockdata__/invite_user'; import { licensesSuccessResponse } from './__mockdata__/licenses'; @@ -141,6 +144,10 @@ export const handlers = [ res(ctx.status(200), ctx.json(dashboardSuccessResponse)), ), + rest.get('http://localhost/api/v1/dashboards/4', (_, res, ctx) => + res(ctx.status(200), ctx.json(getDashboardById)), + ), + rest.get('http://localhost/api/v1/invite', (_, res, ctx) => res(ctx.status(200), ctx.json(inviteUser)), ), diff --git a/frontend/src/tests/test-utils.tsx b/frontend/src/tests/test-utils.tsx index ff2d3c7e51..4eced41eff 100644 --- a/frontend/src/tests/test-utils.tsx +++ b/frontend/src/tests/test-utils.tsx @@ -42,6 +42,7 @@ const mockStored = (role?: string): any => accessJwt: '', refreshJwt: '', }, + isLoggedIn: true, org: [ { createdAt: 0, From 38e694cd36a3f3272a4bb6c3e3cc917fe50231f0 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 30 Jul 2024 02:02:50 +0530 Subject: [PATCH 127/281] chore: only fetch top level operation from the selected time window (#5404) --- .../app/clickhouseReader/reader.go | 38 +++++++++---------- pkg/query-service/app/http_handler.go | 38 ++++++++++++++++++- pkg/query-service/interfaces/interface.go | 2 +- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index ccdffd88bd..632e6522a6 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -706,21 +706,25 @@ func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, erro return &services, nil } -func (r *ClickHouseReader) GetTopLevelOperations(ctx context.Context, skipConfig *model.SkipConfig, start, end time.Time) (*map[string][]string, *map[string][]string, *model.ApiError) { +func (r *ClickHouseReader) GetTopLevelOperations(ctx context.Context, skipConfig *model.SkipConfig, start, end time.Time, services []string) (*map[string][]string, *model.ApiError) { start = start.In(time.UTC) // The `top_level_operations` that have `time` >= start operations := map[string][]string{} - // All top level operations for a service - allOperations := map[string][]string{} - query := fmt.Sprintf(`SELECT DISTINCT name, serviceName, time FROM %s.%s`, r.TraceDB, r.topLevelOperationsTable) + // We can't use the `end` because the `top_level_operations` table has the most recent instances of the operations + // We can only use the `start` time to filter the operations + query := fmt.Sprintf(`SELECT name, serviceName, max(time) as ts FROM %s.%s WHERE time >= @start`, r.TraceDB, r.topLevelOperationsTable) + if len(services) > 0 { + query += ` AND serviceName IN @services` + } + query += ` GROUP BY name, serviceName ORDER BY ts DESC LIMIT 5000` - rows, err := r.db.Query(ctx, query) + rows, err := r.db.Query(ctx, query, clickhouse.Named("start", start), clickhouse.Named("services", services)) if err != nil { zap.L().Error("Error in processing sql query", zap.Error(err)) - return nil, nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in processing sql query")} + return nil, &model.ApiError{Typ: model.ErrorExec, Err: fmt.Errorf("error in processing sql query")} } defer rows.Close() @@ -728,25 +732,17 @@ func (r *ClickHouseReader) GetTopLevelOperations(ctx context.Context, skipConfig var name, serviceName string var t time.Time if err := rows.Scan(&name, &serviceName, &t); err != nil { - return nil, nil, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error in reading data")} + return nil, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error in reading data")} } if _, ok := operations[serviceName]; !ok { - operations[serviceName] = []string{} - } - if _, ok := allOperations[serviceName]; !ok { - allOperations[serviceName] = []string{} + operations[serviceName] = []string{"overflow_operation"} } if skipConfig.ShouldSkip(serviceName, name) { continue } - allOperations[serviceName] = append(allOperations[serviceName], name) - // We can't use the `end` because the `top_level_operations` table has the most recent instances of the operations - // We can only use the `start` time to filter the operations - if t.After(start) { - operations[serviceName] = append(operations[serviceName], name) - } + operations[serviceName] = append(operations[serviceName], name) } - return &operations, &allOperations, nil + return &operations, nil } func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.GetServicesParams, skipConfig *model.SkipConfig) (*[]model.ServiceItem, *model.ApiError) { @@ -755,7 +751,7 @@ func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.G return nil, &model.ApiError{Typ: model.ErrorExec, Err: ErrNoIndexTable} } - topLevelOps, allTopLevelOps, apiErr := r.GetTopLevelOperations(ctx, skipConfig, *queryParams.Start, *queryParams.End) + topLevelOps, apiErr := r.GetTopLevelOperations(ctx, skipConfig, *queryParams.Start, *queryParams.End, nil) if apiErr != nil { return nil, apiErr } @@ -779,7 +775,7 @@ func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.G // the top level operations are high, we want to warn to let user know the issue // with the instrumentation serviceItem.DataWarning = model.DataWarning{ - TopLevelOps: (*allTopLevelOps)[svc], + TopLevelOps: (*topLevelOps)[svc], } // default max_query_size = 262144 @@ -868,7 +864,7 @@ func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.G func (r *ClickHouseReader) GetServiceOverview(ctx context.Context, queryParams *model.GetServiceOverviewParams, skipConfig *model.SkipConfig) (*[]model.ServiceOverviewItem, *model.ApiError) { - topLevelOps, _, apiErr := r.GetTopLevelOperations(ctx, skipConfig, *queryParams.Start, *queryParams.End) + topLevelOps, apiErr := r.GetTopLevelOperations(ctx, skipConfig, *queryParams.Start, *queryParams.End, nil) if apiErr != nil { return nil, apiErr } diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 3e4ddfbfcf..f7482f41f2 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -1347,8 +1347,44 @@ func (aH *APIHandler) getServiceOverview(w http.ResponseWriter, r *http.Request) func (aH *APIHandler) getServicesTopLevelOps(w http.ResponseWriter, r *http.Request) { var start, end time.Time + var services []string - result, _, apiErr := aH.reader.GetTopLevelOperations(r.Context(), aH.skipConfig, start, end) + type topLevelOpsParams struct { + Service string `json:"service"` + Start string `json:"start"` + End string `json:"end"` + } + + var params topLevelOpsParams + err := json.NewDecoder(r.Body).Decode(¶ms) + if err != nil { + zap.L().Error("Error in getting req body for get top operations API", zap.Error(err)) + } + + if params.Service != "" { + services = []string{params.Service} + } + + startEpoch := params.Start + if startEpoch != "" { + startEpochInt, err := strconv.ParseInt(startEpoch, 10, 64) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, "Error reading start time") + return + } + start = time.Unix(0, startEpochInt) + } + endEpoch := params.End + if endEpoch != "" { + endEpochInt, err := strconv.ParseInt(endEpoch, 10, 64) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, "Error reading end time") + return + } + end = time.Unix(0, endEpochInt) + } + + result, apiErr := aH.reader.GetTopLevelOperations(r.Context(), aH.skipConfig, start, end, services) if apiErr != nil { RespondError(w, apiErr, nil) return diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index 385d48173b..239ecf02bb 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -23,7 +23,7 @@ type Reader interface { GetInstantQueryMetricsResult(ctx context.Context, query *model.InstantQueryMetricsParams) (*promql.Result, *stats.QueryStats, *model.ApiError) GetQueryRangeResult(ctx context.Context, query *model.QueryRangeParams) (*promql.Result, *stats.QueryStats, *model.ApiError) GetServiceOverview(ctx context.Context, query *model.GetServiceOverviewParams, skipConfig *model.SkipConfig) (*[]model.ServiceOverviewItem, *model.ApiError) - GetTopLevelOperations(ctx context.Context, skipConfig *model.SkipConfig, start, end time.Time) (*map[string][]string, *map[string][]string, *model.ApiError) + GetTopLevelOperations(ctx context.Context, skipConfig *model.SkipConfig, start, end time.Time, services []string) (*map[string][]string, *model.ApiError) GetServices(ctx context.Context, query *model.GetServicesParams, skipConfig *model.SkipConfig) (*[]model.ServiceItem, *model.ApiError) GetTopOperations(ctx context.Context, query *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) GetUsage(ctx context.Context, query *model.GetUsageParams) (*[]model.UsageItem, error) From 738d62c9cf7ab19fd1a98452fbd757c10610d9f2 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Tue, 30 Jul 2024 18:09:29 +0530 Subject: [PATCH 128/281] fix: show 0 as limit is user has set it to 0 (#5605) --- .../MultiIngestionSettings.tsx | 426 +++++++++--------- 1 file changed, 217 insertions(+), 209 deletions(-) diff --git a/frontend/src/container/IngestionSettings/MultiIngestionSettings.tsx b/frontend/src/container/IngestionSettings/MultiIngestionSettings.tsx index 18b053dbd8..0355d069fb 100644 --- a/frontend/src/container/IngestionSettings/MultiIngestionSettings.tsx +++ b/frontend/src/container/IngestionSettings/MultiIngestionSettings.tsx @@ -34,6 +34,7 @@ import dayjs, { Dayjs } from 'dayjs'; import { useGetAllIngestionsKeys } from 'hooks/IngestionKeys/useGetAllIngestionKeys'; import useDebouncedFn from 'hooks/useDebouncedFunction'; import { useNotifications } from 'hooks/useNotifications'; +import { isNil } from 'lodash-es'; import { ArrowUpRight, CalendarClock, @@ -605,243 +606,250 @@ function MultiIngestionSettings(): JSX.Element {
- {SIGNALS.map((signal) => ( -
-
-
{signal}
-
- {hasLimits(signal) ? ( - <> + {SIGNALS.map((signal) => { + const hasValidDayLimit = !isNil(limits[signal]?.config?.day?.size); + const hasValidSecondLimit = !isNil( + limits[signal]?.config?.second?.size, + ); + + return ( +
+
+
{signal}
+
+ {hasLimits(signal) ? ( + <> + + )} +
+
+ +
+ {activeAPIKey?.id === APIKey.id && + activeSignal?.signal === signal && + isEditAddLimitOpen ? ( +
- Limits - - )} -
-
+
+
+
+
Daily limit
+
+ Add a limit for data ingested daily{' '} +
+
-
- {activeAPIKey?.id === APIKey.id && - activeSignal?.signal === signal && - isEditAddLimitOpen ? ( - -
-
-
-
Daily limit
-
- Add a limit for data ingested daily{' '} +
+ + + + + + + + } + /> +
-
- - - - - - - - } - /> - +
+
+
Per Second limit
+
+ {' '} + Add a limit for data ingested every second{' '} +
+
+ +
+ + + + + + + + } + /> + +
-
-
-
Per Second limit
-
- {' '} - Add a limit for data ingested every second{' '} + {activeAPIKey?.id === APIKey.id && + activeSignal.signal === signal && + !isLoadingLimitForKey && + hasCreateLimitForIngestionKeyError && + createLimitForIngestionKeyError && + createLimitForIngestionKeyError?.error && ( +
+ {createLimitForIngestionKeyError?.error}
-
+ )} -
- - - - - - - - } - /> - -
-
-
+ {activeAPIKey?.id === APIKey.id && + activeSignal.signal === signal && + !isLoadingLimitForKey && + hasUpdateLimitForIngestionKeyError && + updateLimitForIngestionKeyError && ( +
+ {updateLimitForIngestionKeyError?.error} +
+ )} - {activeAPIKey?.id === APIKey.id && - activeSignal.signal === signal && - !isLoadingLimitForKey && - hasCreateLimitForIngestionKeyError && - createLimitForIngestionKeyError && - createLimitForIngestionKeyError?.error && ( -
- {createLimitForIngestionKeyError?.error} -
- )} - - {activeAPIKey?.id === APIKey.id && - activeSignal.signal === signal && - !isLoadingLimitForKey && - hasUpdateLimitForIngestionKeyError && - updateLimitForIngestionKeyError && ( -
- {updateLimitForIngestionKeyError?.error} -
- )} - - {activeAPIKey?.id === APIKey.id && - activeSignal.signal === signal && - isEditAddLimitOpen && ( -
- - + onClick={(): void => { + if (!hasLimits(signal)) { + handleAddLimit(APIKey, signal); + } else { + handleUpdateLimit(APIKey, limits[signal]); + } + }} + > + Save + + +
+ )} + + ) : ( +
+
+
+ Daily {' '}
- )} - - ) : ( -
-
-
- Daily {' '} -
-
- {limits[signal]?.config?.day?.size ? ( - <> - {getYAxisFormattedValue( - (limits[signal]?.metric?.day?.size || 0).toString(), - 'bytes', - )}{' '} - /{' '} - {getYAxisFormattedValue( - (limits[signal]?.config?.day?.size || 0).toString(), - 'bytes', - )} - - ) : ( - <> - NO LIMIT - - )} +
+ {hasValidDayLimit ? ( + <> + {getYAxisFormattedValue( + (limits[signal]?.metric?.day?.size || 0).toString(), + 'bytes', + )}{' '} + /{' '} + {getYAxisFormattedValue( + (limits[signal]?.config?.day?.size || 0).toString(), + 'bytes', + )} + + ) : ( + <> + NO LIMIT + + )} +
-
-
-
- Seconds -
+
+
+ Seconds +
-
- {limits[signal]?.config?.second?.size ? ( - <> - {getYAxisFormattedValue( - (limits[signal]?.metric?.second?.size || 0).toString(), - 'bytes', - )}{' '} - /{' '} - {getYAxisFormattedValue( - (limits[signal]?.config?.second?.size || 0).toString(), - 'bytes', - )} - - ) : ( - <> - NO LIMIT - - )} +
+ {hasValidSecondLimit ? ( + <> + {getYAxisFormattedValue( + (limits[signal]?.metric?.second?.size || 0).toString(), + 'bytes', + )}{' '} + /{' '} + {getYAxisFormattedValue( + (limits[signal]?.config?.second?.size || 0).toString(), + 'bytes', + )} + + ) : ( + <> + NO LIMIT + + )} +
-
- )} + )} +
-
- ))} + ); + })}
From 18b608a1d8b2a45ef4f15efe543fdbd8b2ecf651 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Tue, 30 Jul 2024 18:24:55 +0530 Subject: [PATCH 129/281] feat: update logEvent to silently handle errors (#5599) --- frontend/src/api/common/logEvent.ts | 3 ++- frontend/src/api/index.ts | 16 ++++++++++++++++ frontend/src/hooks/analytics/useAnalytics.tsx | 10 ---------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/frontend/src/api/common/logEvent.ts b/frontend/src/api/common/logEvent.ts index 212d382d77..a1bf3dba7c 100644 --- a/frontend/src/api/common/logEvent.ts +++ b/frontend/src/api/common/logEvent.ts @@ -1,4 +1,4 @@ -import axios from 'api'; +import { ApiBaseInstance as axios } from 'api'; import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; import { AxiosError } from 'axios'; import { ErrorResponse, SuccessResponse } from 'types/api'; @@ -21,6 +21,7 @@ const logEvent = async ( payload: response.data.data, }; } catch (error) { + console.error(error); return ErrorResponseHandler(error as AxiosError); } }; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index 1ec4cda601..7f5e2d476c 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -96,6 +96,10 @@ const interceptorRejected = async ( } }; +const interceptorRejectedBase = async ( + value: AxiosResponse, +): Promise> => Promise.reject(value); + const instance = axios.create({ baseURL: `${ENVIRONMENT.baseURL}${apiV1}`, }); @@ -140,6 +144,18 @@ ApiV4Instance.interceptors.response.use( ApiV4Instance.interceptors.request.use(interceptorsRequestResponse); // +// axios Base +export const ApiBaseInstance = axios.create({ + baseURL: `${ENVIRONMENT.baseURL}${apiV1}`, +}); + +ApiBaseInstance.interceptors.response.use( + interceptorsResponse, + interceptorRejectedBase, +); +ApiBaseInstance.interceptors.request.use(interceptorsRequestResponse); +// + // gateway Api V1 export const GatewayApiV1Instance = axios.create({ baseURL: `${ENVIRONMENT.baseURL}${gatewayApiV1}`, diff --git a/frontend/src/hooks/analytics/useAnalytics.tsx b/frontend/src/hooks/analytics/useAnalytics.tsx index 28213c9579..e3d2081766 100644 --- a/frontend/src/hooks/analytics/useAnalytics.tsx +++ b/frontend/src/hooks/analytics/useAnalytics.tsx @@ -32,16 +32,6 @@ const useAnalytics = (): any => { } }; - // useEffect(() => { - // // Perform any setup or cleanup related to the analytics library - // // For example, initialize analytics library here - - // // Clean-up function (optional) - // return () => { - // // Perform cleanup if needed - // }; - // }, []); // The empty dependency array ensures that this effect runs only once when the component mounts - return { trackPageView, trackEvent }; }; From f300518d61e979528229948c2ad6f9995d6a4187 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Wed, 31 Jul 2024 15:15:19 +0530 Subject: [PATCH 130/281] chore: add telemetry for channel types (#5602) --- pkg/query-service/model/response.go | 6 ++++++ pkg/query-service/telemetry/telemetry.go | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index d13ebd0cdb..7e8e883164 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -638,6 +638,12 @@ type AlertsInfo struct { LogsBasedAlerts int `json:"logsBasedAlerts"` MetricBasedAlerts int `json:"metricBasedAlerts"` TracesBasedAlerts int `json:"tracesBasedAlerts"` + SlackChannels int `json:"slackChannels"` + WebHookChannels int `json:"webHookChannels"` + PagerDutyChannels int `json:"pagerDutyChannels"` + OpsGenieChannels int `json:"opsGenieChannels"` + EmailChannels int `json:"emailChannels"` + MSTeamsChannels int `json:"microsoftTeamsChannels"` } type SavedViewsInfo struct { diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index e8675b3b90..8cfa7aaec4 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -293,6 +293,22 @@ func createTelemetry() { if err == nil { channels, err := telemetry.reader.GetChannels() if err == nil { + for _, channel := range *channels { + switch channel.Type { + case "slack": + alertsInfo.SlackChannels++ + case "webhook": + alertsInfo.WebHookChannels++ + case "pagerduty": + alertsInfo.PagerDutyChannels++ + case "opsgenie": + alertsInfo.OpsGenieChannels++ + case "email": + alertsInfo.EmailChannels++ + case "msteams": + alertsInfo.MSTeamsChannels++ + } + } savedViewsInfo, err := telemetry.reader.GetSavedViewsInfo(ctx) if err == nil { dashboardsAlertsData := map[string]interface{}{ @@ -309,6 +325,12 @@ func createTelemetry() { "totalSavedViews": savedViewsInfo.TotalSavedViews, "logsSavedViews": savedViewsInfo.LogsSavedViews, "tracesSavedViews": savedViewsInfo.TracesSavedViews, + "slackChannels": alertsInfo.SlackChannels, + "webHookChannels": alertsInfo.WebHookChannels, + "pagerDutyChannels": alertsInfo.PagerDutyChannels, + "opsGenieChannels": alertsInfo.OpsGenieChannels, + "emailChannels": alertsInfo.EmailChannels, + "msteamsChannels": alertsInfo.MSTeamsChannels, } // send event only if there are dashboards or alerts or channels if (dashboardsInfo.TotalDashboards > 0 || alertsInfo.TotalAlerts > 0 || len(*channels) > 0 || savedViewsInfo.TotalSavedViews > 0) && apiErr == nil { From aef935a81768fa994a939ad0efa185bb62077b58 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Wed, 31 Jul 2024 16:00:57 +0530 Subject: [PATCH 131/281] feat: faster traceID based filtering (#5607) * feat: faster traceID based filtering * chore: add error log --- .../app/clickhouseReader/reader.go | 24 +++ pkg/query-service/app/http_handler.go | 36 +++- pkg/query-service/app/traces/v3/utils.go | 183 ++++++++++++++++++ pkg/query-service/interfaces/interface.go | 2 + 4 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 pkg/query-service/app/traces/v3/utils.go diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 632e6522a6..6bfa1839ef 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -5001,3 +5001,27 @@ func (r *ClickHouseReader) LiveTailLogsV3(ctx context.Context, query string, tim } } } + +func (r *ClickHouseReader) GetMinAndMaxTimestampForTraceID(ctx context.Context, traceID []string) (int64, int64, error) { + var minTime, maxTime time.Time + + query := fmt.Sprintf("SELECT min(timestamp), max(timestamp) FROM %s.%s WHERE traceID IN ('%s')", + r.TraceDB, r.SpansTable, strings.Join(traceID, "','")) + + zap.L().Debug("GetMinAndMaxTimestampForTraceID", zap.String("query", query)) + + err := r.db.QueryRow(ctx, query).Scan(&minTime, &maxTime) + if err != nil { + zap.L().Error("Error while executing query", zap.Error(err)) + return 0, 0, err + } + + if minTime.IsZero() || maxTime.IsZero() { + zap.L().Debug("minTime or maxTime is zero") + return 0, 0, nil + } + + zap.L().Debug("GetMinAndMaxTimestampForTraceID", zap.Any("minTime", minTime), zap.Any("maxTime", maxTime)) + + return minTime.UnixNano(), maxTime.UnixNano(), nil +} diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index f7482f41f2..4eff84d50c 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -400,8 +400,8 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) { router.HandleFunc("/api/v1/disks", am.ViewAccess(aH.getDisks)).Methods(http.MethodGet) - // === Preference APIs === - + // === Preference APIs === + // user actions router.HandleFunc("/api/v1/user/preferences", am.ViewAccess(aH.getAllUserPreferences)).Methods(http.MethodGet) @@ -3213,6 +3213,22 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que } } + // WARN: Only works for AND operator in traces query + if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder { + // check if traceID is used as filter (with equal/similar operator) in traces query if yes add timestamp filter to queryRange params + isUsed, traceIDs := tracesV3.TraceIdFilterUsedWithEqual(queryRangeParams) + if isUsed == true && len(traceIDs) > 0 { + zap.L().Debug("traceID used as filter in traces query") + // query signoz_spans table with traceID to get min and max timestamp + min, max, err := aH.reader.GetMinAndMaxTimestampForTraceID(ctx, traceIDs) + if err == nil { + // add timestamp filter to queryRange params + tracesV3.AddTimestampFilters(min, max, queryRangeParams) + zap.L().Debug("post adding timestamp filter in traces query", zap.Any("queryRangeParams", queryRangeParams)) + } + } + } + result, errQuriesByName, err = aH.querier.QueryRange(ctx, queryRangeParams, spanKeys) if err != nil { @@ -3482,6 +3498,22 @@ func (aH *APIHandler) queryRangeV4(ctx context.Context, queryRangeParams *v3.Que } } + // WARN: Only works for AND operator in traces query + if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder { + // check if traceID is used as filter (with equal/similar operator) in traces query if yes add timestamp filter to queryRange params + isUsed, traceIDs := tracesV3.TraceIdFilterUsedWithEqual(queryRangeParams) + if isUsed == true && len(traceIDs) > 0 { + zap.L().Debug("traceID used as filter in traces query") + // query signoz_spans table with traceID to get min and max timestamp + min, max, err := aH.reader.GetMinAndMaxTimestampForTraceID(ctx, traceIDs) + if err == nil { + // add timestamp filter to queryRange params + tracesV3.AddTimestampFilters(min, max, queryRangeParams) + zap.L().Debug("post adding timestamp filter in traces query", zap.Any("queryRangeParams", queryRangeParams)) + } + } + } + result, errQuriesByName, err = aH.querierV2.QueryRange(ctx, queryRangeParams, spanKeys) if err != nil { diff --git a/pkg/query-service/app/traces/v3/utils.go b/pkg/query-service/app/traces/v3/utils.go new file mode 100644 index 0000000000..624458f919 --- /dev/null +++ b/pkg/query-service/app/traces/v3/utils.go @@ -0,0 +1,183 @@ +package v3 + +import ( + "strconv" + + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "go.signoz.io/signoz/pkg/query-service/utils" + "go.uber.org/zap" +) + +// check if traceId filter is used in traces query and return the list of traceIds +func TraceIdFilterUsedWithEqual(params *v3.QueryRangeParamsV3) (bool, []string) { + compositeQuery := params.CompositeQuery + if compositeQuery == nil { + return false, []string{} + } + var traceIds []string + var traceIdFilterUsed bool + + // Build queries for each builder query + for queryName, query := range compositeQuery.BuilderQueries { + if query.Expression != queryName && query.DataSource != v3.DataSourceTraces { + continue + } + + // check filter attribute + if query.Filters != nil && len(query.Filters.Items) != 0 { + for _, item := range query.Filters.Items { + + if item.Key.Key == "traceID" && (item.Operator == v3.FilterOperatorIn || + item.Operator == v3.FilterOperatorEqual) { + traceIdFilterUsed = true + // validate value + var err error + val := item.Value + val, err = utils.ValidateAndCastValue(val, item.Key.DataType) + if err != nil { + zap.L().Error("invalid value for key", zap.String("key", item.Key.Key), zap.Error(err)) + return false, []string{} + } + if val != nil { + fmtVal := extractFormattedStringValues(val) + traceIds = append(traceIds, fmtVal...) + } + } + } + } + + } + + zap.L().Debug("traceIds", zap.Any("traceIds", traceIds)) + return traceIdFilterUsed, traceIds +} + +func extractFormattedStringValues(v interface{}) []string { + // if it's pointer convert it to a value + v = getPointerValue(v) + + switch x := v.(type) { + case string: + return []string{x} + + case []interface{}: + if len(x) == 0 { + return []string{} + } + switch x[0].(type) { + case string: + values := []string{} + for _, val := range x { + values = append(values, val.(string)) + } + return values + default: + return []string{} + } + default: + return []string{} + } +} + +func getPointerValue(v interface{}) interface{} { + switch x := v.(type) { + case *uint8: + return *x + case *uint16: + return *x + case *uint32: + return *x + case *uint64: + return *x + case *int: + return *x + case *int8: + return *x + case *int16: + return *x + case *int32: + return *x + case *int64: + return *x + case *float32: + return *x + case *float64: + return *x + case *string: + return *x + case *bool: + return *x + case []interface{}: + values := []interface{}{} + for _, val := range x { + values = append(values, getPointerValue(val)) + } + return values + default: + return v + } +} + +func AddTimestampFilters(minTime int64, maxTime int64, params *v3.QueryRangeParamsV3) { + if minTime == 0 && maxTime == 0 { + return + } + + compositeQuery := params.CompositeQuery + if compositeQuery == nil { + return + } + // Build queries for each builder query + for queryName, query := range compositeQuery.BuilderQueries { + if query.Expression != queryName && query.DataSource != v3.DataSourceTraces { + continue + } + + addTimeStampFilter := false + + // check filter attribute + if query.Filters != nil && len(query.Filters.Items) != 0 { + for _, item := range query.Filters.Items { + if item.Key.Key == "traceID" && (item.Operator == v3.FilterOperatorIn || + item.Operator == v3.FilterOperatorEqual) { + addTimeStampFilter = true + } + } + } + + // add timestamp filter to query only if traceID filter along with equal/similar operator is used + if addTimeStampFilter { + timeFilters := []v3.FilterItem{ + { + Key: v3.AttributeKey{ + Key: "timestamp", + Type: v3.AttributeKeyTypeTag, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: true, + }, + Value: strconv.FormatUint(uint64(minTime), 10), + Operator: v3.FilterOperatorGreaterThanOrEq, + }, + { + Key: v3.AttributeKey{ + Key: "timestamp", + Type: v3.AttributeKeyTypeTag, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: true, + }, + Value: strconv.FormatUint(uint64(maxTime), 10), + Operator: v3.FilterOperatorLessThanOrEq, + }, + } + + // add new timestamp filter to query + if query.Filters == nil { + query.Filters = &v3.FilterSet{ + Items: timeFilters, + } + } else { + query.Filters.Items = append(query.Filters.Items, timeFilters...) + } + } + } +} diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index 239ecf02bb..fea923ac27 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -103,6 +103,8 @@ type Reader interface { CheckClickHouse(ctx context.Context) error GetMetricMetadata(context.Context, string, string) (*v3.MetricMetadataResponse, error) + + GetMinAndMaxTimestampForTraceID(ctx context.Context, traceID []string) (int64, int64, error) } type Querier interface { From 59121bd9328c6960e4cf3ef4d2160410097e638c Mon Sep 17 00:00:00 2001 From: Raj Kamal Singh <1133322+raj-k-singh@users.noreply.github.com> Date: Wed, 31 Jul 2024 17:52:51 +0530 Subject: [PATCH 132/281] chore: nginx integration: add note about adjusting regex if using custom log format (#5615) --- .../builtin_integrations/nginx/config/collect-logs.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/query-service/app/integrations/builtin_integrations/nginx/config/collect-logs.md b/pkg/query-service/app/integrations/builtin_integrations/nginx/config/collect-logs.md index 71712c503b..3e65cbc662 100644 --- a/pkg/query-service/app/integrations/builtin_integrations/nginx/config/collect-logs.md +++ b/pkg/query-service/app/integrations/builtin_integrations/nginx/config/collect-logs.md @@ -110,6 +110,13 @@ service: ``` +### If using non-default nginx log format, adjust log parsing regex + +If you are using a [custom nginx log format](https://docs.nginx.com/nginx/admin-guide/monitoring/logging/#setting-up-the-access-log), +please adjust the regex used for parsing logs in the receivers named +`filelog/nginx-access-logs` and `filelog/nginx-error-logs` in collector config. + + #### Set Environment Variables Set the following environment variables in your otel-collector environment: From 220edd139a17112641178fa848550fa86d1a7b87 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Wed, 31 Jul 2024 21:21:02 +0530 Subject: [PATCH 133/281] fix: do not send query_range api call on every keystroke (#5613) --- frontend/src/providers/QueryBuilder.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/providers/QueryBuilder.tsx b/frontend/src/providers/QueryBuilder.tsx index 3654ce3ef3..c3b50bbc7e 100644 --- a/frontend/src/providers/QueryBuilder.tsx +++ b/frontend/src/providers/QueryBuilder.tsx @@ -27,7 +27,7 @@ import { createIdFromObjectFields } from 'lib/createIdFromObjectFields'; import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName'; import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType'; import { replaceIncorrectObjectFields } from 'lib/replaceIncorrectObjectFields'; -import { get, merge, set } from 'lodash-es'; +import { cloneDeep, get, merge, set } from 'lodash-es'; import { createContext, PropsWithChildren, @@ -532,7 +532,7 @@ export function QueryBuilderProvider({ if (!panelType) { return newQueryItem; } - const queryItem = item as IBuilderQuery; + const queryItem = cloneDeep(item) as IBuilderQuery; const propsRequired = panelTypeDataSourceFormValuesMap[panelType as keyof PartialPanelTypes]?.[ queryItem.dataSource From fff9954da2d2d3e1904169ce0dcbc2f6ed888e83 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Wed, 31 Jul 2024 22:30:42 +0530 Subject: [PATCH 134/281] Schedule maintainence release changes (#5585) * feat: schedule maintenance feedback fixes * feat: schedule maintenance feedback fixes * feat: code refactor * feat: code refactor * feat: fixed incorrect payload values from start and endTime * feat: sorted list by updatedAt * feat: removed dependency on BE response prop - kind * feat: fixed timezone switching and adding different timezones --- .../plannedDowntime/createDowntimeSchedule.ts | 5 +- .../getAllDowntimeSchedules.ts | 3 +- frontend/src/constants/features.ts | 1 - .../DropdownWithSubMenu.styles.scss | 136 ----------- .../DropdownWithSubMenu.tsx | 230 ------------------ .../PlannedDowntime.styles.scss | 20 +- .../PlannedDowntime/PlannedDowntime.tsx | 32 ++- .../PlannedDowntimeDeleteModal.tsx | 14 +- .../PlannedDowntime/PlannedDowntimeForm.tsx | 221 +++++++++++------ .../PlannedDowntime/PlannedDowntimeList.tsx | 22 +- .../PlannedDowntime/PlannedDowntimeutils.ts | 128 +++++++++- frontend/src/pages/AlertList/index.tsx | 8 +- 12 files changed, 323 insertions(+), 497 deletions(-) delete mode 100644 frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss delete mode 100644 frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx diff --git a/frontend/src/api/plannedDowntime/createDowntimeSchedule.ts b/frontend/src/api/plannedDowntime/createDowntimeSchedule.ts index 128fb9bf69..26970264cf 100644 --- a/frontend/src/api/plannedDowntime/createDowntimeSchedule.ts +++ b/frontend/src/api/plannedDowntime/createDowntimeSchedule.ts @@ -1,6 +1,7 @@ import axios from 'api'; import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; import { AxiosError } from 'axios'; +import { Dayjs } from 'dayjs'; import { ErrorResponse, SuccessResponse } from 'types/api'; import { Recurrence } from './getAllDowntimeSchedules'; @@ -11,8 +12,8 @@ export interface DowntimeSchedulePayload { alertIds: string[]; schedule: { timezone?: string; - startTime?: string; - endTime?: string; + startTime?: string | Dayjs; + endTime?: string | Dayjs; recurrence?: Recurrence; }; } diff --git a/frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts b/frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts index 8e77606a3f..d323c63a19 100644 --- a/frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts +++ b/frontend/src/api/plannedDowntime/getAllDowntimeSchedules.ts @@ -1,6 +1,6 @@ import axios from 'api'; import { AxiosError, AxiosResponse } from 'axios'; -import { Option } from 'container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu'; +import { Option } from 'container/PlannedDowntime/PlannedDowntimeutils'; import { useQuery, UseQueryResult } from 'react-query'; export type Recurrence = { @@ -28,6 +28,7 @@ export interface DowntimeSchedules { createdBy: string | null; updatedAt: string | null; updatedBy: string | null; + kind: string | null; } export type PayloadProps = { data: DowntimeSchedules[] }; diff --git a/frontend/src/constants/features.ts b/frontend/src/constants/features.ts index bb905d0d69..f0b170545a 100644 --- a/frontend/src/constants/features.ts +++ b/frontend/src/constants/features.ts @@ -19,6 +19,5 @@ export enum FeatureKeys { OSS = 'OSS', ONBOARDING = 'ONBOARDING', CHAT_SUPPORT = 'CHAT_SUPPORT', - PLANNED_MAINTENANCE = 'PLANNED_MAINTENANCE', GATEWAY = 'GATEWAY', } diff --git a/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss b/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss deleted file mode 100644 index 893acc79c9..0000000000 --- a/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.styles.scss +++ /dev/null @@ -1,136 +0,0 @@ -.options { - width: 100%; - - .option { - padding: 8px 10px; - cursor: pointer; - overflow: auto; - } - - .option:hover { - background-color: var(--bg-slate-200); - } -} - -.submenu-container { - position: absolute; - top: 0; - right: 50%; - z-index: 1; - background-color: var(--bg-ink-400); - border: 1px solid var(--bg-slate-500); - max-height: 300px; - overflow-y: auto; - width: 160px; - display: flex; - flex-direction: column; - gap: 6px; - padding: 12px; - border-radius: 4px; - - .submenu-checkbox { - padding: 0px; - } -} - -.dropdown-submenu { - .dropdown-input { - box-sizing: border-box; - margin: 0; - padding: 4.5px 11px; - color: rgba(255, 255, 255, 0.85); - font-size: 13px; - line-height: 1.6153846153846154; - list-style: none; - font-family: Inter; - position: relative; - display: inline-block; - min-width: 0; - transition: all 0.2s; - } - - .ant-popover-inner { - padding: 0px; - } -} - -.options-container { - position: relative; - --arrow-x: 175px; - --arrow-y: 266px; - width: 350px; - box-sizing: border-box; - margin: 0; - padding: 4px; - color: var(--bg-vanilla-400); - font-size: 12px; - line-height: 1.6153846153846154; - list-style: none; - font-family: Inter; - z-index: 1050; - overflow: hidden; - font-variant: initial; - background-color: var(--bg-ink-400); - border-radius: 2px; - outline: none; - box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.3); -} - -.submenu-popover { - .ant-popover-inner { - padding: 0px; - } - .option { - padding: 8px 10px; - cursor: pointer; - overflow: auto; - } - - .option:hover { - background-color: var(--bg-slate-200); - } -} - -.save-option-btn { - height: 24px; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - align-self: flex-end; - width: 60px; -} - -.submenu-header { - color: var(--bg-vanilla-200); - font-family: Inter; - font-size: 12px; - font-style: normal; - font-weight: 400; - padding-bottom: 8px; - text-transform: uppercase; -} - -.lightMode { - .option:hover { - background-color: var(--bg-vanilla-200); - } - - .submenu-container { - background-color: var(--bg-vanilla-100); - border: 1px solid var(--bg-vanilla-300); - } - - .dropdown-submenu { - .dropdown-input { - color: var(--bg-slate-100); - } - } - .options-container { - color: var(--bg-slate-100); - background-color: var(--bg-vanilla-100); - } - .submenu-header { - color: var(--bg-slate-200); - } -} diff --git a/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx b/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx deleted file mode 100644 index d2a3e041bf..0000000000 --- a/frontend/src/container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu.tsx +++ /dev/null @@ -1,230 +0,0 @@ -import './DropdownWithSubMenu.styles.scss'; - -import { CheckOutlined } from '@ant-design/icons'; -import { Button, Checkbox, Popover, Typography } from 'antd'; -import { CheckboxChangeEvent } from 'antd/es/checkbox'; -import { FormInstance } from 'antd/lib'; -import { useEffect, useState } from 'react'; -import { popupContainer } from 'utils/selectPopupContainer'; - -import { recurrenceOptions } from '../PlannedDowntimeutils'; - -interface SubOption { - label: string; - value: string; -} - -export interface Option { - label: string; - value: string; - submenu?: SubOption[]; -} - -interface DropdownProps { - options: Option[]; - form: FormInstance; - setRecurrenceOption: React.Dispatch< - React.SetStateAction - >; -} - -export function DropdownWithSubMenu(props: DropdownProps): JSX.Element { - const { options, form, setRecurrenceOption } = props; - const [selectedOption, setSelectedOption] = useState
- ); -} - -export const recurrenceOption: Option[] = [ - recurrenceOptions.doesNotRepeat, - recurrenceOptions.daily, - { - ...recurrenceOptions.weekly, - submenu: [ - { label: 'Monday', value: 'monday' }, - { label: 'Tuesday', value: 'tuesday' }, - { label: 'Wednesday', value: 'wednesday' }, - { label: 'Thrusday', value: 'thrusday' }, - { label: 'Friday', value: 'friday' }, - { label: 'Saturday', value: 'saturday' }, - { label: 'Sunday', value: 'sunday' }, - ], - }, - recurrenceOptions.monthly, -]; diff --git a/frontend/src/container/PlannedDowntime/PlannedDowntime.styles.scss b/frontend/src/container/PlannedDowntime/PlannedDowntime.styles.scss index a4982397ea..41949142fa 100644 --- a/frontend/src/container/PlannedDowntime/PlannedDowntime.styles.scss +++ b/frontend/src/container/PlannedDowntime/PlannedDowntime.styles.scss @@ -65,6 +65,18 @@ background: var(--bg-ink-300); } } + + .alert-rule-form { + display: flex; + gap: 8px; + align-items: end; + + .alert-rule-info { + font-size: 11px; + font-weight: 300; + color: var(--bg-vanilla-400); + } + } } .alert-rule-tags { @@ -169,7 +181,7 @@ } } - .view-created-at { + .schedule-created-at { border: 1px solid var(--bg-slate-500); background-color: var(--bg-ink-400); border-top: 0px; @@ -316,7 +328,7 @@ } } -.delete-view-modal { +.delete-schedule-modal { width: calc(100% - 30px) !important; /* Adjust the 20px as needed */ max-width: 384px; .ant-modal-content { @@ -429,7 +441,7 @@ color: var(--bg-ink-500); } - .view-created-at { + .schedule-created-at { border: 1px solid var(--bg-vanilla-300); background-color: var(--bg-vanilla-100); .ant-typography { @@ -459,7 +471,7 @@ } } - .delete-view-modal { + .delete-schedule-modal { .ant-modal-content { border: 1px solid var(--bg-vanilla-200); background: var(--bg-vanilla-100); diff --git a/frontend/src/container/PlannedDowntime/PlannedDowntime.tsx b/frontend/src/container/PlannedDowntime/PlannedDowntime.tsx index b9315bbe7e..fa4ea6f29f 100644 --- a/frontend/src/container/PlannedDowntime/PlannedDowntime.tsx +++ b/frontend/src/container/PlannedDowntime/PlannedDowntime.tsx @@ -13,7 +13,7 @@ import { import dayjs from 'dayjs'; import { useNotifications } from 'hooks/useNotifications'; import { Search } from 'lucide-react'; -import React, { ChangeEvent, useState } from 'react'; +import React, { ChangeEvent, useEffect, useState } from 'react'; import { useQuery } from 'react-query'; import { PlannedDowntimeDeleteModal } from './PlannedDowntimeDeleteModal'; @@ -48,6 +48,12 @@ export function PlannedDowntime(): JSX.Element { [data], ); + useEffect(() => { + if (!isOpen) { + form.resetFields(); + } + }, [form, isOpen]); + const [searchValue, setSearchValue] = React.useState(''); const [deleteData, setDeleteData] = useState<{ id: number; name: string }>(); const [isEditMode, setEditMode] = useState(false); @@ -128,17 +134,19 @@ export function PlannedDowntime(): JSX.Element { setEditMode={setEditMode} searchValue={searchValue} /> - + {isOpen && ( + + )} { + const hideDeleteScheduleModal = (): void => { setIsDeleteModalOpen(false); }; return ( Delete view} + className="delete-schedule-modal" + title={Delete Schedule} open={isDeleteModalOpen} closable={false} - onCancel={hideDeleteViewModal} + onCancel={hideDeleteScheduleModal} footer={[ , ]} > - {`Are you sure you want to delete - ${downtimeSchedule} view? Deleting a view is irreversible and cannot be undone.`} + {`Are you sure you want to delete - ${downtimeSchedule} schedule? Deleting a schedule is irreversible and cannot be undone.`} ); diff --git a/frontend/src/container/PlannedDowntime/PlannedDowntimeForm.tsx b/frontend/src/container/PlannedDowntime/PlannedDowntimeForm.tsx index 90fba78d40..76b0507558 100644 --- a/frontend/src/container/PlannedDowntime/PlannedDowntimeForm.tsx +++ b/frontend/src/container/PlannedDowntime/PlannedDowntimeForm.tsx @@ -1,3 +1,5 @@ +/* eslint-disable no-nested-ternary */ +/* eslint-disable sonarjs/no-identical-functions */ import './PlannedDowntime.styles.scss'; import 'dayjs/locale/en'; @@ -26,25 +28,30 @@ import { ModalTitle, } from 'container/PipelinePage/PipelineListsView/styles'; import dayjs from 'dayjs'; +import timezone from 'dayjs/plugin/timezone'; +import utc from 'dayjs/plugin/utc'; import { useNotifications } from 'hooks/useNotifications'; import { defaultTo, isEmpty } from 'lodash-es'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { ALL_TIME_ZONES } from 'utils/timeZoneUtil'; -import { - DropdownWithSubMenu, - Option, - recurrenceOption, -} from './DropdownWithSubMenu/DropdownWithSubMenu'; import { AlertRuleTags } from './PlannedDowntimeList'; import { createEditDowntimeSchedule, getAlertOptionsFromIds, getDurationInfo, + getEndTime, + handleTimeConvertion, + isScheduleRecurring, recurrenceOptions, + recurrenceOptionWithSubmenu, + recurrenceWeeklyOptions, } from './PlannedDowntimeutils'; dayjs.locale('en'); +dayjs.extend(utc); +dayjs.extend(timezone); + interface PlannedDowntimeFormData { name: string; startTime: dayjs.Dayjs | string; @@ -93,10 +100,19 @@ export function PlannedDowntimeForm( >([]); const alertRuleFormName = 'alertRules'; const [saveLoading, setSaveLoading] = useState(false); - const [durationUnit, setDurationUnit] = useState('m'); - const [selectedRecurrenceOption, setSelectedRecurrenceOption] = useState< - string | null - >(); + const [durationUnit, setDurationUnit] = useState( + getDurationInfo(initialValues.schedule?.recurrence?.duration as string) + ?.unit || 'm', + ); + + const [recurrenceType, setRecurrenceType] = useState( + (initialValues.schedule?.recurrence?.repeatType as string) || + recurrenceOptions.doesNotRepeat.value, + ); + + const timezoneInitialValue = !isEmpty(initialValues.schedule?.timezone) + ? (initialValues.schedule?.timezone as string) + : undefined; const { notifications } = useNotifications(); @@ -107,9 +123,7 @@ export function PlannedDowntimeForm( const saveHanlder = useCallback( async (values: PlannedDowntimeFormData) => { - const formatDate = (date: string | dayjs.Dayjs): string | undefined => - !isEmpty(date) ? dayjs(date).format('YYYY-MM-DDTHH:mm:ss[Z]') : undefined; - + const shouldKeepLocalTime = !isEditMode; const createEditProps: DowntimeScheduleUpdatePayload = { data: { alertIds: values.alertRules @@ -117,9 +131,21 @@ export function PlannedDowntimeForm( .filter((alert) => alert !== undefined) as string[], name: values.name, schedule: { - startTime: formatDate(values.startTime), + startTime: handleTimeConvertion( + values.startTime, + timezoneInitialValue, + values.timezone, + shouldKeepLocalTime, + ), timezone: values.timezone, - endTime: formatDate(values.endTime), + endTime: values.endTime + ? handleTimeConvertion( + values.endTime, + timezoneInitialValue, + values.timezone, + shouldKeepLocalTime, + ) + : undefined, recurrence: values.recurrence as Recurrence, }, }, @@ -152,25 +178,41 @@ export function PlannedDowntimeForm( } setSaveLoading(false); }, - [initialValues.id, isEditMode, notifications, refetchAllSchedules, setIsOpen], + [ + initialValues.id, + isEditMode, + notifications, + refetchAllSchedules, + setIsOpen, + timezoneInitialValue, + ], ); const onFinish = async (values: PlannedDowntimeFormData): Promise => { const recurrenceData: Recurrence | undefined = - (values?.recurrenceSelect?.repeatType as Option)?.value === - recurrenceOptions.doesNotRepeat.value + values?.recurrence?.repeatType === recurrenceOptions.doesNotRepeat.value ? undefined : { duration: values.recurrence?.duration ? `${values.recurrence?.duration}${durationUnit}` : undefined, endTime: !isEmpty(values.endTime) - ? (values.endTime as string) + ? handleTimeConvertion( + values.endTime, + timezoneInitialValue, + values.timezone, + !isEditMode, + ) : undefined, - startTime: values.startTime as string, - repeatOn: !values?.recurrenceSelect?.repeatOn?.length + startTime: handleTimeConvertion( + values.startTime, + timezoneInitialValue, + values.timezone, + !isEditMode, + ), + repeatOn: !values.recurrence?.repeatOn?.length ? undefined - : values?.recurrenceSelect?.repeatOn, - repeatType: (values?.recurrenceSelect?.repeatType as Option)?.value, + : values.recurrence?.repeatOn, + repeatType: values.recurrence?.repeatType, }; const payloadValues = { ...values, recurrence: recurrenceData }; @@ -226,19 +268,15 @@ export function PlannedDowntimeForm( initialValues.alertIds || [], alertOptions, ), - endTime: initialValues.schedule?.endTime - ? dayjs(initialValues.schedule?.endTime) - : '', + endTime: getEndTime(initialValues) ? dayjs(getEndTime(initialValues)) : '', startTime: initialValues.schedule?.startTime ? dayjs(initialValues.schedule?.startTime) : '', - recurrenceSelect: initialValues.schedule?.recurrence - ? initialValues.schedule?.recurrence - : { - repeatType: recurrenceOptions.doesNotRepeat, - }, recurrence: { ...initialValues.schedule?.recurrence, + repeatType: !isScheduleRecurring(initialValues?.schedule) + ? recurrenceOptions.doesNotRepeat.value + : (initialValues.schedule?.recurrence?.repeatType as string), duration: getDurationInfo( initialValues.schedule?.recurrence?.duration as string, )?.value, @@ -283,101 +321,118 @@ export function PlannedDowntimeForm( layout="vertical" className="createForm" onFinish={onFinish} + onValuesChange={(): void => { + setRecurrenceType(form.getFieldValue('recurrence')?.repeatType as string); + }} autoComplete="off" > - + ({ + value: value ? dayjs(value).tz(timezoneInitialValue) : undefined, + })} > + dayjs(date).tz(timezoneInitialValue).format(customFormat) + } showTime renderExtraFooter={datePickerFooter} + showNow={false} popupClassName="datePicker" /> - - {selectedRecurrenceOption !== recurrenceOptions.doesNotRepeat.value && ( + {recurrenceType === recurrenceOptions.weekly.value && ( - setDurationUnit(value)} - > - Mins - Hours - - } - className="duration-input" - type="number" - placeholder="Enter duration" - min={1} - onWheel={(e): void => e.currentTarget.blur()} + { + setDurationUnit(value); + }} + > + Mins + Hours + + } + className="duration-input" + type="number" + placeholder="Enter duration" + min={1} + onWheel={(e): void => e.currentTarget.blur()} + /> + + )} + + (option?.label as string)?.toLowerCase()?.includes(input.toLowerCase()) + } notFoundContent={ isLoading ? ( diff --git a/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx b/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx index b8c1ec405a..6202ca8d31 100644 --- a/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx +++ b/frontend/src/container/PlannedDowntime/PlannedDowntimeList.tsx @@ -22,6 +22,7 @@ import { formatDateTime, getAlertOptionsFromIds, getDuration, + getEndTime, recurrenceInfo, } from './PlannedDowntimeutils'; @@ -122,6 +123,7 @@ export function CollapseListContent({ updated_at, updated_by_name, alertOptions, + timezone, }: { created_at?: string; created_by_name?: string; @@ -131,6 +133,7 @@ export function CollapseListContent({ updated_at?: string; updated_by_name?: string; alertOptions?: DefaultOptionType[]; + timezone?: string; }): JSX.Element { const renderItems = (title: string, value: ReactNode): JSX.Element => (
@@ -180,6 +183,7 @@ export function CollapseListContent({ '-' ), )} + {renderItems('Timezone', {timezone || '-'})} {renderItems('Repeats', {recurrenceInfo(repeats)})} {renderItems( 'Alerts silenced', @@ -220,13 +224,15 @@ export function CustomCollapseList( setModalOpen, handleDeleteDowntime, setEditMode, + kind, } = props; const scheduleTime = schedule?.startTime ? schedule.startTime : createdAt; // Combine time and date const formattedDateAndTime = `Start time ⎯ ${formatDateTime( defaultTo(scheduleTime, ''), - )}`; + )} ${schedule?.timezone}`; + const endTime = getEndTime({ kind, schedule }); return ( <> @@ -255,15 +261,19 @@ export function CustomCollapseList( -
+
{formattedDateAndTime}
@@ -314,6 +324,12 @@ export function PlannedDowntimeList({ const { notifications } = useNotifications(); const tableData = (downtimeSchedules.data?.data?.data || []) + .sort((a, b): number => { + if (a?.updatedAt && b?.updatedAt) { + return b.updatedAt.localeCompare(a.updatedAt); + } + return 0; + }) ?.filter( (data) => data?.name?.includes(searchValue.toLocaleString()) || diff --git a/frontend/src/container/PlannedDowntime/PlannedDowntimeutils.ts b/frontend/src/container/PlannedDowntime/PlannedDowntimeutils.ts index d9d4edd30f..7d0745dc5e 100644 --- a/frontend/src/container/PlannedDowntime/PlannedDowntimeutils.ts +++ b/frontend/src/container/PlannedDowntime/PlannedDowntimeutils.ts @@ -12,6 +12,7 @@ import updateDowntimeSchedule, { } from 'api/plannedDowntime/updateDowntimeSchedule'; import { showErrorNotification } from 'components/ExplorerCard/utils'; import dayjs from 'dayjs'; +import { isEmpty, isEqual } from 'lodash-es'; import { UseMutateAsyncFunction } from 'react-query'; import { ErrorResponse, SuccessResponse } from 'types/api'; @@ -42,7 +43,8 @@ export const formatDateTime = (dateTimeString?: string | null): string => { if (!dateTimeString) { return 'N/A'; } - return dayjs(dateTimeString).format('MMM DD, YYYY h:mm A'); + + return dayjs(dateTimeString.slice(0, 19)).format('MMM DD, YYYY h:mm A'); }; export const getAlertOptionsFromIds = ( @@ -149,6 +151,15 @@ export const recurrenceOptions = { monthly: { label: 'Monthly', value: 'monthly' }, }; +export const recurrenceWeeklyOptions = { + monday: { label: 'Monday', value: 'monday' }, + tuesday: { label: 'Tuesday', value: 'tuesday' }, + wednesday: { label: 'Wednesday', value: 'wednesday' }, + thursday: { label: 'Thursday', value: 'thursday' }, + friday: { label: 'Friday', value: 'friday' }, + saturday: { label: 'Saturday', value: 'saturday' }, + sunday: { label: 'Sunday', value: 'sunday' }, +}; interface DurationInfo { value: number; unit: string; @@ -160,17 +171,108 @@ export function getDurationInfo( if (!durationString) { return null; } - // Regular expression to extract value and unit from the duration string - const durationRegex = /(\d+)([hms])/; - // Match the value and unit parts in the duration string - const match = durationString.match(durationRegex); - if (match && match.length >= 3) { - // Extract value and unit from the match - const value = parseInt(match[1], 10); - const unit = match[2]; - // Return duration info object - return { value, unit }; + + // Regular expressions to extract hours, minutes + const hoursRegex = /(\d+)h/; + const minutesRegex = /(\d+)m/; + + // Extract hours, minutes from the duration string + const hoursMatch = durationString.match(hoursRegex); + const minutesMatch = durationString.match(minutesRegex); + + // Convert extracted values to integers, defaulting to 0 if not found + const hours = hoursMatch ? parseInt(hoursMatch[1], 10) : 0; + const minutes = minutesMatch ? parseInt(minutesMatch[1], 10) : 0; + + // If there are no minutes and only hours, return the hours + if (hours > 0 && minutes === 0) { + return { value: hours, unit: 'h' }; + } + + // Otherwise, calculate the total duration in minutes + const totalMinutes = hours * 60 + minutes; + return { value: totalMinutes, unit: 'm' }; +} + +export interface Option { + label: string; + value: string; +} + +export const recurrenceOptionWithSubmenu: Option[] = [ + recurrenceOptions.doesNotRepeat, + recurrenceOptions.daily, + recurrenceOptions.weekly, + recurrenceOptions.monthly, +]; + +export const getRecurrenceOptionFromValue = ( + value?: string | Option | null, +): Option | null | undefined => { + if (!value) { + return null; + } + if (typeof value === 'string') { + return Object.values(recurrenceOptions).find( + (option) => option.value === value, + ); } - // If no value or unit part found, return null - return null; + return value; +}; + +export const getEndTime = ({ + kind, + schedule, +}: Partial< + DowntimeSchedules & { + editMode: boolean; + } +>): string | dayjs.Dayjs => { + if (kind === 'fixed') { + return schedule?.endTime || ''; + } + + return schedule?.recurrence?.endTime || ''; +}; + +export const isScheduleRecurring = ( + schedule?: DowntimeSchedules['schedule'], +): boolean => (schedule ? !isEmpty(schedule?.recurrence) : false); + +function convertUtcOffsetToTimezoneOffset(offsetMinutes: number): string { + const sign = offsetMinutes >= 0 ? '+' : '-'; + const absOffset = Math.abs(offsetMinutes); + const hours = String(Math.floor(absOffset / 60)).padStart(2, '0'); + const minutes = String(absOffset % 60).padStart(2, '0'); + return `${sign}${hours}:${minutes}`; +} + +export function formatWithTimezone( + dateValue?: string | dayjs.Dayjs, + timezone?: string, +): string { + const parsedDate = + typeof dateValue === 'string' ? dateValue : dateValue?.format(); + console.log('dateValue', parsedDate, 'timezone', timezone); + // Get the target timezone offset + const targetOffset = convertUtcOffsetToTimezoneOffset( + dayjs(dateValue).tz(timezone).utcOffset(), + ); + + return `${parsedDate?.substring(0, 19)}${targetOffset}`; +} + +export function handleTimeConvertion( + dateValue: string | dayjs.Dayjs, + timezoneInit?: string, + timezone?: string, + shouldKeepLocalTime?: boolean, +): string { + const timezoneChanged = !isEqual(timezoneInit, timezone); + const initialTime = dayjs(dateValue).tz(timezoneInit); + + const formattedTime = formatWithTimezone(initialTime, timezone); + return timezoneChanged + ? formattedTime + : dayjs(dateValue).tz(timezone, shouldKeepLocalTime).format(); } diff --git a/frontend/src/pages/AlertList/index.tsx b/frontend/src/pages/AlertList/index.tsx index 77769b2666..1bf3d9a6ea 100644 --- a/frontend/src/pages/AlertList/index.tsx +++ b/frontend/src/pages/AlertList/index.tsx @@ -1,10 +1,8 @@ import { Tabs } from 'antd'; import { TabsProps } from 'antd/lib'; -import { FeatureKeys } from 'constants/features'; import AllAlertRules from 'container/ListAlertRules'; import { PlannedDowntime } from 'container/PlannedDowntime/PlannedDowntime'; import TriggeredAlerts from 'container/TriggeredAlerts'; -import useFeatureFlags from 'hooks/useFeatureFlag'; import useUrlQuery from 'hooks/useUrlQuery'; import history from 'lib/history'; import { useLocation } from 'react-router-dom'; @@ -13,10 +11,6 @@ function AllAlertList(): JSX.Element { const urlQuery = useUrlQuery(); const location = useLocation(); - const isPlannedDowntimeEnabled = useFeatureFlags( - FeatureKeys.PLANNED_MAINTENANCE, - )?.active; - const tab = urlQuery.get('tab'); const items: TabsProps['items'] = [ { label: 'Alert Rules', key: 'AlertRules', children: }, @@ -26,7 +20,7 @@ function AllAlertList(): JSX.Element { children: , }, { - label: isPlannedDowntimeEnabled ? 'Configuration' : '', + label: 'Configuration', key: 'Configuration', children: , }, From ae19eaa76a09c779a51c72b46c4eb2ec21380d58 Mon Sep 17 00:00:00 2001 From: rahulkeswani101 Date: Thu, 1 Aug 2024 08:49:26 +0530 Subject: [PATCH 135/281] feat: redirect to original page after login (#5604) --- frontend/src/AppRoutes/Private.tsx | 3 +-- frontend/src/container/Login/index.tsx | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/AppRoutes/Private.tsx b/frontend/src/AppRoutes/Private.tsx index 669def6f44..43402fdbb2 100644 --- a/frontend/src/AppRoutes/Private.tsx +++ b/frontend/src/AppRoutes/Private.tsx @@ -76,9 +76,8 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { isUserFetching: false, }, }); - if (!isLoggedIn) { - history.push(ROUTES.LOGIN); + history.push(ROUTES.LOGIN, { from: pathname }); } }; diff --git a/frontend/src/container/Login/index.tsx b/frontend/src/container/Login/index.tsx index df9a1c6a37..81a6c535a5 100644 --- a/frontend/src/container/Login/index.tsx +++ b/frontend/src/container/Login/index.tsx @@ -163,8 +163,15 @@ function Login({ response.payload.accessJwt, response.payload.refreshJwt, ); + if (history?.location?.state) { + const historyState = history?.location?.state as any; - history.push(ROUTES.APPLICATION); + if (historyState?.from) { + history.push(historyState?.from); + } else { + history.push(ROUTES.APPLICATION); + } + } } else { notifications.error({ message: response.error || t('unexpected_error'), From d9e94a4067308a4c60813b0ca8659287038ed6c9 Mon Sep 17 00:00:00 2001 From: UnCool-0x <162315894+UnCool-0x@users.noreply.github.com> Date: Thu, 1 Aug 2024 09:27:13 +0530 Subject: [PATCH 136/281] feat: windows onboarding in cloud (#5525) * feat: windows onboarding in cloud * fix: missed file instructions * feat: assigned vars * feat: windows onboarding minor changes --------- --- ...indows-quickStart-instrumentApplication.md | 67 ++++ ...otnet-windows-quickStart-runApplication.md | 10 + ...indows-recommended-installOtelCollector.md | 12 + ...ndows-recommended-instrumentApplication.md | 63 ++++ ...tnet-windows-recommended-runApplication.md | 18 ++ ...indows-quickStart-instrumentApplication.md | 62 ++++ ...lixir-windows-quickStart-runApplication.md | 6 + ...indows-recommended-installOtelCollector.md | 105 ++++++ ...ndows-recommended-instrumentApplication.md | 57 ++++ ...ixir-windows-recommended-runApplication.md | 55 ++++ ...indows-quickStart-instrumentApplication.md | 123 +++++++ ...olang-windows-quickStart-runApplication.md | 13 + ...indows-recommended-installOtelCollector.md | 12 + ...ndows-recommended-instrumentApplication.md | 122 +++++++ ...lang-windows-recommended-runApplication.md | 16 + ...indows-quickStart-instrumentApplication.md | 27 ++ ...jboss-windows-quickStart-runApplication.md | 7 + ...indows-recommended-installOtelCollector.md | 12 + ...ndows-recommended-instrumentApplication.md | 8 + ...boss-windows-recommended-runApplication.md | 13 + ...indows-quickStart-instrumentApplication.md | 11 + ...thers-windows-quickStart-runApplication.md | 15 + ...indows-recommended-installOtelCollector.md | 12 + ...ndows-recommended-instrumentApplication.md | 15 + ...hers-windows-recommended-runApplication.md | 7 + ...indows-quickStart-instrumentApplication.md | 11 + ...gBoot-windows-quickStart-runApplication.md | 21 ++ ...indows-recommended-installOtelCollector.md | 12 + ...ndows-recommended-instrumentApplication.md | 11 + ...Boot-windows-recommended-runApplication.md | 11 + ...indows-quickStart-instrumentApplication.md | 11 + ...omcat-windows-quickStart-runApplication.md | 14 + ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 14 + ...mcat-windows-recommended-runApplication.md | 15 + ...indows-quickStart-instrumentApplication.md | 86 +++++ ...gular-windows-quickStart-runApplication.md | 9 + ...indows-recommended-installOtelCollector.md | 21 ++ ...ndows-recommended-instrumentApplication.md | 82 +++++ ...ular-windows-recommended-runApplication.md | 22 ++ ...indows-quickStart-instrumentApplication.md | 55 ++++ ...press-windows-quickStart-runApplication.md | 13 + ...indows-recommended-installOtelCollector.md | 20 ++ ...ndows-recommended-instrumentApplication.md | 53 +++ ...ress-windows-recommended-runApplication.md | 5 + ...indows-quickStart-instrumentApplication.md | 85 +++++ ...estjs-windows-quickStart-runApplication.md | 9 + ...indows-recommended-installOtelCollector.md | 3 + ...ndows-recommended-instrumentApplication.md | 98 ++++++ ...stjs-windows-recommended-runApplication.md | 16 + ...indows-quickStart-instrumentApplication.md | 57 ++++ ...odejs-windows-quickStart-runApplication.md | 9 + ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 57 ++++ ...dejs-windows-recommended-runApplication.md | 32 ++ ...indows-quickStart-instrumentApplication.md | 71 ++++ ...thers-windows-quickStart-runApplication.md | 18 ++ ...indows-recommended-installOtelCollector.md | 98 ++++++ ...ndows-recommended-instrumentApplication.md | 74 +++++ ...hers-windows-recommended-runApplication.md | 44 +++ ...indows-quickStart-instrumentApplication.md | 77 +++++ ...actjs-windows-quickStart-runApplication.md | 18 ++ ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 71 ++++ ...ctjs-windows-recommended-runApplication.md | 43 +++ ...indows-quickStart-instrumentApplication.md | 127 ++++++++ .../php-windows-quickStart-runApplication.md | 7 + ...indows-recommended-installOtelCollector.md | 3 + ...ndows-recommended-instrumentApplication.md | 108 +++++++ .../php-windows-recommended-runApplication.md | 8 + ...indows-quickStart-instrumentApplication.md | 37 +++ ...jango-windows-quickStart-runApplication.md | 22 ++ ...indows-recommended-installOtelCollector.md | 98 ++++++ ...ndows-recommended-instrumentApplication.md | 37 +++ ...ango-windows-recommended-runApplication.md | 47 +++ ...indows-quickStart-instrumentApplication.md | 32 ++ ...alcon-windows-quickStart-runApplication.md | 23 ++ ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 35 ++ ...lcon-windows-recommended-runApplication.md | 45 +++ ...indows-quickStart-instrumentApplication.md | 36 +++ ...stapi-windows-quickStart-runApplication.md | 22 ++ ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 34 ++ ...tapi-windows-recommended-runApplication.md | 45 +++ ...indows-quickStart-instrumentApplication.md | 36 +++ ...flask-windows-quickStart-runApplication.md | 24 ++ ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 36 +++ ...lask-windows-recommended-runApplication.md | 46 +++ ...indows-quickStart-instrumentApplication.md | 36 +++ ...thers-windows-quickStart-runApplication.md | 23 ++ ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 30 ++ ...hers-windows-recommended-runApplication.md | 44 +++ ...indows-quickStart-instrumentApplication.md | 57 ++++ .../ror-windows-quickStart-runApplication.md | 9 + ...indows-recommended-installOtelCollector.md | 3 + ...ndows-recommended-instrumentApplication.md | 60 ++++ .../ror-windows-recommended-runApplication.md | 7 + ...indows-quickStart-instrumentApplication.md | 167 ++++++++++ .../rust-windows-quickStart-runApplication.md | 12 + ...indows-recommended-installOtelCollector.md | 4 + ...ndows-recommended-instrumentApplication.md | 117 +++++++ ...rust-windows-recommended-runApplication.md | 8 + ...indows-quickStart-instrumentApplication.md | 65 ++++ ...swift-windows-quickStart-runApplication.md | 7 + ...indows-recommended-installOtelCollector.md | 96 ++++++ ...ndows-recommended-instrumentApplication.md | 70 ++++ ...wift-windows-recommended-runApplication.md | 32 ++ .../EnvironmentDetails/EnvironmentDetails.tsx | 10 + .../constants/apmDocFilePaths.ts | 306 ++++++++++++++++++ 112 files changed, 4977 insertions(+) create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-runApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-installOtelCollector.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-instrumentApplication.md create mode 100644 frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-runApplication.md diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..e2dc14886f --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-instrumentApplication.md @@ -0,0 +1,67 @@ +**Step 1: Installing the OpenTelemetry dependency packages:** + +```bash +dotnet add package OpenTelemetry +dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol +dotnet add package OpenTelemetry.Extensions.Hosting +dotnet add package OpenTelemetry.Instrumentation.Runtime +dotnet add package OpenTelemetry.Instrumentation.AspNetCore +dotnet add package OpenTelemetry.AutoInstrumentation +``` + +**Step 2: Adding OpenTelemetry as a service and configuring exporter options in `Program.cs`:** + +In your `Program.cs` file, add OpenTelemetry as a service. + +Here’s a sample `Program.cs` file with the configured variables. + +```bash +using System.Diagnostics; +using OpenTelemetry.Exporter; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +var builder = WebApplication.CreateBuilder(args); + +// Configure OpenTelemetry with tracing and auto-start. +builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => + resource.AddService(serviceName: "{{MYAPP}}")) + .WithTracing(tracing => tracing + .AddAspNetCoreInstrumentation() + .AddOtlpExporter(otlpOptions => + { + //SigNoz Cloud Endpoint + otlpOptions.Endpoint = new Uri("https://ingest.{{REGION}}.signoz.cloud:443"); + + otlpOptions.Protocol = OtlpExportProtocol.Grpc; + + //SigNoz Cloud account Ingestion key + string headerKey = "signoz-access-token"; + string headerValue = "{{SIGNOZ_INGESTION_KEY}}"; + + string formattedHeader = $"{headerKey}={headerValue}"; + otlpOptions.Headers = formattedHeader; + })); + +var app = builder.Build(); + +// The index route ("/") is set up to write out the OpenTelemetry trace information on the response: +app.MapGet("/", () => $"Hello World! OpenTelemetry Trace: {Activity.Current?.Id}"); + +app.Run(); +``` + + +**Step 3. Running the .NET application:** + +```bash +dotnet build +dotnet run +``` + +**Step 4: Generating some load data and checking your application in SigNoz UI** + +Once your application is running, generate some traffic by interacting with it. + +In the SigNoz account, open the `Services` tab. Hit the `Refresh` button on the top right corner, and your application should appear in the list of `Applications`. Ensure that you're checking data for the `time range filter` applied in the top right corner. You might have to wait for a few seconds before the data appears on SigNoz UI. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..556561d63c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/QuickStart/dotnet-windows-quickStart-runApplication.md @@ -0,0 +1,10 @@ +  +To run your .NET application, use the below command : + +```bash +dotnet build +dotnet run +``` + +Once you run your .NET application, interact with your application to generate some load and see your application in the SigNoz UI. + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..fbeda9c337 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-installOtelCollector.md @@ -0,0 +1,12 @@ +## Setup OpenTelemetry Binary as an agent + +  + +As a first step, you should install the OTel collector Binary according to the instructions provided on [this link](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/). + +  + +Once you are done setting up the OTel collector binary, you can follow the next steps. + +  + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..d12edb18db --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-instrumentApplication.md @@ -0,0 +1,63 @@ +After setting up the Otel collector agent, follow the steps below to instrument your .NET Application + +  +  + +### Step 1: Install OpenTelemetry Dependencies +Install the following dependencies in your application. + +```bash +dotnet add package OpenTelemetry +dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol +dotnet add package OpenTelemetry.Extensions.Hosting +dotnet add package OpenTelemetry.Instrumentation.Runtime +dotnet add package OpenTelemetry.Instrumentation.AspNetCore +dotnet add package OpenTelemetry.AutoInstrumentation +``` + +  + +### Step 2: Adding OpenTelemetry as a service and configuring exporter options + +In your `Program.cs` file, add OpenTelemetry as a service. Here, we are configuring these variables: + +  + +Here’s a sample `Program.cs` file with the configured variables: + +```bash +using System.Diagnostics; +using OpenTelemetry.Exporter; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +var builder = WebApplication.CreateBuilder(args); + +// Configure OpenTelemetry with tracing and auto-start. +builder.Services.AddOpenTelemetry() + .ConfigureResource(resource => + resource.AddService(serviceName: "{{MYAPP}}")) + .WithTracing(tracing => tracing + .AddAspNetCoreInstrumentation() + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri("http://localhost:4317"); + + otlpOptions.Protocol = OtlpExportProtocol.Grpc; + })); + +var app = builder.Build(); + +//The index route ("/") is set up to write out the OpenTelemetry trace information on the response: +app.MapGet("/", () => $"Hello World! OpenTelemetry Trace: {Activity.Current?.Id}"); + +app.Run(); +``` +  + +The OpenTelemetry.Exporter.Options get or set the target to which the exporter is going to send traces. Here, we’re configuring it to send traces to the OTel Collector agent. The target must be a valid Uri with the scheme (http or https) and host and may contain a port and a path. + +This is done by configuring an OpenTelemetry [TracerProvider](https://github.com/open-telemetry/opentelemetry-dotnet/tree/main/docs/trace/customizing-the-sdk#readme) using extension methods and setting it to auto-start when the host is started. + + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-runApplication.md new file mode 100644 index 0000000000..6a591b202e --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Dotnet/md-docs/Windows/Recommended/dotnet-windows-recommended-runApplication.md @@ -0,0 +1,18 @@ +  +Once you are done intrumenting your .NET application, you can run it using the below commands +  + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml +``` + +  + +### Step 2: Run your .NET application +```bash +dotnet build +dotnet run +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..d8e7f3e14e --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-instrumentApplication.md @@ -0,0 +1,62 @@ +  + +Follow the steps below to instrument your Elixir (Phoenix + Ecto) Application + +### Step 1: Add dependencies +Install dependencies related to OpenTelemetry by adding them to `mix.exs` file + +```bash + {:opentelemetry_exporter, "~> 1.6"}, + {:opentelemetry_api, "~> 1.2"}, + {:opentelemetry, "~> 1.3"}, + {:opentelemetry_semantic_conventions, "~> 0.2"}, + {:opentelemetry_cowboy, "~> 0.2.1"}, + {:opentelemetry_phoenix, "~> 1.1"}, + {:opentelemetry_ecto, "~> 1.1"} +``` +  + +In your application start, usually the `application.ex` file, setup the telemetry handlers + +```bash + :opentelemetry_cowboy.setup() + OpentelemetryPhoenix.setup(adapter: :cowboy2) + OpentelemetryEcto.setup([:{{MYAPP}}, :repo]) +``` +  + +As an example, this is how you can setup the handlers in your application.ex file for an application called demo : + +```bash +# application.ex +@impl true +def start(_type, _args) do + :opentelemetry_cowboy.setup() + OpentelemetryPhoenix.setup(adapter: :cowboy2) + OpentelemetryEcto.setup([:demo, :repo]) + +end +``` + +  + +### Step 2: Configure Application +You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file: + +```bash +config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"} + +config :opentelemetry, :processors, + otel_batch_processor: %{ + exporter: { + :opentelemetry_exporter, + %{ + endpoints: ["https://ingest.{{REGION}}.signoz.cloud:443"], + headers: [ + {"signoz-access-token", {{SIGNOZ_ACCESS_TOKEN}} } + ] + } + } + } +``` + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..0dabd95519 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/QuickStart/elixir-windows-quickStart-runApplication.md @@ -0,0 +1,6 @@ +### Running your Elixir application +Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would. + +  + +To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..ca02eb18fd --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-installOtelCollector.md @@ -0,0 +1,105 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz + +  + +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder + +  + +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it + +  + +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..a4e0f2c5a6 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-instrumentApplication.md @@ -0,0 +1,57 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your Elixir (Phoenix + Ecto) Application + +### Step 1: Add dependencies +Install dependencies related to OpenTelemetry by adding them to `mix.exs` file + +```bash + {:opentelemetry_exporter, "~> 1.6"}, + {:opentelemetry_api, "~> 1.2"}, + {:opentelemetry, "~> 1.3"}, + {:opentelemetry_semantic_conventions, "~> 0.2"}, + {:opentelemetry_cowboy, "~> 0.2.1"}, + {:opentelemetry_phoenix, "~> 1.1"}, + {:opentelemetry_ecto, "~> 1.1"} +``` +  + +In your application start, usually the `application.ex` file, setup the telemetry handlers + +```bash + :opentelemetry_cowboy.setup() + OpentelemetryPhoenix.setup(adapter: :cowboy2) + OpentelemetryEcto.setup([:{{MYAPP}}, :repo]) +``` +  + +As an example, this is how you can setup the handlers in your application.ex file for an application called demo : + +```bash +# application.ex +@impl true +def start(_type, _args) do + :opentelemetry_cowboy.setup() + OpentelemetryPhoenix.setup(adapter: :cowboy2) + OpentelemetryEcto.setup([:demo, :repo]) + +end +``` + +  + +### Step 2: Configure Application +You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file: + +```bash +config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"} + +config :opentelemetry, :processors, + otel_batch_processor: %{ + exporter: + {:opentelemetry_exporter, + %{endpoints: ["http://localhost:4318"]} + } + } +``` + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-runApplication.md new file mode 100644 index 0000000000..23a93dbcf8 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Elixir/md-docs/Windows/Recommended/elixir-windows-recommended-runApplication.md @@ -0,0 +1,55 @@ + +OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way. + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Elixir (Phoenix + Ecto) application. + +**Step 1. Add dependencies** + +Install dependencies related to OpenTelemetry by adding them to `mix.exs` file + +```bash + {:opentelemetry_exporter, "~> 1.6"}, + {:opentelemetry_api, "~> 1.2"}, + {:opentelemetry, "~> 1.3"}, + {:opentelemetry_semantic_conventions, "~> 0.2"}, + {:opentelemetry_cowboy, "~> 0.2.1"}, + {:opentelemetry_phoenix, "~> 1.1"}, + {:opentelemetry_ecto, "~> 1.1"} +``` + +In your application start, usually the `application.ex` file, setup the telemetry handlers + +```elixir + :opentelemetry_cowboy.setup() + OpentelemetryPhoenix.setup(adapter: :cowboy2) + OpentelemetryEcto.setup([:YOUR_APP_NAME, :repo]) +``` + +As an example, this is how you can setup the handlers in your `application.ex` file for an application called `demo` : + +```elixir +# application.ex +@impl true +def start(_type, _args) do + :opentelemetry_cowboy.setup() + OpentelemetryPhoenix.setup(adapter: :cowboy2) + OpentelemetryEcto.setup([:demo, :repo]) + +end +``` + +**Step 2. Configure Application** + +You need to configure your application to send telemtry data by adding the follwing config to your `runtime.exs` file: + +```elixir +config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"} + +config :opentelemetry, :processors, + otel_batch_processor: %{ + exporter: + {:opentelemetry_exporter, + %{endpoints: ["http://localhost:4318"]} + } + } +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..14fd69014f --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-instrumentApplication.md @@ -0,0 +1,123 @@ + +1. **Install Dependencies**

+ Dependencies related to OpenTelemetry exporter and SDK have to be installed first. Note that we are assuming you are using `gin` request router. If you are using other request routers, check out the [corresponding package](#request-routers). + + Run the below commands after navigating to the application source folder: +   + + ```bash + go get go.opentelemetry.io/otel \ + go.opentelemetry.io/otel/trace \ + go.opentelemetry.io/otel/sdk \ + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \ + go.opentelemetry.io/otel/exporters/otlp/otlptrace \ + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc + ``` +   + +2. **Declare environment variables for configuring OpenTelemetry**

+ Declare the following global variables in `main.go` which we will use to configure OpenTelemetry: + + ```bash + var ( + serviceName = "{{MYAPP}}") + collectorURL = "https://ingest.{{REGION}}.signoz.cloud:443" + headers="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" + insecure = os.Getenv("INSECURE_MODE") + ) + ``` +   + +3. **Instrument your Go application with OpenTelemetry**

+ To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file. + + ```bash + + import ( + ..... + + "google.golang.org/grpc/credentials" + "github.com/gin-gonic/gin" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + ) + + func initTracer() func(context.Context) error { + + var secureOption otlptracegrpc.Option + + if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" { + secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, "")) + } else { + secureOption = otlptracegrpc.WithInsecure() + } + + exporter, err := otlptrace.New( + context.Background(), + otlptracegrpc.NewClient( + secureOption, + otlptracegrpc.WithEndpoint(collectorURL), + otlptracegrpc.WithHeaders(headers), + ), + ) + + if err != nil { + log.Fatalf("Failed to create exporter: %v", err) + } + resources, err := resource.New( + context.Background(), + resource.WithAttributes( + attribute.String("{{MYAPP}}", serviceName), + attribute.String("library.language", "go"), + ), + ) + if err != nil { + log.Fatalf("Could not set resources: %v", err) + } + + otel.SetTracerProvider( + sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource(resources), + ), + ) + return exporter.Shutdown + } + ``` +   + +4. **Initialize the tracer in main.go**

+ Modify the main function to initialise the tracer in `main.go`. Initiate the tracer at the very beginning of our main function. + + ```go + func main() { + cleanup := initTracer() + defer cleanup(context.Background()) + + ...... + } + ``` +   + +5. **Add the OpenTelemetry Gin middleware**

+ Configure Gin to use the middleware by adding the following lines in `main.go`. + + ```go + import ( + .... + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" + ) + + func main() { + ...... + r := gin.Default() + r.Use(otelgin.Middleware(serviceName)) + ...... + } + ``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..c30d4930c1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/QuickStart/golang-windows-quickStart-runApplication.md @@ -0,0 +1,13 @@ +**Set environment variables and run your Go Gin application**

+ The run command must have some environment variables to send data to SigNoz cloud. The run commands: + +   + + ```bash + setx INSECURE_MODE=false + ``` +   + + ```bash + go run main.go + ``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..fbeda9c337 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-installOtelCollector.md @@ -0,0 +1,12 @@ +## Setup OpenTelemetry Binary as an agent + +  + +As a first step, you should install the OTel collector Binary according to the instructions provided on [this link](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/). + +  + +Once you are done setting up the OTel collector binary, you can follow the next steps. + +  + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..c896963285 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-instrumentApplication.md @@ -0,0 +1,122 @@ + +1. **Install Dependencies**

+ Dependencies related to OpenTelemetry exporter and SDK have to be installed first. Note that we are assuming you are using `gin` request router. If you are using other request routers, check out the [corresponding package](#request-routers). + + Run the below commands after navigating to the application source folder: + + ```bash + go get go.opentelemetry.io/otel \ + go.opentelemetry.io/otel/trace \ + go.opentelemetry.io/otel/sdk \ + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \ + go.opentelemetry.io/otel/exporters/otlp/otlptrace \ + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc + ``` +   + +2. **Declare environment variables for configuring OpenTelemetry**

+ Declare the following global variables in `main.go` which we will use to configure OpenTelemetry: + + ```go + var ( + serviceName = os.Getenv("SERVICE_NAME") + collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") + insecure = os.Getenv("INSECURE_MODE") + ) + ``` +   + +3. **Instrument your Go application with OpenTelemetry**

+ To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file. + + ```go + + import ( + ..... + + "google.golang.org/grpc/credentials" + "github.com/gin-gonic/gin" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + ) + + func initTracer() func(context.Context) error { + + var secureOption otlptracegrpc.Option + + if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" { + secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, "")) + } else { + secureOption = otlptracegrpc.WithInsecure() + } + + exporter, err := otlptrace.New( + context.Background(), + otlptracegrpc.NewClient( + secureOption, + otlptracegrpc.WithEndpoint(collectorURL), + ), + ) + + if err != nil { + log.Fatalf("Failed to create exporter: %v", err) + } + resources, err := resource.New( + context.Background(), + resource.WithAttributes( + attribute.String("service.name", serviceName), + attribute.String("library.language", "go"), + ), + ) + if err != nil { + log.Fatalf("Could not set resources: %v", err) + } + + otel.SetTracerProvider( + sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource(resources), + ), + ) + return exporter.Shutdown + } + ``` + +   + +4. **Initialize the tracer in main.go**

+ Modify the main function to initialise the tracer in `main.go`. Initiate the tracer at the very beginning of our main function. + + ```go + func main() { + cleanup := initTracer() + defer cleanup(context.Background()) + + ...... + } + ``` +   + +5. **Add the OpenTelemetry Gin middleware**

+ Configure Gin to use the middleware by adding the following lines in `main.go`. + + ```go + import ( + .... + "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" + ) + + func main() { + ...... + r := gin.Default() + r.Use(otelgin.Middleware(serviceName)) + ...... + } + ``` + \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-runApplication.md new file mode 100644 index 0000000000..3de4c14688 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/GoLang/md-docs/Windows/Recommended/golang-windows-recommended-runApplication.md @@ -0,0 +1,16 @@ +**Set environment variables and run your Go Gin application**

+ The run command must have some environment variables to send data to SigNoz. Then run the following commands: +   + + ```bash + setx SERVICE_NAME={{MYAPP}} + setx INSECURE_MODE=true + setx OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 + ``` + +  +  + + ```bash + go run main.go + ``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..9cf751b58f --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-instrumentApplication.md @@ -0,0 +1,27 @@ + +**Step 1.** Download otel java binary agent + +```bash +wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar +``` +  + +**Step 2.** Edit your configuration file,i.e `standalone.conf` for JBoss with nano or notepad. +  +  + +**Step 3.** Update `JAVA_OPTS` environment variable + +Update `JAVA_OPTS` environment variable with configurations required to send data to SigNoz cloud in your configuration file. + +```bash +set JAVA_OPTS=-javaagent:C:\path\to\opentelemetry-javaagent.jar +set JAVA_OPTS=%JAVA_OPTS% -Dotel.exporter.otlp.endpoint=https://ingest.{{REGION}}.signoz.cloud:443 +set JAVA_OPTS=%JAVA_OPTS% -Dotel.exporter.otlp.headers="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +set JAVA_OPTS=%JAVA_OPTS% -Dotel.resource.attributes="service.name={{MYAPP}}" +``` +  +  +You need to replace the following things based on your environment:

+ +- `path` - Update it to the path of your downloaded Java JAR agent instead of `C:\path\to\opentelemetry-javaagent.jar`

diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..280a98256f --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/QuickStart/jboss-windows-quickStart-runApplication.md @@ -0,0 +1,7 @@ + Write the output/logs of standalone.sh script to a file nohup.out as a background thread + +```bash +/opt/jboss-eap-7.1/bin/standalone.sh > /opt/jboss-eap-7.1/bin/nohup.out & +``` +  +In case you encounter an issue where all applications do not get listed in the services section then please refer to the [troubleshooting section](#troubleshooting-your-installation). \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..fbeda9c337 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-installOtelCollector.md @@ -0,0 +1,12 @@ +## Setup OpenTelemetry Binary as an agent + +  + +As a first step, you should install the OTel collector Binary according to the instructions provided on [this link](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/). + +  + +Once you are done setting up the OTel collector binary, you can follow the next steps. + +  + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..a1cc793bc7 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-instrumentApplication.md @@ -0,0 +1,8 @@ +After setting up the Otel collector agent, follow the steps below to instrument your JavaScript Application + +#### Requirements +- Java 8 or higher + +  + +Open the configuration file, generally at `jboss-eap-7.1/bin/standalone.conf` . \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-runApplication.md new file mode 100644 index 0000000000..8f616235b6 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Jboss/Windows/Recommended/jboss-windows-recommended-runApplication.md @@ -0,0 +1,13 @@ +Update `JAVA_OPTS` environment variable + +Update `JAVA_OPTS` environment variable with configurations required to send data to SigNoz cloud in your configuration file. + +```bash +JAVA_OPTS="-javaagent:C:/path/to/opentelemetry-javaagent.jar" +``` +  + +where, +- `path` - Update it to the path of your downloaded Java JAR agent.

+ +In case you encounter an issue where all applications do not get listed in the services section then please refer to the [troubleshooting section](#troubleshooting-your-installation). \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..4fb800616c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md @@ -0,0 +1,11 @@ + +#### Requirements +- Java 8 or higher + +  + +**Step 1.** Download otel java binary agent + +```bash +wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..9a468609ab --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md @@ -0,0 +1,15 @@ +Run your application +  + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{{REGION}}.signoz.cloud:443 +``` + +  +  + +```bash +java -javaagent:$PWD/opentelemetry-javaagent.jar -jar {{MYAPP}}.jar +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..fbeda9c337 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md @@ -0,0 +1,12 @@ +## Setup OpenTelemetry Binary as an agent + +  + +As a first step, you should install the OTel collector Binary according to the instructions provided on [this link](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/). + +  + +Once you are done setting up the OTel collector binary, you can follow the next steps. + +  + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..29695a2154 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md @@ -0,0 +1,15 @@ +After setting up the Otel collector agent, follow the steps below to instrument your Java Application + +#### Requirements +- Java 8 or higher + +  +  + + Download OTel java binary agent + +   +   + +```bash +wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md new file mode 100644 index 0000000000..218cb921a6 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md @@ -0,0 +1,7 @@ +## Run your application + +  + +```bash +java -javaagent:/opentelemetry-javaagent.jar -jar {{MYAPP}}.jar +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..4fb800616c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-instrumentApplication.md @@ -0,0 +1,11 @@ + +#### Requirements +- Java 8 or higher + +  + +**Step 1.** Download otel java binary agent + +```bash +wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..9f7ee529e6 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/QuickStart/springBoot-windows-quickStart-runApplication.md @@ -0,0 +1,21 @@ +Run your application + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{{REGION}}.signoz.cloud:443 +``` + +  +  + +```bash +java -javaagent:/opentelemetry-javaagent.jar -jar +``` + +  +  + +```bash +.jar +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..fbeda9c337 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-installOtelCollector.md @@ -0,0 +1,12 @@ +## Setup OpenTelemetry Binary as an agent + +  + +As a first step, you should install the OTel collector Binary according to the instructions provided on [this link](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/). + +  + +Once you are done setting up the OTel collector binary, you can follow the next steps. + +  + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..524c38a0c5 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-instrumentApplication.md @@ -0,0 +1,11 @@ +After setting up the Otel collector agent, follow the steps below to instrument your Java Application + +#### Requirements +- Java 8 or higher + +  +  + + Download OTel java binary agent

+```bash +wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-runApplication.md new file mode 100644 index 0000000000..0394eac836 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/SpringBoot/Windows/Recommended/springBoot-windows-recommended-runApplication.md @@ -0,0 +1,11 @@ +Run your application

+ +```bash +java -javaagent:/opentelemetry-javaagent.jar -jar .jar +``` + +  + + +- `` is the name of your application jar file +- In case you download `opentelemetry-javaagent.jar` file in different directory than that of the project, replace `$PWD` with the path of the otel jar file. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..0726ce4d1d --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-instrumentApplication.md @@ -0,0 +1,11 @@ +#### Requirements +- Java 8 or higher +  + +**Step 1.** Download otel java binary agent + +  + +```bash +wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..486ab114f7 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/QuickStart/tomcat-windows-quickStart-runApplication.md @@ -0,0 +1,14 @@ +## Enable the instrumentation agent and run your application + +If you run your `.war` package by putting in `webapps` folder, just add `setenv.bat` in your Tomcat `bin` folder. + +This should set these environment variables and start sending telemetry data to SigNoz Cloud. + +  + +```bash +set CATALINA_OPTS=%CATALINA_OPTS% -javaagent:C:\path\to\opentelemetry-javaagent.jar +set OTEL_EXPORTER_OTLP_HEADERS=signoz-access-token={{SIGNOZ_INGESTION_KEY}} +set OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{{REGION}}.signoz.cloud:443 +set OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..a659f36474 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..ad62843f90 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-instrumentApplication.md @@ -0,0 +1,14 @@ +After setting up the Otel collector agent, follow the steps below to instrument your Java Application + +#### Requirements +- Java 8 or higher +  + +Download OTel java binary agent + +  +  + +```bash +wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-runApplication.md new file mode 100644 index 0000000000..60fa1be66a --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Java/md-docs/Tomcat/Windows/Recommended/tomcat-windows-recommended-runApplication.md @@ -0,0 +1,15 @@ +Enable the instrumentation agent and run your application

+ +If you run your `.war` package by putting in `webapps` folder, just add `setenv.isBreakStatement` in your Tomcat `bin` folder. + +This should set these environment variables and start sending telemetry data to SigNoz Cloud. + +  + +```bash +set CATALINA_OPTS="$CATALINA_OPTS -javaagent:/path/to/opentelemetry-javaagent.jar" +``` + +  + +- path/to - Update it to the path of your downloaded Java JAR agent. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..f42f5ebc1b --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-instrumentApplication.md @@ -0,0 +1,86 @@ +  + +**Step 1.** Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/sdk-trace-web@^1.21.0 +npm install --save @opentelemetry/instrumentation@^0.48.0 +npm install --save @opentelemetry/auto-instrumentations-web@^0.36.0 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.48.0 +npm install --save @opentelemetry/resources@^1.21.0 +npm install --save @opentelemetry/propagator-b3@^1.21.0 +npm install --save @opentelemetry/semantic-conventions@^1.21.0 +``` +  + +**Step 2.** Create `instrument.ts` file

+You need to configure the endpoint for SigNoz cloud in this file. + +```bash +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { + WebTracerProvider, + ConsoleSpanExporter, + SimpleSpanProcessor, + BatchSpanProcessor, +} from '@opentelemetry/sdk-trace-web'; +import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import { B3Propagator } from '@opentelemetry/propagator-b3'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +const resource = Resource.default().merge( + new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + [SemanticResourceAttributes.SERVICE_VERSION]: '0.1.0', + }) +); + +const provider = new WebTracerProvider({ resource }); + +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + +provider.addSpanProcessor( + new BatchSpanProcessor( + new OTLPTraceExporter({ + url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces', + headers: { + 'signoz-access-token': '{{SIGNOZ_INGESTION_KEY}}', + }, + }) + ) +); + +provider.register({ + propagator: new B3Propagator(), +}); + +registerInstrumentations({ + instrumentations: [ + getWebAutoInstrumentations({ + '@opentelemetry/instrumentation-document-load': {}, + '@opentelemetry/instrumentation-user-interaction': {}, + '@opentelemetry/instrumentation-fetch': { + propagateTraceHeaderCorsUrls: /.+/, + }, + '@opentelemetry/instrumentation-xml-http-request': { + propagateTraceHeaderCorsUrls: /.+/, + }, + }), + ], +}); + +``` + +  +  + +OpenTelemetry Node SDK currently does not detect the headers from `.env` files as of today. That’s why we need to include the variables in the `instrument.ts` file itself. + + +**Step 3.** Add the below import to your `main.ts` file. + +```bash +import './app/instrument'; +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..ad99353b32 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/QuickStart/angular-windows-quickStart-runApplication.md @@ -0,0 +1,9 @@ +  + +Once you are done instrumenting your Angular application, you can run it using the below command + +```bash +ng serve +``` + +  \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..3d8eb84070 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-installOtelCollector.md @@ -0,0 +1,21 @@ +**Step 1.** Install OpenTelemetry Collector binary + +OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way. + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. + +While creating the `config.yaml` during the installation fo the OTel Collector Binary, you need to enable CORS under the receivers section of the config file. This is needed so that you don't get +CORS error which can hinder sending your Traces to SigNoz Cloud. See the code snippet below to understand how you can enable CORS in your config file: + +```bash + http: ++ cors: ++ allowed_origins: ++ - # URL of your Frontend application. Example -> http://localhost:4200, https://netflix.com etc. +``` + +  + +`` - URL where your frontend application is running. For Example, http://localhost:4200 or https://netflix.com etc. + +**NOTE:** Make sure to restart your collector after making the config changes \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..e478cb73bf --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-instrumentApplication.md @@ -0,0 +1,82 @@ +After setting up the Otel collector agent, follow the steps below to instrument your Angular Application. + +  + +**Step 1.** Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/sdk-trace-web@^1.21.0 +npm install --save @opentelemetry/instrumentation@^0.48.0 +npm install --save @opentelemetry/auto-instrumentations-web@^0.36.0 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.48.0 +npm install --save @opentelemetry/resources@^1.21.0 +npm install --save @opentelemetry/propagator-b3@^1.21.0 +npm install --save @opentelemetry/semantic-conventions@^1.21.0 +``` +  + +**Step 2.** Create `instrument.ts` file

+ +```bash +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { + WebTracerProvider, + ConsoleSpanExporter, + SimpleSpanProcessor, + BatchSpanProcessor, +} from '@opentelemetry/sdk-trace-web'; +import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import { B3Propagator } from '@opentelemetry/propagator-b3'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +const resource = Resource.default().merge( + new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '', + [SemanticResourceAttributes.SERVICE_VERSION]: '0.1.0', + }) +); + +const provider = new WebTracerProvider({ resource }); + +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + +provider.addSpanProcessor( + new BatchSpanProcessor( + new OTLPTraceExporter({ + url: 'http://localhost:4318/v1/traces', + }) + ) +); + +provider.register({ + propagator: new B3Propagator(), +}); + +registerInstrumentations({ + instrumentations: [ + getWebAutoInstrumentations({ + '@opentelemetry/instrumentation-document-load': {}, + '@opentelemetry/instrumentation-user-interaction': {}, + '@opentelemetry/instrumentation-fetch': { + propagateTraceHeaderCorsUrls: /.+/, + }, + '@opentelemetry/instrumentation-xml-http-request': { + propagateTraceHeaderCorsUrls: /.+/, + }, + }), + ], +}); +``` + +  +  + +- `` : Name of your service. + +**Step 3.** Add the below import to your `main.ts` file. + +```bash +import './app/instrument'; +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-runApplication.md new file mode 100644 index 0000000000..af927a73d9 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Angular/Windows/Recommended/angular-windows-recommended-runApplication.md @@ -0,0 +1,22 @@ +  + +Once you are done instrumenting your Angular application, you can run it using the below commands +  + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +  + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +### Step 2: Run your application + +```bash +ng serve +``` + +  diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..e60880889c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-instrumentApplication.md @@ -0,0 +1,55 @@ + +#### Requirements +- Supported Versions ^4.0.0 +  + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 +``` +  + +### Step 2: Create tracing.js file + +```bash +// tracing.js +'use strict' +const process = require('process'); +const opentelemetry = require('@opentelemetry/sdk-node'); +const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); +const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); +const { Resource } = require('@opentelemetry/resources'); +const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); + +// do not set headers in exporterOptions, the OTel spec recommends setting headers through ENV variables +// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables + +const exporterOptions = { + url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces' +} + +const traceExporter = new OTLPTraceExporter(exporterOptions); +const sdk = new opentelemetry.NodeSDK({ + traceExporter, + instrumentations: [getNodeAutoInstrumentations()], + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}' + }) +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start() + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk.shutdown() + .then(() => console.log('Tracing terminated')) + .catch((error) => console.log('Error terminating tracing', error)) + .finally(() => process.exit(0)); +}); +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..a8a8277af3 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/QuickStart/express-windows-quickStart-runApplication.md @@ -0,0 +1,13 @@ +Set `OTEL_EXPORTER_OTLP_ENDPOINT` as env variable using powershell: + +  + +```bash +$env:OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443/v1/traces" +``` + +**Run the application**

+ +```bash +node -r ./tracing.js app.js +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..56d1d2935f --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-installOtelCollector.md @@ -0,0 +1,20 @@ +**Step 1.** Install OpenTelemetry Collector binary + +OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way. + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. + +While creating the `config.yaml` during the installation fo the OTel Collector Binary, you need to enable CORS under the receivers section of the config file. This is needed so that you don't get +CORS error which can hinder sending your Traces to SigNoz Cloud. See the code snippet below to understand how you can enable CORS in your config file: + +  + +```yml + http: ++ cors: ++ allowed_origins: ++ - # URL of your Frontend application. Example -> http://localhost:4200, https://netflix.com etc. +``` +`` - URL where your frontend application is running. For Example, http://localhost:4200 or https://netflix.com etc. + +**NOTE:** Make sure to restart your collector after making the config changes \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..aca162869a --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-instrumentApplication.md @@ -0,0 +1,53 @@ +After setting up the Otel collector agent, follow the steps below to instrument your JavaScript Application + +#### Requirements +- Supported Versions ^4.0.0 +  + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 +``` +  + +### Step 2: Create tracing.js file + +```bash +// tracing.js +'use strict' +const process = require('process'); +const opentelemetry = require('@opentelemetry/sdk-node'); +const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); +const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); +const { Resource } = require('@opentelemetry/resources'); +const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); + +const exporterOptions = { + url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces', +} + +const traceExporter = new OTLPTraceExporter(exporterOptions); +const sdk = new opentelemetry.NodeSDK({ + traceExporter, + instrumentations: [getNodeAutoInstrumentations()], + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}' + }) +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start() + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk.shutdown() + .then(() => console.log('Tracing terminated')) + .catch((error) => console.log('Error terminating tracing', error)) + .finally(() => process.exit(0)); +}); +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-runApplication.md new file mode 100644 index 0000000000..6666e2ebf5 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Express/Windows/Recommended/express-windows-recommended-runApplication.md @@ -0,0 +1,5 @@ +**Run the application**

+ +```bash +node -r ./tracing.js app.js +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..705195cf3e --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-instrumentApplication.md @@ -0,0 +1,85 @@ +**Step 1.** Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 +``` + +**Step 2.** Create `tracer.ts` file

+You need to configure the endpoint for SigNoz cloud in this file. + +```bash +'use strict'; + +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import * as opentelemetry from '@opentelemetry/sdk-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +// Configure the SDK to export telemetry data to the console +// Enable all auto-instrumentations from the meta package +const exporterOptions = { + //highlight-start + url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces', + headers: { 'signoz-access-token': '{{SIGNOZ_INGESTION_KEY}}' }, + //highlight-end +}; + +const traceExporter = new OTLPTraceExporter(exporterOptions); +const sdk = new opentelemetry.NodeSDK({ + traceExporter, + instrumentations: [getNodeAutoInstrumentations()], + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start(); + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk + .shutdown() + .then(() => console.log('Tracing terminated')) + .catch((error) => console.log('Error terminating tracing', error)) + .finally(() => process.exit(0)); +}); + +export default sdk; +``` + +  + +**Step 3.** On `main.ts` file or file where your app starts import tracer using below command. + +```bash +const tracer = require('./tracer') +``` + +  + +**Step 4.** Start the tracer

+In the `async function boostrap` section of the application code, initialize the tracer as follows: + +```bash +const tracer = require('./tracer') + +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + // All of your application code and any imports that should leverage + // OpenTelemetry automatic instrumentation must go here. + +async function bootstrap() { + // highlight-start + await tracer.start(); + //highlight-end + const app = await NestFactory.create(AppModule); + await app.listen(3001); + } + bootstrap(); +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..d8ce17ed7f --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/QuickStart/nestjs-windows-quickStart-runApplication.md @@ -0,0 +1,9 @@ +After your instrumentation is done, start your application + +```bash +nest start +``` + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/nestjs/#troubleshooting-your-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..02f1e84951 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-installOtelCollector.md @@ -0,0 +1,3 @@ +OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way. + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Javascript application. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..27b8e88b87 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-instrumentApplication.md @@ -0,0 +1,98 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your JavaScript Application + +#### Requirements +- Supported Versions >= `4.0.0` +  + +**Step 1.** Install OpenTelemetry packages + +```js +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 +``` + +  + +**Step 2.** Create `tracer.ts` file

+ +```ts +'use strict'; + +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { Resource } from '@opentelemetry/resources'; +import * as opentelemetry from '@opentelemetry/sdk-node'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; + +// Configure the SDK to export telemetry data to the console +// Enable all auto-instrumentations from the meta package +const exporterOptions = { + //highlight-start + url: 'http://localhost:4318/v1/traces', + //highlight-end +}; + +const traceExporter = new OTLPTraceExporter(exporterOptions); +const sdk = new opentelemetry.NodeSDK({ + traceExporter, + instrumentations: [getNodeAutoInstrumentations()], + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '', + }), +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start(); + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk + .shutdown() + .then(() => console.log('Tracing terminated')) + .catch((error) => console.log('Error terminating tracing', error)) + .finally(() => process.exit(0)); +}); + +export default sdk; +``` + +  +  + +- `` : Name of your service. + +**Step 3.** On `main.ts` file or file where your app starts import tracer using below command. + +The below import should be the first line in the main file of your application (Ex -> `main.ts`) + + +```jsx +const tracer = require('./tracer') +``` + + +**Step 4.** Start the tracer

+In the `async function boostrap` section of the application code, initialize the tracer as follows: + +```jsx +const tracer = require('./tracer') + +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + // All of your application code and any imports that should leverage + // OpenTelemetry automatic instrumentation must go here. + +async function bootstrap() { + // highlight-start + await tracer.start(); + //highlight-end + const app = await NestFactory.create(AppModule); + await app.listen(3001); + } + bootstrap(); +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-runApplication.md new file mode 100644 index 0000000000..edebd164fb --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NestJS/Windows/Recommended/nestjs-windows-recommended-runApplication.md @@ -0,0 +1,16 @@ +  + +Once you are done intrumenting your JavaScript application, you can run it using the below commands + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +### Step 2: Run the application +```bash + nest start +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..ed6f8a0180 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-instrumentApplication.md @@ -0,0 +1,57 @@ + +#### Requirements +- NodeJS Version 14 or newer + +  + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 +``` +  + +### Step 2: Create tracing.js file + +```bash +// tracing.js +'use strict' +const process = require('process'); +const opentelemetry = require('@opentelemetry/sdk-node'); +const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); +const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); +const { Resource } = require('@opentelemetry/resources'); +const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); + +// do not set headers in exporterOptions, the OTel spec recommends setting headers through ENV variables +// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#specifying-headers-via-environment-variables + +const exporterOptions = { + url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces' +} + +const traceExporter = new OTLPTraceExporter(exporterOptions); +const sdk = new opentelemetry.NodeSDK({ + traceExporter, + instrumentations: [getNodeAutoInstrumentations()], + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}' + }) +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start() + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk.shutdown() + .then(() => console.log('Tracing terminated')) + .catch((error) => console.log('Error terminating tracing', error)) + .finally(() => process.exit(0)); +}); +``` + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..3f799989f9 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/QuickStart/nodejs-windows-quickStart-runApplication.md @@ -0,0 +1,9 @@ +Once you are done intrumenting your JavaScript application, you can run it using the below command + +```bash +node -r ./tracing.js app.js +``` + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/javascript/#troubleshooting-your-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..6998ebf60c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_arm64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_arm64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..8fa26346f0 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-instrumentApplication.md @@ -0,0 +1,57 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your JavaScript Application + +#### Requirements +- NodeJS Version 14 or newer + +  + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/api@^1.6.0 +npm install --save @opentelemetry/sdk-node@^0.45.0 +npm install --save @opentelemetry/auto-instrumentations-node@^0.39.4 +npm install --save @opentelemetry/exporter-trace-otlp-http@^0.45.0 +``` +  + +### Step 2: Create tracing.js file + +```bash +// tracing.js +'use strict' +const process = require('process'); +const opentelemetry = require('@opentelemetry/sdk-node'); +const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); +const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http'); +const { Resource } = require('@opentelemetry/resources'); +const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions'); + +const exporterOptions = { + url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces', +} + +const traceExporter = new OTLPTraceExporter(exporterOptions); +const sdk = new opentelemetry.NodeSDK({ + traceExporter, + instrumentations: [getNodeAutoInstrumentations()], + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}' + }) +}); + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk.start() + +// gracefully shut down the SDK on process exit +process.on('SIGTERM', () => { + sdk.shutdown() + .then(() => console.log('Tracing terminated')) + .catch((error) => console.log('Error terminating tracing', error)) + .finally(() => process.exit(0)); +}); +``` + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-runApplication.md new file mode 100644 index 0000000000..278e43f58c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/NodeJS/Windows/Recommended/nodejs-windows-recommended-runApplication.md @@ -0,0 +1,32 @@ +  + +Once you are done intrumenting your JavaScript application, you can run it using the below commands +  + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Run your application +```bash +node -r ./tracing.js app.js +``` + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/javascript/#troubleshooting-your-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..8695cc740d --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md @@ -0,0 +1,71 @@ + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/context-zone +npm install --save @opentelemetry/instrumentation +npm install --save @opentelemetry/auto-instrumentations-web +npm install --save @opentelemetry/sdk-trace-base +npm install --save @opentelemetry/sdk-trace-web +npm install --save @opentelemetry/resources +npm install --save @opentelemetry/semantic-conventions +npm install --save @opentelemetry/exporter-trace-otlp-http +``` +  + +### Step 2: Create tracing.js file + +```javascript +// tracing.js +import { ZoneContextManager } from '@opentelemetry/context-zone'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { Resource } from '@opentelemetry/resources'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; + +const provider = new WebTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); +const exporter = new OTLPTraceExporter({ + url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces', + headers: { + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}", + }, +}); +provider.addSpanProcessor(new BatchSpanProcessor(exporter)); + +provider.register({ + // Changing default contextManager to use ZoneContextManager - supports asynchronous operations so that traces are not broken + contextManager: new ZoneContextManager(), +}); + +// Registering instrumentations +registerInstrumentations({ + instrumentations: [ + getWebAutoInstrumentations({ + + '@opentelemetry/instrumentation-xml-http-request': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + '@opentelemetry/instrumentation-fetch': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + }), + ], +}); +``` +### Step 3: Import tracer in main file + +**Important Note**: The below import should be the first line in the main file of your application (Ex -> `index.js`) +```bash +import './tracing.js' +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..49ee3d7601 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md @@ -0,0 +1,18 @@ +Once you are done intrumenting your JavaScript application, you can run it as you normally would. + +For example: + +If you're using `npm` +```bash +npm start +``` +  + +If you're using `yarn` +```bash +yarn start +``` + +  + +To view more detailed documentation, checkout this [link](https://signoz.io/docs/instrumentation/javascript/) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..b9c2536b14 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md @@ -0,0 +1,98 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_arm64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder + +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_arm64.tar.gz -C otelcol-contrib +``` + +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..cc644c7864 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md @@ -0,0 +1,74 @@ + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/context-zone +npm install --save @opentelemetry/instrumentation +npm install --save @opentelemetry/auto-instrumentations-web +npm install --save @opentelemetry/sdk-trace-base +npm install --save @opentelemetry/sdk-trace-web +npm install --save @opentelemetry/resources +npm install --save @opentelemetry/semantic-conventions +npm install --save @opentelemetry/exporter-trace-otlp-http +``` +  + +### Step 2: Create tracing.js file + +```javascript +// tracing.js +import { ZoneContextManager } from '@opentelemetry/context-zone'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { Resource } from '@opentelemetry/resources'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; + +const provider = new WebTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); +const exporter = new OTLPTraceExporter({ + url: 'http://localhost:4318/v1/traces', +}); +provider.addSpanProcessor(new BatchSpanProcessor(exporter)); + +provider.register({ + // Changing default contextManager to use ZoneContextManager - supports asynchronous operations so that traces are not broken + contextManager: new ZoneContextManager(), +}); + +// Registering instrumentations +registerInstrumentations({ + instrumentations: [ + getWebAutoInstrumentations({ + + '@opentelemetry/instrumentation-xml-http-request': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + '@opentelemetry/instrumentation-fetch': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + }), + ], +}); +``` + +  + +### Step 3: Import tracer in main file + +**Important Note**: The below import should be the first line in the main file of your application (Ex -> `index.js`) + +  + +```bash +import './tracing.js' +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md new file mode 100644 index 0000000000..a67e805786 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md @@ -0,0 +1,44 @@ +  + +Once you are done intrumenting your JavaScript application, you can run it using the below commands +  + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` +  + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Run your application +Run your JavaScript application as you normally would. + +For example: + +If you're using `npm` +```bash +npm start +``` +  + +If you're using `yarn` +```bash +yarn start +``` + +  + +To view more detailed documentation, checkout this [link](https://signoz.io/docs/instrumentation/javascript/) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..5dd9cad42c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-instrumentApplication.md @@ -0,0 +1,77 @@ + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/context-zone +npm install --save @opentelemetry/instrumentation +npm install --save @opentelemetry/auto-instrumentations-web +npm install --save @opentelemetry/sdk-trace-base +npm install --save @opentelemetry/sdk-trace-web +npm install --save @opentelemetry/resources +npm install --save @opentelemetry/semantic-conventions +npm install --save @opentelemetry/exporter-trace-otlp-http +``` +  + +### Step 2: Create tracing.js file + +```javascript +// tracing.js +import { ZoneContextManager } from '@opentelemetry/context-zone'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { Resource } from '@opentelemetry/resources'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; + +const provider = new WebTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); +const exporter = new OTLPTraceExporter({ + url: 'https://ingest.{{REGION}}.signoz.cloud:443/v1/traces', + headers: { + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}", + }, +}); +provider.addSpanProcessor(new BatchSpanProcessor(exporter)); + +provider.register({ + // Changing default contextManager to use ZoneContextManager - supports asynchronous operations so that traces are not broken + contextManager: new ZoneContextManager(), +}); + +// Registering instrumentations +registerInstrumentations({ + instrumentations: [ + getWebAutoInstrumentations({ + + '@opentelemetry/instrumentation-xml-http-request': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + '@opentelemetry/instrumentation-fetch': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + }), + ], +}); +``` + +  + +### Step 3: Import tracer in main file + +**Important Note**: The below import should be the first line in the main file of your application (Ex -> `index.js`) + +  + +```bash +import './tracing.js' +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..49ee3d7601 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/QuickStart/reactjs-windows-quickStart-runApplication.md @@ -0,0 +1,18 @@ +Once you are done intrumenting your JavaScript application, you can run it as you normally would. + +For example: + +If you're using `npm` +```bash +npm start +``` +  + +If you're using `yarn` +```bash +yarn start +``` + +  + +To view more detailed documentation, checkout this [link](https://signoz.io/docs/instrumentation/javascript/) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..6998ebf60c --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_arm64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_arm64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..63e0e82684 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-instrumentApplication.md @@ -0,0 +1,71 @@ + +### Step 1: Install OpenTelemetry packages + +```bash +npm install --save @opentelemetry/context-zone +npm install --save @opentelemetry/instrumentation +npm install --save @opentelemetry/auto-instrumentations-web +npm install --save @opentelemetry/sdk-trace-base +npm install --save @opentelemetry/sdk-trace-web +npm install --save @opentelemetry/resources +npm install --save @opentelemetry/semantic-conventions +npm install --save @opentelemetry/exporter-trace-otlp-http +``` +  + +### Step 2: Create tracing.js file + +```javascript +// tracing.js +import { ZoneContextManager } from '@opentelemetry/context-zone'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { Resource } from '@opentelemetry/resources'; +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; + +const provider = new WebTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAME]: '{{MYAPP}}', + }), +}); +const exporter = new OTLPTraceExporter({ + url: 'http://localhost:4318/v1/traces', +}); +provider.addSpanProcessor(new BatchSpanProcessor(exporter)); + +provider.register({ + // Changing default contextManager to use ZoneContextManager - supports asynchronous operations so that traces are not broken + contextManager: new ZoneContextManager(), +}); + +// Registering instrumentations +registerInstrumentations({ + instrumentations: [ + getWebAutoInstrumentations({ + + '@opentelemetry/instrumentation-xml-http-request': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + '@opentelemetry/instrumentation-fetch': { + propagateTraceHeaderCorsUrls: [ + /.+/g, //Regex to match your backend urls. + ], + }, + }), + ], +}); +``` + +  + +### Step 3: Import tracer in main file + +**Important Note**: The below import should be the first line in the main file of your application (Ex -> `index.js`) +```bash +import './tracing.js' +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-runApplication.md new file mode 100644 index 0000000000..b02f98f840 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Javascript/md-docs/ReactJS/Windows/Recommended/reactjs-windows-recommended-runApplication.md @@ -0,0 +1,43 @@ +  + +Once you are done intrumenting your JavaScript application, you can run it using the below commands +  + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Run your application +Run your JavaScript application as you normally would. + +For example: + +If you're using `npm` +```bash +npm start +``` +  + +If you're using `yarn` +```bash +yarn start +``` + +  + +To view more detailed documentation, checkout this [link](https://signoz.io/docs/instrumentation/javascript/) \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..126c066a3e --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-instrumentApplication.md @@ -0,0 +1,127 @@ +  + +**Step 1: Setup Development Environment** + +Initialize the application using + +```bash +composer init --no-interaction --stability beta --require slim/slim:"^4" --require slim/psr7:"^1" +``` + +After successful run of prior command a file named `composer.json` will be generated if it does not exist. Next run the following command, make sure the version of `php` you are using is `7.4` or else you might encounter some issues running the following command. + +```bash +composer update +``` + +  + +**Step 2: Build the extension** + +With our environment set up we can install the extension using [PECL](https://pecl.php.net/): + +```bash +pecl install opentelemetry +``` +  + +If gRPC is already downloaded and installed, skip the following step. + +```bash +pecl install grpc +``` +  + +After successfully installing the OpenTelemetry extension, add the extension to `php.ini` file of your project: + +```bash +[opentelemetry] +extension=opentelemetry.so +``` +  + +Verify that the extension is enabled by running: + +```bash +php -m | grep opentelemetry +``` +  + +This should output: + +```bash +opentelemetry +``` +  + +**Step 3: Add the dependencies** + +Add dependencies required for OpenTelemetry SDK for PHP to perform automatic instrumentation using this command : + +```bash +composer config allow-plugins.php-http/discovery false +``` +  + +``` +composer require open-telemetry/sdk open-telemetry/exporter-otlp php-http/guzzle7-adapter open-telemetry/transport-grpc guzzlehttp/guzzle +``` +  + +You can install the additional dependencies provided by OpenTelemetry for different PHP frameworks from [here](https://packagist.org/explore/?query=open-telemetry). +  + +**Step 4: Modify php code** + +```bash +merge(ResourceInfo::create(Attributes::create([ + ResourceAttributes::SERVICE_NAME => '{{MYAPP}}' + ]))); + + + $headers = [ + 'signoz-access-token' => "{{SIGNOZ_INGESTION_KEY}}", + ]; + $transport = (new GrpcTransportFactory())->create('https://ingest.{{REGION}}.signoz.cloud:443/v1/traces"' . OtlpUtil::method(Signals::TRACE), 'application/x-protobuf', $headers); + $spanExporter = new SpanExporter($transport); + + + $tracerProvider = TracerProvider::builder() + ->addSpanProcessor( + (new BatchSpanProcessorBuilder($spanExporter))->build() + ) + ->setResource($resource) + ->setSampler(new ParentBased(new AlwaysOnSampler())) + ->build(); + + Sdk::builder() + ->setTracerProvider($tracerProvider) + ->setPropagator(TraceContextPropagator::getInstance()) + ->setAutoShutdown(true) + ->buildAndRegisterGlobal(); + +} +?> +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..b970b50a09 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/QuickStart/php-windows-quickStart-runApplication.md @@ -0,0 +1,7 @@ +**Set environment variables and run app** + +Run application using your run command or the following generalized command (depending on framework you are using): + +```bash + php -S localhost:8080 app.php +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..133306c107 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-installOtelCollector.md @@ -0,0 +1,3 @@ +OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way. + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your PHP application. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..b48c2ec486 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-instrumentApplication.md @@ -0,0 +1,108 @@ +**Step 2: Setup Development Environment** + +To configure our PHP application to send data, you need to use OpenTelemetry PHP extension. Since the extension is built from the source, you need to have the build tools, which can be installed using the following command: + +**Step 3: Build the extension** + +With our environment set up we can install the extension using [PECL](https://pecl.php.net/): + +```bash +pecl install opentelemetry +``` + +If gRPC is already downloaded and installed, skip the following step. + +```bash +pecl install grpc +``` + +After successfully installing the OpenTelemetry extension, add the extension to `php.ini` file of your project: + +```bash +[opentelemetry] +extension=opentelemetry.so +``` + +Verify that the extension is enabled by running: + +```bash +php -m | grep opentelemetry +``` + +This should output: + +```bash +opentelemetry +``` + +**Step 4: Add the dependencies** + +Add dependencies required for OpenTelemetry SDK for PHP to perform automatic instrumentation using this command : + +```bash +composer config allow-plugins.php-http/discovery false +``` + +  + +```bash +composer require open-telemetry/sdk open-telemetry/exporter-otlp php-http/guzzle7-adapter open-telemetry/transport-grpc guzzlehttp/guzzle +``` + +  + +You can install the additional dependencies provided by OpenTelemetry for different PHP frameworks from [here](https://packagist.org/explore/?query=open-telemetry). + + +**Step 5: Modify php code** + +```bash +merge(ResourceInfo::create(Attributes::create([ + ResourceAttributes::SERVICE_NAME => '' + ]))); + + + $headers = []; + $transport = (new GrpcTransportFactory())->create('' . OtlpUtil::method(Signals::TRACE), 'application/x-protobuf', $headers); + $spanExporter = new SpanExporter($transport); + + + $tracerProvider = TracerProvider::builder() + ->addSpanProcessor( + (new BatchSpanProcessorBuilder($spanExporter))->build() + ) + ->setResource($resource) + ->setSampler(new ParentBased(new AlwaysOnSampler())) + ->build(); + + Sdk::builder() + ->setTracerProvider($tracerProvider) + ->setPropagator(TraceContextPropagator::getInstance()) + ->setAutoShutdown(true) + ->buildAndRegisterGlobal(); + +} +?> +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-runApplication.md new file mode 100644 index 0000000000..fd32c1ad3d --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Php/md-docs/Windows/Recommended/php-windows-recommended-runApplication.md @@ -0,0 +1,8 @@ + +**Step 6: Set environment variables and run app** + +Run application using your run command or the following generalized command (depending on framework you are using): + +```bash + php -S localhost:8080 app.php +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..3def6c015b --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-instrumentApplication.md @@ -0,0 +1,37 @@ +#### Requirements +- Python 3.8 or newer +- for Django, you must define `DJANGO_SETTINGS_MODULE` correctly. If your project is called `mysite`, something like following should work: +```bash +export DJANGO_SETTINGS_MODULE=mysite.settings +``` +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +  + +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` +  + +**Note:** +- Please make sure that you have installed all the dependencies of your application before running the command in **Step 3**. The command will not install instrumentation for the dependencies which are not installed. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..364cf08da5 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/QuickStart/django-windows-quickStart-runApplication.md @@ -0,0 +1,22 @@ +Once you are done intrumenting your python application, you can run it using this command + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +  + +`` can be something like `python3 app.py` or `python manage.py runserver --noreload` + +  + +**Note** +- Don’t run app in reloader/hot-reload mode as it breaks instrumentation. For example, you can disable the auto reload with --noreload. + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/django/#troubleshooting-your-installation) for assistance. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..cec9e47446 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-installOtelCollector.md @@ -0,0 +1,98 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder + +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it + +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..3dd7818ff9 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-instrumentApplication.md @@ -0,0 +1,37 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your Python Application + +#### Requirements +- Python 3.8 or newer +- for Django, you must define `DJANGO_SETTINGS_MODULE` correctly. If your project is called `mysite`, something like following should work: +```bash +export DJANGO_SETTINGS_MODULE=mysite.settings +``` +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-runApplication.md new file mode 100644 index 0000000000..aae7835995 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Django/Windows/Recommended/django-windows-recommended-runApplication.md @@ -0,0 +1,47 @@ +  + +Once you are done intrumenting your python application, you can run it using the below steps + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Run your application + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` + +`` can be something like `python3 app.py` or `python manage.py runserver --noreload` + +  + +**Note** +- Use port **`4317`** for gRPC exporter and port **`4318`** for HTTP exporter +- If your OpenTelemetry Collector agent is in different VM, replace `http://localhost:4317` in above run command with `:4317` + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/django/#troubleshooting-your-installation) for assistance. + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..103474a27e --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-instrumentApplication.md @@ -0,0 +1,32 @@ +#### Requirements +- Python 3.8 or newer + +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` +  + +**Note:** +- Please make sure that you have installed all the dependencies of your application before running the command in **Step 3**. The command will not install instrumentation for the dependencies which are not installed. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..44e07e9a66 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/QuickStart/falcon-windows-quickStart-runApplication.md @@ -0,0 +1,23 @@ +Once you are done intrumenting your python application, you can run it using this command + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +  + +`` can be `python3 app.py` or `gunicorn src.app -b 0.0.0.0:8001` + +  + +**Note** +- Don’t run app in reloader/hot-reload mode as it breaks instrumentation. For example, you can disable the auto reload with --noreload. + + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/falcon/#troubleshooting-your-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..3d4f2c1377 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..bf78da14dd --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-instrumentApplication.md @@ -0,0 +1,35 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your Python Application + +#### Requirements +- Python 3.8 or newer +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` +  + +**Note:** +- Please make sure that you have installed all the dependencies of your application before running the command in **Step 3**. The command will not install instrumentation for the dependencies which are not installed. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-runApplication.md new file mode 100644 index 0000000000..548406b953 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Falcon/Windows/Recommended/falcon-windows-recommended-runApplication.md @@ -0,0 +1,45 @@ +  + +Once you are done intrumenting your python application, you can run it using these commands + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Run your application +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +  + +`` can be something like `python3 app.py` or `flask run` +  + +**Note** +- Use port **`4317`** for gRPC exporter and port **`4318`** for HTTP exporter +- If your OpenTelemetry Collector agent is in different VM, replace `http://localhost:4317` in above run command with `:4317` +- In case you're dockerising your application, make sure to dockerise it along with OpenTelemetry instrumentation done in previous step. + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/falcon/#troubleshooting-your-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..f65ffb1bb1 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-instrumentApplication.md @@ -0,0 +1,36 @@ +#### Requirements +- Python 3.8 or newer + +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +  + +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.43b0 +pip install opentelemetry-exporter-otlp==1.22.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` + +  + +**Note:** +- Please make sure that you have installed all the dependencies of your application before running the command in **Step 3**. The command will not install instrumentation for the dependencies which are not installed. + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..6e77ddfbbe --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/QuickStart/fastapi-windows-quickStart-runApplication.md @@ -0,0 +1,22 @@ +Once you are done intrumenting your python application, you can run it using this command + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +  + +`` can be something like `python3 app.py` or `uvicorn main:app --host localhost --port 5002` + +  + +**Note** +- Don’t run app in reloader/hot-reload mode as it breaks instrumentation. For example, you can disable the auto reload with --noreload. + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/fastapi/#troubleshooting-your-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..3d4f2c1377 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..6c501e75f7 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-instrumentApplication.md @@ -0,0 +1,34 @@ +#### Requirements +- Python 3.8 or newer + +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.43b0 +pip install opentelemetry-exporter-otlp==1.22.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` + +  + +**Note:** +- Please make sure that you have installed all the dependencies of your application before running the command in **Step 3**. The command will not install instrumentation for the dependencies which are not installed. + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-runApplication.md new file mode 100644 index 0000000000..4177e3c9ff --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/FastAPI/Windows/Recommended/fastapi-windows-recommended-runApplication.md @@ -0,0 +1,45 @@ +  + +Once you are done intrumenting your python application, you can run it using the below steps + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Run your application + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +`` can be something like `python3 app.py` or `python manage.py runserver --noreload` + +  + +**Note** +- Use port **`4317`** for gRPC exporter and port **`4318`** for HTTP exporter +- If your OpenTelemetry Collector agent is in different VM, replace `http://localhost:4317` in above run command with `:4317` + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/fastapi/#troubleshooting-your-installation) for assistance. + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..62afaf5613 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-instrumentApplication.md @@ -0,0 +1,36 @@ +#### Requirements +- Python 3.8 or newer + +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +  +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` + +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` + +  + +**Note:** +- Please make sure that you have installed all the dependencies of your application before running the command in **Step 3**. The command will not install instrumentation for the dependencies which are not installed. + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..5ff8110bab --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/QuickStart/flask-windows-quickStart-runApplication.md @@ -0,0 +1,24 @@ +  + +Once you are done intrumenting your python application, you can run it using this command + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +  + +`` can be something like `python3 app.py` or `flask run` + +  + +**Note** +- Don’t run app in reloader/hot-reload mode as it breaks instrumentation. For example, you can disable the auto reload with --noreload. + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/flask/#troubleshooting-your-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..3d4f2c1377 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..ffcbc42a40 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-instrumentApplication.md @@ -0,0 +1,36 @@ +#### Requirements +- Python 3.8 or newer + +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +  + +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` + +  + +**Note:** +- Please make sure that you have installed all the dependencies of your application before running the command in **Step 3**. The command will not install instrumentation for the dependencies which are not installed. + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-runApplication.md new file mode 100644 index 0000000000..af313d65cf --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Flask/Windows/Recommended/flask-windows-recommended-runApplication.md @@ -0,0 +1,46 @@ +  + +Once you are done intrumenting your python application, you can run it using the below steps + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` + +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` + +  + +### Step 2: Run your application + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +`` can be something like `python3 app.py` or `flask run` + +  + +**Note** +- Use port **`4317`** for gRPC exporter and port **`4318`** for HTTP exporter +- If your OpenTelemetry Collector agent is in different VM, replace `http://localhost:4317` in above run command with `:4317` + + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/flask/#troubleshooting-your-installation) for assistance. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..a2e25f94d3 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-instrumentApplication.md @@ -0,0 +1,36 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your Python Application + +#### Requirements +- Python 3.8 or newer + +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +  + +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..79e2f7e655 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/QuickStart/others-windows-quickStart-runApplication.md @@ -0,0 +1,23 @@ +Once you are done intrumenting your python application, you can run it using this command + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` + +  + +`` can be something like `python3 app.py` or `flask run` + +  + +**Note** +- Don’t run app in reloader/hot-reload mode as it breaks instrumentation. For example, you can disable the auto reload with --noreload. + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/python/#troubleshooting-your-signoz-installation) for assistance. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..3d4f2c1377 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..66cadfe365 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-instrumentApplication.md @@ -0,0 +1,30 @@ +#### Requirements +- Python 3.8 or newer + +  + +### Step 1 : Create a virtual environment + +```bash +python3 -m venv .venv +source .venv/bin/activate +``` +This will create and activate a virtual environment named `.venv` + +  + +### Step 2 : Install the OpenTelemetry dependencies + +```bash +pip install opentelemetry-distro==0.38b0 +pip install opentelemetry-exporter-otlp==1.17.0 +``` +  + +### Step 3 : Add automatic instrumentation + +```bash +opentelemetry-bootstrap --action=install +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md new file mode 100644 index 0000000000..d83c46506d --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Python/md-docs/Others/Windows/Recommended/others-windows-recommended-runApplication.md @@ -0,0 +1,44 @@ +  + +Once you are done intrumenting your python application, you can run it using the below steps + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` +  + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Run your application + +```bash +setx OTEL_RESOURCE_ATTRIBUTES=service.name={{MYAPP}} +setx OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.{{REGION}}.signoz.cloud:443" +setx OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token={{SIGNOZ_INGESTION_KEY}}" +setx OTEL_EXPORTER_OTLP_PROTOCOL=grpc + +opentelemetry-instrument +``` +`` can be something like `python3 app.py` or `flask run` +  + +**Note** +- Use port **`4317`** for gRPC exporter and port **`4318`** for HTTP exporter +- If your OpenTelemetry Collector agent is in different VM, replace `http://localhost:4317` in above run command with `:4317` + +  + +If you encounter any difficulties, please consult the [troubleshooting section](https://signoz.io/docs/instrumentation/python/#troubleshooting-your-signoz-installation) for assistance. + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..effb061be3 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-instrumentApplication.md @@ -0,0 +1,57 @@ +### Send traces directly to SigNoz Cloud + +**Step 1. Install dependencies** + +Install dependencies related to OpenTelemetry SDK and exporter using gem. + +```bash +gem install opentelemetry-sdk +gem install opentelemetry-exporter-otlp +gem install opentelemetry-instrumentation-all +``` + +  + +Include the required packages into your gemfile. + +```bash +gem 'opentelemetry-sdk' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-instrumentation-all' +``` + +  + +Run the bundle install command: + +```bash +bundle install +``` + +  + +**Step 2. Initialize the OpenTelemetry SDK** + +Initialize the otel sdk by adding below lines to `config/initializers/opentelemetry.rb` of your Ruby on Rails application. + +```bash +require 'opentelemetry/sdk' +require 'opentelemetry/exporter/otlp' +require 'opentelemetry/instrumentation/all' + +OpenTelemetry::SDK.configure do |c| + c.add_span_processor( + OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new( + OpenTelemetry::Exporter::OTLP::Exporter.new( + endpoint: '', + headers: { 'signoz-access-token' => '{{SIGNOZ_INGESTION_KEY}}' } + ) + ) + ) + c.resource = OpenTelemetry::SDK::Resources::Resource.create({ + OpenTelemetry::SemanticConventions::Resource::HOST_NAME => '', + }) + c.service_name = '{{MYAPP}}' # The name of the application. + c.use_all() # The libraries supported by automatic OpenTelemetry observation. +end +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..037b663b31 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/QuickStart/ror-windows-quickStart-runApplication.md @@ -0,0 +1,9 @@ +## Running your Ruby application** + +Run the application using the below: + +  + +```bash +rails server +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..2af6744b2a --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-installOtelCollector.md @@ -0,0 +1,3 @@ +OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way. + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Ruby on Rails application. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..3990f1b795 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-instrumentApplication.md @@ -0,0 +1,60 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your Ruby On Rails Application + +  + +**Step 1. Install dependencies** + +Install dependencies related to OpenTelemetry SDK and exporter using gem. + +```bash +gem install opentelemetry-sdk +gem install opentelemetry-exporter-otlp +gem install opentelemetry-instrumentation-all +``` + +  + +Include the required packages into your gemfile. + +```bash +gem 'opentelemetry-sdk' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-instrumentation-all' +``` + +  + +Run the bundle install command: + +```bash +bundle install +``` + +  + +**Step 2. Initialize the OpenTelemetry SDK** + +Initialize the otel sdk by adding below lines to `config/initializers/opentelemetry.rb` of your Ruby on Rails application. + +```bash +require 'opentelemetry/sdk' +require 'opentelemetry/exporter/otlp' +require 'opentelemetry/instrumentation/all' + +OpenTelemetry::SDK.configure do |c| + c.add_span_processor( + OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new( + OpenTelemetry::Exporter::OTLP::Exporter.new( + endpoint: 'http://localhost:4318' + ) + ) + ) + c.resource = OpenTelemetry::SDK::Resources::Resource.create({ + OpenTelemetry::SemanticConventions::Resource::HOST_NAME => '', + }) + c.service_name = '{{MYAPP}}' # The name of the application. + c.use_all() # The libraries supported by automatic OpenTelemetry observation. +end +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-runApplication.md new file mode 100644 index 0000000000..21bf7eb6ed --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/RubyOnRails/md-docs/Windows/Recommended/ror-windows-recommended-runApplication.md @@ -0,0 +1,7 @@ +## Running your Ruby application** + +Run the application using the below: + +```bash +rails server +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..d6d535a195 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-instrumentApplication.md @@ -0,0 +1,167 @@ +**Step 1 : Instrument your application with OpenTelemetry** + +To configure our Rust application to send data we need to initialize OpenTelemetry, Otel has already created some crates which you need to add into your `Cargo.toml` file, just below `[dependencies]` section. + +```bash +opentelemetry = { version = "0.18.0", features = ["rt-tokio", "metrics", "trace"] } +opentelemetry-otlp = { version = "0.11.0", features = ["trace", "metrics"] } +opentelemetry-semantic-conventions = { version = "0.10.0" } +opentelemetry-proto = { version = "0.1.0"} +tokio = { version = "1", features = ["full"] } +tonic = { version = "0.8.2", features = ["tls-roots"] } +``` + +  + +after adding these in `Cargo.toml` , you need to use these in entry point of your Rust application , which is `main.rs` file in majority of applications. + +  + +```bash +use opentelemetry::global::shutdown_tracer_provider; +use opentelemetry::sdk::Resource; +use opentelemetry::trace::TraceError; +use opentelemetry::{ + global, sdk::trace as sdktrace, + trace::{TraceContextExt, Tracer}, + Context, Key, KeyValue, +}; +use opentelemetry_otlp::WithExportConfig; +use tonic::metadata::{MetadataMap, MetadataValue}; +``` + +  + +**Step 2: Initialize the tracer and create env file** + +Add this function in main.rs file, `init_tracer` is initializing an OpenTelemetry tracer with the OpenTelemetry OTLP exporter which is sending data to SigNoz Cloud. + +```bash +fn init_tracer() -> Result { + let signoz_access_token = std::env::var("SIGNOZ_ACCESS_TOKEN").expect("SIGNOZ_ACCESS_TOKEN not set"); + let mut metadata = MetadataMap::new(); + metadata.insert( + "signoz-access-token", + MetadataValue::from_str(&signoz_access_token).unwrap(), + ); + opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter( + opentelemetry_otlp::new_exporter() + .tonic() + .with_metadata(metadata) + .with_endpoint(std::env::var("SIGNOZ_ENDPOINT").expect("SIGNOZ_ENDPOINT not set")), + ) + .with_trace_config( + sdktrace::config().with_resource(Resource::new(vec![ + KeyValue::new( + opentelemetry_semantic_conventions::resource::SERVICE_NAME, + std::env::var("APP_NAME").expect("APP_NAME not set"), + ), + ])), + ) + .install_batch(opentelemetry::runtime::Tokio) +} +``` + +  + +After adding this function, you need to create a `.env` file in root of project , the structure should look like this. + + +```bash +project_root/ +|-- Cargo.toml +|-- src/ +| |-- main.rs +|-- .env +``` + +  + +Paste these in `.env` file + +  + + +```bash +PORT=3000 +APP_NAME={{MYAPP}} +SIGNOZ_ENDPOINT=https://ingest.{{REGION}}.signoz.cloud:443/v1/traces +SIGNOZ_ACCESS_TOKEN={{SIGNOZ_INGESTION_KEY}} +``` + +  + + +**Step 3: Add the OpenTelemetry instrumentation for your Rust app** + +Open your Cargo.toml file and paste these below `[dependencies]` + +```bash +dotenv = "0.15.0" +``` + +  + + +Import these at top, so you can use variables from `.env` file + +```bash +use dotenv::dotenv; +``` + +  + + +After importing , just call these functions inside `main()` function by pasting this at starting of `main()` function + +```bash +dotenv().ok(); +let _ = init_tracer(); +``` + +  + + +also change +```bash +fn main(){ + //rest of the code +} +``` + +  + + +to + + +```bash +#[tokio::main] +async fn main() { + //rest of the code +} +``` +  + + +Now comes the most interesting part, Sending data to SigNoz to get sense of your traces. After adding the below block you can send data to SigNoz cloud + +```bash + let tracer = global::tracer("global_tracer"); + let _cx = Context::new(); + + tracer.in_span("operation", |cx| { + let span = cx.span(); + span.set_attribute(Key::new("KEY").string("value")); + + span.add_event( + format!("Operations"), + vec![ + Key::new("SigNoz is").string("Awesome"), + ], + ); + }); + shutdown_tracer_provider() +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..79bf9ef350 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/QuickStart/rust-windows-quickStart-runApplication.md @@ -0,0 +1,12 @@ + +**Step 4: Set environment variables and run app** + +Go to your `.env` file and update the value of required variables i.e + +```bash +APP_NAME={{MYAPP}} +SIGNOZ_ENDPOINT=https://ingest.{{REGION}}.signoz.cloud:443 +SIGNOZ_ACCESS_TOKEN={{SIGNOZ_INGESTION_KEY}} +``` + +Now run `cargo run` in terminal to start the application! \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..da6ca3feb9 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-installOtelCollector.md @@ -0,0 +1,4 @@ +## Setup OpenTelemetry Binary as an agent +  + +You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your Windows Machine. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Rust application. \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..eaab55e286 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-instrumentApplication.md @@ -0,0 +1,117 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your Rust Application + +  + +**Step 1 : Instrument your application with OpenTelemetry** + +To configure our Rust application to send traces we need to initialize OpenTelemetry, Otel has already created some crates which you need to add into your `Cargo.toml` file, just below `[dependencies]` section. + +``` +opentelemetry = { version = "0.18.0", features = ["rt-tokio", "metrics", "trace"] } +opentelemetry-otlp = { version = "0.11.0", features = ["trace", "metrics"] } +opentelemetry-semantic-conventions = { version = "0.10.0" } +opentelemetry-proto = { version = "0.1.0"} +tokio = { version = "1", features = ["full"] } +tonic = { version = "0.8.2", features = ["tls-roots"] } +``` +after adding these in `Cargo.toml` , you need to use these in entry point of your Rust application , which is `main.rs` file in majority of applications. + +```rust +use opentelemetry::global::shutdown_tracer_provider; +use opentelemetry::sdk::Resource; +use opentelemetry::trace::TraceError; +use opentelemetry::{ + global, sdk::trace as sdktrace, + trace::{TraceContextExt, Tracer}, + Context, Key, KeyValue, +}; +use opentelemetry_otlp::WithExportConfig; +use tonic::metadata::{MetadataMap, MetadataValue}; +``` + +**Step 2: Initialize the tracer and create env file** + +Add this function in main.rs file, `init_tracer` is initializing an OpenTelemetry tracer with the OpenTelemetry OTLP exporter which is sending data to SigNoz Cloud. + +This tracer initializes the connection with the OTel collector from the system variables passed while starting the app. + +```rust +fn init_tracer() -> Result { + opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter( + opentelemetry_otlp::new_exporter() + .tonic() + .with_endpoint(std::env::var("OTEL_EXPORTER_OTLP_ENDPOINT").expect("OTEL_EXPORTER_OTLP_ENDPOINT not set")), + ) + .with_trace_config( + sdktrace::config().with_resource(Resource::new(vec![ + KeyValue::new( + opentelemetry_semantic_conventions::resource::SERVICE_NAME, + std::env::var("APP_NAME").expect("APP_NAME not set"), + ), + ])), + ) + .install_batch(opentelemetry::runtime::Tokio) +} +``` + +**Step 3: Add the OpenTelemetry instrumentation for your Rust app** + +You need call init_tracer function inside `main()` at starting so that as soon as your rust application starts, tracer will be available globally. +```rust +let _ = init_tracer(); +``` + +also change +```rust +fn main(){ + //rest of the code +} +``` +to +```rust +#[tokio::main] +async fn main() { + //rest of the code +} +``` + +Now comes the most interesting part, Sending data to SigNoz to get sense of your traces. After adding the below block you can send traces to SigNoz cloud + +```rust + let tracer = global::tracer("global_tracer"); + let _cx = Context::new(); + + tracer.in_span("operation", |cx| { + let span = cx.span(); + span.set_attribute(Key::new("KEY").string("value")); + + span.add_event( + format!("Operations"), + vec![ + Key::new("SigNoz is").string("Awesome"), + ], + ); + }); + shutdown_tracer_provider() +``` + +**Step 4: Set environment variables and run app** + +Create a `.env` file in root of project , the structure should look like this. +``` +project_root/ +|-- Cargo.toml +|-- src/ +| |-- main.rs +|-- .env +``` + +Paste these in `.env` file +```bash +OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 +OTEL_RESOURCE_ATTRIBUTES={{MYAPP}} +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-runApplication.md new file mode 100644 index 0000000000..049c94be83 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Rust/md-docs/Windows/Recommended/rust-windows-recommended-runApplication.md @@ -0,0 +1,8 @@ +Now run + +```bash +cargo run +``` + + +in terminal to start the application! \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-instrumentApplication.md new file mode 100644 index 0000000000..a75a7b00a7 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-instrumentApplication.md @@ -0,0 +1,65 @@ +  + +### Step 1: Add dependencies + +To configure your Swift application to send data you need to initialize OpenTelemetry. Add these dependency in `Package.swift` file of your project or if you are using XCode then you need to add this [dependency](https://github.com/open-telemetry/opentelemetry-swift) and then import these below dependencies in the main file. + +```swift +import Foundation +import GRPC +import NIO +import NIOSSL +import OpenTelemetryApi +import OpenTelemetryProtocolExporterCommon +import OpenTelemetryProtocolExporterGrpc +import OpenTelemetrySdk +import ResourceExtension +import SignPostIntegration +import StdoutExporter +import ZipkinExporter +``` + +  + +### Step 2: Initialize tracer +Initialize the tracer using the code block below in the `main.swift` file inside the main function or you can create another function for initializing the tracer and call it in some other block of code. + +```swift +var resources = DefaultResources().get() + +let instrumentationScopeName = "{{MYAPP}}" +let instrumentationScopeVersion = "semver:0.1.0" + +let otlpConfiguration: OtlpConfiguration = OtlpConfiguration(timeout: TimeInterval(10), headers: [("signoz-access-token", {{SIGNOZ_INGESTION_KEY}})]) + +let grpcChannel = ClientConnection.usingPlatformAppropriateTLS(for: MultiThreadedEventLoopGroup(numberOfThreads:1)).connect(host: "https://ingest.{{REGION}}.signoz.cloud:443", port: 443) + +let otlpTraceExporter = OtlpTraceExporter(channel: grpcChannel, + config: otlpConfiguration) +let stdoutExporter = StdoutExporter() + +let spanExporter = MultiSpanExporter(spanExporters: [otlpTraceExporter, stdoutExporter]) + +let spanProcessor = SimpleSpanProcessor(spanExporter: spanExporter) +OpenTelemetry.registerTracerProvider(tracerProvider: + TracerProviderBuilder() + .add(spanProcessor: spanProcessor) + .build() +) +``` + + +### Step 3: Add OpenTelemetry instrumentation + +```swift +func doWork() { + let childSpan = tracer.spanBuilder(spanName: "doWork").setSpanKind(spanKind: .client).startSpan() + childSpan.setAttribute(key: sampleKey, value: sampleValue) + Thread.sleep(forTimeInterval: Double.random(in: 0 ..< 10) / 100) + childSpan.end() +} +``` + +  + +If you call this `doWork` function, it will add a trace with span name "doWork" and attributes with key-value pair. You can modify this function according to your needs. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-runApplication.md new file mode 100644 index 0000000000..2c5e5d0b1b --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/QuickStart/swift-windows-quickStart-runApplication.md @@ -0,0 +1,7 @@ +### Running your Swift application + +Run the application using the below command: + +```bash +swift run +``` diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-installOtelCollector.md b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-installOtelCollector.md new file mode 100644 index 0000000000..3d4f2c1377 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-installOtelCollector.md @@ -0,0 +1,96 @@ +## Setup OpenTelemetry Binary as an agent +  + +### Step 1: Download otel-collector tar.gz +```bash +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v{{OTEL_VERSION}}/otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz +``` +  + +### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder +```bash +mkdir otelcol-contrib && tar xvzf otelcol-contrib_{{OTEL_VERSION}}_linux_amd64.tar.gz -C otelcol-contrib +``` +  + +### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it +```bash +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + hostmetrics: + collection_interval: 60s + scrapers: + cpu: {} + disk: {} + load: {} + filesystem: {} + memory: {} + network: {} + paging: {} + process: + mute_process_name_error: true + mute_process_exe_error: true + mute_process_io_error: true + processes: {} + prometheus: + config: + global: + scrape_interval: 60s + scrape_configs: + - job_name: otel-collector-binary + static_configs: + - targets: + # - localhost:8888 +processors: + batch: + send_batch_size: 1000 + timeout: 10s + # Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md + resourcedetection: + detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure. + # Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels. + timeout: 2s + system: + hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback +extensions: + health_check: {} + zpages: {} +exporters: + otlp: + endpoint: "ingest.{{REGION}}.signoz.cloud:443" + tls: + insecure: false + headers: + "signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}" + logging: + verbosity: normal +service: + telemetry: + metrics: + address: 0.0.0.0:8888 + extensions: [health_check, zpages] + pipelines: + metrics: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics/internal: + receivers: [prometheus, hostmetrics] + processors: [resourcedetection, batch] + exporters: [otlp] + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlp] +``` + + diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-instrumentApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-instrumentApplication.md new file mode 100644 index 0000000000..a3fe109cc4 --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-instrumentApplication.md @@ -0,0 +1,70 @@ +  + +After setting up the Otel collector agent, follow the steps below to instrument your Swift Application + +### Step 1: Add dependencies + +To configure your Swift application to send data you need to initialize OpenTelemetry. Add these dependency in `Package.swift` file of your project or if you are using XCode then you need to add this [dependency](https://github.com/open-telemetry/opentelemetry-swift) and then import these below dependencies in the main file. + +```swift +import Foundation +import GRPC +import NIO +import NIOSSL +import OpenTelemetryApi +import OpenTelemetryProtocolExporterCommon +import OpenTelemetryProtocolExporterGrpc +import OpenTelemetrySdk +import ResourceExtension +import SignPostIntegration +import StdoutExporter +import ZipkinExporter +``` + +  + +### Step 2: Initialize tracer +Initialize the tracer using the code block below in the `main.swift` file inside the main function or you can create another function for initializing the tracer and call it in some other block of code. + +```swift +var resources = DefaultResources().get() + +let instrumentationScopeName = "{{MYAPP}}" +let instrumentationScopeVersion = "semver:0.1.0" + +let otlpConfiguration: OtlpConfiguration = OtlpConfiguration(timeout: TimeInterval(10)) + +let grpcChannel = ClientConnection.usingPlatformAppropriateTLS(for: MultiThreadedEventLoopGroup(numberOfThreads:1)).connect(host: , port: 4317) + +let otlpTraceExporter = OtlpTraceExporter(channel: grpcChannel, + config: otlpConfiguration) +let stdoutExporter = StdoutExporter() + +let spanExporter = MultiSpanExporter(spanExporters: [otlpTraceExporter, stdoutExporter]) + +let spanProcessor = SimpleSpanProcessor(spanExporter: spanExporter) +OpenTelemetry.registerTracerProvider(tracerProvider: + TracerProviderBuilder() + .add(spanProcessor: spanProcessor) + .build() +) +``` +- - The endpoint where Otel Collector is running. For ex -> "localhost" + + + + +### Step 3: Add OpenTelemetry instrumentation + +```swift +func doWork() { + let childSpan = tracer.spanBuilder(spanName: "doWork").setSpanKind(spanKind: .client).startSpan() + childSpan.setAttribute(key: sampleKey, value: sampleValue) + Thread.sleep(forTimeInterval: Double.random(in: 0 ..< 10) / 100) + childSpan.end() +} +``` + +  + +If you call this `doWork` function, it will add a trace with span name "doWork" and attributes with key-value pair. You can modify this function according to your needs. diff --git a/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-runApplication.md b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-runApplication.md new file mode 100644 index 0000000000..5efc5859db --- /dev/null +++ b/frontend/src/container/OnboardingContainer/Modules/APM/Swift/md-docs/Windows/Recommended/swift-windows-recommended-runApplication.md @@ -0,0 +1,32 @@ +  + +Once you are done instrumenting your Swift application, you can run it using the below commands + +  + +### Step 1: Run OTel Collector + Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step + +```bash +./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid +``` +  + +#### (Optional Step): View last 50 lines of `otelcol` logs +```bash +tail -f -n 50 otelcol-output.log +``` + +#### (Optional Step): Stop `otelcol` +```bash +kill "$(< otel-pid)" +``` +  + +### Step 2: Running your Swift application + +Run the application using the below command: + +```bash +swift run +``` \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Steps/EnvironmentDetails/EnvironmentDetails.tsx b/frontend/src/container/OnboardingContainer/Steps/EnvironmentDetails/EnvironmentDetails.tsx index 60ecf403bf..990e5edf0f 100644 --- a/frontend/src/container/OnboardingContainer/Steps/EnvironmentDetails/EnvironmentDetails.tsx +++ b/frontend/src/container/OnboardingContainer/Steps/EnvironmentDetails/EnvironmentDetails.tsx @@ -39,6 +39,10 @@ const supportedEnvironments: SupportedEnvironmentsProps[] = [ name: 'Docker', id: 'docker', }, + { + name: 'Windows', + id: 'windows', + }, ]; export default function EnvironmentDetails(): JSX.Element { @@ -132,6 +136,12 @@ export default function EnvironmentDetails(): JSX.Element { ) { return null; } + if ( + selectedModule?.id !== useCases.APM.id && + environment.id === 'windows' + ) { + return null; + } return ( Date: Thu, 1 Aug 2024 11:29:05 +0530 Subject: [PATCH 137/281] fix: logs context not working because of incorrect request data (#5595) --- .../ContextView/useContextLogData.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts b/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts index b29b8d3ef6..91c7fdf3f8 100644 --- a/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts +++ b/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts @@ -1,3 +1,4 @@ +/* eslint-disable sonarjs/cognitive-complexity */ import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { PANEL_TYPES } from 'constants/queryBuilder'; import { @@ -46,6 +47,8 @@ export const useContextLogData = ({ } => { const [logs, setLogs] = useState([]); + const [lastLog, setLastLog] = useState(log); + const orderByTimestamp = useMemo(() => getOrderByTimestamp(order), [order]); const logsMorePageSize = useMemo(() => (page - 1) * LOGS_MORE_PAGE_SIZE, [ @@ -71,11 +74,11 @@ export const useContextLogData = ({ getRequestData({ stagedQueryData: currentStagedQueryData, query, - log, + log: lastLog, orderByTimestamp, page, }), - [currentStagedQueryData, page, log, query, orderByTimestamp], + [currentStagedQueryData, query, lastLog, orderByTimestamp, page], ); const [requestData, setRequestData] = useState( @@ -95,8 +98,10 @@ export const useContextLogData = ({ if (order === ORDERBY_FILTERS.ASC) { const reversedCurrentLogs = currentLogs.reverse(); setLogs([...reversedCurrentLogs]); + setLastLog(reversedCurrentLogs[0]); } else { setLogs([...currentLogs]); + setLastLog(currentLogs[currentLogs.length - 1]); } } }, @@ -118,7 +123,7 @@ export const useContextLogData = ({ const newRequestData = getRequestData({ stagedQueryData: currentStagedQueryData, query, - log, + log: lastLog, orderByTimestamp, page: page + 1, pageSize: LOGS_MORE_PAGE_SIZE, @@ -131,6 +136,7 @@ export const useContextLogData = ({ query, page, order, + lastLog, currentStagedQueryData, isDisabledFetch, orderByTimestamp, @@ -142,7 +148,7 @@ export const useContextLogData = ({ const newRequestData = getRequestData({ stagedQueryData: currentStagedQueryData, query, - log, + log: lastLog, orderByTimestamp, page: 1, }); From 3783ffdd4cc5a86b91d4698d8601900baa22d171 Mon Sep 17 00:00:00 2001 From: Raj Kamal Singh <1133322+raj-k-singh@users.noreply.github.com> Date: Thu, 1 Aug 2024 12:06:29 +0530 Subject: [PATCH 138/281] feat: show log severity indicator based on severity number if it's available when severity text is unknown (#4971) * feat: set log sev indicator based on severity number if severity text is unknown * chore: some cleanup * chore: some more cleanup * chore: update log state indicator utils test * chore: some more cleanup * fix: priority to severity_number over severity_text and update tests * fix: made the severity_text check case insensitive and added null checks --------- Co-authored-by: Vikrant Gupta --- .../LogStateIndicator/LogStateIndicator.tsx | 7 +- .../Logs/LogStateIndicator/utils.test.ts | 112 ++++++++++++++++-- .../Logs/LogStateIndicator/utils.ts | 88 +++++++++++--- .../src/container/LogDetailedView/utils.tsx | 1 + frontend/src/types/api/logs/log.ts | 1 + 5 files changed, 184 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx index 5355e38017..ebad7bd116 100644 --- a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx +++ b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx @@ -28,12 +28,17 @@ export const SEVERITY_TEXT_TYPE = { FATAL2: 'FATAL2', FATAL3: 'FATAL3', FATAL4: 'FATAL4', + UNKNOWN: 'UNKNOWN', } as const; export const LogType = { + TRACE: 'TRACE', + DEBUG: 'DEBUG', INFO: 'INFO', - WARNING: 'WARNING', + WARN: 'WARN', ERROR: 'ERROR', + FATAL: 'FATAL', + UNKNOWN: 'UNKNOWN', } as const; function LogStateIndicator({ diff --git a/frontend/src/components/Logs/LogStateIndicator/utils.test.ts b/frontend/src/components/Logs/LogStateIndicator/utils.test.ts index 65f6b9664d..17c601ffb4 100644 --- a/frontend/src/components/Logs/LogStateIndicator/utils.test.ts +++ b/frontend/src/components/Logs/LogStateIndicator/utils.test.ts @@ -1,9 +1,10 @@ +/* eslint-disable sonarjs/no-duplicate-string */ import { ILog } from 'types/api/logs/log'; import { getLogIndicatorType, getLogIndicatorTypeForTable } from './utils'; describe('getLogIndicatorType', () => { - it('should return severity type for valid log with severityText', () => { + it('severity_number should be given priority over severity_text', () => { const log = { date: '2024-02-29T12:34:46Z', timestamp: 1646115296, @@ -20,11 +21,57 @@ describe('getLogIndicatorType', () => { attributesInt: {}, attributesFloat: {}, severity_text: 'INFO', + severity_number: 2, }; - expect(getLogIndicatorType(log)).toBe('INFO'); + // severity_number should get priority over severity_text + expect(getLogIndicatorType(log)).toBe('TRACE'); }); - it('should return log level if severityText is missing', () => { + it('severity_text should be used when severity_number is absent ', () => { + const log = { + date: '2024-02-29T12:34:46Z', + timestamp: 1646115296, + id: '123456', + traceId: '987654', + spanId: '54321', + traceFlags: 0, + severityText: 'INFO', + severityNumber: 2, + body: 'Sample log Message', + resources_string: {}, + attributesString: {}, + attributes_string: {}, + attributesInt: {}, + attributesFloat: {}, + severity_text: 'FATAL', + severity_number: 0, + }; + expect(getLogIndicatorType(log)).toBe('FATAL'); + }); + + it('case insensitive severity_text should be valid', () => { + const log = { + date: '2024-02-29T12:34:46Z', + timestamp: 1646115296, + id: '123456', + traceId: '987654', + spanId: '54321', + traceFlags: 0, + severityText: 'INFO', + severityNumber: 2, + body: 'Sample log Message', + resources_string: {}, + attributesString: {}, + attributes_string: {}, + attributesInt: {}, + attributesFloat: {}, + severity_text: 'fatAl', + severity_number: 0, + }; + expect(getLogIndicatorType(log)).toBe('FATAL'); + }); + + it('should return log level if severityText and severityNumber is missing', () => { const log: ILog = { date: '2024-02-29T12:34:58Z', timestamp: 1646115296, @@ -36,13 +83,16 @@ describe('getLogIndicatorType', () => { body: 'Sample log', resources_string: {}, attributesString: {}, - attributes_string: {}, + attributes_string: { + log_level: 'INFO' as never, + }, attributesInt: {}, attributesFloat: {}, - severity_text: 'FATAL', + severity_text: 'some_random', severityText: '', + severity_number: 0, }; - expect(getLogIndicatorType(log)).toBe('FATAL'); + expect(getLogIndicatorType(log)).toBe('INFO'); }); }); @@ -55,6 +105,7 @@ describe('getLogIndicatorTypeForTable', () => { traceId: '987654', spanId: '54321', traceFlags: 0, + severityNumber: 2, severity_number: 2, body: 'Sample log message', resources_string: {}, @@ -64,7 +115,7 @@ describe('getLogIndicatorTypeForTable', () => { attributesFloat: {}, severity_text: 'WARN', }; - expect(getLogIndicatorTypeForTable(log)).toBe('WARN'); + expect(getLogIndicatorTypeForTable(log)).toBe('TRACE'); }); it('should return log level if severityText is missing', () => { @@ -75,7 +126,8 @@ describe('getLogIndicatorTypeForTable', () => { traceId: '987654', spanId: '54321', traceFlags: 0, - severityNumber: 2, + severityNumber: 0, + severity_number: 0, body: 'Sample log message', resources_string: {}, attributesString: {}, @@ -87,3 +139,47 @@ describe('getLogIndicatorTypeForTable', () => { expect(getLogIndicatorTypeForTable(log)).toBe('INFO'); }); }); + +describe('logIndicatorBySeverityNumber', () => { + // https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber + const logLevelExpectations = [ + { minSevNumber: 1, maxSevNumber: 4, expectedIndicatorType: 'TRACE' }, + { minSevNumber: 5, maxSevNumber: 8, expectedIndicatorType: 'DEBUG' }, + { minSevNumber: 9, maxSevNumber: 12, expectedIndicatorType: 'INFO' }, + { minSevNumber: 13, maxSevNumber: 16, expectedIndicatorType: 'WARN' }, + { minSevNumber: 17, maxSevNumber: 20, expectedIndicatorType: 'ERROR' }, + { minSevNumber: 21, maxSevNumber: 24, expectedIndicatorType: 'FATAL' }, + ]; + logLevelExpectations.forEach((e) => { + for (let sevNum = e.minSevNumber; sevNum <= e.maxSevNumber; sevNum++) { + const sevText = (Math.random() + 1).toString(36).substring(2); + + const log = { + date: '2024-02-29T12:34:46Z', + timestamp: 1646115296, + id: '123456', + traceId: '987654', + spanId: '54321', + traceFlags: 0, + severityText: sevText, + severityNumber: sevNum, + body: 'Sample log Message', + resources_string: {}, + attributesString: {}, + attributes_string: {}, + attributesInt: {}, + attributesFloat: {}, + severity_text: sevText, + severity_number: sevNum, + }; + + it(`getLogIndicatorType should return ${e.expectedIndicatorType} for severity_text: ${sevText} and severity_number: ${sevNum}`, () => { + expect(getLogIndicatorType(log)).toBe(e.expectedIndicatorType); + }); + + it(`getLogIndicatorTypeForTable should return ${e.expectedIndicatorType} for severity_text: ${sevText} and severity_number: ${sevNum}`, () => { + expect(getLogIndicatorTypeForTable(log)).toBe(e.expectedIndicatorType); + }); + } + }); +}); diff --git a/frontend/src/components/Logs/LogStateIndicator/utils.ts b/frontend/src/components/Logs/LogStateIndicator/utils.ts index 7bfe7a430a..03989a8dd6 100644 --- a/frontend/src/components/Logs/LogStateIndicator/utils.ts +++ b/frontend/src/components/Logs/LogStateIndicator/utils.ts @@ -2,56 +2,112 @@ import { ILog } from 'types/api/logs/log'; import { LogType, SEVERITY_TEXT_TYPE } from './LogStateIndicator'; -const getSeverityType = (severityText: string): string => { +const getLogTypeBySeverityText = (severityText: string): string => { switch (severityText) { case SEVERITY_TEXT_TYPE.TRACE: case SEVERITY_TEXT_TYPE.TRACE2: case SEVERITY_TEXT_TYPE.TRACE3: case SEVERITY_TEXT_TYPE.TRACE4: - return SEVERITY_TEXT_TYPE.TRACE; + return LogType.TRACE; case SEVERITY_TEXT_TYPE.DEBUG: case SEVERITY_TEXT_TYPE.DEBUG2: case SEVERITY_TEXT_TYPE.DEBUG3: case SEVERITY_TEXT_TYPE.DEBUG4: - return SEVERITY_TEXT_TYPE.DEBUG; + return LogType.DEBUG; case SEVERITY_TEXT_TYPE.INFO: case SEVERITY_TEXT_TYPE.INFO2: case SEVERITY_TEXT_TYPE.INFO3: case SEVERITY_TEXT_TYPE.INFO4: - return SEVERITY_TEXT_TYPE.INFO; + return LogType.INFO; case SEVERITY_TEXT_TYPE.WARN: case SEVERITY_TEXT_TYPE.WARN2: case SEVERITY_TEXT_TYPE.WARN3: case SEVERITY_TEXT_TYPE.WARN4: case SEVERITY_TEXT_TYPE.WARNING: - return SEVERITY_TEXT_TYPE.WARN; + return LogType.WARN; case SEVERITY_TEXT_TYPE.ERROR: case SEVERITY_TEXT_TYPE.ERROR2: case SEVERITY_TEXT_TYPE.ERROR3: case SEVERITY_TEXT_TYPE.ERROR4: - return SEVERITY_TEXT_TYPE.ERROR; + return LogType.ERROR; case SEVERITY_TEXT_TYPE.FATAL: case SEVERITY_TEXT_TYPE.FATAL2: case SEVERITY_TEXT_TYPE.FATAL3: case SEVERITY_TEXT_TYPE.FATAL4: - return SEVERITY_TEXT_TYPE.FATAL; + return LogType.FATAL; default: - return SEVERITY_TEXT_TYPE.INFO; + return LogType.UNKNOWN; } }; -export const getLogIndicatorType = (logData: ILog): string => { - if (logData.severity_text) { - return getSeverityType(logData.severity_text); +// https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber +const getLogTypeBySeverityNumber = (severityNumber: number): string => { + if (severityNumber < 1) { + return LogType.UNKNOWN; + } + if (severityNumber < 5) { + return LogType.TRACE; + } + if (severityNumber < 9) { + return LogType.DEBUG; + } + if (severityNumber < 13) { + return LogType.INFO; + } + if (severityNumber < 17) { + return LogType.WARN; + } + if (severityNumber < 21) { + return LogType.ERROR; + } + if (severityNumber < 25) { + return LogType.FATAL; + } + return LogType.UNKNOWN; +}; + +const getLogType = ( + severityText: string, + severityNumber: number, + defaultType: string, +): string => { + // give priority to the severityNumber + if (severityNumber) { + const logType = getLogTypeBySeverityNumber(severityNumber); + if (logType !== LogType.UNKNOWN) { + return logType; + } } - return logData.attributes_string?.log_level || LogType.INFO; + + // is severityNumber is not present then rely on the severityText + if (severityText) { + const logType = getLogTypeBySeverityText(severityText); + if (logType !== LogType.UNKNOWN) { + return logType; + } + } + + return defaultType; +}; + +export const getLogIndicatorType = (logData: ILog): string => { + const defaultType = logData.attributes_string?.log_level || LogType.INFO; + // convert the severity_text to upper case for the comparison to support case insensitive values + return getLogType( + logData?.severity_text?.toUpperCase(), + logData?.severity_number || 0, + defaultType, + ); }; export const getLogIndicatorTypeForTable = ( log: Record, ): string => { - if (log.severity_text) { - return getSeverityType(log.severity_text as string); - } - return (log.log_level as string) || LogType.INFO; + const defaultType = (log.log_level as string) || LogType.INFO; + // convert the severity_text to upper case for the comparison to support case insensitive values + return getLogType( + (log?.severity_text as string)?.toUpperCase(), + (log?.severity_number as number) || 0, + defaultType, + ); }; diff --git a/frontend/src/container/LogDetailedView/utils.tsx b/frontend/src/container/LogDetailedView/utils.tsx index c81b7dc9ed..f5d7d7e325 100644 --- a/frontend/src/container/LogDetailedView/utils.tsx +++ b/frontend/src/container/LogDetailedView/utils.tsx @@ -188,6 +188,7 @@ export const aggregateAttributesResourcesToString = (logData: ILog): string => { attributes: {}, resources: {}, severity_text: logData.severity_text, + severity_number: logData.severity_number, }; Object.keys(logData).forEach((key) => { diff --git a/frontend/src/types/api/logs/log.ts b/frontend/src/types/api/logs/log.ts index 15b7f24c74..85574248f7 100644 --- a/frontend/src/types/api/logs/log.ts +++ b/frontend/src/types/api/logs/log.ts @@ -14,6 +14,7 @@ export interface ILog { attributesInt: Record; attributesFloat: Record; severity_text: string; + severity_number: number; } type OmitAttributesResources = Pick< From 08a415032ca4ea1db8cc032b8f1285b252cf56b7 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Thu, 1 Aug 2024 14:17:00 +0530 Subject: [PATCH 139/281] chore: added service name and time params for top level operations (#5552) * chore: added service name and time params for top level operations * fix: build issues * chore: update the useTopLevelOpertions to send start and end time * chore: added extra checks to not send the param when undefined * chore: added extra checks to not send the param when undefined --------- Co-authored-by: Srikanth Chekuri --- .../src/api/metrics/getTopLevelOperations.ts | 17 +++++++++++++++-- .../MetricsApplication/Tabs/Overview.tsx | 18 +++++++++++++++--- .../ServiceMetrics/index.tsx | 5 ++++- frontend/src/hooks/useGetTopLevelOperations.ts | 11 +++++++++-- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/frontend/src/api/metrics/getTopLevelOperations.ts b/frontend/src/api/metrics/getTopLevelOperations.ts index 2f5a2026b2..a0f996412b 100644 --- a/frontend/src/api/metrics/getTopLevelOperations.ts +++ b/frontend/src/api/metrics/getTopLevelOperations.ts @@ -1,7 +1,20 @@ import axios from 'api'; +import { isNil } from 'lodash-es'; -const getTopLevelOperations = async (): Promise => { - const response = await axios.post(`/service/top_level_operations`); +interface GetTopLevelOperationsProps { + service?: string; + start?: number; + end?: number; +} + +const getTopLevelOperations = async ( + props: GetTopLevelOperationsProps, +): Promise => { + const response = await axios.post(`/service/top_level_operations`, { + start: !isNil(props.start) ? `${props.start}` : undefined, + end: !isNil(props.end) ? `${props.end}` : undefined, + service: props.service, + }); return response.data; }; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 4fb9a2ccf3..916c3435b5 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -20,12 +20,14 @@ import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { defaultTo } from 'lodash-es'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useQuery } from 'react-query'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { useLocation, useParams } from 'react-router-dom'; import { UpdateTimeInterval } from 'store/actions'; +import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; +import { GlobalReducer } from 'types/reducer/globalTime'; import { v4 as uuid } from 'uuid'; import { GraphTitle, SERVICE_CHART_ID } from '../constant'; @@ -52,6 +54,11 @@ import { function Application(): JSX.Element { const { servicename: encodedServiceName } = useParams(); const servicename = decodeURIComponent(encodedServiceName); + + const { maxTime, minTime } = useSelector( + (state) => state.globalTime, + ); + const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); const { search, pathname } = useLocation(); const { queries } = useResourceAttribute(); @@ -105,8 +112,13 @@ function Application(): JSX.Element { isLoading: topLevelOperationsIsLoading, isError: topLevelOperationsIsError, } = useQuery({ - queryKey: [servicename], - queryFn: getTopLevelOperations, + queryKey: [servicename, minTime, maxTime], + queryFn: (): Promise => + getTopLevelOperations({ + service: servicename || '', + start: minTime, + end: maxTime, + }), }); const selectedTraceTags: string = JSON.stringify( diff --git a/frontend/src/container/ServiceApplication/ServiceMetrics/index.tsx b/frontend/src/container/ServiceApplication/ServiceMetrics/index.tsx index 740168f96c..c132f03a5c 100644 --- a/frontend/src/container/ServiceApplication/ServiceMetrics/index.tsx +++ b/frontend/src/container/ServiceApplication/ServiceMetrics/index.tsx @@ -32,7 +32,10 @@ function ServicesUsingMetrics(): JSX.Element { selectedTags, globalSelectedInterval, ]; - const { data, isLoading, isError } = useGetTopLevelOperations(queryKey); + const { data, isLoading, isError } = useGetTopLevelOperations(queryKey, { + start: minTime, + end: maxTime, + }); const [skipOnboarding, setSkipOnboarding] = useState( localStorageGet(SKIP_ONBOARDING) === 'true', diff --git a/frontend/src/hooks/useGetTopLevelOperations.ts b/frontend/src/hooks/useGetTopLevelOperations.ts index 1500d90169..2c4bdcd1dc 100644 --- a/frontend/src/hooks/useGetTopLevelOperations.ts +++ b/frontend/src/hooks/useGetTopLevelOperations.ts @@ -3,14 +3,21 @@ import getTopLevelOperations, { } from 'api/metrics/getTopLevelOperations'; import { QueryKey, useQuery, UseQueryResult } from 'react-query'; +interface UseGetTopLevelOperationsParams { + start: number; + end: number; +} + type UseGetTopLevelOperations = ( queryKey: QueryKey, + params: UseGetTopLevelOperationsParams, ) => UseQueryResult; -const useGetTopLevelOperations: UseGetTopLevelOperations = (queryKey) => +const useGetTopLevelOperations: UseGetTopLevelOperations = (queryKey, params) => useQuery({ queryKey, - queryFn: getTopLevelOperations, + queryFn: (): Promise => + getTopLevelOperations({ start: params.start, end: params.end }), }); export default useGetTopLevelOperations; From fff61379fe56e421df219fedc54a9b8fce9680fb Mon Sep 17 00:00:00 2001 From: Prashant Shahi Date: Fri, 2 Aug 2024 16:06:21 +0530 Subject: [PATCH 140/281] fix: mount root path in /hostfs for hostmetrics (#5534) Signed-off-by: Prashant Shahi --- deploy/docker-swarm/clickhouse-setup/docker-compose.yaml | 1 + deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml | 1 + deploy/docker/clickhouse-setup/docker-compose-core.yaml | 2 ++ deploy/docker/clickhouse-setup/docker-compose.testing.yaml | 1 + deploy/docker/clickhouse-setup/docker-compose.yaml | 1 + deploy/docker/clickhouse-setup/otel-collector-config.yaml | 1 + pkg/query-service/tests/test-deploy/docker-compose.yaml | 1 + pkg/query-service/tests/test-deploy/otel-collector-config.yaml | 1 + 8 files changed, 9 insertions(+) diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index de7ab6655e..c584bb50e7 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -211,6 +211,7 @@ services: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /:/hostfs:ro environment: - OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}},dockerswarm.service.name={{.Service.Name}},dockerswarm.task.name={{.Task.Name}} - DOCKER_MULTI_NODE_CLUSTER=false diff --git a/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml b/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml index 13ab19ca2c..091d4f504b 100644 --- a/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml +++ b/deploy/docker-swarm/clickhouse-setup/otel-collector-config.yaml @@ -36,6 +36,7 @@ receivers: # endpoint: 0.0.0.0:6832 hostmetrics: collection_interval: 30s + root_path: /hostfs scrapers: cpu: {} load: {} diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index 73b862ea6e..17f7c3e4a3 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -93,6 +93,8 @@ services: volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /:/hostfs:ro environment: - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux ports: diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index 849a536c27..8d4564af31 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -244,6 +244,7 @@ services: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /:/hostfs:ro environment: - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux - DOCKER_MULTI_NODE_CLUSTER=false diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 6528a1826f..17a975e5a6 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -243,6 +243,7 @@ services: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /:/hostfs:ro environment: - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux - DOCKER_MULTI_NODE_CLUSTER=false diff --git a/deploy/docker/clickhouse-setup/otel-collector-config.yaml b/deploy/docker/clickhouse-setup/otel-collector-config.yaml index 85e89498be..e6d4c2418e 100644 --- a/deploy/docker/clickhouse-setup/otel-collector-config.yaml +++ b/deploy/docker/clickhouse-setup/otel-collector-config.yaml @@ -36,6 +36,7 @@ receivers: # endpoint: 0.0.0.0:6832 hostmetrics: collection_interval: 30s + root_path: /hostfs scrapers: cpu: {} load: {} diff --git a/pkg/query-service/tests/test-deploy/docker-compose.yaml b/pkg/query-service/tests/test-deploy/docker-compose.yaml index 19d088dc7a..05c3cddf15 100644 --- a/pkg/query-service/tests/test-deploy/docker-compose.yaml +++ b/pkg/query-service/tests/test-deploy/docker-compose.yaml @@ -219,6 +219,7 @@ services: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml - /var/lib/docker/containers:/var/lib/docker/containers:ro + - /:/hostfs:ro environment: - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux - DOCKER_MULTI_NODE_CLUSTER=false diff --git a/pkg/query-service/tests/test-deploy/otel-collector-config.yaml b/pkg/query-service/tests/test-deploy/otel-collector-config.yaml index d7b9f357fb..589598c383 100644 --- a/pkg/query-service/tests/test-deploy/otel-collector-config.yaml +++ b/pkg/query-service/tests/test-deploy/otel-collector-config.yaml @@ -40,6 +40,7 @@ receivers: # endpoint: 0.0.0.0:6832 hostmetrics: collection_interval: 30s + root_path: /hostfs scrapers: cpu: {} load: {} From 16dc90bbd1a414e2e10e82f65895ce2d9642dcf1 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Fri, 2 Aug 2024 18:45:02 +0530 Subject: [PATCH 141/281] =?UTF-8?q?chore(telemetry):=20add=20telemetry=20f?= =?UTF-8?q?or=20metrics=20query=20type=20and=20count=20prom=E2=80=A6=20(#5?= =?UTF-8?q?627)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/query-service/model/response.go | 24 ++++++++++++++---------- pkg/query-service/rules/db.go | 16 ++++++++++++++++ pkg/query-service/telemetry/telemetry.go | 4 ++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index 7e8e883164..eb979642da 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -634,16 +634,20 @@ type TagsInfo struct { } type AlertsInfo struct { - TotalAlerts int `json:"totalAlerts"` - LogsBasedAlerts int `json:"logsBasedAlerts"` - MetricBasedAlerts int `json:"metricBasedAlerts"` - TracesBasedAlerts int `json:"tracesBasedAlerts"` - SlackChannels int `json:"slackChannels"` - WebHookChannels int `json:"webHookChannels"` - PagerDutyChannels int `json:"pagerDutyChannels"` - OpsGenieChannels int `json:"opsGenieChannels"` - EmailChannels int `json:"emailChannels"` - MSTeamsChannels int `json:"microsoftTeamsChannels"` + TotalAlerts int `json:"totalAlerts"` + LogsBasedAlerts int `json:"logsBasedAlerts"` + MetricBasedAlerts int `json:"metricBasedAlerts"` + TracesBasedAlerts int `json:"tracesBasedAlerts"` + SlackChannels int `json:"slackChannels"` + WebHookChannels int `json:"webHookChannels"` + PagerDutyChannels int `json:"pagerDutyChannels"` + OpsGenieChannels int `json:"opsGenieChannels"` + EmailChannels int `json:"emailChannels"` + MSTeamsChannels int `json:"microsoftTeamsChannels"` + MetricsBuilderQueries int `json:"metricsBuilderQueries"` + MetricsClickHouseQueries int `json:"metricsClickHouseQueries"` + MetricsPrometheusQueries int `json:"metricsPrometheusQueries"` + SpanMetricsPrometheusQueries int `json:"spanMetricsPrometheusQueries"` } type SavedViewsInfo struct { diff --git a/pkg/query-service/rules/db.go b/pkg/query-service/rules/db.go index 4d51d462dd..64a35e3eb9 100644 --- a/pkg/query-service/rules/db.go +++ b/pkg/query-service/rules/db.go @@ -5,12 +5,14 @@ import ( "encoding/json" "fmt" "strconv" + "strings" "time" "github.com/jmoiron/sqlx" "go.signoz.io/signoz/pkg/query-service/auth" "go.signoz.io/signoz/pkg/query-service/common" "go.signoz.io/signoz/pkg/query-service/model" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" "go.uber.org/zap" ) @@ -322,6 +324,20 @@ func (r *ruleDB) GetAlertsInfo(ctx context.Context) (*model.AlertsInfo, error) { alertsInfo.LogsBasedAlerts = alertsInfo.LogsBasedAlerts + 1 } else if rule.AlertType == "METRIC_BASED_ALERT" { alertsInfo.MetricBasedAlerts = alertsInfo.MetricBasedAlerts + 1 + if rule.RuleCondition != nil && rule.RuleCondition.CompositeQuery != nil { + if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeBuilder { + alertsInfo.MetricsBuilderQueries = alertsInfo.MetricsBuilderQueries + 1 + } else if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL { + alertsInfo.MetricsClickHouseQueries = alertsInfo.MetricsClickHouseQueries + 1 + } else if rule.RuleCondition.CompositeQuery.QueryType == v3.QueryTypePromQL { + alertsInfo.MetricsPrometheusQueries = alertsInfo.MetricsPrometheusQueries + 1 + for _, query := range rule.RuleCondition.CompositeQuery.PromQueries { + if strings.Contains(query.Query, "signoz_") { + alertsInfo.SpanMetricsPrometheusQueries = alertsInfo.SpanMetricsPrometheusQueries + 1 + } + } + } + } } else if rule.AlertType == "TRACES_BASED_ALERT" { alertsInfo.TracesBasedAlerts = alertsInfo.TracesBasedAlerts + 1 } diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 8cfa7aaec4..2c9dceb910 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -331,6 +331,10 @@ func createTelemetry() { "opsGenieChannels": alertsInfo.OpsGenieChannels, "emailChannels": alertsInfo.EmailChannels, "msteamsChannels": alertsInfo.MSTeamsChannels, + "metricsBuilderQueries": alertsInfo.MetricsBuilderQueries, + "metricsClickHouseQueries": alertsInfo.MetricsClickHouseQueries, + "metricsPrometheusQueries": alertsInfo.MetricsPrometheusQueries, + "spanMetricsPrometheusQueries": alertsInfo.SpanMetricsPrometheusQueries, } // send event only if there are dashboards or alerts or channels if (dashboardsInfo.TotalDashboards > 0 || alertsInfo.TotalAlerts > 0 || len(*channels) > 0 || savedViewsInfo.TotalSavedViews > 0) && apiErr == nil { From a5d58008713c23431fba26200a68ce7d9fc49e1e Mon Sep 17 00:00:00 2001 From: Yunus M Date: Fri, 2 Aug 2024 21:51:09 +0530 Subject: [PATCH 142/281] feat: enable pagination for service listing,key operations,explorer table and dashboard table (#5625) --- .../container/MetricsApplication/TopOperationsTable.tsx | 7 +++++++ frontend/src/container/QueryTable/QueryTable.tsx | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx index bd518d629d..d897c8a205 100644 --- a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx +++ b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx @@ -164,6 +164,12 @@ function TopOperationsTable({ const downloadableData = convertedTracesToDownloadData(data); + const paginationConfig = { + pageSize: 10, + showSizeChanger: false, + hideOnSinglePage: true, + }; + return (
@@ -181,6 +187,7 @@ function TopOperationsTable({ tableLayout="fixed" dataSource={data} rowKey="name" + pagination={paginationConfig} />
); diff --git a/frontend/src/container/QueryTable/QueryTable.tsx b/frontend/src/container/QueryTable/QueryTable.tsx index 2fa1d05f65..ccf8221bfe 100644 --- a/frontend/src/container/QueryTable/QueryTable.tsx +++ b/frontend/src/container/QueryTable/QueryTable.tsx @@ -48,6 +48,12 @@ export function QueryTable({ const tableColumns = modifyColumns ? modifyColumns(newColumns) : newColumns; + const paginationConfig = { + pageSize: 10, + showSizeChanger: false, + hideOnSinglePage: true, + }; + return (
{isDownloadEnabled && ( @@ -64,6 +70,7 @@ export function QueryTable({ tableLayout="fixed" dataSource={newDataSource} scroll={{ x: true }} + pagination={paginationConfig} // eslint-disable-next-line react/jsx-props-no-spreading {...props} /> From 0021b4d784a7ddfcd377f133705a1b025c8dc652 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 10:51:27 +0530 Subject: [PATCH 143/281] chore(deps): bump fast-loops from 1.1.3 to 1.1.4 in /frontend (#5465) Bumps [fast-loops](https://github.com/robinweser/fast-loops) from 1.1.3 to 1.1.4. - [Commits](https://github.com/robinweser/fast-loops/commits) --- updated-dependencies: - dependency-name: fast-loops dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 78c76e6be5..d12754da95 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8724,9 +8724,9 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-loops@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz" - integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g== + version "1.1.4" + resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.4.tgz#61bc77d518c0af5073a638c6d9d5c7683f069ce2" + integrity sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg== fast-shallow-equal@^1.0.0: version "1.0.0" From f9d1494657a2e9a2181d4326a991e2dd842c40a8 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Mon, 5 Aug 2024 16:54:45 +0530 Subject: [PATCH 144/281] feat: added support for units for formula columns in table panel type (#5638) * feat: added support for formula columns units * chore: add unit test cases for query and formula units --- .../ColumnUnitSelector.styles.scss | 4 + .../ColumnUnitSelector/ColumnUnitSelector.tsx | 4 +- .../__tests__/ColumnSelector.test.tsx | 105 ++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/__tests__/ColumnSelector.test.tsx diff --git a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss index 5426d86672..5c824e7d92 100644 --- a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss +++ b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.styles.scss @@ -15,6 +15,10 @@ .y-axis-unit-selector { flex-direction: row !important; align-items: center; + + .heading { + width: 20px; + } } } diff --git a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx index 8ed4c898a7..ea5ef16c2d 100644 --- a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx +++ b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/ColumnUnitSelector.tsx @@ -20,7 +20,9 @@ export function ColumnUnitSelector( function getAggregateColumnsNamesAndLabels(): string[] { if (currentQuery.queryType === EQueryType.QUERY_BUILDER) { - return currentQuery.builder.queryData.map((q) => q.queryName); + const queries = currentQuery.builder.queryData.map((q) => q.queryName); + const formulas = currentQuery.builder.queryFormulas.map((q) => q.queryName); + return [...queries, ...formulas]; } if (currentQuery.queryType === EQueryType.CLICKHOUSE) { return currentQuery.clickhouse_sql.map((q) => q.name); diff --git a/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/__tests__/ColumnSelector.test.tsx b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/__tests__/ColumnSelector.test.tsx new file mode 100644 index 0000000000..03b0f00cc5 --- /dev/null +++ b/frontend/src/container/NewWidget/RightContainer/ColumnUnitSelector/__tests__/ColumnSelector.test.tsx @@ -0,0 +1,105 @@ +import ROUTES from 'constants/routes'; +import { QueryBuilderProvider } from 'providers/QueryBuilder'; +import { useLocation } from 'react-router-dom'; +import { render } from 'tests/test-utils'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; + +import { ColumnUnitSelector } from '../ColumnUnitSelector'; + +const compositeQueryParam = { + queryType: 'builder', + builder: { + queryData: [ + { + dataSource: 'metrics', + queryName: 'A', + aggregateOperator: 'count', + aggregateAttribute: { + key: 'signoz_latency', + dataType: 'float64', + type: 'ExponentialHistogram', + isColumn: true, + isJSON: false, + id: 'signoz_latency--float64--ExponentialHistogram--true', + }, + timeAggregation: '', + spaceAggregation: 'p90', + functions: [], + filters: { + items: [], + op: 'AND', + }, + expression: 'A', + disabled: false, + stepInterval: 60, + having: [], + limit: null, + orderBy: [], + groupBy: [ + { + key: 'service_name', + dataType: 'string', + type: 'tag', + isColumn: false, + isJSON: false, + id: 'service_name--string--tag--false', + }, + ], + legend: '', + reduceTo: 'avg', + }, + ], + queryFormulas: [ + { + queryName: 'F1', + expression: 'A * 10', + disabled: false, + legend: '', + }, + ], + }, + promql: [ + { + name: 'A', + query: '', + legend: '', + disabled: false, + }, + ], + clickhouse_sql: [ + { + name: 'A', + legend: '', + disabled: false, + query: '', + }, + ], + id: '12e1d311-cb47-4b76-af68-65d8e85c9e0d', +}; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn(), + useRouteMatch: jest.fn(), +})); + +jest.mock('hooks/queryBuilder/useGetCompositeQueryParam', () => ({ + useGetCompositeQueryParam: (): Query => compositeQueryParam as Query, +})); + +describe('Column unit selector panel unit test', () => { + it('unit selectors should be rendered for queries and formula', () => { + const mockLocation = { + pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.DASHBOARD_WIDGET}/`, + }; + (useLocation as jest.Mock).mockReturnValue(mockLocation); + const { getByText } = render( + + {}} />, + , + ); + + expect(getByText('F1')).toBeInTheDocument(); + expect(getByText('A')).toBeInTheDocument(); + }); +}); From 61e6316736f22800af5f722a6ec2189546f79e17 Mon Sep 17 00:00:00 2001 From: Yunus M Date: Mon, 5 Aug 2024 16:57:24 +0530 Subject: [PATCH 145/281] feat: add 1 month option in time range (#5639) --- .../src/container/NewWidget/RightContainer/timeItems.ts | 8 +++++++- .../src/container/TopNav/DateTimeSelectionV2/config.ts | 4 ++++ frontend/src/lib/getMinMax.ts | 4 ++++ frontend/src/lib/getStartAndEndTime/index.ts | 4 ++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frontend/src/container/NewWidget/RightContainer/timeItems.ts b/frontend/src/container/NewWidget/RightContainer/timeItems.ts index dd6f8cd552..dc4008d898 100644 --- a/frontend/src/container/NewWidget/RightContainer/timeItems.ts +++ b/frontend/src/container/NewWidget/RightContainer/timeItems.ts @@ -36,6 +36,10 @@ export const timeItems: timePreferance[] = [ name: 'Last 1 week', enum: 'LAST_1_WEEK', }, + { + name: 'Last 1 month', + enum: 'LAST_1_MONTH', + }, ]; export interface timePreferance { @@ -52,7 +56,8 @@ export type timePreferenceType = | LAST_6_HR | LAST_1_DAY | LAST_3_DAYS - | LAST_1_WEEK; + | LAST_1_WEEK + | LAST_1_MONTH; type GLOBAL_TIME = 'GLOBAL_TIME'; type LAST_5_MIN = 'LAST_5_MIN'; @@ -63,5 +68,6 @@ type LAST_6_HR = 'LAST_6_HR'; type LAST_1_DAY = 'LAST_1_DAY'; type LAST_3_DAYS = 'LAST_3_DAYS'; type LAST_1_WEEK = 'LAST_1_WEEK'; +type LAST_1_MONTH = 'LAST_1_MONTH'; export default timeItems; diff --git a/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts b/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts index 19a3e8c431..473107265e 100644 --- a/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts +++ b/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts @@ -19,6 +19,7 @@ type TenDay = '10d'; type OneWeek = '1w'; type TwoWeek = '2w'; type SixWeek = '6w'; +type OneMonth = '1month'; type TwoMonths = '2months'; type Custom = 'custom'; @@ -42,6 +43,7 @@ export type Time = | TwelveHour | TenDay | TwoWeek + | OneMonth | TwoMonths; export type TimeUnit = 'm' | 'h' | 'd' | 'w'; @@ -57,6 +59,7 @@ export const Options: Option[] = [ { value: '1d', label: 'Last 1 day' }, { value: '3d', label: 'Last 3 days' }, { value: '1w', label: 'Last 1 week' }, + { value: '1month', label: 'Last 1 month' }, { value: 'custom', label: 'Custom' }, ]; @@ -95,6 +98,7 @@ export const RelativeDurationOptions: Option[] = [ { value: '1d', label: 'Last 1 day' }, { value: '3d', label: 'Last 3 days' }, { value: '1w', label: 'Last 1 week' }, + { value: '1month', label: 'Last 1 month' }, ]; export const RelativeDurationSuggestionOptions: Option[] = [ diff --git a/frontend/src/lib/getMinMax.ts b/frontend/src/lib/getMinMax.ts index 4a5076b066..d2062f5a4c 100644 --- a/frontend/src/lib/getMinMax.ts +++ b/frontend/src/lib/getMinMax.ts @@ -99,6 +99,10 @@ const GetMinMax = ( // six week = one day * 42 const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 42 }).getTime(); minTime = minTimeAgo; + } else if (interval === '1month') { + // one month = one day * 30 + const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 30 }).getTime(); + minTime = minTimeAgo; } else if (interval === '2months') { // two months = one day * 60 const minTimeAgo = getMinAgo({ minutes: 24 * 60 * 60 }).getTime(); diff --git a/frontend/src/lib/getStartAndEndTime/index.ts b/frontend/src/lib/getStartAndEndTime/index.ts index ee012858d7..43b7d2f6d6 100644 --- a/frontend/src/lib/getStartAndEndTime/index.ts +++ b/frontend/src/lib/getStartAndEndTime/index.ts @@ -56,6 +56,10 @@ const GetStartAndEndTime = ({ return calculateStartAndEndTime(24 * 60 * 7, endString); } + if (type === 'LAST_1_MONTH') { + return calculateStartAndEndTime(24 * 60 * 30, endString); + } + return { start: getMicroSeconds({ time: minTime / 1000000 }), end: getMicroSeconds({ time: maxTime / 1000000 }), From 481bb6e8b8d68b40d5b6706b91bb78b71d59a3c7 Mon Sep 17 00:00:00 2001 From: shivanshu Date: Fri, 26 Jul 2024 11:50:02 +0530 Subject: [PATCH 146/281] feat: add consumer and producer APIs --- pkg/query-service/app/http_handler.go | 217 +++++++++++---- .../messagingQueues/kafka/consumerLag.md | 251 ++++++++++++++++++ .../messagingQueues/kafka/model.go | 7 + .../integrations/messagingQueues/kafka/sql.go | 165 ++++++++++++ .../messagingQueues/kafka/translator.go | 101 +++++++ .../integrations/messagingQueues/readme.md | 10 + pkg/query-service/app/server.go | 1 + 7 files changed, 696 insertions(+), 56 deletions(-) create mode 100644 pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md create mode 100644 pkg/query-service/app/integrations/messagingQueues/kafka/model.go create mode 100644 pkg/query-service/app/integrations/messagingQueues/kafka/sql.go create mode 100644 pkg/query-service/app/integrations/messagingQueues/kafka/translator.go create mode 100644 pkg/query-service/app/integrations/messagingQueues/readme.md diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 4eff84d50c..b74cafabfb 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -43,6 +43,7 @@ import ( "go.uber.org/zap" + mq "go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/kafka" "go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline" "go.signoz.io/signoz/pkg/query-service/dao" am "go.signoz.io/signoz/pkg/query-service/integrations/alertManager" @@ -2246,9 +2247,113 @@ func (aH *APIHandler) WriteJSON(w http.ResponseWriter, r *http.Request, response w.Write(resp) } +// RegisterMessagingQueuesRoutes adds messaging-queues routes +func (aH *APIHandler) RegisterMessagingQueuesRoutes(router *mux.Router, am *AuthMiddleware) { + // SubRouter for kafka + kafkaSubRouter := router.PathPrefix("/api/v1/messaging-queues/kafka/consumer-lag").Subrouter() + + //kafkaSubRouter.HandleFunc("/consumer-lag", am.ViewAccess(aH.QueryRangeV4)).Methods(http.MethodPost) + kafkaSubRouter.HandleFunc("/producer-details", am.ViewAccess(aH.getProducerData)).Methods(http.MethodPost) + kafkaSubRouter.HandleFunc("/consumer-details", am.ViewAccess(aH.getConsumerData)).Methods(http.MethodPost) + + // for other messaging queues, add SubRouters here +} + +func (aH *APIHandler) getProducerData( + w http.ResponseWriter, r *http.Request, +) { + // parse the query params to retrieve the messaging queue struct + messagingQueue, apiErr := ParseMessagingQueueParams(r) + + if apiErr != nil { + zap.L().Error(apiErr.Err.Error()) + RespondError(w, apiErr, nil) + return + } + + queryRangeParams, err := mq.BuildQueryRangeParams(messagingQueue, "producer") + if err != nil { + zap.L().Error(err.Error()) + RespondError(w, apiErr, nil) + return + } + + if err := validateQueryRangeParamsV3(queryRangeParams); err != nil { + zap.L().Error(err.Error()) + RespondError(w, apiErr, nil) + return + } + + var result []*v3.Result + var errQuriesByName map[string]error + + result, errQuriesByName, err = aH.querierV2.QueryRange(r.Context(), queryRangeParams, nil) + if err != nil { + apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err} + RespondError(w, apiErrObj, errQuriesByName) + return + } + result = postprocess.TransformToTableForClickHouseQueries(result) + + resp := v3.QueryRangeResponse{ + Result: result, + } + aH.Respond(w, resp) +} + +func (aH *APIHandler) getConsumerData( + w http.ResponseWriter, r *http.Request, +) { + messagingQueue, apiErr := ParseMessagingQueueParams(r) + + if apiErr != nil { + zap.L().Error(apiErr.Err.Error()) + RespondError(w, apiErr, nil) + return + } + + queryRangeParams, err := mq.BuildQueryRangeParams(messagingQueue, "consumer") + if err != nil { + zap.L().Error(err.Error()) + RespondError(w, apiErr, nil) + return + } + + if err := validateQueryRangeParamsV3(queryRangeParams); err != nil { + zap.L().Error(err.Error()) + RespondError(w, apiErr, nil) + return + } + + var result []*v3.Result + var errQuriesByName map[string]error + + result, errQuriesByName, err = aH.querierV2.QueryRange(r.Context(), queryRangeParams, nil) + if err != nil { + apiErrObj := &model.ApiError{Typ: model.ErrorBadData, Err: err} + RespondError(w, apiErrObj, errQuriesByName) + return + } + result = postprocess.TransformToTableForClickHouseQueries(result) + + resp := v3.QueryRangeResponse{ + Result: result, + } + aH.Respond(w, resp) +} + +// ParseMessagingQueueParams parse for messaging queue params +func ParseMessagingQueueParams(r *http.Request) (*mq.MessagingQueue, *model.ApiError) { + var messagingQueue *mq.MessagingQueue + if err := json.NewDecoder(r.Body).Decode(&messagingQueue); err != nil { + return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("cannot parse the request body: %v", err)} + } + return messagingQueue, nil +} + // Preferences -func (ah *APIHandler) getUserPreference( +func (aH *APIHandler) getUserPreference( w http.ResponseWriter, r *http.Request, ) { preferenceId := mux.Vars(r)["preferenceId"] @@ -2262,10 +2367,10 @@ func (ah *APIHandler) getUserPreference( return } - ah.Respond(w, preference) + aH.Respond(w, preference) } -func (ah *APIHandler) updateUserPreference( +func (aH *APIHandler) updateUserPreference( w http.ResponseWriter, r *http.Request, ) { preferenceId := mux.Vars(r)["preferenceId"] @@ -2284,10 +2389,10 @@ func (ah *APIHandler) updateUserPreference( return } - ah.Respond(w, preference) + aH.Respond(w, preference) } -func (ah *APIHandler) getAllUserPreferences( +func (aH *APIHandler) getAllUserPreferences( w http.ResponseWriter, r *http.Request, ) { user := common.GetUserFromContext(r.Context()) @@ -2299,10 +2404,10 @@ func (ah *APIHandler) getAllUserPreferences( return } - ah.Respond(w, preference) + aH.Respond(w, preference) } -func (ah *APIHandler) getOrgPreference( +func (aH *APIHandler) getOrgPreference( w http.ResponseWriter, r *http.Request, ) { preferenceId := mux.Vars(r)["preferenceId"] @@ -2315,10 +2420,10 @@ func (ah *APIHandler) getOrgPreference( return } - ah.Respond(w, preference) + aH.Respond(w, preference) } -func (ah *APIHandler) updateOrgPreference( +func (aH *APIHandler) updateOrgPreference( w http.ResponseWriter, r *http.Request, ) { preferenceId := mux.Vars(r)["preferenceId"] @@ -2337,10 +2442,10 @@ func (ah *APIHandler) updateOrgPreference( return } - ah.Respond(w, preference) + aH.Respond(w, preference) } -func (ah *APIHandler) getAllOrgPreferences( +func (aH *APIHandler) getAllOrgPreferences( w http.ResponseWriter, r *http.Request, ) { user := common.GetUserFromContext(r.Context()) @@ -2352,36 +2457,36 @@ func (ah *APIHandler) getAllOrgPreferences( return } - ah.Respond(w, preference) + aH.Respond(w, preference) } -// Integrations -func (ah *APIHandler) RegisterIntegrationRoutes(router *mux.Router, am *AuthMiddleware) { +// RegisterIntegrationRoutes Registers all Integrations +func (aH *APIHandler) RegisterIntegrationRoutes(router *mux.Router, am *AuthMiddleware) { subRouter := router.PathPrefix("/api/v1/integrations").Subrouter() subRouter.HandleFunc( - "/install", am.ViewAccess(ah.InstallIntegration), + "/install", am.ViewAccess(aH.InstallIntegration), ).Methods(http.MethodPost) subRouter.HandleFunc( - "/uninstall", am.ViewAccess(ah.UninstallIntegration), + "/uninstall", am.ViewAccess(aH.UninstallIntegration), ).Methods(http.MethodPost) // Used for polling for status in v0 subRouter.HandleFunc( - "/{integrationId}/connection_status", am.ViewAccess(ah.GetIntegrationConnectionStatus), + "/{integrationId}/connection_status", am.ViewAccess(aH.GetIntegrationConnectionStatus), ).Methods(http.MethodGet) subRouter.HandleFunc( - "/{integrationId}", am.ViewAccess(ah.GetIntegration), + "/{integrationId}", am.ViewAccess(aH.GetIntegration), ).Methods(http.MethodGet) subRouter.HandleFunc( - "", am.ViewAccess(ah.ListIntegrations), + "", am.ViewAccess(aH.ListIntegrations), ).Methods(http.MethodGet) } -func (ah *APIHandler) ListIntegrations( +func (aH *APIHandler) ListIntegrations( w http.ResponseWriter, r *http.Request, ) { params := map[string]string{} @@ -2389,21 +2494,21 @@ func (ah *APIHandler) ListIntegrations( params[k] = values[0] } - resp, apiErr := ah.IntegrationsController.ListIntegrations( + resp, apiErr := aH.IntegrationsController.ListIntegrations( r.Context(), params, ) if apiErr != nil { RespondError(w, apiErr, "Failed to fetch integrations") return } - ah.Respond(w, resp) + aH.Respond(w, resp) } -func (ah *APIHandler) GetIntegration( +func (aH *APIHandler) GetIntegration( w http.ResponseWriter, r *http.Request, ) { integrationId := mux.Vars(r)["integrationId"] - integration, apiErr := ah.IntegrationsController.GetIntegration( + integration, apiErr := aH.IntegrationsController.GetIntegration( r.Context(), integrationId, ) if apiErr != nil { @@ -2411,14 +2516,14 @@ func (ah *APIHandler) GetIntegration( return } - ah.Respond(w, integration) + aH.Respond(w, integration) } -func (ah *APIHandler) GetIntegrationConnectionStatus( +func (aH *APIHandler) GetIntegrationConnectionStatus( w http.ResponseWriter, r *http.Request, ) { integrationId := mux.Vars(r)["integrationId"] - isInstalled, apiErr := ah.IntegrationsController.IsIntegrationInstalled( + isInstalled, apiErr := aH.IntegrationsController.IsIntegrationInstalled( r.Context(), integrationId, ) if apiErr != nil { @@ -2428,11 +2533,11 @@ func (ah *APIHandler) GetIntegrationConnectionStatus( // Do not spend resources calculating connection status unless installed. if !isInstalled { - ah.Respond(w, &integrations.IntegrationConnectionStatus{}) + aH.Respond(w, &integrations.IntegrationConnectionStatus{}) return } - connectionTests, apiErr := ah.IntegrationsController.GetIntegrationConnectionTests( + connectionTests, apiErr := aH.IntegrationsController.GetIntegrationConnectionTests( r.Context(), integrationId, ) if apiErr != nil { @@ -2446,7 +2551,7 @@ func (ah *APIHandler) GetIntegrationConnectionStatus( lookbackSeconds = 15 * 60 } - connectionStatus, apiErr := ah.calculateConnectionStatus( + connectionStatus, apiErr := aH.calculateConnectionStatus( r.Context(), connectionTests, lookbackSeconds, ) if apiErr != nil { @@ -2454,10 +2559,10 @@ func (ah *APIHandler) GetIntegrationConnectionStatus( return } - ah.Respond(w, connectionStatus) + aH.Respond(w, connectionStatus) } -func (ah *APIHandler) calculateConnectionStatus( +func (aH *APIHandler) calculateConnectionStatus( ctx context.Context, connectionTests *integrations.IntegrationConnectionTests, lookbackSeconds int64, @@ -2475,7 +2580,7 @@ func (ah *APIHandler) calculateConnectionStatus( go func() { defer wg.Done() - logsConnStatus, apiErr := ah.calculateLogsConnectionStatus( + logsConnStatus, apiErr := aH.calculateLogsConnectionStatus( ctx, connectionTests.Logs, lookbackSeconds, ) @@ -2498,7 +2603,7 @@ func (ah *APIHandler) calculateConnectionStatus( return } - statusForLastReceivedMetric, apiErr := ah.reader.GetLatestReceivedMetric( + statusForLastReceivedMetric, apiErr := aH.reader.GetLatestReceivedMetric( ctx, connectionTests.Metrics, ) @@ -2542,7 +2647,7 @@ func (ah *APIHandler) calculateConnectionStatus( return result, nil } -func (ah *APIHandler) calculateLogsConnectionStatus( +func (aH *APIHandler) calculateLogsConnectionStatus( ctx context.Context, logsConnectionTest *integrations.LogsConnectionTest, lookbackSeconds int64, @@ -2584,7 +2689,7 @@ func (ah *APIHandler) calculateLogsConnectionStatus( }, }, } - queryRes, _, err := ah.querier.QueryRange( + queryRes, _, err := aH.querier.QueryRange( ctx, qrParams, map[string]v3.AttributeKey{}, ) if err != nil { @@ -2621,7 +2726,7 @@ func (ah *APIHandler) calculateLogsConnectionStatus( return nil, nil } -func (ah *APIHandler) InstallIntegration( +func (aH *APIHandler) InstallIntegration( w http.ResponseWriter, r *http.Request, ) { req := integrations.InstallIntegrationRequest{} @@ -2632,7 +2737,7 @@ func (ah *APIHandler) InstallIntegration( return } - integration, apiErr := ah.IntegrationsController.Install( + integration, apiErr := aH.IntegrationsController.Install( r.Context(), &req, ) if apiErr != nil { @@ -2640,10 +2745,10 @@ func (ah *APIHandler) InstallIntegration( return } - ah.Respond(w, integration) + aH.Respond(w, integration) } -func (ah *APIHandler) UninstallIntegration( +func (aH *APIHandler) UninstallIntegration( w http.ResponseWriter, r *http.Request, ) { req := integrations.UninstallIntegrationRequest{} @@ -2654,13 +2759,13 @@ func (ah *APIHandler) UninstallIntegration( return } - apiErr := ah.IntegrationsController.Uninstall(r.Context(), &req) + apiErr := aH.IntegrationsController.Uninstall(r.Context(), &req) if apiErr != nil { RespondError(w, apiErr, nil) return } - ah.Respond(w, map[string]interface{}{}) + aH.Respond(w, map[string]interface{}{}) } // logs @@ -2807,7 +2912,7 @@ func parseAgentConfigVersion(r *http.Request) (int, *model.ApiError) { return int(version64), nil } -func (ah *APIHandler) PreviewLogsPipelinesHandler(w http.ResponseWriter, r *http.Request) { +func (aH *APIHandler) PreviewLogsPipelinesHandler(w http.ResponseWriter, r *http.Request) { req := logparsingpipeline.PipelinesPreviewRequest{} if err := json.NewDecoder(r.Body).Decode(&req); err != nil { @@ -2815,7 +2920,7 @@ func (ah *APIHandler) PreviewLogsPipelinesHandler(w http.ResponseWriter, r *http return } - resultLogs, apiErr := ah.LogsParsingPipelineController.PreviewLogsPipelines( + resultLogs, apiErr := aH.LogsParsingPipelineController.PreviewLogsPipelines( r.Context(), &req, ) @@ -2824,10 +2929,10 @@ func (ah *APIHandler) PreviewLogsPipelinesHandler(w http.ResponseWriter, r *http return } - ah.Respond(w, resultLogs) + aH.Respond(w, resultLogs) } -func (ah *APIHandler) ListLogsPipelinesHandler(w http.ResponseWriter, r *http.Request) { +func (aH *APIHandler) ListLogsPipelinesHandler(w http.ResponseWriter, r *http.Request) { version, err := parseAgentConfigVersion(r) if err != nil { @@ -2839,20 +2944,20 @@ func (ah *APIHandler) ListLogsPipelinesHandler(w http.ResponseWriter, r *http.Re var apierr *model.ApiError if version != -1 { - payload, apierr = ah.listLogsPipelinesByVersion(context.Background(), version) + payload, apierr = aH.listLogsPipelinesByVersion(context.Background(), version) } else { - payload, apierr = ah.listLogsPipelines(context.Background()) + payload, apierr = aH.listLogsPipelines(context.Background()) } if apierr != nil { RespondError(w, apierr, payload) return } - ah.Respond(w, payload) + aH.Respond(w, payload) } // listLogsPipelines lists logs piplines for latest version -func (ah *APIHandler) listLogsPipelines(ctx context.Context) ( +func (aH *APIHandler) listLogsPipelines(ctx context.Context) ( *logparsingpipeline.PipelinesResponse, *model.ApiError, ) { // get lateset agent config @@ -2866,7 +2971,7 @@ func (ah *APIHandler) listLogsPipelines(ctx context.Context) ( latestVersion = lastestConfig.Version } - payload, err := ah.LogsParsingPipelineController.GetPipelinesByVersion(ctx, latestVersion) + payload, err := aH.LogsParsingPipelineController.GetPipelinesByVersion(ctx, latestVersion) if err != nil { return nil, model.WrapApiError(err, "failed to get pipelines") } @@ -2882,10 +2987,10 @@ func (ah *APIHandler) listLogsPipelines(ctx context.Context) ( } // listLogsPipelinesByVersion lists pipelines along with config version history -func (ah *APIHandler) listLogsPipelinesByVersion(ctx context.Context, version int) ( +func (aH *APIHandler) listLogsPipelinesByVersion(ctx context.Context, version int) ( *logparsingpipeline.PipelinesResponse, *model.ApiError, ) { - payload, err := ah.LogsParsingPipelineController.GetPipelinesByVersion(ctx, version) + payload, err := aH.LogsParsingPipelineController.GetPipelinesByVersion(ctx, version) if err != nil { return nil, model.WrapApiError(err, "failed to get pipelines by version") } @@ -2901,7 +3006,7 @@ func (ah *APIHandler) listLogsPipelinesByVersion(ctx context.Context, version in return payload, nil } -func (ah *APIHandler) CreateLogsPipeline(w http.ResponseWriter, r *http.Request) { +func (aH *APIHandler) CreateLogsPipeline(w http.ResponseWriter, r *http.Request) { req := logparsingpipeline.PostablePipelines{} @@ -2924,7 +3029,7 @@ func (ah *APIHandler) CreateLogsPipeline(w http.ResponseWriter, r *http.Request) } } - return ah.LogsParsingPipelineController.ApplyPipelines(ctx, postable) + return aH.LogsParsingPipelineController.ApplyPipelines(ctx, postable) } res, err := createPipeline(r.Context(), req.Pipelines) @@ -2933,7 +3038,7 @@ func (ah *APIHandler) CreateLogsPipeline(w http.ResponseWriter, r *http.Request) return } - ah.Respond(w, res) + aH.Respond(w, res) } func (aH *APIHandler) getSavedViews(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md new file mode 100644 index 0000000000..c3691ecc36 --- /dev/null +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md @@ -0,0 +1,251 @@ +## Consumer Lag feature break down + +### 1) Consumer Lag Graph + + +--- + +### 2) Consumer Group Details + +API endpoint: + +``` +POST /api/v1/messaging-queues/kafka/consumer-lag/consumer-details +``` + +```json +{ + "start": 1720685296000000000, + "end": 1721290096000000000, + "variables": { + "partition": "0", + "topic": "topic1" + } +} +``` + +response in query range format `series` +```json +{ + "status": "success", + "data": { + "resultType": "", + "result": [ + { + "queryName": "producer", + "series": [ + { + "labels": { + "error_rate": "0", + "p99_query.p99": "150.08830908000002", + "rps": "0.00016534391534391533", + "service_name": "producer-svc" + }, + "labelsArray": [ + { + "service_name": "producer-svc" + }, + { + "p99_query.p99": "150.08830908000002" + }, + { + "error_rate": "0" + }, + { + "rps": "0.00016534391534391533" + } + ], + "values": [] + } + ] + } + ] + } +} +``` +response in query range format `table` +```json +{ + "status": "success", + "data": { + "resultType": "", + "result": [ + { + "table": { + "columns": [ + { + "name": "consumer_group", + "queryName": "", + "isValueColumn": false + }, + { + "name": "service_name", + "queryName": "", + "isValueColumn": false + }, + { + "name": "p99", + "queryName": "", + "isValueColumn": false + }, + { + "name": "error_rate", + "queryName": "", + "isValueColumn": false + }, + { + "name": "throughput", + "queryName": "", + "isValueColumn": false + }, + { + "name": "avg_msg_size", + "queryName": "", + "isValueColumn": false + } + ], + "rows": [ + { + "data": { + "avg_msg_size": "0", + "consumer_group": "cg1", + "error_rate": "0", + "p99": "0.2942205100000016", + "service_name": "consumer-svc", + "throughput": "0.00016534391534391533" + } + }, + { + "data": { + "avg_msg_size": "0", + "consumer_group": "cg3", + "error_rate": "0", + "p99": "0.216600410000002", + "service_name": "consumer-svc", + "throughput": "0.00016534391534391533" + } + } + ] + } + } + ] + } +} +``` + + + +### 3) Producer Details + +API endpoint: + +``` +POST /api/v1/messaging-queues/kafka/consumer-lag/consumer-details +``` + +```json +{ + "start": 1720685296000000000, + "end": 1721290096000000000, + "variables": { + "partition": "0", + "topic": "topic1" + } +} +``` + +response in query range format `series` +```json +{ + "status": "success", + "data": { + "resultType": "", + "result": [ + { + "table": { + "columns": [ + { + "name": "service_name", + "queryName": "", + "isValueColumn": false + }, + { + "name": "p99_query.p99", + "queryName": "", + "isValueColumn": false + }, + { + "name": "error_rate", + "queryName": "", + "isValueColumn": false + }, + { + "name": "rps", + "queryName": "", + "isValueColumn": false + } + ], + "rows": [ + { + "data": { + "error_rate": "0", + "p99_query.p99": "150.08830908000002", + "rps": "0.00016534391534391533", + "service_name": "producer-svc" + } + } + ] + } + } + ] + } +} +``` +response in query range format `table` +```json +{ + "status": "success", + "data": { + "resultType": "", + "result": [ + { + "table": { + "columns": [ + { + "name": "service_name", + "queryName": "", + "isValueColumn": false + }, + { + "name": "p99_query.p99", + "queryName": "", + "isValueColumn": false + }, + { + "name": "error_rate", + "queryName": "", + "isValueColumn": false + }, + { + "name": "rps", + "queryName": "", + "isValueColumn": false + } + ], + "rows": [ + { + "data": { + "error_rate": "0", + "p99_query.p99": "150.08830908000002", + "rps": "0.00016534391534391533", + "service_name": "producer-svc" + } + } + ] + } + } + ] + } +} +``` + diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/model.go b/pkg/query-service/app/integrations/messagingQueues/kafka/model.go new file mode 100644 index 0000000000..61a504eaee --- /dev/null +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/model.go @@ -0,0 +1,7 @@ +package kafka + +type MessagingQueue struct { + Start int64 `json:"start"` + End int64 `json:"end"` + Variables map[string]string `json:"variables,omitempty"` +} diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go new file mode 100644 index 0000000000..b22cc3f9aa --- /dev/null +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go @@ -0,0 +1,165 @@ +package kafka + +import ( + "fmt" +) + +func generateConsumerSQL(start, end int64, topic, partition string) string { + query := fmt.Sprintf(` +WITH +-- Sub query for p99 calculation +p99_query AS ( + SELECT + stringTagMap['messaging.kafka.consumer.group'] as consumer_group, + serviceName, + quantile(0.99)(durationNano) / 1000000 as p99 + FROM signoz_traces.signoz_index_v2 + WHERE + timestamp >= '%d' + AND timestamp <= '%d' + AND kind = 5 + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' + GROUP BY consumer_group, serviceName +), + +-- Sub query for RPS calculation +rps_query AS ( + SELECT + stringTagMap['messaging.kafka.consumer.group'] AS consumer_group, + serviceName, + count(*) / ((%d - %d) / 1000000000) AS rps -- Convert nanoseconds to seconds + FROM signoz_traces.signoz_index_v2 + WHERE + timestamp >= '%d' + AND timestamp <= '%d' + AND kind = 5 + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' + GROUP BY consumer_group, serviceName +), + +-- Sub query for error rate calculation +error_rate_query AS ( + SELECT + stringTagMap['messaging.kafka.consumer.group'] AS consumer_group, + serviceName, + count(*) / ((%d - %d) / 1000000000) AS error_rate -- Convert nanoseconds to seconds + FROM signoz_traces.signoz_index_v2 + WHERE + timestamp >= '%d' + AND timestamp <= '%d' + AND statusCode = 2 + AND kind = 5 + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' + GROUP BY consumer_group, serviceName +), + +-- Sub query for average message size calculation +avg_msg_size_query AS ( + SELECT + stringTagMap['messaging.kafka.consumer.group'] AS consumer_group, + serviceName, + avg(numberTagMap['messaging.message.body.size']) AS avg_msg_size + FROM signoz_traces.signoz_index_v2 + WHERE + timestamp >= '%d' + AND timestamp <= '%d' + AND kind = 5 + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' + GROUP BY consumer_group, serviceName +) + +-- Main query to combine all metrics +SELECT + p99_query.consumer_group AS consumer_group, + p99_query.serviceName AS service_name, + p99_query.p99 AS p99, + COALESCE(error_rate_query.error_rate, 0) AS error_rate, + COALESCE(rps_query.rps, 0) AS throughput, + COALESCE(avg_msg_size_query.avg_msg_size, 0) AS avg_msg_size +FROM + p99_query + LEFT JOIN rps_query ON p99_query.consumer_group = rps_query.consumer_group + AND p99_query.serviceName = rps_query.serviceName + LEFT JOIN error_rate_query ON p99_query.consumer_group = error_rate_query.consumer_group + AND p99_query.serviceName = error_rate_query.serviceName + LEFT JOIN avg_msg_size_query ON p99_query.consumer_group = avg_msg_size_query.consumer_group + AND p99_query.serviceName = avg_msg_size_query.serviceName +ORDER BY + p99_query.consumer_group; +`, start, end, topic, partition, end, start, start, end, topic, partition, end, start, start, end, topic, partition, end, start, topic, partition) + return query +} + +func generateProducerSQL(start, end int64, topic, partition string) string { + query := fmt.Sprintf(` + +-- producer +WITH +-- Subquery for p99 calculation +p99_query AS ( + SELECT + serviceName, + quantile(0.99)(durationNano) / 1000000 as p99 + FROM signoz_traces.signoz_index_v2 + WHERE + timestamp >= '%d' + AND timestamp <= '%d' + AND kind = 4 + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' + GROUP BY serviceName +), + +-- Subquery for RPS calculation +rps_query AS ( + SELECT + serviceName, + count(*) / ((%d - %d) / 1000000000) as rps -- Convert nanoseconds to seconds + FROM signoz_traces.signoz_index_v2 + WHERE + timestamp >= '%d' + AND timestamp <= '%d' + AND kind = 4 + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' + GROUP BY serviceName +), + +-- Subquery for error rate calculation +error_rate_query AS ( + SELECT + serviceName, + count(*) / ((%d - %d) / 1000000000) as error_rate -- Convert nanoseconds to seconds + FROM signoz_traces.signoz_index_v2 + WHERE + timestamp >= '%d' + AND timestamp <= '%d' + AND statusCode = 2 + AND kind = 4 + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' + GROUP BY serviceName +) + +-- Main query to combine all metrics +SELECT + p99_query.serviceName AS service_name, + p99_query.p99, + COALESCE(error_rate_query.error_rate, 0) AS error_rate, + COALESCE(rps_query.rps, 0) AS rps +FROM + p99_query + LEFT JOIN + rps_query ON p99_query.serviceName = rps_query.serviceName + LEFT JOIN + error_rate_query ON p99_query.serviceName = error_rate_query.serviceName +ORDER BY + p99_query.serviceName; + +`, start, end, topic, partition, end, start, start, end, topic, partition, end, start, start, end, topic, partition) + return query +} diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go new file mode 100644 index 0000000000..c8e4abf9fa --- /dev/null +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go @@ -0,0 +1,101 @@ +package kafka + +import ( + "fmt" + + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" +) + +var defaultStepInterval int64 = 60 + +func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) (*v3.QueryRangeParamsV3, error) { + + var cq *v3.CompositeQuery + if queryContext == "producer" { + chq, err := buildProducerClickHouseQuery(messagingQueue) + if err != nil { + return nil, err + } + cq, err = buildCompositeQueryProducer(chq) + + if err != nil { + return nil, err + } + } else if queryContext == "consumer" { + chq, err := buildConsumerClickHouseQuery(messagingQueue) + if err != nil { + return nil, err + } + cq, err = buildCompositeQueryConsumer(chq) + if err != nil { + return nil, err + } + } + + queryRangeParams := &v3.QueryRangeParamsV3{ + Start: messagingQueue.Start, + End: messagingQueue.End, + Step: defaultStepInterval, + CompositeQuery: cq, + Version: "v4", + FormatForWeb: true, + } + + return queryRangeParams, nil +} + +func buildProducerClickHouseQuery(messagingQueue *MessagingQueue) (*v3.ClickHouseQuery, error) { + start := messagingQueue.Start + end := messagingQueue.End + topic, ok := messagingQueue.Variables["topic"] + if !ok { + return nil, fmt.Errorf("invalid type for Topic") + } + + partition, ok := messagingQueue.Variables["partition"] + + if !ok { + return nil, fmt.Errorf("invalid type for Partition") + } + query := generateProducerSQL(start, end, topic, partition) + + return &v3.ClickHouseQuery{ + Query: query, + }, nil +} + +func buildConsumerClickHouseQuery(messagingQueue *MessagingQueue) (*v3.ClickHouseQuery, error) { + start := messagingQueue.Start + end := messagingQueue.End + topic, ok := messagingQueue.Variables["topic"] + if !ok { + return nil, fmt.Errorf("invalid type for Topic") + } + + partition, ok := messagingQueue.Variables["partition"] + + if !ok { + return nil, fmt.Errorf("invalid type for Partition") + } + query := generateConsumerSQL(start, end, topic, partition) + + return &v3.ClickHouseQuery{ + Query: query, + }, nil +} + +func buildCompositeQueryProducer(chq *v3.ClickHouseQuery) (*v3.CompositeQuery, error) { + return &v3.CompositeQuery{ + QueryType: v3.QueryTypeClickHouseSQL, + ClickHouseQueries: map[string]*v3.ClickHouseQuery{"producer": chq}, + PanelType: v3.PanelTypeTable, + }, nil +} + +func buildCompositeQueryConsumer(chq *v3.ClickHouseQuery) (*v3.CompositeQuery, error) { + return &v3.CompositeQuery{ + QueryType: v3.QueryTypeClickHouseSQL, + ClickHouseQueries: map[string]*v3.ClickHouseQuery{"consumer": chq}, + PanelType: v3.PanelTypeTable, + }, nil +} diff --git a/pkg/query-service/app/integrations/messagingQueues/readme.md b/pkg/query-service/app/integrations/messagingQueues/readme.md new file mode 100644 index 0000000000..5b9ae94c8b --- /dev/null +++ b/pkg/query-service/app/integrations/messagingQueues/readme.md @@ -0,0 +1,10 @@ +## Integreation: Messaging Queue + +This package contains the `api`, and `translation` logic to support messaging queue features. + +Currently supported queues: +1) Kafka + +For detailed setup, checkout our public docs for configuring: +1) Trace collection form Clients (Producer and Consumer) +2) Metrics collection from Kafka Brokers, Producers and Consumers diff --git a/pkg/query-service/app/server.go b/pkg/query-service/app/server.go index 5120cd0039..99fe166a70 100644 --- a/pkg/query-service/app/server.go +++ b/pkg/query-service/app/server.go @@ -295,6 +295,7 @@ func (s *Server) createPublicServer(api *APIHandler) (*http.Server, error) { api.RegisterIntegrationRoutes(r, am) api.RegisterQueryRangeV3Routes(r, am) api.RegisterQueryRangeV4Routes(r, am) + api.RegisterMessagingQueuesRoutes(r, am) c := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, From 5c3ce146fa2468e914c08666cd3c6723489e3a2e Mon Sep 17 00:00:00 2001 From: shivanshu Date: Fri, 26 Jul 2024 13:02:45 +0530 Subject: [PATCH 147/281] chore: add queue type --- .../integrations/messagingQueues/kafka/model.go | 6 ++++-- .../app/integrations/messagingQueues/kafka/sql.go | 15 +++++++++++---- .../messagingQueues/kafka/translator.go | 15 +++++++++------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/model.go b/pkg/query-service/app/integrations/messagingQueues/kafka/model.go index 61a504eaee..b24734cf48 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/model.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/model.go @@ -1,7 +1,9 @@ package kafka +const kafkaQueue = "kafka" + type MessagingQueue struct { - Start int64 `json:"start"` - End int64 `json:"end"` + Start int64 `json:"start"` + End int64 `json:"end"` Variables map[string]string `json:"variables,omitempty"` } diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go index b22cc3f9aa..552fdc5d7a 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go @@ -4,7 +4,7 @@ import ( "fmt" ) -func generateConsumerSQL(start, end int64, topic, partition string) string { +func generateConsumerSQL(start, end int64, topic, partition, queueType string) string { query := fmt.Sprintf(` WITH -- Sub query for p99 calculation @@ -18,6 +18,7 @@ p99_query AS ( timestamp >= '%d' AND timestamp <= '%d' AND kind = 5 + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY consumer_group, serviceName @@ -34,6 +35,7 @@ rps_query AS ( timestamp >= '%d' AND timestamp <= '%d' AND kind = 5 + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY consumer_group, serviceName @@ -51,6 +53,7 @@ error_rate_query AS ( AND timestamp <= '%d' AND statusCode = 2 AND kind = 5 + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY consumer_group, serviceName @@ -67,6 +70,7 @@ avg_msg_size_query AS ( timestamp >= '%d' AND timestamp <= '%d' AND kind = 5 + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY consumer_group, serviceName @@ -90,11 +94,11 @@ FROM AND p99_query.serviceName = avg_msg_size_query.serviceName ORDER BY p99_query.consumer_group; -`, start, end, topic, partition, end, start, start, end, topic, partition, end, start, start, end, topic, partition, end, start, topic, partition) +`, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition, end, start, queueType, topic, partition) return query } -func generateProducerSQL(start, end int64, topic, partition string) string { +func generateProducerSQL(start, end int64, topic, partition, queueType string) string { query := fmt.Sprintf(` -- producer @@ -109,6 +113,7 @@ p99_query AS ( timestamp >= '%d' AND timestamp <= '%d' AND kind = 4 + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY serviceName @@ -124,6 +129,7 @@ rps_query AS ( timestamp >= '%d' AND timestamp <= '%d' AND kind = 4 + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY serviceName @@ -140,6 +146,7 @@ error_rate_query AS ( AND timestamp <= '%d' AND statusCode = 2 AND kind = 4 + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY serviceName @@ -160,6 +167,6 @@ FROM ORDER BY p99_query.serviceName; -`, start, end, topic, partition, end, start, start, end, topic, partition, end, start, start, end, topic, partition) +`, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition) return query } diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go index c8e4abf9fa..5cb67d7bf0 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go @@ -10,9 +10,12 @@ var defaultStepInterval int64 = 60 func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) (*v3.QueryRangeParamsV3, error) { + // ToDo: propagate this through APIs when there are different handlers + queueType := kafkaQueue + var cq *v3.CompositeQuery if queryContext == "producer" { - chq, err := buildProducerClickHouseQuery(messagingQueue) + chq, err := buildProducerClickHouseQuery(messagingQueue, queueType) if err != nil { return nil, err } @@ -22,7 +25,7 @@ func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) return nil, err } } else if queryContext == "consumer" { - chq, err := buildConsumerClickHouseQuery(messagingQueue) + chq, err := buildConsumerClickHouseQuery(messagingQueue, queueType) if err != nil { return nil, err } @@ -44,7 +47,7 @@ func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) return queryRangeParams, nil } -func buildProducerClickHouseQuery(messagingQueue *MessagingQueue) (*v3.ClickHouseQuery, error) { +func buildProducerClickHouseQuery(messagingQueue *MessagingQueue, queueType string) (*v3.ClickHouseQuery, error) { start := messagingQueue.Start end := messagingQueue.End topic, ok := messagingQueue.Variables["topic"] @@ -57,14 +60,14 @@ func buildProducerClickHouseQuery(messagingQueue *MessagingQueue) (*v3.ClickHous if !ok { return nil, fmt.Errorf("invalid type for Partition") } - query := generateProducerSQL(start, end, topic, partition) + query := generateProducerSQL(start, end, topic, partition, queueType) return &v3.ClickHouseQuery{ Query: query, }, nil } -func buildConsumerClickHouseQuery(messagingQueue *MessagingQueue) (*v3.ClickHouseQuery, error) { +func buildConsumerClickHouseQuery(messagingQueue *MessagingQueue, queueType string) (*v3.ClickHouseQuery, error) { start := messagingQueue.Start end := messagingQueue.End topic, ok := messagingQueue.Variables["topic"] @@ -77,7 +80,7 @@ func buildConsumerClickHouseQuery(messagingQueue *MessagingQueue) (*v3.ClickHous if !ok { return nil, fmt.Errorf("invalid type for Partition") } - query := generateConsumerSQL(start, end, topic, partition) + query := generateConsumerSQL(start, end, topic, partition, queueType) return &v3.ClickHouseQuery{ Query: query, From 063c9adba60d33a4522c517d89b590cd8f2c80fa Mon Sep 17 00:00:00 2001 From: shivanshu Date: Fri, 26 Jul 2024 15:23:31 +0530 Subject: [PATCH 148/281] chore: pr-reviews --- pkg/query-service/app/http_handler.go | 12 ++-- .../messagingQueues/kafka/translator.go | 64 +++++-------------- 2 files changed, 21 insertions(+), 55 deletions(-) diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index b74cafabfb..729dbf77aa 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -2263,7 +2263,7 @@ func (aH *APIHandler) getProducerData( w http.ResponseWriter, r *http.Request, ) { // parse the query params to retrieve the messaging queue struct - messagingQueue, apiErr := ParseMessagingQueueParams(r) + messagingQueue, apiErr := ParseMessagingQueueBody(r) if apiErr != nil { zap.L().Error(apiErr.Err.Error()) @@ -2304,7 +2304,7 @@ func (aH *APIHandler) getProducerData( func (aH *APIHandler) getConsumerData( w http.ResponseWriter, r *http.Request, ) { - messagingQueue, apiErr := ParseMessagingQueueParams(r) + messagingQueue, apiErr := ParseMessagingQueueBody(r) if apiErr != nil { zap.L().Error(apiErr.Err.Error()) @@ -2342,10 +2342,10 @@ func (aH *APIHandler) getConsumerData( aH.Respond(w, resp) } -// ParseMessagingQueueParams parse for messaging queue params -func ParseMessagingQueueParams(r *http.Request) (*mq.MessagingQueue, *model.ApiError) { - var messagingQueue *mq.MessagingQueue - if err := json.NewDecoder(r.Body).Decode(&messagingQueue); err != nil { +// ParseMessagingQueueBody parse for messaging queue params +func ParseMessagingQueueBody(r *http.Request) (*mq.MessagingQueue, *model.ApiError) { + messagingQueue := new(mq.MessagingQueue) + if err := json.NewDecoder(r.Body).Decode(messagingQueue); err != nil { return nil, &model.ApiError{Typ: model.ErrorBadData, Err: fmt.Errorf("cannot parse the request body: %v", err)} } return messagingQueue, nil diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go index 5cb67d7bf0..99760d7fbb 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go @@ -14,27 +14,15 @@ func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) queueType := kafkaQueue var cq *v3.CompositeQuery - if queryContext == "producer" { - chq, err := buildProducerClickHouseQuery(messagingQueue, queueType) - if err != nil { - return nil, err - } - cq, err = buildCompositeQueryProducer(chq) - - if err != nil { - return nil, err - } - } else if queryContext == "consumer" { - chq, err := buildConsumerClickHouseQuery(messagingQueue, queueType) - if err != nil { - return nil, err - } - cq, err = buildCompositeQueryConsumer(chq) - if err != nil { - return nil, err - } + + chq, err := buildClickHouseQuery(messagingQueue, queueType, queryContext) + + if err != nil { + return nil, err } + cq, err = buildCompositeQuery(chq, queryContext) + queryRangeParams := &v3.QueryRangeParamsV3{ Start: messagingQueue.Start, End: messagingQueue.End, @@ -47,7 +35,7 @@ func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string) return queryRangeParams, nil } -func buildProducerClickHouseQuery(messagingQueue *MessagingQueue, queueType string) (*v3.ClickHouseQuery, error) { +func buildClickHouseQuery(messagingQueue *MessagingQueue, queueType string, queryContext string) (*v3.ClickHouseQuery, error) { start := messagingQueue.Start end := messagingQueue.End topic, ok := messagingQueue.Variables["topic"] @@ -60,45 +48,23 @@ func buildProducerClickHouseQuery(messagingQueue *MessagingQueue, queueType stri if !ok { return nil, fmt.Errorf("invalid type for Partition") } - query := generateProducerSQL(start, end, topic, partition, queueType) - return &v3.ClickHouseQuery{ - Query: query, - }, nil -} - -func buildConsumerClickHouseQuery(messagingQueue *MessagingQueue, queueType string) (*v3.ClickHouseQuery, error) { - start := messagingQueue.Start - end := messagingQueue.End - topic, ok := messagingQueue.Variables["topic"] - if !ok { - return nil, fmt.Errorf("invalid type for Topic") - } - - partition, ok := messagingQueue.Variables["partition"] - - if !ok { - return nil, fmt.Errorf("invalid type for Partition") + var query string + if queryContext == "producer" { + query = generateProducerSQL(start, end, topic, partition, queueType) + } else if queryContext == "consumer" { + query = generateConsumerSQL(start, end, topic, partition, queueType) } - query := generateConsumerSQL(start, end, topic, partition, queueType) return &v3.ClickHouseQuery{ Query: query, }, nil } -func buildCompositeQueryProducer(chq *v3.ClickHouseQuery) (*v3.CompositeQuery, error) { - return &v3.CompositeQuery{ - QueryType: v3.QueryTypeClickHouseSQL, - ClickHouseQueries: map[string]*v3.ClickHouseQuery{"producer": chq}, - PanelType: v3.PanelTypeTable, - }, nil -} - -func buildCompositeQueryConsumer(chq *v3.ClickHouseQuery) (*v3.CompositeQuery, error) { +func buildCompositeQuery(chq *v3.ClickHouseQuery, queryContext string) (*v3.CompositeQuery, error) { return &v3.CompositeQuery{ QueryType: v3.QueryTypeClickHouseSQL, - ClickHouseQueries: map[string]*v3.ClickHouseQuery{"consumer": chq}, + ClickHouseQueries: map[string]*v3.ClickHouseQuery{queryContext: chq}, PanelType: v3.PanelTypeTable, }, nil } From 3ff0aa4b4bc271d5fa83d433e8d7afadbe433a1a Mon Sep 17 00:00:00 2001 From: shivanshu Date: Wed, 31 Jul 2024 17:55:13 +0530 Subject: [PATCH 149/281] chore: consumer group filtering --- .../messagingQueues/kafka/consumerLag.md | 70 +++---------------- .../integrations/messagingQueues/kafka/sql.go | 37 +++++----- .../messagingQueues/kafka/translator.go | 8 ++- 3 files changed, 32 insertions(+), 83 deletions(-) diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md index c3691ecc36..5e12a87e76 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md @@ -19,52 +19,14 @@ POST /api/v1/messaging-queues/kafka/consumer-lag/consumer-details "end": 1721290096000000000, "variables": { "partition": "0", - "topic": "topic1" + "topic": "topic1", + "consumer_group": "cg1" } } ``` response in query range format `series` ```json -{ - "status": "success", - "data": { - "resultType": "", - "result": [ - { - "queryName": "producer", - "series": [ - { - "labels": { - "error_rate": "0", - "p99_query.p99": "150.08830908000002", - "rps": "0.00016534391534391533", - "service_name": "producer-svc" - }, - "labelsArray": [ - { - "service_name": "producer-svc" - }, - { - "p99_query.p99": "150.08830908000002" - }, - { - "error_rate": "0" - }, - { - "rps": "0.00016534391534391533" - } - ], - "values": [] - } - ] - } - ] - } -} -``` -response in query range format `table` -```json { "status": "success", "data": { @@ -73,11 +35,6 @@ response in query range format `table` { "table": { "columns": [ - { - "name": "consumer_group", - "queryName": "", - "isValueColumn": false - }, { "name": "service_name", "queryName": "", @@ -108,22 +65,11 @@ response in query range format `table` { "data": { "avg_msg_size": "0", - "consumer_group": "cg1", "error_rate": "0", "p99": "0.2942205100000016", "service_name": "consumer-svc", "throughput": "0.00016534391534391533" } - }, - { - "data": { - "avg_msg_size": "0", - "consumer_group": "cg3", - "error_rate": "0", - "p99": "0.216600410000002", - "service_name": "consumer-svc", - "throughput": "0.00016534391534391533" - } } ] } @@ -145,12 +91,12 @@ POST /api/v1/messaging-queues/kafka/consumer-lag/consumer-details ```json { - "start": 1720685296000000000, - "end": 1721290096000000000, - "variables": { - "partition": "0", - "topic": "topic1" - } + "start": 1720685296000000000, + "end": 1721290096000000000, + "variables": { + "partition": "0", + "topic": "topic1" + } } ``` diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go index 552fdc5d7a..f479ea5ac9 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go @@ -4,13 +4,12 @@ import ( "fmt" ) -func generateConsumerSQL(start, end int64, topic, partition, queueType string) string { +func generateConsumerSQL(start, end int64, topic, partition, consumerGroup, queueType string) string { query := fmt.Sprintf(` WITH -- Sub query for p99 calculation p99_query AS ( SELECT - stringTagMap['messaging.kafka.consumer.group'] as consumer_group, serviceName, quantile(0.99)(durationNano) / 1000000 as p99 FROM signoz_traces.signoz_index_v2 @@ -21,13 +20,13 @@ p99_query AS ( AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' - GROUP BY consumer_group, serviceName + AND stringTagMap['messaging.kafka.consumer.group'] = '%s' + GROUP BY serviceName ), -- Sub query for RPS calculation rps_query AS ( SELECT - stringTagMap['messaging.kafka.consumer.group'] AS consumer_group, serviceName, count(*) / ((%d - %d) / 1000000000) AS rps -- Convert nanoseconds to seconds FROM signoz_traces.signoz_index_v2 @@ -38,13 +37,13 @@ rps_query AS ( AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' - GROUP BY consumer_group, serviceName + AND stringTagMap['messaging.kafka.consumer.group'] = '%s' + GROUP BY serviceName ), -- Sub query for error rate calculation error_rate_query AS ( SELECT - stringTagMap['messaging.kafka.consumer.group'] AS consumer_group, serviceName, count(*) / ((%d - %d) / 1000000000) AS error_rate -- Convert nanoseconds to seconds FROM signoz_traces.signoz_index_v2 @@ -56,13 +55,13 @@ error_rate_query AS ( AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' - GROUP BY consumer_group, serviceName + AND stringTagMap['messaging.kafka.consumer.group'] = '%s' + GROUP BY serviceName ), -- Sub query for average message size calculation avg_msg_size_query AS ( SELECT - stringTagMap['messaging.kafka.consumer.group'] AS consumer_group, serviceName, avg(numberTagMap['messaging.message.body.size']) AS avg_msg_size FROM signoz_traces.signoz_index_v2 @@ -73,12 +72,12 @@ avg_msg_size_query AS ( AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' - GROUP BY consumer_group, serviceName + AND stringTagMap['messaging.kafka.consumer.group'] = '%s' + GROUP BY serviceName ) -- Main query to combine all metrics SELECT - p99_query.consumer_group AS consumer_group, p99_query.serviceName AS service_name, p99_query.p99 AS p99, COALESCE(error_rate_query.error_rate, 0) AS error_rate, @@ -86,15 +85,14 @@ SELECT COALESCE(avg_msg_size_query.avg_msg_size, 0) AS avg_msg_size FROM p99_query - LEFT JOIN rps_query ON p99_query.consumer_group = rps_query.consumer_group - AND p99_query.serviceName = rps_query.serviceName - LEFT JOIN error_rate_query ON p99_query.consumer_group = error_rate_query.consumer_group - AND p99_query.serviceName = error_rate_query.serviceName - LEFT JOIN avg_msg_size_query ON p99_query.consumer_group = avg_msg_size_query.consumer_group - AND p99_query.serviceName = avg_msg_size_query.serviceName + LEFT JOIN rps_query ON p99_query.serviceName = rps_query.serviceName + LEFT JOIN error_rate_query ON p99_query.serviceName = error_rate_query.serviceName + LEFT JOIN avg_msg_size_query ON p99_query.serviceName = avg_msg_size_query.serviceName ORDER BY - p99_query.consumer_group; -`, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition, end, start, queueType, topic, partition) + p99_query.serviceName; +`, start, end, queueType, topic, partition, consumerGroup, end, start, start, end, queueType, topic, + partition, consumerGroup, end, start, start, end, queueType, topic, partition, + consumerGroup, end, start, queueType, topic, partition, consumerGroup) return query } @@ -167,6 +165,7 @@ FROM ORDER BY p99_query.serviceName; -`, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, partition) +`, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, + partition, end, start, start, end, queueType, topic, partition) return query } diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go index 99760d7fbb..98414ebf0f 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/translator.go @@ -44,16 +44,20 @@ func buildClickHouseQuery(messagingQueue *MessagingQueue, queueType string, quer } partition, ok := messagingQueue.Variables["partition"] - if !ok { return nil, fmt.Errorf("invalid type for Partition") } + consumerGroup, ok := messagingQueue.Variables["consumer_group"] + if !ok { + return nil, fmt.Errorf("invalid type for consumer group") + } + var query string if queryContext == "producer" { query = generateProducerSQL(start, end, topic, partition, queueType) } else if queryContext == "consumer" { - query = generateConsumerSQL(start, end, topic, partition, queueType) + query = generateConsumerSQL(start, end, topic, partition, consumerGroup, queueType) } return &v3.ClickHouseQuery{ From c957c0f75728b65fb8ab32d7778802bf1a042df6 Mon Sep 17 00:00:00 2001 From: shivanshu Date: Mon, 5 Aug 2024 18:14:40 +0530 Subject: [PATCH 150/281] chore: addressing review comments --- pkg/query-service/app/http_handler.go | 1 - .../messagingQueues/kafka/consumerLag.md | 2 +- .../integrations/messagingQueues/kafka/sql.go | 167 ++++-------------- 3 files changed, 37 insertions(+), 133 deletions(-) diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 729dbf77aa..29cb807686 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -2252,7 +2252,6 @@ func (aH *APIHandler) RegisterMessagingQueuesRoutes(router *mux.Router, am *Auth // SubRouter for kafka kafkaSubRouter := router.PathPrefix("/api/v1/messaging-queues/kafka/consumer-lag").Subrouter() - //kafkaSubRouter.HandleFunc("/consumer-lag", am.ViewAccess(aH.QueryRangeV4)).Methods(http.MethodPost) kafkaSubRouter.HandleFunc("/producer-details", am.ViewAccess(aH.getProducerData)).Methods(http.MethodPost) kafkaSubRouter.HandleFunc("/consumer-details", am.ViewAccess(aH.getConsumerData)).Methods(http.MethodPost) diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md index 5e12a87e76..c34bc7ad64 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/consumerLag.md @@ -86,7 +86,7 @@ response in query range format `series` API endpoint: ``` -POST /api/v1/messaging-queues/kafka/consumer-lag/consumer-details +POST /api/v1/messaging-queues/kafka/consumer-lag/producer-details ``` ```json diff --git a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go index f479ea5ac9..e06e35efde 100644 --- a/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go +++ b/pkg/query-service/app/integrations/messagingQueues/kafka/sql.go @@ -5,167 +5,72 @@ import ( ) func generateConsumerSQL(start, end int64, topic, partition, consumerGroup, queueType string) string { + timeRange := (end - start) / 1000000000 query := fmt.Sprintf(` -WITH --- Sub query for p99 calculation -p99_query AS ( +WITH consumer_query AS ( SELECT serviceName, - quantile(0.99)(durationNano) / 1000000 as p99 - FROM signoz_traces.signoz_index_v2 - WHERE - timestamp >= '%d' - AND timestamp <= '%d' - AND kind = 5 - AND msgSystem = '%s' - AND stringTagMap['messaging.destination.name'] = '%s' - AND stringTagMap['messaging.destination.partition.id'] = '%s' - AND stringTagMap['messaging.kafka.consumer.group'] = '%s' - GROUP BY serviceName -), - --- Sub query for RPS calculation -rps_query AS ( - SELECT - serviceName, - count(*) / ((%d - %d) / 1000000000) AS rps -- Convert nanoseconds to seconds - FROM signoz_traces.signoz_index_v2 + quantile(0.99)(durationNano) / 1000000 AS p99, + COUNT(*) AS total_requests, + SUM(CASE WHEN statusCode = 2 THEN 1 ELSE 0 END) AS error_count, + avg(CASE WHEN has(numberTagMap, 'messaging.message.body.size') THEN numberTagMap['messaging.message.body.size'] ELSE NULL END) AS avg_msg_size + FROM signoz_traces.distributed_signoz_index_v2 WHERE timestamp >= '%d' AND timestamp <= '%d' AND kind = 5 - AND msgSystem = '%s' + AND msgSystem = '%s' AND stringTagMap['messaging.destination.name'] = '%s' AND stringTagMap['messaging.destination.partition.id'] = '%s' - AND stringTagMap['messaging.kafka.consumer.group'] = '%s' - GROUP BY serviceName -), - --- Sub query for error rate calculation -error_rate_query AS ( - SELECT - serviceName, - count(*) / ((%d - %d) / 1000000000) AS error_rate -- Convert nanoseconds to seconds - FROM signoz_traces.signoz_index_v2 - WHERE - timestamp >= '%d' - AND timestamp <= '%d' - AND statusCode = 2 - AND kind = 5 - AND msgSystem = '%s' - AND stringTagMap['messaging.destination.name'] = '%s' - AND stringTagMap['messaging.destination.partition.id'] = '%s' - AND stringTagMap['messaging.kafka.consumer.group'] = '%s' - GROUP BY serviceName -), - --- Sub query for average message size calculation -avg_msg_size_query AS ( - SELECT - serviceName, - avg(numberTagMap['messaging.message.body.size']) AS avg_msg_size - FROM signoz_traces.signoz_index_v2 - WHERE - timestamp >= '%d' - AND timestamp <= '%d' - AND kind = 5 - AND msgSystem = '%s' - AND stringTagMap['messaging.destination.name'] = '%s' - AND stringTagMap['messaging.destination.partition.id'] = '%s' - AND stringTagMap['messaging.kafka.consumer.group'] = '%s' + AND stringTagMap['messaging.kafka.consumer.group'] = '%s' GROUP BY serviceName ) --- Main query to combine all metrics +-- Main query to select all metrics SELECT - p99_query.serviceName AS service_name, - p99_query.p99 AS p99, - COALESCE(error_rate_query.error_rate, 0) AS error_rate, - COALESCE(rps_query.rps, 0) AS throughput, - COALESCE(avg_msg_size_query.avg_msg_size, 0) AS avg_msg_size + serviceName AS service_name, + p99, + COALESCE((error_count * 100.0) / total_requests, 0) AS error_rate, + COALESCE(total_requests / %d, 0) AS throughput, -- Convert nanoseconds to seconds + COALESCE(avg_msg_size, 0) AS avg_msg_size FROM - p99_query - LEFT JOIN rps_query ON p99_query.serviceName = rps_query.serviceName - LEFT JOIN error_rate_query ON p99_query.serviceName = error_rate_query.serviceName - LEFT JOIN avg_msg_size_query ON p99_query.serviceName = avg_msg_size_query.serviceName + consumer_query ORDER BY - p99_query.serviceName; -`, start, end, queueType, topic, partition, consumerGroup, end, start, start, end, queueType, topic, - partition, consumerGroup, end, start, start, end, queueType, topic, partition, - consumerGroup, end, start, queueType, topic, partition, consumerGroup) + serviceName; +`, start, end, queueType, topic, partition, consumerGroup, timeRange) return query } func generateProducerSQL(start, end int64, topic, partition, queueType string) string { + timeRange := (end - start) / 1000000000 query := fmt.Sprintf(` - --- producer -WITH --- Subquery for p99 calculation -p99_query AS ( - SELECT - serviceName, - quantile(0.99)(durationNano) / 1000000 as p99 - FROM signoz_traces.signoz_index_v2 - WHERE - timestamp >= '%d' - AND timestamp <= '%d' - AND kind = 4 - AND msgSystem = '%s' - AND stringTagMap['messaging.destination.name'] = '%s' - AND stringTagMap['messaging.destination.partition.id'] = '%s' - GROUP BY serviceName -), - --- Subquery for RPS calculation -rps_query AS ( +WITH producer_query AS ( SELECT serviceName, - count(*) / ((%d - %d) / 1000000000) as rps -- Convert nanoseconds to seconds - FROM signoz_traces.signoz_index_v2 + quantile(0.99)(durationNano) / 1000000 AS p99, + count(*) AS total_count, + SUM(CASE WHEN statusCode = 2 THEN 1 ELSE 0 END) AS error_count + FROM signoz_traces.distributed_signoz_index_v2 WHERE timestamp >= '%d' - AND timestamp <= '%d' - AND kind = 4 - AND msgSystem = '%s' - AND stringTagMap['messaging.destination.name'] = '%s' - AND stringTagMap['messaging.destination.partition.id'] = '%s' - GROUP BY serviceName -), - --- Subquery for error rate calculation -error_rate_query AS ( - SELECT - serviceName, - count(*) / ((%d - %d) / 1000000000) as error_rate -- Convert nanoseconds to seconds - FROM signoz_traces.signoz_index_v2 - WHERE - timestamp >= '%d' - AND timestamp <= '%d' - AND statusCode = 2 - AND kind = 4 - AND msgSystem = '%s' - AND stringTagMap['messaging.destination.name'] = '%s' - AND stringTagMap['messaging.destination.partition.id'] = '%s' + AND timestamp <= '%d' + AND kind = 4 + AND msgSystem = '%s' + AND stringTagMap['messaging.destination.name'] = '%s' + AND stringTagMap['messaging.destination.partition.id'] = '%s' GROUP BY serviceName ) --- Main query to combine all metrics SELECT - p99_query.serviceName AS service_name, - p99_query.p99, - COALESCE(error_rate_query.error_rate, 0) AS error_rate, - COALESCE(rps_query.rps, 0) AS rps + serviceName AS service_name, + p99, + COALESCE((error_count * 100.0) / total_count, 0) AS error_percentage, + COALESCE(total_count / %d, 0) AS rps -- Convert nanoseconds to seconds FROM - p99_query - LEFT JOIN - rps_query ON p99_query.serviceName = rps_query.serviceName - LEFT JOIN - error_rate_query ON p99_query.serviceName = error_rate_query.serviceName + producer_query ORDER BY - p99_query.serviceName; + serviceName; -`, start, end, queueType, topic, partition, end, start, start, end, queueType, topic, - partition, end, start, start, end, queueType, topic, partition) +`, start, end, queueType, topic, partition, timeRange) return query } From 4a4c9f26a2903bb1aed20aeb8b8a17aa0a7f0556 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Mon, 5 Aug 2024 20:53:52 +0530 Subject: [PATCH 151/281] chore: add Reduce To for pie chart (#5629) --- frontend/src/container/NewWidget/utils.ts | 3 +++ .../QueryBuilder/components/Query/Query.tsx | 26 ++++++++++--------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/frontend/src/container/NewWidget/utils.ts b/frontend/src/container/NewWidget/utils.ts index 015830434d..cc5b7b3bce 100644 --- a/frontend/src/container/NewWidget/utils.ts +++ b/frontend/src/container/NewWidget/utils.ts @@ -268,6 +268,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'aggregateOperator', 'aggregateAttribute', 'groupBy', + 'reduceTo', 'limit', 'having', 'orderBy', @@ -286,6 +287,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'aggregateOperator', 'aggregateAttribute', 'groupBy', + 'reduceTo', 'limit', 'having', 'orderBy', @@ -305,6 +307,7 @@ export const panelTypeDataSourceFormValuesMap: Record< 'aggregateOperator', 'aggregateAttribute', 'groupBy', + 'reduceTo', 'limit', 'having', 'orderBy', diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.tsx b/frontend/src/container/QueryBuilder/components/Query/Query.tsx index 78892759a4..619c7fd716 100644 --- a/frontend/src/container/QueryBuilder/components/Query/Query.tsx +++ b/frontend/src/container/QueryBuilder/components/Query/Query.tsx @@ -544,19 +544,21 @@ export const Query = memo(function Query({ )} - {isVersionV4 && isMetricsDataSource && panelType === PANEL_TYPES.TABLE && ( - - - - - + {isVersionV4 && + isMetricsDataSource && + (panelType === PANEL_TYPES.TABLE || panelType === PANEL_TYPES.PIE) && ( + + + + + - - - - - - )} + + + + + + )} )} From 80a7b9d16dd4da05731a22d18ba638afb41d21b2 Mon Sep 17 00:00:00 2001 From: rahulkeswani101 Date: Tue, 6 Aug 2024 13:33:51 +0530 Subject: [PATCH 152/281] feat: added link for dashboard name (#5544) * feat: added link for dashboard name * refactor: added getLink function to get the link of dashboard details page * refactor: changed the color for dashboard name * refactor: updated the classname for dashboard name * fix: update css tokens and light mode design --------- Co-authored-by: vikrantgupta25 --- .../container/ListOfDashboard/DashboardList.styles.scss | 8 ++++++++ frontend/src/container/ListOfDashboard/DashboardsList.tsx | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/ListOfDashboard/DashboardList.styles.scss b/frontend/src/container/ListOfDashboard/DashboardList.styles.scss index 11dac46fde..221d59dcf4 100644 --- a/frontend/src/container/ListOfDashboard/DashboardList.styles.scss +++ b/frontend/src/container/ListOfDashboard/DashboardList.styles.scss @@ -43,6 +43,10 @@ background: var(--bg-ink-400); cursor: pointer; + .dashboard-title { + color: var(--bg-vanilla-100); + } + .title-with-action { display: flex; justify-content: space-between; @@ -1048,6 +1052,10 @@ border: 1px solid var(--bg-vanilla-200); background: var(--bg-vanilla-100); + .dashboard-title { + color: var(--bg-slate-300); + } + .title-with-action { .dashboard-title { .ant-typography { diff --git a/frontend/src/container/ListOfDashboard/DashboardsList.tsx b/frontend/src/container/ListOfDashboard/DashboardsList.tsx index 17b6fe0863..d9994b3776 100644 --- a/frontend/src/container/ListOfDashboard/DashboardsList.tsx +++ b/frontend/src/container/ListOfDashboard/DashboardsList.tsx @@ -66,7 +66,7 @@ import { } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import { generatePath } from 'react-router-dom'; +import { generatePath, Link } from 'react-router-dom'; import { useCopyToClipboard } from 'react-use'; import { AppState } from 'store/reducers'; import { Dashboard } from 'types/api/dashboard/getAll'; @@ -455,7 +455,9 @@ function DashboardsList(): JSX.Element { alt="dashboard-image" /> - {dashboard.name} + + {dashboard.name} +
From a6848f6abdfeb00bc54de9e53dc976d83a6efe1b Mon Sep 17 00:00:00 2001 From: rahulkeswani101 Date: Tue, 6 Aug 2024 19:09:49 +0530 Subject: [PATCH 153/281] fix: added card to show message for deleted alert id (#5565) * fix: added card to show message for deleted alert id * refactor: added new constants for handling error message when alert is deleted * refactor: added error response to error message field * refactor: removed console statement * refactor: renamed the variables --- frontend/src/api/ErrorResponseHandler.ts | 7 ++-- .../src/pages/EditRules/EditRules.styles.scss | 32 +++++++++++++++ frontend/src/pages/EditRules/constants.ts | 6 +++ frontend/src/pages/EditRules/index.tsx | 41 ++++++++++++++++--- 4 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 frontend/src/pages/EditRules/EditRules.styles.scss create mode 100644 frontend/src/pages/EditRules/constants.ts diff --git a/frontend/src/api/ErrorResponseHandler.ts b/frontend/src/api/ErrorResponseHandler.ts index be2dd5e31a..6d972ec90f 100644 --- a/frontend/src/api/ErrorResponseHandler.ts +++ b/frontend/src/api/ErrorResponseHandler.ts @@ -9,9 +9,9 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse { // making the error status code as standard Error Status Code const statusCode = response.status as ErrorStatusCode; - if (statusCode >= 400 && statusCode < 500) { - const { data } = response as AxiosResponse; + const { data } = response as AxiosResponse; + if (statusCode >= 400 && statusCode < 500) { if (statusCode === 404) { return { statusCode, @@ -34,12 +34,11 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse { body: JSON.stringify((response.data as any).data), }; } - return { statusCode, payload: null, error: 'Something went wrong', - message: null, + message: data?.error, }; } if (request) { diff --git a/frontend/src/pages/EditRules/EditRules.styles.scss b/frontend/src/pages/EditRules/EditRules.styles.scss new file mode 100644 index 0000000000..412cddd1ad --- /dev/null +++ b/frontend/src/pages/EditRules/EditRules.styles.scss @@ -0,0 +1,32 @@ +.edit-rules-container { + display: flex; + justify-content: center; + align-items: center; + margin-top: 5rem; +} + + +.edit-rules-card { + width: 20rem; + padding: 1rem; +} + +.content { + font-style: normal; + font-weight: 300; + font-size: 18px; + line-height: 20px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + margin: 0; +} + +.btn-container { + display: flex; + justify-content: center; + align-items: center; + margin-top: 2rem; +} + diff --git a/frontend/src/pages/EditRules/constants.ts b/frontend/src/pages/EditRules/constants.ts new file mode 100644 index 0000000000..a35eebe680 --- /dev/null +++ b/frontend/src/pages/EditRules/constants.ts @@ -0,0 +1,6 @@ +export const returnToAlertsPage = 'Return to Alerts Page'; + +export const errorMessageReceivedFromBackend = 'sql: no rows in result set'; + +export const improvedErrorMessage = + 'The Alert that you are trying to access does not exist.'; diff --git a/frontend/src/pages/EditRules/index.tsx b/frontend/src/pages/EditRules/index.tsx index af884f5692..cccfc6aee2 100644 --- a/frontend/src/pages/EditRules/index.tsx +++ b/frontend/src/pages/EditRules/index.tsx @@ -1,19 +1,27 @@ +import './EditRules.styles.scss'; + +import { Button, Card } from 'antd'; import get from 'api/alerts/get'; import Spinner from 'components/Spinner'; +import { QueryParams } from 'constants/query'; import ROUTES from 'constants/routes'; import EditRulesContainer from 'container/EditRules'; import { useNotifications } from 'hooks/useNotifications'; +import useUrlQuery from 'hooks/useUrlQuery'; import history from 'lib/history'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; -import { useLocation } from 'react-router-dom'; + +import { + errorMessageReceivedFromBackend, + improvedErrorMessage, + returnToAlertsPage, +} from './constants'; function EditRules(): JSX.Element { - const { search } = useLocation(); - const params = new URLSearchParams(search); + const params = useUrlQuery(); const ruleId = params.get('ruleId'); - const { t } = useTranslation('common'); const isValidRuleId = ruleId !== null && String(ruleId).length !== 0; @@ -31,6 +39,14 @@ function EditRules(): JSX.Element { const { notifications } = useNotifications(); + const clickHandler = (): void => { + params.delete(QueryParams.compositeQuery); + params.delete(QueryParams.panelTypes); + params.delete(QueryParams.ruleId); + params.delete(QueryParams.relativeTime); + history.push(`${ROUTES.LIST_ALL_ALERT}?${params.toString()}`); + }; + useEffect(() => { if (!isValidRuleId) { notifications.error({ @@ -45,7 +61,22 @@ function EditRules(): JSX.Element { ruleId == null || (data?.payload?.data === undefined && !isLoading) ) { - return
{data?.error || t('something_went_wrong')}
; + return ( +
+ +

+ {data?.message === errorMessageReceivedFromBackend + ? improvedErrorMessage + : data?.error || t('something_went_wrong')} +

+
+ +
+
+
+ ); } if (isLoading || isRefetching || !data?.payload) { From b819a90c804c97c0b0006b167aa328705e4d17ba Mon Sep 17 00:00:00 2001 From: rahulkeswani101 Date: Tue, 6 Aug 2024 19:18:48 +0530 Subject: [PATCH 154/281] feat: added links to integrations page in onboarding section (#5606) * feat: added links to integrations page in onboarding section * feat: removed box shadow for button * refactor: added routes object to navigate to integrations page * feat: added new styles for data source name --- .../Steps/DataSource/DataSource.styles.scss | 34 +++++++++++++++++++ .../Steps/DataSource/DataSource.tsx | 25 ++++++++++++-- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.styles.scss b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.styles.scss index a3d8468559..2bbd18bab2 100644 --- a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.styles.scss +++ b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.styles.scss @@ -41,3 +41,37 @@ div[class*='-setup-instructions-container'] { .service-name-container { width: 100%; } + +.intgeration-page-container { + background-color: var(--bg-ink-400); + border-color: var(--bg-slate-500); +} + +.intgeration-page-container-text { + color: var(--bg-vanilla-400); +} + +.navigate-integrations-page-btn { + display: flex; + align-items: center; + color: var(--bg-vanilla-100); + background-color: var(--bg-slate-300); + box-shadow: none; + border: none; +} + +.dataSourceName { + color: var(--bg-vanilla-100); + font-weight: 600; + cursor: pointer; + + display: flex; + align-items: center; + gap: 8px; +} + +.lightMode { + .dataSourceName { + color: var(--bg-slate-500); + } +} \ No newline at end of file diff --git a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx index 5a06cdef94..138aac775c 100644 --- a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx +++ b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx @@ -6,6 +6,7 @@ import { LoadingOutlined } from '@ant-design/icons'; import { Button, Card, Form, Input, Select, Space, Typography } from 'antd'; import logEvent from 'api/common/logEvent'; import cx from 'classnames'; +import ROUTES from 'constants/routes'; import { useOnboardingContext } from 'container/OnboardingContainer/context/OnboardingContext'; import { useCases } from 'container/OnboardingContainer/OnboardingContainer'; import { @@ -14,9 +15,10 @@ import { hasFrameworks, } from 'container/OnboardingContainer/utils/dataSourceUtils'; import { useNotifications } from 'hooks/useNotifications'; -import { Check } from 'lucide-react'; +import { Blocks, Check } from 'lucide-react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useHistory } from 'react-router-dom'; import { popupContainer } from 'utils/selectPopupContainer'; export interface DataSourceType { @@ -29,6 +31,7 @@ export interface DataSourceType { export default function DataSource(): JSX.Element { const [form] = Form.useForm(); const { t } = useTranslation(['common']); + const history = useHistory(); const { serviceName, @@ -127,6 +130,10 @@ export default function DataSource(): JSX.Element { } }; + const goToIntegrationsPage = (): void => { + history.push(ROUTES.INTEGRATIONS); + }; + return (
@@ -156,7 +163,7 @@ export default function DataSource(): JSX.Element {
- + {dataSource.name}
@@ -214,6 +221,20 @@ export default function DataSource(): JSX.Element { )} +
+ + Not able to find datasources you are looking for, check our Integrations + page which allows more sources of sending data + + +
+
Cannot find what you’re looking for? Request a data source From fd6f0574f526136127a205ceb880509bbbc75c52 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Tue, 6 Aug 2024 20:24:06 +0530 Subject: [PATCH 155/281] fix: make timeshift work with cache (#5646) --- pkg/query-service/app/querier/querier_test.go | 28 +++++++++---------- pkg/query-service/app/querier/v2/helper.go | 2 +- .../app/querier/v2/querier_test.go | 24 ++++++++-------- .../app/queryBuilder/query_builder.go | 8 ++++++ 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/pkg/query-service/app/querier/querier_test.go b/pkg/query-service/app/querier/querier_test.go index 5160c564da..962ca3832a 100644 --- a/pkg/query-service/app/querier/querier_test.go +++ b/pkg/query-service/app/querier/querier_test.go @@ -758,8 +758,8 @@ func TestQueryRangeTimeShift(t *testing.T) { func TestQueryRangeTimeShiftWithCache(t *testing.T) { params := []*v3.QueryRangeParamsV3{ { - Start: 1675115596722 + 60*60*1000 - 86400*1000, //30, 4:23 - End: 1675115596722 + 120*60*1000 - 86400*1000, //30, 5:23 + Start: 1675115596722 + 60*60*1000 - 86400*1000, //30th Jan, 4:23 + End: 1675115596722 + 120*60*1000 - 86400*1000, //30th Jan, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ QueryType: v3.QueryTypeBuilder, @@ -785,8 +785,8 @@ func TestQueryRangeTimeShiftWithCache(t *testing.T) { }, }, { - Start: 1675115596722, //31, 3:23 - End: 1675115596722 + 120*60*1000, //31, 5:23 + Start: 1675115596722, //31st Jan, 3:23 + End: 1675115596722 + 120*60*1000, //31st Jan, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ QueryType: v3.QueryTypeBuilder, @@ -824,8 +824,8 @@ func TestQueryRangeTimeShiftWithCache(t *testing.T) { { Labels: map[string]string{}, Points: []v3.Point{ - {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, - {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, // 30th Jan, 4:23 + {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, // 30th Jan, 6:23 }, }, }, @@ -835,7 +835,7 @@ func TestQueryRangeTimeShiftWithCache(t *testing.T) { // logs queries are generates in ns expectedTimeRangeInQueryString := []string{ fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722+60*60*1000-86400*1000)*1000000, (1675115596722+120*60*1000-86400*1000)*1000000), - fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+60*60*1000)-86400*1000-1)*1000000), + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+120*60*1000)-86400*1000)*1000000), } for i, param := range params { @@ -856,8 +856,8 @@ func TestQueryRangeTimeShiftWithCache(t *testing.T) { func TestQueryRangeTimeShiftWithLimitAndCache(t *testing.T) { params := []*v3.QueryRangeParamsV3{ { - Start: 1675115596722 + 60*60*1000 - 86400*1000, //30, 4:23 - End: 1675115596722 + 120*60*1000 - 86400*1000, //30, 5:23 + Start: 1675115596722 + 60*60*1000 - 86400*1000, //30th Jan, 4:23 + End: 1675115596722 + 120*60*1000 - 86400*1000, //30th Jan, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ QueryType: v3.QueryTypeBuilder, @@ -884,8 +884,8 @@ func TestQueryRangeTimeShiftWithLimitAndCache(t *testing.T) { }, }, { - Start: 1675115596722, //31, 3:23 - End: 1675115596722 + 120*60*1000, //31, 5:23 + Start: 1675115596722, //31st Jan, 3:23 + End: 1675115596722 + 120*60*1000, //31st Jan, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ QueryType: v3.QueryTypeBuilder, @@ -924,8 +924,8 @@ func TestQueryRangeTimeShiftWithLimitAndCache(t *testing.T) { { Labels: map[string]string{}, Points: []v3.Point{ - {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, - {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, // 30th Jan, 4:23 + {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, // 30th Jan, 6:23 }, }, }, @@ -935,7 +935,7 @@ func TestQueryRangeTimeShiftWithLimitAndCache(t *testing.T) { // logs queries are generates in ns expectedTimeRangeInQueryString := []string{ fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722+60*60*1000-86400*1000)*1000000, (1675115596722+120*60*1000-86400*1000)*1000000), - fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+60*60*1000)-86400*1000-1)*1000000), + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+120*60*1000)-86400*1000)*1000000), } for i, param := range params { diff --git a/pkg/query-service/app/querier/v2/helper.go b/pkg/query-service/app/querier/v2/helper.go index 9df9965b5c..9ee90fb913 100644 --- a/pkg/query-service/app/querier/v2/helper.go +++ b/pkg/query-service/app/querier/v2/helper.go @@ -306,7 +306,7 @@ func (q *querier) runBuilderQuery( } // response doesn't need everything - filterCachedPoints(mergedSeries, params.Start, params.End) + filterCachedPoints(mergedSeries, start, end) ch <- channelResult{ Err: nil, diff --git a/pkg/query-service/app/querier/v2/querier_test.go b/pkg/query-service/app/querier/v2/querier_test.go index d29785b310..174f9ccd17 100644 --- a/pkg/query-service/app/querier/v2/querier_test.go +++ b/pkg/query-service/app/querier/v2/querier_test.go @@ -766,7 +766,7 @@ func TestV2QueryRangeTimeShift(t *testing.T) { func TestV2QueryRangeTimeShiftWithCache(t *testing.T) { params := []*v3.QueryRangeParamsV3{ { - Start: 1675115596722 + 60*60*1000 - 86400*1000, //30, 4:23 + Start: 1675115596722 + 60*60*1000 - 86400*1000, //30th Jan, 4:23 End: 1675115596722 + 120*60*1000 - 86400*1000, //30, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ @@ -793,8 +793,8 @@ func TestV2QueryRangeTimeShiftWithCache(t *testing.T) { }, }, { - Start: 1675115596722, //31, 3:23 - End: 1675115596722 + 120*60*1000, //31, 5:23 + Start: 1675115596722, //31st Jan, 3:23 + End: 1675115596722 + 120*60*1000, //31st Jan, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ QueryType: v3.QueryTypeBuilder, @@ -832,8 +832,8 @@ func TestV2QueryRangeTimeShiftWithCache(t *testing.T) { { Labels: map[string]string{}, Points: []v3.Point{ - {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, - {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, // 30th Jan, 4:23 + {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, // 30th Jan, 6:23 }, }, }, @@ -843,7 +843,7 @@ func TestV2QueryRangeTimeShiftWithCache(t *testing.T) { // logs queries are generates in ns expectedTimeRangeInQueryString := []string{ fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722+60*60*1000-86400*1000)*1000000, (1675115596722+120*60*1000-86400*1000)*1000000), - fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+60*60*1000)-86400*1000-1)*1000000), + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+120*60*1000)-86400*1000)*1000000), } for i, param := range params { @@ -864,7 +864,7 @@ func TestV2QueryRangeTimeShiftWithCache(t *testing.T) { func TestV2QueryRangeTimeShiftWithLimitAndCache(t *testing.T) { params := []*v3.QueryRangeParamsV3{ { - Start: 1675115596722 + 60*60*1000 - 86400*1000, //30, 4:23 + Start: 1675115596722 + 60*60*1000 - 86400*1000, //30th Jan, 4:23 End: 1675115596722 + 120*60*1000 - 86400*1000, //30, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ @@ -892,8 +892,8 @@ func TestV2QueryRangeTimeShiftWithLimitAndCache(t *testing.T) { }, }, { - Start: 1675115596722, //31, 3:23 - End: 1675115596722 + 120*60*1000, //31, 5:23 + Start: 1675115596722, //31st Jan, 3:23 + End: 1675115596722 + 120*60*1000, //31st Jan, 5:23 Step: 5 * time.Minute.Milliseconds(), CompositeQuery: &v3.CompositeQuery{ QueryType: v3.QueryTypeBuilder, @@ -932,8 +932,8 @@ func TestV2QueryRangeTimeShiftWithLimitAndCache(t *testing.T) { { Labels: map[string]string{}, Points: []v3.Point{ - {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, - {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, + {Timestamp: 1675115596722 + 60*60*1000 - 86400*1000, Value: 1}, // 30th Jan, 4:23 + {Timestamp: 1675115596722 + 120*60*1000 - 86400*1000 + 60*60*1000, Value: 2}, // 30th Jan, 6:23 }, }, }, @@ -943,7 +943,7 @@ func TestV2QueryRangeTimeShiftWithLimitAndCache(t *testing.T) { // logs queries are generates in ns expectedTimeRangeInQueryString := []string{ fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722+60*60*1000-86400*1000)*1000000, (1675115596722+120*60*1000-86400*1000)*1000000), - fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+60*60*1000)-86400*1000-1)*1000000), + fmt.Sprintf("timestamp >= %d AND timestamp <= %d", (1675115596722-86400*1000)*1000000, ((1675115596722+120*60*1000)-86400*1000)*1000000), } for i, param := range params { diff --git a/pkg/query-service/app/queryBuilder/query_builder.go b/pkg/query-service/app/queryBuilder/query_builder.go index a3a957c985..2237c10f83 100644 --- a/pkg/query-service/app/queryBuilder/query_builder.go +++ b/pkg/query-service/app/queryBuilder/query_builder.go @@ -346,6 +346,10 @@ func (c *cacheKeyGenerator) GenerateKeys(params *v3.QueryRangeParamsV3) map[stri parts = append(parts, fmt.Sprintf("aggregate=%s", query.AggregateOperator)) parts = append(parts, fmt.Sprintf("limit=%d", query.Limit)) + if query.ShiftBy != 0 { + parts = append(parts, fmt.Sprintf("shiftBy=%d", query.ShiftBy)) + } + if query.AggregateAttribute.Key != "" { parts = append(parts, fmt.Sprintf("aggregateAttribute=%s", query.AggregateAttribute.CacheKey())) } @@ -387,6 +391,10 @@ func (c *cacheKeyGenerator) GenerateKeys(params *v3.QueryRangeParamsV3) map[stri parts = append(parts, fmt.Sprintf("timeAggregation=%s", query.TimeAggregation)) parts = append(parts, fmt.Sprintf("spaceAggregation=%s", query.SpaceAggregation)) + if query.ShiftBy != 0 { + parts = append(parts, fmt.Sprintf("shiftBy=%d", query.ShiftBy)) + } + if query.AggregateAttribute.Key != "" { parts = append(parts, fmt.Sprintf("aggregateAttribute=%s", query.AggregateAttribute.CacheKey())) } From ae325ec1cacb47c64f447f879d975730ab29e263 Mon Sep 17 00:00:00 2001 From: Vishal Sharma Date: Thu, 8 Aug 2024 08:32:11 +0530 Subject: [PATCH 156/281] chore: handle traceID search 404 performance issue (#5654) By setting max and min timestamp filter same as current timestamp when traceIDs are not found --- pkg/query-service/app/clickhouseReader/reader.go | 5 +++-- pkg/query-service/app/traces/v3/utils.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 6bfa1839ef..e9123d1ff6 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -5016,9 +5016,10 @@ func (r *ClickHouseReader) GetMinAndMaxTimestampForTraceID(ctx context.Context, return 0, 0, err } + // return current time if traceID not found if minTime.IsZero() || maxTime.IsZero() { - zap.L().Debug("minTime or maxTime is zero") - return 0, 0, nil + zap.L().Debug("minTime or maxTime is zero, traceID not found") + return time.Now().UnixNano(), time.Now().UnixNano(), nil } zap.L().Debug("GetMinAndMaxTimestampForTraceID", zap.Any("minTime", minTime), zap.Any("maxTime", maxTime)) diff --git a/pkg/query-service/app/traces/v3/utils.go b/pkg/query-service/app/traces/v3/utils.go index 624458f919..cbd0940a16 100644 --- a/pkg/query-service/app/traces/v3/utils.go +++ b/pkg/query-service/app/traces/v3/utils.go @@ -127,7 +127,7 @@ func AddTimestampFilters(minTime int64, maxTime int64, params *v3.QueryRangePara if compositeQuery == nil { return } - // Build queries for each builder query + // Build queries for each builder query and apply timestamp filter only if TraceID is present for queryName, query := range compositeQuery.BuilderQueries { if query.Expression != queryName && query.DataSource != v3.DataSourceTraces { continue From eb146491f279a34714aa5d814d11de5eb347bcbd Mon Sep 17 00:00:00 2001 From: Raj Kamal Singh <1133322+raj-k-singh@users.noreply.github.com> Date: Thu, 8 Aug 2024 09:27:41 +0530 Subject: [PATCH 157/281] Feat: QS: query builder suggestions api v0 (#5634) * chore: stash initial work with API signature * chore: put together setup for integration testing filter suggestions * feat: filter suggestions: suggest attribs using existing autocomplete logic * chore: filter suggestions test: add expectation for example queries * feat: filter suggestions: default suggestions when data yet to be received * feat: finish plumbing basic example queries * chore: add test for filter suggestions with an existing query * feat: filter suggestions: don't suggest attribs already included in existing filter * chore: generate example queries by including existing filter first * chore: upgrade ClickHouse-go-mock * chore: some cleanup of reader.GetQBFilterSuggestionsForLogs * chore: some cleanup of filter suggestion tests * chore: some cleanup to http handler and request parsing logic for filter suggestions * chore: remove expectation that attrib suggestions won't contain attribs already used in filter --- go.mod | 8 +- go.sum | 8 + .../app/clickhouseReader/reader.go | 123 ++++++++ pkg/query-service/app/http_handler.go | 26 ++ pkg/query-service/app/parser.go | 45 +++ pkg/query-service/constants/constants.go | 2 + pkg/query-service/interfaces/interface.go | 4 + pkg/query-service/model/v3/v3.go | 12 + .../integration/filter_suggestions_test.go | 279 ++++++++++++++++++ .../integration/logparsingpipeline_test.go | 4 +- .../integration/signoz_integrations_test.go | 31 +- .../tests/integration/test_utils.go | 33 ++- 12 files changed, 541 insertions(+), 34 deletions(-) create mode 100644 pkg/query-service/tests/integration/filter_suggestions_test.go diff --git a/go.mod b/go.mod index b62ebab95f..f2dadeede1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module go.signoz.io/signoz go 1.21.3 require ( - github.com/ClickHouse/clickhouse-go/v2 v2.20.0 + github.com/ClickHouse/clickhouse-go/v2 v2.23.2 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd github.com/SigNoz/signoz-otel-collector v0.102.2 @@ -46,7 +46,7 @@ require ( github.com/sethvargo/go-password v0.2.0 github.com/smartystreets/goconvey v1.8.1 github.com/soheilhy/cmux v0.1.5 - github.com/srikanthccv/ClickHouse-go-mock v0.7.0 + github.com/srikanthccv/ClickHouse-go-mock v0.8.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.102.1 go.opentelemetry.io/collector/confmap v0.102.1 @@ -83,7 +83,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect - github.com/ClickHouse/ch-go v0.61.3 // indirect + github.com/ClickHouse/ch-go v0.61.5 // indirect github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/aws/aws-sdk-go v1.53.16 // indirect @@ -156,7 +156,7 @@ require ( github.com/segmentio/backo-go v1.0.1 // indirect github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/shopspring/decimal v1.3.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smarty/assertions v1.15.0 // indirect github.com/spf13/cobra v1.8.0 // indirect diff --git a/go.sum b/go.sum index 6638d7b40d..e3df44618e 100644 --- a/go.sum +++ b/go.sum @@ -50,8 +50,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.61.3 h1:MmBwUhXrAOBZK7n/sWBzq6FdIQ01cuF2SaaO8KlDRzI= github.com/ClickHouse/ch-go v0.61.3/go.mod h1:1PqXjMz/7S1ZUaKvwPA3i35W2bz2mAMFeCi6DIXgGwQ= +github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= +github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= github.com/ClickHouse/clickhouse-go/v2 v2.20.0 h1:bvlLQ31XJfl7MxIqAq2l1G6JhHYzqEXdvfpMeU6bkKc= github.com/ClickHouse/clickhouse-go/v2 v2.20.0/go.mod h1:VQfyA+tCwCRw2G7ogfY8V0fq/r0yJWzy8UDrjiP/Lbs= +github.com/ClickHouse/clickhouse-go/v2 v2.23.2 h1:+DAKPMnxLS7pduQZsrJc8OhdLS2L9MfDEJ2TS+hpYDM= +github.com/ClickHouse/clickhouse-go/v2 v2.23.2/go.mod h1:aNap51J1OM3yxQJRgM+AlP/MPkGBCL8A74uQThoQhR0= github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU= github.com/Code-Hex/go-generics-cache v1.5.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= @@ -693,6 +697,8 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= 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.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= @@ -718,6 +724,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/srikanthccv/ClickHouse-go-mock v0.7.0 h1:XhRMX2663xkDGq3DYavw8m75O94s9u76hOIjo9QBl8c= github.com/srikanthccv/ClickHouse-go-mock v0.7.0/go.mod h1:IJZ/eL1h4cOy/Jo3PzNKXSPmqRus15BC2MbduYPpA/g= +github.com/srikanthccv/ClickHouse-go-mock v0.8.0 h1:DeeM8XLbTFl6sjYPPwazPEXx7kmRV8TgPFVkt1SqT0Y= +github.com/srikanthccv/ClickHouse-go-mock v0.8.0/go.mod h1:pgJm+apjvi7FHxEdgw1Bt4MRbUYpVxyhKQ/59Wkig24= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index e9123d1ff6..0c8ba834fe 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -13,6 +13,7 @@ import ( "os" "reflect" "regexp" + "slices" "sort" "strconv" "strings" @@ -4357,6 +4358,128 @@ func (r *ClickHouseReader) GetLogAttributeValues(ctx context.Context, req *v3.Fi } +func (r *ClickHouseReader) GetQBFilterSuggestionsForLogs( + ctx context.Context, + req *v3.QBFilterSuggestionsRequest, +) (*v3.QBFilterSuggestionsResponse, *model.ApiError) { + suggestions := v3.QBFilterSuggestionsResponse{ + AttributeKeys: []v3.AttributeKey{}, + ExampleQueries: []v3.FilterSet{}, + } + + // Use existing autocomplete logic for generating attribute suggestions + attribKeysResp, err := r.GetLogAttributeKeys( + ctx, &v3.FilterAttributeKeyRequest{ + SearchText: req.SearchText, + DataSource: v3.DataSourceLogs, + Limit: req.Limit, + }) + if err != nil { + return nil, model.InternalError(fmt.Errorf("couldn't get attribute keys: %w", err)) + } + + suggestions.AttributeKeys = attribKeysResp.AttributeKeys + + // Rank suggested attributes + slices.SortFunc(suggestions.AttributeKeys, func(a v3.AttributeKey, b v3.AttributeKey) int { + + // Higher score => higher rank + attribKeyScore := func(a v3.AttributeKey) int { + + // Scoring criteria is expected to get more sophisticated in follow up changes + if a.Type == v3.AttributeKeyTypeResource { + return 2 + } + + if a.Type == v3.AttributeKeyTypeTag { + return 1 + } + + return 0 + } + + // To sort in descending order of score the return value must be negative when a > b + return attribKeyScore(b) - attribKeyScore(a) + }) + + // Put together suggested example queries. + + newExampleQuery := func() v3.FilterSet { + // Include existing filter in example query if specified. + if req.ExistingFilter != nil { + return *req.ExistingFilter + } + + return v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{}, + } + } + + // Suggest example query for top suggested attribute using existing + // autocomplete logic for recommending attrib values + // + // Example queries for multiple top attributes using a batch version of + // GetLogAttributeValues is expected to come in a follow up change + if len(suggestions.AttributeKeys) > 0 { + topAttrib := suggestions.AttributeKeys[0] + + resp, err := r.GetLogAttributeValues(ctx, &v3.FilterAttributeValueRequest{ + DataSource: v3.DataSourceLogs, + FilterAttributeKey: topAttrib.Key, + FilterAttributeKeyDataType: topAttrib.DataType, + TagType: v3.TagType(topAttrib.Type), + Limit: 1, + }) + + if err != nil { + // Do not fail the entire request if only example query generation fails + zap.L().Error("could not find attribute values for creating example query", zap.Error(err)) + + } else { + addExampleQuerySuggestion := func(value any) { + exampleQuery := newExampleQuery() + + exampleQuery.Items = append(exampleQuery.Items, v3.FilterItem{ + Key: topAttrib, + Operator: "=", + Value: value, + }) + + suggestions.ExampleQueries = append( + suggestions.ExampleQueries, exampleQuery, + ) + } + + if len(resp.StringAttributeValues) > 0 { + addExampleQuerySuggestion(resp.StringAttributeValues[0]) + } else if len(resp.NumberAttributeValues) > 0 { + addExampleQuerySuggestion(resp.NumberAttributeValues[0]) + } else if len(resp.BoolAttributeValues) > 0 { + addExampleQuerySuggestion(resp.BoolAttributeValues[0]) + } + } + } + + // Suggest static example queries for standard log attributes if needed. + if len(suggestions.ExampleQueries) < req.Limit { + exampleQuery := newExampleQuery() + exampleQuery.Items = append(exampleQuery.Items, v3.FilterItem{ + Key: v3.AttributeKey{ + Key: "body", + DataType: v3.AttributeKeyDataTypeString, + Type: v3.AttributeKeyTypeUnspecified, + IsColumn: true, + }, + Operator: "contains", + Value: "error", + }) + suggestions.ExampleQueries = append(suggestions.ExampleQueries, exampleQuery) + } + + return &suggestions, nil +} + func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([]string, map[string]string, []map[string]string, *v3.Point) { // Each row will have a value and a timestamp, and an optional list of label values // example: {Timestamp: ..., Value: ...} diff --git a/pkg/query-service/app/http_handler.go b/pkg/query-service/app/http_handler.go index 29cb807686..33dd443555 100644 --- a/pkg/query-service/app/http_handler.go +++ b/pkg/query-service/app/http_handler.go @@ -302,6 +302,8 @@ func (aH *APIHandler) RegisterQueryRangeV3Routes(router *mux.Router, am *AuthMid subRouter.HandleFunc("/query_range", am.ViewAccess(aH.QueryRangeV3)).Methods(http.MethodPost) subRouter.HandleFunc("/query_range/format", am.ViewAccess(aH.QueryRangeV3Format)).Methods(http.MethodPost) + subRouter.HandleFunc("/filter_suggestions", am.ViewAccess(aH.getQueryBuilderSuggestions)).Methods(http.MethodGet) + // live logs subRouter.HandleFunc("/logs/livetail", am.ViewAccess(aH.liveTailLogs)).Methods(http.MethodGet) } @@ -3150,6 +3152,30 @@ func (aH *APIHandler) autocompleteAggregateAttributes(w http.ResponseWriter, r * aH.Respond(w, response) } +func (aH *APIHandler) getQueryBuilderSuggestions(w http.ResponseWriter, r *http.Request) { + req, err := parseQBFilterSuggestionsRequest(r) + if err != nil { + RespondError(w, err, nil) + return + } + + if req.DataSource != v3.DataSourceLogs { + // Support for traces and metrics might come later + RespondError(w, model.BadRequest( + fmt.Errorf("suggestions not supported for %s", req.DataSource), + ), nil) + return + } + + response, err := aH.reader.GetQBFilterSuggestionsForLogs(r.Context(), req) + if err != nil { + RespondError(w, err, nil) + return + } + + aH.Respond(w, response) +} + func (aH *APIHandler) autoCompleteAttributeKeys(w http.ResponseWriter, r *http.Request) { var response *v3.FilterAttributeKeyResponse req, err := parseFilterAttributeKeyRequest(r) diff --git a/pkg/query-service/app/parser.go b/pkg/query-service/app/parser.go index 47da531d0b..9bdde09be6 100644 --- a/pkg/query-service/app/parser.go +++ b/pkg/query-service/app/parser.go @@ -2,6 +2,7 @@ package app import ( "bytes" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -837,6 +838,50 @@ func parseAggregateAttributeRequest(r *http.Request) (*v3.AggregateAttributeRequ return &req, nil } +func parseQBFilterSuggestionsRequest(r *http.Request) ( + *v3.QBFilterSuggestionsRequest, *model.ApiError, +) { + dataSource := v3.DataSource(r.URL.Query().Get("dataSource")) + if err := dataSource.Validate(); err != nil { + return nil, model.BadRequest(err) + } + + limit := baseconstants.DefaultFilterSuggestionsLimit + limitStr := r.URL.Query().Get("limit") + if len(limitStr) > 0 { + limit, err := strconv.Atoi(limitStr) + if err != nil || limit < 1 { + return nil, model.BadRequest(fmt.Errorf( + "invalid limit: %s", limitStr, + )) + } + } + + var existingFilter *v3.FilterSet + existingFilterB64 := r.URL.Query().Get("existingFilter") + if len(existingFilterB64) > 0 { + decodedFilterJson, err := base64.RawURLEncoding.DecodeString(existingFilterB64) + if err != nil { + return nil, model.BadRequest(fmt.Errorf("couldn't base64 decode existingFilter: %w", err)) + } + + existingFilter = &v3.FilterSet{} + err = json.Unmarshal(decodedFilterJson, existingFilter) + if err != nil { + return nil, model.BadRequest(fmt.Errorf("couldn't JSON decode existingFilter: %w", err)) + } + } + + searchText := r.URL.Query().Get("searchText") + + return &v3.QBFilterSuggestionsRequest{ + DataSource: dataSource, + Limit: limit, + SearchText: searchText, + ExistingFilter: existingFilter, + }, nil +} + func parseFilterAttributeKeyRequest(r *http.Request) (*v3.FilterAttributeKeyRequest, error) { var req v3.FilterAttributeKeyRequest diff --git a/pkg/query-service/constants/constants.go b/pkg/query-service/constants/constants.go index 561480b81a..e7e3bcf2a7 100644 --- a/pkg/query-service/constants/constants.go +++ b/pkg/query-service/constants/constants.go @@ -407,3 +407,5 @@ var TracesListViewDefaultSelectedColumns = []v3.AttributeKey{ IsColumn: true, }, } + +const DefaultFilterSuggestionsLimit = 100 diff --git a/pkg/query-service/interfaces/interface.go b/pkg/query-service/interfaces/interface.go index fea923ac27..53c5ae8d68 100644 --- a/pkg/query-service/interfaces/interface.go +++ b/pkg/query-service/interfaces/interface.go @@ -93,6 +93,10 @@ type Reader interface { GetLogAttributeValues(ctx context.Context, req *v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) GetLogAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error) GetUsers(ctx context.Context) ([]model.UserPayload, error) + GetQBFilterSuggestionsForLogs( + ctx context.Context, + req *v3.QBFilterSuggestionsRequest, + ) (*v3.QBFilterSuggestionsResponse, *model.ApiError) // Connection needed for rules, not ideal but required GetConn() clickhouse.Conn diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index e6ac8441d6..76e781ffc1 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -252,6 +252,18 @@ type FilterAttributeKeyRequest struct { Limit int `json:"limit"` } +type QBFilterSuggestionsRequest struct { + DataSource DataSource `json:"dataSource"` + SearchText string `json:"searchText"` + Limit int `json:"limit"` + ExistingFilter *FilterSet `json:"existing_filter"` +} + +type QBFilterSuggestionsResponse struct { + AttributeKeys []AttributeKey `json:"attributes"` + ExampleQueries []FilterSet `json:"example_queries"` +} + type AttributeKeyDataType string const ( diff --git a/pkg/query-service/tests/integration/filter_suggestions_test.go b/pkg/query-service/tests/integration/filter_suggestions_test.go new file mode 100644 index 0000000000..8c379b1c10 --- /dev/null +++ b/pkg/query-service/tests/integration/filter_suggestions_test.go @@ -0,0 +1,279 @@ +package tests + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "slices" + "strings" + "testing" + + mockhouse "github.com/srikanthccv/ClickHouse-go-mock" + "github.com/stretchr/testify/require" + "go.signoz.io/signoz/pkg/query-service/app" + "go.signoz.io/signoz/pkg/query-service/auth" + "go.signoz.io/signoz/pkg/query-service/constants" + "go.signoz.io/signoz/pkg/query-service/dao" + "go.signoz.io/signoz/pkg/query-service/featureManager" + "go.signoz.io/signoz/pkg/query-service/model" + v3 "go.signoz.io/signoz/pkg/query-service/model/v3" + "go.signoz.io/signoz/pkg/query-service/utils" +) + +// If no data has been received yet, filter suggestions should contain +// standard log fields and static example queries based on them +func TestDefaultLogsFilterSuggestions(t *testing.T) { + require := require.New(t) + tb := NewFilterSuggestionsTestBed(t) + + tb.mockAttribKeysQueryResponse([]v3.AttributeKey{}) + suggestionsQueryParams := map[string]string{} + suggestionsResp := tb.GetQBFilterSuggestionsForLogs(suggestionsQueryParams) + + require.Greater(len(suggestionsResp.AttributeKeys), 0) + require.True(slices.ContainsFunc( + suggestionsResp.AttributeKeys, func(a v3.AttributeKey) bool { + return a.Key == "body" + }, + )) + + require.Greater(len(suggestionsResp.ExampleQueries), 0) + require.False(slices.ContainsFunc( + suggestionsResp.AttributeKeys, func(a v3.AttributeKey) bool { + return a.Type == v3.AttributeKeyTypeTag || a.Type == v3.AttributeKeyTypeResource + }, + )) +} + +func TestLogsFilterSuggestionsWithoutExistingFilter(t *testing.T) { + require := require.New(t) + tb := NewFilterSuggestionsTestBed(t) + + testAttrib := v3.AttributeKey{ + Key: "container_id", + Type: v3.AttributeKeyTypeResource, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: false, + } + testAttribValue := "test-container" + + tb.mockAttribKeysQueryResponse([]v3.AttributeKey{testAttrib}) + tb.mockAttribValuesQueryResponse(testAttrib, []string{testAttribValue}) + suggestionsQueryParams := map[string]string{} + suggestionsResp := tb.GetQBFilterSuggestionsForLogs(suggestionsQueryParams) + + require.Greater(len(suggestionsResp.AttributeKeys), 0) + require.True(slices.ContainsFunc( + suggestionsResp.AttributeKeys, func(a v3.AttributeKey) bool { + return a.Key == testAttrib.Key && a.Type == testAttrib.Type + }, + )) + + require.Greater(len(suggestionsResp.ExampleQueries), 0) + require.True(slices.ContainsFunc( + suggestionsResp.ExampleQueries, func(q v3.FilterSet) bool { + return slices.ContainsFunc(q.Items, func(i v3.FilterItem) bool { + return i.Key.Key == testAttrib.Key && i.Value == testAttribValue + }) + }, + )) +} + +// If a filter already exists, suggested example queries should +// contain existing filter +func TestLogsFilterSuggestionsWithExistingFilter(t *testing.T) { + require := require.New(t) + tb := NewFilterSuggestionsTestBed(t) + + testAttrib := v3.AttributeKey{ + Key: "container_id", + Type: v3.AttributeKeyTypeResource, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: false, + } + testAttribValue := "test-container" + + testFilterAttrib := v3.AttributeKey{ + Key: "tenant_id", + Type: v3.AttributeKeyTypeTag, + DataType: v3.AttributeKeyDataTypeString, + IsColumn: false, + } + testFilterAttribValue := "test-tenant" + testFilter := v3.FilterSet{ + Operator: "AND", + Items: []v3.FilterItem{ + { + Key: testFilterAttrib, + Operator: "=", + Value: testFilterAttribValue, + }, + }, + } + + tb.mockAttribKeysQueryResponse([]v3.AttributeKey{testAttrib, testFilterAttrib}) + tb.mockAttribValuesQueryResponse(testAttrib, []string{testAttribValue}) + + testFilterJson, err := json.Marshal(testFilter) + require.Nil(err, "couldn't serialize existing filter to JSON") + suggestionsQueryParams := map[string]string{ + "existingFilter": base64.RawURLEncoding.EncodeToString(testFilterJson), + } + suggestionsResp := tb.GetQBFilterSuggestionsForLogs(suggestionsQueryParams) + + require.Greater(len(suggestionsResp.AttributeKeys), 0) + + // All example queries should contain the existing filter as a prefix + require.Greater(len(suggestionsResp.ExampleQueries), 0) + for _, q := range suggestionsResp.ExampleQueries { + require.Equal(q.Items[0], testFilter.Items[0]) + } +} + +// Mocks response for CH queries made by reader.GetLogAttributeKeys +func (tb *FilterSuggestionsTestBed) mockAttribKeysQueryResponse( + attribsToReturn []v3.AttributeKey, +) { + cols := []mockhouse.ColumnType{} + cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "tagKey"}) + cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "tagType"}) + cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "tagDataType"}) + + values := [][]any{} + for _, a := range attribsToReturn { + rowValues := []any{} + rowValues = append(rowValues, a.Key) + rowValues = append(rowValues, string(a.Type)) + rowValues = append(rowValues, string(a.DataType)) + values = append(values, rowValues) + } + + tb.mockClickhouse.ExpectQuery( + "select.*from.*signoz_logs.distributed_tag_attributes.*", + ).WithArgs( + constants.DefaultFilterSuggestionsLimit, + ).WillReturnRows( + mockhouse.NewRows(cols, values), + ) + + // Add expectation for the create table query used to determine + // if an attribute is a column + cols = []mockhouse.ColumnType{{Type: "String", Name: "statement"}} + values = [][]any{{"CREATE TABLE signoz_logs.distributed_logs"}} + tb.mockClickhouse.ExpectSelect( + "SHOW CREATE TABLE.*", + ).WillReturnRows(mockhouse.NewRows(cols, values)) + +} + +// Mocks response for CH queries made by reader.GetLogAttributeValues +func (tb *FilterSuggestionsTestBed) mockAttribValuesQueryResponse( + expectedAttrib v3.AttributeKey, + stringValuesToReturn []string, +) { + cols := []mockhouse.ColumnType{} + cols = append(cols, mockhouse.ColumnType{Type: "String", Name: "stringTagValue"}) + + values := [][]any{} + for _, v := range stringValuesToReturn { + rowValues := []any{} + rowValues = append(rowValues, v) + values = append(values, rowValues) + } + + tb.mockClickhouse.ExpectQuery( + "select distinct.*stringTagValue.*from.*signoz_logs.distributed_tag_attributes.*", + ).WithArgs(string(expectedAttrib.Key), v3.TagType(expectedAttrib.Type), 1).WillReturnRows(mockhouse.NewRows(cols, values)) +} + +type FilterSuggestionsTestBed struct { + t *testing.T + testUser *model.User + qsHttpHandler http.Handler + mockClickhouse mockhouse.ClickConnMockCommon +} + +func (tb *FilterSuggestionsTestBed) GetQBFilterSuggestionsForLogs( + queryParams map[string]string, +) *v3.QBFilterSuggestionsResponse { + + _, dsExistsInQP := queryParams["dataSource"] + require.False(tb.t, dsExistsInQP) + queryParams["dataSource"] = "logs" + + result := tb.QSGetRequest("/api/v3/filter_suggestions", queryParams) + + dataJson, err := json.Marshal(result.Data) + if err != nil { + tb.t.Fatalf("could not marshal apiResponse.Data: %v", err) + } + + var resp v3.QBFilterSuggestionsResponse + err = json.Unmarshal(dataJson, &resp) + if err != nil { + tb.t.Fatalf("could not unmarshal apiResponse.Data json into PipelinesResponse") + } + + return &resp +} + +func NewFilterSuggestionsTestBed(t *testing.T) *FilterSuggestionsTestBed { + testDB := utils.NewQueryServiceDBForTests(t) + + fm := featureManager.StartManager() + reader, mockClickhouse := NewMockClickhouseReader(t, testDB, fm) + mockClickhouse.MatchExpectationsInOrder(false) + + apiHandler, err := app.NewAPIHandler(app.APIHandlerOpts{ + Reader: reader, + AppDao: dao.DB(), + FeatureFlags: fm, + }) + if err != nil { + t.Fatalf("could not create a new ApiHandler: %v", err) + } + + router := app.NewRouter() + am := app.NewAuthMiddleware(auth.GetUserFromRequest) + apiHandler.RegisterRoutes(router, am) + apiHandler.RegisterQueryRangeV3Routes(router, am) + + user, apiErr := createTestUser() + if apiErr != nil { + t.Fatalf("could not create a test user: %v", apiErr) + } + + return &FilterSuggestionsTestBed{ + t: t, + testUser: user, + qsHttpHandler: router, + mockClickhouse: mockClickhouse, + } +} + +func (tb *FilterSuggestionsTestBed) QSGetRequest( + path string, + queryParams map[string]string, +) *app.ApiResponse { + if len(queryParams) > 0 { + qps := []string{} + for q, v := range queryParams { + qps = append(qps, fmt.Sprintf("%s=%s", q, v)) + } + path = fmt.Sprintf("%s?%s", path, strings.Join(qps, "&")) + } + + req, err := AuthenticatedRequestForTest( + tb.testUser, path, nil, + ) + if err != nil { + tb.t.Fatalf("couldn't create authenticated test request: %v", err) + } + + result, err := HandleTestRequest(tb.qsHttpHandler, req, 200) + if err != nil { + tb.t.Fatalf("test request failed: %v", err) + } + return result +} diff --git a/pkg/query-service/tests/integration/logparsingpipeline_test.go b/pkg/query-service/tests/integration/logparsingpipeline_test.go index 93efe30dc2..e06f7a280b 100644 --- a/pkg/query-service/tests/integration/logparsingpipeline_test.go +++ b/pkg/query-service/tests/integration/logparsingpipeline_test.go @@ -512,7 +512,7 @@ func (tb *LogPipelinesTestBed) PostPipelinesToQSExpectingStatusCode( postablePipelines logparsingpipeline.PostablePipelines, expectedStatusCode int, ) *logparsingpipeline.PipelinesResponse { - req, err := NewAuthenticatedTestRequest( + req, err := AuthenticatedRequestForTest( tb.testUser, "/api/v1/logs/pipelines", postablePipelines, ) if err != nil { @@ -562,7 +562,7 @@ func (tb *LogPipelinesTestBed) PostPipelinesToQS( } func (tb *LogPipelinesTestBed) GetPipelinesFromQS() *logparsingpipeline.PipelinesResponse { - req, err := NewAuthenticatedTestRequest( + req, err := AuthenticatedRequestForTest( tb.testUser, "/api/v1/logs/pipelines/latest", nil, ) if err != nil { diff --git a/pkg/query-service/tests/integration/signoz_integrations_test.go b/pkg/query-service/tests/integration/signoz_integrations_test.go index eae9603888..20b55a5551 100644 --- a/pkg/query-service/tests/integration/signoz_integrations_test.go +++ b/pkg/query-service/tests/integration/signoz_integrations_test.go @@ -3,10 +3,7 @@ package tests import ( "encoding/json" "fmt" - "io" "net/http" - "net/http/httptest" - "runtime/debug" "slices" "testing" "time" @@ -501,38 +498,18 @@ func (tb *IntegrationsTestBed) RequestQS( path string, postData interface{}, ) *app.ApiResponse { - req, err := NewAuthenticatedTestRequest( + req, err := AuthenticatedRequestForTest( tb.testUser, path, postData, ) if err != nil { tb.t.Fatalf("couldn't create authenticated test request: %v", err) } - respWriter := httptest.NewRecorder() - tb.qsHttpHandler.ServeHTTP(respWriter, req) - response := respWriter.Result() - responseBody, err := io.ReadAll(response.Body) + result, err := HandleTestRequest(tb.qsHttpHandler, req, 200) if err != nil { - tb.t.Fatalf("couldn't read response body received from QS: %v", err) + tb.t.Fatalf("test request failed: %v", err) } - - if response.StatusCode != 200 { - tb.t.Fatalf( - "unexpected response status from query service for path %s. status: %d, body: %v\n%v", - path, response.StatusCode, string(responseBody), string(debug.Stack()), - ) - } - - var result app.ApiResponse - err = json.Unmarshal(responseBody, &result) - if err != nil { - tb.t.Fatalf( - "Could not unmarshal QS response into an ApiResponse.\nResponse body: %s", - string(responseBody), - ) - } - - return &result + return result } func (tb *IntegrationsTestBed) mockLogQueryResponse(logsInResponse []model.SignozLog) { diff --git a/pkg/query-service/tests/integration/test_utils.go b/pkg/query-service/tests/integration/test_utils.go index 7775171310..65140e5fc8 100644 --- a/pkg/query-service/tests/integration/test_utils.go +++ b/pkg/query-service/tests/integration/test_utils.go @@ -5,8 +5,10 @@ import ( "context" "encoding/json" "fmt" + "io" "net/http" "net/http/httptest" + "runtime/debug" "testing" "time" @@ -16,6 +18,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza/entry" mockhouse "github.com/srikanthccv/ClickHouse-go-mock" "github.com/stretchr/testify/require" + "go.signoz.io/signoz/pkg/query-service/app" "go.signoz.io/signoz/pkg/query-service/app/clickhouseReader" "go.signoz.io/signoz/pkg/query-service/auth" "go.signoz.io/signoz/pkg/query-service/constants" @@ -172,7 +175,7 @@ func createTestUser() (*model.User, *model.ApiError) { ) } -func NewAuthenticatedTestRequest( +func AuthenticatedRequestForTest( user *model.User, path string, postData interface{}, @@ -198,3 +201,31 @@ func NewAuthenticatedTestRequest( req.Header.Add("Authorization", "Bearer "+userJwt.AccessJwt) return req, nil } + +func HandleTestRequest(handler http.Handler, req *http.Request, expectedStatus int) (*app.ApiResponse, error) { + respWriter := httptest.NewRecorder() + handler.ServeHTTP(respWriter, req) + response := respWriter.Result() + responseBody, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf("couldn't read response body received from QS: %w", err) + } + + if response.StatusCode != expectedStatus { + return nil, fmt.Errorf( + "unexpected response status from query service for path %s. status: %d, body: %v\n%v", + req.URL.Path, response.StatusCode, string(responseBody), string(debug.Stack()), + ) + } + + var result app.ApiResponse + err = json.Unmarshal(responseBody, &result) + if err != nil { + return nil, fmt.Errorf( + "Could not unmarshal QS response into an ApiResponse.\nResponse body: %s", + string(responseBody), + ) + } + + return &result, nil +} From 6781c29082296f3f4bcc471ee7f905f3fb05c9b6 Mon Sep 17 00:00:00 2001 From: Shaheer Kochai Date: Thu, 8 Aug 2024 08:52:15 +0430 Subject: [PATCH 158/281] feat: tests for alert channels settings (#5563) * feat: tests for alert channels settings * chore: overall improvements to alert channel settings tests * chore: improve alerts dummy data --- .../__tests__/AlertChannels.test.tsx | 78 ++++ .../AlertChannelsNormalUser.test.tsx | 72 +++ .../__tests__/CreateAlertChannel.test.tsx | 424 ++++++++++++++++++ .../CreateAlertChannelNormalUser.test.tsx | 348 ++++++++++++++ .../__tests__/EditAlertChannel.test.tsx | 118 +++++ .../AllAlertChannels/__tests__/testUtils.ts | 31 ++ .../FormAlertChannels/Settings/Email.tsx | 1 + .../FormAlertChannels/Settings/MsTeams.tsx | 3 + .../FormAlertChannels/Settings/Opsgenie.tsx | 8 +- .../FormAlertChannels/Settings/Pager.tsx | 8 + .../FormAlertChannels/Settings/Slack.tsx | 4 + .../FormAlertChannels/Settings/Webhook.tsx | 3 + .../src/container/FormAlertChannels/index.tsx | 29 +- .../src/mocks-server/__mockdata__/alerts.ts | 49 ++ frontend/src/mocks-server/handlers.ts | 13 + .../src/pages/GettingStarted/renderConfig.tsx | 2 +- 16 files changed, 1182 insertions(+), 9 deletions(-) create mode 100644 frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx create mode 100644 frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx create mode 100644 frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx create mode 100644 frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannelNormalUser.test.tsx create mode 100644 frontend/src/container/AllAlertChannels/__tests__/EditAlertChannel.test.tsx create mode 100644 frontend/src/container/AllAlertChannels/__tests__/testUtils.ts create mode 100644 frontend/src/mocks-server/__mockdata__/alerts.ts diff --git a/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx new file mode 100644 index 0000000000..14dbb70084 --- /dev/null +++ b/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx @@ -0,0 +1,78 @@ +import AlertChannels from 'container/AllAlertChannels'; +import { allAlertChannels } from 'mocks-server/__mockdata__/alerts'; +import { act, fireEvent, render, screen, waitFor } from 'tests/test-utils'; + +jest.mock('hooks/useFetch', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + payload: allAlertChannels, + })), +})); + +const successNotification = jest.fn(); +jest.mock('hooks/useNotifications', () => ({ + __esModule: true, + useNotifications: jest.fn(() => ({ + notifications: { + success: successNotification, + error: jest.fn(), + }, + })), +})); + +describe('Alert Channels Settings List page', () => { + beforeEach(() => { + render(); + }); + afterEach(() => { + jest.restoreAllMocks(); + }); + describe('Should display the Alert Channels page properly', () => { + it('Should check if "The alerts will be sent to all the configured channels." is visible ', () => { + expect(screen.getByText('sending_channels_note')).toBeInTheDocument(); + }); + it('Should check if "New Alert Channel" Button is visble ', () => { + expect(screen.getByText('button_new_channel')).toBeInTheDocument(); + }); + it('Should check if the help icon is visible and displays "tooltip_notification_channels ', async () => { + const helpIcon = screen.getByLabelText('question-circle'); + + fireEvent.mouseOver(helpIcon); + + await waitFor(() => { + const tooltip = screen.getByText('tooltip_notification_channels'); + expect(tooltip).toBeInTheDocument(); + }); + }); + }); + describe('Should check if the channels table is properly displayed', () => { + it('Should check if the table columns are properly displayed', () => { + expect(screen.getByText('column_channel_name')).toBeInTheDocument(); + expect(screen.getByText('column_channel_type')).toBeInTheDocument(); + expect(screen.getByText('column_channel_action')).toBeInTheDocument(); + }); + + it('Should check if the data in the table is displayed properly', () => { + expect(screen.getByText('Dummy-Channel')).toBeInTheDocument(); + expect(screen.getAllByText('slack')[0]).toBeInTheDocument(); + expect(screen.getAllByText('column_channel_edit')[0]).toBeInTheDocument(); + expect(screen.getAllByText('Delete')[0]).toBeInTheDocument(); + }); + + it('Should check if clicking on Delete displays Success Toast "Channel Deleted Successfully"', async () => { + const deleteButton = screen.getAllByRole('button', { name: 'Delete' })[0]; + expect(deleteButton).toBeInTheDocument(); + + act(() => { + fireEvent.click(deleteButton); + }); + + await waitFor(() => { + expect(successNotification).toBeCalledWith({ + message: 'Success', + description: 'channel_delete_success', + }); + }); + }); + }); +}); diff --git a/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx new file mode 100644 index 0000000000..3d957af104 --- /dev/null +++ b/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx @@ -0,0 +1,72 @@ +import AlertChannels from 'container/AllAlertChannels'; +import { allAlertChannels } from 'mocks-server/__mockdata__/alerts'; +import { fireEvent, render, screen, waitFor } from 'tests/test-utils'; + +jest.mock('hooks/useFetch', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + payload: allAlertChannels, + })), +})); + +const successNotification = jest.fn(); +jest.mock('hooks/useNotifications', () => ({ + __esModule: true, + useNotifications: jest.fn(() => ({ + notifications: { + success: successNotification, + error: jest.fn(), + }, + })), +})); + +jest.mock('hooks/useComponentPermission', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => [false]), +})); + +describe('Alert Channels Settings List page (Normal User)', () => { + beforeEach(() => { + render(); + }); + afterEach(() => { + jest.restoreAllMocks(); + }); + describe('Should display the Alert Channels page properly', () => { + it('Should check if "The alerts will be sent to all the configured channels." is visible ', () => { + expect(screen.getByText('sending_channels_note')).toBeInTheDocument(); + }); + + it('Should check if "New Alert Channel" Button is visble and disabled', () => { + const newAlertButton = screen.getByRole('button', { + name: 'plus button_new_channel', + }); + expect(newAlertButton).toBeInTheDocument(); + expect(newAlertButton).toBeDisabled(); + }); + it('Should check if the help icon is visible and displays "tooltip_notification_channels ', async () => { + const helpIcon = screen.getByLabelText('question-circle'); + + fireEvent.mouseOver(helpIcon); + + await waitFor(() => { + const tooltip = screen.getByText('tooltip_notification_channels'); + expect(tooltip).toBeInTheDocument(); + }); + }); + }); + describe('Should check if the channels table is properly displayed', () => { + it('Should check if the table columns are properly displayed', () => { + expect(screen.getByText('column_channel_name')).toBeInTheDocument(); + expect(screen.getByText('column_channel_type')).toBeInTheDocument(); + expect(screen.queryByText('column_channel_action')).not.toBeInTheDocument(); + }); + + it('Should check if the data in the table is displayed properly', () => { + expect(screen.getByText('Dummy-Channel')).toBeInTheDocument(); + expect(screen.getAllByText('slack')[0]).toBeInTheDocument(); + expect(screen.queryByText('column_channel_edit')).not.toBeInTheDocument(); + expect(screen.queryByText('Delete')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx new file mode 100644 index 0000000000..0406df814f --- /dev/null +++ b/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannel.test.tsx @@ -0,0 +1,424 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +/* eslint-disable sonarjs/no-identical-functions */ + +import CreateAlertChannels from 'container/CreateAlertChannels'; +import { ChannelType } from 'container/CreateAlertChannels/config'; +import { + opsGenieDescriptionDefaultValue, + opsGenieMessageDefaultValue, + opsGeniePriorityDefaultValue, + pagerDutyAdditionalDetailsDefaultValue, + pagerDutyDescriptionDefaultVaule, + pagerDutySeverityTextDefaultValue, + slackDescriptionDefaultValue, + slackTitleDefaultValue, +} from 'mocks-server/__mockdata__/alerts'; +import { server } from 'mocks-server/server'; +import { rest } from 'msw'; +import { fireEvent, render, screen, waitFor } from 'tests/test-utils'; + +import { testLabelInputAndHelpValue } from './testUtils'; + +const successNotification = jest.fn(); +const errorNotification = jest.fn(); +jest.mock('hooks/useNotifications', () => ({ + __esModule: true, + useNotifications: jest.fn(() => ({ + notifications: { + success: successNotification, + error: errorNotification, + }, + })), +})); + +jest.mock('hooks/useFeatureFlag', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + active: true, + })), +})); + +describe('Create Alert Channel', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + describe('Should check if the new alert channel is properly displayed with the cascading fields of slack channel ', () => { + beforeEach(() => { + render(); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + it('Should check if the title is "New Notification Channels"', () => { + expect(screen.getByText('page_title_create')).toBeInTheDocument(); + }); + it('Should check if the name label and textbox are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_channel_name', + testId: 'channel-name-textbox', + }); + }); + it('Should check if Send resolved alerts label and checkbox are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_send_resolved', + testId: 'field-send-resolved-checkbox', + }); + }); + it('Should check if channel type label and dropdown are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_channel_type', + testId: 'channel-type-select', + }); + }); + // Default Channel type (Slack) fields + it('Should check if the selected item in the type dropdown has text "Slack"', () => { + expect(screen.getByText('Slack')).toBeInTheDocument(); + }); + it('Should check if Webhook URL label and input are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_url', + testId: 'webhook-url-textbox', + }); + }); + it('Should check if Recepient label, input, and help text are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_recipient', + testId: 'slack-channel-textbox', + helpText: 'slack_channel_help', + }); + }); + + it('Should check if Title label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_title', + testId: 'title-textarea', + }); + }); + it('Should check if Title contains template', () => { + const titleTextArea = screen.getByTestId('title-textarea'); + + expect(titleTextArea).toHaveTextContent(slackTitleDefaultValue); + }); + it('Should check if Description label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_description', + testId: 'description-textarea', + }); + }); + it('Should check if Description contains template', () => { + const descriptionTextArea = screen.getByTestId('description-textarea'); + + expect(descriptionTextArea).toHaveTextContent(slackDescriptionDefaultValue); + }); + it('Should check if the form buttons are displayed properly (Save, Test, Back)', () => { + expect(screen.getByText('button_save_channel')).toBeInTheDocument(); + expect(screen.getByText('button_test_channel')).toBeInTheDocument(); + expect(screen.getByText('button_return')).toBeInTheDocument(); + }); + it('Should check if saving the form without filling the name displays "Something went wrong"', async () => { + const saveButton = screen.getByRole('button', { + name: 'button_save_channel', + }); + + fireEvent.click(saveButton); + + await waitFor(() => + expect(errorNotification).toHaveBeenCalledWith({ + description: 'Something went wrong', + message: 'Error', + }), + ); + }); + it('Should check if clicking on Test button shows "An alert has been sent to this channel" success message if testing passes', async () => { + server.use( + rest.post('http://localhost/api/v1/testChannel', (req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + status: 'success', + data: 'test alert sent', + }), + ), + ), + ); + const testButton = screen.getByRole('button', { + name: 'button_test_channel', + }); + + fireEvent.click(testButton); + + await waitFor(() => + expect(successNotification).toHaveBeenCalledWith({ + message: 'Success', + description: 'channel_test_done', + }), + ); + }); + it('Should check if clicking on Test button shows "Something went wrong" error message if testing fails', async () => { + const testButton = screen.getByRole('button', { + name: 'button_test_channel', + }); + + fireEvent.click(testButton); + + await waitFor(() => + expect(errorNotification).toHaveBeenCalledWith({ + message: 'Error', + description: 'channel_test_failed', + }), + ); + }); + }); + describe('New Alert Channel Cascading Fields Based on Channel Type', () => { + describe('Webhook', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Webhook"', () => { + expect(screen.getByText('Webhook')).toBeInTheDocument(); + }); + it('Should check if Webhook URL label and input are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_url', + testId: 'webhook-url-textbox', + }); + }); + it('Should check if Webhook User Name label, input, and help text are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_username', + testId: 'webhook-username-textbox', + helpText: 'help_webhook_username', + }); + }); + it('Should check if Password label and textbox, and help text are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'Password (optional)', + testId: 'webhook-password-textbox', + helpText: 'help_webhook_password', + }); + }); + }); + describe('PagerDuty', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Pagerduty"', () => { + expect(screen.getByText('Pagerduty')).toBeInTheDocument(); + }); + it('Should check if Routing key label, required, and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_routing_key', + testId: 'pager-routing-key-textbox', + }); + }); + it('Should check if Description label, required, info (Shows up as description in pagerduty), and text area are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_description', + testId: 'pager-description-textarea', + helpText: 'help_pager_description', + }); + }); + it('Should check if the description contains default template', () => { + const descriptionTextArea = screen.getByTestId( + 'pager-description-textarea', + ); + + expect(descriptionTextArea).toHaveTextContent( + pagerDutyDescriptionDefaultVaule, + ); + }); + it('Should check if Severity label, info (help_pager_severity), and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_severity', + testId: 'pager-severity-textbox', + helpText: 'help_pager_severity', + }); + }); + it('Should check if Severity contains the default template', () => { + const severityTextbox = screen.getByTestId('pager-severity-textbox'); + + expect(severityTextbox).toHaveValue(pagerDutySeverityTextDefaultValue); + }); + it('Should check if Additional Information label, text area, and help text (help_pager_details) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_details', + testId: 'pager-additional-details-textarea', + helpText: 'help_pager_details', + }); + }); + it('Should check if Additional Information contains the default template', () => { + const detailsTextArea = screen.getByTestId( + 'pager-additional-details-textarea', + ); + + expect(detailsTextArea).toHaveValue(pagerDutyAdditionalDetailsDefaultValue); + }); + it('Should check if Group label, text area, and info (help_pager_group) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_group', + testId: 'pager-group-textarea', + helpText: 'help_pager_group', + }); + }); + it('Should check if Class label, text area, and info (help_pager_class) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_class', + testId: 'pager-class-textarea', + helpText: 'help_pager_class', + }); + }); + it('Should check if Client label, text area, and info (Shows up as event source in Pagerduty) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_client', + testId: 'pager-client-textarea', + helpText: 'help_pager_client', + }); + }); + it('Should check if Client input contains the default value "SigNoz Alert Manager"', () => { + const clientTextArea = screen.getByTestId('pager-client-textarea'); + + expect(clientTextArea).toHaveValue('SigNoz Alert Manager'); + }); + it('Should check if Client URL label, text area, and info (Shows up as event source link in Pagerduty) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_client_url', + testId: 'pager-client-url-textarea', + helpText: 'help_pager_client_url', + }); + }); + it('Should check if Client URL contains the default value "https://enter-signoz-host-n-port-here/alerts"', () => { + const clientUrlTextArea = screen.getByTestId('pager-client-url-textarea'); + + expect(clientUrlTextArea).toHaveValue( + 'https://enter-signoz-host-n-port-here/alerts', + ); + }); + }); + describe('Opsgenie', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Opsgenie"', () => { + expect(screen.getByText('Opsgenie')).toBeInTheDocument(); + }); + + it('Should check if API key label, required, and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_api_key', + testId: 'opsgenie-api-key-textbox', + required: true, + }); + }); + + it('Should check if Message label, required, info (Shows up as message in opsgenie), and text area are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_message', + testId: 'opsgenie-message-textarea', + helpText: 'help_opsgenie_message', + required: true, + }); + }); + + it('Should check if Message contains the default template ', () => { + const messageTextArea = screen.getByTestId('opsgenie-message-textarea'); + + expect(messageTextArea).toHaveValue(opsGenieMessageDefaultValue); + }); + + it('Should check if Description label, required, info (Shows up as description in opsgenie), and text area are displayed properly `{{ if gt (len .Alerts.Firing) 0 -}}', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_description', + testId: 'opsgenie-description-textarea', + helpText: 'help_opsgenie_description', + required: true, + }); + }); + + it('Should check if Description label, required, info (Shows up as description in opsgenie), and text area are displayed properly `{{ if gt (len .Alerts.Firing) 0 -}}', () => { + const descriptionTextArea = screen.getByTestId( + 'opsgenie-description-textarea', + ); + + expect(descriptionTextArea).toHaveTextContent( + opsGenieDescriptionDefaultValue, + ); + }); + + it('Should check if Priority label, required, info (help_opsgenie_priority), and text area are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_priority', + testId: 'opsgenie-priority-textarea', + helpText: 'help_opsgenie_priority', + required: true, + }); + }); + + it('Should check if Message contains the default template', () => { + const priorityTextArea = screen.getByTestId('opsgenie-priority-textarea'); + + expect(priorityTextArea).toHaveValue(opsGeniePriorityDefaultValue); + }); + }); + describe('Opsgenie', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Email"', () => { + expect(screen.getByText('Email')).toBeInTheDocument(); + }); + it('Should check if API key label, required, info(help_email_to), and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_email_to', + testId: 'email-to-textbox', + helpText: 'help_email_to', + required: true, + }); + }); + }); + describe('Microsoft Teams', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "msteams"', () => { + expect(screen.getByText('msteams')).toBeInTheDocument(); + }); + + it('Should check if Webhook URL label and input are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_url', + testId: 'webhook-url-textbox', + }); + }); + + it('Should check if Title label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_title', + testId: 'title-textarea', + }); + }); + + it('Should check if Title contains template', () => { + const titleTextArea = screen.getByTestId('title-textarea'); + + expect(titleTextArea).toHaveTextContent(slackTitleDefaultValue); + }); + it('Should check if Description label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_description', + testId: 'description-textarea', + }); + }); + + it('Should check if Description contains template', () => { + const descriptionTextArea = screen.getByTestId('description-textarea'); + + expect(descriptionTextArea).toHaveTextContent(slackDescriptionDefaultValue); + }); + }); + }); +}); diff --git a/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannelNormalUser.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannelNormalUser.test.tsx new file mode 100644 index 0000000000..7c9ec5618f --- /dev/null +++ b/frontend/src/container/AllAlertChannels/__tests__/CreateAlertChannelNormalUser.test.tsx @@ -0,0 +1,348 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +/* eslint-disable sonarjs/no-identical-functions */ + +import { SIGNOZ_UPGRADE_PLAN_URL } from 'constants/app'; +import CreateAlertChannels from 'container/CreateAlertChannels'; +import { ChannelType } from 'container/CreateAlertChannels/config'; +import { + opsGenieDescriptionDefaultValue, + opsGenieMessageDefaultValue, + opsGeniePriorityDefaultValue, + pagerDutyAdditionalDetailsDefaultValue, + pagerDutyDescriptionDefaultVaule, + pagerDutySeverityTextDefaultValue, + slackDescriptionDefaultValue, + slackTitleDefaultValue, +} from 'mocks-server/__mockdata__/alerts'; +import { render, screen } from 'tests/test-utils'; + +import { testLabelInputAndHelpValue } from './testUtils'; + +describe('Create Alert Channel (Normal User)', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + describe('Should check if the new alert channel is properly displayed with the cascading fields of slack channel ', () => { + beforeEach(() => { + render(); + }); + it('Should check if the title is "New Notification Channels"', () => { + expect(screen.getByText('page_title_create')).toBeInTheDocument(); + }); + it('Should check if the name label and textbox are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_channel_name', + testId: 'channel-name-textbox', + }); + }); + it('Should check if Send resolved alerts label and checkbox are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_send_resolved', + testId: 'field-send-resolved-checkbox', + }); + }); + it('Should check if channel type label and dropdown are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_channel_type', + testId: 'channel-type-select', + }); + }); + // Default Channel type (Slack) fields + it('Should check if the selected item in the type dropdown has text "Slack"', () => { + expect(screen.getByText('Slack')).toBeInTheDocument(); + }); + it('Should check if Webhook URL label and input are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_url', + testId: 'webhook-url-textbox', + }); + }); + it('Should check if Recepient label, input, and help text are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_recipient', + testId: 'slack-channel-textbox', + helpText: 'slack_channel_help', + }); + }); + + it('Should check if Title label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_title', + testId: 'title-textarea', + }); + }); + it('Should check if Title contains template', () => { + const titleTextArea = screen.getByTestId('title-textarea'); + + expect(titleTextArea).toHaveTextContent(slackTitleDefaultValue); + }); + it('Should check if Description label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_description', + testId: 'description-textarea', + }); + }); + it('Should check if Description contains template', () => { + const descriptionTextArea = screen.getByTestId('description-textarea'); + + expect(descriptionTextArea).toHaveTextContent(slackDescriptionDefaultValue); + }); + it('Should check if the form buttons are displayed properly (Save, Test, Back)', () => { + expect(screen.getByText('button_save_channel')).toBeInTheDocument(); + expect(screen.getByText('button_test_channel')).toBeInTheDocument(); + expect(screen.getByText('button_return')).toBeInTheDocument(); + }); + }); + describe('New Alert Channel Cascading Fields Based on Channel Type', () => { + describe('Webhook', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Webhook"', () => { + expect(screen.getByText('Webhook')).toBeInTheDocument(); + }); + it('Should check if Webhook URL label and input are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_url', + testId: 'webhook-url-textbox', + }); + }); + it('Should check if Webhook User Name label, input, and help text are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_username', + testId: 'webhook-username-textbox', + helpText: 'help_webhook_username', + }); + }); + it('Should check if Password label and textbox, and help text are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'Password (optional)', + testId: 'webhook-password-textbox', + helpText: 'help_webhook_password', + }); + }); + }); + describe('PagerDuty', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Pagerduty"', () => { + expect(screen.getByText('Pagerduty')).toBeInTheDocument(); + }); + it('Should check if Routing key label, required, and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_routing_key', + testId: 'pager-routing-key-textbox', + }); + }); + it('Should check if Description label, required, info (Shows up as description in pagerduty), and text area are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_description', + testId: 'pager-description-textarea', + helpText: 'help_pager_description', + }); + }); + it('Should check if the description contains default template', () => { + const descriptionTextArea = screen.getByTestId( + 'pager-description-textarea', + ); + + expect(descriptionTextArea).toHaveTextContent( + pagerDutyDescriptionDefaultVaule, + ); + }); + it('Should check if Severity label, info (help_pager_severity), and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_severity', + testId: 'pager-severity-textbox', + helpText: 'help_pager_severity', + }); + }); + it('Should check if Severity contains the default template', () => { + const severityTextbox = screen.getByTestId('pager-severity-textbox'); + + expect(severityTextbox).toHaveValue(pagerDutySeverityTextDefaultValue); + }); + it('Should check if Additional Information label, text area, and help text (help_pager_details) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_details', + testId: 'pager-additional-details-textarea', + helpText: 'help_pager_details', + }); + }); + it('Should check if Additional Information contains the default template', () => { + const detailsTextArea = screen.getByTestId( + 'pager-additional-details-textarea', + ); + + expect(detailsTextArea).toHaveValue(pagerDutyAdditionalDetailsDefaultValue); + }); + it('Should check if Group label, text area, and info (help_pager_group) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_group', + testId: 'pager-group-textarea', + helpText: 'help_pager_group', + }); + }); + it('Should check if Class label, text area, and info (help_pager_class) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_class', + testId: 'pager-class-textarea', + helpText: 'help_pager_class', + }); + }); + it('Should check if Client label, text area, and info (Shows up as event source in Pagerduty) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_client', + testId: 'pager-client-textarea', + helpText: 'help_pager_client', + }); + }); + it('Should check if Client input contains the default value "SigNoz Alert Manager"', () => { + const clientTextArea = screen.getByTestId('pager-client-textarea'); + + expect(clientTextArea).toHaveValue('SigNoz Alert Manager'); + }); + it('Should check if Client URL label, text area, and info (Shows up as event source link in Pagerduty) are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_pager_client_url', + testId: 'pager-client-url-textarea', + helpText: 'help_pager_client_url', + }); + }); + it('Should check if Client URL contains the default value "https://enter-signoz-host-n-port-here/alerts"', () => { + const clientUrlTextArea = screen.getByTestId('pager-client-url-textarea'); + + expect(clientUrlTextArea).toHaveValue( + 'https://enter-signoz-host-n-port-here/alerts', + ); + }); + }); + describe('Opsgenie', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Opsgenie"', () => { + expect(screen.getByText('Opsgenie')).toBeInTheDocument(); + }); + + it('Should check if API key label, required, and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_api_key', + testId: 'opsgenie-api-key-textbox', + required: true, + }); + }); + + it('Should check if Message label, required, info (Shows up as message in opsgenie), and text area are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_message', + testId: 'opsgenie-message-textarea', + helpText: 'help_opsgenie_message', + required: true, + }); + }); + + it('Should check if Message contains the default template ', () => { + const messageTextArea = screen.getByTestId('opsgenie-message-textarea'); + + expect(messageTextArea).toHaveValue(opsGenieMessageDefaultValue); + }); + + it('Should check if Description label, required, info (Shows up as description in opsgenie), and text area are displayed properly `{{ if gt (len .Alerts.Firing) 0 -}}', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_description', + testId: 'opsgenie-description-textarea', + helpText: 'help_opsgenie_description', + required: true, + }); + }); + + it('Should check if Description label, required, info (Shows up as description in opsgenie), and text area are displayed properly `{{ if gt (len .Alerts.Firing) 0 -}}', () => { + const descriptionTextArea = screen.getByTestId( + 'opsgenie-description-textarea', + ); + + expect(descriptionTextArea).toHaveTextContent( + opsGenieDescriptionDefaultValue, + ); + }); + + it('Should check if Priority label, required, info (help_opsgenie_priority), and text area are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_opsgenie_priority', + testId: 'opsgenie-priority-textarea', + helpText: 'help_opsgenie_priority', + required: true, + }); + }); + + it('Should check if Message contains the default template', () => { + const priorityTextArea = screen.getByTestId('opsgenie-priority-textarea'); + + expect(priorityTextArea).toHaveValue(opsGeniePriorityDefaultValue); + }); + }); + describe('Opsgenie', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Email"', () => { + expect(screen.getByText('Email')).toBeInTheDocument(); + }); + it('Should check if API key label, required, info(help_email_to), and textbox are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_email_to', + testId: 'email-to-textbox', + helpText: 'help_email_to', + required: true, + }); + }); + }); + describe('Microsoft Teams', () => { + beforeEach(() => { + render(); + }); + + it('Should check if the selected item in the type dropdown has text "Microsoft Teams (Supported in Paid Plans Only)"', () => { + expect( + screen.getByText('Microsoft Teams (Supported in Paid Plans Only)'), + ).toBeInTheDocument(); + }); + + it('Should check if the upgrade plan message is shown', () => { + expect(screen.getByText('Upgrade to a Paid Plan')).toBeInTheDocument(); + expect( + screen.getByText(/This feature is available for paid plans only./), + ).toBeInTheDocument(); + const link = screen.getByRole('link', { name: 'Click here' }); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute('href', SIGNOZ_UPGRADE_PLAN_URL); + expect(screen.getByText(/to Upgrade/)).toBeInTheDocument(); + }); + it('Should check if the form buttons are displayed properly (Save, Test, Back)', () => { + expect( + screen.getByRole('button', { name: 'button_save_channel' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'button_test_channel' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'button_return' }), + ).toBeInTheDocument(); + }); + it('Should check if save and test buttons are disabled', () => { + expect( + screen.getByRole('button', { name: 'button_save_channel' }), + ).toBeDisabled(); + expect( + screen.getByRole('button', { name: 'button_test_channel' }), + ).toBeDisabled(); + }); + }); + }); +}); diff --git a/frontend/src/container/AllAlertChannels/__tests__/EditAlertChannel.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/EditAlertChannel.test.tsx new file mode 100644 index 0000000000..afd1a20bfd --- /dev/null +++ b/frontend/src/container/AllAlertChannels/__tests__/EditAlertChannel.test.tsx @@ -0,0 +1,118 @@ +import EditAlertChannels from 'container/EditAlertChannels'; +import { + editAlertChannelInitialValue, + editSlackDescriptionDefaultValue, + slackTitleDefaultValue, +} from 'mocks-server/__mockdata__/alerts'; +import { render, screen } from 'tests/test-utils'; + +import { testLabelInputAndHelpValue } from './testUtils'; + +const successNotification = jest.fn(); +const errorNotification = jest.fn(); +jest.mock('hooks/useNotifications', () => ({ + __esModule: true, + useNotifications: jest.fn(() => ({ + notifications: { + success: successNotification, + error: errorNotification, + }, + })), +})); + +jest.mock('hooks/useFeatureFlag', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => ({ + active: true, + })), +})); + +describe('Should check if the edit alert channel is properly displayed ', () => { + beforeEach(() => { + render(); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + it('Should check if the title is "Edit Notification Channels"', () => { + expect(screen.getByText('page_title_edit')).toBeInTheDocument(); + }); + + it('Should check if the name label and textbox are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_channel_name', + testId: 'channel-name-textbox', + value: 'Dummy-Channel', + }); + }); + it('Should check if Send resolved alerts label and checkbox are displayed properly and the checkbox is checked ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_send_resolved', + testId: 'field-send-resolved-checkbox', + }); + expect(screen.getByTestId('field-send-resolved-checkbox')).toBeChecked(); + }); + + it('Should check if channel type label and dropdown are displayed properly', () => { + testLabelInputAndHelpValue({ + labelText: 'field_channel_type', + testId: 'channel-type-select', + }); + }); + + it('Should check if the selected item in the type dropdown has text "Slack"', () => { + expect(screen.getByText('Slack')).toBeInTheDocument(); + }); + + it('Should check if Webhook URL label and input are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_webhook_url', + testId: 'webhook-url-textbox', + value: + 'https://discord.com/api/webhooks/dummy_webhook_id/dummy_webhook_token/slack', + }); + }); + + it('Should check if Recepient label, input, and help text are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_recipient', + testId: 'slack-channel-textbox', + helpText: 'slack_channel_help', + value: '#dummy_channel', + }); + }); + + it('Should check if Title label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_title', + testId: 'title-textarea', + }); + }); + + it('Should check if Title contains template', () => { + const titleTextArea = screen.getByTestId('title-textarea'); + + expect(titleTextArea).toHaveTextContent(slackTitleDefaultValue); + }); + + it('Should check if Description label and text area are displayed properly ', () => { + testLabelInputAndHelpValue({ + labelText: 'field_slack_description', + testId: 'description-textarea', + }); + }); + + it('Should check if Description contains template', () => { + const descriptionTextArea = screen.getByTestId('description-textarea'); + + expect(descriptionTextArea).toHaveTextContent( + editSlackDescriptionDefaultValue, + ); + }); + + it('Should check if the form buttons are displayed properly (Save, Test, Back)', () => { + expect(screen.getByText('button_save_channel')).toBeInTheDocument(); + expect(screen.getByText('button_test_channel')).toBeInTheDocument(); + expect(screen.getByText('button_return')).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/container/AllAlertChannels/__tests__/testUtils.ts b/frontend/src/container/AllAlertChannels/__tests__/testUtils.ts new file mode 100644 index 0000000000..bae773f2fb --- /dev/null +++ b/frontend/src/container/AllAlertChannels/__tests__/testUtils.ts @@ -0,0 +1,31 @@ +import { screen } from 'tests/test-utils'; + +export const testLabelInputAndHelpValue = ({ + labelText, + testId, + helpText, + required = false, + value, +}: { + labelText: string; + testId: string; + helpText?: string; + required?: boolean; + value?: string; +}): void => { + const label = screen.getByText(labelText); + expect(label).toBeInTheDocument(); + + const input = screen.getByTestId(testId); + expect(input).toBeInTheDocument(); + + if (helpText !== undefined) { + expect(screen.getByText(helpText)).toBeInTheDocument(); + } + if (required) { + expect(input).toBeRequired(); + } + if (value) { + expect(input).toHaveValue(value); + } +}; diff --git a/frontend/src/container/FormAlertChannels/Settings/Email.tsx b/frontend/src/container/FormAlertChannels/Settings/Email.tsx index 398e172a57..4d57d72f6d 100644 --- a/frontend/src/container/FormAlertChannels/Settings/Email.tsx +++ b/frontend/src/container/FormAlertChannels/Settings/Email.tsx @@ -27,6 +27,7 @@ function EmailForm({ setSelectedConfig }: EmailFormProps): JSX.Element { diff --git a/frontend/src/container/FormAlertChannels/Settings/MsTeams.tsx b/frontend/src/container/FormAlertChannels/Settings/MsTeams.tsx index 48751f4acc..52ef85acff 100644 --- a/frontend/src/container/FormAlertChannels/Settings/MsTeams.tsx +++ b/frontend/src/container/FormAlertChannels/Settings/MsTeams.tsx @@ -17,6 +17,7 @@ function MsTeams({ setSelectedConfig }: MsTeamsProps): JSX.Element { webhook_url: event.target.value, })); }} + data-testid="webhook-url-textbox" /> @@ -30,6 +31,7 @@ function MsTeams({ setSelectedConfig }: MsTeamsProps): JSX.Element { title: event.target.value, })) } + data-testid="title-textarea" /> @@ -41,6 +43,7 @@ function MsTeams({ setSelectedConfig }: MsTeamsProps): JSX.Element { text: event.target.value, })) } + data-testid="description-textarea" placeholder={t('placeholder_slack_description')} /> diff --git a/frontend/src/container/FormAlertChannels/Settings/Opsgenie.tsx b/frontend/src/container/FormAlertChannels/Settings/Opsgenie.tsx index 009dd01882..1472ca0b4e 100644 --- a/frontend/src/container/FormAlertChannels/Settings/Opsgenie.tsx +++ b/frontend/src/container/FormAlertChannels/Settings/Opsgenie.tsx @@ -20,7 +20,10 @@ function OpsgenieForm({ setSelectedConfig }: OpsgenieFormProps): JSX.Element { return ( <> - + @@ -46,6 +50,7 @@ function OpsgenieForm({ setSelectedConfig }: OpsgenieFormProps): JSX.Element { rows={4} onChange={handleInputChange('description')} placeholder={t('placeholder_opsgenie_description')} + data-testid="opsgenie-description-textarea" /> @@ -59,6 +64,7 @@ function OpsgenieForm({ setSelectedConfig }: OpsgenieFormProps): JSX.Element { rows={4} onChange={handleInputChange('priority')} placeholder={t('placeholder_opsgenie_priority')} + data-testid="opsgenie-priority-textarea" /> diff --git a/frontend/src/container/FormAlertChannels/Settings/Pager.tsx b/frontend/src/container/FormAlertChannels/Settings/Pager.tsx index ec228f4b8d..61df458941 100644 --- a/frontend/src/container/FormAlertChannels/Settings/Pager.tsx +++ b/frontend/src/container/FormAlertChannels/Settings/Pager.tsx @@ -18,6 +18,7 @@ function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element { routing_key: event.target.value, })); }} + data-testid="pager-routing-key-textbox" /> @@ -36,6 +37,7 @@ function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element { })) } placeholder={t('placeholder_pager_description')} + data-testid="pager-description-textarea" /> @@ -51,6 +53,7 @@ function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element { severity: event.target.value, })) } + data-testid="pager-severity-textbox" /> @@ -67,6 +70,7 @@ function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element { details: event.target.value, })) } + data-testid="pager-additional-details-textarea" /> @@ -97,6 +101,7 @@ function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element { group: event.target.value, })) } + data-testid="pager-group-textarea" /> @@ -112,6 +117,7 @@ function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element { class: event.target.value, })) } + data-testid="pager-class-textarea" /> @@ -141,6 +148,7 @@ function PagerForm({ setSelectedConfig }: PagerFormProps): JSX.Element { client_url: event.target.value, })) } + data-testid="pager-client-url-textarea" /> diff --git a/frontend/src/container/FormAlertChannels/Settings/Slack.tsx b/frontend/src/container/FormAlertChannels/Settings/Slack.tsx index c344df8ff5..5321fa5fe6 100644 --- a/frontend/src/container/FormAlertChannels/Settings/Slack.tsx +++ b/frontend/src/container/FormAlertChannels/Settings/Slack.tsx @@ -19,6 +19,7 @@ function Slack({ setSelectedConfig }: SlackProps): JSX.Element { api_url: event.target.value, })); }} + data-testid="webhook-url-textbox" /> @@ -34,11 +35,13 @@ function Slack({ setSelectedConfig }: SlackProps): JSX.Element { channel: event.target.value, })) } + data-testid="slack-channel-textbox" />