mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-18 03:35:57 +08:00
commit
36d8bc7bc6
5
.github/workflows/e2e-k3s.yaml
vendored
5
.github/workflows/e2e-k3s.yaml
vendored
@ -52,14 +52,11 @@ jobs:
|
|||||||
helm install my-release signoz/signoz -n platform \
|
helm install my-release signoz/signoz -n platform \
|
||||||
--wait \
|
--wait \
|
||||||
--timeout 10m0s \
|
--timeout 10m0s \
|
||||||
--set cloud=null \
|
|
||||||
--set frontend.service.type=LoadBalancer \
|
--set frontend.service.type=LoadBalancer \
|
||||||
--set query-service.image.tag=$DOCKER_TAG \
|
--set queryService.image.tag=$DOCKER_TAG \
|
||||||
--set frontend.image.tag=$DOCKER_TAG
|
--set frontend.image.tag=$DOCKER_TAG
|
||||||
|
|
||||||
# get pods, services and the container images
|
# get pods, services and the container images
|
||||||
kubectl describe deploy/my-release-frontend -n platform | grep Image
|
|
||||||
kubectl describe statefulset/my-release-query-service -n platform | grep Image
|
|
||||||
kubectl get pods -n platform
|
kubectl get pods -n platform
|
||||||
kubectl get svc -n platform
|
kubectl get svc -n platform
|
||||||
|
|
||||||
|
25
.github/workflows/repo-stats.yml
vendored
Normal file
25
.github/workflows/repo-stats.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run this once per day, towards the end of the day for keeping the most
|
||||||
|
# recent data point most meaningful (hours are interpreted in UTC).
|
||||||
|
- cron: "0 8 * * *"
|
||||||
|
workflow_dispatch: # Allow for running this manually.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
j1:
|
||||||
|
name: repostats
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: run-ghrs
|
||||||
|
uses: jgehrcke/github-repo-stats@v1.1.0
|
||||||
|
with:
|
||||||
|
# Define the stats repository (the repo to fetch
|
||||||
|
# stats for and to generate the report for).
|
||||||
|
# Remove the parameter when the stats repository
|
||||||
|
# and the data repository are the same.
|
||||||
|
repository: signoz/signoz
|
||||||
|
# Set a GitHub API token that can read the stats
|
||||||
|
# repository, and that can push to the data
|
||||||
|
# repository (which this workflow file lives in),
|
||||||
|
# to store data and the report files.
|
||||||
|
ghtoken: ${{ github.token }}
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -42,4 +42,11 @@ frontend/cypress.env.json
|
|||||||
|
|
||||||
frontend/*.env
|
frontend/*.env
|
||||||
pkg/query-service/signoz.db
|
pkg/query-service/signoz.db
|
||||||
|
|
||||||
|
pkg/query-service/tframe/test-deploy/data/
|
||||||
|
|
||||||
|
|
||||||
|
# local data
|
||||||
|
|
||||||
/deploy/docker/clickhouse-setup/data/
|
/deploy/docker/clickhouse-setup/data/
|
||||||
|
/deploy/docker-swarm/clickhouse-setup/data/
|
||||||
|
@ -21,6 +21,12 @@ Need to update [https://github.com/SigNoz/signoz/tree/main/frontend](https://git
|
|||||||
- comment out frontend service section at `deploy/docker/clickhouse-setup/docker-compose.yaml#L59`
|
- comment out frontend service section at `deploy/docker/clickhouse-setup/docker-compose.yaml#L59`
|
||||||
- run `cd deploy` to move to deploy directory
|
- run `cd deploy` to move to deploy directory
|
||||||
- Install signoz locally without the frontend
|
- Install signoz locally without the frontend
|
||||||
|
- Add below configuration to query-service section at `docker/clickhouse-setup/docker-compose.yaml#L36`
|
||||||
|
|
||||||
|
```docker
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
```
|
||||||
- If you are using x86_64 processors (All Intel/AMD processors) run `sudo docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d`
|
- If you are using x86_64 processors (All Intel/AMD processors) run `sudo docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d`
|
||||||
- If you are on arm64 processors (Apple M1 Macbooks) run `sudo docker-compose -f docker/clickhouse-setup/docker-compose.arm.yaml up -d`
|
- If you are on arm64 processors (Apple M1 Macbooks) run `sudo docker-compose -f docker/clickhouse-setup/docker-compose.arm.yaml up -d`
|
||||||
- `cd ../frontend` and change baseURL to `http://localhost:8080` in file `src/constants/env.ts`
|
- `cd ../frontend` and change baseURL to `http://localhost:8080` in file `src/constants/env.ts`
|
||||||
@ -47,20 +53,32 @@ Need to update [https://github.com/SigNoz/signoz/tree/main/pkg/query-service](ht
|
|||||||
### To run ClickHouse setup (recommended for local development)
|
### To run ClickHouse setup (recommended for local development)
|
||||||
|
|
||||||
- git clone https://github.com/SigNoz/signoz.git
|
- git clone https://github.com/SigNoz/signoz.git
|
||||||
|
- run `cd signoz` to move to signoz directory
|
||||||
- run `sudo make dev-setup` to configure local setup to run query-service
|
- run `sudo make dev-setup` to configure local setup to run query-service
|
||||||
- comment out frontend service section at `docker/clickhouse-setup/docker-compose.yaml#L45`
|
- comment out frontend service section at `docker/clickhouse-setup/docker-compose.yaml#L45`
|
||||||
- comment out query-service section at `docker/clickhouse-setup/docker-compose.yaml#L28`
|
- comment out query-service section at `docker/clickhouse-setup/docker-compose.yaml#L28`
|
||||||
- add below configuration to clickhouse section at `docker/clickhouse-setup/docker-compose.yaml`
|
- add below configuration to clickhouse section at `docker/clickhouse-setup/docker-compose.yaml#L6`
|
||||||
```
|
```docker
|
||||||
expose:
|
expose:
|
||||||
- 9000
|
- 9000
|
||||||
ports:
|
ports:
|
||||||
- 9001:9000
|
- 9001:9000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- run `cd pkg/query-service/` to move to query-service directory
|
||||||
|
- Open ./constants/constants.go
|
||||||
|
- Replace ```const RELATIONAL_DATASOURCE_PATH = "/var/lib/signoz/signoz.db"``` \
|
||||||
|
with ```const RELATIONAL_DATASOURCE_PATH = "./signoz.db".```
|
||||||
|
|
||||||
- Install signoz locally without the frontend and query-service
|
- Install signoz locally without the frontend and query-service
|
||||||
- If you are using x86_64 processors (All Intel/AMD processors) run `sudo make run-x86`
|
- If you are using x86_64 processors (All Intel/AMD processors) run `sudo make run-x86`
|
||||||
- If you are on arm64 processors (Apple M1 Macbooks) run `sudo make run-arm`
|
- If you are on arm64 processors (Apple M1 Macbooks) run `sudo make run-arm`
|
||||||
|
|
||||||
|
#### Run locally
|
||||||
|
```console
|
||||||
|
ClickHouseUrl=tcp://localhost:9001 STORAGE=clickhouse go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
> Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh`
|
> Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh`
|
||||||
|
|
||||||
**_Query Service should now be available at `http://localhost:8080`_**
|
**_Query Service should now be available at `http://localhost:8080`_**
|
||||||
@ -68,13 +86,13 @@ Need to update [https://github.com/SigNoz/signoz/tree/main/pkg/query-service](ht
|
|||||||
> If you want to see how, frontend plays with query service, you can run frontend also in you local env with the baseURL changed to `http://localhost:8080` in file `src/constants/env.ts` as the query-service is now running at port `8080`
|
> If you want to see how, frontend plays with query service, you can run frontend also in you local env with the baseURL changed to `http://localhost:8080` in file `src/constants/env.ts` as the query-service is now running at port `8080`
|
||||||
|
|
||||||
---
|
---
|
||||||
Instead of configuring a local setup, you can also use [Gitpod](https://www.gitpod.io/), a VSCode-based Web IDE.
|
<!-- Instead of configuring a local setup, you can also use [Gitpod](https://www.gitpod.io/), a VSCode-based Web IDE.
|
||||||
|
|
||||||
Click the button below. A workspace with all required environments will be created.
|
Click the button below. A workspace with all required environments will be created.
|
||||||
|
|
||||||
[](https://gitpod.io/#https://github.com/SigNoz/signoz)
|
[](https://gitpod.io/#https://github.com/SigNoz/signoz)
|
||||||
|
|
||||||
> To use it on your forked repo, edit the 'Open in Gitpod' button url to `https://gitpod.io/#https://github.com/<your-github-username>/signoz`
|
> To use it on your forked repo, edit the 'Open in Gitpod' button url to `https://gitpod.io/#https://github.com/<your-github-username>/signoz` -->
|
||||||
|
|
||||||
# Contribute to SigNoz Helm Chart
|
# Contribute to SigNoz Helm Chart
|
||||||
|
|
||||||
|
42
Makefile
42
Makefile
@ -12,6 +12,8 @@ BUILD_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
|
|||||||
FRONTEND_DIRECTORY ?= frontend
|
FRONTEND_DIRECTORY ?= frontend
|
||||||
FLATTENER_DIRECTORY ?= pkg/processors/flattener
|
FLATTENER_DIRECTORY ?= pkg/processors/flattener
|
||||||
QUERY_SERVICE_DIRECTORY ?= pkg/query-service
|
QUERY_SERVICE_DIRECTORY ?= pkg/query-service
|
||||||
|
STANDALONE_DIRECTORY ?= deploy/docker/clickhouse-setup
|
||||||
|
SWARM_DIRECTORY ?= deploy/docker-swarm/clickhouse-setup
|
||||||
|
|
||||||
REPONAME ?= signoz
|
REPONAME ?= signoz
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
@ -38,7 +40,8 @@ build-frontend-amd64:
|
|||||||
@echo "--> Building frontend docker image for amd64"
|
@echo "--> Building frontend docker image for amd64"
|
||||||
@echo "------------------"
|
@echo "------------------"
|
||||||
@cd $(FRONTEND_DIRECTORY) && \
|
@cd $(FRONTEND_DIRECTORY) && \
|
||||||
docker build -f Dockerfile --no-cache -t $(REPONAME)/$(FRONTEND_DOCKER_IMAGE):$(DOCKER_TAG) --build-arg TARGETPLATFORM="linux/amd64" .
|
docker build -f Dockerfile --no-cache -t $(REPONAME)/$(FRONTEND_DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
|
--build-arg TARGETPLATFORM="linux/amd64" .
|
||||||
|
|
||||||
# Step to build and push docker image of frontend(used in push pipeline)
|
# Step to build and push docker image of frontend(used in push pipeline)
|
||||||
build-push-frontend:
|
build-push-frontend:
|
||||||
@ -46,7 +49,8 @@ build-push-frontend:
|
|||||||
@echo "--> Building and pushing frontend docker image"
|
@echo "--> Building and pushing frontend docker image"
|
||||||
@echo "------------------"
|
@echo "------------------"
|
||||||
@cd $(FRONTEND_DIRECTORY) && \
|
@cd $(FRONTEND_DIRECTORY) && \
|
||||||
docker buildx build --file Dockerfile --progress plane --no-cache --push --platform linux/amd64 --tag $(REPONAME)/$(FRONTEND_DOCKER_IMAGE):$(DOCKER_TAG) .
|
docker buildx build --file Dockerfile --progress plane --no-cache --push --platform linux/amd64 \
|
||||||
|
--tag $(REPONAME)/$(FRONTEND_DOCKER_IMAGE):$(DOCKER_TAG) .
|
||||||
|
|
||||||
# Steps to build and push docker image of query service
|
# Steps to build and push docker image of query service
|
||||||
.PHONY: build-query-service-amd64 build-push-query-service
|
.PHONY: build-query-service-amd64 build-push-query-service
|
||||||
@ -56,7 +60,8 @@ build-query-service-amd64:
|
|||||||
@echo "--> Building query-service docker image for amd64"
|
@echo "--> Building query-service docker image for amd64"
|
||||||
@echo "------------------"
|
@echo "------------------"
|
||||||
@cd $(QUERY_SERVICE_DIRECTORY) && \
|
@cd $(QUERY_SERVICE_DIRECTORY) && \
|
||||||
docker build -f Dockerfile --no-cache -t $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) . --build-arg TARGETPLATFORM="linux/amd64" --build-arg LD_FLAGS=$(LD_FLAGS)
|
docker build -f Dockerfile --no-cache -t $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
|
--build-arg TARGETPLATFORM="linux/amd64" --build-arg LD_FLAGS=$(LD_FLAGS) .
|
||||||
|
|
||||||
# Step to build and push docker image of query in amd64 and arm64 (used in push pipeline)
|
# Step to build and push docker image of query in amd64 and arm64 (used in push pipeline)
|
||||||
build-push-query-service:
|
build-push-query-service:
|
||||||
@ -64,7 +69,9 @@ build-push-query-service:
|
|||||||
@echo "--> Building and pushing query-service docker image"
|
@echo "--> Building and pushing query-service docker image"
|
||||||
@echo "------------------"
|
@echo "------------------"
|
||||||
@cd $(QUERY_SERVICE_DIRECTORY) && \
|
@cd $(QUERY_SERVICE_DIRECTORY) && \
|
||||||
docker buildx build --file Dockerfile --progress plane --no-cache --push --platform linux/arm64,linux/amd64 --build-arg LD_FLAGS=$(LD_FLAGS) --tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) .
|
docker buildx build --file Dockerfile --progress plane --no-cache \
|
||||||
|
--push --platform linux/arm64,linux/amd64 --build-arg LD_FLAGS=$(LD_FLAGS) \
|
||||||
|
--tag $(REPONAME)/$(QUERY_SERVICE_DOCKER_IMAGE):$(DOCKER_TAG) .
|
||||||
|
|
||||||
# Steps to build and push docker image of flattener
|
# Steps to build and push docker image of flattener
|
||||||
.PHONY: build-flattener-amd64 build-push-flattener
|
.PHONY: build-flattener-amd64 build-push-flattener
|
||||||
@ -74,7 +81,8 @@ build-flattener-amd64:
|
|||||||
@echo "--> Building flattener docker image for amd64"
|
@echo "--> Building flattener docker image for amd64"
|
||||||
@echo "------------------"
|
@echo "------------------"
|
||||||
@cd $(FLATTENER_DIRECTORY) && \
|
@cd $(FLATTENER_DIRECTORY) && \
|
||||||
docker build -f Dockerfile --no-cache -t $(REPONAME)/$(FLATTERNER_DOCKER_IMAGE):$(DOCKER_TAG) . --build-arg TARGETPLATFORM="linux/amd64"
|
docker build -f Dockerfile --no-cache -t $(REPONAME)/$(FLATTERNER_DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
|
--build-arg TARGETPLATFORM="linux/amd64" .
|
||||||
|
|
||||||
# Step to build and push docker image of flattener in amd64 (used in push pipeline)
|
# Step to build and push docker image of flattener in amd64 (used in push pipeline)
|
||||||
build-push-flattener:
|
build-push-flattener:
|
||||||
@ -82,7 +90,9 @@ build-push-flattener:
|
|||||||
@echo "--> Building and pushing flattener docker image"
|
@echo "--> Building and pushing flattener docker image"
|
||||||
@echo "------------------"
|
@echo "------------------"
|
||||||
@cd $(FLATTENER_DIRECTORY) && \
|
@cd $(FLATTENER_DIRECTORY) && \
|
||||||
docker buildx build --file Dockerfile --progress plane --no-cache --push --platform linux/arm64,linux/amd64 --tag $(REPONAME)/$(FLATTERNER_DOCKER_IMAGE):$(DOCKER_TAG) .
|
docker buildx build --file Dockerfile --progress plane \
|
||||||
|
--no-cache --push --platform linux/arm64,linux/amd64 \
|
||||||
|
--tag $(REPONAME)/$(FLATTERNER_DOCKER_IMAGE):$(DOCKER_TAG) .
|
||||||
|
|
||||||
dev-setup:
|
dev-setup:
|
||||||
mkdir -p /var/lib/signoz
|
mkdir -p /var/lib/signoz
|
||||||
@ -93,7 +103,23 @@ dev-setup:
|
|||||||
@echo "------------------"
|
@echo "------------------"
|
||||||
|
|
||||||
run-x86:
|
run-x86:
|
||||||
@sudo docker-compose -f ./deploy/docker/clickhouse-setup/docker-compose.yaml up -d
|
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml up -d
|
||||||
|
|
||||||
run-arm:
|
run-arm:
|
||||||
@sudo docker-compose -f ./deploy/docker/clickhouse-setup/docker-compose.arm.yaml up -d
|
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.arm.yaml up -d
|
||||||
|
|
||||||
|
down-x86:
|
||||||
|
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml down -v
|
||||||
|
|
||||||
|
down-arm:
|
||||||
|
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.arm.yaml down -v
|
||||||
|
|
||||||
|
clear-standalone-data:
|
||||||
|
@cd $(STANDALONE_DIRECTORY)
|
||||||
|
@docker run --rm -v "data:/pwd" busybox \
|
||||||
|
sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*"
|
||||||
|
|
||||||
|
clear-swarm-data:
|
||||||
|
@cd $(SWARM_DIRECTORY)
|
||||||
|
@docker run --rm -v "data:/pwd" busybox \
|
||||||
|
sh -c "cd /pwd && rm -rf alertmanager/* clickhouse/* signoz/*"
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="License" src="https://img.shields.io/badge/license-MIT-brightgreen"> </a>
|
<img alt="License" src="https://img.shields.io/badge/license-MIT-brightgreen"> </a>
|
||||||
<img alt="Downloads" src="https://img.shields.io/docker/pulls/signoz/frontend?label=Downloads"> </a>
|
<img alt="Downloads" src="https://img.shields.io/docker/pulls/signoz/query-service?label=Downloads"> </a>
|
||||||
<img alt="GitHub issues" src="https://img.shields.io/github/issues/signoz/signoz"> </a>
|
<img alt="GitHub issues" src="https://img.shields.io/github/issues/signoz/signoz"> </a>
|
||||||
<a href="https://twitter.com/intent/tweet?text=Monitor%20your%20applications%20and%20troubleshoot%20problems%20with%20SigNoz,%20an%20open-source%20alternative%20to%20DataDog,%20NewRelic.&url=https://signoz.io/&via=SigNozHQ&hashtags=opensource,signoz,observability">
|
<a href="https://twitter.com/intent/tweet?text=Monitor%20your%20applications%20and%20troubleshoot%20problems%20with%20SigNoz,%20an%20open-source%20alternative%20to%20DataDog,%20NewRelic.&url=https://signoz.io/&via=SigNozHQ&hashtags=opensource,signoz,observability">
|
||||||
<img alt="tweet" src="https://img.shields.io/twitter/url/http/shields.io.svg?style=social"> </a>
|
<img alt="tweet" src="https://img.shields.io/twitter/url/http/shields.io.svg?style=social"> </a>
|
||||||
@ -34,8 +34,10 @@ SigNoz helps developers monitor applications and troubleshoot problems in their
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
<br />
|
||||||

|

|
||||||
|
<br />
|
||||||
|

|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
|
35
deploy/docker-swarm/clickhouse-setup/alertmanager.yml
Normal file
35
deploy/docker-swarm/clickhouse-setup/alertmanager.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
global:
|
||||||
|
resolve_timeout: 1m
|
||||||
|
slack_api_url: 'https://hooks.slack.com/services/xxx'
|
||||||
|
|
||||||
|
route:
|
||||||
|
receiver: 'slack-notifications'
|
||||||
|
|
||||||
|
receivers:
|
||||||
|
- name: 'slack-notifications'
|
||||||
|
slack_configs:
|
||||||
|
- channel: '#alerts'
|
||||||
|
send_resolved: true
|
||||||
|
icon_url: https://avatars3.githubusercontent.com/u/3380462
|
||||||
|
title: |-
|
||||||
|
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}
|
||||||
|
{{- if gt (len .CommonLabels) (len .GroupLabels) -}}
|
||||||
|
{{" "}}(
|
||||||
|
{{- with .CommonLabels.Remove .GroupLabels.Names }}
|
||||||
|
{{- range $index, $label := .SortedPairs -}}
|
||||||
|
{{ if $index }}, {{ end }}
|
||||||
|
{{- $label.Name }}="{{ $label.Value -}}"
|
||||||
|
{{- end }}
|
||||||
|
{{- end -}}
|
||||||
|
)
|
||||||
|
{{- end }}
|
||||||
|
text: >-
|
||||||
|
{{ range .Alerts -}}
|
||||||
|
*Alert:* {{ .Annotations.title }}{{ if .Labels.severity }} - `{{ .Labels.severity }}`{{ end }}
|
||||||
|
|
||||||
|
*Description:* {{ .Annotations.description }}
|
||||||
|
|
||||||
|
*Details:*
|
||||||
|
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}`
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
11
deploy/docker-swarm/clickhouse-setup/alerts.yml
Normal file
11
deploy/docker-swarm/clickhouse-setup/alerts.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
groups:
|
||||||
|
- name: ExampleCPULoadGroup
|
||||||
|
rules:
|
||||||
|
- alert: HighCpuLoad
|
||||||
|
expr: system_cpu_load_average_1m > 0.1
|
||||||
|
for: 0m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
summary: High CPU load
|
||||||
|
description: "CPU load is > 0.1\n VALUE = {{ $value }}\n LABELS = {{ $labels }}"
|
@ -1,19 +1,14 @@
|
|||||||
version: "3"
|
version: "3.9"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
image: yandex/clickhouse-server
|
image: yandex/clickhouse-server:21.12.3.32
|
||||||
expose:
|
|
||||||
- 8123
|
|
||||||
- 9000
|
|
||||||
ports:
|
|
||||||
- 9001:9000
|
|
||||||
- 8123:8123
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
||||||
- ./docker-entrypoint-initdb.d/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
|
|
||||||
- ./data/clickhouse/:/var/lib/clickhouse/
|
- ./data/clickhouse/:/var/lib/clickhouse/
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
||||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
||||||
@ -21,10 +16,20 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
|
alertmanager:
|
||||||
|
image: signoz/alertmanager:0.5.0
|
||||||
|
volumes:
|
||||||
|
- ./alertmanager.yml:/prometheus/alertmanager.yml
|
||||||
|
- ./data/alertmanager:/data
|
||||||
|
command:
|
||||||
|
- '--config.file=/prometheus/alertmanager.yml'
|
||||||
|
- '--storage.path=/data'
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.4.1
|
image: signoz/query-service:0.7.2
|
||||||
container_name: query-service
|
|
||||||
restart: always
|
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
ports:
|
ports:
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
@ -35,72 +40,75 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- ClickHouseUrl=tcp://clickhouse:9000
|
- ClickHouseUrl=tcp://clickhouse:9000
|
||||||
- STORAGE=clickhouse
|
- STORAGE=clickhouse
|
||||||
- POSTHOG_API_KEY=H-htDCae7CR3RV57gUzmol6IAKtm5IMCvbcm_fwnL-w
|
|
||||||
- GODEBUG=netdns=go
|
- GODEBUG=netdns=go
|
||||||
|
- TELEMETRY_ENABLED=true
|
||||||
|
- DEPLOYMENT_TYPE=docker-swarm
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
- clickhouse
|
- clickhouse
|
||||||
|
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.4.1
|
image: signoz/frontend:0.7.2
|
||||||
container_name: frontend
|
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
links:
|
|
||||||
- "query-service"
|
|
||||||
ports:
|
ports:
|
||||||
- "3301:3301"
|
- "3301:3301"
|
||||||
volumes:
|
volumes:
|
||||||
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
|
||||||
otel-collector:
|
otel-collector:
|
||||||
image: signoz/otelcontribcol:0.4.0
|
image: signoz/otelcontribcol:0.43.0
|
||||||
command: ["--config=/etc/otel-collector-config.yaml", "--mem-ballast-size-mib=2000"]
|
command: ["--config=/etc/otel-collector-config.yaml"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
ports:
|
ports:
|
||||||
- "1777:1777" # pprof extension
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
- "8887:8888" # Prometheus metrics exposed by the agent
|
- "4318:4318" # OTLP HTTP receiver
|
||||||
- "14268:14268" # Jaeger receiver
|
# - "8889:8889" # Prometheus metrics exposed by the agent
|
||||||
- "55678" # OpenCensus receiver
|
# - "13133" # health_check
|
||||||
- "55680:55680" # OTLP HTTP/2.0 legacy port
|
# - "14268:14268" # Jaeger receiver
|
||||||
- "55681:55681" # OTLP HTTP/1.0 receiver
|
# - "55678:55678" # OpenCensus receiver
|
||||||
- "4317:4317" # OTLP GRPC receiver
|
# - "55679:55679" # zpages extension
|
||||||
- "55679:55679" # zpages extension
|
# - "55680:55680" # OTLP gRPC legacy receiver
|
||||||
- "13133" # health_check
|
# - "55681:55681" # OTLP HTTP legacy receiver
|
||||||
deploy:
|
deploy:
|
||||||
mode: replicated
|
mode: replicated
|
||||||
replicas: 3
|
replicas: 3
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2000m
|
||||||
depends_on:
|
depends_on:
|
||||||
- clickhouse
|
- clickhouse
|
||||||
|
|
||||||
otel-collector-hostmetrics:
|
otel-collector-metrics:
|
||||||
image: signoz/otelcontribcol:0.4.0
|
image: signoz/otelcontribcol:0.43.0
|
||||||
command: ["--config=/etc/otel-collector-config-hostmetrics.yaml", "--mem-ballast-size-mib=683"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config-hostmetrics.yaml:/etc/otel-collector-config-hostmetrics.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
|
deploy:
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
- clickhouse
|
- clickhouse
|
||||||
|
|
||||||
|
|
||||||
hotrod:
|
hotrod:
|
||||||
image: jaegertracing/example-hotrod:latest
|
image: jaegertracing/example-hotrod:1.30
|
||||||
container_name: hotrod
|
|
||||||
ports:
|
|
||||||
- "9000:8080"
|
|
||||||
command: ["all"]
|
command: ["all"]
|
||||||
environment:
|
environment:
|
||||||
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
|
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
|
||||||
|
logging:
|
||||||
|
options:
|
||||||
|
max-size: 50m
|
||||||
|
max-file: "3"
|
||||||
|
|
||||||
load-hotrod:
|
load-hotrod:
|
||||||
image: "grubykarol/locust:1.2.3-python3.9-alpine3.12"
|
image: "grubykarol/locust:1.2.3-python3.9-alpine3.12"
|
||||||
container_name: load-hotrod
|
|
||||||
hostname: load-hotrod
|
hostname: load-hotrod
|
||||||
ports:
|
|
||||||
- "8089:8089"
|
|
||||||
environment:
|
environment:
|
||||||
ATTACKED_HOST: http://hotrod:8080
|
ATTACKED_HOST: http://hotrod:8080
|
||||||
LOCUST_MODE: standalone
|
LOCUST_MODE: standalone
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
receivers:
|
receivers:
|
||||||
|
otlp/spanmetrics:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: "localhost:12345"
|
||||||
otlp:
|
otlp:
|
||||||
protocols:
|
protocols:
|
||||||
grpc:
|
grpc:
|
||||||
@ -7,18 +11,34 @@ receivers:
|
|||||||
protocols:
|
protocols:
|
||||||
grpc:
|
grpc:
|
||||||
thrift_http:
|
thrift_http:
|
||||||
|
hostmetrics:
|
||||||
|
collection_interval: 30s
|
||||||
|
scrapers:
|
||||||
|
cpu:
|
||||||
|
load:
|
||||||
|
memory:
|
||||||
|
disk:
|
||||||
|
filesystem:
|
||||||
|
network:
|
||||||
processors:
|
processors:
|
||||||
batch:
|
batch:
|
||||||
send_batch_size: 1000
|
send_batch_size: 1000
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
memory_limiter:
|
signozspanmetrics/prometheus:
|
||||||
# Same as --mem-ballast-size-mib CLI argument
|
metrics_exporter: prometheus
|
||||||
ballast_size_mib: 683
|
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
||||||
# 80% of maximum memory up to 2G
|
dimensions_cache_size: 10000
|
||||||
limit_mib: 1500
|
# memory_limiter:
|
||||||
# 25% of limit up to 2G
|
# # 80% of maximum memory up to 2G
|
||||||
spike_limit_mib: 512
|
# limit_mib: 1500
|
||||||
check_interval: 5s
|
# # 25% of limit up to 2G
|
||||||
|
# spike_limit_mib: 512
|
||||||
|
# check_interval: 5s
|
||||||
|
#
|
||||||
|
# # 50% of the maximum memory
|
||||||
|
# limit_percentage: 50
|
||||||
|
# # 20% of max memory usage spike expected
|
||||||
|
# spike_limit_percentage: 20
|
||||||
# queued_retry:
|
# queued_retry:
|
||||||
# num_workers: 4
|
# num_workers: 4
|
||||||
# queue_size: 100
|
# queue_size: 100
|
||||||
@ -33,15 +53,19 @@ exporters:
|
|||||||
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
|
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
|
||||||
resource_to_telemetry_conversion:
|
resource_to_telemetry_conversion:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
prometheus:
|
||||||
|
endpoint: "0.0.0.0:8889"
|
||||||
service:
|
service:
|
||||||
extensions: [health_check, zpages]
|
extensions: [health_check, zpages]
|
||||||
pipelines:
|
pipelines:
|
||||||
traces:
|
traces:
|
||||||
receivers: [jaeger, otlp]
|
receivers: [jaeger, otlp]
|
||||||
processors: [batch]
|
processors: [signozspanmetrics/prometheus, batch]
|
||||||
exporters: [clickhouse]
|
exporters: [clickhouse]
|
||||||
metrics:
|
metrics:
|
||||||
receivers: [otlp]
|
receivers: [otlp, hostmetrics]
|
||||||
processors: [batch]
|
processors: [batch]
|
||||||
exporters: [clickhousemetricswrite]
|
exporters: [clickhousemetricswrite]
|
||||||
|
metrics/spanmetrics:
|
||||||
|
receivers: [otlp/spanmetrics]
|
||||||
|
exporters: [prometheus]
|
@ -0,0 +1,47 @@
|
|||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
http:
|
||||||
|
|
||||||
|
# Data sources: metrics
|
||||||
|
prometheus:
|
||||||
|
config:
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: "otel-collector"
|
||||||
|
scrape_interval: 30s
|
||||||
|
static_configs:
|
||||||
|
- targets: ["otel-collector:8889"]
|
||||||
|
processors:
|
||||||
|
batch:
|
||||||
|
send_batch_size: 1000
|
||||||
|
timeout: 10s
|
||||||
|
# memory_limiter:
|
||||||
|
# # 80% of maximum memory up to 2G
|
||||||
|
# limit_mib: 1500
|
||||||
|
# # 25% of limit up to 2G
|
||||||
|
# spike_limit_mib: 512
|
||||||
|
# check_interval: 5s
|
||||||
|
#
|
||||||
|
# # 50% of the maximum memory
|
||||||
|
# limit_percentage: 50
|
||||||
|
# # 20% of max memory usage spike expected
|
||||||
|
# spike_limit_percentage: 20
|
||||||
|
# queued_retry:
|
||||||
|
# num_workers: 4
|
||||||
|
# queue_size: 100
|
||||||
|
# retry_on_failure: true
|
||||||
|
extensions:
|
||||||
|
health_check: {}
|
||||||
|
zpages: {}
|
||||||
|
exporters:
|
||||||
|
clickhousemetricswrite:
|
||||||
|
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
|
||||||
|
|
||||||
|
service:
|
||||||
|
extensions: [health_check, zpages]
|
||||||
|
pipelines:
|
||||||
|
metrics:
|
||||||
|
receivers: [otlp, prometheus]
|
||||||
|
processors: [batch]
|
||||||
|
exporters: [clickhousemetricswrite]
|
@ -9,12 +9,13 @@ alerting:
|
|||||||
alertmanagers:
|
alertmanagers:
|
||||||
- static_configs:
|
- static_configs:
|
||||||
- targets:
|
- targets:
|
||||||
# - alertmanager:9093
|
- alertmanager:9093
|
||||||
|
|
||||||
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
|
||||||
rule_files:
|
rule_files:
|
||||||
# - "first_rules.yml"
|
# - "first_rules.yml"
|
||||||
# - "second_rules.yml"
|
# - "second_rules.yml"
|
||||||
|
- 'alerts.yml'
|
||||||
|
|
||||||
# A scrape configuration containing exactly one endpoint to scrape:
|
# A scrape configuration containing exactly one endpoint to scrape:
|
||||||
# Here it's Prometheus itself.
|
# Here it's Prometheus itself.
|
||||||
|
@ -45,6 +45,33 @@
|
|||||||
</client>
|
</client>
|
||||||
</openSSL>
|
</openSSL>
|
||||||
|
|
||||||
|
<!-- Example config for tiered storage -->
|
||||||
|
<!-- <storage_configuration> -->
|
||||||
|
<!-- <disks> -->
|
||||||
|
<!-- <default> -->
|
||||||
|
<!-- </default> -->
|
||||||
|
<!-- <s3> -->
|
||||||
|
<!-- <type>s3</type> -->
|
||||||
|
<!-- <endpoint>http://172.17.0.1:9100/test/random/</endpoint> -->
|
||||||
|
<!-- <access_key_id>ash</access_key_id> -->
|
||||||
|
<!-- <secret_access_key>password</secret_access_key> -->
|
||||||
|
<!-- </s3> -->
|
||||||
|
<!-- </disks> -->
|
||||||
|
<!-- <policies> -->
|
||||||
|
<!-- <tiered> -->
|
||||||
|
<!-- <volumes> -->
|
||||||
|
<!-- <default> -->
|
||||||
|
<!-- <disk>default</disk> -->
|
||||||
|
<!-- </default> -->
|
||||||
|
<!-- <s3> -->
|
||||||
|
<!-- <disk>s3</disk> -->
|
||||||
|
<!-- </s3> -->
|
||||||
|
<!-- </volumes> -->
|
||||||
|
<!-- </tiered> -->
|
||||||
|
<!-- </policies> -->
|
||||||
|
<!-- </storage_configuration> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
|
<!-- Default root page on http[s] server. For example load UI from https://tabix.io/ when opening http://localhost:8123 -->
|
||||||
<!--
|
<!--
|
||||||
<http_server_default_response><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></http_server_default_response>
|
<http_server_default_response><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></http_server_default_response>
|
||||||
|
0
deploy/docker/clickhouse-setup/data/signoz/.gitkeep
Normal file
0
deploy/docker/clickhouse-setup/data/signoz/.gitkeep
Normal file
@ -6,6 +6,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
||||||
- ./data/clickhouse/:/var/lib/clickhouse/
|
- ./data/clickhouse/:/var/lib/clickhouse/
|
||||||
|
restart: on-failure
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
||||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
||||||
@ -23,7 +24,7 @@ services:
|
|||||||
- '--storage.path=/data'
|
- '--storage.path=/data'
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.7.1
|
image: signoz/query-service:0.7.2
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -35,12 +36,15 @@ services:
|
|||||||
- STORAGE=clickhouse
|
- STORAGE=clickhouse
|
||||||
- GODEBUG=netdns=go
|
- GODEBUG=netdns=go
|
||||||
- TELEMETRY_ENABLED=true
|
- TELEMETRY_ENABLED=true
|
||||||
|
- DEPLOYMENT_TYPE=docker-standalone-arm
|
||||||
|
|
||||||
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.1
|
image: signoz/frontend:0.7.2
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
@ -55,9 +59,17 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
ports:
|
ports:
|
||||||
- "4317:4317" # OTLP GRPC receiver
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
|
- "4318:4318" # OTLP HTTP receiver
|
||||||
|
# - "8889:8889" # Prometheus metrics exposed by the agent
|
||||||
|
# - "13133" # health_check
|
||||||
|
# - "14268:14268" # Jaeger receiver
|
||||||
|
# - "55678:55678" # OpenCensus receiver
|
||||||
|
# - "55679:55679" # zpages extension
|
||||||
|
# - "55680:55680" # OTLP gRPC legacy port
|
||||||
|
# - "55681:55681" # OTLP HTTP legacy receiver
|
||||||
mem_limit: 2000m
|
mem_limit: 2000m
|
||||||
restart: always
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@ -67,6 +79,7 @@ services:
|
|||||||
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
@ -6,6 +6,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
|
||||||
- ./data/clickhouse/:/var/lib/clickhouse/
|
- ./data/clickhouse/:/var/lib/clickhouse/
|
||||||
|
restart: on-failure
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
|
||||||
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
|
||||||
@ -26,7 +27,7 @@ services:
|
|||||||
|
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.7.1
|
image: signoz/query-service:0.7.2
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -38,12 +39,15 @@ services:
|
|||||||
- STORAGE=clickhouse
|
- STORAGE=clickhouse
|
||||||
- GODEBUG=netdns=go
|
- GODEBUG=netdns=go
|
||||||
- TELEMETRY_ENABLED=true
|
- TELEMETRY_ENABLED=true
|
||||||
|
- DEPLOYMENT_TYPE=docker-standalone-amd
|
||||||
|
|
||||||
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.7.1
|
image: signoz/frontend:0.7.2
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
@ -58,9 +62,17 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
ports:
|
ports:
|
||||||
- "4317:4317" # OTLP GRPC receiver
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
|
- "4318:4318" # OTLP HTTP receiver
|
||||||
|
# - "8889:8889" # Prometheus metrics exposed by the agent
|
||||||
|
# - "13133" # health_check
|
||||||
|
# - "14268:14268" # Jaeger receiver
|
||||||
|
# - "55678:55678" # OpenCensus receiver
|
||||||
|
# - "55679:55679" # zpages extension
|
||||||
|
# - "55680:55680" # OTLP gRPC legacy port
|
||||||
|
# - "55681:55681" # OTLP HTTP legacy receiver
|
||||||
mem_limit: 2000m
|
mem_limit: 2000m
|
||||||
restart: always
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@ -70,6 +82,7 @@ services:
|
|||||||
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
clickhouse:
|
clickhouse:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
@ -27,14 +27,18 @@ processors:
|
|||||||
signozspanmetrics/prometheus:
|
signozspanmetrics/prometheus:
|
||||||
metrics_exporter: prometheus
|
metrics_exporter: prometheus
|
||||||
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
|
||||||
|
dimensions_cache_size: 10000
|
||||||
# memory_limiter:
|
# memory_limiter:
|
||||||
# # Same as --mem-ballast-size-mib CLI argument
|
|
||||||
# ballast_size_mib: 683
|
|
||||||
# # 80% of maximum memory up to 2G
|
# # 80% of maximum memory up to 2G
|
||||||
# limit_mib: 1500
|
# limit_mib: 1500
|
||||||
# # 25% of limit up to 2G
|
# # 25% of limit up to 2G
|
||||||
# spike_limit_mib: 512
|
# spike_limit_mib: 512
|
||||||
# check_interval: 5s
|
# check_interval: 5s
|
||||||
|
#
|
||||||
|
# # 50% of the maximum memory
|
||||||
|
# limit_percentage: 50
|
||||||
|
# # 20% of max memory usage spike expected
|
||||||
|
# spike_limit_percentage: 20
|
||||||
# queued_retry:
|
# queued_retry:
|
||||||
# num_workers: 4
|
# num_workers: 4
|
||||||
# queue_size: 100
|
# queue_size: 100
|
||||||
|
@ -17,13 +17,16 @@ processors:
|
|||||||
send_batch_size: 1000
|
send_batch_size: 1000
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
# memory_limiter:
|
# memory_limiter:
|
||||||
# # Same as --mem-ballast-size-mib CLI argument
|
|
||||||
# ballast_size_mib: 683
|
|
||||||
# # 80% of maximum memory up to 2G
|
# # 80% of maximum memory up to 2G
|
||||||
# limit_mib: 1500
|
# limit_mib: 1500
|
||||||
# # 25% of limit up to 2G
|
# # 25% of limit up to 2G
|
||||||
# spike_limit_mib: 512
|
# spike_limit_mib: 512
|
||||||
# check_interval: 5s
|
# check_interval: 5s
|
||||||
|
#
|
||||||
|
# # 50% of the maximum memory
|
||||||
|
# limit_percentage: 50
|
||||||
|
# # 20% of max memory usage spike expected
|
||||||
|
# spike_limit_percentage: 20
|
||||||
# queued_retry:
|
# queued_retry:
|
||||||
# num_workers: 4
|
# num_workers: 4
|
||||||
# queue_size: 100
|
# queue_size: 100
|
||||||
|
@ -102,7 +102,7 @@ check_os() {
|
|||||||
# The script should error out in case they aren't available
|
# The script should error out in case they aren't available
|
||||||
check_ports_occupied() {
|
check_ports_occupied() {
|
||||||
local port_check_output
|
local port_check_output
|
||||||
local ports_pattern="80|3301|8080"
|
local ports_pattern="3301|4317"
|
||||||
|
|
||||||
if is_mac; then
|
if is_mac; then
|
||||||
port_check_output="$(netstat -anp tcp | awk '$6 == "LISTEN" && $4 ~ /^.*\.('"$ports_pattern"')$/')"
|
port_check_output="$(netstat -anp tcp | awk '$6 == "LISTEN" && $4 ~ /^.*\.('"$ports_pattern"')$/')"
|
||||||
@ -119,7 +119,7 @@ check_ports_occupied() {
|
|||||||
send_event "port_not_available"
|
send_event "port_not_available"
|
||||||
|
|
||||||
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
||||||
echo "SigNoz requires ports 80 & 443 to be open. Please shut down any other service(s) that may be running on these ports."
|
echo "SigNoz requires ports 3301 & 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/deployment/docker#troubleshooting"
|
echo "You can run SigNoz on another port following this guide https://signoz.io/docs/deployment/docker#troubleshooting"
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
echo ""
|
echo ""
|
||||||
@ -133,58 +133,42 @@ install_docker() {
|
|||||||
|
|
||||||
|
|
||||||
if [[ $package_manager == apt-get ]]; then
|
if [[ $package_manager == apt-get ]]; then
|
||||||
apt_cmd="sudo apt-get --yes --quiet"
|
apt_cmd="$sudo_cmd apt-get --yes --quiet"
|
||||||
$apt_cmd update
|
$apt_cmd update
|
||||||
$apt_cmd install software-properties-common gnupg-agent
|
$apt_cmd install software-properties-common gnupg-agent
|
||||||
curl -fsSL "https://download.docker.com/linux/$os/gpg" | sudo apt-key add -
|
curl -fsSL "https://download.docker.com/linux/$os/gpg" | $sudo_cmd apt-key add -
|
||||||
sudo add-apt-repository \
|
$sudo_cmd add-apt-repository \
|
||||||
"deb [arch=amd64] https://download.docker.com/linux/$os $(lsb_release -cs) stable"
|
"deb [arch=amd64] https://download.docker.com/linux/$os $(lsb_release -cs) stable"
|
||||||
$apt_cmd update
|
$apt_cmd update
|
||||||
echo "Installing docker"
|
echo "Installing docker"
|
||||||
$apt_cmd install docker-ce docker-ce-cli containerd.io
|
$apt_cmd install docker-ce docker-ce-cli containerd.io
|
||||||
elif [[ $package_manager == zypper ]]; then
|
elif [[ $package_manager == zypper ]]; then
|
||||||
zypper_cmd="sudo zypper --quiet --no-gpg-checks --non-interactive"
|
zypper_cmd="$sudo_cmd zypper --quiet --no-gpg-checks --non-interactive"
|
||||||
echo "Installing docker"
|
echo "Installing docker"
|
||||||
if [[ $os == sles ]]; then
|
if [[ $os == sles ]]; then
|
||||||
os_sp="$(cat /etc/*-release | awk -F= '$1 == "VERSION_ID" { gsub(/"/, ""); print $2; exit }')"
|
os_sp="$(cat /etc/*-release | awk -F= '$1 == "VERSION_ID" { gsub(/"/, ""); print $2; exit }')"
|
||||||
os_arch="$(uname -i)"
|
os_arch="$(uname -i)"
|
||||||
sudo SUSEConnect -p sle-module-containers/$os_sp/$os_arch -r ''
|
SUSEConnect -p sle-module-containers/$os_sp/$os_arch -r ''
|
||||||
fi
|
fi
|
||||||
$zypper_cmd install docker docker-runc containerd
|
$zypper_cmd install docker docker-runc containerd
|
||||||
sudo systemctl enable docker.service
|
$sudo_cmd systemctl enable docker.service
|
||||||
elif [[ $package_manager == yum && $os == 'amazon linux' ]]; then
|
elif [[ $package_manager == yum && $os == 'amazon linux' ]]; then
|
||||||
echo
|
echo
|
||||||
echo "Amazon Linux detected ... "
|
echo "Amazon Linux detected ... "
|
||||||
echo
|
echo
|
||||||
# sudo yum install docker
|
# yum install docker
|
||||||
# sudo service docker start
|
# service docker start
|
||||||
sudo amazon-linux-extras install docker
|
$sudo_cmd amazon-linux-extras install docker
|
||||||
else
|
else
|
||||||
|
|
||||||
yum_cmd="sudo yum --assumeyes --quiet"
|
yum_cmd="$sudo_cmd yum --assumeyes --quiet"
|
||||||
$yum_cmd install yum-utils
|
$yum_cmd install yum-utils
|
||||||
sudo yum-config-manager --add-repo https://download.docker.com/linux/$os/docker-ce.repo
|
$sudo_cmd yum-config-manager --add-repo https://download.docker.com/linux/$os/docker-ce.repo
|
||||||
echo "Installing docker"
|
echo "Installing docker"
|
||||||
$yum_cmd install docker-ce docker-ce-cli containerd.io
|
$yum_cmd install docker-ce docker-ce-cli containerd.io
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
|
||||||
install_docker_machine() {
|
|
||||||
|
|
||||||
echo "\nInstalling docker machine ..."
|
|
||||||
|
|
||||||
if [[ $os == "Mac" ]];then
|
|
||||||
curl -sL https://github.com/docker/machine/releases/download/v0.16.2/docker-machine-`uname -s`-`uname -m` >/usr/local/bin/docker-machine
|
|
||||||
chmod +x /usr/local/bin/docker-machine
|
|
||||||
else
|
|
||||||
curl -sL https://github.com/docker/machine/releases/download/v0.16.2/docker-machine-`uname -s`-`uname -m` >/tmp/docker-machine
|
|
||||||
chmod +x /tmp/docker-machine
|
|
||||||
sudo cp /tmp/docker-machine /usr/local/bin/docker-machine
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
install_docker_compose() {
|
install_docker_compose() {
|
||||||
@ -192,9 +176,9 @@ install_docker_compose() {
|
|||||||
if [[ ! -f /usr/bin/docker-compose ]];then
|
if [[ ! -f /usr/bin/docker-compose ]];then
|
||||||
echo "++++++++++++++++++++++++"
|
echo "++++++++++++++++++++++++"
|
||||||
echo "Installing docker-compose"
|
echo "Installing docker-compose"
|
||||||
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
$sudo_cmd curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||||
sudo chmod +x /usr/local/bin/docker-compose
|
$sudo_cmd chmod +x /usr/local/bin/docker-compose
|
||||||
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
|
$sudo_cmd ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
|
||||||
echo "docker-compose installed!"
|
echo "docker-compose installed!"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
@ -210,16 +194,23 @@ install_docker_compose() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start_docker() {
|
start_docker() {
|
||||||
echo "Starting Docker ..."
|
echo -e "🐳 Starting Docker ...\n"
|
||||||
if [ $os = "Mac" ]; then
|
if [ $os = "Mac" ]; then
|
||||||
open --background -a Docker && while ! docker system info > /dev/null 2>&1; do sleep 1; done
|
open --background -a Docker && while ! docker system info > /dev/null 2>&1; do sleep 1; done
|
||||||
else
|
else
|
||||||
if ! sudo systemctl is-active docker.service > /dev/null; then
|
if ! $sudo_cmd systemctl is-active docker.service > /dev/null; then
|
||||||
echo "Starting docker service"
|
echo "Starting docker service"
|
||||||
sudo systemctl start docker.service
|
$sudo_cmd systemctl start docker.service
|
||||||
|
fi
|
||||||
|
if [ -z $sudo_cmd ]; then
|
||||||
|
docker ps > /dev/null && true
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
request_sudo
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_for_containers_start() {
|
wait_for_containers_start() {
|
||||||
local timeout=$1
|
local timeout=$1
|
||||||
|
|
||||||
@ -229,16 +220,6 @@ wait_for_containers_start() {
|
|||||||
if [[ status_code -eq 200 ]]; then
|
if [[ status_code -eq 200 ]]; then
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
if [ $setup_type == 'druid' ]; then
|
|
||||||
SUPERVISORS="$(curl -so - http://localhost:8888/druid/indexer/v1/supervisor)"
|
|
||||||
LEN_SUPERVISORS="${#SUPERVISORS}"
|
|
||||||
|
|
||||||
if [[ LEN_SUPERVISORS -ne 19 && $timeout -eq 50 ]];then
|
|
||||||
echo -e "\n🟠 Supervisors taking time to start ⏳ ... let's wait for some more time ⏱️\n\n"
|
|
||||||
sudo docker-compose -f ./docker/druid-kafka-setup/docker-compose-tiny.yaml up -d
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -ne "Waiting for all containers to start. This check will timeout in $timeout seconds ...\r\c"
|
echo -ne "Waiting for all containers to start. This check will timeout in $timeout seconds ...\r\c"
|
||||||
fi
|
fi
|
||||||
((timeout--))
|
((timeout--))
|
||||||
@ -254,19 +235,17 @@ bye() { # Prints a friendly good bye message and exits the script.
|
|||||||
|
|
||||||
echo "🔴 The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:"
|
echo "🔴 The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:"
|
||||||
echo ""
|
echo ""
|
||||||
if [ $setup_type == 'clickhouse' ]; then
|
|
||||||
if is_arm64; then
|
if is_arm64; then
|
||||||
echo -e "sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml ps -a"
|
echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml ps -a"
|
||||||
else
|
else
|
||||||
echo -e "sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e "sudo docker-compose -f ./docker/druid-kafka-setup/docker-compose-tiny.yaml ps -a"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# echo "Please read our troubleshooting guide https://signoz.io/docs/deployment/docker#troubleshooting"
|
# echo "Please read our troubleshooting guide https://signoz.io/docs/deployment/docker#troubleshooting"
|
||||||
echo "or reach us for support in #help channel in our Slack Community https://signoz.io/slack"
|
echo "or reach us for support in #help channel in our Slack Community https://signoz.io/slack"
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
|
|
||||||
|
if [[ email == "" ]]; then
|
||||||
echo -e "\n📨 Please share your email to receive support with the installation"
|
echo -e "\n📨 Please share your email to receive support with the installation"
|
||||||
read -rp 'Email: ' email
|
read -rp 'Email: ' email
|
||||||
|
|
||||||
@ -274,6 +253,7 @@ bye() { # Prints a friendly good bye message and exits the script.
|
|||||||
do
|
do
|
||||||
read -rp 'Email: ' email
|
read -rp 'Email: ' email
|
||||||
done
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
send_event "installation_support"
|
send_event "installation_support"
|
||||||
|
|
||||||
@ -284,16 +264,47 @@ bye() { # Prints a friendly good bye message and exits the script.
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request_sudo() {
|
||||||
|
if hash sudo 2>/dev/null; then
|
||||||
|
sudo_cmd="sudo"
|
||||||
|
echo -e "\n\n🙇 We will need sudo access to complete the installation."
|
||||||
|
if ! $sudo_cmd -v && (( $EUID != 0 )); then
|
||||||
|
echo -e "Please enter your sudo password now:"
|
||||||
|
|
||||||
|
if ! $sudo_cmd -v; then
|
||||||
|
echo "Need sudo privileges to proceed with the installation."
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "Thanks! 🙏\n"
|
||||||
|
echo -e "Okay! We will bring up the SigNoz cluster from here 🚀\n"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ""
|
||||||
echo -e "👋 Thank you for trying out SigNoz! "
|
echo -e "👋 Thank you for trying out SigNoz! "
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
sudo_cmd=""
|
||||||
|
|
||||||
|
# Check sudo permissions
|
||||||
|
if (( $EUID != 0 )); then
|
||||||
|
echo "🟡 Running installer with non-sudo permissions."
|
||||||
|
if ! is_command_present docker; then
|
||||||
|
$sudo_cmd docker ps
|
||||||
|
fi
|
||||||
|
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
|
# Checking OS and assigning package manager
|
||||||
desired_os=0
|
desired_os=0
|
||||||
os=""
|
os=""
|
||||||
email=""
|
email=""
|
||||||
echo -e "Detecting your OS ..."
|
echo -e "🌏 Detecting your OS ...\n"
|
||||||
check_os
|
check_os
|
||||||
|
|
||||||
# Obtain unique installation id
|
# Obtain unique installation id
|
||||||
@ -301,9 +312,22 @@ sysinfo="$(uname -a)"
|
|||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
uuid="$(uuidgen)"
|
uuid="$(uuidgen)"
|
||||||
uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
|
uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
|
||||||
SIGNOZ_INSTALLATION_ID="${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
|
else
|
||||||
SIGNOZ_INSTALLATION_ID=$(echo "$sysinfo" | shasum | cut -d ' ' -f1)
|
SIGNOZ_INSTALLATION_ID=$(echo "$sysinfo" | $digest_cmd | grep -E -o '[a-zA-Z0-9]{64}')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# echo ""
|
# echo ""
|
||||||
@ -364,13 +388,7 @@ send_event() {
|
|||||||
'installation_error_checks')
|
'installation_error_checks')
|
||||||
event="Installation Error - Checks"
|
event="Installation Error - Checks"
|
||||||
error="Containers not started"
|
error="Containers not started"
|
||||||
if [ $setup_type == 'clickhouse' ]; then
|
|
||||||
others='"data": "some_checks",'
|
others='"data": "some_checks",'
|
||||||
else
|
|
||||||
supervisors="$(curl -so - http://localhost:8888/druid/indexer/v1/supervisor)"
|
|
||||||
datasources="$(curl -so - http://localhost:8888/druid/coordinator/v1/datasources)"
|
|
||||||
others='"supervisors": "'"$supervisors"'", "datasources": "'"$datasources"'",'
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
'installation_support')
|
'installation_support')
|
||||||
event="Installation Support"
|
event="Installation Support"
|
||||||
@ -412,15 +430,28 @@ 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
|
# 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 ! is_command_present docker; then
|
||||||
|
|
||||||
if [[ $package_manager == "apt-get" || $package_manager == "zypper" || $package_manager == "yum" ]]; then
|
if [[ $package_manager == "apt-get" || $package_manager == "zypper" || $package_manager == "yum" ]]; then
|
||||||
|
request_sudo
|
||||||
install_docker
|
install_docker
|
||||||
else
|
# enable docker without sudo from next reboot
|
||||||
|
sudo usermod -aG docker "${USER}"
|
||||||
|
elif is_mac; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "+++++++++++ IMPORTANT READ ++++++++++++++++++++++"
|
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 "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 "https://docs.docker.com/docker-for-mac/install/"
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++++++++++"
|
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"
|
send_event "docker_not_installed"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@ -428,42 +459,32 @@ fi
|
|||||||
|
|
||||||
# Install docker-compose
|
# Install docker-compose
|
||||||
if ! is_command_present docker-compose; then
|
if ! is_command_present docker-compose; then
|
||||||
|
request_sudo
|
||||||
install_docker_compose
|
install_docker_compose
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
start_docker
|
start_docker
|
||||||
|
|
||||||
|
# $sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up -d --remove-orphans || true
|
||||||
# sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up -d --remove-orphans || true
|
|
||||||
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "\n🟡 Pulling the latest container images for SigNoz. To run as sudo it may ask for system password\n"
|
echo -e "\n🟡 Pulling the latest container images for SigNoz.\n"
|
||||||
if [ $setup_type == 'clickhouse' ]; then
|
if is_arm64; then
|
||||||
if is_arm64; then
|
$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml pull
|
||||||
sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml pull
|
|
||||||
else
|
|
||||||
sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml pull
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
sudo docker-compose -f ./docker/druid-kafka-setup/docker-compose-tiny.yaml pull
|
$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml pull
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🟡 Starting the SigNoz containers. It may take a few minutes ..."
|
echo "🟡 Starting the SigNoz containers. It may take a few minutes ..."
|
||||||
echo
|
echo
|
||||||
# The docker-compose command does some nasty stuff for the `--detach` functionality. So we add a `|| true` so that the
|
# The docker-compose 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.
|
# script doesn't exit because this command looks like it failed to do it's thing.
|
||||||
if [ $setup_type == 'clickhouse' ]; then
|
if is_arm64; then
|
||||||
if is_arm64; then
|
$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml up --detach --remove-orphans || true
|
||||||
sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml up --detach --remove-orphans || true
|
|
||||||
else
|
|
||||||
sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up --detach --remove-orphans || true
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
sudo docker-compose -f ./docker/druid-kafka-setup/docker-compose-tiny.yaml up --detach --remove-orphans || true
|
$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up --detach --remove-orphans || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
wait_for_containers_start 60
|
wait_for_containers_start 60
|
||||||
@ -473,11 +494,9 @@ if [[ $status_code -ne 200 ]]; then
|
|||||||
echo "+++++++++++ ERROR ++++++++++++++++++++++"
|
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 "🔴 The containers didn't seem to start correctly. Please run the following command to check containers that may have errored out:"
|
||||||
echo ""
|
echo ""
|
||||||
if [ $setup_type == 'clickhouse' ]; then
|
|
||||||
echo -e "sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
echo -e "$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml ps -a"
|
||||||
else
|
|
||||||
echo -e "sudo docker-compose -f ./docker/druid-kafka-setup/docker-compose-tiny.yaml ps -a"
|
|
||||||
fi
|
|
||||||
echo "Please read our troubleshooting guide https://signoz.io/docs/deployment/docker/#troubleshooting-of-common-issues"
|
echo "Please read our troubleshooting guide https://signoz.io/docs/deployment/docker/#troubleshooting-of-common-issues"
|
||||||
echo "or reach us on SigNoz for support https://signoz.io/slack"
|
echo "or reach us on SigNoz for support https://signoz.io/slack"
|
||||||
echo "++++++++++++++++++++++++++++++++++++++++"
|
echo "++++++++++++++++++++++++++++++++++++++++"
|
||||||
@ -495,14 +514,10 @@ else
|
|||||||
echo -e "🟢 Your frontend is running on http://localhost:3301"
|
echo -e "🟢 Your frontend is running on http://localhost:3301"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [ $setup_type == 'clickhouse' ]; then
|
|
||||||
if is_arm64; then
|
if is_arm64; then
|
||||||
echo "ℹ️ To bring down SigNoz and clean volumes : sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml down -v"
|
echo "ℹ️ To bring down SigNoz and clean volumes : $sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.arm.yaml down -v"
|
||||||
else
|
else
|
||||||
echo "ℹ️ To bring down SigNoz and clean volumes : sudo docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml down -v"
|
echo "ℹ️ To bring down SigNoz and clean volumes : $sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml down -v"
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "ℹ️ To bring down SigNoz and clean volumes : sudo docker-compose -f ./docker/druid-kafka-setup/docker-compose-tiny.yaml down -v"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
@ -3,17 +3,23 @@ module.exports = {
|
|||||||
browser: true,
|
browser: true,
|
||||||
es2021: true,
|
es2021: true,
|
||||||
node: true,
|
node: true,
|
||||||
|
'jest/globals': true,
|
||||||
},
|
},
|
||||||
extends: [
|
extends: [
|
||||||
|
'airbnb',
|
||||||
|
'airbnb-typescript',
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'plugin:react/recommended',
|
'plugin:react/recommended',
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
'plugin:@typescript-eslint/eslint-recommended',
|
'plugin:@typescript-eslint/eslint-recommended',
|
||||||
'plugin:prettier/recommended',
|
'plugin:prettier/recommended',
|
||||||
'plugin:sonarjs/recommended',
|
'plugin:sonarjs/recommended',
|
||||||
|
'plugin:import/errors',
|
||||||
|
'plugin:import/warnings',
|
||||||
],
|
],
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
project: './tsconfig.json',
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true,
|
jsx: true,
|
||||||
},
|
},
|
||||||
@ -26,10 +32,17 @@ module.exports = {
|
|||||||
'simple-import-sort',
|
'simple-import-sort',
|
||||||
'react-hooks',
|
'react-hooks',
|
||||||
'prettier',
|
'prettier',
|
||||||
|
'jest',
|
||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
version: 'latest',
|
version: 'detect',
|
||||||
|
},
|
||||||
|
'import/resolver': {
|
||||||
|
node: {
|
||||||
|
paths: ['src'],
|
||||||
|
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
@ -41,12 +54,13 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
'react/prop-types': 'off',
|
'react/prop-types': 'off',
|
||||||
'@typescript-eslint/explicit-function-return-type': 'error',
|
'@typescript-eslint/explicit-function-return-type': 'error',
|
||||||
'@typescript-eslint/no-var-requires': 0,
|
'@typescript-eslint/no-var-requires': 'error',
|
||||||
'react/no-array-index-key': 2,
|
'react/no-array-index-key': 'error',
|
||||||
'linebreak-style': [
|
'linebreak-style': [
|
||||||
'error',
|
'error',
|
||||||
process.platform === 'win32' ? 'windows' : 'unix',
|
process.platform === 'win32' ? 'windows' : 'unix',
|
||||||
],
|
],
|
||||||
|
'@typescript-eslint/default-param-last': 'off',
|
||||||
|
|
||||||
// simple sort error
|
// simple sort error
|
||||||
'simple-import-sort/imports': 'error',
|
'simple-import-sort/imports': 'error',
|
||||||
@ -54,7 +68,29 @@ module.exports = {
|
|||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
'react-hooks/exhaustive-deps': 'warn',
|
'react-hooks/exhaustive-deps': 'error',
|
||||||
|
|
||||||
|
// airbnb
|
||||||
|
'no-underscore-dangle': 'off',
|
||||||
|
'no-console': 'off',
|
||||||
|
'import/prefer-default-export': 'off',
|
||||||
|
'import/extensions': [
|
||||||
|
'error',
|
||||||
|
'ignorePackages',
|
||||||
|
{
|
||||||
|
js: 'never',
|
||||||
|
jsx: 'never',
|
||||||
|
ts: 'never',
|
||||||
|
tsx: 'never',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// eslint rules need to remove
|
||||||
|
'no-shadow': 'off',
|
||||||
|
'@typescript-eslint/no-shadow': 'off',
|
||||||
|
'global-require': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'import/no-cycle': 'off',
|
||||||
|
|
||||||
'prettier/prettier': [
|
'prettier/prettier': [
|
||||||
'error',
|
'error',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# stage1 as builder
|
# stage1 as builder
|
||||||
FROM node:12.18.0 as builder
|
FROM node:12.22.0 as builder
|
||||||
|
|
||||||
# Add Maintainer Info
|
# Add Maintainer Info
|
||||||
LABEL maintainer="signoz"
|
LABEL maintainer="signoz"
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
const resizeObserverLoopErrRe = /ResizeObserver loop limit exceeded/;
|
const resizeObserverLoopErrRe = /ResizeObserver loop limit exceeded/;
|
||||||
|
|
||||||
const unCaughtExpection = () => {
|
const unCaughtExpection = (): void => {
|
||||||
cy.on('uncaught:exception', (err) => {
|
cy.on('uncaught:exception', (err) => {
|
||||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
|
||||||
// returning false here prevents Cypress from
|
// returning false here prevents Cypress from
|
||||||
// failing the test
|
// failing the test
|
||||||
return false;
|
return !resizeObserverLoopErrRe.test(err.message);
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import ROUTES from 'constants/routes';
|
|||||||
|
|
||||||
import defaultRules from '../../fixtures/defaultRules.json';
|
import defaultRules from '../../fixtures/defaultRules.json';
|
||||||
|
|
||||||
|
const defaultRuleRoutes = `**/rules/**`;
|
||||||
|
|
||||||
describe('Alerts', () => {
|
describe('Alerts', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
window.localStorage.setItem('isLoggedIn', 'yes');
|
window.localStorage.setItem('isLoggedIn', 'yes');
|
||||||
@ -21,7 +23,7 @@ describe('Alerts', () => {
|
|||||||
|
|
||||||
it('Edit Rules Page Failure', async () => {
|
it('Edit Rules Page Failure', async () => {
|
||||||
cy
|
cy
|
||||||
.intercept('**/rules/**', {
|
.intercept(defaultRuleRoutes, {
|
||||||
statusCode: 500,
|
statusCode: 500,
|
||||||
})
|
})
|
||||||
.as('Get Rules Error');
|
.as('Get Rules Error');
|
||||||
@ -49,7 +51,7 @@ describe('Alerts', () => {
|
|||||||
const text = 'this is the sample value';
|
const text = 'this is the sample value';
|
||||||
|
|
||||||
cy
|
cy
|
||||||
.intercept('**/rules/**', {
|
.intercept(defaultRuleRoutes, {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
body: {
|
body: {
|
||||||
data: {
|
data: {
|
||||||
@ -103,7 +105,7 @@ describe('Alerts', () => {
|
|||||||
|
|
||||||
it('Rules are Deleted', async () => {
|
it('Rules are Deleted', async () => {
|
||||||
cy
|
cy
|
||||||
.intercept('**/rules/**', {
|
.intercept(defaultRuleRoutes, {
|
||||||
body: {
|
body: {
|
||||||
data: 'Deleted',
|
data: 'Deleted',
|
||||||
message: 'Success',
|
message: 'Success',
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
|
/* eslint-disable sonarjs/no-duplicate-string */
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { TraceFilterEnum } from 'types/reducer/trace';
|
|
||||||
import TableInitialResponse from '../../fixtures/trace/initialSpans.json';
|
|
||||||
import FilterInitialResponse from '../../fixtures/trace/initialSpanFilter.json';
|
|
||||||
import GraphInitialResponse from '../../fixtures/trace/initialAggregates.json';
|
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import { TraceFilterEnum } from 'types/reducer/trace';
|
||||||
|
|
||||||
|
import GraphInitialResponse from '../../fixtures/trace/initialAggregates.json';
|
||||||
|
import FilterInitialResponse from '../../fixtures/trace/initialSpanFilter.json';
|
||||||
|
import TableInitialResponse from '../../fixtures/trace/initialSpans.json';
|
||||||
|
|
||||||
|
const allFilters = '@Filters.all';
|
||||||
|
const allGraphs = '@Graph.all';
|
||||||
|
const allTable = '@Table.all';
|
||||||
|
|
||||||
describe('Trace', () => {
|
describe('Trace', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -74,9 +80,9 @@ describe('Trace', () => {
|
|||||||
JSON.stringify(TableInitialResponse),
|
JSON.stringify(TableInitialResponse),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
cy.get('@Filters.all').should('have.length', 1);
|
cy.get(allFilters).should('have.length', 1);
|
||||||
cy.get('@Graph.all').should('have.length', 1);
|
cy.get(allGraphs).should('have.length', 1);
|
||||||
cy.get('@Table.all').should('have.length', 1);
|
cy.get(allTable).should('have.length', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Clear All', () => {
|
it('Clear All', () => {
|
||||||
@ -102,9 +108,9 @@ describe('Trace', () => {
|
|||||||
cy.wait(['@Filters', '@Graph', '@Table']);
|
cy.wait(['@Filters', '@Graph', '@Table']);
|
||||||
|
|
||||||
// insuring the api get call
|
// insuring the api get call
|
||||||
cy.get('@Filters.all').should('have.length', 2);
|
cy.get(allFilters).should('have.length', 2);
|
||||||
cy.get('@Graph.all').should('have.length', 2);
|
cy.get(allGraphs).should('have.length', 2);
|
||||||
cy.get('@Table.all').should('have.length', 2);
|
cy.get(allTable).should('have.length', 2);
|
||||||
|
|
||||||
cy
|
cy
|
||||||
.window()
|
.window()
|
||||||
@ -146,9 +152,9 @@ describe('Trace', () => {
|
|||||||
expect(tableBody.exclude[0] === 'status').to.be.true;
|
expect(tableBody.exclude[0] === 'status').to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('@Filters.all').should('have.length', 2);
|
cy.get(allFilters).should('have.length', 2);
|
||||||
cy.get('@Graph.all').should('have.length', 2);
|
cy.get(allGraphs).should('have.length', 2);
|
||||||
cy.get('@Table.all').should('have.length', 2);
|
cy.get(allTable).should('have.length', 2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
"dev": "cross-env NODE_ENV=development webpack serve --progress",
|
"dev": "cross-env NODE_ENV=development webpack serve --progress",
|
||||||
"build": "webpack --config=webpack.config.prod.js --progress",
|
"build": "webpack --config=webpack.config.prod.js --progress",
|
||||||
"prettify": "prettier --write .",
|
"prettify": "prettier --write .",
|
||||||
"lint": "eslint . --debug",
|
"lint": "eslint ./src",
|
||||||
"lint:fix": "eslint . --fix --debug",
|
"lint:fix": "eslint ./src --fix",
|
||||||
"cypress:open": "cypress open",
|
"cypress:open": "cypress open",
|
||||||
"cypress:run": "cypress run",
|
"cypress:run": "cypress run",
|
||||||
"jest": "jest",
|
"jest": "jest",
|
||||||
@ -22,10 +22,13 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.6.2",
|
"@ant-design/icons": "^4.6.2",
|
||||||
|
"@grafana/data": "^8.4.3",
|
||||||
|
"@monaco-editor/react": "^4.3.1",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.1.0",
|
"@testing-library/react": "^11.1.0",
|
||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
"antd": "^4.16.13",
|
"@welldone-software/why-did-you-render": "^6.2.1",
|
||||||
|
"antd": "4.19.2",
|
||||||
"axios": "^0.21.0",
|
"axios": "^0.21.0",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"babel-jest": "^26.6.0",
|
"babel-jest": "^26.6.0",
|
||||||
@ -39,6 +42,7 @@
|
|||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "4.3.0",
|
"css-loader": "4.3.0",
|
||||||
"css-minimizer-webpack-plugin": "^3.2.0",
|
"css-minimizer-webpack-plugin": "^3.2.0",
|
||||||
|
"cypress": "^8.3.0",
|
||||||
"d3": "^6.2.0",
|
"d3": "^6.2.0",
|
||||||
"d3-flame-graph": "^3.1.1",
|
"d3-flame-graph": "^3.1.1",
|
||||||
"d3-tip": "^0.9.1",
|
"d3-tip": "^0.9.1",
|
||||||
@ -47,22 +51,27 @@
|
|||||||
"file-loader": "6.1.1",
|
"file-loader": "6.1.1",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"html-webpack-plugin": "5.1.0",
|
"html-webpack-plugin": "5.1.0",
|
||||||
|
"i18next": "^21.6.12",
|
||||||
|
"i18next-browser-languagedetector": "^6.1.3",
|
||||||
|
"i18next-http-backend": "^1.3.2",
|
||||||
"jest": "26.6.0",
|
"jest": "26.6.0",
|
||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
"less-loader": "^10.2.0",
|
"less-loader": "^10.2.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"mini-css-extract-plugin": "2.4.5",
|
"mini-css-extract-plugin": "2.4.5",
|
||||||
"monaco-editor": "^0.30.0",
|
|
||||||
"react": "17.0.0",
|
"react": "17.0.0",
|
||||||
"react-dom": "17.0.0",
|
"react-dom": "17.0.0",
|
||||||
"react-force-graph": "^1.41.0",
|
"react-force-graph": "^1.41.0",
|
||||||
"react-graph-vis": "^1.0.5",
|
"react-graph-vis": "^1.0.5",
|
||||||
"react-grid-layout": "^1.2.5",
|
"react-grid-layout": "^1.2.5",
|
||||||
|
"react-i18next": "^11.16.1",
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-use": "^17.3.2",
|
"react-use": "^17.3.2",
|
||||||
"react-vis": "^1.11.7",
|
"react-vis": "^1.11.7",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
|
"stream": "^0.0.2",
|
||||||
"style-loader": "1.3.0",
|
"style-loader": "1.3.0",
|
||||||
"styled-components": "^5.2.1",
|
"styled-components": "^5.2.1",
|
||||||
"terser-webpack-plugin": "^5.2.5",
|
"terser-webpack-plugin": "^5.2.5",
|
||||||
@ -101,6 +110,7 @@
|
|||||||
"@types/d3-tip": "^3.5.5",
|
"@types/d3-tip": "^3.5.5",
|
||||||
"@types/jest": "^26.0.15",
|
"@types/jest": "^26.0.15",
|
||||||
"@types/lodash-es": "^4.17.4",
|
"@types/lodash-es": "^4.17.4",
|
||||||
|
"@types/mini-css-extract-plugin": "^2.5.1",
|
||||||
"@types/node": "^16.10.3",
|
"@types/node": "^16.10.3",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.0",
|
||||||
"@types/react-dom": "^16.9.9",
|
"@types/react-dom": "^16.9.9",
|
||||||
@ -115,17 +125,19 @@
|
|||||||
"@types/webpack-dev-server": "^4.3.0",
|
"@types/webpack-dev-server": "^4.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
||||||
"@typescript-eslint/parser": "^4.28.2",
|
"@typescript-eslint/parser": "^4.28.2",
|
||||||
"@welldone-software/why-did-you-render": "^6.2.1",
|
|
||||||
"autoprefixer": "^9.0.0",
|
"autoprefixer": "^9.0.0",
|
||||||
"babel-plugin-styled-components": "^1.12.0",
|
"babel-plugin-styled-components": "^1.12.0",
|
||||||
"compression-webpack-plugin": "^9.0.0",
|
"compression-webpack-plugin": "^9.0.0",
|
||||||
"copy-webpack-plugin": "^8.1.0",
|
"copy-webpack-plugin": "^8.1.0",
|
||||||
"critters-webpack-plugin": "^3.0.1",
|
"critters-webpack-plugin": "^3.0.1",
|
||||||
"cypress": "^8.3.0",
|
|
||||||
"eslint": "^7.30.0",
|
"eslint": "^7.30.0",
|
||||||
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
|
"eslint-config-airbnb-typescript": "^16.1.4",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-config-standard": "^16.0.3",
|
"eslint-config-standard": "^16.0.3",
|
||||||
"eslint-plugin-import": "^2.23.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
|
"eslint-plugin-jest": "^26.1.2",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-promise": "^5.1.0",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
@ -136,7 +148,6 @@
|
|||||||
"husky": "4.3.8",
|
"husky": "4.3.8",
|
||||||
"less-plugin-npm-import": "^2.1.0",
|
"less-plugin-npm-import": "^2.1.0",
|
||||||
"lint-staged": "10.5.3",
|
"lint-staged": "10.5.3",
|
||||||
"lodash-es": "^4.17.21",
|
|
||||||
"portfinder-sync": "^0.0.2",
|
"portfinder-sync": "^0.0.2",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.2.1",
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
|
3
frontend/public/locales/en/translation.json
Normal file
3
frontend/public/locales/en/translation.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"monitor_signup": "Monitor your applications. Find what is causing issues."
|
||||||
|
}
|
@ -11,8 +11,7 @@ import AppReducer from 'types/reducer/app';
|
|||||||
|
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
|
|
||||||
|
function App(): JSX.Element {
|
||||||
const App = (): JSX.Element => {
|
|
||||||
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -20,8 +19,8 @@ const App = (): JSX.Element => {
|
|||||||
<AppLayout>
|
<AppLayout>
|
||||||
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
|
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes.map(({ path, component, exact }, index) => (
|
{routes.map(({ path, component, exact }) => (
|
||||||
<Route key={index} exact={exact} path={path} component={component} />
|
<Route key={`${path}`} exact={exact} path={path} component={component} />
|
||||||
))}
|
))}
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
@ -40,7 +39,6 @@ const App = (): JSX.Element => {
|
|||||||
</AppLayout>
|
</AppLayout>
|
||||||
</Router>
|
</Router>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -17,8 +17,8 @@ import {
|
|||||||
ServicesTablePage,
|
ServicesTablePage,
|
||||||
SettingsPage,
|
SettingsPage,
|
||||||
SignupPage,
|
SignupPage,
|
||||||
TraceFilter,
|
|
||||||
TraceDetail,
|
TraceDetail,
|
||||||
|
TraceFilter,
|
||||||
UsageExplorerPage,
|
UsageExplorerPage,
|
||||||
} from './pageComponents';
|
} from './pageComponents';
|
||||||
|
|
||||||
|
26
frontend/src/ReactI18/index.tsx
Normal file
26
frontend/src/ReactI18/index.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import i18n from 'i18next';
|
||||||
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
|
import Backend from 'i18next-http-backend';
|
||||||
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
|
i18n
|
||||||
|
// load translation using http -> see /public/locales
|
||||||
|
.use(Backend)
|
||||||
|
// detect user language
|
||||||
|
.use(LanguageDetector)
|
||||||
|
// pass the i18n instance to react-i18next.
|
||||||
|
.use(initReactI18next)
|
||||||
|
// init i18next
|
||||||
|
.init({
|
||||||
|
debug: true,
|
||||||
|
fallbackLng: 'en',
|
||||||
|
interpolation: {
|
||||||
|
escapeValue: false, // not needed for react as it escapes by default
|
||||||
|
},
|
||||||
|
|
||||||
|
react: {
|
||||||
|
useSuspense: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
@ -2,7 +2,7 @@ import { AxiosError } from 'axios';
|
|||||||
import { ErrorResponse } from 'types/api';
|
import { ErrorResponse } from 'types/api';
|
||||||
import { ErrorStatusCode } from 'types/common';
|
import { ErrorStatusCode } from 'types/common';
|
||||||
|
|
||||||
export const ErrorResponseHandler = (error: AxiosError): ErrorResponse => {
|
export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
// client received an error response (5xx, 4xx)
|
// client received an error response (5xx, 4xx)
|
||||||
// making the error status code as standard Error Status Code
|
// making the error status code as standard Error Status Code
|
||||||
@ -54,4 +54,4 @@ export const ErrorResponseHandler = (error: AxiosError): ErrorResponse => {
|
|||||||
error: error.toString(),
|
error: error.toString(),
|
||||||
message: null,
|
message: null,
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { AxiosAlertManagerInstance } from 'api';
|
import { AxiosAlertManagerInstance } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
import convertObjectIntoParams from 'lib/query/convertObjectIntoParams';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { PayloadProps, Props } from 'types/api/alerts/getGroups';
|
import { PayloadProps, Props } from 'types/api/alerts/getGroups';
|
||||||
import convertObjectIntoParams from 'lib/query/convertObjectIntoParams';
|
|
||||||
|
|
||||||
const getGroups = async (
|
const getGroups = async (
|
||||||
props: Props,
|
props: Props,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const get = (key: string): string | null => {
|
const get = (key: string): string | null => {
|
||||||
try {
|
try {
|
||||||
const value = localStorage.getItem(key);
|
return localStorage.getItem(key);
|
||||||
return value;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import axios from 'api';
|
import axios from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
import omitBy from 'lodash-es/omitBy';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { PayloadProps, Props } from 'types/api/trace/getFilters';
|
import { PayloadProps, Props } from 'types/api/trace/getFilters';
|
||||||
import omitBy from 'lodash-es/omitBy';
|
|
||||||
|
|
||||||
const getFilters = async (
|
const getFilters = async (
|
||||||
props: Props,
|
props: Props,
|
||||||
@ -29,9 +29,9 @@ const getFilters = async (
|
|||||||
end: props.end,
|
end: props.end,
|
||||||
getFilters: props.getFilters,
|
getFilters: props.getFilters,
|
||||||
...nonDuration,
|
...nonDuration,
|
||||||
maxDuration: String((duration['duration'] || [])[0] || ''),
|
maxDuration: String((duration.duration || [])[0] || ''),
|
||||||
minDuration: String((duration['duration'] || [])[1] || ''),
|
minDuration: String((duration.duration || [])[1] || ''),
|
||||||
exclude: exclude,
|
exclude,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -39,8 +39,8 @@ const getSpans = async (
|
|||||||
step: props.step,
|
step: props.step,
|
||||||
tags: updatedSelectedTags,
|
tags: updatedSelectedTags,
|
||||||
...nonDuration,
|
...nonDuration,
|
||||||
maxDuration: String((duration['duration'] || [])[0] || ''),
|
maxDuration: String((duration.duration || [])[0] || ''),
|
||||||
minDuration: String((duration['duration'] || [])[1] || ''),
|
minDuration: String((duration.duration || [])[1] || ''),
|
||||||
exclude,
|
exclude,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -42,8 +42,8 @@ const getSpanAggregate = async (
|
|||||||
...preProps,
|
...preProps,
|
||||||
tags: updatedSelectedTags,
|
tags: updatedSelectedTags,
|
||||||
...nonDuration,
|
...nonDuration,
|
||||||
maxDuration: String((duration['duration'] || [])[0] || ''),
|
maxDuration: String((duration.duration || [])[0] || ''),
|
||||||
minDuration: String((duration['duration'] || [])[1] || ''),
|
minDuration: String((duration.duration || [])[1] || ''),
|
||||||
exclude,
|
exclude,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ const getTagFilters = async (
|
|||||||
start: String(props.start),
|
start: String(props.start),
|
||||||
end: String(props.end),
|
end: String(props.end),
|
||||||
...nonDuration,
|
...nonDuration,
|
||||||
maxDuration: String((duration['duration'] || [])[0] || ''),
|
maxDuration: String((duration.duration || [])[0] || ''),
|
||||||
minDuration: String((duration['duration'] || [])[1] || ''),
|
minDuration: String((duration.duration || [])[1] || ''),
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -2,7 +2,7 @@ import axios from 'api';
|
|||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { Props, PayloadProps } from 'types/api/trace/getTraceItem';
|
import { PayloadProps, Props } from 'types/api/trace/getTraceItem';
|
||||||
|
|
||||||
const getTraceItem = async (
|
const getTraceItem = async (
|
||||||
props: Props,
|
props: Props,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const TimeSeries = (props: TimeSeriesProps): JSX.Element => (
|
function TimeSeries(): JSX.Element {
|
||||||
<React.Fragment>
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="81"
|
width="81"
|
||||||
height="46"
|
height="46"
|
||||||
@ -31,12 +31,7 @@ const TimeSeries = (props: TimeSeriesProps): JSX.Element => (
|
|||||||
/>
|
/>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</React.Fragment>
|
);
|
||||||
);
|
|
||||||
|
|
||||||
export interface TimeSeriesProps{
|
|
||||||
fillColor: React.CSSProperties['color'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default TimeSeries;
|
export default TimeSeries;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Value = (props: ValueProps): JSX.Element => {
|
function Value(props: ValueProps): JSX.Element {
|
||||||
return(
|
return (
|
||||||
<React.Fragment>
|
|
||||||
<svg
|
<svg
|
||||||
width="78"
|
width="78"
|
||||||
height="32"
|
height="32"
|
||||||
@ -15,10 +14,10 @@ const Value = (props: ValueProps): JSX.Element => {
|
|||||||
fill={props.fillColor}
|
fill={props.fillColor}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</React.Fragment>
|
);
|
||||||
)};
|
}
|
||||||
|
|
||||||
interface ValueProps{
|
interface ValueProps {
|
||||||
fillColor: React.CSSProperties['color'];
|
fillColor: React.CSSProperties['color'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const NotFound = (): JSX.Element => {
|
function NotFound(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="360"
|
width="360"
|
||||||
@ -261,6 +261,6 @@ const NotFound = (): JSX.Element => {
|
|||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NotFound;
|
export default NotFound;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
import generatePicker from 'antd/es/date-picker/generatePicker';
|
||||||
import { Dayjs } from 'dayjs';
|
import { Dayjs } from 'dayjs';
|
||||||
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
|
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
|
||||||
import generatePicker from 'antd/es/date-picker/generatePicker';
|
|
||||||
|
|
||||||
const DatePicker = generatePicker<Dayjs>(dayjsGenerateConfig);
|
const DatePicker = generatePicker<Dayjs>(dayjsGenerateConfig);
|
||||||
|
|
||||||
|
@ -1,42 +1,17 @@
|
|||||||
import * as monaco from 'monaco-editor';
|
import MEditor from '@monaco-editor/react';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Container } from './styles';
|
function Editor({ value }: EditorProps): JSX.Element {
|
||||||
|
return (
|
||||||
const Editor = ({ value }: EditorProps): JSX.Element => {
|
<MEditor
|
||||||
const divEl = useRef<HTMLDivElement>(null);
|
theme="vs-dark"
|
||||||
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
|
defaultLanguage="yaml"
|
||||||
|
value={value.current}
|
||||||
useEffect(() => {
|
options={{ fontSize: 16, automaticLayout: true }}
|
||||||
let editor = editorRef.current;
|
height="40vh"
|
||||||
|
/>
|
||||||
if (divEl.current) {
|
);
|
||||||
editor = monaco.editor.create(divEl.current, {
|
}
|
||||||
value: value.current || '',
|
|
||||||
useShadowDOM: true,
|
|
||||||
theme: 'vs-dark',
|
|
||||||
automaticLayout: true,
|
|
||||||
fontSize: 16,
|
|
||||||
minimap: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
language: 'yaml',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
editor?.getModel()?.onDidChangeContent(() => {
|
|
||||||
value.current = editor?.getValue() || '';
|
|
||||||
});
|
|
||||||
|
|
||||||
return (): void => {
|
|
||||||
if (editor) {
|
|
||||||
editor.dispose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
return <Container ref={divEl} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface EditorProps {
|
interface EditorProps {
|
||||||
value: React.MutableRefObject<string>;
|
value: React.MutableRefObject<string>;
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
export const Container = styled.div`
|
|
||||||
&&& {
|
|
||||||
min-height: 40vh;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
@ -1,7 +1,11 @@
|
|||||||
import { Plugin, ChartType, Chart, ChartOptions } from 'chart.js';
|
import { Chart, ChartType, Plugin } from 'chart.js';
|
||||||
import { colors } from 'lib/getRandomColor';
|
import { colors } from 'lib/getRandomColor';
|
||||||
|
|
||||||
const getOrCreateLegendList = (chart: Chart, id: string, isLonger: boolean) => {
|
const getOrCreateLegendList = (
|
||||||
|
chart: Chart,
|
||||||
|
id: string,
|
||||||
|
isLonger: boolean,
|
||||||
|
): HTMLUListElement => {
|
||||||
const legendContainer = document.getElementById(id);
|
const legendContainer = document.getElementById(id);
|
||||||
let listContainer = legendContainer?.querySelector('ul');
|
let listContainer = legendContainer?.querySelector('ul');
|
||||||
|
|
||||||
@ -27,7 +31,7 @@ const getOrCreateLegendList = (chart: Chart, id: string, isLonger: boolean) => {
|
|||||||
export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => {
|
export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => {
|
||||||
return {
|
return {
|
||||||
id: 'htmlLegend',
|
id: 'htmlLegend',
|
||||||
afterUpdate(chart, args, options: ChartOptions) {
|
afterUpdate(chart): void {
|
||||||
const ul = getOrCreateLegendList(chart, id || 'legend', isLonger);
|
const ul = getOrCreateLegendList(chart, id || 'legend', isLonger);
|
||||||
|
|
||||||
// Remove old legend items
|
// Remove old legend items
|
||||||
@ -46,7 +50,7 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => {
|
|||||||
li.style.marginLeft = '10px';
|
li.style.marginLeft = '10px';
|
||||||
li.style.marginTop = '5px';
|
li.style.marginTop = '5px';
|
||||||
|
|
||||||
li.onclick = () => {
|
li.onclick = (): void => {
|
||||||
const { type } = chart.config;
|
const { type } = chart.config;
|
||||||
if (type === 'pie' || type === 'doughnut') {
|
if (type === 'pie' || type === 'doughnut') {
|
||||||
// Pie and doughnut charts only have a single dataset and visibility is per item
|
// Pie and doughnut charts only have a single dataset and visibility is per item
|
||||||
@ -64,7 +68,7 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => {
|
|||||||
const boxSpan = document.createElement('span');
|
const boxSpan = document.createElement('span');
|
||||||
boxSpan.style.background = item.strokeStyle || colors[0];
|
boxSpan.style.background = item.strokeStyle || colors[0];
|
||||||
boxSpan.style.borderColor = item?.strokeStyle;
|
boxSpan.style.borderColor = item?.strokeStyle;
|
||||||
boxSpan.style.borderWidth = item.lineWidth + 'px';
|
boxSpan.style.borderWidth = `${item.lineWidth}px`;
|
||||||
boxSpan.style.display = 'inline-block';
|
boxSpan.style.display = 'inline-block';
|
||||||
boxSpan.style.minHeight = '20px';
|
boxSpan.style.minHeight = '20px';
|
||||||
boxSpan.style.marginRight = '10px';
|
boxSpan.style.marginRight = '10px';
|
||||||
|
75
frontend/src/components/Graph/__tests__/xAxisConfig.test.ts
Normal file
75
frontend/src/components/Graph/__tests__/xAxisConfig.test.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { expect } from '@jest/globals';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
import { convertTimeRange, TIME_UNITS } from '../xAxisConfig';
|
||||||
|
|
||||||
|
describe('xAxisConfig for Chart', () => {
|
||||||
|
describe('convertTimeRange', () => {
|
||||||
|
it('should return relevant time units for given range', () => {
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'millisecond');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.millisecond,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'second');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.second,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'minute');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.minute,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'hour');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.hour,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'day');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.day,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'week');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.week,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'month');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.month,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const start = dayjs();
|
||||||
|
const end = start.add(10, 'year');
|
||||||
|
|
||||||
|
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||||
|
TIME_UNITS.year,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -27,6 +27,11 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
|
import { legend } from './Plugin';
|
||||||
|
import { LegendsContainer } from './styles';
|
||||||
|
import { useXAxisTimeUnit } from './xAxisConfig';
|
||||||
|
import { getYAxisFormattedValue } from './yAxisConfig';
|
||||||
|
|
||||||
Chart.register(
|
Chart.register(
|
||||||
LineElement,
|
LineElement,
|
||||||
PointElement,
|
PointElement,
|
||||||
@ -44,24 +49,24 @@ Chart.register(
|
|||||||
BarController,
|
BarController,
|
||||||
BarElement,
|
BarElement,
|
||||||
);
|
);
|
||||||
import { legend } from './Plugin';
|
|
||||||
import { LegendsContainer } from './styles';
|
|
||||||
|
|
||||||
const Graph = ({
|
function Graph({
|
||||||
|
animate = true,
|
||||||
data,
|
data,
|
||||||
type,
|
type,
|
||||||
title,
|
title,
|
||||||
isStacked,
|
isStacked,
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
name,
|
name,
|
||||||
}: GraphProps): JSX.Element => {
|
yAxisUnit = 'short',
|
||||||
|
forceReRender,
|
||||||
|
}: GraphProps): JSX.Element {
|
||||||
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
const chartRef = useRef<HTMLCanvasElement>(null);
|
const chartRef = useRef<HTMLCanvasElement>(null);
|
||||||
const currentTheme = isDarkMode ? 'dark' : 'light';
|
const currentTheme = isDarkMode ? 'dark' : 'light';
|
||||||
|
const xAxisTimeUnit = useXAxisTimeUnit(data); // Computes the relevant time unit for x axis by analyzing the time stamp data
|
||||||
|
|
||||||
// const [tooltipVisible, setTooltipVisible] = useState<boolean>(false);
|
|
||||||
const lineChartRef = useRef<Chart>();
|
const lineChartRef = useRef<Chart>();
|
||||||
|
|
||||||
const getGridColor = useCallback(() => {
|
const getGridColor = useCallback(() => {
|
||||||
if (currentTheme === undefined) {
|
if (currentTheme === undefined) {
|
||||||
return 'rgba(231,233,237,0.1)';
|
return 'rgba(231,233,237,0.1)';
|
||||||
@ -81,6 +86,9 @@ const Graph = ({
|
|||||||
|
|
||||||
if (chartRef.current !== null) {
|
if (chartRef.current !== null) {
|
||||||
const options: ChartOptions = {
|
const options: ChartOptions = {
|
||||||
|
animation: {
|
||||||
|
duration: animate ? 200 : 0,
|
||||||
|
},
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
interaction: {
|
interaction: {
|
||||||
@ -89,7 +97,7 @@ const Graph = ({
|
|||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
title: {
|
title: {
|
||||||
display: title === undefined ? false : true,
|
display: title !== undefined,
|
||||||
text: title,
|
text: title,
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
@ -109,7 +117,18 @@ const Graph = ({
|
|||||||
date: chartjsAdapter,
|
date: chartjsAdapter,
|
||||||
},
|
},
|
||||||
time: {
|
time: {
|
||||||
unit: 'minute',
|
unit: xAxisTimeUnit?.unitName || 'minute',
|
||||||
|
stepSize: xAxisTimeUnit?.stepSize || 1,
|
||||||
|
displayFormats: {
|
||||||
|
millisecond: 'HH:mm:ss',
|
||||||
|
second: 'HH:mm:ss',
|
||||||
|
minute: 'HH:mm',
|
||||||
|
hour: 'MM/dd HH:mm',
|
||||||
|
day: 'MM/dd',
|
||||||
|
week: 'MM/dd',
|
||||||
|
month: 'yy-MM',
|
||||||
|
year: 'yy',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
type: 'time',
|
type: 'time',
|
||||||
},
|
},
|
||||||
@ -119,6 +138,12 @@ const Graph = ({
|
|||||||
display: true,
|
display: true,
|
||||||
color: getGridColor(),
|
color: getGridColor(),
|
||||||
},
|
},
|
||||||
|
ticks: {
|
||||||
|
// Include a dollar sign in the ticks
|
||||||
|
callback(value, index, ticks) {
|
||||||
|
return getYAxisFormattedValue(value, yAxisUnit);
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
stacked: {
|
stacked: {
|
||||||
display: isStacked === undefined ? false : 'auto',
|
display: isStacked === undefined ? false : 'auto',
|
||||||
@ -138,17 +163,29 @@ const Graph = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
lineChartRef.current = new Chart(chartRef.current, {
|
lineChartRef.current = new Chart(chartRef.current, {
|
||||||
type: type,
|
type,
|
||||||
data: data,
|
data,
|
||||||
options,
|
options,
|
||||||
plugins: [legend(name, data.datasets.length > 3)],
|
plugins: [legend(name, data.datasets.length > 3)],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [chartRef, data, type, title, isStacked, getGridColor, onClickHandler]);
|
}, [
|
||||||
|
animate,
|
||||||
|
title,
|
||||||
|
getGridColor,
|
||||||
|
xAxisTimeUnit?.unitName,
|
||||||
|
xAxisTimeUnit?.stepSize,
|
||||||
|
isStacked,
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
name,
|
||||||
|
yAxisUnit,
|
||||||
|
onClickHandler,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
buildChart();
|
buildChart();
|
||||||
}, [buildChart]);
|
}, [buildChart, forceReRender]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: '85%' }}>
|
<div style={{ height: '85%' }}>
|
||||||
@ -156,9 +193,10 @@ const Graph = ({
|
|||||||
<LegendsContainer id={name} />
|
<LegendsContainer id={name} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface GraphProps {
|
interface GraphProps {
|
||||||
|
animate?: boolean;
|
||||||
type: ChartType;
|
type: ChartType;
|
||||||
data: Chart['data'];
|
data: Chart['data'];
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -166,6 +204,8 @@ interface GraphProps {
|
|||||||
label?: string[];
|
label?: string[];
|
||||||
onClickHandler?: graphOnClickHandler;
|
onClickHandler?: graphOnClickHandler;
|
||||||
name: string;
|
name: string;
|
||||||
|
yAxisUnit?: string;
|
||||||
|
forceReRender?: boolean | null | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type graphOnClickHandler = (
|
export type graphOnClickHandler = (
|
||||||
|
146
frontend/src/components/Graph/xAxisConfig.ts
Normal file
146
frontend/src/components/Graph/xAxisConfig.ts
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { Chart, TimeUnit } from 'chart.js';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
|
interface ITimeUnit {
|
||||||
|
[key: string]: TimeUnit;
|
||||||
|
}
|
||||||
|
interface IAxisTimeUintConfig {
|
||||||
|
unitName: TimeUnit;
|
||||||
|
multiplier: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IAxisTimeConfig {
|
||||||
|
unitName: TimeUnit;
|
||||||
|
stepSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITimeRange {
|
||||||
|
minTime: number | null;
|
||||||
|
maxTime: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TIME_UNITS: ITimeUnit = {
|
||||||
|
millisecond: 'millisecond',
|
||||||
|
second: 'second',
|
||||||
|
minute: 'minute',
|
||||||
|
hour: 'hour',
|
||||||
|
day: 'day',
|
||||||
|
week: 'week',
|
||||||
|
month: 'month',
|
||||||
|
year: 'year',
|
||||||
|
};
|
||||||
|
|
||||||
|
const TIME_UNITS_CONFIG: IAxisTimeUintConfig[] = [
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.millisecond,
|
||||||
|
multiplier: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.second,
|
||||||
|
multiplier: 1 / 1e3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.minute,
|
||||||
|
multiplier: 1 / (1e3 * 60),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.hour,
|
||||||
|
multiplier: 1 / (1e3 * 60 * 60),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.day,
|
||||||
|
multiplier: 1 / (1e3 * 60 * 60 * 24),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.week,
|
||||||
|
multiplier: 1 / (1e3 * 60 * 60 * 24 * 7),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.month,
|
||||||
|
multiplier: 1 / (1e3 * 60 * 60 * 24 * 30),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unitName: TIME_UNITS.year,
|
||||||
|
multiplier: 1 / (1e3 * 60 * 60 * 24 * 365),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts Chart.js data's data-structure and returns the relevant time unit for the axis based on the range of the data.
|
||||||
|
*/
|
||||||
|
export const useXAxisTimeUnit = (data: Chart['data']): IAxisTimeConfig => {
|
||||||
|
// Local time is the time range inferred from the input chart data.
|
||||||
|
let localTime: ITimeRange | null;
|
||||||
|
try {
|
||||||
|
let minTime = Number.POSITIVE_INFINITY;
|
||||||
|
let maxTime = Number.NEGATIVE_INFINITY;
|
||||||
|
data?.labels?.forEach((timeStamp: string | number): void => {
|
||||||
|
if (typeof timeStamp === 'string') timeStamp = Date.parse(timeStamp);
|
||||||
|
minTime = Math.min(timeStamp, minTime);
|
||||||
|
maxTime = Math.max(timeStamp, maxTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
localTime = {
|
||||||
|
minTime: minTime === Number.POSITIVE_INFINITY ? null : minTime,
|
||||||
|
maxTime: maxTime === Number.NEGATIVE_INFINITY ? null : maxTime,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
localTime = null;
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global time is the time selected from the global time selector menu.
|
||||||
|
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use local time if valid else use the global time range
|
||||||
|
const { maxTime, minTime } = useMemo(() => {
|
||||||
|
if (localTime && localTime.maxTime && localTime.minTime) {
|
||||||
|
return {
|
||||||
|
minTime: localTime.minTime,
|
||||||
|
maxTime: localTime.maxTime,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
minTime: globalTime.minTime / 1e6,
|
||||||
|
maxTime: globalTime.maxTime / 1e6,
|
||||||
|
};
|
||||||
|
}, [globalTime, localTime]);
|
||||||
|
|
||||||
|
return convertTimeRange(minTime, maxTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the relevant time unit based on the input time stamps (in ms)
|
||||||
|
*/
|
||||||
|
export const convertTimeRange = (
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
): IAxisTimeConfig => {
|
||||||
|
const MIN_INTERVALS = 6;
|
||||||
|
const range = end - start;
|
||||||
|
let relevantTimeUnit = TIME_UNITS_CONFIG[1];
|
||||||
|
let stepSize = 1;
|
||||||
|
try {
|
||||||
|
for (let idx = TIME_UNITS_CONFIG.length - 1; idx >= 0; idx--) {
|
||||||
|
const timeUnit = TIME_UNITS_CONFIG[idx];
|
||||||
|
const units = range * timeUnit.multiplier;
|
||||||
|
const steps = units / MIN_INTERVALS;
|
||||||
|
if (steps >= 1) {
|
||||||
|
relevantTimeUnit = timeUnit;
|
||||||
|
stepSize = steps;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
unitName: relevantTimeUnit.unitName,
|
||||||
|
stepSize: Math.floor(stepSize) || 1,
|
||||||
|
};
|
||||||
|
};
|
16
frontend/src/components/Graph/yAxisConfig.ts
Normal file
16
frontend/src/components/Graph/yAxisConfig.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { formattedValueToString, getValueFormat } from '@grafana/data';
|
||||||
|
|
||||||
|
export const getYAxisFormattedValue = (
|
||||||
|
value: number,
|
||||||
|
format: string,
|
||||||
|
decimal?: number,
|
||||||
|
): string => {
|
||||||
|
try {
|
||||||
|
return formattedValueToString(
|
||||||
|
getValueFormat(format)(value, undefined, undefined, undefined),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
return `${value}`;
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import { Form, Input, InputProps } from 'antd';
|
import { Form, Input, InputProps } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const InputComponent = ({
|
function InputComponent({
|
||||||
value,
|
value,
|
||||||
type = 'text',
|
type = 'text',
|
||||||
onChangeHandler,
|
onChangeHandler,
|
||||||
@ -14,7 +14,8 @@ const InputComponent = ({
|
|||||||
labelOnTop,
|
labelOnTop,
|
||||||
addonBefore,
|
addonBefore,
|
||||||
...props
|
...props
|
||||||
}: InputComponentProps): JSX.Element => (
|
}: InputComponentProps): JSX.Element {
|
||||||
|
return (
|
||||||
<Form.Item labelCol={{ span: labelOnTop ? 24 : 4 }} label={label}>
|
<Form.Item labelCol={{ span: labelOnTop ? 24 : 4 }} label={label}>
|
||||||
<Input
|
<Input
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
@ -29,7 +30,8 @@ const InputComponent = ({
|
|||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface InputComponentProps extends InputProps {
|
interface InputComponentProps extends InputProps {
|
||||||
value: InputProps['value'];
|
value: InputProps['value'];
|
||||||
|
@ -3,9 +3,7 @@ import { ComponentType, lazy } from 'react';
|
|||||||
function Loadable(importPath: {
|
function Loadable(importPath: {
|
||||||
(): LoadableProps;
|
(): LoadableProps;
|
||||||
}): React.LazyExoticComponent<LazyComponent> {
|
}): React.LazyExoticComponent<LazyComponent> {
|
||||||
const LazyComponent = lazy(() => importPath());
|
return lazy(() => importPath());
|
||||||
|
|
||||||
return LazyComponent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LazyComponent = ComponentType<Record<string, unknown>>;
|
type LazyComponent = ComponentType<Record<string, unknown>>;
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import { Modal, ModalProps as Props } from 'antd';
|
import { Modal, ModalProps as Props } from 'antd';
|
||||||
import React, { ReactElement } from 'react';
|
import React, { ReactElement } from 'react';
|
||||||
|
|
||||||
const CustomModal = ({
|
function CustomModal({
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
isModalVisible,
|
isModalVisible,
|
||||||
footer,
|
footer,
|
||||||
closable = true,
|
closable = true,
|
||||||
}: ModalProps): JSX.Element => {
|
}: ModalProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Modal
|
<Modal
|
||||||
title={title}
|
title={title}
|
||||||
visible={isModalVisible}
|
visible={isModalVisible}
|
||||||
@ -18,9 +17,8 @@ const CustomModal = ({
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
isModalVisible: boolean;
|
isModalVisible: boolean;
|
||||||
|
@ -4,7 +4,7 @@ import React from 'react';
|
|||||||
|
|
||||||
import { Button, Container, Text, TextContainer } from './styles';
|
import { Button, Container, Text, TextContainer } from './styles';
|
||||||
|
|
||||||
const NotFound = (): JSX.Element => {
|
function NotFound(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<NotFoundImage />
|
<NotFoundImage />
|
||||||
@ -19,6 +19,6 @@ const NotFound = (): JSX.Element => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default NotFound;
|
export default NotFound;
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Tabs, TabsProps } from 'antd';
|
import { Tabs, TabsProps } from 'antd';
|
||||||
|
import history from 'lib/history';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
import history from 'lib/history';
|
|
||||||
|
|
||||||
const RouteTab = ({
|
function RouteTab({
|
||||||
routes,
|
routes,
|
||||||
activeKey,
|
activeKey,
|
||||||
onChangeHandler,
|
onChangeHandler,
|
||||||
...rest
|
...rest
|
||||||
}: RouteTabProps & TabsProps): JSX.Element => {
|
}: RouteTabProps & TabsProps): JSX.Element {
|
||||||
const onChange = (activeRoute: string) => {
|
const onChange = (activeRoute: string) => {
|
||||||
onChangeHandler && onChangeHandler();
|
onChangeHandler && onChangeHandler();
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ const RouteTab = ({
|
|||||||
)}
|
)}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface RouteTabProps {
|
interface RouteTabProps {
|
||||||
routes: {
|
routes: {
|
||||||
|
@ -4,11 +4,13 @@ import React from 'react';
|
|||||||
|
|
||||||
import { SpinerStyle } from './styles';
|
import { SpinerStyle } from './styles';
|
||||||
|
|
||||||
const Spinner = ({ size, tip, height }: SpinnerProps): JSX.Element => (
|
function Spinner({ size, tip, height }: SpinnerProps): JSX.Element {
|
||||||
|
return (
|
||||||
<SpinerStyle height={height}>
|
<SpinerStyle height={height}>
|
||||||
<Spin spinning size={size} tip={tip} indicator={<LoadingOutlined spin />} />
|
<Spin spinning size={size} tip={tip} indicator={<LoadingOutlined spin />} />
|
||||||
</SpinerStyle>
|
</SpinerStyle>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface SpinnerProps {
|
interface SpinnerProps {
|
||||||
size?: SpinProps['size'];
|
size?: SpinProps['size'];
|
||||||
|
72
frontend/src/components/Styled/index.ts
Normal file
72
frontend/src/components/Styled/index.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import * as AntD from 'antd';
|
||||||
|
import { TextProps } from 'antd/lib/typography/Text';
|
||||||
|
import { TitleProps } from 'antd/lib/typography/Title';
|
||||||
|
import React from 'react';
|
||||||
|
import styled, { FlattenSimpleInterpolation } from 'styled-components';
|
||||||
|
|
||||||
|
import { IStyledClass } from './types';
|
||||||
|
|
||||||
|
const styledClass = (props: IStyledClass): FlattenSimpleInterpolation =>
|
||||||
|
props.styledclass;
|
||||||
|
|
||||||
|
type TStyledCol = AntD.ColProps & IStyledClass;
|
||||||
|
const StyledCol = styled(AntD.Col)<TStyledCol>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type TStyledRow = AntD.RowProps & IStyledClass;
|
||||||
|
const StyledRow = styled(AntD.Row)<TStyledRow>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type TStyledDivider = AntD.DividerProps & IStyledClass;
|
||||||
|
const StyledDivider = styled(AntD.Divider)<TStyledDivider>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type TStyledSpace = AntD.SpaceProps & IStyledClass;
|
||||||
|
const StyledSpace = styled(AntD.Space)<TStyledSpace>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type TStyledTabs = AntD.TabsProps & IStyledClass;
|
||||||
|
const StyledTabs = styled(AntD.Divider)<TStyledTabs>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type TStyledButton = AntD.ButtonProps & IStyledClass;
|
||||||
|
const StyledButton = styled(AntD.Button)<TStyledButton>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const { Text } = AntD.Typography;
|
||||||
|
type TStyledTypographyText = TextProps & IStyledClass;
|
||||||
|
const StyledTypographyText = styled(Text)<TStyledTypographyText>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const { Title } = AntD.Typography;
|
||||||
|
type TStyledTypographyTitle = TitleProps & IStyledClass;
|
||||||
|
const StyledTypographyTitle = styled(Title)<TStyledTypographyTitle>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
type TStyledDiv = React.HTMLAttributes<HTMLDivElement> & IStyledClass;
|
||||||
|
const StyledDiv = styled.div<TStyledDiv>`
|
||||||
|
${styledClass}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTypography = {
|
||||||
|
Text: StyledTypographyText,
|
||||||
|
Title: StyledTypographyTitle,
|
||||||
|
};
|
||||||
|
export {
|
||||||
|
StyledButton,
|
||||||
|
StyledCol,
|
||||||
|
StyledDiv,
|
||||||
|
StyledDivider,
|
||||||
|
StyledRow,
|
||||||
|
StyledSpace,
|
||||||
|
StyledTabs,
|
||||||
|
StyledTypography,
|
||||||
|
};
|
41
frontend/src/components/Styled/styles.ts
Normal file
41
frontend/src/components/Styled/styles.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { css, FlattenSimpleInterpolation } from 'styled-components';
|
||||||
|
|
||||||
|
const cssProprty = (key: string, value): FlattenSimpleInterpolation =>
|
||||||
|
key &&
|
||||||
|
value &&
|
||||||
|
css`
|
||||||
|
${key}: ${value};
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface IFlexProps {
|
||||||
|
flexDirection?: string; // Need to replace this with exact css props. Not able to find any :(
|
||||||
|
flex?: number | string;
|
||||||
|
}
|
||||||
|
export const Flex = ({
|
||||||
|
flexDirection,
|
||||||
|
flex,
|
||||||
|
}: IFlexProps): FlattenSimpleInterpolation => css`
|
||||||
|
${cssProprty('flex-direction', flexDirection)}
|
||||||
|
${cssProprty('flex', flex)}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface IDisplayProps {
|
||||||
|
display?: string;
|
||||||
|
}
|
||||||
|
export const Display = ({
|
||||||
|
display,
|
||||||
|
}: IDisplayProps): FlattenSimpleInterpolation => css`
|
||||||
|
${cssProprty('display', display)}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface ISpacingProps {
|
||||||
|
margin?: string;
|
||||||
|
padding?: string;
|
||||||
|
}
|
||||||
|
export const Spacing = ({
|
||||||
|
margin,
|
||||||
|
padding,
|
||||||
|
}: ISpacingProps): FlattenSimpleInterpolation => css`
|
||||||
|
${cssProprty('margin', margin)}
|
||||||
|
${cssProprty('padding', padding)}
|
||||||
|
`;
|
5
frontend/src/components/Styled/types.ts
Normal file
5
frontend/src/components/Styled/types.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { FlattenSimpleInterpolation } from 'styled-components';
|
||||||
|
|
||||||
|
export interface IStyledClass {
|
||||||
|
styledclass?: FlattenSimpleInterpolation[];
|
||||||
|
}
|
@ -2,13 +2,14 @@ import { QuestionCircleFilled } from '@ant-design/icons';
|
|||||||
import { Tooltip } from 'antd';
|
import { Tooltip } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const TextToolTip = ({ text, url }: TextToolTipProps) => (
|
function TextToolTip({ text, url }: TextToolTipProps) {
|
||||||
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
overlay={() => {
|
overlay={() => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{`${text} `}
|
{`${text} `}
|
||||||
<a href={url} rel="noopener noreferrer" target={'_blank'}>
|
<a href={url} rel="noopener noreferrer" target="_blank">
|
||||||
here
|
here
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -17,7 +18,8 @@ const TextToolTip = ({ text, url }: TextToolTipProps) => (
|
|||||||
>
|
>
|
||||||
<QuestionCircleFilled style={{ fontSize: '1.3125rem' }} />
|
<QuestionCircleFilled style={{ fontSize: '1.3125rem' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface TextToolTipProps {
|
interface TextToolTipProps {
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -7,10 +7,10 @@ import React, { useCallback } from 'react';
|
|||||||
|
|
||||||
import { TextContainer } from './styles';
|
import { TextContainer } from './styles';
|
||||||
|
|
||||||
const TimePreference = ({
|
function TimePreference({
|
||||||
setSelectedTime,
|
setSelectedTime,
|
||||||
selectedTime,
|
selectedTime,
|
||||||
}: TimePreferenceDropDownProps): JSX.Element => {
|
}: TimePreferenceDropDownProps): JSX.Element {
|
||||||
const timeMenuItemOnChangeHandler = useCallback(
|
const timeMenuItemOnChangeHandler = useCallback(
|
||||||
(event: TimeMenuItemOnChangeHandlerEvent) => {
|
(event: TimeMenuItemOnChangeHandlerEvent) => {
|
||||||
const selectedTime = timeItems.find((e) => e.enum === event.key);
|
const selectedTime = timeItems.find((e) => e.enum === event.key);
|
||||||
@ -38,7 +38,7 @@ const TimePreference = ({
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
</TextContainer>
|
</TextContainer>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface TimeMenuItemOnChangeHandlerEvent {
|
interface TimeMenuItemOnChangeHandlerEvent {
|
||||||
key: timePreferenceType | string;
|
key: timePreferenceType | string;
|
||||||
|
@ -2,9 +2,9 @@ import React from 'react';
|
|||||||
|
|
||||||
import { Value } from './styles';
|
import { Value } from './styles';
|
||||||
|
|
||||||
const ValueGraph = ({ value }: ValueGraphProps): JSX.Element => (
|
function ValueGraph({ value }: ValueGraphProps): JSX.Element {
|
||||||
<Value>{value}</Value>
|
return <Value>{value}</Value>;
|
||||||
);
|
}
|
||||||
|
|
||||||
interface ValueGraphProps {
|
interface ValueGraphProps {
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -5,3 +5,5 @@ export const WITHOUT_SESSION_PATH = ['/redirect'];
|
|||||||
export const AUTH0_REDIRECT_PATH = '/redirect';
|
export const AUTH0_REDIRECT_PATH = '/redirect';
|
||||||
|
|
||||||
export const DEFAULT_AUTH0_APP_REDIRECTION_PATH = ROUTES.APPLICATION;
|
export const DEFAULT_AUTH0_APP_REDIRECTION_PATH = ROUTES.APPLICATION;
|
||||||
|
|
||||||
|
export const IS_SIDEBAR_COLLAPSED = 'isSideBarCollapsed'
|
@ -9,7 +9,7 @@ import { Channels, PayloadProps } from 'types/api/channels/getAll';
|
|||||||
|
|
||||||
import Delete from './Delete';
|
import Delete from './Delete';
|
||||||
|
|
||||||
const AlertChannels = ({ allChannels }: AlertChannelsProps): JSX.Element => {
|
function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element {
|
||||||
const [notifications, Element] = notification.useNotification();
|
const [notifications, Element] = notification.useNotification();
|
||||||
const [channels, setChannels] = useState<Channels[]>(allChannels);
|
const [channels, setChannels] = useState<Channels[]>(allChannels);
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ const AlertChannels = ({ allChannels }: AlertChannelsProps): JSX.Element => {
|
|||||||
<Table rowKey="id" dataSource={channels} columns={columns} />
|
<Table rowKey="id" dataSource={channels} columns={columns} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface AlertChannelsProps {
|
interface AlertChannelsProps {
|
||||||
allChannels: PayloadProps;
|
allChannels: PayloadProps;
|
||||||
|
@ -4,11 +4,7 @@ import deleteAlert from 'api/channels/delete';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Channels } from 'types/api/channels/getAll';
|
import { Channels } from 'types/api/channels/getAll';
|
||||||
|
|
||||||
const Delete = ({
|
function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element {
|
||||||
notifications,
|
|
||||||
setChannels,
|
|
||||||
id,
|
|
||||||
}: DeleteProps): JSX.Element => {
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const onClickHandler = async (): Promise<void> => {
|
const onClickHandler = async (): Promise<void> => {
|
||||||
@ -50,7 +46,7 @@ const Delete = ({
|
|||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface DeleteProps {
|
interface DeleteProps {
|
||||||
notifications: NotificationInstance;
|
notifications: NotificationInstance;
|
||||||
|
@ -7,12 +7,13 @@ import ROUTES from 'constants/routes';
|
|||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
const { Paragraph } = Typography;
|
|
||||||
|
|
||||||
import AlertChannlesComponent from './AlertChannels';
|
import AlertChannlesComponent from './AlertChannels';
|
||||||
import { ButtonContainer, Button } from './styles';
|
import { Button, ButtonContainer } from './styles';
|
||||||
|
|
||||||
const AlertChannels = (): JSX.Element => {
|
const { Paragraph } = Typography;
|
||||||
|
|
||||||
|
function AlertChannels(): JSX.Element {
|
||||||
const onToggleHandler = useCallback(() => {
|
const onToggleHandler = useCallback(() => {
|
||||||
history.push(ROUTES.CHANNELS_NEW);
|
history.push(ROUTES.CHANNELS_NEW);
|
||||||
}, []);
|
}, []);
|
||||||
@ -24,7 +25,7 @@ const AlertChannels = (): JSX.Element => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loading || payload === undefined) {
|
if (loading || payload === undefined) {
|
||||||
return <Spinner tip="Loading Channels.." height={'90vh'} />;
|
return <Spinner tip="Loading Channels.." height="90vh" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -36,7 +37,7 @@ const AlertChannels = (): JSX.Element => {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<TextToolTip
|
<TextToolTip
|
||||||
text={`More details on how to setting notification channels`}
|
text="More details on how to setting notification channels"
|
||||||
url="https://signoz.io/docs/userguide/alerts-management/#setting-notification-channel"
|
url="https://signoz.io/docs/userguide/alerts-management/#setting-notification-channel"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -49,6 +50,6 @@ const AlertChannels = (): JSX.Element => {
|
|||||||
<AlertChannlesComponent allChannels={payload} />
|
<AlertChannlesComponent allChannels={payload} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default AlertChannels;
|
export default AlertChannels;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
import { Button as ButtonComponent } from 'antd';
|
import { Button as ButtonComponent } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const ButtonContainer = styled.div`
|
export const ButtonContainer = styled.div`
|
||||||
&&& {
|
&&& {
|
||||||
|
@ -7,7 +7,7 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import { Content, Footer, Layout } from './styles';
|
import { Content, Layout } from './styles';
|
||||||
|
|
||||||
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
@ -20,15 +20,11 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
|||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
setIsSignUpPage(true);
|
setIsSignUpPage(true);
|
||||||
history.push(ROUTES.SIGN_UP);
|
history.push(ROUTES.SIGN_UP);
|
||||||
} else {
|
} else if (isSignUpPage) {
|
||||||
if (isSignUpPage) {
|
|
||||||
setIsSignUpPage(false);
|
setIsSignUpPage(false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, [isLoggedIn, isSignUpPage]);
|
}, [isLoggedIn, isSignUpPage]);
|
||||||
|
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
{!isSignUpPage && <SideNav />}
|
{!isSignUpPage && <SideNav />}
|
||||||
@ -37,7 +33,6 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
|||||||
{!isSignUpPage && <TopNav />}
|
{!isSignUpPage && <TopNav />}
|
||||||
{children}
|
{children}
|
||||||
</Content>
|
</Content>
|
||||||
{/* <Footer>{`SigNoz Inc. © ${currentYear}`}</Footer> */}
|
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
@ -16,10 +16,3 @@ export const Content = styled(LayoutComponent.Content)`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Footer = styled(LayoutComponent.Footer)`
|
|
||||||
&&& {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
@ -7,9 +7,9 @@ import React, { useCallback, useState } from 'react';
|
|||||||
|
|
||||||
import { ChannelType, SlackChannel } from './config';
|
import { ChannelType, SlackChannel } from './config';
|
||||||
|
|
||||||
const CreateAlertChannels = ({
|
function CreateAlertChannels({
|
||||||
preType = 'slack',
|
preType = 'slack',
|
||||||
}: CreateAlertChannelsProps): JSX.Element => {
|
}: CreateAlertChannelsProps): JSX.Element {
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
||||||
text: ` {{ range .Alerts -}}
|
text: ` {{ range .Alerts -}}
|
||||||
@ -87,7 +87,6 @@ const CreateAlertChannels = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<FormAlertChannels
|
<FormAlertChannels
|
||||||
{...{
|
{...{
|
||||||
formInstance,
|
formInstance,
|
||||||
@ -100,14 +99,13 @@ const CreateAlertChannels = ({
|
|||||||
NotificationElement,
|
NotificationElement,
|
||||||
title: 'New Notification Channels',
|
title: 'New Notification Channels',
|
||||||
initialValue: {
|
initialValue: {
|
||||||
type: type,
|
type,
|
||||||
...selectedConfig,
|
...selectedConfig,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface CreateAlertChannelsProps {
|
interface CreateAlertChannelsProps {
|
||||||
preType?: ChannelType;
|
preType?: ChannelType;
|
||||||
|
@ -11,9 +11,9 @@ import { Store } from 'rc-field-form/lib/interface';
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
|
|
||||||
const EditAlertChannels = ({
|
function EditAlertChannels({
|
||||||
initialValue,
|
initialValue,
|
||||||
}: EditAlertChannelsProps): JSX.Element => {
|
}: EditAlertChannelsProps): JSX.Element {
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
||||||
...initialValue,
|
...initialValue,
|
||||||
@ -56,7 +56,7 @@ const EditAlertChannels = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSavingState(false);
|
setSavingState(false);
|
||||||
}, [selectedConfig, notifications, toggleSettingsTab, id]);
|
}, [selectedConfig, notifications, id]);
|
||||||
|
|
||||||
const onSaveHandler = useCallback(
|
const onSaveHandler = useCallback(
|
||||||
(value: ChannelType) => {
|
(value: ChannelType) => {
|
||||||
@ -72,7 +72,6 @@ const EditAlertChannels = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<FormAlertChannels
|
<FormAlertChannels
|
||||||
{...{
|
{...{
|
||||||
formInstance,
|
formInstance,
|
||||||
@ -88,9 +87,8 @@ const EditAlertChannels = ({
|
|||||||
nameDisable: true,
|
nameDisable: true,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface EditAlertChannelsProps {
|
interface EditAlertChannelsProps {
|
||||||
initialValue: Store;
|
initialValue: Store;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { SaveFilled } from '@ant-design/icons';
|
import { SaveFilled } from '@ant-design/icons';
|
||||||
import { Button } from 'antd';
|
import { Button, notification } from 'antd';
|
||||||
import { notification } from 'antd';
|
|
||||||
import put from 'api/alerts/put';
|
import put from 'api/alerts/put';
|
||||||
import Editor from 'components/Editor';
|
import Editor from 'components/Editor';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
@ -12,7 +11,7 @@ import { PayloadProps as PutPayloadProps } from 'types/api/alerts/put';
|
|||||||
|
|
||||||
import { ButtonContainer } from './styles';
|
import { ButtonContainer } from './styles';
|
||||||
|
|
||||||
const EditRules = ({ initialData, ruleId }: EditRulesProps): JSX.Element => {
|
function EditRules({ initialData, ruleId }: EditRulesProps): JSX.Element {
|
||||||
const value = useRef<string>(initialData);
|
const value = useRef<string>(initialData);
|
||||||
const [notifications, Element] = notification.useNotification();
|
const [notifications, Element] = notification.useNotification();
|
||||||
const [editButtonState, setEditButtonState] = useState<State<PutPayloadProps>>(
|
const [editButtonState, setEditButtonState] = useState<State<PutPayloadProps>>(
|
||||||
@ -93,7 +92,7 @@ const EditRules = ({ initialData, ruleId }: EditRulesProps): JSX.Element => {
|
|||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface EditRulesProps {
|
interface EditRulesProps {
|
||||||
initialData: PayloadProps['data'];
|
initialData: PayloadProps['data'];
|
||||||
|
@ -6,7 +6,8 @@ import { SlackChannel } from '../../CreateAlertChannels/config';
|
|||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
const Slack = ({ setSelectedConfig }: SlackProps): JSX.Element => (
|
function Slack({ setSelectedConfig }: SlackProps): JSX.Element {
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<FormItem name="api_url" label="Webhook URL">
|
<FormItem name="api_url" label="Webhook URL">
|
||||||
<Input
|
<Input
|
||||||
@ -21,9 +22,7 @@ const Slack = ({ setSelectedConfig }: SlackProps): JSX.Element => (
|
|||||||
|
|
||||||
<FormItem
|
<FormItem
|
||||||
name="channel"
|
name="channel"
|
||||||
help={
|
help="Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace),"
|
||||||
'Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace),'
|
|
||||||
}
|
|
||||||
label="Recipient"
|
label="Recipient"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
@ -61,7 +60,8 @@ const Slack = ({ setSelectedConfig }: SlackProps): JSX.Element => (
|
|||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
interface SlackProps {
|
interface SlackProps {
|
||||||
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<SlackChannel>>>;
|
setSelectedConfig: React.Dispatch<React.SetStateAction<Partial<SlackChannel>>>;
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
import { Form, FormInstance, Input, Select, Typography } from 'antd';
|
import { Form, FormInstance, Input, Select, Typography } from 'antd';
|
||||||
import FormItem from 'antd/lib/form/FormItem';
|
import FormItem from 'antd/lib/form/FormItem';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
SlackChannel,
|
SlackChannel,
|
||||||
} from 'container/CreateAlertChannels/config';
|
} from 'container/CreateAlertChannels/config';
|
||||||
import React from 'react';
|
|
||||||
const { Option } = Select;
|
|
||||||
const { Title } = Typography;
|
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { Store } from 'rc-field-form/lib/interface';
|
import { Store } from 'rc-field-form/lib/interface';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import SlackSettings from './Settings/Slack';
|
import SlackSettings from './Settings/Slack';
|
||||||
import { Button } from './styles';
|
import { Button } from './styles';
|
||||||
|
|
||||||
const FormAlertChannels = ({
|
const { Option } = Select;
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
function FormAlertChannels({
|
||||||
formInstance,
|
formInstance,
|
||||||
type,
|
type,
|
||||||
setSelectedConfig,
|
setSelectedConfig,
|
||||||
@ -26,7 +27,7 @@ const FormAlertChannels = ({
|
|||||||
title,
|
title,
|
||||||
initialValue,
|
initialValue,
|
||||||
nameDisable = false,
|
nameDisable = false,
|
||||||
}: FormAlertChannelsProps): JSX.Element => {
|
}: FormAlertChannelsProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{NotificationElement}
|
{NotificationElement}
|
||||||
@ -81,7 +82,7 @@ const FormAlertChannels = ({
|
|||||||
</Form>
|
</Form>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface FormAlertChannelsProps {
|
interface FormAlertChannelsProps {
|
||||||
formInstance: FormInstance;
|
formInstance: FormInstance;
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
import { Tooltip, Typography } from 'antd';
|
import { Tooltip, Typography } from 'antd';
|
||||||
import React from 'react';
|
import {
|
||||||
import { SpanBorder, SpanText, SpanWrapper, SpanLine } from './styles';
|
IIntervalUnit,
|
||||||
import { toFixed } from 'utils/toFixed'
|
resolveTimeFromInterval,
|
||||||
import { IIntervalUnit, resolveTimeFromInterval } from 'container/TraceDetail/utils';
|
} from 'container/TraceDetail/utils';
|
||||||
import useThemeMode from 'hooks/useThemeMode';
|
import useThemeMode from 'hooks/useThemeMode';
|
||||||
|
import React from 'react';
|
||||||
|
import { toFixed } from 'utils/toFixed';
|
||||||
|
|
||||||
|
import { SpanBorder, SpanLine, SpanText, SpanWrapper } from './styles';
|
||||||
|
|
||||||
interface SpanLengthProps {
|
interface SpanLengthProps {
|
||||||
width: string;
|
width: string;
|
||||||
leftOffset: string;
|
leftOffset: string;
|
||||||
@ -14,16 +19,19 @@ interface SpanLengthProps {
|
|||||||
intervalUnit: IIntervalUnit;
|
intervalUnit: IIntervalUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpanLength = (props: SpanLengthProps): JSX.Element => {
|
function SpanLength(props: SpanLengthProps): JSX.Element {
|
||||||
const { width, leftOffset, bgColor, intervalUnit } = props;
|
const { width, leftOffset, bgColor, intervalUnit } = props;
|
||||||
const { isDarkMode } = useThemeMode()
|
const { isDarkMode } = useThemeMode();
|
||||||
return (
|
return (
|
||||||
<SpanWrapper>
|
<SpanWrapper>
|
||||||
<SpanLine leftOffset={leftOffset} isDarkMode={isDarkMode} />
|
<SpanLine leftOffset={leftOffset} isDarkMode={isDarkMode} />
|
||||||
<SpanBorder bgColor={bgColor} leftOffset={leftOffset} width={width} />
|
<SpanBorder bgColor={bgColor} leftOffset={leftOffset} width={width} />
|
||||||
<SpanText leftOffset={leftOffset} isDarkMode={isDarkMode}>{`${toFixed(resolveTimeFromInterval(props.inMsCount, intervalUnit), 2)} ${intervalUnit.name}`}</SpanText>
|
<SpanText leftOffset={leftOffset} isDarkMode={isDarkMode}>{`${toFixed(
|
||||||
|
resolveTimeFromInterval(props.inMsCount, intervalUnit),
|
||||||
|
2,
|
||||||
|
)} ${intervalUnit.name}`}</SpanText>
|
||||||
</SpanWrapper>
|
</SpanWrapper>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default SpanLength;
|
export default SpanLength;
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Container,
|
||||||
Service,
|
Service,
|
||||||
Span,
|
Span,
|
||||||
SpanWrapper,
|
|
||||||
SpanConnector,
|
SpanConnector,
|
||||||
Container,
|
|
||||||
SpanName,
|
SpanName,
|
||||||
|
SpanWrapper,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
const SpanNameComponent = ({
|
function SpanNameComponent({
|
||||||
name,
|
name,
|
||||||
serviceName,
|
serviceName,
|
||||||
}: SpanNameComponent): JSX.Element => {
|
}: SpanNameComponent): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Container title={`${name} ${serviceName}`}>
|
<Container title={`${name} ${serviceName}`}>
|
||||||
<SpanWrapper>
|
<SpanWrapper>
|
||||||
@ -20,7 +21,7 @@ const SpanNameComponent = ({
|
|||||||
</SpanWrapper>
|
</SpanWrapper>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface SpanNameComponent {
|
interface SpanNameComponent {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const Span = styled(Typography.Paragraph)`
|
export const Span = styled(Typography.Paragraph)`
|
||||||
&&& {
|
&&& {
|
||||||
|
@ -1,24 +1,26 @@
|
|||||||
import React, { useRef, useState, useEffect } from 'react';
|
import { CaretDownFilled, CaretRightFilled } from '@ant-design/icons';
|
||||||
|
import { Col } from 'antd';
|
||||||
|
import { StyledCol, StyledRow } from 'components/Styled';
|
||||||
|
import { IIntervalUnit } from 'container/TraceDetail/utils';
|
||||||
|
import useThemeMode from 'hooks/useThemeMode';
|
||||||
|
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { pushDStree } from 'store/actions';
|
||||||
|
|
||||||
|
import { ITraceMetaData } from '..';
|
||||||
|
import SpanLength from '../SpanLength';
|
||||||
|
import SpanName from '../SpanName';
|
||||||
|
import { getMetaDataFromSpanTree, getTopLeftFromBody } from '../utils';
|
||||||
import {
|
import {
|
||||||
CardComponent,
|
CardComponent,
|
||||||
CardContainer,
|
CardContainer,
|
||||||
CaretContainer,
|
CaretContainer,
|
||||||
Wrapper,
|
|
||||||
HoverCard,
|
HoverCard,
|
||||||
|
styles,
|
||||||
|
Wrapper,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import { CaretDownFilled, CaretRightFilled } from '@ant-design/icons';
|
|
||||||
import SpanLength from '../SpanLength';
|
|
||||||
import SpanName from '../SpanName';
|
|
||||||
import { pushDStree } from 'store/actions';
|
|
||||||
import { getMetaDataFromSpanTree, getTopLeftFromBody } from '../utils';
|
|
||||||
import { ITraceMetaData } from '..';
|
|
||||||
import { Col, Row } from 'antd';
|
|
||||||
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants'
|
|
||||||
import { IIntervalUnit, resolveTimeFromInterval } from 'container/TraceDetail/utils';
|
|
||||||
import useThemeMode from 'hooks/useThemeMode';
|
|
||||||
|
|
||||||
const Trace = (props: TraceProps): JSX.Element => {
|
function Trace(props: TraceProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
activeHoverId,
|
activeHoverId,
|
||||||
@ -38,7 +40,7 @@ const Trace = (props: TraceProps): JSX.Element => {
|
|||||||
intervalUnit,
|
intervalUnit,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { isDarkMode } = useThemeMode()
|
const { isDarkMode } = useThemeMode();
|
||||||
const [isOpen, setOpen] = useState<boolean>(activeSpanPath[level] === id);
|
const [isOpen, setOpen] = useState<boolean>(activeSpanPath[level] === id);
|
||||||
|
|
||||||
const localTreeExpandInteraction = useRef<boolean | 0>(0); // Boolean is for the state of the expansion whereas the number i.e. 0 is for skipping the user interaction.
|
const localTreeExpandInteraction = useRef<boolean | 0>(0); // Boolean is for the state of the expansion whereas the number i.e. 0 is for skipping the user interaction.
|
||||||
@ -47,20 +49,18 @@ const Trace = (props: TraceProps): JSX.Element => {
|
|||||||
if (localTreeExpandInteraction.current !== 0) {
|
if (localTreeExpandInteraction.current !== 0) {
|
||||||
setOpen(localTreeExpandInteraction.current);
|
setOpen(localTreeExpandInteraction.current);
|
||||||
localTreeExpandInteraction.current = 0;
|
localTreeExpandInteraction.current = 0;
|
||||||
|
} else if (!isOpen) {
|
||||||
|
setOpen(activeSpanPath[level] === id);
|
||||||
}
|
}
|
||||||
else if (!isOpen) {
|
}, [activeSpanPath, isOpen]);
|
||||||
setOpen(activeSpanPath[level] === id)
|
|
||||||
}
|
|
||||||
}, [activeSpanPath, isOpen])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isExpandAll) {
|
if (isExpandAll) {
|
||||||
setOpen(isExpandAll)
|
setOpen(isExpandAll);
|
||||||
|
} else {
|
||||||
|
setOpen(activeSpanPath[level] === id);
|
||||||
}
|
}
|
||||||
else {
|
}, [isExpandAll]);
|
||||||
setOpen(activeSpanPath[level] === id)
|
|
||||||
}
|
|
||||||
}, [isExpandAll])
|
|
||||||
|
|
||||||
const isOnlyChild = props.children.length === 1;
|
const isOnlyChild = props.children.length === 1;
|
||||||
const [top, setTop] = useState<number>(0);
|
const [top, setTop] = useState<number>(0);
|
||||||
@ -69,9 +69,13 @@ const Trace = (props: TraceProps): JSX.Element => {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (activeSelectedId === id) {
|
if (activeSelectedId === id) {
|
||||||
ref.current?.scrollIntoView({ block: 'nearest', behavior: 'auto', inline: 'nearest' });
|
ref.current?.scrollIntoView({
|
||||||
|
block: 'nearest',
|
||||||
|
behavior: 'auto',
|
||||||
|
inline: 'nearest',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [activeSelectedId])
|
}, [activeSelectedId]);
|
||||||
|
|
||||||
const onMouseEnterHandler = () => {
|
const onMouseEnterHandler = () => {
|
||||||
setActiveHoverId(props.id);
|
setActiveHoverId(props.id);
|
||||||
@ -87,21 +91,23 @@ const Trace = (props: TraceProps): JSX.Element => {
|
|||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
setActiveSelectedId(id);
|
setActiveSelectedId(id);
|
||||||
}
|
};
|
||||||
|
|
||||||
const onClickTreeExpansion = (event) => {
|
const onClickTreeExpansion = (event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation();
|
||||||
setOpen((state) => { localTreeExpandInteraction.current = !isOpen; return !state });
|
setOpen((state) => {
|
||||||
}
|
localTreeExpandInteraction.current = !isOpen;
|
||||||
|
return !state;
|
||||||
|
});
|
||||||
|
};
|
||||||
const { totalSpans } = getMetaDataFromSpanTree(props);
|
const { totalSpans } = getMetaDataFromSpanTree(props);
|
||||||
|
|
||||||
const inMsCount = value;
|
const inMsCount = value;
|
||||||
const nodeLeftOffset = ((startTime - globalStart) * 1e2) / globalSpread;
|
const nodeLeftOffset = ((startTime - globalStart) * 1e2) / globalSpread;
|
||||||
const width = (value * 1e2) / (globalSpread * 1e6);
|
const width = (value * 1e2) / (globalSpread * 1e6);
|
||||||
const panelWidth = SPAN_DETAILS_LEFT_COL_WIDTH - (level * (16 + 1)) - 16;
|
const panelWidth = SPAN_DETAILS_LEFT_COL_WIDTH - level * (16 + 1) - 48;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Wrapper
|
<Wrapper
|
||||||
onMouseEnter={onMouseEnterHandler}
|
onMouseEnter={onMouseEnterHandler}
|
||||||
onMouseLeave={onMouseLeaveHandler}
|
onMouseLeave={onMouseLeaveHandler}
|
||||||
@ -115,17 +121,12 @@ const Trace = (props: TraceProps): JSX.Element => {
|
|||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CardContainer
|
<CardContainer onClick={onClick}>
|
||||||
onClick={onClick}
|
<StyledCol flex={`${panelWidth}px`} styledclass={[styles.overFlowHidden]}>
|
||||||
>
|
<StyledRow styledclass={[styles.flexNoWrap]}>
|
||||||
<Col flex={`${panelWidth}px`} style={{ overflow: 'hidden' }}>
|
|
||||||
<Row style={{ flexWrap: 'nowrap' }}>
|
|
||||||
<Col>
|
<Col>
|
||||||
{totalSpans !== 1 && (
|
{totalSpans !== 1 && (
|
||||||
<CardComponent
|
<CardComponent isDarkMode={isDarkMode} onClick={onClickTreeExpansion}>
|
||||||
isDarkMode={isDarkMode}
|
|
||||||
onClick={onClickTreeExpansion}
|
|
||||||
>
|
|
||||||
{totalSpans}
|
{totalSpans}
|
||||||
<CaretContainer>
|
<CaretContainer>
|
||||||
{isOpen ? <CaretDownFilled /> : <CaretRightFilled />}
|
{isOpen ? <CaretDownFilled /> : <CaretRightFilled />}
|
||||||
@ -136,15 +137,15 @@ const Trace = (props: TraceProps): JSX.Element => {
|
|||||||
<Col>
|
<Col>
|
||||||
<SpanName name={name} serviceName={serviceName} />
|
<SpanName name={name} serviceName={serviceName} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</StyledRow>
|
||||||
</Col>
|
</StyledCol>
|
||||||
<Col flex={'1'} >
|
<Col flex="1">
|
||||||
<SpanLength
|
<SpanLength
|
||||||
leftOffset={nodeLeftOffset.toString()}
|
leftOffset={nodeLeftOffset.toString()}
|
||||||
width={width.toString()}
|
width={width.toString()}
|
||||||
bgColor={serviceColour}
|
bgColor={serviceColour}
|
||||||
id={id}
|
id={id}
|
||||||
inMsCount={(inMsCount / 1e6)}
|
inMsCount={inMsCount / 1e6}
|
||||||
intervalUnit={intervalUnit}
|
intervalUnit={intervalUnit}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
@ -171,9 +172,8 @@ const Trace = (props: TraceProps): JSX.Element => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface ITraceGlobal {
|
interface ITraceGlobal {
|
||||||
globalSpread: ITraceMetaData['spread'];
|
globalSpread: ITraceMetaData['spread'];
|
||||||
|
@ -75,3 +75,16 @@ export const HoverCard = styled.div<HoverCardProps>`
|
|||||||
height: 3rem;
|
height: 3rem;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const flexNoWrap = css`
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const overFlowHidden = css`
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const styles = {
|
||||||
|
flexNoWrap,
|
||||||
|
overFlowHidden,
|
||||||
|
};
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
|
import { MinusSquareOutlined, PlusSquareOutlined } from '@ant-design/icons';
|
||||||
|
import { IIntervalUnit } from 'container/TraceDetail/utils';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import Trace from './Trace';
|
|
||||||
import { MinusSquareOutlined, PlusSquareOutlined } from '@ant-design/icons'
|
|
||||||
import { Wrapper, CardWrapper, CardContainer, CollapseButton } from './styles';
|
|
||||||
import { getSpanPath } from './utils';
|
|
||||||
import { IIntervalUnit } from 'container/TraceDetail/utils'
|
|
||||||
import { ITraceTree } from 'types/api/trace/getTraceItem';
|
import { ITraceTree } from 'types/api/trace/getTraceItem';
|
||||||
|
|
||||||
const GanttChart = (props: GanttChartProps): JSX.Element => {
|
import { CardContainer, CardWrapper, CollapseButton, Wrapper } from './styles';
|
||||||
|
import Trace from './Trace';
|
||||||
|
import { getSpanPath } from './utils';
|
||||||
|
|
||||||
|
function GanttChart(props: GanttChartProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
traceMetaData,
|
traceMetaData,
|
||||||
@ -15,7 +16,7 @@ const GanttChart = (props: GanttChartProps): JSX.Element => {
|
|||||||
activeSelectedId,
|
activeSelectedId,
|
||||||
setActiveSelectedId,
|
setActiveSelectedId,
|
||||||
spanId,
|
spanId,
|
||||||
intervalUnit
|
intervalUnit,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const { globalStart, spread: globalSpread } = traceMetaData;
|
const { globalStart, spread: globalSpread } = traceMetaData;
|
||||||
@ -24,21 +25,23 @@ const GanttChart = (props: GanttChartProps): JSX.Element => {
|
|||||||
const [activeSpanPath, setActiveSpanPath] = useState<string[]>([]);
|
const [activeSpanPath, setActiveSpanPath] = useState<string[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveSpanPath(getSpanPath(data, spanId))
|
setActiveSpanPath(getSpanPath(data, spanId));
|
||||||
}, [spanId]);
|
}, [spanId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActiveSpanPath(getSpanPath(data, activeSelectedId))
|
setActiveSpanPath(getSpanPath(data, activeSelectedId));
|
||||||
}, [activeSelectedId]);
|
}, [activeSelectedId]);
|
||||||
|
|
||||||
const handleCollapse = () => {
|
const handleCollapse = () => {
|
||||||
setIsExpandAll((prev) => !prev);
|
setIsExpandAll((prev) => !prev);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<CardContainer>
|
<CardContainer>
|
||||||
<CollapseButton onClick={handleCollapse} style={{ fontSize: '1.2rem' }} title={isExpandAll ? 'Collapse All' : "Expand All"}>
|
<CollapseButton
|
||||||
|
onClick={handleCollapse}
|
||||||
|
title={isExpandAll ? 'Collapse All' : 'Expand All'}
|
||||||
|
>
|
||||||
{isExpandAll ? <MinusSquareOutlined /> : <PlusSquareOutlined />}
|
{isExpandAll ? <MinusSquareOutlined /> : <PlusSquareOutlined />}
|
||||||
</CollapseButton>
|
</CollapseButton>
|
||||||
<CardWrapper>
|
<CardWrapper>
|
||||||
@ -61,9 +64,8 @@ const GanttChart = (props: GanttChartProps): JSX.Element => {
|
|||||||
</CardWrapper>
|
</CardWrapper>
|
||||||
</CardContainer>
|
</CardContainer>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface ITraceMetaData {
|
export interface ITraceMetaData {
|
||||||
globalEnd: number;
|
globalEnd: number;
|
||||||
@ -81,7 +83,7 @@ export interface GanttChartProps {
|
|||||||
setActiveHoverId: React.Dispatch<React.SetStateAction<string>>;
|
setActiveHoverId: React.Dispatch<React.SetStateAction<string>>;
|
||||||
setActiveSelectedId: React.Dispatch<React.SetStateAction<string>>;
|
setActiveSelectedId: React.Dispatch<React.SetStateAction<string>>;
|
||||||
spanId: string;
|
spanId: string;
|
||||||
intervalUnit: IIntervalUnit
|
intervalUnit: IIntervalUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GanttChart;
|
export default GanttChart;
|
||||||
|
@ -44,4 +44,5 @@ export const CollapseButton = styled.div`
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
`;
|
`;
|
||||||
|
@ -5,13 +5,13 @@ export const getMetaDataFromSpanTree = (treeData: ITraceTree) => {
|
|||||||
let globalEnd = Number.NEGATIVE_INFINITY;
|
let globalEnd = Number.NEGATIVE_INFINITY;
|
||||||
let totalSpans = 0;
|
let totalSpans = 0;
|
||||||
let levels = 1;
|
let levels = 1;
|
||||||
const traverse = (treeNode: ITraceTree, level: number = 0) => {
|
const traverse = (treeNode: ITraceTree, level = 0) => {
|
||||||
if (!treeNode) {
|
if (!treeNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
totalSpans++;
|
totalSpans++;
|
||||||
levels = Math.max(levels, level);
|
levels = Math.max(levels, level);
|
||||||
const startTime = treeNode.startTime;
|
const { startTime } = treeNode;
|
||||||
const endTime = startTime + treeNode.value;
|
const endTime = startTime + treeNode.value;
|
||||||
globalStart = Math.min(globalStart, startTime);
|
globalStart = Math.min(globalStart, startTime);
|
||||||
globalEnd = Math.max(globalEnd, endTime);
|
globalEnd = Math.max(globalEnd, endTime);
|
||||||
@ -22,8 +22,8 @@ export const getMetaDataFromSpanTree = (treeData: ITraceTree) => {
|
|||||||
};
|
};
|
||||||
traverse(treeData, 1);
|
traverse(treeData, 1);
|
||||||
|
|
||||||
globalStart = globalStart * 1e6;
|
globalStart *= 1e6;
|
||||||
globalEnd = globalEnd * 1e6;
|
globalEnd *= 1e6;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
globalStart,
|
globalStart,
|
||||||
@ -35,19 +35,19 @@ export const getMetaDataFromSpanTree = (treeData: ITraceTree) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getTopLeftFromBody(elem: HTMLElement) {
|
export function getTopLeftFromBody(elem: HTMLElement) {
|
||||||
let box = elem.getBoundingClientRect();
|
const box = elem.getBoundingClientRect();
|
||||||
|
|
||||||
let body = document.body;
|
const { body } = document;
|
||||||
let docEl = document.documentElement;
|
const docEl = document.documentElement;
|
||||||
|
|
||||||
let scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
|
const scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
|
||||||
let scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
|
const scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
|
||||||
|
|
||||||
let clientTop = docEl.clientTop || body.clientTop || 0;
|
const clientTop = docEl.clientTop || body.clientTop || 0;
|
||||||
let clientLeft = docEl.clientLeft || body.clientLeft || 0;
|
const clientLeft = docEl.clientLeft || body.clientLeft || 0;
|
||||||
|
|
||||||
let top = box.top + scrollTop - clientTop;
|
const top = box.top + scrollTop - clientTop;
|
||||||
let left = box.left + scrollLeft - clientLeft;
|
const left = box.left + scrollLeft - clientLeft;
|
||||||
|
|
||||||
return { top: Math.round(top), left: Math.round(left) };
|
return { top: Math.round(top), left: Math.round(left) };
|
||||||
}
|
}
|
||||||
@ -56,8 +56,8 @@ export const getNodeById = (
|
|||||||
searchingId: string,
|
searchingId: string,
|
||||||
treeData: ITraceTree,
|
treeData: ITraceTree,
|
||||||
): ITraceTree | undefined => {
|
): ITraceTree | undefined => {
|
||||||
let foundNode: ITraceTree | undefined = undefined;
|
let foundNode: ITraceTree | undefined;
|
||||||
const traverse = (treeNode: ITraceTree, level: number = 0) => {
|
const traverse = (treeNode: ITraceTree, level = 0) => {
|
||||||
if (!treeNode) {
|
if (!treeNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ export const isSpanPresent = (
|
|||||||
|
|
||||||
const traverse = (
|
const traverse = (
|
||||||
treeNode: ITraceTree,
|
treeNode: ITraceTree,
|
||||||
level: number = 0,
|
level = 0,
|
||||||
foundNode: ITraceTree[],
|
foundNode: ITraceTree[],
|
||||||
) => {
|
) => {
|
||||||
if (!treeNode) {
|
if (!treeNode) {
|
||||||
|
@ -12,13 +12,13 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
const Retention = ({
|
function Retention({
|
||||||
retentionValue,
|
retentionValue,
|
||||||
setRentionValue,
|
setRentionValue,
|
||||||
selectedRetentionPeroid,
|
selectedRetentionPeroid,
|
||||||
setSelectedRetentionPeroid,
|
setSelectedRetentionPeroid,
|
||||||
text,
|
text,
|
||||||
}: RetentionProps): JSX.Element => {
|
}: RetentionProps): JSX.Element {
|
||||||
const options: Option[] = [
|
const options: Option[] = [
|
||||||
{
|
{
|
||||||
key: 'hr',
|
key: 'hr',
|
||||||
@ -58,7 +58,7 @@ const Retention = ({
|
|||||||
e: React.ChangeEvent<HTMLInputElement>,
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
func: React.Dispatch<React.SetStateAction<string>>,
|
func: React.Dispatch<React.SetStateAction<string>>,
|
||||||
): void => {
|
): void => {
|
||||||
const value = e.target.value;
|
const { value } = e.target;
|
||||||
const integerValue = parseInt(value, 10);
|
const integerValue = parseInt(value, 10);
|
||||||
|
|
||||||
if (value.length > 0 && integerValue.toString() === value) {
|
if (value.length > 0 && integerValue.toString() === value) {
|
||||||
@ -89,7 +89,7 @@ const Retention = ({
|
|||||||
</Dropdown>
|
</Dropdown>
|
||||||
</RetentionContainer>
|
</RetentionContainer>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
key: SettingPeroid;
|
key: SettingPeroid;
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
ToolTipContainer,
|
ToolTipContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
const GeneralSettings = (): JSX.Element => {
|
function GeneralSettings(): JSX.Element {
|
||||||
const [
|
const [
|
||||||
selectedMetricsPeroid,
|
selectedMetricsPeroid,
|
||||||
setSelectedMetricsPeroid,
|
setSelectedMetricsPeroid,
|
||||||
@ -207,7 +207,7 @@ const GeneralSettings = (): JSX.Element => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Retention
|
<Retention
|
||||||
text={'Retention Period for Metrics'}
|
text="Retention Period for Metrics"
|
||||||
selectedRetentionPeroid={selectedMetricsPeroid}
|
selectedRetentionPeroid={selectedMetricsPeroid}
|
||||||
setRentionValue={setRetentionPeroidMetrics}
|
setRentionValue={setRetentionPeroidMetrics}
|
||||||
retentionValue={retentionPeroidMetrics}
|
retentionValue={retentionPeroidMetrics}
|
||||||
@ -215,7 +215,7 @@ const GeneralSettings = (): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Retention
|
<Retention
|
||||||
text={'Retention Period for Traces'}
|
text="Retention Period for Traces"
|
||||||
selectedRetentionPeroid={selectedTracePeroid}
|
selectedRetentionPeroid={selectedTracePeroid}
|
||||||
setRentionValue={setRetentionPeroidTrace}
|
setRentionValue={setRetentionPeroidTrace}
|
||||||
retentionValue={retentionPeroidTrace}
|
retentionValue={retentionPeroidTrace}
|
||||||
@ -250,7 +250,7 @@ const GeneralSettings = (): JSX.Element => {
|
|||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export type SettingPeroid = 'hr' | 'day' | 'month';
|
export type SettingPeroid = 'hr' | 'day' | 'month';
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import React from 'react';
|
|||||||
|
|
||||||
import { TitleContainer, ValueContainer } from './styles';
|
import { TitleContainer, ValueContainer } from './styles';
|
||||||
|
|
||||||
const GridGraphComponent = ({
|
function GridGraphComponent({
|
||||||
GRAPH_TYPES,
|
GRAPH_TYPES,
|
||||||
data,
|
data,
|
||||||
title,
|
title,
|
||||||
@ -16,7 +16,8 @@ const GridGraphComponent = ({
|
|||||||
isStacked,
|
isStacked,
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
name,
|
name,
|
||||||
}: GridGraphComponentProps): JSX.Element | null => {
|
yAxisUnit,
|
||||||
|
}: GridGraphComponentProps): JSX.Element | null {
|
||||||
const location = history.location.pathname;
|
const location = history.location.pathname;
|
||||||
|
|
||||||
const isDashboardPage = location.split('/').length === 3;
|
const isDashboardPage = location.split('/').length === 3;
|
||||||
@ -31,8 +32,9 @@ const GridGraphComponent = ({
|
|||||||
isStacked,
|
isStacked,
|
||||||
opacity,
|
opacity,
|
||||||
xAxisType: 'time',
|
xAxisType: 'time',
|
||||||
onClickHandler: onClickHandler,
|
onClickHandler,
|
||||||
name,
|
name,
|
||||||
|
yAxisUnit,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -62,7 +64,7 @@ const GridGraphComponent = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface GridGraphComponentProps {
|
export interface GridGraphComponentProps {
|
||||||
GRAPH_TYPES: GRAPH_TYPES;
|
GRAPH_TYPES: GRAPH_TYPES;
|
||||||
@ -72,6 +74,7 @@ export interface GridGraphComponentProps {
|
|||||||
isStacked?: boolean;
|
isStacked?: boolean;
|
||||||
onClickHandler?: graphOnClickHandler;
|
onClickHandler?: graphOnClickHandler;
|
||||||
name: string;
|
name: string;
|
||||||
|
yAxisUnit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GridGraphComponent;
|
export default GridGraphComponent;
|
||||||
|
@ -14,7 +14,7 @@ import DashboardReducer from 'types/reducer/dashboards';
|
|||||||
|
|
||||||
import { Button, Container } from './styles';
|
import { Button, Container } from './styles';
|
||||||
|
|
||||||
const AddWidget = ({ toggleAddWidget }: Props): JSX.Element => {
|
function AddWidget({ toggleAddWidget }: Props): JSX.Element {
|
||||||
const { isAddWidget } = useSelector<AppState, DashboardReducer>(
|
const { isAddWidget } = useSelector<AppState, DashboardReducer>(
|
||||||
(state) => state.dashboards,
|
(state) => state.dashboards,
|
||||||
);
|
);
|
||||||
@ -26,17 +26,15 @@ const AddWidget = ({ toggleAddWidget }: Props): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{!isAddWidget ? (
|
{!isAddWidget ? (
|
||||||
<>
|
|
||||||
<Button onClick={onToggleHandler} icon={<PlusOutlined />}>
|
<Button onClick={onToggleHandler} icon={<PlusOutlined />}>
|
||||||
Add Widgets
|
Add Widgets
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<Typography>Click a widget icon to add it here</Typography>
|
<Typography>Click a widget icon to add it here</Typography>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
toggleAddWidget: (
|
toggleAddWidget: (
|
||||||
|
@ -3,24 +3,23 @@ import {
|
|||||||
EditFilled,
|
EditFilled,
|
||||||
FullscreenOutlined,
|
FullscreenOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import React, { useCallback } from 'react';
|
import history from 'lib/history';
|
||||||
import { useHistory, useLocation } from 'react-router';
|
import React from 'react';
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
const Bar = ({
|
function Bar({
|
||||||
widget,
|
widget,
|
||||||
onViewFullScreenHandler,
|
onViewFullScreenHandler,
|
||||||
onDeleteHandler,
|
onDeleteHandler,
|
||||||
}: BarProps): JSX.Element => {
|
}: BarProps): JSX.Element {
|
||||||
const { push } = useHistory();
|
const onEditHandler = (): void => {
|
||||||
const { pathname } = useLocation();
|
|
||||||
|
|
||||||
const onEditHandler = useCallback(() => {
|
|
||||||
const widgetId = widget.id;
|
const widgetId = widget.id;
|
||||||
push(`${pathname}/new?widgetId=${widgetId}&graphType=${widget.panelTypes}`);
|
history.push(
|
||||||
}, [push, pathname, widget]);
|
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${widget.panelTypes}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
@ -29,7 +28,7 @@ const Bar = ({
|
|||||||
<DeleteOutlined onClick={onDeleteHandler} />
|
<DeleteOutlined onClick={onDeleteHandler} />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface BarProps {
|
interface BarProps {
|
||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
|
@ -10,11 +10,11 @@ import { AppState } from 'store/reducers';
|
|||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
const EmptyGraph = ({
|
function EmptyGraph({
|
||||||
selectedTime,
|
selectedTime,
|
||||||
widget,
|
widget,
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
}: EmptyGraphProps): JSX.Element => {
|
}: EmptyGraphProps): JSX.Element {
|
||||||
const { minTime, maxTime, loading } = useSelector<AppState, GlobalReducer>(
|
const { minTime, maxTime, loading } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
@ -47,7 +47,7 @@ const EmptyGraph = ({
|
|||||||
while (endDate >= startDate) {
|
while (endDate >= startDate) {
|
||||||
const newDate = new Date(startDate);
|
const newDate = new Date(startDate);
|
||||||
|
|
||||||
startDate = startDate + 20000;
|
startDate += 20000;
|
||||||
|
|
||||||
dates.push(newDate);
|
dates.push(newDate);
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ const EmptyGraph = ({
|
|||||||
<Graph
|
<Graph
|
||||||
{...{
|
{...{
|
||||||
type: 'line',
|
type: 'line',
|
||||||
onClickHandler: onClickHandler,
|
onClickHandler,
|
||||||
data: {
|
data: {
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
@ -79,7 +79,7 @@ const EmptyGraph = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface EmptyGraphProps {
|
interface EmptyGraphProps {
|
||||||
selectedTime: timePreferance;
|
selectedTime: timePreferance;
|
||||||
|
@ -26,13 +26,14 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
|||||||
import EmptyGraph from './EmptyGraph';
|
import EmptyGraph from './EmptyGraph';
|
||||||
import { NotFoundContainer, TimeContainer } from './styles';
|
import { NotFoundContainer, TimeContainer } from './styles';
|
||||||
|
|
||||||
const FullView = ({
|
function FullView({
|
||||||
widget,
|
widget,
|
||||||
fullViewOptions = true,
|
fullViewOptions = true,
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
noDataGraph = false,
|
noDataGraph = false,
|
||||||
name,
|
name,
|
||||||
}: FullViewProps): JSX.Element => {
|
yAxisUnit,
|
||||||
|
}: FullViewProps): JSX.Element {
|
||||||
const { minTime, maxTime, selectedTime: globalSelectedTime } = useSelector<
|
const { minTime, maxTime, selectedTime: globalSelectedTime } = useSelector<
|
||||||
AppState,
|
AppState,
|
||||||
GlobalReducer
|
GlobalReducer
|
||||||
@ -82,7 +83,6 @@ const FullView = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const queryMinMax = getMinMax(selectedTime.enum);
|
const queryMinMax = getMinMax(selectedTime.enum);
|
||||||
|
|
||||||
const response = await Promise.all(
|
const response = await Promise.all(
|
||||||
widget.query
|
widget.query
|
||||||
.filter((e) => e.query.length !== 0)
|
.filter((e) => e.query.length !== 0)
|
||||||
@ -220,14 +220,15 @@ const FullView = ({
|
|||||||
isStacked: widget.isStacked,
|
isStacked: widget.isStacked,
|
||||||
opacity: widget.opacity,
|
opacity: widget.opacity,
|
||||||
title: widget.title,
|
title: widget.title,
|
||||||
onClickHandler: onClickHandler,
|
onClickHandler,
|
||||||
name,
|
name,
|
||||||
|
yAxisUnit,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* </GraphContainer> */}
|
{/* </GraphContainer> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface FullViewState {
|
interface FullViewState {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -242,6 +243,7 @@ interface FullViewProps {
|
|||||||
onClickHandler?: graphOnClickHandler;
|
onClickHandler?: graphOnClickHandler;
|
||||||
noDataGraph?: boolean;
|
noDataGraph?: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
|
yAxisUnit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FullView;
|
export default FullView;
|
||||||
|
@ -8,8 +8,7 @@ import getChartData from 'lib/getChartData';
|
|||||||
import GetMaxMinTime from 'lib/getMaxMinTime';
|
import GetMaxMinTime from 'lib/getMaxMinTime';
|
||||||
import GetStartAndEndTime from 'lib/getStartAndEndTime';
|
import GetStartAndEndTime from 'lib/getStartAndEndTime';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import {
|
import {
|
||||||
@ -23,14 +22,15 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
|||||||
|
|
||||||
import Bar from './Bar';
|
import Bar from './Bar';
|
||||||
import FullView from './FullView';
|
import FullView from './FullView';
|
||||||
import { Modal, FullViewContainer, ErrorContainer } from './styles';
|
import { ErrorContainer, FullViewContainer, Modal } from './styles';
|
||||||
|
|
||||||
const GridCardGraph = ({
|
function GridCardGraph({
|
||||||
widget,
|
widget,
|
||||||
deleteWidget,
|
deleteWidget,
|
||||||
isDeleted,
|
isDeleted,
|
||||||
name,
|
name,
|
||||||
}: GridCardGraphProps): JSX.Element => {
|
yAxisUnit,
|
||||||
|
}: GridCardGraphProps): JSX.Element {
|
||||||
const [state, setState] = useState<GridCardGraphState>({
|
const [state, setState] = useState<GridCardGraphState>({
|
||||||
loading: true,
|
loading: true,
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
@ -47,7 +47,7 @@ const GridCardGraph = ({
|
|||||||
(async (): Promise<void> => {
|
(async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const getMaxMinTime = GetMaxMinTime({
|
const getMaxMinTime = GetMaxMinTime({
|
||||||
graphType: widget.panelTypes,
|
graphType: widget?.panelTypes,
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
});
|
});
|
||||||
@ -65,7 +65,7 @@ const GridCardGraph = ({
|
|||||||
const result = await getQueryResult({
|
const result = await getQueryResult({
|
||||||
end,
|
end,
|
||||||
query: query.query,
|
query: query.query,
|
||||||
start: start,
|
start,
|
||||||
step: '60',
|
step: '60',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ const GridCardGraph = ({
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const getModals = () => {
|
const getModals = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
@ -149,7 +149,11 @@ const GridCardGraph = ({
|
|||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
<FullViewContainer>
|
<FullViewContainer>
|
||||||
<FullView name={name + 'expanded'} widget={widget} />
|
<FullView
|
||||||
|
name={`${name}expanded`}
|
||||||
|
widget={widget}
|
||||||
|
yAxisUnit={yAxisUnit}
|
||||||
|
/>
|
||||||
</FullViewContainer>
|
</FullViewContainer>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
@ -199,11 +203,12 @@ const GridCardGraph = ({
|
|||||||
opacity: widget.opacity,
|
opacity: widget.opacity,
|
||||||
title: widget.title,
|
title: widget.title,
|
||||||
name,
|
name,
|
||||||
|
yAxisUnit,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface GridCardGraphState {
|
interface GridCardGraphState {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@ -222,6 +227,7 @@ interface GridCardGraphProps extends DispatchProps {
|
|||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
isDeleted: React.MutableRefObject<boolean>;
|
isDeleted: React.MutableRefObject<boolean>;
|
||||||
name: string;
|
name: string;
|
||||||
|
yAxisUnit: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
|
/* eslint-disable react/jsx-no-useless-fragment */
|
||||||
|
/* eslint-disable react/no-unstable-nested-components */
|
||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
import { SaveFilled } from '@ant-design/icons';
|
import { SaveFilled } from '@ant-design/icons';
|
||||||
|
import { notification } from 'antd';
|
||||||
import updateDashboardApi from 'api/dashboard/update';
|
import updateDashboardApi from 'api/dashboard/update';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
|
import history from 'lib/history';
|
||||||
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { Layout } from 'react-grid-layout';
|
import { Layout } from 'react-grid-layout';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import DashboardReducer from 'types/reducer/dashboards';
|
import DashboardReducer from 'types/reducer/dashboards';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
@ -20,11 +24,9 @@ import {
|
|||||||
CardContainer,
|
CardContainer,
|
||||||
ReactGridLayout,
|
ReactGridLayout,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
import { updateDashboard } from './utils';
|
||||||
|
|
||||||
const GridGraph = (): JSX.Element => {
|
function GridGraph(): JSX.Element {
|
||||||
const { push } = useHistory();
|
|
||||||
const { pathname } = useLocation();
|
|
||||||
|
|
||||||
const { dashboards, loading } = useSelector<AppState, DashboardReducer>(
|
const { dashboards, loading } = useSelector<AppState, DashboardReducer>(
|
||||||
(state) => state.dashboards,
|
(state) => state.dashboards,
|
||||||
);
|
);
|
||||||
@ -38,7 +40,6 @@ const GridGraph = (): JSX.Element => {
|
|||||||
const [selectedDashboard] = dashboards;
|
const [selectedDashboard] = dashboards;
|
||||||
const { data } = selectedDashboard;
|
const { data } = selectedDashboard;
|
||||||
const { widgets } = data;
|
const { widgets } = data;
|
||||||
|
|
||||||
const [layouts, setLayout] = useState<LayoutProps[]>([]);
|
const [layouts, setLayout] = useState<LayoutProps[]>([]);
|
||||||
|
|
||||||
const AddWidgetWrapper = useCallback(() => <AddWidget />, []);
|
const AddWidgetWrapper = useCallback(() => <AddWidget />, []);
|
||||||
@ -51,6 +52,7 @@ const GridGraph = (): JSX.Element => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when the layout is not present
|
||||||
if (data.layout === undefined) {
|
if (data.layout === undefined) {
|
||||||
return widgets.map((e, index) => {
|
return widgets.map((e, index) => {
|
||||||
return {
|
return {
|
||||||
@ -61,22 +63,33 @@ const GridGraph = (): JSX.Element => {
|
|||||||
x: (index % 2) * 6,
|
x: (index % 2) * 6,
|
||||||
Component: (): JSX.Element => (
|
Component: (): JSX.Element => (
|
||||||
<Graph
|
<Graph
|
||||||
name={e.id + index + 'non-expanded'}
|
name={`${e.id + index}non-expanded`}
|
||||||
isDeleted={isDeleted}
|
isDeleted={isDeleted}
|
||||||
widget={widgets[index]}
|
widget={widgets[index]}
|
||||||
|
yAxisUnit={e.yAxisUnit}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return data.layout.map((e, index) => ({
|
|
||||||
...e,
|
|
||||||
y: 0,
|
|
||||||
Component: (): JSX.Element => (
|
|
||||||
<Graph name={e.i + index} isDeleted={isDeleted} widget={widgets[index]} />
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
return data.layout
|
||||||
|
.filter((_, index) => widgets[index])
|
||||||
|
.map((e, index) => ({
|
||||||
|
...e,
|
||||||
|
Component: (): JSX.Element => {
|
||||||
|
if (widgets[index]) {
|
||||||
|
return (
|
||||||
|
<Graph
|
||||||
|
name={e.i + index}
|
||||||
|
isDeleted={isDeleted}
|
||||||
|
widget={widgets[index]}
|
||||||
|
yAxisUnit={widgets[index].yAxisUnit}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <></>;
|
||||||
|
},
|
||||||
|
}));
|
||||||
}, [widgets, data.layout]);
|
}, [widgets, data.layout]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -85,11 +98,21 @@ const GridGraph = (): JSX.Element => {
|
|||||||
(isMounted.current === true || isDeleted.current === true)
|
(isMounted.current === true || isDeleted.current === true)
|
||||||
) {
|
) {
|
||||||
const preLayouts = getPreLayouts();
|
const preLayouts = getPreLayouts();
|
||||||
setLayout(() => [
|
setLayout(() => {
|
||||||
|
const getX = (): number => {
|
||||||
|
if (preLayouts && preLayouts?.length > 0) {
|
||||||
|
const last = preLayouts[(preLayouts?.length || 0) - 1];
|
||||||
|
|
||||||
|
return (last.w + last.x) % 12;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
return [
|
||||||
...preLayouts,
|
...preLayouts,
|
||||||
{
|
{
|
||||||
i: (preLayouts.length + 1).toString(),
|
i: (preLayouts.length + 1).toString(),
|
||||||
x: (preLayouts.length % 2) * 6,
|
x: getX(),
|
||||||
y: Infinity,
|
y: Infinity,
|
||||||
w: 6,
|
w: 6,
|
||||||
h: 2,
|
h: 2,
|
||||||
@ -99,7 +122,8 @@ const GridGraph = (): JSX.Element => {
|
|||||||
isResizable: false,
|
isResizable: false,
|
||||||
isBounded: true,
|
isBounded: true,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return (): void => {
|
return (): void => {
|
||||||
@ -108,18 +132,39 @@ const GridGraph = (): JSX.Element => {
|
|||||||
}, [widgets, layouts.length, AddWidgetWrapper, loading, getPreLayouts]);
|
}, [widgets, layouts.length, AddWidgetWrapper, loading, getPreLayouts]);
|
||||||
|
|
||||||
const onDropHandler = useCallback(
|
const onDropHandler = useCallback(
|
||||||
(allLayouts: Layout[], currectLayout: Layout, event: DragEvent) => {
|
async (allLayouts: Layout[], currentLayout: Layout, event: DragEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (event.dataTransfer) {
|
if (event.dataTransfer) {
|
||||||
|
try {
|
||||||
const graphType = event.dataTransfer.getData('text') as GRAPH_TYPES;
|
const graphType = event.dataTransfer.getData('text') as GRAPH_TYPES;
|
||||||
const generateWidgetId = v4();
|
const generateWidgetId = v4();
|
||||||
push(`${pathname}/new?graphType=${graphType}&widgetId=${generateWidgetId}`);
|
|
||||||
|
await updateDashboard({
|
||||||
|
data,
|
||||||
|
generateWidgetId,
|
||||||
|
graphType,
|
||||||
|
selectedDashboard,
|
||||||
|
layout: allLayouts
|
||||||
|
.map((e, index) => ({
|
||||||
|
...e,
|
||||||
|
i: index.toString(),
|
||||||
|
// when a new element drops
|
||||||
|
w: e.i === '__dropping-elem__' ? 6 : e.w,
|
||||||
|
h: e.i === '__dropping-elem__' ? 2 : e.h,
|
||||||
|
}))
|
||||||
|
.filter((e) => e.maxW === undefined),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
message: error.toString() || 'Something went wrong',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[pathname, push],
|
[data, selectedDashboard],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLayoutSaveHanlder = async (): Promise<void> => {
|
const onLayoutSaveHandler = async (): Promise<void> => {
|
||||||
setSaveLayoutState((state) => ({
|
setSaveLayoutState((state) => ({
|
||||||
...state,
|
...state,
|
||||||
error: false,
|
error: false,
|
||||||
@ -171,7 +216,7 @@ const GridGraph = (): JSX.Element => {
|
|||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<Button
|
<Button
|
||||||
loading={saveLayoutState.loading}
|
loading={saveLayoutState.loading}
|
||||||
onClick={onLayoutSaveHanlder}
|
onClick={onLayoutSaveHandler}
|
||||||
icon={<SaveFilled />}
|
icon={<SaveFilled />}
|
||||||
danger={saveLayoutState.error}
|
danger={saveLayoutState.error}
|
||||||
>
|
>
|
||||||
@ -194,7 +239,7 @@ const GridGraph = (): JSX.Element => {
|
|||||||
{layouts.map(({ Component, ...rest }, index) => {
|
{layouts.map(({ Component, ...rest }, index) => {
|
||||||
const widget = (widgets || [])[index] || {};
|
const widget = (widgets || [])[index] || {};
|
||||||
|
|
||||||
const type = widget.panelTypes;
|
const type = widget?.panelTypes || 'TIME_SERIES';
|
||||||
|
|
||||||
const isQueryType = type === 'VALUE';
|
const isQueryType = type === 'VALUE';
|
||||||
|
|
||||||
@ -209,7 +254,7 @@ const GridGraph = (): JSX.Element => {
|
|||||||
</ReactGridLayout>
|
</ReactGridLayout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface LayoutProps extends Layout {
|
interface LayoutProps extends Layout {
|
||||||
Component: () => JSX.Element;
|
Component: () => JSX.Element;
|
||||||
|
66
frontend/src/container/GridGraphLayout/utils.ts
Normal file
66
frontend/src/container/GridGraphLayout/utils.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { notification } from 'antd';
|
||||||
|
import updateDashboardApi from 'api/dashboard/update';
|
||||||
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
|
import history from 'lib/history';
|
||||||
|
import { Layout } from 'react-grid-layout';
|
||||||
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
|
export const updateDashboard = async ({
|
||||||
|
data,
|
||||||
|
graphType,
|
||||||
|
generateWidgetId,
|
||||||
|
layout,
|
||||||
|
selectedDashboard,
|
||||||
|
}: UpdateDashboardProps): Promise<void> => {
|
||||||
|
const response = await updateDashboardApi({
|
||||||
|
title: data.title,
|
||||||
|
uuid: selectedDashboard.uuid,
|
||||||
|
description: data.description,
|
||||||
|
name: data.name,
|
||||||
|
tags: data.tags,
|
||||||
|
widgets: [
|
||||||
|
...(data.widgets || []),
|
||||||
|
{
|
||||||
|
description: '',
|
||||||
|
id: generateWidgetId,
|
||||||
|
isStacked: false,
|
||||||
|
nullZeroValues: '',
|
||||||
|
opacity: '',
|
||||||
|
panelTypes: graphType,
|
||||||
|
query: [
|
||||||
|
{
|
||||||
|
query: '',
|
||||||
|
legend: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
queryData: {
|
||||||
|
data: [],
|
||||||
|
error: false,
|
||||||
|
errorMessage: '',
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
timePreferance: 'GLOBAL_TIME',
|
||||||
|
title: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
layout,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.statusCode === 200) {
|
||||||
|
history.push(
|
||||||
|
`${history.location.pathname}/new?graphType=${graphType}&widgetId=${generateWidgetId}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
notification.error({
|
||||||
|
message: response.error || 'Something went wrong',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface UpdateDashboardProps {
|
||||||
|
data: Dashboard['data'];
|
||||||
|
graphType: GRAPH_TYPES;
|
||||||
|
generateWidgetId: string;
|
||||||
|
layout: Layout[];
|
||||||
|
selectedDashboard: Dashboard;
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { Breadcrumb } from 'antd';
|
import { Breadcrumb } from 'antd';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { RouteComponentProps, withRouter } from 'react-router';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
const breadcrumbNameMap = {
|
const breadcrumbNameMap = {
|
||||||
@ -13,9 +14,7 @@ const breadcrumbNameMap = {
|
|||||||
[ROUTES.DASHBOARD]: 'Dashboard',
|
[ROUTES.DASHBOARD]: 'Dashboard',
|
||||||
};
|
};
|
||||||
|
|
||||||
import { RouteComponentProps, withRouter } from 'react-router';
|
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
||||||
|
|
||||||
const ShowBreadcrumbs = (props: RouteComponentProps): JSX.Element => {
|
|
||||||
const pathArray = props.location.pathname.split('/').filter((i) => i);
|
const pathArray = props.location.pathname.split('/').filter((i) => i);
|
||||||
|
|
||||||
const extraBreadcrumbItems = pathArray.map((_, index) => {
|
const extraBreadcrumbItems = pathArray.map((_, index) => {
|
||||||
@ -27,13 +26,12 @@ const ShowBreadcrumbs = (props: RouteComponentProps): JSX.Element => {
|
|||||||
<Link to={url}>{url.split('/').slice(-1)[0]}</Link>
|
<Link to={url}>{url.split('/').slice(-1)[0]}</Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
return (
|
return (
|
||||||
<Breadcrumb.Item key={url}>
|
<Breadcrumb.Item key={url}>
|
||||||
<Link to={url}>{breadcrumbNameMap[url]}</Link>
|
<Link to={url}>{breadcrumbNameMap[url]}</Link>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const breadcrumbItems = [
|
const breadcrumbItems = [
|
||||||
@ -43,6 +41,6 @@ const ShowBreadcrumbs = (props: RouteComponentProps): JSX.Element => {
|
|||||||
].concat(extraBreadcrumbItems);
|
].concat(extraBreadcrumbItems);
|
||||||
|
|
||||||
return <Breadcrumb>{breadcrumbItems}</Breadcrumb>;
|
return <Breadcrumb>{breadcrumbItems}</Breadcrumb>;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default withRouter(ShowBreadcrumbs);
|
export default withRouter(ShowBreadcrumbs);
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { Modal } from 'antd';
|
import { Modal } from 'antd';
|
||||||
import React, { useState } from 'react';
|
|
||||||
export type DateTimeRangeType = [Dayjs | null, Dayjs | null] | null;
|
|
||||||
import DatePicker from 'components/DatePicker';
|
import DatePicker from 'components/DatePicker';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
export type DateTimeRangeType = [Dayjs | null, Dayjs | null] | null;
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
const CustomDateTimeModal = ({
|
function CustomDateTimeModal({
|
||||||
visible,
|
visible,
|
||||||
onCreate,
|
onCreate,
|
||||||
onCancel,
|
onCancel,
|
||||||
}: CustomDateTimeModalProps): JSX.Element => {
|
}: CustomDateTimeModalProps): JSX.Element {
|
||||||
const [
|
const [
|
||||||
customDateTimeRange,
|
customDateTimeRange,
|
||||||
setCustomDateTimeRange,
|
setCustomDateTimeRange,
|
||||||
@ -23,9 +24,8 @@ const CustomDateTimeModal = ({
|
|||||||
function disabledDate(current: Dayjs): boolean {
|
function disabledDate(current: Dayjs): boolean {
|
||||||
if (current > dayjs()) {
|
if (current > dayjs()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -36,7 +36,7 @@ const CustomDateTimeModal = ({
|
|||||||
cancelText="Cancel"
|
cancelText="Cancel"
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
style={{ position: 'absolute', top: 60, right: 40 }}
|
style={{ position: 'absolute', top: 60, right: 40 }}
|
||||||
onOk={(): void => onCreate(customDateTimeRange ? customDateTimeRange : null)}
|
onOk={(): void => onCreate(customDateTimeRange || null)}
|
||||||
>
|
>
|
||||||
<RangePicker
|
<RangePicker
|
||||||
disabledDate={disabledDate}
|
disabledDate={disabledDate}
|
||||||
@ -45,7 +45,7 @@ const CustomDateTimeModal = ({
|
|||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface CustomDateTimeModalProps {
|
interface CustomDateTimeModalProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
@ -2,9 +2,7 @@ import React, { useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { RefreshTextContainer, Typography } from './styles';
|
import { RefreshTextContainer, Typography } from './styles';
|
||||||
|
|
||||||
const RefreshText = ({
|
function RefreshText({ onLastRefreshHandler }: RefreshTextProps): JSX.Element {
|
||||||
onLastRefreshHandler,
|
|
||||||
}: RefreshTextProps): JSX.Element => {
|
|
||||||
const [refreshText, setRefreshText] = useState<string>('');
|
const [refreshText, setRefreshText] = useState<string>('');
|
||||||
|
|
||||||
// this is to update the refresh text
|
// this is to update the refresh text
|
||||||
@ -25,7 +23,7 @@ const RefreshText = ({
|
|||||||
<Typography>{refreshText}</Typography>
|
<Typography>{refreshText}</Typography>
|
||||||
</RefreshTextContainer>
|
</RefreshTextContainer>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface RefreshTextProps {
|
interface RefreshTextProps {
|
||||||
onLastRefreshHandler: () => string;
|
onLastRefreshHandler: () => string;
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
import { Button, Select as DefaultSelect } from 'antd';
|
import { Button, Select as DefaultSelect } from 'antd';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { getDefaultOption, getOptions, Time } from './config';
|
|
||||||
import { Container, Form, FormItem } from './styles';
|
|
||||||
const { Option } = DefaultSelect;
|
|
||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||||
import { LOCAL_STORAGE } from 'constants/localStorage';
|
import { LOCAL_STORAGE } from 'constants/localStorage';
|
||||||
import getTimeString from 'lib/getTimeString';
|
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
import getTimeString from 'lib/getTimeString';
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
import { RouteComponentProps, withRouter } from 'react-router';
|
import { RouteComponentProps, withRouter } from 'react-router';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
@ -19,13 +15,17 @@ import AppActions from 'types/actions';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import CustomDateTimeModal, { DateTimeRangeType } from '../CustomDateTimeModal';
|
import CustomDateTimeModal, { DateTimeRangeType } from '../CustomDateTimeModal';
|
||||||
|
import { getDefaultOption, getOptions, Time } from './config';
|
||||||
import RefreshText from './Refresh';
|
import RefreshText from './Refresh';
|
||||||
|
import { Container, Form, FormItem } from './styles';
|
||||||
|
|
||||||
const DateTimeSelection = ({
|
const { Option } = DefaultSelect;
|
||||||
|
|
||||||
|
function DateTimeSelection({
|
||||||
location,
|
location,
|
||||||
updateTimeInterval,
|
updateTimeInterval,
|
||||||
globalTimeLoading,
|
globalTimeLoading,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element {
|
||||||
const [form_dtselector] = Form.useForm();
|
const [form_dtselector] = Form.useForm();
|
||||||
|
|
||||||
const params = new URLSearchParams(location.search);
|
const params = new URLSearchParams(location.search);
|
||||||
@ -290,7 +290,7 @@ const DateTimeSelection = ({
|
|||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
updateTimeInterval: (
|
updateTimeInterval: (
|
||||||
|
@ -2,7 +2,7 @@ import { Col } from 'antd';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useLocation, matchPath } from 'react-router-dom';
|
import { matchPath, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import ShowBreadcrumbs from './Breadcrumbs';
|
import ShowBreadcrumbs from './Breadcrumbs';
|
||||||
import DateTimeSelector from './DateTimeSelection';
|
import DateTimeSelector from './DateTimeSelection';
|
||||||
@ -12,16 +12,15 @@ const routesToSkip = [
|
|||||||
ROUTES.SETTINGS,
|
ROUTES.SETTINGS,
|
||||||
ROUTES.LIST_ALL_ALERT,
|
ROUTES.LIST_ALL_ALERT,
|
||||||
ROUTES.TRACE_DETAIL,
|
ROUTES.TRACE_DETAIL,
|
||||||
|
ROUTES.ALL_CHANNELS,
|
||||||
];
|
];
|
||||||
|
|
||||||
const TopNav = (): JSX.Element | null => {
|
function TopNav(): JSX.Element | null {
|
||||||
const { pathname } = useLocation();
|
|
||||||
|
|
||||||
if (history.location.pathname === ROUTES.SIGN_UP) {
|
if (history.location.pathname === ROUTES.SIGN_UP) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkRouteExists = (currentPath: string) => {
|
const checkRouteExists = (currentPath: string): boolean => {
|
||||||
for (let i = 0; i < routesToSkip.length; ++i) {
|
for (let i = 0; i < routesToSkip.length; ++i) {
|
||||||
if (
|
if (
|
||||||
matchPath(currentPath, { path: routesToSkip[i], exact: true, strict: true })
|
matchPath(currentPath, { path: routesToSkip[i], exact: true, strict: true })
|
||||||
@ -38,13 +37,13 @@ const TopNav = (): JSX.Element | null => {
|
|||||||
<ShowBreadcrumbs />
|
<ShowBreadcrumbs />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
{!checkRouteExists(pathname) && (
|
{!checkRouteExists(history.location.pathname) && (
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<DateTimeSelector />
|
<DateTimeSelector />
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default TopNav;
|
export default TopNav;
|
||||||
|
@ -6,11 +6,11 @@ import React, { useState } from 'react';
|
|||||||
import { PayloadProps as DeleteAlertPayloadProps } from 'types/api/alerts/delete';
|
import { PayloadProps as DeleteAlertPayloadProps } from 'types/api/alerts/delete';
|
||||||
import { Alerts } from 'types/api/alerts/getAll';
|
import { Alerts } from 'types/api/alerts/getAll';
|
||||||
|
|
||||||
const DeleteAlert = ({
|
function DeleteAlert({
|
||||||
id,
|
id,
|
||||||
setData,
|
setData,
|
||||||
notifications,
|
notifications,
|
||||||
}: DeleteAlertProps): JSX.Element => {
|
}: DeleteAlertProps): JSX.Element {
|
||||||
const [deleteAlertState, setDeleteAlertState] = useState<
|
const [deleteAlertState, setDeleteAlertState] = useState<
|
||||||
State<DeleteAlertPayloadProps>
|
State<DeleteAlertPayloadProps>
|
||||||
>({
|
>({
|
||||||
@ -70,7 +70,6 @@ const DeleteAlert = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<Button
|
<Button
|
||||||
disabled={deleteAlertState.loading || false}
|
disabled={deleteAlertState.loading || false}
|
||||||
loading={deleteAlertState.loading || false}
|
loading={deleteAlertState.loading || false}
|
||||||
@ -79,9 +78,8 @@ const DeleteAlert = ({
|
|||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface DeleteAlertProps {
|
interface DeleteAlertProps {
|
||||||
id: Alerts['id'];
|
id: Alerts['id'];
|
||||||
|
@ -12,10 +12,10 @@ import { generatePath } from 'react-router';
|
|||||||
import { Alerts } from 'types/api/alerts/getAll';
|
import { Alerts } from 'types/api/alerts/getAll';
|
||||||
|
|
||||||
import DeleteAlert from './DeleteAlert';
|
import DeleteAlert from './DeleteAlert';
|
||||||
import { ButtonContainer, Button } from './styles';
|
import { Button, ButtonContainer } from './styles';
|
||||||
import Status from './TableComponents/Status';
|
import Status from './TableComponents/Status';
|
||||||
|
|
||||||
const ListAlert = ({ allAlertRules }: ListAlertProps): JSX.Element => {
|
function ListAlert({ allAlertRules }: ListAlertProps): JSX.Element {
|
||||||
const [data, setData] = useState<Alerts[]>(allAlertRules || []);
|
const [data, setData] = useState<Alerts[]>(allAlertRules || []);
|
||||||
|
|
||||||
useInterval(() => {
|
useInterval(() => {
|
||||||
@ -62,7 +62,7 @@ const ListAlert = ({ allAlertRules }: ListAlertProps): JSX.Element => {
|
|||||||
dataIndex: 'labels',
|
dataIndex: 'labels',
|
||||||
key: 'severity',
|
key: 'severity',
|
||||||
sorter: (a, b): number =>
|
sorter: (a, b): number =>
|
||||||
a.labels['severity'].length - b.labels['severity'].length,
|
a.labels.severity.length - b.labels.severity.length,
|
||||||
render: (value): JSX.Element => {
|
render: (value): JSX.Element => {
|
||||||
const objectKeys = Object.keys(value);
|
const objectKeys = Object.keys(value);
|
||||||
const withSeverityKey = objectKeys.find((e) => e === 'severity') || '';
|
const withSeverityKey = objectKeys.find((e) => e === 'severity') || '';
|
||||||
@ -144,7 +144,7 @@ const ListAlert = ({ allAlertRules }: ListAlertProps): JSX.Element => {
|
|||||||
<Table rowKey="id" columns={columns} dataSource={data} />
|
<Table rowKey="id" columns={columns} dataSource={data} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
interface ListAlertProps {
|
interface ListAlertProps {
|
||||||
allAlertRules: Alerts[];
|
allAlertRules: Alerts[];
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user