mirror of
https://github.com/SigNoz/signoz.git
synced 2026-06-29 03:20:32 +01:00
Compare commits
33 Commits
issue_5325
...
fix/toolti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a410f9348 | ||
|
|
1e887dc9c9 | ||
|
|
7646aabb2b | ||
|
|
18851afb6d | ||
|
|
d5221a6ff3 | ||
|
|
19712c3579 | ||
|
|
e31683be11 | ||
|
|
1700ad06e6 | ||
|
|
ee3b45b80d | ||
|
|
4771e30c03 | ||
|
|
e933fa74c7 | ||
|
|
853397a79e | ||
|
|
bd526df11d | ||
|
|
8ac07d3d37 | ||
|
|
9bab8e0ae2 | ||
|
|
8040f222ad | ||
|
|
a45212cb79 | ||
|
|
a609a4044c | ||
|
|
f78d98ea71 | ||
|
|
f60e5039be | ||
|
|
a483ef81a4 | ||
|
|
b9c107a851 | ||
|
|
5f6cc4c297 | ||
|
|
69e4c3c6f3 | ||
|
|
bc8c36095c | ||
|
|
385135ba6c | ||
|
|
ea4ac08666 | ||
|
|
65cdf8b74d | ||
|
|
a4ff4093cd | ||
|
|
b2acf3e856 | ||
|
|
949d18f028 | ||
|
|
8180436432 | ||
|
|
ad243b88aa |
88
.github/CODEOWNERS
vendored
88
.github/CODEOWNERS
vendored
@@ -109,6 +109,25 @@ go.mod @therealpandey
|
||||
/pkg/modules/role/ @therealpandey
|
||||
/pkg/types/coretypes/ @therealpandey @vikrantgupta25
|
||||
|
||||
/frontend/src/hooks/useAuthZ/ @H4ad
|
||||
/frontend/src/components/GuardAuthZ/ @H4ad
|
||||
/frontend/src/components/AuthZTooltip/ @H4ad
|
||||
/frontend/src/components/createGuardedRoute/ @H4ad
|
||||
/frontend/src/container/RolesSettings/ @H4ad
|
||||
/frontend/src/components/RolesSelect/ @H4ad
|
||||
/frontend/src/pages/MembersSettings/ @H4ad
|
||||
/frontend/src/container/MembersSettings/ @H4ad
|
||||
/frontend/src/components/MembersTable/ @H4ad
|
||||
/frontend/src/components/EditMemberDrawer/ @H4ad
|
||||
/frontend/src/components/InviteMembersModal/ @H4ad
|
||||
/frontend/src/hooks/member/ @H4ad
|
||||
/frontend/src/pages/ServiceAccountsSettings/ @H4ad
|
||||
/frontend/src/container/ServiceAccountsSettings/ @H4ad
|
||||
/frontend/src/components/ServiceAccountsTable/ @H4ad
|
||||
/frontend/src/components/ServiceAccountDrawer/ @H4ad
|
||||
/frontend/src/components/CreateServiceAccountModal/ @H4ad
|
||||
/frontend/src/hooks/serviceAccount/ @H4ad
|
||||
|
||||
# IdentN Owners
|
||||
|
||||
/pkg/identn/ @therealpandey
|
||||
@@ -199,3 +218,72 @@ go.mod @therealpandey
|
||||
## OpenAPI Schema - Generated
|
||||
/frontend/src/api/generated/services/ @therealpandey @vikrantgupta25 @srikanthccv
|
||||
/docs/api/openapi.yml @therealpandey @vikrantgupta25 @srikanthccv
|
||||
|
||||
## Logs
|
||||
/frontend/src/pages/Logs/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/LogsExplorer/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/LogsModulePage/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/LogsSettings/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/LiveLogs/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsExplorerChart/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsExplorerContext/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsExplorerList/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsExplorerTable/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsExplorerViews/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsFilters/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsSearchFilter/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsTable/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsAggregate/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsContextList/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsIndexToFields/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsLoading/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogsPanelTable/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogControls/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogDetailedView/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogExplorerQuerySection/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LogLiveTail/ @SigNoz/events-frontend
|
||||
/frontend/src/container/LiveLogs/ @SigNoz/events-frontend
|
||||
/frontend/src/container/EmptyLogsSearch/ @SigNoz/events-frontend
|
||||
/frontend/src/container/NoLogs/ @SigNoz/events-frontend
|
||||
/frontend/src/components/Logs/ @SigNoz/events-frontend
|
||||
/frontend/src/components/LogDetail/ @SigNoz/events-frontend
|
||||
/frontend/src/components/LogsFormatOptionsMenu/ @SigNoz/events-frontend
|
||||
/frontend/src/hooks/logs/ @SigNoz/events-frontend
|
||||
|
||||
## Logs Pipelines
|
||||
/frontend/src/pages/Pipelines/ @SigNoz/events-frontend
|
||||
/frontend/src/container/PipelinePage/ @SigNoz/events-frontend
|
||||
|
||||
## Traces / Trace Explorer
|
||||
/frontend/src/pages/Trace/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/TracesExplorer/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/TracesModulePage/ @SigNoz/events-frontend
|
||||
/frontend/src/container/Trace/ @SigNoz/events-frontend
|
||||
/frontend/src/container/TracesExplorer/ @SigNoz/events-frontend
|
||||
/frontend/src/container/TracesTableComponent/ @SigNoz/events-frontend
|
||||
|
||||
## Trace Funnels
|
||||
/frontend/src/pages/TracesFunnels/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/TracesFunnelDetails/ @SigNoz/events-frontend
|
||||
/frontend/src/hooks/TracesFunnels/ @SigNoz/events-frontend
|
||||
|
||||
## Trace Details
|
||||
/frontend/src/pages/TraceDetailsV3/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/TraceDetailOldRedirect/ @SigNoz/events-frontend
|
||||
/frontend/src/hooks/trace/ @SigNoz/events-frontend
|
||||
|
||||
## Exceptions
|
||||
/frontend/src/pages/AllErrors/ @SigNoz/events-frontend
|
||||
/frontend/src/pages/ErrorDetails/ @SigNoz/events-frontend
|
||||
/frontend/src/container/AllError/ @SigNoz/events-frontend
|
||||
/frontend/src/container/ErrorDetails/ @SigNoz/events-frontend
|
||||
|
||||
## External APIs
|
||||
/frontend/src/pages/ApiMonitoring/ @SigNoz/events-frontend
|
||||
/frontend/src/container/ApiMonitoring/ @SigNoz/events-frontend
|
||||
|
||||
## Messaging Queues
|
||||
/frontend/src/pages/MessagingQueues/ @SigNoz/events-frontend
|
||||
/frontend/src/components/MessagingQueues/ @SigNoz/events-frontend
|
||||
/frontend/src/components/MessagingQueueHealthCheck/ @SigNoz/events-frontend
|
||||
/frontend/src/hooks/messagingQueue/ @SigNoz/events-frontend
|
||||
|
||||
6
.github/workflows/e2eci.yaml
vendored
6
.github/workflows/e2eci.yaml
vendored
@@ -70,7 +70,11 @@ jobs:
|
||||
cd tests/e2e && pnpm install --frozen-lockfile
|
||||
- name: playwright-browsers
|
||||
run: |
|
||||
cd tests/e2e && pnpm playwright install --with-deps ${{ matrix.project }}
|
||||
docker create --name pw mcr.microsoft.com/playwright:v1.57.0-noble
|
||||
docker cp pw:/ms-playwright "$RUNNER_TEMP/ms-playwright"
|
||||
docker rm pw
|
||||
echo "PLAYWRIGHT_BROWSERS_PATH=$RUNNER_TEMP/ms-playwright" >> "$GITHUB_ENV"
|
||||
cd tests/e2e && pnpm playwright install-deps ${{ matrix.project }}
|
||||
- name: bring-up-stack
|
||||
run: |
|
||||
cd tests && \
|
||||
|
||||
17
.github/workflows/goci.yaml
vendored
17
.github/workflows/goci.yaml
vendored
@@ -140,3 +140,20 @@ jobs:
|
||||
run: |
|
||||
go run cmd/enterprise/*.go generate config web-settings
|
||||
git diff --compact-summary --exit-code || (echo; echo "Unexpected difference in web settings schema. Run go run cmd/enterprise/*.go generate config web-settings locally and commit."; exit 1)
|
||||
transaction-groups:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: self-checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: generate-transaction-groups
|
||||
run: |
|
||||
go run cmd/enterprise/*.go generate config transaction-groups
|
||||
git diff --compact-summary --exit-code || (echo; echo "Unexpected difference in transaction groups schema. Run go run cmd/enterprise/*.go generate config transaction-groups locally and commit."; exit 1)
|
||||
|
||||
11
.github/workflows/jsci.yaml
vendored
11
.github/workflows/jsci.yaml
vendored
@@ -56,17 +56,6 @@ jobs:
|
||||
PRIMUS_REF: main
|
||||
JS_SRC: frontend
|
||||
JS_PKG_MANAGER: pnpm
|
||||
languages:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: self-checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: run
|
||||
run: bash frontend/scripts/validate-md-languages.sh
|
||||
openapi:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
|
||||
@@ -29,6 +29,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/cloudintegration/implcloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricreductionrule"
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricreductionrule/implmetricreductionrule"
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/modules/retention"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
@@ -119,6 +121,9 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
func(_ sqlstore.SQLStore, _ dashboard.Module, _ global.Global, _ zeus.Zeus, _ gateway.Gateway, _ licensing.Licensing, _ serviceaccount.Module, _ cloudintegration.Config) (cloudintegration.Module, error) {
|
||||
return implcloudintegration.NewModule(), nil
|
||||
},
|
||||
func(_ sqlstore.SQLStore, _ telemetrystore.TelemetryStore, _ dashboard.Module, _ queryparser.QueryParser, _ licensing.Licensing, _ flagger.Flagger, _ telemetrytypes.MetadataStore, _ factory.ProviderSettings, _ int) metricreductionrule.Module {
|
||||
return implmetricreductionrule.NewModule()
|
||||
},
|
||||
func(c cache.Cache, am alertmanager.Alertmanager, ss sqlstore.SQLStore, ts telemetrystore.TelemetryStore, ms telemetrytypes.MetadataStore, p prometheus.Prometheus, og organization.Getter, rsh rulestatehistory.Module, q querier.Querier, qp queryparser.QueryParser) factory.NamedMap[factory.ProviderFactory[ruler.Ruler, ruler.Config]] {
|
||||
return factory.MustNewNamedMap(signozruler.NewFactory(c, am, ss, ts, ms, p, og, rsh, q, qp, nil, nil))
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/SigNoz/signoz/ee/modules/cloudintegration/implcloudintegration"
|
||||
"github.com/SigNoz/signoz/ee/modules/cloudintegration/implcloudintegration/implcloudprovider"
|
||||
"github.com/SigNoz/signoz/ee/modules/dashboard/impldashboard"
|
||||
eeimplmetricreductionrule "github.com/SigNoz/signoz/ee/modules/metricreductionrule/implmetricreductionrule"
|
||||
eequerier "github.com/SigNoz/signoz/ee/querier"
|
||||
enterpriseapp "github.com/SigNoz/signoz/ee/query-service/app"
|
||||
eerules "github.com/SigNoz/signoz/ee/query-service/rules"
|
||||
@@ -46,6 +47,7 @@ import (
|
||||
pkgcloudintegration "github.com/SigNoz/signoz/pkg/modules/cloudintegration/implcloudintegration"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
pkgimpldashboard "github.com/SigNoz/signoz/pkg/modules/dashboard/impldashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricreductionrule"
|
||||
"github.com/SigNoz/signoz/pkg/modules/organization"
|
||||
"github.com/SigNoz/signoz/pkg/modules/retention"
|
||||
"github.com/SigNoz/signoz/pkg/modules/rulestatehistory"
|
||||
@@ -182,6 +184,9 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
|
||||
return implcloudintegration.NewModule(pkgcloudintegration.NewStore(sqlStore), dashboardModule, global, zeus, gateway, licensing, serviceAccount, cloudProvidersMap, config)
|
||||
},
|
||||
func(sqlStore sqlstore.SQLStore, ts telemetrystore.TelemetryStore, dashboardModule dashboard.Module, queryParser queryparser.QueryParser, lic licensing.Licensing, flgr pkgflagger.Flagger, ms telemetrytypes.MetadataStore, ps factory.ProviderSettings, threads int) metricreductionrule.Module {
|
||||
return eeimplmetricreductionrule.NewModule(sqlStore, ts, dashboardModule, queryParser, lic, flgr, ms, ps, threads)
|
||||
},
|
||||
func(c cache.Cache, am alertmanager.Alertmanager, ss sqlstore.SQLStore, ts telemetrystore.TelemetryStore, ms telemetrytypes.MetadataStore, p prometheus.Prometheus, og organization.Getter, rsh rulestatehistory.Module, q querier.Querier, qp queryparser.QueryParser) factory.NamedMap[factory.ProviderFactory[ruler.Ruler, ruler.Config]] {
|
||||
return factory.MustNewNamedMap(signozruler.NewFactory(c, am, ss, ts, ms, p, og, rsh, q, qp, eerules.PrepareTaskFunc, eerules.TestNotification))
|
||||
},
|
||||
|
||||
@@ -6,12 +6,15 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/web"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/swaggest/jsonschema-go"
|
||||
)
|
||||
|
||||
const webSettingsSchemaPath = "docs/config/web-settings.json"
|
||||
const webSettingsSchemaPath = "frontend/src/schemas/generated/webSettings.schema.json"
|
||||
|
||||
const transactionGroupsSchemaPath = "frontend/src/schemas/generated/transactionGroups.schema.json"
|
||||
|
||||
func registerGenerateConfig(parentCmd *cobra.Command) {
|
||||
configCmd := &cobra.Command{
|
||||
@@ -27,6 +30,14 @@ func registerGenerateConfig(parentCmd *cobra.Command) {
|
||||
},
|
||||
})
|
||||
|
||||
configCmd.AddCommand(&cobra.Command{
|
||||
Use: "transaction-groups",
|
||||
Short: "Generate JSON Schema for transaction groups",
|
||||
RunE: func(currCmd *cobra.Command, args []string) error {
|
||||
return generateTransactionGroups()
|
||||
},
|
||||
})
|
||||
|
||||
parentCmd.AddCommand(configCmd)
|
||||
}
|
||||
|
||||
@@ -52,6 +63,7 @@ func generateWebSettings() error {
|
||||
return err
|
||||
}
|
||||
|
||||
schema.WithTitle("WebSettings")
|
||||
data, err := json.MarshalIndent(schema, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -59,3 +71,31 @@ func generateWebSettings() error {
|
||||
|
||||
return os.WriteFile(webSettingsSchemaPath, append(data, '\n'), 0o600)
|
||||
}
|
||||
|
||||
func generateTransactionGroups() error {
|
||||
falseVal := false
|
||||
noAdditional := jsonschema.SchemaOrBool{TypeBoolean: &falseVal}
|
||||
|
||||
reflector := jsonschema.Reflector{}
|
||||
reflector.DefaultOptions = append(reflector.DefaultOptions,
|
||||
jsonschema.InterceptSchema(func(params jsonschema.InterceptSchemaParams) (bool, error) {
|
||||
if params.Value.Kind() == reflect.Struct {
|
||||
params.Schema.AdditionalProperties = &noAdditional
|
||||
}
|
||||
return false, nil
|
||||
}),
|
||||
)
|
||||
|
||||
schema, err := reflector.Reflect(authtypes.TransactionGroups{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
schema.WithTitle("TransactionGroups")
|
||||
data, err := json.MarshalIndent(schema, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(transactionGroupsSchemaPath, append(data, '\n'), 0o600)
|
||||
}
|
||||
|
||||
270
deploy/MIGRATION.md
Normal file
270
deploy/MIGRATION.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Migrating from the install script and `deploy/` to Foundry
|
||||
|
||||
The install script (`install.sh`) and the bundled Compose and Swarm files
|
||||
under `deploy/` are deprecated in favor of [Foundry][foundry], the supported
|
||||
way to install and manage SigNoz. This guide moves an existing Docker Compose
|
||||
or Docker Swarm deployment to Foundry and reattaches your existing volumes, so
|
||||
your data is preserved.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This guide is only for **existing** `install.sh` / `deploy/` deployments.
|
||||
> Setting up SigNoz for the first time? Skip migration and install Foundry
|
||||
> directly: [SigNoz install docs][install-docs].
|
||||
|
||||
## How it works
|
||||
|
||||
Foundry splits a deployment into two commands:
|
||||
|
||||
- `foundryctl forge` generates the deployment manifests from a `casting.yaml`.
|
||||
It never touches running containers, so it is safe to re-run while you
|
||||
iterate.
|
||||
- `foundryctl cast` applies those manifests: it (re)creates the containers and
|
||||
reuses the volumes you point it at.
|
||||
|
||||
You write one `casting.yaml`, point a few patches at your existing data
|
||||
volumes, then cast. The steps below are the same for Compose and Swarm; they
|
||||
differ only in the casting (step 3) and how you stop the old stack (step 5).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An existing SigNoz deployment from `install.sh` or `deploy/` (Compose or
|
||||
Swarm).
|
||||
- `foundryctl` (installed in step 1).
|
||||
|
||||
## Migrate
|
||||
|
||||
### 1. Install Foundry
|
||||
|
||||
```bash
|
||||
curl -fsSL https://signoz.io/foundry.sh | bash
|
||||
```
|
||||
|
||||
### 2. Keep your rollback path
|
||||
|
||||
This migration reattaches your existing volumes in place; it does not move or
|
||||
delete your data. The only destructive action is passing `--volumes` / `-v`
|
||||
when you stop the old stack (step 5), so avoid that flag.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Keep a copy of your existing `docker-compose.yaml` / stack file (and any
|
||||
> config it references). SigNoz no longer distributes these files, so this copy
|
||||
> is your only way to roll back.
|
||||
|
||||
### 3. Write your `casting.yaml`
|
||||
|
||||
Use the casting for your deployment. Both reproduce the legacy single-node
|
||||
setup (ClickHouse + ZooKeeper + SQLite) and reattach your existing volumes;
|
||||
they differ only in `spec.deployment.flavor` and the volume-reuse patch
|
||||
(Compose volumes have a `name` to replace; Swarm volumes are bare, so the whole
|
||||
entry is replaced). If your deployment ran more than one shard or replica,
|
||||
adjust the volume patches accordingly. The
|
||||
[Docker Compose example][compose-example] is a useful reference.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The `replica` and `shard` macros are placeholders. Replace them with the
|
||||
> values from your existing ClickHouse config (the `macros` section of
|
||||
> `config.xml` / `metrika.xml`), or the generated manifests will not match your
|
||||
> existing data.
|
||||
|
||||
<details>
|
||||
<summary><b>Docker Compose</b> casting.yaml</summary>
|
||||
|
||||
```yaml
|
||||
# casting.yaml
|
||||
apiVersion: v1alpha1
|
||||
kind: Installation
|
||||
metadata:
|
||||
name: signoz
|
||||
spec:
|
||||
deployment:
|
||||
flavor: compose
|
||||
mode: docker
|
||||
metastore:
|
||||
kind: sqlite
|
||||
telemetrykeeper:
|
||||
kind: zookeeper
|
||||
telemetrystore:
|
||||
spec:
|
||||
config:
|
||||
data:
|
||||
config-0-0.yaml: |
|
||||
macros:
|
||||
replica: "example01-01-1" # replace with your replica macro
|
||||
shard: "01" # replace with your shard macro
|
||||
patches:
|
||||
- target: "deployment/compose.yaml"
|
||||
operations:
|
||||
- op: replace
|
||||
path: /volumes/signoz-telemetrykeeper-0-data/name
|
||||
value: signoz-zookeeper-1
|
||||
- op: replace
|
||||
path: /volumes/signoz-telemetrystore-0-0-data/name
|
||||
value: signoz-clickhouse
|
||||
- op: replace
|
||||
path: /volumes/signoz-metastore-sqlite-0-data/name
|
||||
value: signoz-sqlite
|
||||
- op: add
|
||||
path: /services/signoz-telemetrykeeper-zookeeper-0/user
|
||||
value: root
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Docker Swarm</b> casting.yaml</summary>
|
||||
|
||||
```yaml
|
||||
# casting.yaml
|
||||
apiVersion: v1alpha1
|
||||
kind: Installation
|
||||
metadata:
|
||||
name: signoz
|
||||
spec:
|
||||
deployment:
|
||||
flavor: swarm
|
||||
mode: docker
|
||||
metastore:
|
||||
kind: sqlite
|
||||
telemetrykeeper:
|
||||
kind: zookeeper
|
||||
telemetrystore:
|
||||
spec:
|
||||
config:
|
||||
data:
|
||||
config-0-0.yaml: |
|
||||
macros:
|
||||
replica: "example01-01-1" # replace with your replica macro
|
||||
shard: "01" # replace with your shard macro
|
||||
patches:
|
||||
- target: "deployment/compose.yaml"
|
||||
operations:
|
||||
- op: replace
|
||||
path: /volumes/signoz-telemetrykeeper-0-data
|
||||
value:
|
||||
name: signoz-zookeeper-1
|
||||
- op: replace
|
||||
path: /volumes/signoz-telemetrystore-0-0-data
|
||||
value:
|
||||
name: signoz-clickhouse
|
||||
- op: replace
|
||||
path: /volumes/signoz-metastore-sqlite-0-data
|
||||
value:
|
||||
name: signoz-sqlite
|
||||
- op: add
|
||||
path: /services/signoz-telemetrykeeper-zookeeper-0/user
|
||||
value: root
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
> [!NOTE]
|
||||
> The `user: root` patch on the ZooKeeper service lets the container read and
|
||||
> write the data in your reused ZooKeeper volume, whose files the legacy setup
|
||||
> created as `root`. Without it, ZooKeeper may fail to start with permission
|
||||
> errors.
|
||||
|
||||
If you had custom configuration (SMTP, extra ingestion receivers/processors,
|
||||
or custom ClickHouse settings), carry it over via [patches][patches],
|
||||
[custom config files][custom-config], or [environment variables][env-vars].
|
||||
|
||||
### 4. Generate and review the manifests
|
||||
|
||||
```bash
|
||||
foundryctl forge -f casting.yaml
|
||||
```
|
||||
|
||||
Review `pours/deployment/` before deploying:
|
||||
|
||||
- [ ] Container images match your current deployment. Foundry generates with
|
||||
`latest` by default; if your SigNoz version was older than latest, check the
|
||||
[upgrade path][upgrade-path] first.
|
||||
- [ ] The generated manifests match your previous configuration, especially
|
||||
`compose.yaml` (the new entry point for your deployment).
|
||||
- [ ] The ClickHouse config is now YAML rather than XML; confirm your custom
|
||||
settings carried over (see [ClickHouse configuration files][ch-config] for
|
||||
the XML-to-YAML mapping).
|
||||
|
||||
### 5. Stop the old deployment
|
||||
|
||||
Use the command for your deployment. Do **not** pass `--volumes` / `-v`; that
|
||||
would delete the data you are migrating.
|
||||
|
||||
```bash
|
||||
docker compose down # Compose
|
||||
docker stack rm signoz # Swarm
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This causes downtime, so plan accordingly.
|
||||
|
||||
Confirm nothing is still bound to the volumes before continuing:
|
||||
|
||||
```bash
|
||||
docker ps -a
|
||||
```
|
||||
|
||||
### 6. Deploy with Foundry
|
||||
|
||||
```bash
|
||||
foundryctl cast -f casting.yaml
|
||||
```
|
||||
|
||||
This recreates the containers against your existing volumes and pulls the
|
||||
images. The migration container runs the schema migrations as part of `cast`.
|
||||
|
||||
**Prefer not to use `cast`?** The manifests in `pours/deployment/` are standard
|
||||
Docker artifacts you can apply yourself. Run the command from that directory so
|
||||
the relative config paths resolve:
|
||||
|
||||
```bash
|
||||
cd pours/deployment
|
||||
docker compose up -d # Compose
|
||||
docker stack deploy -c compose.yaml signoz # Swarm
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
- All SigNoz containers are running.
|
||||
- The UI is reachable on `http://localhost:8080`, and OTLP on `4317` (gRPC)
|
||||
and `4318` (HTTP), so already-instrumented apps and saved bookmarks keep
|
||||
working.
|
||||
- Your existing data is present in the UI, and new data is being ingested.
|
||||
- ClickHouse and ZooKeeper logs show no errors.
|
||||
|
||||
## Roll back
|
||||
|
||||
Step 5 left your volumes untouched, so your data is intact. To return to the
|
||||
previous setup:
|
||||
|
||||
1. Bring down the Foundry deployment (`docker compose down` or
|
||||
`docker stack rm signoz`, again without `-v`).
|
||||
2. Confirm the containers are gone with `docker ps -a`.
|
||||
3. Re-apply your backed-up stack: `docker compose up -d` (Compose) or
|
||||
`docker stack deploy -c docker-compose.yaml signoz` (Swarm). It reattaches
|
||||
the same volumes and restores your prior state.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the migration runs into trouble, reach out on [Slack][slack] or open a
|
||||
[Foundry issue][foundry-issues].
|
||||
|
||||
## References
|
||||
|
||||
- [Foundry][foundry]
|
||||
- [Casting file reference][casting-ref]
|
||||
- [Custom config files][custom-config]
|
||||
- [Patches][patches]
|
||||
- [SigNoz documentation][signoz-docs]
|
||||
|
||||
[foundry]: https://github.com/SigNoz/foundry
|
||||
[install-docs]: https://signoz.io/docs/install/
|
||||
[compose-example]: https://github.com/SigNoz/foundry/tree/main/docs/examples/docker/compose
|
||||
[patches]: https://github.com/SigNoz/foundry/blob/main/docs/concepts/patches.md
|
||||
[custom-config]: https://github.com/SigNoz/foundry/blob/main/docs/concepts/moldings.md#custom-config-files
|
||||
[env-vars]: https://github.com/SigNoz/foundry/blob/main/docs/reference/casting-file.md#molding-spec
|
||||
[casting-ref]: https://github.com/SigNoz/foundry/blob/main/docs/reference/casting-file.md
|
||||
[ch-config]: https://clickhouse.com/docs/operations/configuration-files
|
||||
[upgrade-path]: https://signoz.io/docs/operate/upgrade/
|
||||
[slack]: https://signoz.io/slack
|
||||
[foundry-issues]: https://github.com/SigNoz/foundry/issues
|
||||
[signoz-docs]: https://signoz.io/docs
|
||||
@@ -3,77 +3,16 @@
|
||||
Check that you have cloned [signoz/signoz](https://github.com/signoz/signoz)
|
||||
and currently are in `signoz/deploy` folder.
|
||||
|
||||
## Docker
|
||||
## Installation
|
||||
|
||||
If you don't have docker set up, please follow [this guide](https://docs.docker.com/engine/install/)
|
||||
to set up docker before proceeding with the next steps.
|
||||
> **Note:** The `install.sh` script and the `docker-compose` manifests have been deprecated.
|
||||
|
||||
### Using Install Script
|
||||
SigNoz now installs and runs through [Foundry](https://signoz.io/docs/install/docker/).
|
||||
|
||||
Now run the following command to install:
|
||||
> **Already running SigNoz via Docker Compose?** See the [Migration Guide](./MIGRATION.md) to transition your existing deployment to Foundry.
|
||||
|
||||
```sh
|
||||
./install.sh
|
||||
```
|
||||
|
||||
### Using Docker Compose
|
||||
|
||||
If you don't have docker compose set up, please follow [this guide](https://docs.docker.com/compose/install/)
|
||||
to set up docker compose before proceeding with the next steps.
|
||||
|
||||
```sh
|
||||
cd deploy/docker
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Open http://localhost:8080 in your favourite browser.
|
||||
|
||||
To start collecting logs and metrics from your infrastructure, run the following command:
|
||||
|
||||
```sh
|
||||
cd generator/infra
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
To start generating sample traces, run the following command:
|
||||
|
||||
```sh
|
||||
cd generator/hotrod
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
In a couple of minutes, you should see the data generated from hotrod in SigNoz UI.
|
||||
|
||||
For more details, please refer to the [SigNoz documentation](https://signoz.io/docs/install/docker/).
|
||||
|
||||
## Docker Swarm
|
||||
|
||||
To install SigNoz using Docker Swarm, run the following command:
|
||||
|
||||
```sh
|
||||
cd deploy/docker-swarm
|
||||
docker stack deploy -c docker-compose.yaml signoz
|
||||
```
|
||||
|
||||
Open http://localhost:8080 in your favourite browser.
|
||||
|
||||
To start collecting logs and metrics from your infrastructure, run the following command:
|
||||
|
||||
```sh
|
||||
cd generator/infra
|
||||
docker stack deploy -c docker-compose.yaml infra
|
||||
```
|
||||
|
||||
To start generating sample traces, run the following command:
|
||||
|
||||
```sh
|
||||
cd generator/hotrod
|
||||
docker stack deploy -c docker-compose.yaml hotrod
|
||||
```
|
||||
|
||||
In a couple of minutes, you should see the data generated from hotrod in SigNoz UI.
|
||||
|
||||
For more details, please refer to the [SigNoz documentation](https://signoz.io/docs/install/docker-swarm/).
|
||||
Please follow the latest installation instructions at [signoz.io/docs/install/docker](https://signoz.io/docs/install/docker/).
|
||||
Foundry has support for **different platforms and architectures**, please review the project documentation for more details.
|
||||
|
||||
## Uninstall/Troubleshoot?
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<clickhouse>
|
||||
<!-- ZooKeeper is used to store metadata about replicas, when using Replicated tables.
|
||||
Optional. If you don't use replicated tables, you could omit that.
|
||||
|
||||
See https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/replication/
|
||||
-->
|
||||
<zookeeper>
|
||||
<node index="1">
|
||||
<host>zookeeper-1</host>
|
||||
<port>2181</port>
|
||||
</node>
|
||||
<node index="2">
|
||||
<host>zookeeper-2</host>
|
||||
<port>2181</port>
|
||||
</node>
|
||||
<node index="3">
|
||||
<host>zookeeper-3</host>
|
||||
<port>2181</port>
|
||||
</node>
|
||||
</zookeeper>
|
||||
|
||||
<!-- Configuration of clusters that could be used in Distributed tables.
|
||||
https://clickhouse.com/docs/en/operations/table_engines/distributed/
|
||||
-->
|
||||
<remote_servers>
|
||||
<cluster>
|
||||
<!-- Inter-server per-cluster secret for Distributed queries
|
||||
default: no secret (no authentication will be performed)
|
||||
|
||||
If set, then Distributed queries will be validated on shards, so at least:
|
||||
- such cluster should exist on the shard,
|
||||
- such cluster should have the same secret.
|
||||
|
||||
And also (and which is more important), the initial_user will
|
||||
be used as current user for the query.
|
||||
|
||||
Right now the protocol is pretty simple and it only takes into account:
|
||||
- cluster name
|
||||
- query
|
||||
|
||||
Also it will be nice if the following will be implemented:
|
||||
- source hostname (see interserver_http_host), but then it will depends from DNS,
|
||||
it can use IP address instead, but then the you need to get correct on the initiator node.
|
||||
- target hostname / ip address (same notes as for source hostname)
|
||||
- time-based security tokens
|
||||
-->
|
||||
<!-- <secret></secret> -->
|
||||
<shard>
|
||||
<!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). -->
|
||||
<!-- <internal_replication>false</internal_replication> -->
|
||||
<!-- Optional. Shard weight when writing data. Default: 1. -->
|
||||
<!-- <weight>1</weight> -->
|
||||
<replica>
|
||||
<host>clickhouse</host>
|
||||
<port>9000</port>
|
||||
<!-- Optional. Priority of the replica for load_balancing. Default: 1 (less value has more priority). -->
|
||||
<!-- <priority>1</priority> -->
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse-2</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse-3</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</cluster>
|
||||
</remote_servers>
|
||||
</clickhouse>
|
||||
@@ -1,75 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<clickhouse>
|
||||
<!-- ZooKeeper is used to store metadata about replicas, when using Replicated tables.
|
||||
Optional. If you don't use replicated tables, you could omit that.
|
||||
|
||||
See https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/replication/
|
||||
-->
|
||||
<zookeeper>
|
||||
<node index="1">
|
||||
<host>zookeeper-1</host>
|
||||
<port>2181</port>
|
||||
</node>
|
||||
<!-- <node index="2">
|
||||
<host>zookeeper-2</host>
|
||||
<port>2181</port>
|
||||
</node>
|
||||
<node index="3">
|
||||
<host>zookeeper-3</host>
|
||||
<port>2181</port>
|
||||
</node> -->
|
||||
</zookeeper>
|
||||
|
||||
<!-- Configuration of clusters that could be used in Distributed tables.
|
||||
https://clickhouse.com/docs/en/operations/table_engines/distributed/
|
||||
-->
|
||||
<remote_servers>
|
||||
<cluster>
|
||||
<!-- Inter-server per-cluster secret for Distributed queries
|
||||
default: no secret (no authentication will be performed)
|
||||
|
||||
If set, then Distributed queries will be validated on shards, so at least:
|
||||
- such cluster should exist on the shard,
|
||||
- such cluster should have the same secret.
|
||||
|
||||
And also (and which is more important), the initial_user will
|
||||
be used as current user for the query.
|
||||
|
||||
Right now the protocol is pretty simple and it only takes into account:
|
||||
- cluster name
|
||||
- query
|
||||
|
||||
Also it will be nice if the following will be implemented:
|
||||
- source hostname (see interserver_http_host), but then it will depends from DNS,
|
||||
it can use IP address instead, but then the you need to get correct on the initiator node.
|
||||
- target hostname / ip address (same notes as for source hostname)
|
||||
- time-based security tokens
|
||||
-->
|
||||
<!-- <secret></secret> -->
|
||||
<shard>
|
||||
<!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). -->
|
||||
<!-- <internal_replication>false</internal_replication> -->
|
||||
<!-- Optional. Shard weight when writing data. Default: 1. -->
|
||||
<!-- <weight>1</weight> -->
|
||||
<replica>
|
||||
<host>clickhouse</host>
|
||||
<port>9000</port>
|
||||
<!-- Optional. Priority of the replica for load_balancing. Default: 1 (less value has more priority). -->
|
||||
<!-- <priority>1</priority> -->
|
||||
</replica>
|
||||
</shard>
|
||||
<!-- <shard>
|
||||
<replica>
|
||||
<host>clickhouse-2</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>clickhouse-3</host>
|
||||
<port>9000</port>
|
||||
</replica>
|
||||
</shard> -->
|
||||
</cluster>
|
||||
</remote_servers>
|
||||
</clickhouse>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
||||
<functions>
|
||||
<function>
|
||||
<type>executable</type>
|
||||
<name>histogramQuantile</name>
|
||||
<return_type>Float64</return_type>
|
||||
<argument>
|
||||
<type>Array(Float64)</type>
|
||||
<name>buckets</name>
|
||||
</argument>
|
||||
<argument>
|
||||
<type>Array(Float64)</type>
|
||||
<name>counts</name>
|
||||
</argument>
|
||||
<argument>
|
||||
<type>Float64</type>
|
||||
<name>quantile</name>
|
||||
</argument>
|
||||
<format>CSV</format>
|
||||
<command>./histogramQuantile</command>
|
||||
</function>
|
||||
</functions>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<clickhouse>
|
||||
<storage_configuration>
|
||||
<disks>
|
||||
<default>
|
||||
<keep_free_space_bytes>10485760</keep_free_space_bytes>
|
||||
</default>
|
||||
<s3>
|
||||
<type>s3</type>
|
||||
<!-- For S3 cold storage,
|
||||
if region is us-east-1, endpoint can be https://<bucket-name>.s3.amazonaws.com
|
||||
if region is not us-east-1, endpoint should be https://<bucket-name>.s3-<region>.amazonaws.com
|
||||
For GCS cold storage,
|
||||
endpoint should be https://storage.googleapis.com/<bucket-name>/data/
|
||||
-->
|
||||
<endpoint>https://BUCKET-NAME.s3-REGION-NAME.amazonaws.com/data/</endpoint>
|
||||
<access_key_id>ACCESS-KEY-ID</access_key_id>
|
||||
<secret_access_key>SECRET-ACCESS-KEY</secret_access_key>
|
||||
<!-- In case of S3, uncomment the below configuration in case you want to read
|
||||
AWS credentials from the Environment variables if they exist. -->
|
||||
<!-- <use_environment_credentials>true</use_environment_credentials> -->
|
||||
<!-- In case of GCS, uncomment the below configuration, since GCS does
|
||||
not support batch deletion and result in error messages in logs. -->
|
||||
<!-- <support_batch_delete>false</support_batch_delete> -->
|
||||
</s3>
|
||||
</disks>
|
||||
<policies>
|
||||
<tiered>
|
||||
<volumes>
|
||||
<default>
|
||||
<disk>default</disk>
|
||||
</default>
|
||||
<s3>
|
||||
<disk>s3</disk>
|
||||
<perform_ttl_move_on_insert>0</perform_ttl_move_on_insert>
|
||||
</s3>
|
||||
</volumes>
|
||||
</tiered>
|
||||
</policies>
|
||||
</storage_configuration>
|
||||
</clickhouse>
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<clickhouse>
|
||||
<!-- See also the files in users.d directory where the settings can be overridden. -->
|
||||
|
||||
<!-- Profiles of settings. -->
|
||||
<profiles>
|
||||
<!-- Default settings. -->
|
||||
<default>
|
||||
<!-- Maximum memory usage for processing single query, in bytes. -->
|
||||
<max_memory_usage>10000000000</max_memory_usage>
|
||||
|
||||
<!-- How to choose between replicas during distributed query processing.
|
||||
random - choose random replica from set of replicas with minimum number of errors
|
||||
nearest_hostname - from set of replicas with minimum number of errors, choose replica
|
||||
with minimum number of different symbols between replica's hostname and local hostname
|
||||
(Hamming distance).
|
||||
in_order - first live replica is chosen in specified order.
|
||||
first_or_random - if first replica one has higher number of errors, pick a random one from replicas with minimum number of errors.
|
||||
-->
|
||||
<load_balancing>random</load_balancing>
|
||||
</default>
|
||||
|
||||
<!-- Profile that allows only read queries. -->
|
||||
<readonly>
|
||||
<readonly>1</readonly>
|
||||
</readonly>
|
||||
</profiles>
|
||||
|
||||
<!-- Users and ACL. -->
|
||||
<users>
|
||||
<!-- If user name was not specified, 'default' user is used. -->
|
||||
<default>
|
||||
<!-- See also the files in users.d directory where the password can be overridden.
|
||||
|
||||
Password could be specified in plaintext or in SHA256 (in hex format).
|
||||
|
||||
If you want to specify password in plaintext (not recommended), place it in 'password' element.
|
||||
Example: <password>qwerty</password>.
|
||||
Password could be empty.
|
||||
|
||||
If you want to specify SHA256, place it in 'password_sha256_hex' element.
|
||||
Example: <password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>
|
||||
Restrictions of SHA256: impossibility to connect to ClickHouse using MySQL JS client (as of July 2019).
|
||||
|
||||
If you want to specify double SHA1, place it in 'password_double_sha1_hex' element.
|
||||
Example: <password_double_sha1_hex>e395796d6546b1b65db9d665cd43f0e858dd4303</password_double_sha1_hex>
|
||||
|
||||
If you want to specify a previously defined LDAP server (see 'ldap_servers' in the main config) for authentication,
|
||||
place its name in 'server' element inside 'ldap' element.
|
||||
Example: <ldap><server>my_ldap_server</server></ldap>
|
||||
|
||||
If you want to authenticate the user via Kerberos (assuming Kerberos is enabled, see 'kerberos' in the main config),
|
||||
place 'kerberos' element instead of 'password' (and similar) elements.
|
||||
The name part of the canonical principal name of the initiator must match the user name for authentication to succeed.
|
||||
You can also place 'realm' element inside 'kerberos' element to further restrict authentication to only those requests
|
||||
whose initiator's realm matches it.
|
||||
Example: <kerberos />
|
||||
Example: <kerberos><realm>EXAMPLE.COM</realm></kerberos>
|
||||
|
||||
How to generate decent password:
|
||||
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha256sum | tr -d '-'
|
||||
In first line will be password and in second - corresponding SHA256.
|
||||
|
||||
How to generate double SHA1:
|
||||
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha1sum | tr -d '-' | xxd -r -p | sha1sum | tr -d '-'
|
||||
In first line will be password and in second - corresponding double SHA1.
|
||||
-->
|
||||
<password></password>
|
||||
|
||||
<!-- List of networks with open access.
|
||||
|
||||
To open access from everywhere, specify:
|
||||
<ip>::/0</ip>
|
||||
|
||||
To open access only from localhost, specify:
|
||||
<ip>::1</ip>
|
||||
<ip>127.0.0.1</ip>
|
||||
|
||||
Each element of list has one of the following forms:
|
||||
<ip> IP-address or network mask. Examples: 213.180.204.3 or 10.0.0.1/8 or 10.0.0.1/255.255.255.0
|
||||
2a02:6b8::3 or 2a02:6b8::3/64 or 2a02:6b8::3/ffff:ffff:ffff:ffff::.
|
||||
<host> Hostname. Example: server01.clickhouse.com.
|
||||
To check access, DNS query is performed, and all received addresses compared to peer address.
|
||||
<host_regexp> Regular expression for host names. Example, ^server\d\d-\d\d-\d\.clickhouse\.com$
|
||||
To check access, DNS PTR query is performed for peer address and then regexp is applied.
|
||||
Then, for result of PTR query, another DNS query is performed and all received addresses compared to peer address.
|
||||
Strongly recommended that regexp is ends with $
|
||||
All results of DNS requests are cached till server restart.
|
||||
-->
|
||||
<networks>
|
||||
<ip>::/0</ip>
|
||||
</networks>
|
||||
|
||||
<!-- Settings profile for user. -->
|
||||
<profile>default</profile>
|
||||
|
||||
<!-- Quota for user. -->
|
||||
<quota>default</quota>
|
||||
|
||||
<!-- User can create other users and grant rights to them. -->
|
||||
<!-- <access_management>1</access_management> -->
|
||||
</default>
|
||||
</users>
|
||||
|
||||
<!-- Quotas. -->
|
||||
<quotas>
|
||||
<!-- Name of quota. -->
|
||||
<default>
|
||||
<!-- Limits for time interval. You could specify many intervals with different limits. -->
|
||||
<interval>
|
||||
<!-- Length of interval. -->
|
||||
<duration>3600</duration>
|
||||
|
||||
<!-- No limits. Just calculate resource usage for time interval. -->
|
||||
<queries>0</queries>
|
||||
<errors>0</errors>
|
||||
<result_rows>0</result_rows>
|
||||
<read_rows>0</read_rows>
|
||||
<execution_time>0</execution_time>
|
||||
</interval>
|
||||
</default>
|
||||
</quotas>
|
||||
</clickhouse>
|
||||
@@ -1,16 +0,0 @@
|
||||
from locust import HttpUser, task, between
|
||||
class UserTasks(HttpUser):
|
||||
wait_time = between(5, 15)
|
||||
|
||||
@task
|
||||
def rachel(self):
|
||||
self.client.get("/dispatch?customer=123&nonse=0.6308392664170006")
|
||||
@task
|
||||
def trom(self):
|
||||
self.client.get("/dispatch?customer=392&nonse=0.015296363321630757")
|
||||
@task
|
||||
def japanese(self):
|
||||
self.client.get("/dispatch?customer=731&nonse=0.8022286220408668")
|
||||
@task
|
||||
def coffee(self):
|
||||
self.client.get("/dispatch?customer=567&nonse=0.0022220379420636593")
|
||||
@@ -1 +0,0 @@
|
||||
server_endpoint: ws://signoz:4320/v1/opamp
|
||||
@@ -1,25 +0,0 @@
|
||||
# my global config
|
||||
global:
|
||||
scrape_interval: 5s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
|
||||
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
|
||||
# scrape_timeout is set to the global default (10s).
|
||||
|
||||
# Alertmanager configuration
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- alertmanager:9093
|
||||
|
||||
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
||||
rule_files: []
|
||||
# - "first_rules.yml"
|
||||
# - "second_rules.yml"
|
||||
# - 'alerts.yml'
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape:
|
||||
# Here it's Prometheus itself.
|
||||
scrape_configs: []
|
||||
|
||||
remote_read:
|
||||
- url: tcp://clickhouse:9000/signoz_metrics
|
||||
@@ -1,288 +0,0 @@
|
||||
version: "3"
|
||||
x-common: &common
|
||||
networks:
|
||||
- signoz-net
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
logging:
|
||||
options:
|
||||
max-size: 50m
|
||||
max-file: "3"
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
!!merge <<: *common
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
tty: true
|
||||
deploy:
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9363"
|
||||
signoz.io/path: "/metrics"
|
||||
depends_on:
|
||||
- zookeeper-1
|
||||
- zookeeper-2
|
||||
- zookeeper-3
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- 0.0.0.0:8123/ping
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
ulimits:
|
||||
nproc: 65535
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
environment:
|
||||
- CLICKHOUSE_SKIP_USER_SETUP=1
|
||||
x-zookeeper-defaults: &zookeeper-defaults
|
||||
!!merge <<: *common
|
||||
image: signoz/zookeeper:3.7.1
|
||||
user: root
|
||||
deploy:
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9141"
|
||||
signoz.io/path: "/metrics"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- curl -s -m 2 http://localhost:8080/commands/ruok | grep error | grep null
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
x-db-depend: &db-depend
|
||||
!!merge <<: *common
|
||||
depends_on:
|
||||
- clickhouse
|
||||
- clickhouse-2
|
||||
- clickhouse-3
|
||||
services:
|
||||
init-clickhouse:
|
||||
!!merge <<: *common
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
version="v0.0.1"
|
||||
node_os=$$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
node_arch=$$(uname -m | sed s/aarch64/arm64/ | sed s/x86_64/amd64/)
|
||||
echo "Fetching histogram-binary for $${node_os}/$${node_arch}"
|
||||
cd /tmp
|
||||
wget -O histogram-quantile.tar.gz "https://github.com/SigNoz/signoz/releases/download/histogram-quantile%2F$${version}/histogram-quantile_$${node_os}_$${node_arch}.tar.gz"
|
||||
tar -xvzf histogram-quantile.tar.gz
|
||||
mv histogram-quantile /var/lib/clickhouse/user_scripts/histogramQuantile
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
zookeeper-1:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
# ports:
|
||||
# - "2181:2181"
|
||||
# - "2888:2888"
|
||||
# - "3888:3888"
|
||||
volumes:
|
||||
- ./clickhouse-setup/data/zookeeper-1:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=1
|
||||
- ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
zookeeper-2:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
# ports:
|
||||
# - "2182:2181"
|
||||
# - "2889:2888"
|
||||
# - "3889:3888"
|
||||
volumes:
|
||||
- ./clickhouse-setup/data/zookeeper-2:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=2
|
||||
- ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
zookeeper-3:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
# ports:
|
||||
# - "2183:2181"
|
||||
# - "2890:2888"
|
||||
# - "3890:3888"
|
||||
volumes:
|
||||
- ./clickhouse-setup/data/zookeeper-3:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=3
|
||||
- ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
clickhouse:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
# TODO: needed for schema-migrator to work, remove this redundancy once we have a better solution
|
||||
hostname: clickhouse
|
||||
# ports:
|
||||
# - "9000:9000"
|
||||
# - "8123:8123"
|
||||
# - "9181:9181"
|
||||
configs:
|
||||
- source: clickhouse-config
|
||||
target: /etc/clickhouse-server/config.xml
|
||||
- source: clickhouse-users
|
||||
target: /etc/clickhouse-server/users.xml
|
||||
- source: clickhouse-custom-function
|
||||
target: /etc/clickhouse-server/custom-function.xml
|
||||
- source: clickhouse-cluster
|
||||
target: /etc/clickhouse-server/config.d/cluster.ha.xml
|
||||
volumes:
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
- ./clickhouse-setup/data/clickhouse/:/var/lib/clickhouse/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
clickhouse-2:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
hostname: clickhouse-2
|
||||
# ports:
|
||||
# - "9001:9000"
|
||||
# - "8124:8123"
|
||||
# - "9182:9181"
|
||||
configs:
|
||||
- source: clickhouse-config
|
||||
target: /etc/clickhouse-server/config.xml
|
||||
- source: clickhouse-users
|
||||
target: /etc/clickhouse-server/users.xml
|
||||
- source: clickhouse-custom-function
|
||||
target: /etc/clickhouse-server/custom-function.xml
|
||||
- source: clickhouse-cluster
|
||||
target: /etc/clickhouse-server/config.d/cluster.ha.xml
|
||||
volumes:
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
- ./clickhouse-setup/data/clickhouse-2/:/var/lib/clickhouse/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
clickhouse-3:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
hostname: clickhouse-3
|
||||
# ports:
|
||||
# - "9002:9000"
|
||||
# - "8125:8123"
|
||||
# - "9183:9181"
|
||||
configs:
|
||||
- source: clickhouse-config
|
||||
target: /etc/clickhouse-server/config.xml
|
||||
- source: clickhouse-users
|
||||
target: /etc/clickhouse-server/users.xml
|
||||
- source: clickhouse-custom-function
|
||||
target: /etc/clickhouse-server/custom-function.xml
|
||||
- source: clickhouse-cluster
|
||||
target: /etc/clickhouse-server/config.d/cluster.ha.xml
|
||||
volumes:
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
- ./clickhouse-setup/data/clickhouse-3/:/var/lib/clickhouse/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.129.0
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
# - "6060:6060" # pprof port
|
||||
volumes:
|
||||
- ./clickhouse-setup/data/signoz/:/var/lib/signoz/
|
||||
environment:
|
||||
- SIGNOZ_ALERTMANAGER_PROVIDER=signoz
|
||||
- SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_SQLSTORE_SQLITE_PATH=/var/lib/signoz/signoz.db
|
||||
- SIGNOZ_TOKENIZER_JWT_SECRET=secret
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- localhost:8080/api/v1/health
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate sync check &&
|
||||
/signoz-otel-collector --config=/etc/otel-collector-config.yaml --manager-config=/etc/manager-config.yaml --copy-path=/var/tmp/collector-config.yaml
|
||||
configs:
|
||||
- source: otel-collector-config
|
||||
target: /etc/otel-collector-config.yaml
|
||||
- source: otel-manager-config
|
||||
target: /etc/manager-config.yaml
|
||||
environment:
|
||||
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}}
|
||||
- LOW_CARDINAL_EXCEPTION_GROUPING=false
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
ports:
|
||||
# - "1777:1777" # pprof extension
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
deploy:
|
||||
replicas: 3
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate bootstrap &&
|
||||
/signoz-otel-collector migrate sync up &&
|
||||
/signoz-otel-collector migrate async up
|
||||
networks:
|
||||
signoz-net:
|
||||
name: signoz-net
|
||||
volumes:
|
||||
clickhouse:
|
||||
name: signoz-clickhouse
|
||||
clickhouse-2:
|
||||
name: signoz-clickhouse-2
|
||||
clickhouse-3:
|
||||
name: signoz-clickhouse-3
|
||||
sqlite:
|
||||
name: signoz-sqlite
|
||||
zookeeper-1:
|
||||
name: signoz-zookeeper-1
|
||||
zookeeper-2:
|
||||
name: signoz-zookeeper-2
|
||||
zookeeper-3:
|
||||
name: signoz-zookeeper-3
|
||||
configs:
|
||||
clickhouse-config:
|
||||
file: ../common/clickhouse/config.xml
|
||||
clickhouse-users:
|
||||
file: ../common/clickhouse/users.xml
|
||||
clickhouse-custom-function:
|
||||
file: ../common/clickhouse/custom-function.xml
|
||||
clickhouse-cluster:
|
||||
file: ../common/clickhouse/cluster.ha.xml
|
||||
otel-collector-config:
|
||||
file: ./otel-collector-config.yaml
|
||||
otel-manager-config:
|
||||
file: ../common/signoz/otel-collector-opamp-config.yaml
|
||||
@@ -1,206 +0,0 @@
|
||||
version: "3"
|
||||
x-common: &common
|
||||
networks:
|
||||
- signoz-net
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
logging:
|
||||
options:
|
||||
max-size: 50m
|
||||
max-file: "3"
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
!!merge <<: *common
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
tty: true
|
||||
deploy:
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9363"
|
||||
signoz.io/path: "/metrics"
|
||||
depends_on:
|
||||
- init-clickhouse
|
||||
- zookeeper-1
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- 0.0.0.0:8123/ping
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
ulimits:
|
||||
nproc: 65535
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
environment:
|
||||
- CLICKHOUSE_SKIP_USER_SETUP=1
|
||||
x-zookeeper-defaults: &zookeeper-defaults
|
||||
!!merge <<: *common
|
||||
image: signoz/zookeeper:3.7.1
|
||||
user: root
|
||||
deploy:
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9141"
|
||||
signoz.io/path: "/metrics"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- curl -s -m 2 http://localhost:8080/commands/ruok | grep error | grep null
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
x-db-depend: &db-depend
|
||||
!!merge <<: *common
|
||||
depends_on:
|
||||
- clickhouse
|
||||
services:
|
||||
init-clickhouse:
|
||||
!!merge <<: *common
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
version="v0.0.1"
|
||||
node_os=$$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
node_arch=$$(uname -m | sed s/aarch64/arm64/ | sed s/x86_64/amd64/)
|
||||
echo "Fetching histogram-binary for $${node_os}/$${node_arch}"
|
||||
cd /tmp
|
||||
wget -O histogram-quantile.tar.gz "https://github.com/SigNoz/signoz/releases/download/histogram-quantile%2F$${version}/histogram-quantile_$${node_os}_$${node_arch}.tar.gz"
|
||||
tar -xvzf histogram-quantile.tar.gz
|
||||
mv histogram-quantile /var/lib/clickhouse/user_scripts/histogramQuantile
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
zookeeper-1:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
# ports:
|
||||
# - "2181:2181"
|
||||
# - "2888:2888"
|
||||
# - "3888:3888"
|
||||
volumes:
|
||||
- zookeeper-1:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=1
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
clickhouse:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
# TODO: needed for clickhouse TCP connectio
|
||||
hostname: clickhouse
|
||||
# ports:
|
||||
# - "9000:9000"
|
||||
# - "8123:8123"
|
||||
# - "9181:9181"
|
||||
|
||||
configs:
|
||||
- source: clickhouse-config
|
||||
target: /etc/clickhouse-server/config.xml
|
||||
- source: clickhouse-users
|
||||
target: /etc/clickhouse-server/users.xml
|
||||
- source: clickhouse-custom-function
|
||||
target: /etc/clickhouse-server/custom-function.xml
|
||||
- source: clickhouse-cluster
|
||||
target: /etc/clickhouse-server/config.d/cluster.xml
|
||||
volumes:
|
||||
- clickhouse:/var/lib/clickhouse/
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.129.0
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
volumes:
|
||||
- sqlite:/var/lib/signoz/
|
||||
environment:
|
||||
- SIGNOZ_ALERTMANAGER_PROVIDER=signoz
|
||||
- SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_SQLSTORE_SQLITE_PATH=/var/lib/signoz/signoz.db
|
||||
- SIGNOZ_TOKENIZER_JWT_SECRET=secret
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- localhost:8080/api/v1/health
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate sync check &&
|
||||
/signoz-otel-collector --config=/etc/otel-collector-config.yaml --manager-config=/etc/manager-config.yaml --copy-path=/var/tmp/collector-config.yaml
|
||||
configs:
|
||||
- source: otel-collector-config
|
||||
target: /etc/otel-collector-config.yaml
|
||||
- source: otel-manager-config
|
||||
target: /etc/manager-config.yaml
|
||||
environment:
|
||||
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}}
|
||||
- LOW_CARDINAL_EXCEPTION_GROUPING=false
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
ports:
|
||||
# - "1777:1777" # pprof extension
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
deploy:
|
||||
replicas: 3
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:v0.144.5
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate bootstrap &&
|
||||
/signoz-otel-collector migrate sync up &&
|
||||
/signoz-otel-collector migrate async up
|
||||
networks:
|
||||
signoz-net:
|
||||
name: signoz-net
|
||||
volumes:
|
||||
clickhouse:
|
||||
name: signoz-clickhouse
|
||||
sqlite:
|
||||
name: signoz-sqlite
|
||||
zookeeper-1:
|
||||
name: signoz-zookeeper-1
|
||||
configs:
|
||||
clickhouse-config:
|
||||
file: ../common/clickhouse/config.xml
|
||||
clickhouse-users:
|
||||
file: ../common/clickhouse/users.xml
|
||||
clickhouse-custom-function:
|
||||
file: ../common/clickhouse/custom-function.xml
|
||||
clickhouse-cluster:
|
||||
file: ../common/clickhouse/cluster.xml
|
||||
otel-collector-config:
|
||||
file: ./otel-collector-config.yaml
|
||||
otel-manager-config:
|
||||
file: ../common/signoz/otel-collector-opamp-config.yaml
|
||||
@@ -1,118 +0,0 @@
|
||||
connectors:
|
||||
signozmeter:
|
||||
metrics_flush_interval: 1h
|
||||
dimensions:
|
||||
- name: service.name
|
||||
- name: deployment.environment
|
||||
- name: host.name
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
prometheus:
|
||||
config:
|
||||
global:
|
||||
scrape_interval: 60s
|
||||
scrape_configs:
|
||||
- job_name: otel-collector
|
||||
static_configs:
|
||||
- targets:
|
||||
- localhost:8888
|
||||
labels:
|
||||
job_name: otel-collector
|
||||
processors:
|
||||
batch:
|
||||
send_batch_size: 10000
|
||||
send_batch_max_size: 11000
|
||||
timeout: 10s
|
||||
batch/meter:
|
||||
send_batch_max_size: 25000
|
||||
send_batch_size: 20000
|
||||
timeout: 1s
|
||||
resourcedetection:
|
||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||
detectors: [env, system]
|
||||
timeout: 2s
|
||||
signozspanmetrics/delta:
|
||||
metrics_exporter: signozclickhousemetrics
|
||||
metrics_flush_interval: 60s
|
||||
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
||||
dimensions_cache_size: 100000
|
||||
aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA
|
||||
enable_exp_histogram: true
|
||||
dimensions:
|
||||
- name: service.namespace
|
||||
default: default
|
||||
- name: deployment.environment
|
||||
default: default
|
||||
# This is added to ensure the uniqueness of the timeseries
|
||||
# Otherwise, identical timeseries produced by multiple replicas of
|
||||
# collectors result in incorrect APM metrics
|
||||
- name: signoz.collector.id
|
||||
- name: service.version
|
||||
- name: browser.platform
|
||||
- name: browser.mobile
|
||||
- name: k8s.cluster.name
|
||||
- name: k8s.node.name
|
||||
- name: k8s.namespace.name
|
||||
- name: host.name
|
||||
- name: host.type
|
||||
- name: container.name
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 0.0.0.0:13133
|
||||
pprof:
|
||||
endpoint: 0.0.0.0:1777
|
||||
exporters:
|
||||
clickhousetraces:
|
||||
datasource: tcp://clickhouse:9000/signoz_traces
|
||||
low_cardinal_exception_grouping: ${env:LOW_CARDINAL_EXCEPTION_GROUPING}
|
||||
use_new_schema: true
|
||||
signozclickhousemetrics:
|
||||
dsn: tcp://clickhouse:9000/signoz_metrics
|
||||
clickhouselogsexporter:
|
||||
dsn: tcp://clickhouse:9000/signoz_logs
|
||||
timeout: 10s
|
||||
use_new_schema: true
|
||||
signozclickhousemeter:
|
||||
dsn: tcp://clickhouse:9000/signoz_meter
|
||||
timeout: 45s
|
||||
sending_queue:
|
||||
enabled: false
|
||||
metadataexporter:
|
||||
cache:
|
||||
provider: in_memory
|
||||
dsn: tcp://clickhouse:9000/signoz_metadata
|
||||
enabled: true
|
||||
timeout: 45s
|
||||
service:
|
||||
telemetry:
|
||||
logs:
|
||||
encoding: json
|
||||
extensions:
|
||||
- health_check
|
||||
- pprof
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [signozspanmetrics/delta, batch]
|
||||
exporters: [clickhousetraces, metadataexporter, signozmeter]
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [signozclickhousemetrics, metadataexporter, signozmeter]
|
||||
metrics/prometheus:
|
||||
receivers: [prometheus]
|
||||
processors: [batch]
|
||||
exporters: [signozclickhousemetrics, metadataexporter, signozmeter]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [clickhouselogsexporter, metadataexporter, signozmeter]
|
||||
metrics/meter:
|
||||
receivers: [signozmeter]
|
||||
processors: [batch/meter]
|
||||
exporters: [signozclickhousemeter]
|
||||
@@ -1 +0,0 @@
|
||||
COMPOSE_PROJECT_NAME=signoz
|
||||
@@ -1,3 +0,0 @@
|
||||
This data directory is deprecated and will be removed in the future.
|
||||
Please use the migration script under `scripts/volume-migration` to migrate data from bind mounts to Docker volumes.
|
||||
The script also renames the project name to `signoz` and the network name to `signoz-net` (if not already in place).
|
||||
@@ -1,2 +0,0 @@
|
||||
This directory is deprecated and will be removed in the future.
|
||||
Please use the new directory for Clickhouse setup scripts: `scripts/clickhouse` instead.
|
||||
Binary file not shown.
@@ -1,265 +0,0 @@
|
||||
version: "3"
|
||||
x-common: &common
|
||||
networks:
|
||||
- signoz-net
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
options:
|
||||
max-size: 50m
|
||||
max-file: "3"
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
!!merge <<: *common
|
||||
# addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
tty: true
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9363"
|
||||
signoz.io/path: "/metrics"
|
||||
depends_on:
|
||||
init-clickhouse:
|
||||
condition: service_completed_successfully
|
||||
zookeeper-1:
|
||||
condition: service_healthy
|
||||
zookeeper-2:
|
||||
condition: service_healthy
|
||||
zookeeper-3:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- 0.0.0.0:8123/ping
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
ulimits:
|
||||
nproc: 65535
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
environment:
|
||||
- CLICKHOUSE_SKIP_USER_SETUP=1
|
||||
x-zookeeper-defaults: &zookeeper-defaults
|
||||
!!merge <<: *common
|
||||
image: signoz/zookeeper:3.7.1
|
||||
user: root
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9141"
|
||||
signoz.io/path: "/metrics"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- curl -s -m 2 http://localhost:8080/commands/ruok | grep error | grep null
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
x-db-depend: &db-depend
|
||||
!!merge <<: *common
|
||||
depends_on:
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
clickhouse-2:
|
||||
condition: service_healthy
|
||||
clickhouse-3:
|
||||
condition: service_healthy
|
||||
services:
|
||||
init-clickhouse:
|
||||
!!merge <<: *common
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
container_name: signoz-init-clickhouse
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
version="v0.0.1"
|
||||
node_os=$$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
node_arch=$$(uname -m | sed s/aarch64/arm64/ | sed s/x86_64/amd64/)
|
||||
echo "Fetching histogram-binary for $${node_os}/$${node_arch}"
|
||||
cd /tmp
|
||||
wget -O histogram-quantile.tar.gz "https://github.com/SigNoz/signoz/releases/download/histogram-quantile%2F$${version}/histogram-quantile_$${node_os}_$${node_arch}.tar.gz"
|
||||
tar -xvzf histogram-quantile.tar.gz
|
||||
mv histogram-quantile /var/lib/clickhouse/user_scripts/histogramQuantile
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
zookeeper-1:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
container_name: signoz-zookeeper-1
|
||||
# ports:
|
||||
# - "2181:2181"
|
||||
# - "2888:2888"
|
||||
# - "3888:3888"
|
||||
volumes:
|
||||
- zookeeper-1:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=1
|
||||
- ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
zookeeper-2:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
container_name: signoz-zookeeper-2
|
||||
# ports:
|
||||
# - "2182:2181"
|
||||
# - "2889:2888"
|
||||
# - "3889:3888"
|
||||
volumes:
|
||||
- zookeeper-2:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=2
|
||||
- ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
zookeeper-3:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
container_name: signoz-zookeeper-3
|
||||
# ports:
|
||||
# - "2183:2181"
|
||||
# - "2890:2888"
|
||||
# - "3890:3888"
|
||||
volumes:
|
||||
- zookeeper-3:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=3
|
||||
- ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
clickhouse:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
container_name: signoz-clickhouse
|
||||
# ports:
|
||||
# - "9000:9000"
|
||||
# - "8123:8123"
|
||||
# - "9181:9181"
|
||||
volumes:
|
||||
- ../common/clickhouse/config.xml:/etc/clickhouse-server/config.xml
|
||||
- ../common/clickhouse/users.xml:/etc/clickhouse-server/users.xml
|
||||
- ../common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
- ../common/clickhouse/cluster.ha.xml:/etc/clickhouse-server/config.d/cluster.xml
|
||||
- clickhouse:/var/lib/clickhouse/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
clickhouse-2:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
container_name: signoz-clickhouse-2
|
||||
# ports:
|
||||
# - "9001:9000"
|
||||
# - "8124:8123"
|
||||
# - "9182:9181"
|
||||
volumes:
|
||||
- ../common/clickhouse/config.xml:/etc/clickhouse-server/config.xml
|
||||
- ../common/clickhouse/users.xml:/etc/clickhouse-server/users.xml
|
||||
- ../common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
- ../common/clickhouse/cluster.ha.xml:/etc/clickhouse-server/config.d/cluster.xml
|
||||
- clickhouse-2:/var/lib/clickhouse/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
clickhouse-3:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
container_name: signoz-clickhouse-3
|
||||
# ports:
|
||||
# - "9002:9000"
|
||||
# - "8125:8123"
|
||||
# - "9183:9181"
|
||||
volumes:
|
||||
- ../common/clickhouse/config.xml:/etc/clickhouse-server/config.xml
|
||||
- ../common/clickhouse/users.xml:/etc/clickhouse-server/users.xml
|
||||
- ../common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
- ../common/clickhouse/cluster.ha.xml:/etc/clickhouse-server/config.d/cluster.xml
|
||||
- clickhouse-3:/var/lib/clickhouse/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.129.0}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
volumes:
|
||||
- sqlite:/var/lib/signoz/
|
||||
environment:
|
||||
- SIGNOZ_ALERTMANAGER_PROVIDER=signoz
|
||||
- SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_SQLSTORE_SQLITE_PATH=/var/lib/signoz/signoz.db
|
||||
- SIGNOZ_TOKENIZER_JWT_SECRET=secret
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- localhost:8080/api/v1/health
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
container_name: signoz-otel-collector
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate sync check &&
|
||||
/signoz-otel-collector --config=/etc/otel-collector-config.yaml --manager-config=/etc/manager-config.yaml --copy-path=/var/tmp/collector-config.yaml
|
||||
volumes:
|
||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||
- ../common/signoz/otel-collector-opamp-config.yaml:/etc/manager-config.yaml
|
||||
environment:
|
||||
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
|
||||
- LOW_CARDINAL_EXCEPTION_GROUPING=false
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
ports:
|
||||
# - "1777:1777" # pprof extension
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
container_name: signoz-telemetrystore-migrator
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate bootstrap &&
|
||||
/signoz-otel-collector migrate sync up &&
|
||||
/signoz-otel-collector migrate async up
|
||||
restart: on-failure
|
||||
networks:
|
||||
signoz-net:
|
||||
name: signoz-net
|
||||
volumes:
|
||||
clickhouse:
|
||||
name: signoz-clickhouse
|
||||
clickhouse-2:
|
||||
name: signoz-clickhouse-2
|
||||
clickhouse-3:
|
||||
name: signoz-clickhouse-3
|
||||
sqlite:
|
||||
name: signoz-sqlite
|
||||
zookeeper-1:
|
||||
name: signoz-zookeeper-1
|
||||
zookeeper-2:
|
||||
name: signoz-zookeeper-2
|
||||
zookeeper-3:
|
||||
name: signoz-zookeeper-3
|
||||
@@ -1,185 +0,0 @@
|
||||
version: "3"
|
||||
x-common: &common
|
||||
networks:
|
||||
- signoz-net
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
options:
|
||||
max-size: 50m
|
||||
max-file: "3"
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
!!merge <<: *common
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
tty: true
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9363"
|
||||
signoz.io/path: "/metrics"
|
||||
depends_on:
|
||||
init-clickhouse:
|
||||
condition: service_completed_successfully
|
||||
zookeeper-1:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- 0.0.0.0:8123/ping
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
ulimits:
|
||||
nproc: 65535
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
environment:
|
||||
- CLICKHOUSE_SKIP_USER_SETUP=1
|
||||
x-zookeeper-defaults: &zookeeper-defaults
|
||||
!!merge <<: *common
|
||||
image: signoz/zookeeper:3.7.1
|
||||
user: root
|
||||
labels:
|
||||
signoz.io/scrape: "true"
|
||||
signoz.io/port: "9141"
|
||||
signoz.io/path: "/metrics"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- curl -s -m 2 http://localhost:8080/commands/ruok | grep error | grep null
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
x-db-depend: &db-depend
|
||||
!!merge <<: *common
|
||||
depends_on:
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
services:
|
||||
init-clickhouse:
|
||||
!!merge <<: *common
|
||||
image: clickhouse/clickhouse-server:25.5.6
|
||||
container_name: signoz-init-clickhouse
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
version="v0.0.1"
|
||||
node_os=$$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
node_arch=$$(uname -m | sed s/aarch64/arm64/ | sed s/x86_64/amd64/)
|
||||
echo "Fetching histogram-binary for $${node_os}/$${node_arch}"
|
||||
cd /tmp
|
||||
wget -O histogram-quantile.tar.gz "https://github.com/SigNoz/signoz/releases/download/histogram-quantile%2F$${version}/histogram-quantile_$${node_os}_$${node_arch}.tar.gz"
|
||||
tar -xvzf histogram-quantile.tar.gz
|
||||
mv histogram-quantile /var/lib/clickhouse/user_scripts/histogramQuantile
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
zookeeper-1:
|
||||
!!merge <<: *zookeeper-defaults
|
||||
container_name: signoz-zookeeper-1
|
||||
# ports:
|
||||
# - "2181:2181"
|
||||
# - "2888:2888"
|
||||
# - "3888:3888"
|
||||
volumes:
|
||||
- zookeeper-1:/bitnami/zookeeper
|
||||
environment:
|
||||
- ZOO_SERVER_ID=1
|
||||
- ALLOW_ANONYMOUS_LOGIN=yes
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
- ZOO_ENABLE_PROMETHEUS_METRICS=yes
|
||||
- ZOO_PROMETHEUS_METRICS_PORT_NUMBER=9141
|
||||
clickhouse:
|
||||
!!merge <<: *clickhouse-defaults
|
||||
container_name: signoz-clickhouse
|
||||
# ports:
|
||||
# - "9000:9000"
|
||||
# - "8123:8123"
|
||||
# - "9181:9181"
|
||||
volumes:
|
||||
- ../common/clickhouse/config.xml:/etc/clickhouse-server/config.xml
|
||||
- ../common/clickhouse/users.xml:/etc/clickhouse-server/users.xml
|
||||
- ../common/clickhouse/custom-function.xml:/etc/clickhouse-server/custom-function.xml
|
||||
- ../common/clickhouse/user_scripts:/var/lib/clickhouse/user_scripts/
|
||||
- ../common/clickhouse/cluster.xml:/etc/clickhouse-server/config.d/cluster.xml
|
||||
- clickhouse:/var/lib/clickhouse/
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.129.0}
|
||||
container_name: signoz
|
||||
ports:
|
||||
- "8080:8080" # signoz port
|
||||
volumes:
|
||||
- sqlite:/var/lib/signoz/
|
||||
environment:
|
||||
- SIGNOZ_ALERTMANAGER_PROVIDER=signoz
|
||||
- SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_SQLSTORE_SQLITE_PATH=/var/lib/signoz/signoz.db
|
||||
- SIGNOZ_TOKENIZER_JWT_SECRET=secret
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --spider
|
||||
- -q
|
||||
- localhost:8080/api/v1/health
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
otel-collector:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
container_name: signoz-otel-collector
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate sync check &&
|
||||
/signoz-otel-collector --config=/etc/otel-collector-config.yaml --manager-config=/etc/manager-config.yaml --copy-path=/var/tmp/collector-config.yaml
|
||||
volumes:
|
||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||
- ../common/signoz/otel-collector-opamp-config.yaml:/etc/manager-config.yaml
|
||||
environment:
|
||||
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
|
||||
- LOW_CARDINAL_EXCEPTION_GROUPING=false
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
ports:
|
||||
# - "1777:1777" # pprof extension
|
||||
- "4317:4317" # OTLP gRPC receiver
|
||||
- "4318:4318" # OTLP HTTP receiver
|
||||
signoz-telemetrystore-migrator:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-v0.144.5}
|
||||
container_name: signoz-telemetrystore-migrator
|
||||
environment:
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_DSN=tcp://clickhouse:9000
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_CLUSTER=cluster
|
||||
- SIGNOZ_OTEL_COLLECTOR_CLICKHOUSE_REPLICATION=true
|
||||
- SIGNOZ_OTEL_COLLECTOR_TIMEOUT=10m
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
command:
|
||||
- -c
|
||||
- |
|
||||
/signoz-otel-collector migrate bootstrap &&
|
||||
/signoz-otel-collector migrate sync up &&
|
||||
/signoz-otel-collector migrate async up
|
||||
restart: on-failure
|
||||
networks:
|
||||
signoz-net:
|
||||
name: signoz-net
|
||||
volumes:
|
||||
clickhouse:
|
||||
name: signoz-clickhouse
|
||||
sqlite:
|
||||
name: signoz-sqlite
|
||||
zookeeper-1:
|
||||
name: signoz-zookeeper-1
|
||||
@@ -1,118 +0,0 @@
|
||||
connectors:
|
||||
signozmeter:
|
||||
metrics_flush_interval: 1h
|
||||
dimensions:
|
||||
- name: service.name
|
||||
- name: deployment.environment
|
||||
- name: host.name
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
prometheus:
|
||||
config:
|
||||
global:
|
||||
scrape_interval: 60s
|
||||
scrape_configs:
|
||||
- job_name: otel-collector
|
||||
static_configs:
|
||||
- targets:
|
||||
- localhost:8888
|
||||
labels:
|
||||
job_name: otel-collector
|
||||
processors:
|
||||
batch:
|
||||
send_batch_size: 10000
|
||||
send_batch_max_size: 11000
|
||||
timeout: 10s
|
||||
batch/meter:
|
||||
send_batch_max_size: 25000
|
||||
send_batch_size: 20000
|
||||
timeout: 1s
|
||||
resourcedetection:
|
||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||
detectors: [env, system]
|
||||
timeout: 2s
|
||||
signozspanmetrics/delta:
|
||||
metrics_exporter: signozclickhousemetrics
|
||||
metrics_flush_interval: 60s
|
||||
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
||||
dimensions_cache_size: 100000
|
||||
aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA
|
||||
enable_exp_histogram: true
|
||||
dimensions:
|
||||
- name: service.namespace
|
||||
default: default
|
||||
- name: deployment.environment
|
||||
default: default
|
||||
# This is added to ensure the uniqueness of the timeseries
|
||||
# Otherwise, identical timeseries produced by multiple replicas of
|
||||
# collectors result in incorrect APM metrics
|
||||
- name: signoz.collector.id
|
||||
- name: service.version
|
||||
- name: browser.platform
|
||||
- name: browser.mobile
|
||||
- name: k8s.cluster.name
|
||||
- name: k8s.node.name
|
||||
- name: k8s.namespace.name
|
||||
- name: host.name
|
||||
- name: host.type
|
||||
- name: container.name
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 0.0.0.0:13133
|
||||
pprof:
|
||||
endpoint: 0.0.0.0:1777
|
||||
exporters:
|
||||
clickhousetraces:
|
||||
datasource: tcp://clickhouse:9000/signoz_traces
|
||||
low_cardinal_exception_grouping: ${env:LOW_CARDINAL_EXCEPTION_GROUPING}
|
||||
use_new_schema: true
|
||||
signozclickhousemetrics:
|
||||
dsn: tcp://clickhouse:9000/signoz_metrics
|
||||
clickhouselogsexporter:
|
||||
dsn: tcp://clickhouse:9000/signoz_logs
|
||||
timeout: 10s
|
||||
use_new_schema: true
|
||||
signozclickhousemeter:
|
||||
dsn: tcp://clickhouse:9000/signoz_meter
|
||||
timeout: 45s
|
||||
sending_queue:
|
||||
enabled: false
|
||||
metadataexporter:
|
||||
cache:
|
||||
provider: in_memory
|
||||
dsn: tcp://clickhouse:9000/signoz_metadata
|
||||
enabled: true
|
||||
timeout: 45s
|
||||
service:
|
||||
telemetry:
|
||||
logs:
|
||||
encoding: json
|
||||
extensions:
|
||||
- health_check
|
||||
- pprof
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [signozspanmetrics/delta, batch]
|
||||
exporters: [clickhousetraces, metadataexporter, signozmeter]
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [signozclickhousemetrics, metadataexporter, signozmeter]
|
||||
metrics/prometheus:
|
||||
receivers: [prometheus]
|
||||
processors: [batch]
|
||||
exporters: [signozclickhousemetrics, metadataexporter, signozmeter]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [clickhouselogsexporter, metadataexporter, signozmeter]
|
||||
metrics/meter:
|
||||
receivers: [signozmeter]
|
||||
processors: [batch/meter]
|
||||
exporters: [signozclickhousemeter]
|
||||
@@ -2,562 +2,24 @@
|
||||
|
||||
set -o errexit
|
||||
|
||||
# Variables
|
||||
BASE_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
DOCKER_STANDALONE_DIR="docker"
|
||||
DOCKER_SWARM_DIR="docker-swarm" # TODO: Add docker swarm support
|
||||
|
||||
# Regular Colors
|
||||
Black='\033[0;30m' # Black
|
||||
Red='\[\e[0;31m\]' # Red
|
||||
Green='\033[0;32m' # Green
|
||||
Yellow='\033[0;33m' # Yellow
|
||||
Blue='\033[0;34m' # Blue
|
||||
Purple='\033[0;35m' # Purple
|
||||
Cyan='\033[0;36m' # Cyan
|
||||
White='\033[0;37m' # White
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
is_command_present() {
|
||||
type "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Check whether 'wget' command exists.
|
||||
has_wget() {
|
||||
has_cmd wget
|
||||
}
|
||||
|
||||
# Check whether 'curl' command exists.
|
||||
has_curl() {
|
||||
has_cmd curl
|
||||
}
|
||||
|
||||
# Check whether the given command exists.
|
||||
has_cmd() {
|
||||
command -v "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
# Check if docker compose plugin is present
|
||||
has_docker_compose_plugin() {
|
||||
docker compose version > /dev/null 2>&1
|
||||
}
|
||||
|
||||
is_mac() {
|
||||
[[ $OSTYPE == darwin* ]]
|
||||
}
|
||||
|
||||
is_arm64(){
|
||||
[[ `uname -m` == 'arm64' || `uname -m` == 'aarch64' ]]
|
||||
}
|
||||
|
||||
check_os() {
|
||||
if is_mac; then
|
||||
package_manager="brew"
|
||||
desired_os=1
|
||||
os="Mac"
|
||||
return
|
||||
fi
|
||||
|
||||
if is_arm64; then
|
||||
arch="arm64"
|
||||
arch_official="aarch64"
|
||||
else
|
||||
arch="amd64"
|
||||
arch_official="x86_64"
|
||||
fi
|
||||
|
||||
platform=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
os_name="$(cat /etc/*-release | awk -F= '$1 == "NAME" { gsub(/"/, ""); print $2; exit }')"
|
||||
|
||||
case "$os_name" in
|
||||
Ubuntu*|Pop!_OS)
|
||||
desired_os=1
|
||||
os="ubuntu"
|
||||
package_manager="apt-get"
|
||||
;;
|
||||
Amazon\ Linux*)
|
||||
desired_os=1
|
||||
os="amazon linux"
|
||||
package_manager="yum"
|
||||
;;
|
||||
Debian*)
|
||||
desired_os=1
|
||||
os="debian"
|
||||
package_manager="apt-get"
|
||||
;;
|
||||
Linux\ Mint*)
|
||||
desired_os=1
|
||||
os="linux mint"
|
||||
package_manager="apt-get"
|
||||
;;
|
||||
Red\ Hat*)
|
||||
desired_os=1
|
||||
os="rhel"
|
||||
package_manager="yum"
|
||||
;;
|
||||
CentOS*)
|
||||
desired_os=1
|
||||
os="centos"
|
||||
package_manager="yum"
|
||||
;;
|
||||
Rocky*)
|
||||
desired_os=1
|
||||
os="centos"
|
||||
package_manager="yum"
|
||||
;;
|
||||
SLES*)
|
||||
desired_os=1
|
||||
os="sles"
|
||||
package_manager="zypper"
|
||||
;;
|
||||
openSUSE*)
|
||||
desired_os=1
|
||||
os="opensuse"
|
||||
package_manager="zypper"
|
||||
;;
|
||||
*)
|
||||
desired_os=0
|
||||
os="Not Found: $os_name"
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
# This function checks if the relevant ports required by SigNoz are available or not
|
||||
# The script should error out in case they aren't available
|
||||
check_ports_occupied() {
|
||||
local port_check_output
|
||||
local ports_pattern="8080|4317"
|
||||
|
||||
if is_mac; then
|
||||
port_check_output="$(netstat -anp tcp | awk '$6 == "LISTEN" && $4 ~ /^.*\.('"$ports_pattern"')$/')"
|
||||
elif is_command_present ss; then
|
||||
# The `ss` command seems to be a better/faster version of `netstat`, but is not available on all Linux
|
||||
# distributions by default. Other distributions have `ss` but no `netstat`. So, we try for `ss` first, then
|
||||
# fallback to `netstat`.
|
||||
port_check_output="$(ss --all --numeric --tcp | awk '$1 == "LISTEN" && $4 ~ /^.*:('"$ports_pattern"')$/')"
|
||||
elif is_command_present netstat; then
|
||||
port_check_output="$(netstat --all --numeric --tcp | awk '$6 == "LISTEN" && $4 ~ /^.*:('"$ports_pattern"')$/')"
|
||||
fi
|
||||
|
||||
if [[ -n $port_check_output ]]; then
|
||||
send_event "port_not_available"
|
||||
|
||||
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
||||
echo "SigNoz requires ports 8080 & 4317 to be open. Please shut down any other service(s) that may be running on these ports."
|
||||
echo "You can run SigNoz on another port following this guide https://signoz.io/docs/install/troubleshooting/"
|
||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_docker() {
|
||||
echo "++++++++++++++++++++++++"
|
||||
echo "Setting up docker repos"
|
||||
|
||||
|
||||
if [[ $package_manager == apt-get ]]; then
|
||||
apt_cmd="$sudo_cmd apt-get --yes --quiet"
|
||||
$apt_cmd update
|
||||
$apt_cmd install software-properties-common gnupg-agent
|
||||
curl -fsSL "https://download.docker.com/linux/$os/gpg" | $sudo_cmd apt-key add -
|
||||
$sudo_cmd add-apt-repository \
|
||||
"deb [arch=$arch] https://download.docker.com/linux/$os $(lsb_release -cs) stable"
|
||||
$apt_cmd update
|
||||
echo "Installing docker"
|
||||
$apt_cmd install docker-ce docker-ce-cli containerd.io
|
||||
elif [[ $package_manager == zypper ]]; then
|
||||
zypper_cmd="$sudo_cmd zypper --quiet --no-gpg-checks --non-interactive"
|
||||
echo "Installing docker"
|
||||
if [[ $os == sles ]]; then
|
||||
os_sp="$(cat /etc/*-release | awk -F= '$1 == "VERSION_ID" { gsub(/"/, ""); print $2; exit }')"
|
||||
os_arch="$(uname -i)"
|
||||
SUSEConnect -p sle-module-containers/$os_sp/$os_arch -r ''
|
||||
fi
|
||||
$zypper_cmd install docker docker-runc containerd
|
||||
$sudo_cmd systemctl enable docker.service
|
||||
elif [[ $package_manager == yum && $os == 'amazon linux' ]]; then
|
||||
echo
|
||||
echo "Amazon Linux detected ... "
|
||||
echo
|
||||
# yum install docker
|
||||
# service docker start
|
||||
$sudo_cmd yum install -y amazon-linux-extras
|
||||
$sudo_cmd amazon-linux-extras enable docker
|
||||
$sudo_cmd yum install -y docker
|
||||
else
|
||||
|
||||
yum_cmd="$sudo_cmd yum --assumeyes --quiet"
|
||||
$yum_cmd install yum-utils
|
||||
$sudo_cmd yum-config-manager --add-repo https://download.docker.com/linux/$os/docker-ce.repo
|
||||
echo "Installing docker"
|
||||
$yum_cmd install docker-ce docker-ce-cli containerd.io
|
||||
fi
|
||||
}
|
||||
|
||||
compose_version () {
|
||||
local compose_version
|
||||
compose_version="$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)"
|
||||
echo "${compose_version:-v2.18.1}"
|
||||
}
|
||||
|
||||
install_docker_compose() {
|
||||
if [[ $package_manager == "apt-get" || $package_manager == "zypper" || $package_manager == "yum" ]]; then
|
||||
if [[ ! -f /usr/bin/docker-compose ]];then
|
||||
echo "++++++++++++++++++++++++"
|
||||
echo "Installing docker-compose"
|
||||
compose_url="https://github.com/docker/compose/releases/download/$(compose_version)/docker-compose-$platform-$arch_official"
|
||||
echo "Downloading docker-compose from $compose_url"
|
||||
$sudo_cmd curl -L "$compose_url" -o /usr/local/bin/docker-compose
|
||||
$sudo_cmd chmod +x /usr/local/bin/docker-compose
|
||||
$sudo_cmd ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||
echo "docker-compose installed!"
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
send_event "docker_compose_not_found"
|
||||
|
||||
echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++"
|
||||
echo "docker-compose not found! Please install docker-compose first and then continue with this installation."
|
||||
echo "Refer https://docs.docker.com/compose/install/ for installing docker-compose."
|
||||
echo "+++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
start_docker() {
|
||||
echo -e "🐳 Starting Docker ...\n"
|
||||
if [[ $os == "Mac" ]]; then
|
||||
open --background -a Docker && while ! docker system info > /dev/null 2>&1; do sleep 1; done
|
||||
else
|
||||
if ! $sudo_cmd systemctl is-active docker.service > /dev/null; then
|
||||
echo "Starting docker service"
|
||||
$sudo_cmd systemctl start docker.service
|
||||
fi
|
||||
if [[ -z $sudo_cmd ]]; then
|
||||
if ! docker ps > /dev/null && true; then
|
||||
request_sudo
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_containers_start() {
|
||||
local timeout=$1
|
||||
|
||||
# The while loop is important because for-loops don't work for dynamic values
|
||||
while [[ $timeout -gt 0 ]]; do
|
||||
status_code="$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:8080/api/v1/health?live=1" || true)"
|
||||
if [[ status_code -eq 200 ]]; then
|
||||
break
|
||||
else
|
||||
echo -ne "Waiting for all containers to start. This check will timeout in $timeout seconds ...\r\c"
|
||||
fi
|
||||
((timeout--))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
bye() { # Prints a friendly good bye message and exits the script.
|
||||
# Switch back to the original directory
|
||||
popd > /dev/null 2>&1
|
||||
if [[ "$?" -ne 0 ]]; then
|
||||
set +o errexit
|
||||
|
||||
echo "🔴 The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:"
|
||||
echo ""
|
||||
echo -e "cd ${DOCKER_STANDALONE_DIR}"
|
||||
echo -e "$sudo_cmd $docker_compose_cmd ps -a"
|
||||
|
||||
echo "Please read our troubleshooting guide https://signoz.io/docs/install/troubleshooting/"
|
||||
echo "or reach us for support in #help channel in our Slack Community https://signoz.io/slack"
|
||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||
|
||||
if [[ $email == "" ]]; then
|
||||
echo -e "\n📨 Please share your email to receive support with the installation"
|
||||
read -rp 'Email: ' email
|
||||
|
||||
while [[ $email == "" ]]
|
||||
do
|
||||
read -rp 'Email: ' email
|
||||
done
|
||||
fi
|
||||
|
||||
send_event "installation_support"
|
||||
|
||||
|
||||
echo ""
|
||||
echo -e "\nWe will reach out to you at the email provided shortly, Exiting for now. Bye! 👋 \n"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
request_sudo() {
|
||||
if hash sudo 2>/dev/null; then
|
||||
echo -e "\n\n🙇 We will need sudo access to complete the installation."
|
||||
if (( $EUID != 0 )); then
|
||||
sudo_cmd="sudo"
|
||||
echo -e "Please enter your sudo password, if prompted."
|
||||
if ! $sudo_cmd -l | grep -e "NOPASSWD: ALL" > /dev/null && ! $sudo_cmd -v; then
|
||||
echo "Need sudo privileges to proceed with the installation."
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
echo -e "Got it! Thanks!! 🙏\n"
|
||||
echo -e "Okay! We will bring up the SigNoz cluster from here 🚀\n"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
Yellow='\033[0;33m' # Yellow
|
||||
Green='\033[0;32m' # Green
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo ""
|
||||
echo -e "👋 Thank you for trying out SigNoz! "
|
||||
echo -e "👋 Thank you for trying out SigNoz!"
|
||||
echo ""
|
||||
echo -e "SigNoz now installs and runs through ${Green}Foundry${NC}."
|
||||
echo ""
|
||||
echo -e "${Yellow}⚠️ This install script has been deprecated and is no longer maintained.${NC}"
|
||||
echo -e "${Yellow}⚠️ Please see https://github.com/SigNoz/signoz/blob/main/deploy/README.md for new installation and migrations to Foundry.${NC}"
|
||||
echo ""
|
||||
echo ""
|
||||
echo -e "Please follow the latest installation instructions here:"
|
||||
echo -e "${Green}👉 https://signoz.io/docs/install/docker/${NC}"
|
||||
echo ""
|
||||
echo -e "🙏 Thank you!"
|
||||
echo ""
|
||||
|
||||
sudo_cmd=""
|
||||
docker_compose_cmd=""
|
||||
|
||||
# Check sudo permissions
|
||||
if (( $EUID != 0 )); then
|
||||
echo "🟡 Running installer with non-sudo permissions."
|
||||
echo " In case of any failure or prompt, please consider running the script with sudo privileges."
|
||||
echo ""
|
||||
else
|
||||
sudo_cmd="sudo"
|
||||
fi
|
||||
|
||||
# Checking OS and assigning package manager
|
||||
desired_os=0
|
||||
os=""
|
||||
email=""
|
||||
echo -e "🌏 Detecting your OS ...\n"
|
||||
check_os
|
||||
|
||||
# Obtain unique installation id
|
||||
# sysinfo="$(uname -a)"
|
||||
# if [[ $? -ne 0 ]]; then
|
||||
# uuid="$(uuidgen)"
|
||||
# uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
|
||||
# sysinfo="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
|
||||
# fi
|
||||
if ! sysinfo="$(uname -a)"; then
|
||||
uuid="$(uuidgen)"
|
||||
uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
|
||||
sysinfo="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
|
||||
fi
|
||||
|
||||
digest_cmd=""
|
||||
if hash shasum 2>/dev/null; then
|
||||
digest_cmd="shasum -a 256"
|
||||
elif hash sha256sum 2>/dev/null; then
|
||||
digest_cmd="sha256sum"
|
||||
elif hash openssl 2>/dev/null; then
|
||||
digest_cmd="openssl dgst -sha256"
|
||||
fi
|
||||
|
||||
if [[ -z $digest_cmd ]]; then
|
||||
SIGNOZ_INSTALLATION_ID="$sysinfo"
|
||||
else
|
||||
SIGNOZ_INSTALLATION_ID=$(echo "$sysinfo" | $digest_cmd | grep -E -o '[a-zA-Z0-9]{64}')
|
||||
fi
|
||||
|
||||
setup_type='clickhouse'
|
||||
|
||||
# Run bye if failure happens
|
||||
trap bye EXIT
|
||||
|
||||
URL="https://api.segment.io/v1/track"
|
||||
HEADER_1="Content-Type: application/json"
|
||||
HEADER_2="Authorization: Basic OWtScko3b1BDR1BFSkxGNlFqTVBMdDVibGpGaFJRQnI="
|
||||
|
||||
send_event() {
|
||||
error=""
|
||||
|
||||
case "$1" in
|
||||
'install_started')
|
||||
event="Installation Started"
|
||||
;;
|
||||
'os_not_supported')
|
||||
event="Installation Error"
|
||||
error="OS Not Supported"
|
||||
;;
|
||||
'docker_not_installed')
|
||||
event="Installation Error"
|
||||
error="Docker not installed"
|
||||
;;
|
||||
'docker_compose_not_found')
|
||||
event="Installation Error"
|
||||
event="Docker Compose not found"
|
||||
;;
|
||||
'port_not_available')
|
||||
event="Installation Error"
|
||||
error="port not available"
|
||||
;;
|
||||
'installation_error_checks')
|
||||
event="Installation Error - Checks"
|
||||
error="Containers not started"
|
||||
others='"data": "some_checks",'
|
||||
;;
|
||||
'installation_support')
|
||||
event="Installation Support"
|
||||
others='"email": "'"$email"'",'
|
||||
;;
|
||||
'installation_success')
|
||||
event="Installation Success"
|
||||
;;
|
||||
'identify_successful_installation')
|
||||
event="Identify Successful Installation"
|
||||
others='"email": "'"$email"'",'
|
||||
;;
|
||||
*)
|
||||
print_error "unknown event type: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$error" != "" ]]; then
|
||||
error='"error": "'"$error"'", '
|
||||
fi
|
||||
|
||||
DATA='{ "anonymousId": "'"$SIGNOZ_INSTALLATION_ID"'", "event": "'"$event"'", "properties": { "os": "'"$os"'", '"$error $others"' "setup_type": "'"$setup_type"'" } }'
|
||||
|
||||
if has_curl; then
|
||||
curl -sfL -d "$DATA" --header "$HEADER_1" --header "$HEADER_2" "$URL" > /dev/null 2>&1
|
||||
elif has_wget; then
|
||||
wget -q --post-data="$DATA" --header "$HEADER_1" --header "$HEADER_2" "$URL" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
send_event "install_started"
|
||||
|
||||
if [[ $desired_os -eq 0 ]]; then
|
||||
send_event "os_not_supported"
|
||||
fi
|
||||
|
||||
# Check is Docker daemon is installed and available. If not, the install & start Docker for Linux machines. We cannot automatically install Docker Desktop on Mac OS
|
||||
if ! is_command_present docker; then
|
||||
|
||||
if [[ $package_manager == "apt-get" || $package_manager == "zypper" || $package_manager == "yum" ]]; then
|
||||
request_sudo
|
||||
install_docker
|
||||
# enable docker without sudo from next reboot
|
||||
sudo usermod -aG docker "${USER}"
|
||||
elif is_mac; then
|
||||
echo ""
|
||||
echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++"
|
||||
echo "Docker Desktop must be installed manually on Mac OS to proceed. Docker can only be installed automatically on Ubuntu / openSUSE / SLES / Redhat / Cent OS"
|
||||
echo "https://docs.docker.com/docker-for-mac/install/"
|
||||
echo "++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||
|
||||
send_event "docker_not_installed"
|
||||
exit 1
|
||||
else
|
||||
echo ""
|
||||
echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++"
|
||||
echo "Docker must be installed manually on your machine to proceed. Docker can only be installed automatically on Ubuntu / openSUSE / SLES / Redhat / Cent OS"
|
||||
echo "https://docs.docker.com/get-docker/"
|
||||
echo "++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||
|
||||
send_event "docker_not_installed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if has_docker_compose_plugin; then
|
||||
echo "docker compose plugin is present, using it"
|
||||
docker_compose_cmd="docker compose"
|
||||
# Install docker-compose
|
||||
else
|
||||
docker_compose_cmd="docker-compose"
|
||||
if ! is_command_present docker-compose; then
|
||||
request_sudo
|
||||
install_docker_compose
|
||||
fi
|
||||
fi
|
||||
|
||||
start_docker
|
||||
|
||||
# Switch to the Docker Standalone directory
|
||||
pushd "${BASE_DIR}/${DOCKER_STANDALONE_DIR}" > /dev/null 2>&1
|
||||
|
||||
# check for open ports, if signoz is not installed
|
||||
if is_command_present docker-compose; then
|
||||
if $sudo_cmd $docker_compose_cmd ps | grep "signoz" | grep -q "healthy" > /dev/null 2>&1; then
|
||||
echo "SigNoz already installed, skipping the occupied ports check"
|
||||
else
|
||||
check_ports_occupied
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "\n🟡 Pulling the latest container images for SigNoz.\n"
|
||||
$sudo_cmd $docker_compose_cmd pull
|
||||
|
||||
echo ""
|
||||
echo "🟡 Starting the SigNoz containers. It may take a few minutes ..."
|
||||
echo
|
||||
# The $docker_compose_cmd command does some nasty stuff for the `--detach` functionality. So we add a `|| true` so that the
|
||||
# script doesn't exit because this command looks like it failed to do it's thing.
|
||||
$sudo_cmd $docker_compose_cmd up --detach --remove-orphans || true
|
||||
|
||||
wait_for_containers_start 60
|
||||
echo ""
|
||||
|
||||
if [[ $status_code -ne 200 ]]; then
|
||||
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
||||
echo "🔴 The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:"
|
||||
echo ""
|
||||
|
||||
echo "cd ${DOCKER_STANDALONE_DIR}"
|
||||
echo "$sudo_cmd $docker_compose_cmd ps -a"
|
||||
echo ""
|
||||
|
||||
echo "Try bringing down the containers and retrying the installation"
|
||||
echo "cd ${DOCKER_STANDALONE_DIR}"
|
||||
echo "$sudo_cmd $docker_compose_cmd down -v"
|
||||
echo ""
|
||||
|
||||
echo "Please read our troubleshooting guide https://signoz.io/docs/install/troubleshooting/"
|
||||
echo "or reach us on SigNoz for support https://signoz.io/slack"
|
||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||
|
||||
send_event "installation_error_checks"
|
||||
exit 1
|
||||
|
||||
else
|
||||
send_event "installation_success"
|
||||
|
||||
echo "++++++++++++++++++ SUCCESS ++++++++++++++++++++++"
|
||||
echo ""
|
||||
echo "🟢 Your installation is complete!"
|
||||
echo ""
|
||||
echo -e "🟢 SigNoz is running on http://localhost:8080"
|
||||
echo ""
|
||||
echo "ℹ️ By default, retention period is set to 15 days for logs and traces, and 30 days for metrics."
|
||||
echo -e "To change this, navigate to the General tab on the Settings page of SigNoz UI. For more details, refer to https://signoz.io/docs/userguide/retention-period \n"
|
||||
|
||||
echo "ℹ️ To bring down SigNoz and clean volumes:"
|
||||
echo ""
|
||||
echo "cd ${DOCKER_STANDALONE_DIR}"
|
||||
echo "$sudo_cmd $docker_compose_cmd down -v"
|
||||
|
||||
echo ""
|
||||
echo "+++++++++++++++++++++++++++++++++++++++++++++++++"
|
||||
echo ""
|
||||
echo "👉 Need help in Getting Started?"
|
||||
echo -e "Join us on Slack https://signoz.io/slack"
|
||||
echo ""
|
||||
echo -e "\n📨 Please share your email to receive support & updates about SigNoz!"
|
||||
read -rp 'Email: ' email
|
||||
|
||||
while [[ $email == "" ]]
|
||||
do
|
||||
read -rp 'Email: ' email
|
||||
done
|
||||
|
||||
send_event "identify_successful_installation"
|
||||
fi
|
||||
|
||||
echo -e "\n🙏 Thank you!\n"
|
||||
exit 0
|
||||
|
||||
1489
docs/api/openapi.yml
1489
docs/api/openapi.yml
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,55 @@ var (
|
||||
|
||||
> 💡 **Note**: Error codes must match the regex `^[a-z_]+$` otherwise the code will panic.
|
||||
|
||||
### Message
|
||||
The primary, human-readable summary of what went wrong, set when the error is created via `errors.New` / `errors.Newf`. Note there are two distinct `message` fields in the response: this top-level one states the overall failure, while each entry under [Additional](#additional) carries its own [message](#message-1) explaining one specific facet of it.
|
||||
|
||||
### Url
|
||||
An optional link to documentation that explains the error in more depth, set with `WithUrl`. It is left empty when the error has no associated doc.
|
||||
|
||||
```go
|
||||
return errors.New(errors.TypeInvalidInput, CodeBadThing, "bad thing").
|
||||
WithUrl("https://signoz.io/docs/...")
|
||||
```
|
||||
|
||||
### Additional
|
||||
`errors` is a list of supplementary details that explain the top-level `message`. Each entry has its own `message` and `suggestions`, so a single error can surface several distinct problems individually. Attach details with `WithAdditional` (message only) or `WithSuggestiveAdditional` (message plus the suggestions that belong to it):
|
||||
|
||||
#### Message
|
||||
A single, self-contained sentence describing one specific facet of the error (e.g. ``field `filed` not found``), distinct from the top-level [Message](#message). Prefer one detail per distinct problem over concatenating several into one message.
|
||||
|
||||
#### Suggestions
|
||||
The suggestions tied to that specific detail — typically a ``did you mean: `x` `` correction for the value the detail is about. These are distinct from the error-wide [Suggestions](#suggestions) below: detail-scoped suggestions never leak into the top-level list.
|
||||
|
||||
```go
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "unknown field %q", field).
|
||||
WithAdditional("field `field` not found")
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "unknown field %q", field).
|
||||
WithSuggestiveAdditional("field `filed` not found", "did you mean: `field`")
|
||||
```
|
||||
|
||||
### Retry
|
||||
Carries the `delay` the client should wait before retrying, set with `WithRetryAfter`. It is `null` when the error is not retryable.
|
||||
|
||||
```go
|
||||
return errors.NewTimeoutf(CodeSlow, "upstream timed out").
|
||||
WithRetryAfter(5 * time.Second)
|
||||
```
|
||||
|
||||
### Suggestions
|
||||
`WithSuggestions` sets the error-wide `suggestions` list — hints about the error as a whole (e.g. "narrow the time range window"), as opposed to suggestions tied to a single detail. Prefer the builders in [pkg/errors/suggestions.go](/pkg/errors/suggestions.go) over hand-writing the strings so the phrasing stays consistent:
|
||||
|
||||
- `NewSuggestionsOnLevenshteinDistance(invalidInput, noun, validInputs)` — returns a ``did you mean: `x` `` correction (when a close typo match exists) followed by the valid-references list.
|
||||
- `NewValidReferences(noun, values...)` — formats a capped list as ``valid <noun> are `a`, `b` `` (e.g. `"valid fields are"`, `"valid keys are"`). Returns `""` for an empty set.
|
||||
- `NewSuggestionsFromFunc(produce)` — wraps a caller-computed correction string as a one-element ``did you mean: `x` `` slice (or nil when it returns `""`), for callers with their own matching strategy.
|
||||
|
||||
`noun` names the kind of value being suggested. Use one of the exported `Noun*` constants (`errors.NounFields`, `errors.NounKeys`, `errors.NounServices`, …) so the wording stays uniform across the codebase.
|
||||
|
||||
```go
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "unknown field %q", field).
|
||||
WithSuggestions(errors.NewSuggestionsOnLevenshteinDistance(field, errors.NounFields, validFields)...)
|
||||
```
|
||||
|
||||
## Show me some examples
|
||||
|
||||
### Using the error
|
||||
|
||||
@@ -143,6 +143,10 @@ func (provider *provider) List(ctx context.Context, orgID valuer.UUID) ([]*autht
|
||||
return provider.pkgAuthzService.List(ctx, orgID)
|
||||
}
|
||||
|
||||
func (provider *provider) Collect(ctx context.Context, orgID valuer.UUID) (map[string]any, error) {
|
||||
return provider.pkgAuthzService.Collect(ctx, orgID)
|
||||
}
|
||||
|
||||
func (provider *provider) ListByOrgIDAndNames(ctx context.Context, orgID valuer.UUID, names []string) ([]*authtypes.Role, error) {
|
||||
return provider.pkgAuthzService.ListByOrgIDAndNames(ctx, orgID, names)
|
||||
}
|
||||
@@ -370,7 +374,7 @@ func (provider *provider) Delete(ctx context.Context, orgID valuer.UUID, id valu
|
||||
}
|
||||
|
||||
for _, cb := range provider.onBeforeRoleDelete {
|
||||
if err := cb(ctx, orgID, id); err != nil {
|
||||
if err := cb(ctx, orgID, id, role.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,565 @@
|
||||
package implmetricreductionrule
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
sqlbuilder "github.com/huandu/go-sqlbuilder"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metricreductionruletypes"
|
||||
)
|
||||
|
||||
var (
|
||||
reductionRulesTable = telemetrymetrics.DBName + "." + telemetrymetrics.ReductionRulesTableName
|
||||
metadataTable = telemetrymetrics.DBName + "." + telemetrymetrics.AttributesMetadataTableName
|
||||
bufferSeriesTable = telemetrymetrics.DBName + "." + telemetrymetrics.TimeseriesV4BufferTableName
|
||||
)
|
||||
|
||||
const timeSeriesBucketMilli = int64(time.Hour / time.Millisecond)
|
||||
|
||||
type volumeRow struct {
|
||||
MetricName string
|
||||
Ingested uint64
|
||||
Reduced uint64
|
||||
}
|
||||
|
||||
type volumePoint struct {
|
||||
TimestampMs int64
|
||||
Ingested uint64
|
||||
Reduced uint64
|
||||
}
|
||||
|
||||
type clickhouse struct {
|
||||
telemetryStore telemetrystore.TelemetryStore
|
||||
threads int
|
||||
}
|
||||
|
||||
func newClickhouse(telemetryStore telemetrystore.TelemetryStore, threads int) *clickhouse {
|
||||
return &clickhouse{telemetryStore: telemetryStore, threads: threads}
|
||||
}
|
||||
|
||||
func (c *clickhouse) withThreads(ctx context.Context) context.Context {
|
||||
return ctxtypes.SetClickhouseMaxThreads(ctx, c.threads)
|
||||
}
|
||||
|
||||
func floorToTimeSeriesBucket(ms int64) int64 {
|
||||
return ms - (ms % timeSeriesBucketMilli)
|
||||
}
|
||||
|
||||
func strictEffectiveFrom(sb *sqlbuilder.SelectBuilder, metricNames []string, effectiveFrom map[string]int64) string {
|
||||
names := make([]any, 0, len(metricNames))
|
||||
froms := make([]any, 0, len(metricNames))
|
||||
for _, name := range metricNames {
|
||||
names = append(names, name)
|
||||
froms = append(froms, effectiveFrom[name])
|
||||
}
|
||||
return "unix_milli >= transform(metric_name, " + sb.Var(names) + ", " + sb.Var(froms) + ", 0)"
|
||||
}
|
||||
|
||||
func (c *clickhouse) Sync(ctx context.Context, metricName string, labels []string, matchType string, effectiveFromMs int64, deleted bool, updatedAt time.Time) error {
|
||||
ctx = c.withThreads(ctx)
|
||||
|
||||
ib := sqlbuilder.NewInsertBuilder()
|
||||
ib.InsertInto(reductionRulesTable)
|
||||
ib.Cols("metric_name", "labels", "match_type", "effective_from_unix_milli", "deleted", "updated_at")
|
||||
ib.Values(metricName, labels, matchType, effectiveFromMs, deleted, updatedAt)
|
||||
|
||||
query, args := ib.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
if err := c.telemetryStore.ClickhouseDB().Exec(ctx, query, args...); err != nil {
|
||||
return errors.WrapInternalf(err, errors.CodeInternal, "failed to sync reduction rule to clickhouse")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clickhouse) AttributeKeys(ctx context.Context, metricName string, startMs, endMs int64) ([]string, error) {
|
||||
ctx = c.withThreads(ctx)
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("attr_name")
|
||||
sb.Distinct()
|
||||
sb.From(metadataTable)
|
||||
sb.Where(
|
||||
sb.E("metric_name", metricName),
|
||||
"NOT startsWith(attr_name, '__')",
|
||||
sb.GE("last_reported_unix_milli", startMs),
|
||||
sb.LE("first_reported_unix_milli", endMs),
|
||||
)
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
rows, err := c.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to fetch metric attribute keys")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
keys := make([]string, 0)
|
||||
for rows.Next() {
|
||||
var key string
|
||||
if err := rows.Scan(&key); err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to scan attribute key")
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys, rows.Err()
|
||||
}
|
||||
|
||||
func (c *clickhouse) EstimateCardinality(ctx context.Context, metricName string, keptLabels []string, startMs, endMs int64) (uint64, uint64, error) {
|
||||
ctx = c.withThreads(ctx)
|
||||
startMs = floorToTimeSeriesBucket(startMs)
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
|
||||
reducedExpr := "1"
|
||||
if len(keptLabels) > 0 {
|
||||
reducedExpr = "uniq(("
|
||||
for i, label := range keptLabels {
|
||||
if i > 0 {
|
||||
reducedExpr += ", "
|
||||
}
|
||||
reducedExpr += "JSONExtractString(labels, " + sb.Var(label) + ")"
|
||||
}
|
||||
reducedExpr += "))"
|
||||
}
|
||||
|
||||
sb.Select("uniq(fingerprint)", reducedExpr)
|
||||
sb.From(bufferSeriesTable)
|
||||
conds := []string{
|
||||
sb.E("metric_name", metricName),
|
||||
sb.GE("unix_milli", startMs),
|
||||
sb.LT("unix_milli", endMs),
|
||||
sb.E("is_reduced", false),
|
||||
}
|
||||
sb.Where(conds...)
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
var current, reduced uint64
|
||||
if err := c.telemetryStore.ClickhouseDB().QueryRow(ctx, query, args...).Scan(¤t, &reduced); err != nil {
|
||||
return 0, 0, errors.WrapInternalf(err, errors.CodeInternal, "failed to estimate reduction impact")
|
||||
}
|
||||
if len(keptLabels) == 0 && current == 0 {
|
||||
reduced = 0
|
||||
}
|
||||
if reduced > current {
|
||||
reduced = current
|
||||
}
|
||||
return current, reduced, nil
|
||||
}
|
||||
|
||||
// VolumeByMetric returns ingested vs reduced series counts per metric.
|
||||
func (c *clickhouse) VolumeByMetric(ctx context.Context, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (map[string]volumeRow, error) {
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]volumeRow{}, nil
|
||||
}
|
||||
ctx = c.withThreads(ctx)
|
||||
|
||||
ingested, err := c.ingestedSeriesCount(ctx, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reduced, err := c.reducedSeriesCount(ctx, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]volumeRow, len(metricNames))
|
||||
for metricName, count := range ingested {
|
||||
out[metricName] = volumeRow{MetricName: metricName, Ingested: count, Reduced: out[metricName].Reduced}
|
||||
}
|
||||
for metricName, count := range reduced {
|
||||
row := out[metricName]
|
||||
row.MetricName = metricName
|
||||
row.Reduced = count
|
||||
out[metricName] = row
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ingestedSeriesCount counts distinct raw fingerprints per metric from the samples buffer over the
|
||||
// window.
|
||||
func (c *clickhouse) ingestedSeriesCount(ctx context.Context, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (map[string]uint64, error) {
|
||||
names := make([]any, len(metricNames))
|
||||
for i, name := range metricNames {
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("metric_name", "uniq(fingerprint)")
|
||||
sb.From(telemetrymetrics.DBName + "." + telemetrymetrics.SamplesV4BufferTableName)
|
||||
conds := []string{
|
||||
sb.In("metric_name", names...),
|
||||
sb.GE("unix_milli", startMs),
|
||||
sb.LT("unix_milli", endMs),
|
||||
}
|
||||
if len(effectiveFrom) > 0 {
|
||||
conds = append(conds, strictEffectiveFrom(sb, metricNames, effectiveFrom))
|
||||
}
|
||||
sb.Where(conds...)
|
||||
sb.GroupBy("metric_name")
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
rows, err := c.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to count ingested series")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[string]uint64, len(metricNames))
|
||||
for rows.Next() {
|
||||
var (
|
||||
metricName string
|
||||
count uint64
|
||||
)
|
||||
if err := rows.Scan(&metricName, &count); err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to scan series count")
|
||||
}
|
||||
out[metricName] = count
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// reducedSeriesCount counts distinct reduced_fingerprints per metric, summed across the two 60s
|
||||
// reduced sample tables.
|
||||
func (c *clickhouse) reducedSeriesCount(ctx context.Context, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (map[string]uint64, error) {
|
||||
out := make(map[string]uint64, len(metricNames))
|
||||
for _, table := range []string{telemetrymetrics.SamplesV4ReducedLastTableName, telemetrymetrics.SamplesV4ReducedSumTableName} {
|
||||
counts, err := c.reducedSeriesCountForTable(ctx, telemetrymetrics.DBName+"."+table, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for metricName, count := range counts {
|
||||
out[metricName] += count
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clickhouse) reducedSeriesCountForTable(ctx context.Context, table string, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (map[string]uint64, error) {
|
||||
names := make([]any, len(metricNames))
|
||||
for i, name := range metricNames {
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("metric_name", "uniq(reduced_fingerprint)")
|
||||
sb.From(table)
|
||||
conds := []string{
|
||||
sb.In("metric_name", names...),
|
||||
sb.GE("unix_milli", startMs),
|
||||
sb.LT("unix_milli", endMs),
|
||||
}
|
||||
if len(effectiveFrom) > 0 {
|
||||
conds = append(conds, strictEffectiveFrom(sb, metricNames, effectiveFrom))
|
||||
}
|
||||
sb.Where(conds...)
|
||||
sb.GroupBy("metric_name")
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
rows, err := c.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to count reduced series")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[string]uint64, len(metricNames))
|
||||
for rows.Next() {
|
||||
var (
|
||||
metricName string
|
||||
count uint64
|
||||
)
|
||||
if err := rows.Scan(&metricName, &count); err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to scan series count")
|
||||
}
|
||||
out[metricName] = count
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// RankByVolume ranks metrics by ingested/reduced series volume. Like VolumeByMetric, the counts read
|
||||
// the samples tables with a strict effective_from gate; the reduced count sums distinct
|
||||
// reduced_fingerprints across the two 60s reduced sample tables.
|
||||
func (c *clickhouse) RankByVolume(ctx context.Context, metricNames []string, effectiveFrom map[string]int64, orderBy metricreductionruletypes.ReductionRuleOrderBy, order metricreductionruletypes.Order, startMs, endMs int64, offset, limit int) ([]volumeRow, error) {
|
||||
if len(metricNames) == 0 {
|
||||
return []volumeRow{}, nil
|
||||
}
|
||||
ctx = c.withThreads(ctx)
|
||||
|
||||
orderExpr := "ingested"
|
||||
switch orderBy {
|
||||
case metricreductionruletypes.OrderByReducedVolume:
|
||||
orderExpr = "reduced"
|
||||
case metricreductionruletypes.OrderByReduction:
|
||||
orderExpr = "if(ingested = 0, 0, (toFloat64(ingested) - toFloat64(reduced)) / toFloat64(ingested))"
|
||||
}
|
||||
direction := "ASC"
|
||||
if order == metricreductionruletypes.OrderDesc {
|
||||
direction = "DESC"
|
||||
}
|
||||
|
||||
ingestedTable := telemetrymetrics.DBName + "." + telemetrymetrics.SamplesV4BufferTableName
|
||||
reducedLast := telemetrymetrics.DBName + "." + telemetrymetrics.SamplesV4ReducedLastTableName
|
||||
reducedSum := telemetrymetrics.DBName + "." + telemetrymetrics.SamplesV4ReducedSumTableName
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("base.metric_name AS metric_name", "ifNull(i.cnt, 0) AS ingested", "ifNull(d.cnt, 0) AS reduced")
|
||||
sb.From("(SELECT arrayJoin(" + sb.Var(metricNames) + ") AS metric_name) AS base")
|
||||
sb.JoinWithOption(
|
||||
sqlbuilder.LeftJoin,
|
||||
"(SELECT metric_name, uniq(fingerprint) AS cnt FROM "+ingestedTable+" WHERE has("+sb.Var(metricNames)+", metric_name) AND unix_milli >= "+sb.Var(startMs)+" AND unix_milli < "+sb.Var(endMs)+" AND "+strictEffectiveFrom(sb, metricNames, effectiveFrom)+" GROUP BY metric_name) AS i",
|
||||
"base.metric_name = i.metric_name",
|
||||
)
|
||||
// Reduced series are spread across two type-specific tables; union the per-table distinct
|
||||
// reduced_fingerprints and sum per metric (a metric only lands in the table matching its type).
|
||||
sb.JoinWithOption(
|
||||
sqlbuilder.LeftJoin,
|
||||
"(SELECT metric_name, sum(cnt) AS cnt FROM ("+
|
||||
"SELECT metric_name, uniq(reduced_fingerprint) AS cnt FROM "+reducedLast+" WHERE has("+sb.Var(metricNames)+", metric_name) AND unix_milli >= "+sb.Var(startMs)+" AND unix_milli < "+sb.Var(endMs)+" AND "+strictEffectiveFrom(sb, metricNames, effectiveFrom)+" GROUP BY metric_name"+
|
||||
" UNION ALL "+
|
||||
"SELECT metric_name, uniq(reduced_fingerprint) AS cnt FROM "+reducedSum+" WHERE has("+sb.Var(metricNames)+", metric_name) AND unix_milli >= "+sb.Var(startMs)+" AND unix_milli < "+sb.Var(endMs)+" AND "+strictEffectiveFrom(sb, metricNames, effectiveFrom)+" GROUP BY metric_name"+
|
||||
") GROUP BY metric_name) AS d",
|
||||
"base.metric_name = d.metric_name",
|
||||
)
|
||||
sb.OrderBy(orderExpr + " " + direction)
|
||||
if limit > 0 {
|
||||
sb.Limit(limit).Offset(offset)
|
||||
}
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
rows, err := c.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to rank reduction rules by volume")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make([]volumeRow, 0, len(metricNames))
|
||||
for rows.Next() {
|
||||
var row volumeRow
|
||||
if err := rows.Scan(&row.MetricName, &row.Ingested, &row.Reduced); err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to scan volume row")
|
||||
}
|
||||
out = append(out, row)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (c *clickhouse) SampleVolume(ctx context.Context, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (uint64, uint64, error) {
|
||||
if len(metricNames) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
ctx = c.withThreads(ctx)
|
||||
|
||||
ingested, err := c.countRawSamples(ctx, telemetrymetrics.DBName+"."+telemetrymetrics.SamplesV4BufferTableName, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
last, err := c.countReducedSamples(ctx, telemetrymetrics.DBName+"."+telemetrymetrics.SamplesV4ReducedLastTableName, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
sum, err := c.countReducedSamples(ctx, telemetrymetrics.DBName+"."+telemetrymetrics.SamplesV4ReducedSumTableName, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return ingested, min(last+sum, ingested), nil
|
||||
}
|
||||
|
||||
func (c *clickhouse) countRawSamples(ctx context.Context, table string, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (uint64, error) {
|
||||
names := make([]any, len(metricNames))
|
||||
for i, name := range metricNames {
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select("count()")
|
||||
sb.From(table)
|
||||
conds := []string{sb.In("metric_name", names...), sb.GE("unix_milli", startMs), sb.LT("unix_milli", endMs)}
|
||||
if len(effectiveFrom) > 0 {
|
||||
conds = append(conds, strictEffectiveFrom(sb, metricNames, effectiveFrom))
|
||||
}
|
||||
sb.Where(conds...)
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
var count uint64
|
||||
if err := c.telemetryStore.ClickhouseDB().QueryRow(ctx, query, args...).Scan(&count); err != nil {
|
||||
return 0, errors.WrapInternalf(err, errors.CodeInternal, "failed to count ingested samples")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (c *clickhouse) countReducedSamples(ctx context.Context, table string, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (uint64, error) {
|
||||
names := make([]any, len(metricNames))
|
||||
for i, name := range metricNames {
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
// Reduced tables key the series on reduced_fingerprint (not fingerprint); dedupe ReplacingMergeTree recomputes.
|
||||
sb.Select("uniq(reduced_fingerprint, unix_milli)")
|
||||
sb.From(table)
|
||||
conds := []string{sb.In("metric_name", names...), sb.GE("unix_milli", startMs), sb.LT("unix_milli", endMs)}
|
||||
if len(effectiveFrom) > 0 {
|
||||
conds = append(conds, strictEffectiveFrom(sb, metricNames, effectiveFrom))
|
||||
}
|
||||
sb.Where(conds...)
|
||||
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
var count uint64
|
||||
if err := c.telemetryStore.ClickhouseDB().QueryRow(ctx, query, args...).Scan(&count); err != nil {
|
||||
return 0, errors.WrapInternalf(err, errors.CodeInternal, "failed to count reduced samples")
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// SeriesTimeseries returns ingested vs reduced series per 60s bucket from the samples tables, gated
|
||||
// to each metric's strict effective_from (see strictEffectiveFrom).
|
||||
func (c *clickhouse) SeriesTimeseries(ctx context.Context, allMetrics, reducedMetrics []string, effectiveFrom map[string]int64, startMs, endMs int64) ([]volumePoint, error) {
|
||||
if len(allMetrics) == 0 {
|
||||
return []volumePoint{}, nil
|
||||
}
|
||||
ctx = c.withThreads(ctx)
|
||||
|
||||
ingested, err := c.ingestedSeriesByBucket(ctx, allMetrics, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
retained := make(map[int64]uint64)
|
||||
if len(reducedMetrics) > 0 {
|
||||
reduced, err := c.reducedSeriesByBucket(ctx, reducedMetrics, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for ts, count := range reduced {
|
||||
retained[ts] += count
|
||||
}
|
||||
}
|
||||
reducedSet := make(map[string]struct{}, len(reducedMetrics))
|
||||
for _, name := range reducedMetrics {
|
||||
reducedSet[name] = struct{}{}
|
||||
}
|
||||
nonReduced := make([]string, 0, len(allMetrics))
|
||||
for _, name := range allMetrics {
|
||||
if _, ok := reducedSet[name]; !ok {
|
||||
nonReduced = append(nonReduced, name)
|
||||
}
|
||||
}
|
||||
if len(nonReduced) > 0 {
|
||||
nonReducedIngested, err := c.ingestedSeriesByBucket(ctx, nonReduced, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for ts, count := range nonReducedIngested {
|
||||
retained[ts] += count
|
||||
}
|
||||
}
|
||||
|
||||
return mergeVolumePoints(ingested, retained), nil
|
||||
}
|
||||
|
||||
func mergeVolumePoints(ingested, reduced map[int64]uint64) []volumePoint {
|
||||
buckets := make(map[int64]struct{}, len(ingested))
|
||||
for ts := range ingested {
|
||||
buckets[ts] = struct{}{}
|
||||
}
|
||||
for ts := range reduced {
|
||||
buckets[ts] = struct{}{}
|
||||
}
|
||||
timestamps := make([]int64, 0, len(buckets))
|
||||
for ts := range buckets {
|
||||
timestamps = append(timestamps, ts)
|
||||
}
|
||||
slices.Sort(timestamps)
|
||||
|
||||
points := make([]volumePoint, 0, len(timestamps))
|
||||
for _, ts := range timestamps {
|
||||
points = append(points, volumePoint{
|
||||
TimestampMs: ts,
|
||||
Ingested: ingested[ts],
|
||||
Reduced: reduced[ts],
|
||||
})
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
// ingestedSeriesByBucket counts distinct raw fingerprints per hourly bucket from the samples buffer.
|
||||
func (c *clickhouse) ingestedSeriesByBucket(ctx context.Context, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (map[int64]uint64, error) {
|
||||
names := make([]any, len(metricNames))
|
||||
for i, name := range metricNames {
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
bucketExpr := "toInt64(toUnixTimestamp(toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), toIntervalHour(1)))) * 1000 AS bucket"
|
||||
sb.Select(bucketExpr, "uniq(fingerprint)")
|
||||
sb.From(telemetrymetrics.DBName + "." + telemetrymetrics.SamplesV4BufferTableName)
|
||||
conds := []string{sb.In("metric_name", names...), sb.GE("unix_milli", startMs), sb.LT("unix_milli", endMs)}
|
||||
if len(effectiveFrom) > 0 {
|
||||
conds = append(conds, strictEffectiveFrom(sb, metricNames, effectiveFrom))
|
||||
}
|
||||
sb.Where(conds...)
|
||||
sb.GroupBy("bucket")
|
||||
|
||||
return c.scanBuckets(ctx, sb)
|
||||
}
|
||||
|
||||
// reducedSeriesByBucket counts distinct reduced_fingerprints per hourly bucket, summed across the two
|
||||
// reduced sample tables (a metric only lands in the table matching its type, so per-bucket sums are
|
||||
// exact).
|
||||
func (c *clickhouse) reducedSeriesByBucket(ctx context.Context, metricNames []string, effectiveFrom map[string]int64, startMs, endMs int64) (map[int64]uint64, error) {
|
||||
out := make(map[int64]uint64)
|
||||
for _, table := range []string{telemetrymetrics.SamplesV4ReducedLastTableName, telemetrymetrics.SamplesV4ReducedSumTableName} {
|
||||
names := make([]any, len(metricNames))
|
||||
for i, name := range metricNames {
|
||||
names[i] = name
|
||||
}
|
||||
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
bucketExpr := "toInt64(toUnixTimestamp(toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), toIntervalHour(1)))) * 1000 AS bucket"
|
||||
sb.Select(bucketExpr, "uniq(reduced_fingerprint)")
|
||||
sb.From(telemetrymetrics.DBName + "." + table)
|
||||
conds := []string{sb.In("metric_name", names...), sb.GE("unix_milli", startMs), sb.LT("unix_milli", endMs)}
|
||||
if len(effectiveFrom) > 0 {
|
||||
conds = append(conds, strictEffectiveFrom(sb, metricNames, effectiveFrom))
|
||||
}
|
||||
sb.Where(conds...)
|
||||
sb.GroupBy("bucket")
|
||||
|
||||
counts, err := c.scanBuckets(ctx, sb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for ts, count := range counts {
|
||||
out[ts] += count
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clickhouse) scanBuckets(ctx context.Context, sb *sqlbuilder.SelectBuilder) (map[int64]uint64, error) {
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
rows, err := c.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to bucket series by time")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make(map[int64]uint64)
|
||||
for rows.Next() {
|
||||
var (
|
||||
ts int64
|
||||
count uint64
|
||||
)
|
||||
if err := rows.Scan(&ts, &count); err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to scan series bucket")
|
||||
}
|
||||
out[ts] = count
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
571
ee/modules/metricreductionrule/implmetricreductionrule/module.go
Normal file
571
ee/modules/metricreductionrule/implmetricreductionrule/module.go
Normal file
@@ -0,0 +1,571 @@
|
||||
package implmetricreductionrule
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/flagger"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/modules/dashboard"
|
||||
"github.com/SigNoz/signoz/pkg/modules/metricreductionrule"
|
||||
"github.com/SigNoz/signoz/pkg/queryparser"
|
||||
"github.com/SigNoz/signoz/pkg/ruler/rulestore/sqlrulestore"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metricreductionruletypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/ruletypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
const (
|
||||
// effectiveFromMargin delays effective_from so the collector picks up the synced rule before it
|
||||
// goes live; it must be >= the collector's rule-refresh interval (see signoz-otel-collector#839).
|
||||
effectiveFromMargin = 5 * time.Minute
|
||||
defaultPreviewLookback = 24 * time.Hour
|
||||
|
||||
pricePerMillionSamplesUSD = 0.1
|
||||
monthDuration = 30 * 24 * time.Hour
|
||||
)
|
||||
|
||||
type module struct {
|
||||
store metricreductionruletypes.Store
|
||||
ch *clickhouse
|
||||
dashboard dashboard.Module
|
||||
ruleStore ruletypes.RuleStore
|
||||
licensing licensing.Licensing
|
||||
flagger flagger.Flagger
|
||||
metadataStore telemetrytypes.MetadataStore
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewModule(sqlStore sqlstore.SQLStore, telemetryStore telemetrystore.TelemetryStore, dashboardModule dashboard.Module, queryParser queryparser.QueryParser, licensing licensing.Licensing, flagger flagger.Flagger, metadataStore telemetrytypes.MetadataStore, providerSettings factory.ProviderSettings, threads int) metricreductionrule.Module {
|
||||
scoped := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/modules/metricreductionrule/implmetricreductionrule")
|
||||
return &module{
|
||||
store: NewStore(sqlStore),
|
||||
ch: newClickhouse(telemetryStore, threads),
|
||||
dashboard: dashboardModule,
|
||||
ruleStore: sqlrulestore.NewRuleStore(sqlStore, queryParser, providerSettings),
|
||||
licensing: licensing,
|
||||
flagger: flagger,
|
||||
metadataStore: metadataStore,
|
||||
logger: scoped.Logger(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *module) checkAccess(ctx context.Context, orgID valuer.UUID) error {
|
||||
if !m.flagger.BooleanOrEmpty(ctx, flagger.FeatureEnableMetricsReduction, featuretypes.NewFlaggerEvaluationContext(orgID)) {
|
||||
return errors.Newf(errors.TypeUnsupported, metricreductionruletypes.ErrCodeMetricReductionRuleUnsupported, "metric volume control is not enabled")
|
||||
}
|
||||
if _, err := m.licensing.GetActive(ctx, orgID); err != nil {
|
||||
return errors.New(errors.TypeLicenseUnavailable, errors.CodeLicenseUnavailable, "metric volume control requires a valid license").WithAdditional(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *module) List(ctx context.Context, orgID valuer.UUID, params *metricreductionruletypes.ListReductionRulesParams) (*metricreductionruletypes.GettableReductionRules, error) {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := params.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
startMs := now.Add(-defaultPreviewLookback).UnixMilli()
|
||||
endMs := now.UnixMilli()
|
||||
|
||||
switch params.OrderBy {
|
||||
case metricreductionruletypes.OrderByMetricName, metricreductionruletypes.OrderByLastUpdated:
|
||||
return m.listSortedByColumn(ctx, orgID, params, startMs, endMs)
|
||||
default:
|
||||
return m.listSortedByVolume(ctx, orgID, params, startMs, endMs)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *module) listSortedByColumn(ctx context.Context, orgID valuer.UUID, params *metricreductionruletypes.ListReductionRulesParams, startMs, endMs int64) (*metricreductionruletypes.GettableReductionRules, error) {
|
||||
domainRules, total, err := m.store.List(ctx, orgID, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metricNames := make([]string, len(domainRules))
|
||||
effectiveFrom := make(map[string]int64, len(domainRules))
|
||||
for i, rule := range domainRules {
|
||||
metricNames[i] = rule.MetricName
|
||||
effectiveFrom[rule.MetricName] = rule.EffectiveFrom.UnixMilli()
|
||||
}
|
||||
volumes, err := m.ch.VolumeByMetric(ctx, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]metricreductionruletypes.GettableReductionRule, 0, len(domainRules))
|
||||
for _, rule := range domainRules {
|
||||
rules = append(rules, withVolume(toGettableReductionRule(rule), volumes[rule.MetricName]))
|
||||
}
|
||||
|
||||
return &metricreductionruletypes.GettableReductionRules{Rules: rules, Total: total}, nil
|
||||
}
|
||||
|
||||
func (m *module) listSortedByVolume(ctx context.Context, orgID valuer.UUID, params *metricreductionruletypes.ListReductionRulesParams, startMs, endMs int64) (*metricreductionruletypes.GettableReductionRules, error) {
|
||||
allRules, total, err := m.store.List(ctx, orgID, &metricreductionruletypes.ListReductionRulesParams{Search: params.Search, MetricName: params.MetricName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
return &metricreductionruletypes.GettableReductionRules{Rules: []metricreductionruletypes.GettableReductionRule{}, Total: 0}, nil
|
||||
}
|
||||
|
||||
metricNames := make([]string, len(allRules))
|
||||
effectiveFrom := make(map[string]int64, len(allRules))
|
||||
ruleByMetric := make(map[string]*metricreductionruletypes.ReductionRule, len(allRules))
|
||||
for i, rule := range allRules {
|
||||
metricNames[i] = rule.MetricName
|
||||
effectiveFrom[rule.MetricName] = rule.EffectiveFrom.UnixMilli()
|
||||
ruleByMetric[rule.MetricName] = rule
|
||||
}
|
||||
|
||||
ranked, err := m.ch.RankByVolume(ctx, metricNames, effectiveFrom, params.OrderBy, params.Order, startMs, endMs, params.Offset, params.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := make([]metricreductionruletypes.GettableReductionRule, 0, len(ranked))
|
||||
for _, row := range ranked {
|
||||
rule, ok := ruleByMetric[row.MetricName]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rules = append(rules, withVolume(toGettableReductionRule(rule), row))
|
||||
}
|
||||
|
||||
return &metricreductionruletypes.GettableReductionRules{Rules: rules, Total: total}, nil
|
||||
}
|
||||
|
||||
func (m *module) Create(ctx context.Context, orgID valuer.UUID, userEmail string, req *metricreductionruletypes.PostableReductionRule) (*metricreductionruletypes.GettableReductionRule, error) {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.validateMetricForReduction(ctx, orgID, req.MetricName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
now := time.Now()
|
||||
rule := metricreductionruletypes.NewReductionRule(orgID, req.MetricName, req.MatchType, req.Labels, now.Add(effectiveFromMargin), userEmail)
|
||||
|
||||
if err := m.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||
if err := m.store.Create(ctx, rule); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.ch.Sync(ctx, rule.MetricName, rule.Labels, rule.MatchType.StringValue(), rule.EffectiveFrom.UnixMilli(), false, rule.UpdatedAt)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gettable := toGettableReductionRule(rule)
|
||||
return &gettable, nil
|
||||
}
|
||||
|
||||
func (m *module) GetByID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*metricreductionruletypes.GettableReductionRule, error) {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rule, err := m.store.GetByID(ctx, orgID, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gettable := toGettableReductionRule(rule)
|
||||
return &gettable, nil
|
||||
}
|
||||
|
||||
func (m *module) UpdateByID(ctx context.Context, orgID valuer.UUID, userEmail string, id valuer.UUID, req *metricreductionruletypes.UpdatableReductionRule) (*metricreductionruletypes.GettableReductionRule, error) {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
existing, err := m.store.GetByID(ctx, orgID, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
existing.MatchType = req.MatchType
|
||||
existing.Labels = metricreductionruletypes.LabelList(req.Labels)
|
||||
existing.EffectiveFrom = now.Add(effectiveFromMargin)
|
||||
existing.UpdatedAt = now
|
||||
existing.UpdatedBy = userEmail
|
||||
|
||||
if err := m.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||
if err := m.store.Upsert(ctx, existing); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.ch.Sync(ctx, existing.MetricName, existing.Labels, existing.MatchType.StringValue(), existing.EffectiveFrom.UnixMilli(), false, existing.UpdatedAt)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gettable := toGettableReductionRule(existing)
|
||||
return &gettable, nil
|
||||
}
|
||||
|
||||
func (m *module) DeleteByID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
rule, err := m.store.GetByID(ctx, orgID, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
effectiveFromMs := now.Add(effectiveFromMargin).UnixMilli()
|
||||
return m.store.RunInTx(ctx, func(ctx context.Context) error {
|
||||
if err := m.store.DeleteByID(ctx, orgID, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.ch.Sync(ctx, rule.MetricName, []string{}, metricreductionruletypes.MatchTypeDrop.StringValue(), effectiveFromMs, true, now)
|
||||
})
|
||||
}
|
||||
|
||||
func (m *module) Preview(ctx context.Context, orgID valuer.UUID, req *metricreductionruletypes.PostableReductionRulePreview) (*metricreductionruletypes.GettableReductionRulePreview, error) {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.validateMetricForReduction(ctx, orgID, req.MetricName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lookback := time.Duration(req.LookbackMs) * time.Millisecond
|
||||
if lookback <= 0 {
|
||||
lookback = defaultPreviewLookback
|
||||
}
|
||||
now := time.Now()
|
||||
startMs := now.Add(-lookback).UnixMilli()
|
||||
endMs := now.UnixMilli()
|
||||
current, reduced, reductionPercent, dropped, err := m.estimateVolume(ctx, req.MetricName, req.MatchType, req.Labels, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Baseline is what the metric keeps today (its current rule, or raw if none) so the preview reads
|
||||
// as current -> proposed.
|
||||
currentReduced := current
|
||||
if existing, gerr := m.store.Get(ctx, orgID, req.MetricName); gerr == nil {
|
||||
if _, existingReduced, _, _, eerr := m.estimateVolume(ctx, req.MetricName, existing.MatchType, existing.Labels, startMs, endMs); eerr == nil {
|
||||
currentReduced = existingReduced
|
||||
}
|
||||
}
|
||||
|
||||
return &metricreductionruletypes.GettableReductionRulePreview{
|
||||
IngestedSeries: current,
|
||||
CurrentRetainedSeries: currentReduced,
|
||||
RetainedSeries: reduced,
|
||||
ReductionPercent: reductionPercent,
|
||||
DroppedLabels: dropped,
|
||||
AffectedAssets: m.relatedAssetImpact(ctx, orgID, req.MetricName, dropped),
|
||||
EffectiveFrom: now.Add(effectiveFromMargin),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *module) Stats(ctx context.Context, orgID valuer.UUID) (*metricreductionruletypes.GettableReductionRuleStats, error) {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
startMs := now.Add(-defaultPreviewLookback).UnixMilli()
|
||||
endMs := now.UnixMilli()
|
||||
|
||||
allRules, total, err := m.store.List(ctx, orgID, &metricreductionruletypes.ListReductionRulesParams{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
return &metricreductionruletypes.GettableReductionRuleStats{}, nil
|
||||
}
|
||||
|
||||
metricNames := make([]string, len(allRules))
|
||||
effectiveFrom := make(map[string]int64, len(allRules))
|
||||
for i, rule := range allRules {
|
||||
metricNames[i] = rule.MetricName
|
||||
effectiveFrom[rule.MetricName] = rule.EffectiveFrom.UnixMilli()
|
||||
}
|
||||
|
||||
volumes, err := m.ch.VolumeByMetric(ctx, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ingestedSeries, retainedSeries uint64
|
||||
reducedMetricNames := make([]string, 0, len(volumes))
|
||||
reducedEffectiveFrom := make(map[string]int64, len(volumes))
|
||||
for name, volume := range volumes {
|
||||
ingestedSeries += volume.Ingested
|
||||
retained := effectiveRetained(volume.Ingested, volume.Reduced)
|
||||
retainedSeries += retained
|
||||
if retained < volume.Ingested {
|
||||
reducedMetricNames = append(reducedMetricNames, name)
|
||||
reducedEffectiveFrom[name] = effectiveFrom[name]
|
||||
}
|
||||
}
|
||||
|
||||
ingestedSamples, reducedSamples, err := m.ch.SampleVolume(ctx, reducedMetricNames, reducedEffectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &metricreductionruletypes.GettableReductionRuleStats{
|
||||
IngestedSeries: ingestedSeries,
|
||||
RetainedSeries: retainedSeries,
|
||||
EstimatedMonthlySavingsUsd: monthlySavingsUSD(ingestedSamples, reducedSamples, startMs, endMs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// monthlySavingsUSD extrapolates the windowed sample reduction to a monthly figure at the per-sample
|
||||
// list price. Ingested is gated to effective_from upstream, so pre-activation hours don't inflate it.
|
||||
func monthlySavingsUSD(ingestedSamples, reducedSamples uint64, startMs, endMs int64) float64 {
|
||||
if reducedSamples >= ingestedSamples || endMs <= startMs {
|
||||
return 0
|
||||
}
|
||||
savedSamples := float64(ingestedSamples - reducedSamples)
|
||||
monthlySamples := savedSamples * float64(monthDuration.Milliseconds()) / float64(endMs-startMs)
|
||||
return monthlySamples / 1_000_000 * pricePerMillionSamplesUSD
|
||||
}
|
||||
|
||||
func (m *module) Timeseries(ctx context.Context, orgID valuer.UUID) (*querybuildertypesv5.QueryRangeResponse, error) {
|
||||
if err := m.checkAccess(ctx, orgID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
startMs := now.Add(-defaultPreviewLookback).UnixMilli()
|
||||
endMs := now.UnixMilli()
|
||||
|
||||
allRules, _, err := m.store.List(ctx, orgID, &metricreductionruletypes.ListReductionRulesParams{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metricNames := make([]string, len(allRules))
|
||||
effectiveFrom := make(map[string]int64, len(allRules))
|
||||
for i, rule := range allRules {
|
||||
metricNames[i] = rule.MetricName
|
||||
effectiveFrom[rule.MetricName] = rule.EffectiveFrom.UnixMilli()
|
||||
}
|
||||
|
||||
volumes, err := m.ch.VolumeByMetric(ctx, metricNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reducedNames := make([]string, 0, len(volumes))
|
||||
for name, volume := range volumes {
|
||||
if effectiveRetained(volume.Ingested, volume.Reduced) < volume.Ingested {
|
||||
reducedNames = append(reducedNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
points, err := m.ch.SeriesTimeseries(ctx, metricNames, reducedNames, effectiveFrom, startMs, endMs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildVolumeTimeseries(points), nil
|
||||
}
|
||||
|
||||
func buildVolumeTimeseries(points []volumePoint) *querybuildertypesv5.QueryRangeResponse {
|
||||
ingested := make([]*querybuildertypesv5.TimeSeriesValue, 0, len(points))
|
||||
reduced := make([]*querybuildertypesv5.TimeSeriesValue, 0, len(points))
|
||||
for _, point := range points {
|
||||
ingested = append(ingested, &querybuildertypesv5.TimeSeriesValue{Timestamp: point.TimestampMs, Value: float64(point.Ingested)})
|
||||
reduced = append(reduced, &querybuildertypesv5.TimeSeriesValue{Timestamp: point.TimestampMs, Value: float64(point.Reduced)})
|
||||
}
|
||||
|
||||
return &querybuildertypesv5.QueryRangeResponse{
|
||||
Type: querybuildertypesv5.RequestTypeTimeSeries,
|
||||
Data: querybuildertypesv5.QueryData{
|
||||
Results: []any{
|
||||
&querybuildertypesv5.TimeSeriesData{
|
||||
QueryName: "reduction_volume",
|
||||
Aggregations: []*querybuildertypesv5.AggregationBucket{
|
||||
{
|
||||
Series: []*querybuildertypesv5.TimeSeries{
|
||||
{Labels: []*querybuildertypesv5.Label{{Key: telemetrytypes.TelemetryFieldKey{Name: "series"}, Value: "ingested"}}, Values: ingested},
|
||||
{Labels: []*querybuildertypesv5.Label{{Key: telemetrytypes.TelemetryFieldKey{Name: "series"}, Value: "retained"}}, Values: reduced},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *module) validateMetricForReduction(ctx context.Context, orgID valuer.UUID, metricName string) error {
|
||||
lastSeen, err := m.metadataStore.FetchLastSeenInfoMulti(ctx, metricName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lastSeen[metricName] == 0 {
|
||||
return errors.NewNotFoundf(errors.CodeNotFound, "metric not found: %q", metricName)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
startTs := uint64(now.Add(-defaultPreviewLookback).UnixMilli())
|
||||
endTs := uint64(now.UnixMilli())
|
||||
_, types, _, err := m.metadataStore.FetchTemporalityAndTypeMulti(ctx, orgID, startTs, endTs, metricName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if types[metricName] == metrictypes.ExpHistogramType {
|
||||
return errors.Newf(errors.TypeInvalidInput, metricreductionruletypes.ErrCodeMetricReductionRuleUnsupportedMetricType,
|
||||
"exponential histogram metrics cannot be reduced in v1")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *module) relatedAssetImpact(ctx context.Context, orgID valuer.UUID, metricName string, dropped []string) []metricreductionruletypes.AffectedAsset {
|
||||
affected := make([]metricreductionruletypes.AffectedAsset, 0)
|
||||
droppedSet := make(map[string]struct{}, len(dropped))
|
||||
for _, label := range dropped {
|
||||
droppedSet[label] = struct{}{}
|
||||
}
|
||||
|
||||
if dashboards, err := m.dashboard.GetByMetricNames(ctx, orgID, []string{metricName}); err != nil {
|
||||
m.logger.WarnContext(ctx, "failed to fetch related dashboards for reduction preview", slog.String("metric_name", metricName), errors.Attr(err))
|
||||
} else {
|
||||
for _, item := range dashboards[metricName] {
|
||||
usedLabels := append(splitCSV(item["group_by"]), splitCSV(item["filter_by"])...)
|
||||
affected = append(affected, metricreductionruletypes.AffectedAsset{
|
||||
Type: metricreductionruletypes.AssetTypeDashboard,
|
||||
ID: item["dashboard_id"],
|
||||
Name: item["dashboard_name"],
|
||||
Widget: &metricreductionruletypes.AffectedWidget{ID: item["widget_id"], Name: item["widget_name"]},
|
||||
ImpactedLabels: intersectLabels(usedLabels, droppedSet),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if alerts, err := m.ruleStore.GetStoredRulesByMetricName(ctx, orgID.String(), metricName); err != nil {
|
||||
m.logger.WarnContext(ctx, "failed to fetch related alerts for reduction preview", slog.String("metric_name", metricName), errors.Attr(err))
|
||||
} else {
|
||||
for _, a := range alerts {
|
||||
affected = append(affected, metricreductionruletypes.AffectedAsset{
|
||||
Type: metricreductionruletypes.AssetTypeAlert,
|
||||
ID: a.AlertID,
|
||||
Name: a.AlertName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return affected
|
||||
}
|
||||
|
||||
func toGettableReductionRule(rule *metricreductionruletypes.ReductionRule) metricreductionruletypes.GettableReductionRule {
|
||||
return metricreductionruletypes.GettableReductionRule{
|
||||
Identifiable: rule.Identifiable,
|
||||
TimeAuditable: rule.TimeAuditable,
|
||||
UserAuditable: rule.UserAuditable,
|
||||
MetricName: rule.MetricName,
|
||||
MatchType: rule.MatchType,
|
||||
Labels: rule.Labels,
|
||||
EffectiveFrom: rule.EffectiveFrom,
|
||||
Active: !rule.EffectiveFrom.After(time.Now()),
|
||||
}
|
||||
}
|
||||
|
||||
func effectiveRetained(ingested, reduced uint64) uint64 {
|
||||
if reduced == 0 || reduced > ingested {
|
||||
return ingested
|
||||
}
|
||||
return reduced
|
||||
}
|
||||
|
||||
func withVolume(rule metricreductionruletypes.GettableReductionRule, volume volumeRow) metricreductionruletypes.GettableReductionRule {
|
||||
rule.IngestedSeries = volume.Ingested
|
||||
rule.RetainedSeries = effectiveRetained(volume.Ingested, volume.Reduced)
|
||||
if volume.Ingested > 0 {
|
||||
rule.ReductionPercent = (1 - float64(rule.RetainedSeries)/float64(volume.Ingested)) * 100
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
func intersectLabels(keys []string, droppedSet map[string]struct{}) []string {
|
||||
seen := make(map[string]struct{})
|
||||
var out []string
|
||||
for _, key := range keys {
|
||||
if _, ok := droppedSet[key]; !ok {
|
||||
continue
|
||||
}
|
||||
if _, dup := seen[key]; dup {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
out = append(out, key)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func splitCSV(s string) []string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(s, ",")
|
||||
}
|
||||
|
||||
func resolveDroppedKept(matchType metricreductionruletypes.MatchType, ruleLabels, keys []string) (dropped, kept []string) {
|
||||
ruleSet := make(map[string]struct{}, len(ruleLabels))
|
||||
for _, l := range ruleLabels {
|
||||
ruleSet[l] = struct{}{}
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
if metricreductionruletypes.IsProtectedLabel(k) {
|
||||
kept = append(kept, k)
|
||||
continue
|
||||
}
|
||||
_, listed := ruleSet[k]
|
||||
drop := listed
|
||||
if matchType == metricreductionruletypes.MatchTypeKeep {
|
||||
drop = !listed
|
||||
}
|
||||
if drop {
|
||||
dropped = append(dropped, k)
|
||||
} else {
|
||||
kept = append(kept, k)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(dropped)
|
||||
sort.Strings(kept)
|
||||
return dropped, kept
|
||||
}
|
||||
|
||||
func (m *module) estimateVolume(ctx context.Context, metricName string, matchType metricreductionruletypes.MatchType, labels []string, startMs, endMs int64) (current uint64, reduced uint64, reductionPercent float64, dropped []string, err error) {
|
||||
keys, err := m.ch.AttributeKeys(ctx, metricName, startMs, endMs)
|
||||
if err != nil {
|
||||
return 0, 0, 0, nil, err
|
||||
}
|
||||
dropped, kept := resolveDroppedKept(matchType, labels, keys)
|
||||
|
||||
current, reduced, err = m.ch.EstimateCardinality(ctx, metricName, kept, startMs, endMs)
|
||||
if err != nil {
|
||||
return 0, 0, 0, nil, err
|
||||
}
|
||||
if current > 0 && reduced <= current {
|
||||
reductionPercent = (1 - float64(reduced)/float64(current)) * 100
|
||||
}
|
||||
return current, reduced, reductionPercent, dropped, nil
|
||||
}
|
||||
145
ee/modules/metricreductionrule/implmetricreductionrule/store.go
Normal file
145
ee/modules/metricreductionrule/implmetricreductionrule/store.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package implmetricreductionrule
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/types/metricreductionruletypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewStore(sqlstore sqlstore.SQLStore) metricreductionruletypes.Store {
|
||||
return &store{sqlstore: sqlstore}
|
||||
}
|
||||
|
||||
func (s *store) List(ctx context.Context, orgID valuer.UUID, params *metricreductionruletypes.ListReductionRulesParams) ([]*metricreductionruletypes.ReductionRule, int, error) {
|
||||
column := "metric_name"
|
||||
if params.OrderBy == metricreductionruletypes.OrderByLastUpdated {
|
||||
column = "updated_at"
|
||||
}
|
||||
direction := "ASC"
|
||||
if params.Order == metricreductionruletypes.OrderDesc {
|
||||
direction = "DESC"
|
||||
}
|
||||
|
||||
rules := make([]*metricreductionruletypes.ReductionRule, 0)
|
||||
query := s.sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(&rules).
|
||||
Where("org_id = ?", orgID).
|
||||
Order(column + " " + direction)
|
||||
if params.Search != "" {
|
||||
query = query.Where("metric_name LIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
if params.MetricName != "" {
|
||||
query = query.Where("metric_name = ?", params.MetricName)
|
||||
}
|
||||
if params.Limit > 0 {
|
||||
query = query.Limit(params.Limit).Offset(params.Offset)
|
||||
}
|
||||
|
||||
total, err := query.ScanAndCount(ctx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return rules, total, nil
|
||||
}
|
||||
|
||||
func (s *store) Get(ctx context.Context, orgID valuer.UUID, metricName string) (*metricreductionruletypes.ReductionRule, error) {
|
||||
rule := new(metricreductionruletypes.ReductionRule)
|
||||
err := s.sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(rule).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("metric_name = ?", metricName).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, s.sqlstore.WrapNotFoundErrf(err, metricreductionruletypes.ErrCodeMetricReductionRuleNotFound, "no reduction rule found for metric %q", metricName)
|
||||
}
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
func (s *store) GetByID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*metricreductionruletypes.ReductionRule, error) {
|
||||
rule := new(metricreductionruletypes.ReductionRule)
|
||||
err := s.sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(rule).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("id = ?", id).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, s.sqlstore.WrapNotFoundErrf(err, metricreductionruletypes.ErrCodeMetricReductionRuleNotFound, "no reduction rule found with id %q", id.String())
|
||||
}
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
func (s *store) Create(ctx context.Context, rule *metricreductionruletypes.ReductionRule) error {
|
||||
res, err := s.sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewInsert().
|
||||
Model(rule).
|
||||
On("CONFLICT (org_id, metric_name) DO NOTHING").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return errors.Newf(errors.TypeAlreadyExists, metricreductionruletypes.ErrCodeMetricReductionRuleAlreadyExists,
|
||||
"a reduction rule for metric %q already exists", rule.MetricName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) Upsert(ctx context.Context, rule *metricreductionruletypes.ReductionRule) error {
|
||||
_, err := s.sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewInsert().
|
||||
Model(rule).
|
||||
On("CONFLICT (org_id, metric_name) DO UPDATE").
|
||||
Set("match_type = EXCLUDED.match_type").
|
||||
Set("labels = EXCLUDED.labels").
|
||||
Set("effective_from = EXCLUDED.effective_from").
|
||||
Set("updated_at = EXCLUDED.updated_at").
|
||||
Set("updated_by = EXCLUDED.updated_by").
|
||||
Exec(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *store) DeleteByID(ctx context.Context, orgID valuer.UUID, id valuer.UUID) error {
|
||||
res, err := s.sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewDelete().
|
||||
Model((*metricreductionruletypes.ReductionRule)(nil)).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("id = ?", id).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rowsAffected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
return errors.Newf(errors.TypeNotFound, metricreductionruletypes.ErrCodeMetricReductionRuleNotFound, "no reduction rule found with id %q", id.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *store) RunInTx(ctx context.Context, cb func(ctx context.Context) error) error {
|
||||
return s.sqlstore.RunInTxCtx(ctx, nil, cb)
|
||||
}
|
||||
@@ -107,6 +107,15 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
Route: "",
|
||||
})
|
||||
|
||||
metricsReduction := ah.Signoz.Flagger.BooleanOrEmpty(ctx, flagger.FeatureEnableMetricsReduction, evalCtx)
|
||||
featureSet = append(featureSet, &licensetypes.Feature{
|
||||
Name: valuer.NewString(flagger.FeatureEnableMetricsReduction.String()),
|
||||
Active: metricsReduction,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
})
|
||||
|
||||
if constants.IsDotMetricsEnabled {
|
||||
for idx, feature := range featureSet {
|
||||
if feature.Name == licensetypes.DotMetricsEnabled {
|
||||
|
||||
@@ -90,8 +90,12 @@ func NewServer(config signoz.Config, signoz *signoz.SigNoz) (*Server, error) {
|
||||
|
||||
// initiate agent config handler
|
||||
agentConfMgr, err := agentConf.Initiate(&agentConf.ManagerOptions{
|
||||
Store: signoz.SQLStore,
|
||||
AgentFeatures: []agentConf.AgentFeature{logParsingPipelineController},
|
||||
Store: signoz.SQLStore,
|
||||
AgentFeatures: []agentConf.AgentFeature{
|
||||
logParsingPipelineController,
|
||||
signoz.Modules.SpanMapper,
|
||||
signoz.Modules.LLMPricingRule,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -29,6 +29,18 @@ if (!HTMLElement.prototype.scrollIntoView) {
|
||||
HTMLElement.prototype.scrollIntoView = function (): void {};
|
||||
}
|
||||
|
||||
// jsdom doesn't implement the Pointer Capture API, which Radix UI primitives
|
||||
// (e.g. @signozhq/ui Select) call when opening. Stub them so those components
|
||||
// can be exercised in tests.
|
||||
if (!HTMLElement.prototype.hasPointerCapture) {
|
||||
HTMLElement.prototype.hasPointerCapture = function (): boolean {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
if (!HTMLElement.prototype.releasePointerCapture) {
|
||||
HTMLElement.prototype.releasePointerCapture = function (): void {};
|
||||
}
|
||||
|
||||
if (typeof window.IntersectionObserver === 'undefined') {
|
||||
class IntersectionObserverMock {
|
||||
observe(): void {}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"test": "jest",
|
||||
"test:changedsince": "jest --changedSince=main --coverage --silent",
|
||||
"generate:api": "orval --config ./orval.config.ts && sh scripts/post-types-generation.sh",
|
||||
"generate:config:web-settings": "json2ts ../docs/config/web-settings.json -o src/types/generated/webSettings.ts --style.useTabs --style.tabWidth=1 --style.singleQuote --bannerComment '/* AUTO GENERATED FILE - DO NOT EDIT - GENERATED FROM docs/config/web-settings.json */'"
|
||||
"generate:config:web-settings": "json2ts ./src/schemas/generated/webSettings.schema.json -o src/types/generated/webSettings.ts --style.useTabs --style.tabWidth=1 --style.singleQuote --bannerComment '/* AUTO GENERATED FILE - DO NOT EDIT - GENERATED FROM frontend/src/schemas/generated/webSettings.schema.json */'"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
"logs_to_metrics": "Logs To Metrics",
|
||||
"roles": "Roles",
|
||||
"role_details": "Role Details",
|
||||
"role_edit": "Edit Role",
|
||||
"role_create": "Create Role",
|
||||
"members": "Members",
|
||||
"service_accounts": "Service Accounts",
|
||||
"mcp_server": "MCP Server"
|
||||
|
||||
@@ -82,6 +82,8 @@
|
||||
"TRACE_DETAIL_OLD": "SigNoz | Trace Detail",
|
||||
"SERVICE_TOP_LEVEL_OPERATIONS": "SigNoz | Service Operations",
|
||||
"ROLE_DETAILS": "SigNoz | Role Details",
|
||||
"ROLE_CREATE": "SigNoz | Create Role",
|
||||
"ROLE_EDIT": "SigNoz | Edit Role",
|
||||
"TRACES_FUNNELS_DETAIL": "SigNoz | Funnel",
|
||||
"INTEGRATIONS_DETAIL": "SigNoz | Integration",
|
||||
"PUBLIC_DASHBOARD": "SigNoz | Dashboard"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Extracts unique fenced code block language identifiers from all .md files under frontend/src/
|
||||
# Usage: bash frontend/scripts/extract-md-languages.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SRC_DIR="$SCRIPT_DIR/../src"
|
||||
|
||||
grep -roh '```[a-zA-Z0-9_+-]*' "$SRC_DIR" --include='*.md' \
|
||||
| sed 's/^```//' \
|
||||
| grep -v '^$' \
|
||||
| sort -u
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Validates that all fenced code block languages used in .md files are registered
|
||||
# in the syntax highlighter.
|
||||
# Usage: bash frontend/scripts/validate-md-languages.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SYNTAX_HIGHLIGHTER="$SCRIPT_DIR/../src/components/MarkdownRenderer/syntaxHighlighter.ts"
|
||||
|
||||
# Get all languages used in .md files
|
||||
md_languages=$("$SCRIPT_DIR/extract-md-languages.sh")
|
||||
|
||||
# Get all registered languages from syntaxHighlighter.ts
|
||||
registered_languages=$(grep -oP "registerLanguage\('\K[^']+" "$SYNTAX_HIGHLIGHTER" | sort -u)
|
||||
|
||||
missing_languages=()
|
||||
|
||||
for lang in $md_languages; do
|
||||
# Skip ai-* block markers — these are custom AI block types rendered by
|
||||
# RichCodeBlock as React components (e.g. ActionBlock, LineChartBlock),
|
||||
# not real syntax languages, so they don't need highlighter registration.
|
||||
if [[ "$lang" == ai-* ]]; then
|
||||
continue
|
||||
fi
|
||||
if ! echo "$registered_languages" | grep -qx "$lang"; then
|
||||
missing_languages+=("$lang")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_languages[@]} -gt 0 ]; then
|
||||
echo "Error: The following languages are used in .md files but not registered in syntaxHighlighter.ts:"
|
||||
for lang in "${missing_languages[@]}"; do
|
||||
echo " - $lang"
|
||||
done
|
||||
echo ""
|
||||
echo "Please add them to: frontend/src/components/MarkdownRenderer/syntaxHighlighter.ts"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All markdown code block languages are registered in syntaxHighlighter.ts"
|
||||
@@ -3,7 +3,6 @@ import { matchPath, Redirect, useLocation } from 'react-router-dom';
|
||||
import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||
import setLocalStorageApi from 'api/browser/localstorage/set';
|
||||
import { useListUsers } from 'api/generated/services/users';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { ORG_PREFERENCES } from 'constants/orgPreferences';
|
||||
import ROUTES from 'constants/routes';
|
||||
@@ -37,7 +36,6 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
activeLicense,
|
||||
isFetchingActiveLicense,
|
||||
trialInfo,
|
||||
featureFlags,
|
||||
} = useAppContext();
|
||||
|
||||
const isAdmin = user.role === USER_ROLES.ADMIN;
|
||||
@@ -212,14 +210,6 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for GET_STARTED → GET_STARTED_WITH_CLOUD redirect (feature flag)
|
||||
if (
|
||||
currentRoute?.path === ROUTES.GET_STARTED &&
|
||||
featureFlags?.find((e) => e.name === FeatureKeys.ONBOARDING_V3)?.active
|
||||
) {
|
||||
return <Redirect to={ROUTES.GET_STARTED_WITH_CLOUD} />;
|
||||
}
|
||||
|
||||
// Main routing logic
|
||||
if (currentRoute) {
|
||||
const { isPrivate, key } = currentRoute;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ReactElement } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { MemoryRouter, Route, Switch, useLocation } from 'react-router-dom';
|
||||
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { ORG_PREFERENCES } from 'constants/orgPreferences';
|
||||
import ROUTES from 'constants/routes';
|
||||
@@ -1263,80 +1262,6 @@ describe('PrivateRoute', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get Started Route Redirect', () => {
|
||||
it('should redirect to GET_STARTED_WITH_CLOUD when on GET_STARTED and ONBOARDING_V3 feature flag is active', async () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.GET_STARTED,
|
||||
appContext: {
|
||||
isLoggedIn: true,
|
||||
featureFlags: [
|
||||
{
|
||||
name: FeatureKeys.ONBOARDING_V3,
|
||||
active: true,
|
||||
usage: 0,
|
||||
usage_limit: -1,
|
||||
route: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await assertRedirectsTo(ROUTES.GET_STARTED_WITH_CLOUD);
|
||||
});
|
||||
|
||||
it('should not redirect when on GET_STARTED and ONBOARDING_V3 feature flag is inactive', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.GET_STARTED,
|
||||
appContext: {
|
||||
isLoggedIn: true,
|
||||
featureFlags: [
|
||||
{
|
||||
name: FeatureKeys.ONBOARDING_V3,
|
||||
active: false,
|
||||
usage: 0,
|
||||
usage_limit: -1,
|
||||
route: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.GET_STARTED);
|
||||
});
|
||||
|
||||
it('should not redirect when on GET_STARTED and ONBOARDING_V3 feature flag is not present', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.GET_STARTED,
|
||||
appContext: {
|
||||
isLoggedIn: true,
|
||||
featureFlags: [],
|
||||
},
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.GET_STARTED);
|
||||
});
|
||||
|
||||
it('should not redirect when on different route even if ONBOARDING_V3 is active', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.HOME,
|
||||
appContext: {
|
||||
isLoggedIn: true,
|
||||
featureFlags: [
|
||||
{
|
||||
name: FeatureKeys.ONBOARDING_V3,
|
||||
active: true,
|
||||
usage: 0,
|
||||
usage_limit: -1,
|
||||
route: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.HOME);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Loading States', () => {
|
||||
it('should not redirect while license is still being fetched', () => {
|
||||
renderPrivateRoute({
|
||||
@@ -1496,16 +1421,16 @@ describe('PrivateRoute', () => {
|
||||
await assertRedirectsTo(ROUTES.UN_AUTHORIZED);
|
||||
});
|
||||
|
||||
it('should allow EDITOR to access /get-started route', () => {
|
||||
it('should allow EDITOR to access /get-started-with-signoz-cloud route', () => {
|
||||
renderPrivateRoute({
|
||||
initialRoute: ROUTES.GET_STARTED,
|
||||
initialRoute: ROUTES.GET_STARTED_WITH_CLOUD,
|
||||
appContext: {
|
||||
isLoggedIn: true,
|
||||
user: createMockUser({ role: USER_ROLES.EDITOR as ROLES }),
|
||||
},
|
||||
});
|
||||
|
||||
assertStaysOnRoute(ROUTES.GET_STARTED);
|
||||
assertStaysOnRoute(ROUTES.GET_STARTED_WITH_CLOUD);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -90,14 +90,6 @@ export const SettingsPage = Loadable(
|
||||
() => import(/* webpackChunkName: "SettingsPage" */ 'pages/Settings'),
|
||||
);
|
||||
|
||||
export const GettingStarted = Loadable(
|
||||
() => import(/* webpackChunkName: "GettingStarted" */ 'pages/GettingStarted'),
|
||||
);
|
||||
|
||||
export const Onboarding = Loadable(
|
||||
() => import(/* webpackChunkName: "Onboarding" */ 'pages/OnboardingPage'),
|
||||
);
|
||||
|
||||
export const OrgOnboarding = Loadable(
|
||||
() => import(/* webpackChunkName: "OrgOnboarding" */ 'pages/OrgOnboarding'),
|
||||
);
|
||||
@@ -122,6 +114,13 @@ export const DashboardWidget = Loadable(
|
||||
import(/* webpackChunkName: "DashboardWidgetPage" */ 'pages/DashboardWidget'),
|
||||
);
|
||||
|
||||
export const DashboardPanelEditorPage = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "DashboardPanelEditorPage" */ 'pages/DashboardPageV2/PanelEditorPage/PanelEditorPage'
|
||||
),
|
||||
);
|
||||
|
||||
export const EditRulesPage = Loadable(
|
||||
() => import(/* webpackChunkName: "Alerts Edit Page" */ 'pages/EditRules'),
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ChannelsNew,
|
||||
CreateNewAlerts,
|
||||
DashboardPage,
|
||||
DashboardPanelEditorPage,
|
||||
DashboardsListPage,
|
||||
DashboardWidget,
|
||||
EditRulesPage,
|
||||
@@ -32,7 +33,6 @@ import {
|
||||
MeterExplorerPage,
|
||||
MetricsExplorer,
|
||||
OldLogsExplorer,
|
||||
Onboarding,
|
||||
OnboardingV2,
|
||||
OrgOnboarding,
|
||||
PasswordReset,
|
||||
@@ -69,13 +69,6 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: false,
|
||||
key: 'SIGN_UP',
|
||||
},
|
||||
{
|
||||
path: ROUTES.GET_STARTED,
|
||||
exact: false,
|
||||
component: Onboarding,
|
||||
isPrivate: true,
|
||||
key: 'GET_STARTED',
|
||||
},
|
||||
{
|
||||
path: ROUTES.GET_STARTED_WITH_CLOUD,
|
||||
exact: false,
|
||||
@@ -196,6 +189,13 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: true,
|
||||
key: 'DASHBOARD_WIDGET',
|
||||
},
|
||||
{
|
||||
path: ROUTES.DASHBOARD_PANEL_EDITOR,
|
||||
exact: true,
|
||||
component: DashboardPanelEditorPage,
|
||||
isPrivate: true,
|
||||
key: 'DASHBOARD_PANEL_EDITOR',
|
||||
},
|
||||
{
|
||||
path: ROUTES.EDIT_ALERTS,
|
||||
exact: true,
|
||||
@@ -469,6 +469,13 @@ const routes: AppRoutes[] = [
|
||||
key: 'METRICS_EXPLORER_VIEWS',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.METRICS_EXPLORER_VOLUME_CONTROL,
|
||||
exact: true,
|
||||
component: MetricsExplorer,
|
||||
key: 'METRICS_EXPLORER_VOLUME_CONTROL',
|
||||
isPrivate: true,
|
||||
},
|
||||
|
||||
{
|
||||
path: ROUTES.METER,
|
||||
|
||||
@@ -4,14 +4,22 @@
|
||||
* * regenerate with 'pnpm generate:api'
|
||||
* SigNoz
|
||||
*/
|
||||
import { useMutation } from 'react-query';
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import type {
|
||||
InvalidateOptions,
|
||||
MutationFunction,
|
||||
QueryClient,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
UseMutationOptions,
|
||||
UseMutationResult,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from 'react-query';
|
||||
|
||||
import type {
|
||||
GetChecks200,
|
||||
GetChecksParams,
|
||||
InframonitoringtypesPostableClustersDTO,
|
||||
InframonitoringtypesPostableDaemonSetsDTO,
|
||||
InframonitoringtypesPostableDeploymentsDTO,
|
||||
@@ -39,7 +47,94 @@ import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
|
||||
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
|
||||
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes clusters with key aggregated metrics derived by summing per-node values within the group: CPU usage, CPU allocatable, memory working set, memory allocatable. Each row also reports per-group nodeCountsByReadiness ({ ready, notReady } from each node's latest k8s.node.condition_ready value) and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each cluster includes metadata attributes (k8s.cluster.name). The response type is 'list' for the default k8s.cluster.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates nodes and pods in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_allocatable / memory / memory_allocatable, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (clusterCPU, clusterCPUAllocatable, clusterMemory, clusterMemoryAllocatable) return -1 as a sentinel when no data is available for that field.
|
||||
* Checks whether the metrics and attributes required to power the infra-monitoring section selected by the 'type' query parameter (hosts, processes, pods, nodes, deployments, daemonsets, statefulsets, jobs, namespaces, clusters, volumes) are being received. For each collector receiver or processor that contributes required metrics or attributes, lists what is present and what is missing, with a prebuilt user-facing message and a docs link per missing component. Default-enabled metrics are those expected as soon as the receiver is configured; optional metrics require 'enabled: true' in receiver config. 'ready' is true only when every missing list is empty.
|
||||
* @summary Run Infra Monitoring Setup Checks
|
||||
*/
|
||||
export const getChecks = (params: GetChecksParams, signal?: AbortSignal) => {
|
||||
return GeneratedAPIInstance<GetChecks200>({
|
||||
url: `/api/v2/infra_monitoring/checks`,
|
||||
method: 'GET',
|
||||
params,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetChecksQueryKey = (params?: GetChecksParams) => {
|
||||
return [
|
||||
`/api/v2/infra_monitoring/checks`,
|
||||
...(params ? [params] : []),
|
||||
] as const;
|
||||
};
|
||||
|
||||
export const getGetChecksQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getChecks>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params: GetChecksParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<Awaited<ReturnType<typeof getChecks>>, TError, TData>;
|
||||
},
|
||||
) => {
|
||||
const { query: queryOptions } = options ?? {};
|
||||
|
||||
const queryKey = queryOptions?.queryKey ?? getGetChecksQueryKey(params);
|
||||
|
||||
const queryFn: QueryFunction<Awaited<ReturnType<typeof getChecks>>> = ({
|
||||
signal,
|
||||
}) => getChecks(params, signal);
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getChecks>>,
|
||||
TError,
|
||||
TData
|
||||
> & { queryKey: QueryKey };
|
||||
};
|
||||
|
||||
export type GetChecksQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getChecks>>
|
||||
>;
|
||||
export type GetChecksQueryError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Run Infra Monitoring Setup Checks
|
||||
*/
|
||||
|
||||
export function useGetChecks<
|
||||
TData = Awaited<ReturnType<typeof getChecks>>,
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
>(
|
||||
params: GetChecksParams,
|
||||
options?: {
|
||||
query?: UseQueryOptions<Awaited<ReturnType<typeof getChecks>>, TError, TData>;
|
||||
},
|
||||
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
|
||||
const queryOptions = getGetChecksQueryOptions(params, options);
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
|
||||
queryKey: QueryKey;
|
||||
};
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Run Infra Monitoring Setup Checks
|
||||
*/
|
||||
export const invalidateGetChecks = async (
|
||||
queryClient: QueryClient,
|
||||
params: GetChecksParams,
|
||||
options?: InvalidateOptions,
|
||||
): Promise<QueryClient> => {
|
||||
await queryClient.invalidateQueries(
|
||||
{ queryKey: getGetChecksQueryKey(params) },
|
||||
options,
|
||||
);
|
||||
|
||||
return queryClient;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes clusters with key aggregated metrics derived by summing per-node values within the group: CPU usage, CPU allocatable, memory working set, memory allocatable. Each row also reports per-group nodeCountsByReadiness ({ ready, notReady } from each node's latest k8s.node.condition_ready value) and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each cluster includes metadata attributes (k8s.cluster.name). The response type is 'list' for the default k8s.cluster.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates nodes and pods in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_allocatable / memory / memory_allocatable, and pagination via offset/limit. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (clusterCPU, clusterCPUAllocatable, clusterMemory, clusterMemoryAllocatable) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Clusters for Infra Monitoring
|
||||
*/
|
||||
export const listClusters = (
|
||||
@@ -122,7 +217,7 @@ export const useListClusters = <
|
||||
return useMutation(getListClustersMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes DaemonSets with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the daemonset, plus average CPU/memory request and limit utilization (daemonSetCPURequest, daemonSetCPULimit, daemonSetMemoryRequest, daemonSetMemoryLimit). Each row also reports the latest known node-level counters from kube-state-metrics: desiredNodes (k8s.daemonset.desired_scheduled_nodes, the number of nodes the daemonset wants to run on) and currentNodes (k8s.daemonset.current_scheduled_nodes, the number of nodes the daemonset currently runs on) — note these are node counts, not pod counts. It also reports per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each daemonset includes metadata attributes (k8s.daemonset.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.daemonset.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by daemonsets in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_nodes / current_nodes, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (daemonSetCPU, daemonSetCPURequest, daemonSetCPULimit, daemonSetMemory, daemonSetMemoryRequest, daemonSetMemoryLimit, desiredNodes, currentNodes) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes DaemonSets with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the daemonset, plus average CPU/memory request and limit utilization (daemonSetCPURequest, daemonSetCPULimit, daemonSetMemoryRequest, daemonSetMemoryLimit). Each row also reports the latest known node-level counters from kube-state-metrics: desiredNodes (k8s.daemonset.desired_scheduled_nodes, the number of nodes the daemonset wants to run on) and currentNodes (k8s.daemonset.current_scheduled_nodes, the number of nodes the daemonset currently runs on) — note these are node counts, not pod counts. It also reports per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each daemonset includes metadata attributes (k8s.daemonset.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.daemonset.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by daemonsets in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_nodes / current_nodes, and pagination via offset/limit. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (daemonSetCPU, daemonSetCPURequest, daemonSetCPULimit, daemonSetMemory, daemonSetMemoryRequest, daemonSetMemoryLimit, desiredNodes, currentNodes) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List DaemonSets for Infra Monitoring
|
||||
*/
|
||||
export const listDaemonSets = (
|
||||
@@ -205,7 +300,7 @@ export const useListDaemonSets = <
|
||||
return useMutation(getListDaemonSetsMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes Deployments with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the deployment, plus average CPU/memory request and limit utilization (deploymentCPURequest, deploymentCPULimit, deploymentMemoryRequest, deploymentMemoryLimit). Each row also reports the latest known desiredPods (k8s.deployment.desired) and availablePods (k8s.deployment.available) replica counts and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each deployment includes metadata attributes (k8s.deployment.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.deployment.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by deployments in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_pods / available_pods, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (deploymentCPU, deploymentCPURequest, deploymentCPULimit, deploymentMemory, deploymentMemoryRequest, deploymentMemoryLimit, desiredPods, availablePods) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes Deployments with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the deployment, plus average CPU/memory request and limit utilization (deploymentCPURequest, deploymentCPULimit, deploymentMemoryRequest, deploymentMemoryLimit). Each row also reports the latest known desiredPods (k8s.deployment.desired) and availablePods (k8s.deployment.available) replica counts and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each deployment includes metadata attributes (k8s.deployment.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.deployment.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by deployments in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_pods / available_pods, and pagination via offset/limit. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (deploymentCPU, deploymentCPURequest, deploymentCPULimit, deploymentMemory, deploymentMemoryRequest, deploymentMemoryLimit, desiredPods, availablePods) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Deployments for Infra Monitoring
|
||||
*/
|
||||
export const listDeployments = (
|
||||
@@ -288,7 +383,7 @@ export const useListDeployments = <
|
||||
return useMutation(getListDeploymentsMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of hosts with key infrastructure metrics: CPU usage (%), memory usage (%), I/O wait (%), disk usage (%), and 15-minute load average. Each host includes its current status (active/inactive based on metrics reported in the last 10 minutes) and metadata attributes (e.g., os.type). Supports filtering via a filter expression, filtering by host status, custom groupBy to aggregate hosts by any attribute, ordering by any of the five metrics, and pagination via offset/limit. The response type is 'list' for the default host.name grouping or 'grouped_list' for custom groupBy keys. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (cpu, memory, wait, load15, diskUsage) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of hosts with key infrastructure metrics: CPU usage (%), memory usage (%), I/O wait (%), disk usage (%), and 15-minute load average. Each host includes its current status (active/inactive based on metrics reported in the last 10 minutes) and metadata attributes (e.g., os.type). Supports filtering via a filter expression, filtering by host status, custom groupBy to aggregate hosts by any attribute, ordering by any of the five metrics, and pagination via offset/limit. The response type is 'list' for the default host.name grouping or 'grouped_list' for custom groupBy keys. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (cpu, memory, wait, load15, diskUsage) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Hosts for Infra Monitoring
|
||||
*/
|
||||
export const listHosts = (
|
||||
@@ -371,7 +466,7 @@ export const useListHosts = <
|
||||
return useMutation(getListHostsMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes Jobs with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the job, plus average CPU/memory request and limit utilization (jobCPURequest, jobCPULimit, jobMemoryRequest, jobMemoryLimit). Each row also reports the latest known job-level counters from kube-state-metrics: desiredSuccessfulPods (k8s.job.desired_successful_pods, the target completion count), activePods (k8s.job.active_pods), failedPods (k8s.job.failed_pods, cumulative across the lifetime of the job), and successfulPods (k8s.job.successful_pods, cumulative). It also reports per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value); note podCountsByPhase.failed (current pod-phase) is distinct from failedPods (cumulative job kube-state-metric). Each job includes metadata attributes (k8s.job.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.job.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by jobs in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_successful_pods / active_pods / failed_pods / successful_pods, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (jobCPU, jobCPURequest, jobCPULimit, jobMemory, jobMemoryRequest, jobMemoryLimit, desiredSuccessfulPods, activePods, failedPods, successfulPods) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes Jobs with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the job, plus average CPU/memory request and limit utilization (jobCPURequest, jobCPULimit, jobMemoryRequest, jobMemoryLimit). Each row also reports the latest known job-level counters from kube-state-metrics: desiredSuccessfulPods (k8s.job.desired_successful_pods, the target completion count), activePods (k8s.job.active_pods), failedPods (k8s.job.failed_pods, cumulative across the lifetime of the job), and successfulPods (k8s.job.successful_pods, cumulative). It also reports per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value); note podCountsByPhase.failed (current pod-phase) is distinct from failedPods (cumulative job kube-state-metric). Each job includes metadata attributes (k8s.job.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.job.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by jobs in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_successful_pods / active_pods / failed_pods / successful_pods, and pagination via offset/limit. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (jobCPU, jobCPURequest, jobCPULimit, jobMemory, jobMemoryRequest, jobMemoryLimit, desiredSuccessfulPods, activePods, failedPods, successfulPods) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Jobs for Infra Monitoring
|
||||
*/
|
||||
export const listJobs = (
|
||||
@@ -454,7 +549,7 @@ export const useListJobs = <
|
||||
return useMutation(getListJobsMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes namespaces with key aggregated pod metrics: CPU usage and memory working set (summed across pods in the group), plus per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value in the window). Each namespace includes metadata attributes (k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.namespace.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / memory, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (namespaceCPU, namespaceMemory) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes namespaces with key aggregated pod metrics: CPU usage and memory working set (summed across pods in the group), plus per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value in the window). Each namespace includes metadata attributes (k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.namespace.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / memory, and pagination via offset/limit. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (namespaceCPU, namespaceMemory) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Namespaces for Infra Monitoring
|
||||
*/
|
||||
export const listNamespaces = (
|
||||
@@ -537,7 +632,7 @@ export const useListNamespaces = <
|
||||
return useMutation(getListNamespacesMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes nodes with key metrics: CPU usage, CPU allocatable, memory working set, memory allocatable, per-group nodeCountsByReadiness ({ ready, notReady } from each node's latest k8s.node.condition_ready in the window) and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } for pods scheduled on the listed nodes). Each node includes metadata attributes (k8s.node.uid, k8s.cluster.name). The response type is 'list' for the default k8s.node.name grouping (each row is one node with its current condition string: ready / not_ready / no_data) or 'grouped_list' for custom groupBy keys (each row aggregates nodes in the group; condition stays no_data). Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_allocatable / memory / memory_allocatable, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (nodeCPU, nodeCPUAllocatable, nodeMemory, nodeMemoryAllocatable) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes nodes with key metrics: CPU usage, CPU allocatable, memory working set, memory allocatable, per-group nodeCountsByReadiness ({ ready, notReady } from each node's latest k8s.node.condition_ready in the window) and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } for pods scheduled on the listed nodes). Each node includes metadata attributes (k8s.node.uid, k8s.cluster.name). The response type is 'list' for the default k8s.node.name grouping (each row is one node with its current condition string: ready / not_ready / no_data) or 'grouped_list' for custom groupBy keys (each row aggregates nodes in the group; condition stays no_data). Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_allocatable / memory / memory_allocatable, and pagination via offset/limit. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (nodeCPU, nodeCPUAllocatable, nodeMemory, nodeMemoryAllocatable) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Nodes for Infra Monitoring
|
||||
*/
|
||||
export const listNodes = (
|
||||
@@ -620,7 +715,7 @@ export const useListNodes = <
|
||||
return useMutation(getListNodesMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes pods with key metrics: CPU usage, CPU request/limit utilization, memory working set, memory request/limit utilization, current pod phase (pending/running/succeeded/failed/unknown/no_data), and pod age (ms since start time). Each pod includes metadata attributes (namespace, node, workload owner such as deployment/statefulset/daemonset/job/cronjob, cluster). Supports filtering via a filter expression, custom groupBy to aggregate pods by any attribute, ordering by any of the six metrics (cpu, cpu_request, cpu_limit, memory, memory_request, memory_limit), and pagination via offset/limit. The response type is 'list' for the default k8s.pod.uid grouping (each row is one pod with its current phase) or 'grouped_list' for custom groupBy keys (each row aggregates pods in the group with per-phase counts under podCountsByPhase: { pending, running, succeeded, failed, unknown } derived from each pod's latest phase in the window). Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (podCPU, podCPURequest, podCPULimit, podMemory, podMemoryRequest, podMemoryLimit, podAge) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes pods with key metrics: CPU usage, CPU request/limit utilization, memory working set, memory request/limit utilization, current pod phase (pending/running/succeeded/failed/unknown/no_data), and pod age (ms since start time). Each pod includes metadata attributes (namespace, node, workload owner such as deployment/statefulset/daemonset/job/cronjob, cluster). Supports filtering via a filter expression, custom groupBy to aggregate pods by any attribute, ordering by any of the six metrics (cpu, cpu_request, cpu_limit, memory, memory_request, memory_limit), and pagination via offset/limit. The response type is 'list' for the default k8s.pod.uid grouping (each row is one pod with its current phase) or 'grouped_list' for custom groupBy keys (each row aggregates pods in the group with per-phase counts under podCountsByPhase: { pending, running, succeeded, failed, unknown } derived from each pod's latest phase in the window). Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (podCPU, podCPURequest, podCPULimit, podMemory, podMemoryRequest, podMemoryLimit, podAge) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Pods for Infra Monitoring
|
||||
*/
|
||||
export const listPods = (
|
||||
@@ -703,7 +798,7 @@ export const useListPods = <
|
||||
return useMutation(getListPodsMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes persistent volume claims (PVCs) with key volume metrics: available bytes, capacity bytes, usage (capacity - available), inodes, free inodes, and used inodes. Each row also includes metadata attributes (k8s.persistentvolumeclaim.name, k8s.pod.uid, k8s.pod.name, k8s.namespace.name, k8s.node.name, k8s.statefulset.name, k8s.cluster.name). Supports filtering via a filter expression, custom groupBy to aggregate volumes by any attribute, ordering by any of the six metrics (available, capacity, usage, inodes, inodes_free, inodes_used), and pagination via offset/limit. The response type is 'list' for the default k8s.persistentvolumeclaim.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates volumes in the group. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (volumeAvailable, volumeCapacity, volumeUsage, volumeInodes, volumeInodesFree, volumeInodesUsed) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes persistent volume claims (PVCs) with key volume metrics: available bytes, capacity bytes, usage (capacity - available), inodes, free inodes, and used inodes. Each row also includes metadata attributes (k8s.persistentvolumeclaim.name, k8s.pod.uid, k8s.pod.name, k8s.namespace.name, k8s.node.name, k8s.statefulset.name, k8s.cluster.name). Supports filtering via a filter expression, custom groupBy to aggregate volumes by any attribute, ordering by any of the six metrics (available, capacity, usage, inodes, inodes_free, inodes_used), and pagination via offset/limit. The response type is 'list' for the default k8s.persistentvolumeclaim.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates volumes in the group. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (volumeAvailable, volumeCapacity, volumeUsage, volumeInodes, volumeInodesFree, volumeInodesUsed) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Volumes for Infra Monitoring
|
||||
*/
|
||||
export const listVolumes = (
|
||||
@@ -786,7 +881,7 @@ export const useListVolumes = <
|
||||
return useMutation(getListVolumesMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes StatefulSets with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the statefulset, plus average CPU/memory request and limit utilization (statefulSetCPURequest, statefulSetCPULimit, statefulSetMemoryRequest, statefulSetMemoryLimit). Each row also reports the latest known desiredPods (k8s.statefulset.desired_pods) and currentPods (k8s.statefulset.current_pods) replica counts and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each statefulset includes metadata attributes (k8s.statefulset.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.statefulset.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by statefulsets in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_pods / current_pods, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (statefulSetCPU, statefulSetCPURequest, statefulSetCPULimit, statefulSetMemory, statefulSetMemoryRequest, statefulSetMemoryLimit, desiredPods, currentPods) return -1 as a sentinel when no data is available for that field.
|
||||
* Returns a paginated list of Kubernetes StatefulSets with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the statefulset, plus average CPU/memory request and limit utilization (statefulSetCPURequest, statefulSetCPULimit, statefulSetMemoryRequest, statefulSetMemoryLimit). Each row also reports the latest known desiredPods (k8s.statefulset.desired_pods) and currentPods (k8s.statefulset.current_pods) replica counts and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each statefulset includes metadata attributes (k8s.statefulset.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.statefulset.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by statefulsets in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_pods / current_pods, and pagination via offset/limit. Also reports whether the requested time range falls before the data retention boundary. Numeric metric fields (statefulSetCPU, statefulSetCPURequest, statefulSetCPULimit, statefulSetMemory, statefulSetMemoryRequest, statefulSetMemoryLimit, desiredPods, currentPods) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List StatefulSets for Infra Monitoring
|
||||
*/
|
||||
export const listStatefulSets = (
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2094,6 +2094,45 @@ export interface AuthtypesGettableTokenDTO {
|
||||
tokenType?: string;
|
||||
}
|
||||
|
||||
export enum CoretypesKindDTO {
|
||||
anonymous = 'anonymous',
|
||||
organization = 'organization',
|
||||
role = 'role',
|
||||
serviceaccount = 'serviceaccount',
|
||||
user = 'user',
|
||||
'notification-channel' = 'notification-channel',
|
||||
'route-policy' = 'route-policy',
|
||||
'apdex-setting' = 'apdex-setting',
|
||||
'auth-domain' = 'auth-domain',
|
||||
session = 'session',
|
||||
'cloud-integration' = 'cloud-integration',
|
||||
'cloud-integration-service' = 'cloud-integration-service',
|
||||
integration = 'integration',
|
||||
dashboard = 'dashboard',
|
||||
'public-dashboard' = 'public-dashboard',
|
||||
'ingestion-key' = 'ingestion-key',
|
||||
'ingestion-limit' = 'ingestion-limit',
|
||||
pipeline = 'pipeline',
|
||||
'user-preference' = 'user-preference',
|
||||
'org-preference' = 'org-preference',
|
||||
'quick-filter' = 'quick-filter',
|
||||
'ttl-setting' = 'ttl-setting',
|
||||
rule = 'rule',
|
||||
'planned-maintenance' = 'planned-maintenance',
|
||||
'saved-view' = 'saved-view',
|
||||
'trace-funnel' = 'trace-funnel',
|
||||
'factor-password' = 'factor-password',
|
||||
'factor-api-key' = 'factor-api-key',
|
||||
license = 'license',
|
||||
subscription = 'subscription',
|
||||
logs = 'logs',
|
||||
traces = 'traces',
|
||||
metrics = 'metrics',
|
||||
'audit-logs' = 'audit-logs',
|
||||
'meter-metrics' = 'meter-metrics',
|
||||
'logs-field' = 'logs-field',
|
||||
'traces-field' = 'traces-field',
|
||||
}
|
||||
export enum CoretypesTypeDTO {
|
||||
user = 'user',
|
||||
serviceaccount = 'serviceaccount',
|
||||
@@ -2104,10 +2143,7 @@ export enum CoretypesTypeDTO {
|
||||
telemetryresource = 'telemetryresource',
|
||||
}
|
||||
export interface CoretypesResourceRefDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
kind: string;
|
||||
kind: CoretypesKindDTO;
|
||||
type: CoretypesTypeDTO;
|
||||
}
|
||||
|
||||
@@ -2142,16 +2178,21 @@ export interface ErrorsResponseerroradditionalDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
message?: string;
|
||||
message: string;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
suggestions?: string[];
|
||||
suggestions: string[];
|
||||
}
|
||||
|
||||
export interface ErrorsResponseretryjsonDTO {
|
||||
delay?: TimeDurationDTO;
|
||||
}
|
||||
export type ErrorsResponseretryjsonDTOAnyOf = {
|
||||
delay: TimeDurationDTO;
|
||||
};
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type ErrorsResponseretryjsonDTO = ErrorsResponseretryjsonDTOAnyOf | null;
|
||||
|
||||
export interface ErrorsJSONDTO {
|
||||
/**
|
||||
@@ -2161,24 +2202,24 @@ export interface ErrorsJSONDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
errors?: ErrorsResponseerroradditionalDTO[];
|
||||
errors: ErrorsResponseerroradditionalDTO[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
message: string;
|
||||
retry?: ErrorsResponseretryjsonDTO;
|
||||
retry: ErrorsResponseretryjsonDTO | null;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
suggestions?: string[];
|
||||
suggestions: string[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
type?: string;
|
||||
type: string;
|
||||
/**
|
||||
* @type string
|
||||
* @type string,null
|
||||
*/
|
||||
url?: string;
|
||||
url: string | null;
|
||||
}
|
||||
|
||||
export interface AuthtypesOrgSessionContextDTO {
|
||||
@@ -2243,12 +2284,12 @@ export interface AuthtypesPostableRoleDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
description: string;
|
||||
description?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name: string;
|
||||
transactionGroups: AuthtypesTransactionGroupsDTO;
|
||||
transactionGroups?: AuthtypesTransactionGroupsDTO;
|
||||
}
|
||||
|
||||
export interface AuthtypesPostableRotateTokenDTO {
|
||||
@@ -2281,7 +2322,7 @@ export interface AuthtypesPostableUserDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
userRoles: AuthtypesPostableUserRoleDTO[];
|
||||
userRoles?: AuthtypesPostableUserRoleDTO[];
|
||||
}
|
||||
|
||||
export interface AuthtypesRoleDTO {
|
||||
@@ -3391,12 +3432,14 @@ export interface Querybuildertypesv5FilterDTO {
|
||||
expression?: string;
|
||||
}
|
||||
|
||||
export type Querybuildertypesv5FunctionArgDTOValue = number | string;
|
||||
|
||||
export interface Querybuildertypesv5FunctionArgDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name?: string;
|
||||
value?: unknown;
|
||||
value?: Querybuildertypesv5FunctionArgDTOValue;
|
||||
}
|
||||
|
||||
export enum Querybuildertypesv5FunctionNameDTO {
|
||||
@@ -4229,26 +4272,21 @@ export interface DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDa
|
||||
export enum DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQueryDTOKind {
|
||||
'signoz/CompositeQuery' = 'signoz/CompositeQuery',
|
||||
}
|
||||
export enum Querybuildertypesv5QueryTypeDTO {
|
||||
export type Querybuildertypesv5BuilderQuerySpecDTO =
|
||||
| Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregationDTO
|
||||
| Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregationDTO
|
||||
| Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregationDTO;
|
||||
|
||||
export enum Querybuildertypesv5QueryEnvelopeBuilderDTOType {
|
||||
builder_query = 'builder_query',
|
||||
builder_formula = 'builder_formula',
|
||||
builder_trace_operator = 'builder_trace_operator',
|
||||
clickhouse_sql = 'clickhouse_sql',
|
||||
promql = 'promql',
|
||||
}
|
||||
export interface Querybuildertypesv5QueryEnvelopeBuilderTraceDTO {
|
||||
spec?: Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregationDTO;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5QueryEnvelopeBuilderLogDTO {
|
||||
spec?: Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregationDTO;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5QueryEnvelopeBuilderMetricDTO {
|
||||
spec?: Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5MetricAggregationDTO;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
export interface Querybuildertypesv5QueryEnvelopeBuilderDTO {
|
||||
spec?: Querybuildertypesv5BuilderQuerySpecDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @enum builder_query
|
||||
*/
|
||||
type: Querybuildertypesv5QueryEnvelopeBuilderDTOType;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5QueryBuilderFormulaDTO {
|
||||
@@ -4283,9 +4321,16 @@ export interface Querybuildertypesv5QueryBuilderFormulaDTO {
|
||||
order?: Querybuildertypesv5OrderByDTO[];
|
||||
}
|
||||
|
||||
export enum Querybuildertypesv5QueryEnvelopeFormulaDTOType {
|
||||
builder_formula = 'builder_formula',
|
||||
}
|
||||
export interface Querybuildertypesv5QueryEnvelopeFormulaDTO {
|
||||
spec?: Querybuildertypesv5QueryBuilderFormulaDTO;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @enum builder_formula
|
||||
*/
|
||||
type: Querybuildertypesv5QueryEnvelopeFormulaDTOType;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5QueryBuilderTraceOperatorDTO {
|
||||
@@ -4346,9 +4391,16 @@ export interface Querybuildertypesv5QueryBuilderTraceOperatorDTO {
|
||||
stepInterval?: Querybuildertypesv5StepDTO;
|
||||
}
|
||||
|
||||
export enum Querybuildertypesv5QueryEnvelopeTraceOperatorDTOType {
|
||||
builder_trace_operator = 'builder_trace_operator',
|
||||
}
|
||||
export interface Querybuildertypesv5QueryEnvelopeTraceOperatorDTO {
|
||||
spec?: Querybuildertypesv5QueryBuilderTraceOperatorDTO;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @enum builder_trace_operator
|
||||
*/
|
||||
type: Querybuildertypesv5QueryEnvelopeTraceOperatorDTOType;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5PromQueryDTO {
|
||||
@@ -4375,9 +4427,16 @@ export interface Querybuildertypesv5PromQueryDTO {
|
||||
step?: Querybuildertypesv5StepDTO;
|
||||
}
|
||||
|
||||
export enum Querybuildertypesv5QueryEnvelopePromQLDTOType {
|
||||
promql = 'promql',
|
||||
}
|
||||
export interface Querybuildertypesv5QueryEnvelopePromQLDTO {
|
||||
spec?: Querybuildertypesv5PromQueryDTO;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @enum promql
|
||||
*/
|
||||
type: Querybuildertypesv5QueryEnvelopePromQLDTOType;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5ClickHouseQueryDTO {
|
||||
@@ -4399,40 +4458,24 @@ export interface Querybuildertypesv5ClickHouseQueryDTO {
|
||||
query?: string;
|
||||
}
|
||||
|
||||
export enum Querybuildertypesv5QueryEnvelopeClickHouseSQLDTOType {
|
||||
clickhouse_sql = 'clickhouse_sql',
|
||||
}
|
||||
export interface Querybuildertypesv5QueryEnvelopeClickHouseSQLDTO {
|
||||
spec?: Querybuildertypesv5ClickHouseQueryDTO;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @enum clickhouse_sql
|
||||
*/
|
||||
type: Querybuildertypesv5QueryEnvelopeClickHouseSQLDTOType;
|
||||
}
|
||||
|
||||
export type Querybuildertypesv5QueryEnvelopeDTO =
|
||||
| (Querybuildertypesv5QueryEnvelopeBuilderTraceDTO & {
|
||||
spec?: unknown;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
})
|
||||
| (Querybuildertypesv5QueryEnvelopeBuilderLogDTO & {
|
||||
spec?: unknown;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
})
|
||||
| (Querybuildertypesv5QueryEnvelopeBuilderMetricDTO & {
|
||||
spec?: unknown;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
})
|
||||
| (Querybuildertypesv5QueryEnvelopeFormulaDTO & {
|
||||
spec?: unknown;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
})
|
||||
| (Querybuildertypesv5QueryEnvelopeTraceOperatorDTO & {
|
||||
spec?: unknown;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
})
|
||||
| (Querybuildertypesv5QueryEnvelopePromQLDTO & {
|
||||
spec?: unknown;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
})
|
||||
| (Querybuildertypesv5QueryEnvelopeClickHouseSQLDTO & {
|
||||
spec?: unknown;
|
||||
type?: Querybuildertypesv5QueryTypeDTO;
|
||||
});
|
||||
| Querybuildertypesv5QueryEnvelopeBuilderDTO
|
||||
| Querybuildertypesv5QueryEnvelopeFormulaDTO
|
||||
| Querybuildertypesv5QueryEnvelopeTraceOperatorDTO
|
||||
| Querybuildertypesv5QueryEnvelopePromQLDTO
|
||||
| Querybuildertypesv5QueryEnvelopeClickHouseSQLDTO;
|
||||
|
||||
/**
|
||||
* Composite query containing one or more query envelopes. Each query envelope specifies its type and corresponding spec.
|
||||
@@ -5422,6 +5465,121 @@ export interface GlobaltypesConfigDTO {
|
||||
mcp_url: string | null;
|
||||
}
|
||||
|
||||
export enum InframonitoringtypesCheckComponentTypeDTO {
|
||||
receiver = 'receiver',
|
||||
processor = 'processor',
|
||||
}
|
||||
export interface InframonitoringtypesAssociatedComponentDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name: string;
|
||||
type: InframonitoringtypesCheckComponentTypeDTO;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesAttributesComponentEntryDTO {
|
||||
associatedComponent: InframonitoringtypesAssociatedComponentDTO;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
attributes: string[] | null;
|
||||
}
|
||||
|
||||
export enum InframonitoringtypesCheckTypeDTO {
|
||||
hosts = 'hosts',
|
||||
processes = 'processes',
|
||||
pods = 'pods',
|
||||
nodes = 'nodes',
|
||||
deployments = 'deployments',
|
||||
daemonsets = 'daemonsets',
|
||||
statefulsets = 'statefulsets',
|
||||
jobs = 'jobs',
|
||||
namespaces = 'namespaces',
|
||||
clusters = 'clusters',
|
||||
volumes = 'volumes',
|
||||
}
|
||||
export interface InframonitoringtypesMissingMetricsComponentEntryDTO {
|
||||
associatedComponent: InframonitoringtypesAssociatedComponentDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
documentationLink: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
message: string;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
metrics: string[] | null;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesMissingAttributesComponentEntryDTO {
|
||||
associatedComponent: InframonitoringtypesAssociatedComponentDTO;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
attributes: string[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
documentationLink: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesMetricsComponentEntryDTO {
|
||||
associatedComponent: InframonitoringtypesAssociatedComponentDTO;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
metrics: string[] | null;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesChecksDTO {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
missingDefaultEnabledMetrics:
|
||||
| InframonitoringtypesMissingMetricsComponentEntryDTO[]
|
||||
| null;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
missingOptionalMetrics:
|
||||
| InframonitoringtypesMissingMetricsComponentEntryDTO[]
|
||||
| null;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
missingRequiredAttributes:
|
||||
| InframonitoringtypesMissingAttributesComponentEntryDTO[]
|
||||
| null;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
presentDefaultEnabledMetrics:
|
||||
| InframonitoringtypesMetricsComponentEntryDTO[]
|
||||
| null;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
presentOptionalMetrics: InframonitoringtypesMetricsComponentEntryDTO[] | null;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
presentRequiredAttributes:
|
||||
| InframonitoringtypesAttributesComponentEntryDTO[]
|
||||
| null;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
ready: boolean;
|
||||
type: InframonitoringtypesCheckTypeDTO;
|
||||
}
|
||||
|
||||
export type InframonitoringtypesClusterRecordDTOMetaAnyOf = {
|
||||
[key: string]: string;
|
||||
};
|
||||
@@ -5499,13 +5657,6 @@ export interface InframonitoringtypesClusterRecordDTO {
|
||||
podCountsByPhase: InframonitoringtypesPodCountsByPhaseDTO;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesRequiredMetricsCheckDTO {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
missingMetrics: string[] | null;
|
||||
}
|
||||
|
||||
export enum InframonitoringtypesResponseTypeDTO {
|
||||
list = 'list',
|
||||
grouped_list = 'grouped_list',
|
||||
@@ -5541,7 +5692,6 @@ export interface InframonitoringtypesClustersDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesClusterRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -5619,7 +5769,6 @@ export interface InframonitoringtypesDaemonSetsDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesDaemonSetRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -5697,7 +5846,6 @@ export interface InframonitoringtypesDeploymentsDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesDeploymentRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -5783,7 +5931,6 @@ export interface InframonitoringtypesHostsDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesHostRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -5869,7 +6016,6 @@ export interface InframonitoringtypesJobsDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesJobRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -5919,7 +6065,6 @@ export interface InframonitoringtypesNamespacesDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesNamespaceRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -5986,7 +6131,6 @@ export interface InframonitoringtypesNodesDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesNodeRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -6070,7 +6214,6 @@ export interface InframonitoringtypesPodsDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesPodRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -6418,7 +6561,6 @@ export interface InframonitoringtypesStatefulSetsDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesStatefulSetRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -6487,7 +6629,6 @@ export interface InframonitoringtypesVolumesDTO {
|
||||
* @type array
|
||||
*/
|
||||
records: InframonitoringtypesVolumeRecordDTO[];
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
@@ -6653,6 +6794,213 @@ export interface LlmpricingruletypesUpdatableLLMPricingRulesDTO {
|
||||
rules: LlmpricingruletypesUpdatableLLMPricingRuleDTO[] | null;
|
||||
}
|
||||
|
||||
export enum MetricreductionruletypesAssetTypeDTO {
|
||||
dashboard = 'dashboard',
|
||||
alert_rule = 'alert_rule',
|
||||
}
|
||||
export interface MetricreductionruletypesAffectedWidgetDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface MetricreductionruletypesAffectedAssetDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
impactedLabels: string[] | null;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name: string;
|
||||
type: MetricreductionruletypesAssetTypeDTO;
|
||||
widget?: MetricreductionruletypesAffectedWidgetDTO;
|
||||
}
|
||||
|
||||
export enum MetricreductionruletypesMatchTypeDTO {
|
||||
drop = 'drop',
|
||||
keep = 'keep',
|
||||
}
|
||||
export interface MetricreductionruletypesGettableReductionRuleDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
active: boolean;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
createdAt?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
createdBy?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
effectiveFrom: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
ingestedSeries: number;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
labels: string[] | null;
|
||||
matchType: MetricreductionruletypesMatchTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
metricName: string;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
reductionPercent: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
retainedSeries: number;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
updatedAt?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
updatedBy?: string;
|
||||
}
|
||||
|
||||
export interface MetricreductionruletypesGettableReductionRulePreviewDTO {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
affectedAssets: MetricreductionruletypesAffectedAssetDTO[] | null;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
currentRetainedSeries: number;
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
droppedLabels: string[] | null;
|
||||
/**
|
||||
* @type string
|
||||
* @format date-time
|
||||
*/
|
||||
effectiveFrom: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
ingestedSeries: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
reductionPercent: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
retainedSeries: number;
|
||||
}
|
||||
|
||||
export interface MetricreductionruletypesGettableReductionRuleStatsDTO {
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
estimatedMonthlySavingsUsd: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
ingestedSeries: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
retainedSeries: number;
|
||||
}
|
||||
|
||||
export interface MetricreductionruletypesGettableReductionRulesDTO {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
rules: MetricreductionruletypesGettableReductionRuleDTO[] | null;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
total: number;
|
||||
}
|
||||
|
||||
export enum MetricreductionruletypesOrderDTO {
|
||||
asc = 'asc',
|
||||
desc = 'desc',
|
||||
}
|
||||
export interface MetricreductionruletypesPostableReductionRuleDTO {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
labels: string[] | null;
|
||||
matchType: MetricreductionruletypesMatchTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
metricName: string;
|
||||
}
|
||||
|
||||
export interface MetricreductionruletypesPostableReductionRulePreviewDTO {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
labels: string[] | null;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
lookbackMs?: number;
|
||||
matchType: MetricreductionruletypesMatchTypeDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
metricName: string;
|
||||
}
|
||||
|
||||
export enum MetricreductionruletypesReductionRuleOrderByDTO {
|
||||
metric = 'metric',
|
||||
ingested_volume = 'ingested_volume',
|
||||
reduced_volume = 'reduced_volume',
|
||||
reduction = 'reduction',
|
||||
last_updated = 'last_updated',
|
||||
}
|
||||
export interface MetricreductionruletypesUpdatableReductionRuleDTO {
|
||||
/**
|
||||
* @type array,null
|
||||
*/
|
||||
labels: string[] | null;
|
||||
matchType: MetricreductionruletypesMatchTypeDTO;
|
||||
}
|
||||
|
||||
export interface MetricsexplorertypesInspectMetricsRequestDTO {
|
||||
/**
|
||||
* @type integer
|
||||
@@ -6671,9 +7019,11 @@ export interface MetricsexplorertypesInspectMetricsRequestDTO {
|
||||
start: number;
|
||||
}
|
||||
|
||||
export type Querybuildertypesv5LabelDTOValue = string | number | boolean;
|
||||
|
||||
export interface Querybuildertypesv5LabelDTO {
|
||||
key?: TelemetrytypesTelemetryFieldKeyDTO;
|
||||
value?: unknown;
|
||||
value?: Querybuildertypesv5LabelDTOValue;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5BucketDTO {
|
||||
@@ -7283,9 +7633,20 @@ export enum Querybuildertypesv5VariableTypeDTO {
|
||||
custom = 'custom',
|
||||
text = 'text',
|
||||
}
|
||||
export type Querybuildertypesv5VariableItemDTOValueOneOfItem =
|
||||
| string
|
||||
| number
|
||||
| boolean;
|
||||
|
||||
export type Querybuildertypesv5VariableItemDTOValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| Querybuildertypesv5VariableItemDTOValueOneOfItem[];
|
||||
|
||||
export interface Querybuildertypesv5VariableItemDTO {
|
||||
type?: Querybuildertypesv5VariableTypeDTO;
|
||||
value?: unknown;
|
||||
value?: Querybuildertypesv5VariableItemDTOValue;
|
||||
}
|
||||
|
||||
export type Querybuildertypesv5QueryRangeRequestDTOVariables = {
|
||||
@@ -7333,6 +7694,13 @@ export interface Querybuildertypesv5QueryRangeResponseDTO {
|
||||
warning?: Querybuildertypesv5QueryWarnDataDTO;
|
||||
}
|
||||
|
||||
export enum Querybuildertypesv5QueryTypeDTO {
|
||||
builder_query = 'builder_query',
|
||||
builder_formula = 'builder_formula',
|
||||
builder_trace_operator = 'builder_trace_operator',
|
||||
clickhouse_sql = 'clickhouse_sql',
|
||||
promql = 'promql',
|
||||
}
|
||||
export interface RenderErrorResponseDTO {
|
||||
error: ErrorsJSONDTO;
|
||||
/**
|
||||
@@ -9922,7 +10290,7 @@ export type ListUsersDeprecated200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type DeleteUserPathParameters = {
|
||||
export type DeleteUserDeprecatedPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetUserDeprecatedPathParameters = {
|
||||
@@ -10210,6 +10578,21 @@ export type Healthz503 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetChecksParams = {
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
type: InframonitoringtypesCheckTypeDTO;
|
||||
};
|
||||
|
||||
export type GetChecks200 = {
|
||||
data: InframonitoringtypesChecksDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListClusters200 = {
|
||||
data: InframonitoringtypesClustersDTO;
|
||||
/**
|
||||
@@ -10298,6 +10681,102 @@ export type Livez200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListMetricReductionRulesParams = {
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
orderBy?: MetricreductionruletypesReductionRuleOrderByDTO;
|
||||
/**
|
||||
* @description undefined
|
||||
*/
|
||||
order?: MetricreductionruletypesOrderDTO;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
search?: string;
|
||||
/**
|
||||
* @type string
|
||||
* @description undefined
|
||||
*/
|
||||
metricName?: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @description undefined
|
||||
*/
|
||||
offset?: number;
|
||||
/**
|
||||
* @type integer
|
||||
* @description undefined
|
||||
*/
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
export type ListMetricReductionRules200 = {
|
||||
data: MetricreductionruletypesGettableReductionRulesDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type CreateMetricReductionRule201 = {
|
||||
data: MetricreductionruletypesGettableReductionRuleDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type DeleteMetricReductionRuleByIDPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetMetricReductionRuleByIDPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetMetricReductionRuleByID200 = {
|
||||
data: MetricreductionruletypesGettableReductionRuleDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type UpdateMetricReductionRuleByIDPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type UpdateMetricReductionRuleByID200 = {
|
||||
data: MetricreductionruletypesGettableReductionRuleDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type PreviewMetricReductionRule200 = {
|
||||
data: MetricreductionruletypesGettableReductionRulePreviewDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMetricReductionRuleStats200 = {
|
||||
data: MetricreductionruletypesGettableReductionRuleStatsDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMetricReductionRuleTimeseries200 = {
|
||||
data: Querybuildertypesv5QueryRangeResponseDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListMetricsParams = {
|
||||
/**
|
||||
* @type integer,null
|
||||
@@ -10334,9 +10813,14 @@ export type ListMetrics200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMetricAlertsPathParameters = {
|
||||
export type GetMetricAlertsParams = {
|
||||
/**
|
||||
* @type string
|
||||
* @description The name of the metric. May contain slashes (e.g. cloud-provider metrics like run.googleapis.com/request_latencies).
|
||||
*/
|
||||
metricName: string;
|
||||
};
|
||||
|
||||
export type GetMetricAlerts200 = {
|
||||
data: MetricsexplorertypesMetricAlertsResponseDTO;
|
||||
/**
|
||||
@@ -10345,18 +10829,20 @@ export type GetMetricAlerts200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMetricAttributesPathParameters = {
|
||||
metricName: string;
|
||||
};
|
||||
export type GetMetricAttributesParams = {
|
||||
/**
|
||||
* @type string
|
||||
* @description The name of the metric. May contain slashes (e.g. cloud-provider metrics like run.googleapis.com/request_latencies).
|
||||
*/
|
||||
metricName: string;
|
||||
/**
|
||||
* @type integer,null
|
||||
* @description undefined
|
||||
* @description Start of the time range as a Unix timestamp in milliseconds.
|
||||
*/
|
||||
start?: number | null;
|
||||
/**
|
||||
* @type integer,null
|
||||
* @description undefined
|
||||
* @description End of the time range as a Unix timestamp in milliseconds.
|
||||
*/
|
||||
end?: number | null;
|
||||
};
|
||||
@@ -10369,9 +10855,14 @@ export type GetMetricAttributes200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMetricDashboardsPathParameters = {
|
||||
export type GetMetricDashboardsParams = {
|
||||
/**
|
||||
* @type string
|
||||
* @description The name of the metric. May contain slashes (e.g. cloud-provider metrics like run.googleapis.com/request_latencies).
|
||||
*/
|
||||
metricName: string;
|
||||
};
|
||||
|
||||
export type GetMetricDashboards200 = {
|
||||
data: MetricsexplorertypesMetricDashboardsResponseDTO;
|
||||
/**
|
||||
@@ -10380,9 +10871,14 @@ export type GetMetricDashboards200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMetricHighlightsPathParameters = {
|
||||
export type GetMetricHighlightsParams = {
|
||||
/**
|
||||
* @type string
|
||||
* @description The name of the metric. May contain slashes (e.g. cloud-provider metrics like run.googleapis.com/request_latencies).
|
||||
*/
|
||||
metricName: string;
|
||||
};
|
||||
|
||||
export type GetMetricHighlights200 = {
|
||||
data: MetricsexplorertypesMetricHighlightsResponseDTO;
|
||||
/**
|
||||
@@ -10391,22 +10887,24 @@ export type GetMetricHighlights200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetMetricMetadataPathParameters = {
|
||||
metricName: string;
|
||||
};
|
||||
export type GetMetricMetadata200 = {
|
||||
data: MetricsexplorertypesMetricMetadataDTO;
|
||||
export type InspectMetrics200 = {
|
||||
data: MetricsexplorertypesInspectMetricsResponseDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type UpdateMetricMetadataPathParameters = {
|
||||
export type GetMetricMetadataParams = {
|
||||
/**
|
||||
* @type string
|
||||
* @description The name of the metric. May contain slashes (e.g. cloud-provider metrics like run.googleapis.com/request_latencies).
|
||||
*/
|
||||
metricName: string;
|
||||
};
|
||||
export type InspectMetrics200 = {
|
||||
data: MetricsexplorertypesInspectMetricsResponseDTO;
|
||||
|
||||
export type GetMetricMetadata200 = {
|
||||
data: MetricsexplorertypesMetricMetadataDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -10841,6 +11339,9 @@ export type CreateUser201 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type DeleteUserPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
export type GetUserPathParameters = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ import type {
|
||||
CreateResetPasswordToken201,
|
||||
CreateResetPasswordTokenPathParameters,
|
||||
CreateUser201,
|
||||
DeleteUserDeprecatedPathParameters,
|
||||
DeleteUserPathParameters,
|
||||
GetMyUser200,
|
||||
GetMyUserDeprecated200,
|
||||
@@ -511,10 +512,11 @@ export const invalidateListUsersDeprecated = async (
|
||||
|
||||
/**
|
||||
* This endpoint deletes the user by id
|
||||
* @deprecated
|
||||
* @summary Delete user
|
||||
*/
|
||||
export const deleteUser = (
|
||||
{ id }: DeleteUserPathParameters,
|
||||
export const deleteUserDeprecated = (
|
||||
{ id }: DeleteUserDeprecatedPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
@@ -524,23 +526,23 @@ export const deleteUser = (
|
||||
});
|
||||
};
|
||||
|
||||
export const getDeleteUserMutationOptions = <
|
||||
export const getDeleteUserDeprecatedMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
Awaited<ReturnType<typeof deleteUserDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
{ pathParams: DeleteUserDeprecatedPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
Awaited<ReturnType<typeof deleteUserDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
{ pathParams: DeleteUserDeprecatedPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['deleteUser'];
|
||||
const mutationKey = ['deleteUserDeprecated'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
@@ -550,43 +552,45 @@ export const getDeleteUserMutationOptions = <
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
{ pathParams: DeleteUserPathParameters }
|
||||
Awaited<ReturnType<typeof deleteUserDeprecated>>,
|
||||
{ pathParams: DeleteUserDeprecatedPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return deleteUser(pathParams);
|
||||
return deleteUserDeprecated(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type DeleteUserMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof deleteUser>>
|
||||
export type DeleteUserDeprecatedMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof deleteUserDeprecated>>
|
||||
>;
|
||||
|
||||
export type DeleteUserMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
export type DeleteUserDeprecatedMutationError =
|
||||
ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @summary Delete user
|
||||
*/
|
||||
export const useDeleteUser = <
|
||||
export const useDeleteUserDeprecated = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
Awaited<ReturnType<typeof deleteUserDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
{ pathParams: DeleteUserDeprecatedPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
Awaited<ReturnType<typeof deleteUserDeprecated>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
{ pathParams: DeleteUserDeprecatedPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getDeleteUserMutationOptions(options));
|
||||
return useMutation(getDeleteUserDeprecatedMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the user by id
|
||||
@@ -1309,6 +1313,85 @@ export const useCreateUser = <
|
||||
> => {
|
||||
return useMutation(getCreateUserMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint deletes the user by id
|
||||
* @summary Delete user
|
||||
*/
|
||||
export const deleteUser = (
|
||||
{ id }: DeleteUserPathParameters,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<void>({
|
||||
url: `/api/v2/users/${id}`,
|
||||
method: 'DELETE',
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getDeleteUserMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['deleteUser'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
{ pathParams: DeleteUserPathParameters }
|
||||
> = (props) => {
|
||||
const { pathParams } = props ?? {};
|
||||
|
||||
return deleteUser(pathParams);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type DeleteUserMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof deleteUser>>
|
||||
>;
|
||||
|
||||
export type DeleteUserMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Delete user
|
||||
*/
|
||||
export const useDeleteUser = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof deleteUser>>,
|
||||
TError,
|
||||
{ pathParams: DeleteUserPathParameters },
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getDeleteUserMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* This endpoint returns the user by id
|
||||
* @summary Get user by user id
|
||||
|
||||
@@ -21,6 +21,8 @@ interface ErrorInPlaceProps {
|
||||
width?: string | number;
|
||||
/** Custom content instead of ErrorContent */
|
||||
children?: ReactNode;
|
||||
/** Test ID for testing */
|
||||
'data-testid'?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,6 +46,7 @@ function ErrorInPlace({
|
||||
height = '100%',
|
||||
width = '100%',
|
||||
children,
|
||||
'data-testid': dataTestId,
|
||||
}: ErrorInPlaceProps): JSX.Element {
|
||||
const containerStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
@@ -59,7 +62,11 @@ function ErrorInPlace({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`error-in-place ${className}`.trim()} style={containerStyle}>
|
||||
<div
|
||||
className={`error-in-place ${className}`.trim()}
|
||||
style={containerStyle}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
{children || <ErrorContent error={error} />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -62,13 +62,13 @@ function ErrorTitleAndKey({
|
||||
|
||||
switch (parentTitle) {
|
||||
case 'Consumers':
|
||||
link = `${ROUTES.GET_STARTED_APPLICATION_MONITORING}?${QueryParams.getStartedSource}=kafka&${QueryParams.getStartedSourceService}=${MessagingQueueHealthCheckService.Consumers}`;
|
||||
link = `${ROUTES.GET_STARTED_WITH_CLOUD}?${QueryParams.getStartedSource}=self-hosted-kafka&${QueryParams.getStartedSourceService}=${MessagingQueueHealthCheckService.Consumers}`;
|
||||
break;
|
||||
case 'Producers':
|
||||
link = `${ROUTES.GET_STARTED_APPLICATION_MONITORING}?${QueryParams.getStartedSource}=kafka&${QueryParams.getStartedSourceService}=${MessagingQueueHealthCheckService.Producers}`;
|
||||
link = `${ROUTES.GET_STARTED_WITH_CLOUD}?${QueryParams.getStartedSource}=self-hosted-kafka&${QueryParams.getStartedSourceService}=${MessagingQueueHealthCheckService.Producers}`;
|
||||
break;
|
||||
case 'Kafka':
|
||||
link = `${ROUTES.GET_STARTED_INFRASTRUCTURE_MONITORING}?${QueryParams.getStartedSource}=kafka&${QueryParams.getStartedSourceService}=${MessagingQueueHealthCheckService.Kafka}`;
|
||||
link = `${ROUTES.GET_STARTED_WITH_CLOUD}?${QueryParams.getStartedSource}=self-hosted-kafka&${QueryParams.getStartedSourceService}=${MessagingQueueHealthCheckService.Kafka}`;
|
||||
break;
|
||||
default:
|
||||
link = '';
|
||||
|
||||
@@ -5,13 +5,9 @@ describe('PermissionDeniedFullPage', () => {
|
||||
it('renders the title and subtitle with the permissionName interpolated', () => {
|
||||
render(<PermissionDeniedFullPage permissionName="serviceaccount:list" />);
|
||||
|
||||
expect(
|
||||
screen.getByText("Uh-oh! You don't have permission to view this page."),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('Uh-oh! You are not authorized')).toBeInTheDocument();
|
||||
expect(screen.getByText(/serviceaccount:list/)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Please ask your SigNoz administrator to grant access/),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/is not authorized to perform/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with a different permissionName', () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CircleSlash2 } from '@signozhq/icons';
|
||||
|
||||
import styles from './PermissionDeniedFullPage.module.scss';
|
||||
import { Style } from '@signozhq/design-tokens';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
|
||||
interface PermissionDeniedFullPageProps {
|
||||
permissionName: string;
|
||||
@@ -10,18 +11,18 @@ interface PermissionDeniedFullPageProps {
|
||||
function PermissionDeniedFullPage({
|
||||
permissionName,
|
||||
}: PermissionDeniedFullPageProps): JSX.Element {
|
||||
const { user } = useAppContext();
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.icon}>
|
||||
<CircleSlash2 color={Style.CALLOUT_WARNING_TITLE} size={14} />
|
||||
</span>
|
||||
<p className={styles.title}>
|
||||
Uh-oh! You don't have permission to view this page.
|
||||
</p>
|
||||
<p className={styles.title}>Uh-oh! You are not authorized</p>
|
||||
<p className={styles.subtitle}>
|
||||
You need <code className={styles.permission}>{permissionName}</code> to
|
||||
view this page. Please ask your SigNoz administrator to grant access.
|
||||
<code className={styles.permission}>user/{user.id}</code> is not authorized
|
||||
to perform <code className={styles.permission}>{permissionName}</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { useCallback } from 'react';
|
||||
import { LockKeyhole } from '@signozhq/icons';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Check, Copy, LockKeyhole } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import type { AuthtypesRoleDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import AuthZTooltip from 'components/AuthZTooltip/AuthZTooltip';
|
||||
import RolesSelect from 'components/RolesSelect';
|
||||
@@ -46,6 +48,23 @@ function OverviewTab({
|
||||
saveErrors = [],
|
||||
}: OverviewTabProps): JSX.Element {
|
||||
const { formatTimezoneAdjustedTimestamp } = useTimezone();
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
const [hasCopiedId, setHasCopiedId] = useState(false);
|
||||
|
||||
const handleCopyId = useCallback((): void => {
|
||||
if (account.id) {
|
||||
copyToClipboard(account.id);
|
||||
setHasCopiedId(true);
|
||||
}
|
||||
}, [account.id, copyToClipboard]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasCopiedId) {
|
||||
const timer = setTimeout(() => setHasCopiedId(false), 2000);
|
||||
return (): void => clearTimeout(timer);
|
||||
}
|
||||
return undefined;
|
||||
}, [hasCopiedId]);
|
||||
|
||||
const formatTimestamp = useCallback(
|
||||
(ts: string | null | undefined): string => {
|
||||
@@ -93,6 +112,17 @@ function OverviewTab({
|
||||
</label>
|
||||
<div className="sa-drawer__input-wrapper sa-drawer__input-wrapper--disabled">
|
||||
<span className="sa-drawer__input-text">{account.id || '—'}</span>
|
||||
{account.id && (
|
||||
<Button
|
||||
variant="link"
|
||||
color="secondary"
|
||||
onClick={handleCopyId}
|
||||
className="sa-drawer__copy-btn"
|
||||
data-testid="copy-id-btn"
|
||||
>
|
||||
{hasCopiedId ? <Check size={14} /> : <Copy size={14} />}
|
||||
</Button>
|
||||
)}
|
||||
<LockKeyhole size={14} className="sa-drawer__lock-icon" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -203,6 +203,19 @@
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&__copy-btn {
|
||||
flex-shrink: 0;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
min-height: auto;
|
||||
color: var(--foreground);
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__disabled-roles {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
import type { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import { AxiosError } from 'axios';
|
||||
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
|
||||
import { GuardAuthZ } from 'components/GuardAuthZ/GuardAuthZ';
|
||||
import PermissionDeniedCallout from 'components/PermissionDeniedCallout/PermissionDeniedCallout';
|
||||
import { useRoles } from 'components/RolesSelect';
|
||||
import { SA_QUERY_PARAMS } from 'container/ServiceAccountsSettings/constants';
|
||||
@@ -477,15 +476,9 @@ function ServiceAccountDrawer({
|
||||
!isAccountLoading &&
|
||||
!isAccountError &&
|
||||
selectedAccountId && (
|
||||
<GuardAuthZ
|
||||
relation="read"
|
||||
object={`serviceaccount:${selectedAccountId}`}
|
||||
fallbackOnNoPermissions={(): JSX.Element => (
|
||||
<PermissionDeniedCallout permissionName="serviceaccount:read" />
|
||||
)}
|
||||
>
|
||||
<>
|
||||
{activeTab === ServiceAccountDrawerTab.Overview && account && (
|
||||
<>
|
||||
{activeTab === ServiceAccountDrawerTab.Overview &&
|
||||
(canRead && account ? (
|
||||
<OverviewTab
|
||||
account={account}
|
||||
localName={localName}
|
||||
@@ -504,23 +497,24 @@ function ServiceAccountDrawer({
|
||||
onRefetchRoles={refetchRoles}
|
||||
saveErrors={saveErrors}
|
||||
/>
|
||||
)}
|
||||
{activeTab === ServiceAccountDrawerTab.Keys &&
|
||||
(canListKeys ? (
|
||||
<KeysTab
|
||||
keys={keys}
|
||||
isLoading={keysLoading}
|
||||
isDisabled={isDeleted}
|
||||
canUpdate={canUpdate}
|
||||
accountId={selectedAccountId}
|
||||
currentPage={keysPage}
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
) : (
|
||||
<PermissionDeniedCallout permissionName="factor-api-key:list" />
|
||||
))}
|
||||
</>
|
||||
</GuardAuthZ>
|
||||
) : (
|
||||
<PermissionDeniedCallout permissionName="serviceaccount:read" />
|
||||
))}
|
||||
{activeTab === ServiceAccountDrawerTab.Keys &&
|
||||
(canListKeys ? (
|
||||
<KeysTab
|
||||
keys={keys}
|
||||
isLoading={keysLoading}
|
||||
isDisabled={isDeleted}
|
||||
canUpdate={canUpdate}
|
||||
accountId={selectedAccountId}
|
||||
currentPage={keysPage}
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
) : (
|
||||
<PermissionDeniedCallout permissionName="factor-api-key:list" />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@ jest.mock('providers/Timezone', () => ({
|
||||
},
|
||||
updateTimezone: jest.fn(),
|
||||
formatTimezoneAdjustedTimestamp: jest.fn(() => 'mock-date'),
|
||||
formatTimezoneAdjustedTimestampOptional: jest.fn(() => 'mock-date'),
|
||||
isAdaptationEnabled: true,
|
||||
setIsAdaptationEnabled: jest.fn(),
|
||||
}),
|
||||
|
||||
@@ -7,10 +7,10 @@ export enum FeatureKeys {
|
||||
GATEWAY = 'gateway',
|
||||
PREMIUM_SUPPORT = 'premium_support',
|
||||
ANOMALY_DETECTION = 'anomaly_detection',
|
||||
ONBOARDING_V3 = 'onboarding_v3',
|
||||
DOT_METRICS_ENABLED = 'dot_metrics_enabled',
|
||||
USE_JSON_BODY = 'use_json_body',
|
||||
USE_FINE_GRAINED_AUTHZ = 'use_fine_grained_authz',
|
||||
USE_DASHBOARD_V2 = 'use_dashboard_v2',
|
||||
EMABLE_AI_OBSERVABILITY = 'enable_ai_observability',
|
||||
ENABLE_METRICS_REDUCTION = 'enable_metrics_reduction',
|
||||
}
|
||||
|
||||
@@ -44,4 +44,5 @@ export enum LOCALSTORAGE {
|
||||
ACTIVE_SIGNOZ_INSTANCE_URL = 'ACTIVE_SIGNOZ_INSTANCE_URL',
|
||||
DASHBOARDS_LIST_VISIBLE_COLUMNS = 'DASHBOARDS_LIST_VISIBLE_COLUMNS',
|
||||
DASHBOARDS_LIST_VIEWS = 'DASHBOARDS_LIST_VIEWS',
|
||||
DASHBOARD_V2_PANEL_COLUMN_WIDTHS = 'DASHBOARD_V2_PANEL_COLUMN_WIDTHS',
|
||||
}
|
||||
|
||||
@@ -11,19 +11,13 @@ const ROUTES = {
|
||||
TRACE_DETAIL_OLD: '/trace-old/:id',
|
||||
TRACES_EXPLORER: '/traces-explorer',
|
||||
ONBOARDING: '/onboarding',
|
||||
GET_STARTED: '/get-started',
|
||||
GET_STARTED_WITH_CLOUD: '/get-started-with-signoz-cloud',
|
||||
GET_STARTED_APPLICATION_MONITORING: '/get-started/application-monitoring',
|
||||
GET_STARTED_LOGS_MANAGEMENT: '/get-started/logs-management',
|
||||
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',
|
||||
DASHBOARD: '/dashboard/:dashboardId',
|
||||
DASHBOARD_WIDGET: '/dashboard/:dashboardId/:widgetId',
|
||||
DASHBOARD_PANEL_EDITOR: '/dashboard/:dashboardId/panel/:panelId',
|
||||
EDIT_ALERTS: '/alerts/edit',
|
||||
LIST_ALL_ALERT: '/alerts',
|
||||
ALERTS_NEW: '/alerts/new',
|
||||
@@ -55,7 +49,9 @@ const ROUTES = {
|
||||
TRACE_EXPLORER: '/trace-explorer',
|
||||
BILLING: '/settings/billing',
|
||||
ROLES_SETTINGS: '/settings/roles',
|
||||
ROLE_CREATE: '/settings/roles/new',
|
||||
ROLE_DETAILS: '/settings/roles/:roleId',
|
||||
ROLE_EDIT: '/settings/roles/:roleId/edit',
|
||||
MEMBERS_SETTINGS: '/settings/members',
|
||||
SUPPORT: '/support',
|
||||
LOGS_SAVE_VIEWS: '/logs/saved-views',
|
||||
@@ -78,6 +74,7 @@ const ROUTES = {
|
||||
METRICS_EXPLORER: '/metrics-explorer/summary',
|
||||
METRICS_EXPLORER_EXPLORER: '/metrics-explorer/explorer',
|
||||
METRICS_EXPLORER_VIEWS: '/metrics-explorer/views',
|
||||
METRICS_EXPLORER_VOLUME_CONTROL: '/metrics-explorer/volume-control',
|
||||
API_MONITORING_BASE: '/api-monitoring',
|
||||
API_MONITORING: '/api-monitoring/explorer',
|
||||
METRICS_EXPLORER_BASE: '/metrics-explorer',
|
||||
|
||||
@@ -408,17 +408,15 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
|
||||
const isPublicDashboard = pathname.startsWith('/public/dashboard/');
|
||||
const isAIAssistantPage = pathname.startsWith('/ai-assistant/');
|
||||
// The V2 panel editor is a chromeless full-page route (no side nav / top nav),
|
||||
// like the onboarding and public-dashboard screens.
|
||||
const isPanelEditorV2 = routeKey === 'DASHBOARD_PANEL_EDITOR';
|
||||
|
||||
const renderFullScreen =
|
||||
pathname === ROUTES.GET_STARTED ||
|
||||
pathname === ROUTES.ONBOARDING ||
|
||||
pathname === ROUTES.GET_STARTED_WITH_CLOUD ||
|
||||
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_AZURE_MONITORING ||
|
||||
isPublicDashboard;
|
||||
isPublicDashboard ||
|
||||
isPanelEditorV2;
|
||||
|
||||
const [showTrialExpiryBanner, setShowTrialExpiryBanner] = useState(false);
|
||||
|
||||
|
||||
@@ -7,15 +7,17 @@
|
||||
|
||||
&--legend-right {
|
||||
flex-direction: row;
|
||||
|
||||
.chart-layout__legend-wrapper {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__legend-wrapper {
|
||||
// The inline height is the legend rectangle from calculateChartDimensions;
|
||||
// border-box keeps the padding inside it so the wrapper doesn't grow past
|
||||
// that height and steal space from the chart. overflow:hidden clips to the
|
||||
// rectangle so the virtualized legend scrolls within it.
|
||||
box-sizing: border-box;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
padding-left: 12px;
|
||||
padding-bottom: 12px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
.full-screen-header-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 24px 0;
|
||||
|
||||
.brand-logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.brand-logo-name {
|
||||
font-family: 'Work Sans', sans-serif;
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import history from 'lib/history';
|
||||
|
||||
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
|
||||
|
||||
import './FullScreenHeader.styles.scss';
|
||||
|
||||
export default function FullScreenHeader({
|
||||
overrideRoute,
|
||||
}: {
|
||||
overrideRoute?: string;
|
||||
}): React.ReactElement {
|
||||
const handleLogoClick = (): void => {
|
||||
history.push(overrideRoute || '/');
|
||||
};
|
||||
return (
|
||||
<div className="full-screen-header-container">
|
||||
<div className="brand-logo" onClick={handleLogoClick}>
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" />
|
||||
|
||||
<div className="brand-logo-name">SigNoz</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
FullScreenHeader.defaultProps = {
|
||||
overrideRoute: '/',
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
.members-settings {
|
||||
.members-settings-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-8);
|
||||
padding: var(--padding-4) var(--padding-2) var(--padding-6) var(--padding-4);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.members-settings {
|
||||
&__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -9,6 +9,7 @@ import EditMemberDrawer from 'components/EditMemberDrawer/EditMemberDrawer';
|
||||
import InviteMembersModal from 'components/InviteMembersModal/InviteMembersModal';
|
||||
import MembersTable, { MemberRow } from 'components/MembersTable/MembersTable';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs';
|
||||
import { toISOString } from 'utils/app';
|
||||
|
||||
import { FilterMode, MemberStatus, toMemberStatus } from './utils';
|
||||
@@ -26,7 +27,10 @@ function MembersSettings(): JSX.Element {
|
||||
// TODO(nuqs): Replace with nuqs once the nuqs setup and integration is done - for search
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [filterMode, setFilterMode] = useState<FilterMode>(FilterMode.All);
|
||||
const [isInviteModalOpen, setIsInviteModalOpen] = useState(false);
|
||||
const [isInviteModalOpen, setIsInviteModalOpen] = useQueryState(
|
||||
'invite',
|
||||
parseAsBoolean.withDefault(false),
|
||||
);
|
||||
const [selectedMember, setSelectedMember] = useState<MemberRow | null>(null);
|
||||
|
||||
const { data: usersData, isLoading, refetch: refetchUsers } = useListUsers();
|
||||
@@ -160,7 +164,7 @@ function MembersSettings(): JSX.Element {
|
||||
}, [refetchUsers]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="members-settings-page">
|
||||
<div className="members-settings">
|
||||
<div className="members-settings__header">
|
||||
<h1 className="members-settings__title">Members</h1>
|
||||
@@ -201,7 +205,7 @@ function MembersSettings(): JSX.Element {
|
||||
<Button
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={(): void => setIsInviteModalOpen(true)}
|
||||
onClick={(): void => void setIsInviteModalOpen(true)}
|
||||
>
|
||||
<Plus size={12} />
|
||||
Invite member
|
||||
@@ -221,7 +225,7 @@ function MembersSettings(): JSX.Element {
|
||||
|
||||
<InviteMembersModal
|
||||
open={isInviteModalOpen}
|
||||
onClose={(): void => setIsInviteModalOpen(false)}
|
||||
onClose={(): void => void setIsInviteModalOpen(null)}
|
||||
onComplete={handleInviteComplete}
|
||||
/>
|
||||
|
||||
@@ -231,7 +235,7 @@ function MembersSettings(): JSX.Element {
|
||||
onClose={handleDrawerClose}
|
||||
onComplete={handleMemberEditComplete}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -130,4 +130,14 @@ describe('MembersSettings (integration)', () => {
|
||||
screen.findAllByPlaceholderText('john@signoz.io'),
|
||||
).resolves.toHaveLength(3);
|
||||
});
|
||||
|
||||
it('opens InviteMembersModal when invite=true query param is present', async () => {
|
||||
render(<MembersSettings />, undefined, {
|
||||
initialRoute: '/settings/members?invite=true',
|
||||
});
|
||||
|
||||
await expect(
|
||||
screen.findAllByPlaceholderText('john@signoz.io'),
|
||||
).resolves.toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -191,9 +191,6 @@ function TimeSeries({
|
||||
if (metrics[0] && yAxisUnit) {
|
||||
updateMetricMetadata(
|
||||
{
|
||||
pathParams: {
|
||||
metricName: metricNames[0],
|
||||
},
|
||||
data: buildUpdateMetricYAxisUnitPayload(
|
||||
metricNames[0],
|
||||
metrics[0],
|
||||
|
||||
@@ -48,18 +48,14 @@ function AllAttributes({
|
||||
isLoading: isLoadingAttributes,
|
||||
isError: isErrorAttributes,
|
||||
refetch: refetchAttributes,
|
||||
} = useGetMetricAttributes(
|
||||
{
|
||||
metricName,
|
||||
},
|
||||
{
|
||||
start: minTime ? Math.floor(minTime / 1000000) : undefined,
|
||||
end: maxTime ? Math.floor(maxTime / 1000000) : undefined,
|
||||
},
|
||||
);
|
||||
} = useGetMetricAttributes({
|
||||
metricName,
|
||||
start: minTime ? Math.floor(minTime / 1000000) : undefined,
|
||||
end: maxTime ? Math.floor(maxTime / 1000000) : undefined,
|
||||
});
|
||||
|
||||
const attributes = useMemo(
|
||||
() => attributesData?.data?.attributes ?? [],
|
||||
() => attributesData?.data.attributes ?? [],
|
||||
[attributesData],
|
||||
);
|
||||
|
||||
|
||||
@@ -237,9 +237,6 @@ function Metadata({
|
||||
const handleSave = useCallback(() => {
|
||||
updateMetricMetadata(
|
||||
{
|
||||
pathParams: {
|
||||
metricName,
|
||||
},
|
||||
data: transformUpdateMetricMetadataRequest(metricName, metricMetadataState),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ import AllAttributes from './AllAttributes';
|
||||
import DashboardsAndAlertsPopover from './DashboardsAndAlertsPopover';
|
||||
import Highlights from './Highlights';
|
||||
import Metadata from './Metadata';
|
||||
import VolumeControlSection from '../VolumeControl/components/VolumeControlSection/VolumeControlSection';
|
||||
import { MetricDetailsProps } from './types';
|
||||
import { getMetricDetailsQuery } from './utils';
|
||||
|
||||
@@ -56,7 +57,7 @@ function MetricDetails({
|
||||
);
|
||||
|
||||
const metadata = useMemo(() => {
|
||||
if (!metricMetadataResponse?.data) {
|
||||
if (!metricMetadataResponse) {
|
||||
return null;
|
||||
}
|
||||
const { type, description, unit, temporality, isMonotonic } =
|
||||
@@ -190,6 +191,7 @@ function MetricDetails({
|
||||
isLoadingMetricMetadata={isLoadingMetricMetadata}
|
||||
refetchMetricMetadata={refetchMetricMetadata}
|
||||
/>
|
||||
<VolumeControlSection metricName={metricName} />
|
||||
<AllAttributes
|
||||
metricName={metricName}
|
||||
metricType={metadata?.type}
|
||||
|
||||
@@ -195,14 +195,12 @@ describe('Metadata', () => {
|
||||
expect(mockUseUpdateMetricMetadata).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
metricName: MOCK_METRIC_NAME,
|
||||
type: MetrictypesTypeDTO.sum,
|
||||
temporality: MetrictypesTemporalityDTO.cumulative,
|
||||
unit: 'By',
|
||||
isMonotonic: true,
|
||||
}),
|
||||
pathParams: {
|
||||
metricName: MOCK_METRIC_NAME,
|
||||
},
|
||||
}),
|
||||
expect.objectContaining({
|
||||
onSuccess: expect.any(Function),
|
||||
|
||||
@@ -77,6 +77,14 @@ jest.mock(
|
||||
},
|
||||
);
|
||||
|
||||
jest.mock(
|
||||
'container/MetricsExplorer/VolumeControl/components/VolumeControlSection/VolumeControlSection',
|
||||
() =>
|
||||
function MockVolumeControlSection(): JSX.Element {
|
||||
return <div data-testid="volume-control-section-mock">Volume Control</div>;
|
||||
},
|
||||
);
|
||||
|
||||
const useGetMetricMetadataMock = jest.spyOn(
|
||||
metricsExplorerHooks,
|
||||
'useGetMetricMetadata',
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
}
|
||||
|
||||
.metrics-table-container {
|
||||
padding-bottom: 48px;
|
||||
.ant-table {
|
||||
margin-left: -16px;
|
||||
margin-right: -16px;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
Querybuildertypesv5QueryRangeResponseDTO,
|
||||
Querybuildertypesv5TimeSeriesDataDTO,
|
||||
Querybuildertypesv5TimeSeriesDTO,
|
||||
Querybuildertypesv5TimeSeriesValueDTO,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
function findSeries(
|
||||
series: Querybuildertypesv5TimeSeriesDTO[] | null | undefined,
|
||||
label: string,
|
||||
): Querybuildertypesv5TimeSeriesDTO | undefined {
|
||||
return series?.find((entry) =>
|
||||
(entry.labels ?? []).some((value) => value.value === label),
|
||||
);
|
||||
}
|
||||
|
||||
function toChartValues(
|
||||
points: Querybuildertypesv5TimeSeriesValueDTO[],
|
||||
): [number, string][] {
|
||||
return points.map((point) => [
|
||||
Math.floor((point.timestamp ?? 0) / 1000),
|
||||
String(point.value ?? 0),
|
||||
]);
|
||||
}
|
||||
|
||||
export function buildVolumeChartPayload(
|
||||
response?: Querybuildertypesv5QueryRangeResponseDTO,
|
||||
): SuccessResponse<MetricRangePayloadProps> {
|
||||
const result = response?.data?.results?.[0] as
|
||||
| Querybuildertypesv5TimeSeriesDataDTO
|
||||
| undefined;
|
||||
const series = result?.aggregations?.[0]?.series;
|
||||
|
||||
const ingested = findSeries(series, 'ingested')?.values ?? [];
|
||||
const retained = findSeries(series, 'retained')?.values ?? [];
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
message: 'Success',
|
||||
error: null,
|
||||
payload: {
|
||||
data: {
|
||||
resultType: 'matrix',
|
||||
result: [
|
||||
{
|
||||
queryName: 'ingested',
|
||||
legend: 'Ingested',
|
||||
metric: {},
|
||||
values: toChartValues(ingested),
|
||||
},
|
||||
{
|
||||
queryName: 'retained',
|
||||
legend: 'Retained',
|
||||
metric: {},
|
||||
values: toChartValues(retained),
|
||||
},
|
||||
],
|
||||
newResult: { data: { result: [], resultType: 'matrix' } },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Gauge } from '@signozhq/icons';
|
||||
import { MetricreductionruletypesGettableReductionRuleDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
|
||||
interface VolumeControlBadgeProps {
|
||||
rule: MetricreductionruletypesGettableReductionRuleDTO;
|
||||
}
|
||||
|
||||
function VolumeControlBadge({ rule }: VolumeControlBadgeProps): JSX.Element {
|
||||
return (
|
||||
<Badge
|
||||
data-testid="vc-badge-active"
|
||||
variant="outline"
|
||||
color={!rule.active ? 'success' : 'warning'}
|
||||
>
|
||||
<Gauge size={12} />
|
||||
{!rule.active ? 'Active' : 'Pending'}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
export default VolumeControlBadge;
|
||||
@@ -0,0 +1,17 @@
|
||||
.chart {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
border: 1px solid var(--bg-slate-400, #1d212d);
|
||||
border-radius: 6px;
|
||||
padding: 12px 16px 0 16px;
|
||||
}
|
||||
|
||||
.chartTitle {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.chartBody {
|
||||
height: 340px;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { useGetMetricReductionRuleTimeseries } from 'api/generated/services/metrics';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import BarChart from 'container/DashboardContainer/visualization/charts/BarChart/BarChart';
|
||||
import { buildBaseConfig } from 'container/DashboardContainer/visualization/panels/utils/baseConfigBuilder';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useResizeObserver } from 'hooks/useDimensions';
|
||||
import { LegendPosition } from 'lib/uPlotV2/components/types';
|
||||
import { DrawStyle } from 'lib/uPlotV2/config/types';
|
||||
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import { useMemo, useRef } from 'react';
|
||||
|
||||
import { buildVolumeChartPayload } from '../../chartUtils';
|
||||
import styles from './VolumeControlChart.module.scss';
|
||||
|
||||
const COLOR_MAPPING: Record<string, string> = {
|
||||
Ingested: Color.BG_ROBIN_500,
|
||||
Retained: Color.BG_FOREST_500,
|
||||
};
|
||||
|
||||
interface VolumeControlChartProps {
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
function VolumeControlChart({ enabled }: VolumeControlChartProps): JSX.Element {
|
||||
const { data } = useGetMetricReductionRuleTimeseries({ query: { enabled } });
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { timezone } = useTimezone();
|
||||
const graphRef = useRef<HTMLDivElement>(null);
|
||||
const dimensions = useResizeObserver(graphRef);
|
||||
|
||||
const payload = useMemo(
|
||||
() => buildVolumeChartPayload(data?.data).payload,
|
||||
[data],
|
||||
);
|
||||
const chartData = useMemo(() => getUPlotChartData(payload), [payload]);
|
||||
|
||||
const config = useMemo(() => {
|
||||
const timestamps = (chartData[0] as number[]) ?? [];
|
||||
const builder = buildBaseConfig({
|
||||
id: 'metric-volume-control',
|
||||
isDarkMode,
|
||||
apiResponse: payload,
|
||||
timezone,
|
||||
panelType: PANEL_TYPES.BAR,
|
||||
yAxisUnit: 'short',
|
||||
onDragSelect: (): void => {},
|
||||
minTimeScale: timestamps[0],
|
||||
maxTimeScale: timestamps[timestamps.length - 1],
|
||||
});
|
||||
(payload.data.result ?? []).forEach((series) => {
|
||||
builder.addSeries({
|
||||
scaleKey: 'y',
|
||||
drawStyle: DrawStyle.Bar,
|
||||
label: series.legend ?? series.queryName,
|
||||
colorMapping: COLOR_MAPPING,
|
||||
isDarkMode,
|
||||
});
|
||||
});
|
||||
return builder;
|
||||
}, [payload, chartData, isDarkMode, timezone]);
|
||||
|
||||
return (
|
||||
<div className={styles.chart} data-testid="volume-control-chart">
|
||||
<Typography.Text className={styles.chartTitle} size={'small'}>
|
||||
Series volume over time · ingested vs retained
|
||||
</Typography.Text>
|
||||
<div className={styles.chartBody} ref={graphRef}>
|
||||
{dimensions.width > 0 && (
|
||||
<BarChart
|
||||
config={config}
|
||||
data={chartData}
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
yAxisUnit="short"
|
||||
timezone={timezone}
|
||||
legendConfig={{ position: LegendPosition.BOTTOM }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default VolumeControlChart;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user