mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 18:45:57 +08:00
commit
9c8125ffc1
21
.github/workflows/build.yaml
vendored
21
.github/workflows/build.yaml
vendored
@ -27,6 +27,27 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
make build-frontend-amd64
|
make build-frontend-amd64
|
||||||
|
|
||||||
|
build-frontend-ee:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Create .env file
|
||||||
|
run: echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env
|
||||||
|
- name: Install dependencies
|
||||||
|
run: cd frontend && yarn install
|
||||||
|
- name: Run ESLint
|
||||||
|
run: cd frontend && npm run lint
|
||||||
|
- name: Run Jest
|
||||||
|
run: cd frontend && npm run jest
|
||||||
|
- name: TSC
|
||||||
|
run: yarn tsc
|
||||||
|
working-directory: ./frontend
|
||||||
|
- name: Build frontend docker image
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
make build-frontend-amd64
|
||||||
|
|
||||||
build-query-service:
|
build-query-service:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
46
.github/workflows/push.yaml
vendored
46
.github/workflows/push.yaml
vendored
@ -123,3 +123,49 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
- name: Build and push docker image
|
- name: Build and push docker image
|
||||||
run: make build-push-frontend
|
run: make build-push-frontend
|
||||||
|
|
||||||
|
image-build-and-push-frontend-ee:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Create .env file
|
||||||
|
run: echo 'INTERCOM_APP_ID="${{ secrets.INTERCOM_APP_ID }}"' > frontend/.env
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: frontend
|
||||||
|
run: yarn install
|
||||||
|
- name: Run Prettier
|
||||||
|
working-directory: frontend
|
||||||
|
run: npm run prettify
|
||||||
|
continue-on-error: true
|
||||||
|
- name: Run ESLint
|
||||||
|
working-directory: frontend
|
||||||
|
run: npm run lint
|
||||||
|
continue-on-error: true
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- uses: benjlevesque/short-sha@v2.2
|
||||||
|
id: short-sha
|
||||||
|
- name: Get branch name
|
||||||
|
id: branch-name
|
||||||
|
uses: tj-actions/branch-names@v5.1
|
||||||
|
- name: Set docker tag environment
|
||||||
|
run: |
|
||||||
|
if [ '${{ steps.branch-name.outputs.is_tag }}' == 'true' ]; then
|
||||||
|
tag="${{ steps.branch-name.outputs.tag }}"
|
||||||
|
tag="${tag:1}"
|
||||||
|
echo "DOCKER_TAG=${tag}-ee" >> $GITHUB_ENV
|
||||||
|
elif [ '${{ steps.branch-name.outputs.current_branch }}' == 'main' ]; then
|
||||||
|
echo "DOCKER_TAG=latest-ee" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "DOCKER_TAG=${{ steps.branch-name.outputs.current_branch }}-ee" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
- name: Build and push docker image
|
||||||
|
run: make build-push-frontend
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -37,7 +37,7 @@ frontend/src/constants/env.ts
|
|||||||
**/locust-scripts/__pycache__/
|
**/locust-scripts/__pycache__/
|
||||||
**/__debug_bin
|
**/__debug_bin
|
||||||
|
|
||||||
frontend/*.env
|
frontend/.env
|
||||||
pkg/query-service/signoz.db
|
pkg/query-service/signoz.db
|
||||||
|
|
||||||
pkg/query-service/tests/test-deploy/data/
|
pkg/query-service/tests/test-deploy/data/
|
||||||
|
1
Makefile
1
Makefile
@ -151,3 +151,4 @@ test:
|
|||||||
go test ./pkg/query-service/app/querier/...
|
go test ./pkg/query-service/app/querier/...
|
||||||
go test ./pkg/query-service/converter/...
|
go test ./pkg/query-service/converter/...
|
||||||
go test ./pkg/query-service/formatter/...
|
go test ./pkg/query-service/formatter/...
|
||||||
|
go test ./pkg/query-service/tests/integration/...
|
@ -131,7 +131,7 @@ services:
|
|||||||
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
# - ./data/clickhouse-3/:/var/lib/clickhouse/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:0.23.3
|
image: signoz/alertmanager:0.23.4
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
command:
|
command:
|
||||||
@ -144,8 +144,12 @@ services:
|
|||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.28.0
|
image: signoz/query-service:0.29.0
|
||||||
command: [ "-config=/root/config/prometheus.yml" ]
|
command:
|
||||||
|
[
|
||||||
|
"-config=/root/config/prometheus.yml",
|
||||||
|
"--prefer-delta=true"
|
||||||
|
]
|
||||||
# ports:
|
# ports:
|
||||||
# - "6060:6060" # pprof port
|
# - "6060:6060" # pprof port
|
||||||
# - "8080:8080" # query-service port
|
# - "8080:8080" # query-service port
|
||||||
@ -180,7 +184,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.28.0
|
image: signoz/frontend:0.29.0
|
||||||
deploy:
|
deploy:
|
||||||
restart_policy:
|
restart_policy:
|
||||||
condition: on-failure
|
condition: on-failure
|
||||||
@ -193,7 +197,7 @@ services:
|
|||||||
- ../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/signoz-otel-collector:0.79.6
|
image: signoz/signoz-otel-collector:0.79.7
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-config.yaml",
|
"--config=/etc/otel-collector-config.yaml",
|
||||||
@ -226,7 +230,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/signoz-otel-collector:0.79.6
|
image: signoz/signoz-otel-collector:0.79.7
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||||
|
@ -34,7 +34,7 @@ services:
|
|||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
container_name: signoz-alertmanager
|
container_name: signoz-alertmanager
|
||||||
image: signoz/alertmanager:0.23.3
|
image: signoz/alertmanager:0.23.4
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -48,7 +48,7 @@ services:
|
|||||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||||
otel-collector:
|
otel-collector:
|
||||||
container_name: signoz-otel-collector
|
container_name: signoz-otel-collector
|
||||||
image: signoz/signoz-otel-collector:0.79.6
|
image: signoz/signoz-otel-collector:0.79.7
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-config.yaml",
|
"--config=/etc/otel-collector-config.yaml",
|
||||||
@ -78,7 +78,7 @@ services:
|
|||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
container_name: signoz-otel-collector-metrics
|
container_name: signoz-otel-collector-metrics
|
||||||
image: signoz/signoz-otel-collector:0.79.6
|
image: signoz/signoz-otel-collector:0.79.7
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||||
|
@ -22,7 +22,11 @@ services:
|
|||||||
- ./prometheus.yml:/root/config/prometheus.yml
|
- ./prometheus.yml:/root/config/prometheus.yml
|
||||||
- ../dashboards:/root/config/dashboards
|
- ../dashboards:/root/config/dashboards
|
||||||
- ./data/signoz/:/var/lib/signoz/
|
- ./data/signoz/:/var/lib/signoz/
|
||||||
command: [ "-config=/root/config/prometheus.yml" ]
|
command:
|
||||||
|
[
|
||||||
|
"-config=/root/config/prometheus.yml",
|
||||||
|
"--prefer-delta=true"
|
||||||
|
]
|
||||||
ports:
|
ports:
|
||||||
- "6060:6060"
|
- "6060:6060"
|
||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
|
@ -147,7 +147,7 @@ services:
|
|||||||
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
# - ./user_scripts:/var/lib/clickhouse/user_scripts/
|
||||||
|
|
||||||
alertmanager:
|
alertmanager:
|
||||||
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.3}
|
image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.4}
|
||||||
container_name: signoz-alertmanager
|
container_name: signoz-alertmanager
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/alertmanager:/data
|
- ./data/alertmanager:/data
|
||||||
@ -162,9 +162,13 @@ services:
|
|||||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:${DOCKER_TAG:-0.28.0}
|
image: signoz/query-service:${DOCKER_TAG:-0.29.0}
|
||||||
container_name: signoz-query-service
|
container_name: signoz-query-service
|
||||||
command: [ "-config=/root/config/prometheus.yml" ]
|
command:
|
||||||
|
[
|
||||||
|
"-config=/root/config/prometheus.yml",
|
||||||
|
"--prefer-delta=true"
|
||||||
|
]
|
||||||
# ports:
|
# ports:
|
||||||
# - "6060:6060" # pprof port
|
# - "6060:6060" # pprof port
|
||||||
# - "8080:8080" # query-service port
|
# - "8080:8080" # query-service port
|
||||||
@ -197,7 +201,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:${DOCKER_TAG:-0.28.0}
|
image: signoz/frontend:${DOCKER_TAG:-0.29.0}
|
||||||
container_name: signoz-frontend
|
container_name: signoz-frontend
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -209,7 +213,7 @@ services:
|
|||||||
- ../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/signoz-otel-collector:${OTELCOL_TAG:-0.79.6}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.7}
|
||||||
container_name: signoz-otel-collector
|
container_name: signoz-otel-collector
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
@ -240,7 +244,7 @@ services:
|
|||||||
<<: *clickhouse-depend
|
<<: *clickhouse-depend
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.6}
|
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.79.7}
|
||||||
container_name: signoz-otel-collector-metrics
|
container_name: signoz-otel-collector-metrics
|
||||||
command:
|
command:
|
||||||
[
|
[
|
||||||
|
@ -22,7 +22,7 @@ RUN cd ee/query-service \
|
|||||||
|
|
||||||
|
|
||||||
# use a minimal alpine image
|
# use a minimal alpine image
|
||||||
FROM alpine:3.7
|
FROM alpine:3.16.7
|
||||||
|
|
||||||
# Add Maintainer Info
|
# Add Maintainer Info
|
||||||
LABEL maintainer="signoz"
|
LABEL maintainer="signoz"
|
||||||
|
@ -107,7 +107,7 @@ func (ah *APIHandler) registerUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
RespondError(w, model.InternalError(basemodel.ErrSignupFailed{}), nil)
|
RespondError(w, model.InternalError(basemodel.ErrSignupFailed{}), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
precheckResp := &model.PrecheckResponse{
|
precheckResp := &basemodel.PrecheckResponse{
|
||||||
SSO: false,
|
SSO: false,
|
||||||
IsUser: false,
|
IsUser: false,
|
||||||
}
|
}
|
||||||
|
@ -137,8 +137,8 @@ func (ah *APIHandler) queryRangeMetricsV2(w http.ResponseWriter, r *http.Request
|
|||||||
var s basemodel.Series
|
var s basemodel.Series
|
||||||
s.QueryName = name
|
s.QueryName = name
|
||||||
s.Labels = v.Metric.Copy().Map()
|
s.Labels = v.Metric.Copy().Map()
|
||||||
for _, p := range v.Points {
|
for _, p := range v.Floats {
|
||||||
s.Points = append(s.Points, basemodel.MetricPoint{Timestamp: p.T, Value: p.V})
|
s.Points = append(s.Points, basemodel.MetricPoint{Timestamp: p.T, Value: p.F})
|
||||||
}
|
}
|
||||||
seriesList = append(seriesList, &s)
|
seriesList = append(seriesList, &s)
|
||||||
}
|
}
|
||||||
|
@ -548,7 +548,7 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
zap.S().Info("Starting OpAmp Websocket server", zap.String("addr", baseconst.OpAmpWsEndpoint))
|
zap.S().Info("Starting OpAmp Websocket server", zap.String("addr", baseconst.OpAmpWsEndpoint))
|
||||||
err := opamp.InitalizeServer(baseconst.OpAmpWsEndpoint, &opAmpModel.AllAgents)
|
err := opamp.InitializeAndStartServer(baseconst.OpAmpWsEndpoint, &opAmpModel.AllAgents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zap.S().Info("opamp ws server failed to start", err)
|
zap.S().Info("opamp ws server failed to start", err)
|
||||||
s.unavailableChannel <- healthcheck.Unavailable
|
s.unavailableChannel <- healthcheck.Unavailable
|
||||||
|
@ -21,7 +21,6 @@ type ModelDao interface {
|
|||||||
DB() *sqlx.DB
|
DB() *sqlx.DB
|
||||||
|
|
||||||
// auth methods
|
// auth methods
|
||||||
PrecheckLogin(ctx context.Context, email, sourceUrl string) (*model.PrecheckResponse, basemodel.BaseApiError)
|
|
||||||
CanUsePassword(ctx context.Context, email string) (bool, basemodel.BaseApiError)
|
CanUsePassword(ctx context.Context, email string) (bool, basemodel.BaseApiError)
|
||||||
PrepareSsoRedirect(ctx context.Context, redirectUri, email string) (redirectURL string, apierr basemodel.BaseApiError)
|
PrepareSsoRedirect(ctx context.Context, redirectUri, email string) (redirectURL string, apierr basemodel.BaseApiError)
|
||||||
GetDomainFromSsoResponse(ctx context.Context, relayState *url.URL) (*model.OrgDomain, error)
|
GetDomainFromSsoResponse(ctx context.Context, relayState *url.URL) (*model.OrgDomain, error)
|
||||||
|
@ -120,10 +120,10 @@ func (m *modelDao) CanUsePassword(ctx context.Context, email string) (bool, base
|
|||||||
|
|
||||||
// PrecheckLogin is called when the login or signup page is loaded
|
// PrecheckLogin is called when the login or signup page is loaded
|
||||||
// to check sso login is to be prompted
|
// to check sso login is to be prompted
|
||||||
func (m *modelDao) PrecheckLogin(ctx context.Context, email, sourceUrl string) (*model.PrecheckResponse, basemodel.BaseApiError) {
|
func (m *modelDao) PrecheckLogin(ctx context.Context, email, sourceUrl string) (*basemodel.PrecheckResponse, basemodel.BaseApiError) {
|
||||||
|
|
||||||
// assume user is valid unless proven otherwise
|
// assume user is valid unless proven otherwise
|
||||||
resp := &model.PrecheckResponse{IsUser: true, CanSelfRegister: false}
|
resp := &basemodel.PrecheckResponse{IsUser: true, CanSelfRegister: false}
|
||||||
|
|
||||||
// check if email is a valid user
|
// check if email is a valid user
|
||||||
userPayload, baseApiErr := m.GetUserByEmail(ctx, email)
|
userPayload, baseApiErr := m.GetUserByEmail(ctx, email)
|
||||||
|
@ -4,18 +4,9 @@ import (
|
|||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrecheckResponse contains login precheck response
|
|
||||||
type PrecheckResponse struct {
|
|
||||||
SSO bool `json:"sso"`
|
|
||||||
SsoUrl string `json:"ssoUrl"`
|
|
||||||
CanSelfRegister bool `json:"canSelfRegister"`
|
|
||||||
IsUser bool `json:"isUser"`
|
|
||||||
SsoError string `json:"ssoError"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GettableInvitation overrides base object and adds precheck into
|
// GettableInvitation overrides base object and adds precheck into
|
||||||
// response
|
// response
|
||||||
type GettableInvitation struct {
|
type GettableInvitation struct {
|
||||||
*basemodel.InvitationResponseObject
|
*basemodel.InvitationResponseObject
|
||||||
Precheck *PrecheckResponse `json:"precheck"`
|
Precheck *basemodel.PrecheckResponse `json:"precheck"`
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ const Basic = "BASIC_PLAN"
|
|||||||
const Pro = "PRO_PLAN"
|
const Pro = "PRO_PLAN"
|
||||||
const Enterprise = "ENTERPRISE_PLAN"
|
const Enterprise = "ENTERPRISE_PLAN"
|
||||||
const DisableUpsell = "DISABLE_UPSELL"
|
const DisableUpsell = "DISABLE_UPSELL"
|
||||||
|
const Onboarding = "ONBOARDING"
|
||||||
|
const ChatSupport = "CHAT_SUPPORT"
|
||||||
|
|
||||||
var BasicPlan = basemodel.FeatureSet{
|
var BasicPlan = basemodel.FeatureSet{
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
@ -276,4 +278,18 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: Onboarding,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
|
basemodel.Feature{
|
||||||
|
Name: ChatSupport,
|
||||||
|
Active: true,
|
||||||
|
Usage: 0,
|
||||||
|
UsageLimit: -1,
|
||||||
|
Route: "",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.vscode
|
.vscode
|
||||||
build
|
build
|
||||||
.env
|
|
||||||
.git
|
.git
|
||||||
|
7
frontend/example.env
Normal file
7
frontend/example.env
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
NODE_ENV="development"
|
||||||
|
BUNDLE_ANALYSER="true"
|
||||||
|
FRONTEND_API_ENDPOINT="http://localhost:3301/"
|
||||||
|
INTERCOM_APP_ID="intercom-app-id"
|
||||||
|
|
||||||
|
PLAYWRIGHT_TEST_BASE_URL="http://localhost:3301"
|
||||||
|
CI="1"
|
@ -29,27 +29,31 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "6.0.0",
|
"@ant-design/colors": "6.0.0",
|
||||||
"@ant-design/icons": "4.8.0",
|
"@ant-design/icons": "4.8.0",
|
||||||
"@grafana/data": "^8.4.3",
|
"@grafana/data": "^9.5.2",
|
||||||
|
"@mdx-js/loader": "2.3.0",
|
||||||
|
"@mdx-js/react": "2.3.0",
|
||||||
"@monaco-editor/react": "^4.3.1",
|
"@monaco-editor/react": "^4.3.1",
|
||||||
|
"@uiw/react-md-editor": "3.23.5",
|
||||||
"@xstate/react": "^3.0.0",
|
"@xstate/react": "^3.0.0",
|
||||||
"ansi-to-html": "0.7.2",
|
"ansi-to-html": "0.7.2",
|
||||||
"antd": "5.0.5",
|
"antd": "5.0.5",
|
||||||
"antd-table-saveas-excel": "2.2.1",
|
"antd-table-saveas-excel": "2.2.1",
|
||||||
"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": "^29.6.4",
|
||||||
"babel-loader": "8.1.0",
|
"babel-loader": "9.1.3",
|
||||||
"babel-plugin-named-asset-import": "^0.3.7",
|
"babel-plugin-named-asset-import": "^0.3.7",
|
||||||
"babel-preset-minify": "^0.5.1",
|
"babel-preset-minify": "^0.5.1",
|
||||||
"babel-preset-react-app": "^10.0.0",
|
"babel-preset-react-app": "^10.0.1",
|
||||||
"chart.js": "3.9.1",
|
"chart.js": "3.9.1",
|
||||||
"chartjs-adapter-date-fns": "^2.0.0",
|
"chartjs-adapter-date-fns": "^2.0.0",
|
||||||
"chartjs-plugin-annotation": "^1.4.0",
|
"chartjs-plugin-annotation": "^1.4.0",
|
||||||
|
"classnames": "2.3.2",
|
||||||
"color": "^4.2.1",
|
"color": "^4.2.1",
|
||||||
"color-alpha": "1.1.3",
|
"color-alpha": "1.1.3",
|
||||||
"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": "5.0.1",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"dompurify": "3.0.0",
|
"dompurify": "3.0.0",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
@ -58,7 +62,7 @@
|
|||||||
"file-loader": "6.1.1",
|
"file-loader": "6.1.1",
|
||||||
"fontfaceobserver": "2.3.0",
|
"fontfaceobserver": "2.3.0",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"html-webpack-plugin": "5.1.0",
|
"html-webpack-plugin": "5.5.0",
|
||||||
"i18next": "^21.6.12",
|
"i18next": "^21.6.12",
|
||||||
"i18next-browser-languagedetector": "^6.1.3",
|
"i18next-browser-languagedetector": "^6.1.3",
|
||||||
"i18next-http-backend": "^1.3.2",
|
"i18next-http-backend": "^1.3.2",
|
||||||
@ -75,7 +79,7 @@
|
|||||||
"react-dnd-html5-backend": "16.0.1",
|
"react-dnd-html5-backend": "16.0.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-drag-listview": "2.0.0",
|
"react-drag-listview": "2.0.0",
|
||||||
"react-force-graph": "^1.41.0",
|
"react-force-graph": "^1.43.0",
|
||||||
"react-grid-layout": "^1.3.4",
|
"react-grid-layout": "^1.3.4",
|
||||||
"react-helmet-async": "1.3.0",
|
"react-helmet-async": "1.3.0",
|
||||||
"react-i18next": "^11.16.1",
|
"react-i18next": "^11.16.1",
|
||||||
@ -89,7 +93,7 @@
|
|||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"stream": "^0.0.2",
|
"stream": "^0.0.2",
|
||||||
"style-loader": "1.3.0",
|
"style-loader": "1.3.0",
|
||||||
"styled-components": "^5.2.1",
|
"styled-components": "^5.3.11",
|
||||||
"terser-webpack-plugin": "^5.2.5",
|
"terser-webpack-plugin": "^5.2.5",
|
||||||
"timestamp-nano": "^1.0.0",
|
"timestamp-nano": "^1.0.0",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
@ -97,8 +101,8 @@
|
|||||||
"typescript": "^4.0.5",
|
"typescript": "^4.0.5",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"web-vitals": "^0.2.4",
|
"web-vitals": "^0.2.4",
|
||||||
"webpack": "^5.23.0",
|
"webpack": "5.88.2",
|
||||||
"webpack-dev-server": "^4.3.1",
|
"webpack-dev-server": "^4.15.1",
|
||||||
"xstate": "^4.31.0"
|
"xstate": "^4.31.0"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
@ -114,13 +118,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.3",
|
"@babel/core": "^7.22.11",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.12.13",
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||||
"@babel/plugin-syntax-jsx": "^7.12.13",
|
"@babel/plugin-syntax-jsx": "^7.12.13",
|
||||||
"@babel/preset-env": "^7.12.17",
|
"@babel/preset-env": "^7.22.14",
|
||||||
"@babel/preset-react": "^7.12.13",
|
"@babel/preset-react": "^7.12.13",
|
||||||
"@babel/preset-typescript": "^7.12.17",
|
"@babel/preset-typescript": "^7.21.4",
|
||||||
"@commitlint/cli": "^16.2.4",
|
"@commitlint/cli": "^16.3.0",
|
||||||
"@commitlint/config-conventional": "^16.2.4",
|
"@commitlint/config-conventional": "^16.2.4",
|
||||||
"@jest/globals": "^27.5.1",
|
"@jest/globals": "^27.5.1",
|
||||||
"@playwright/test": "^1.22.0",
|
"@playwright/test": "^1.22.0",
|
||||||
@ -149,22 +153,21 @@
|
|||||||
"@types/styled-components": "^5.1.4",
|
"@types/styled-components": "^5.1.4",
|
||||||
"@types/uuid": "^8.3.1",
|
"@types/uuid": "^8.3.1",
|
||||||
"@types/webpack": "^5.28.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"@types/webpack-dev-server": "^4.3.0",
|
"@types/webpack-dev-server": "^4.7.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||||
"@typescript-eslint/parser": "^4.28.2",
|
"@typescript-eslint/parser": "^4.33.0",
|
||||||
"@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",
|
||||||
"eslint": "^7.30.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"eslint-config-airbnb-typescript": "^16.1.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.25.4",
|
"eslint-plugin-import": "^2.28.1",
|
||||||
"eslint-plugin-jest": "^26.1.2",
|
"eslint-plugin-jest": "^26.9.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
"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",
|
||||||
@ -175,15 +178,17 @@
|
|||||||
"eslint-plugin-sonarjs": "^0.12.0",
|
"eslint-plugin-sonarjs": "^0.12.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"is-ci": "^3.0.1",
|
"is-ci": "^3.0.1",
|
||||||
"jest-playwright-preset": "^1.7.0",
|
"jest-playwright-preset": "^1.7.2",
|
||||||
"jest-styled-components": "^7.0.8",
|
"jest-styled-components": "^7.0.8",
|
||||||
"lint-staged": "^12.3.7",
|
"lint-staged": "^12.5.0",
|
||||||
"portfinder-sync": "^0.0.2",
|
"portfinder-sync": "^0.0.2",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.2.1",
|
||||||
"react-hooks-testing-library": "0.6.0",
|
"react-hooks-testing-library": "0.6.0",
|
||||||
"react-hot-loader": "^4.13.0",
|
"react-hot-loader": "^4.13.0",
|
||||||
"react-resizable": "3.0.4",
|
"react-resizable": "3.0.4",
|
||||||
"ts-jest": "^27.1.4",
|
"sass": "1.66.1",
|
||||||
|
"sass-loader": "13.3.2",
|
||||||
|
"ts-jest": "^27.1.5",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"typescript-plugin-css-modules": "5.0.1",
|
"typescript-plugin-css-modules": "5.0.1",
|
||||||
"webpack-bundle-analyzer": "^4.5.0",
|
"webpack-bundle-analyzer": "^4.5.0",
|
||||||
@ -196,6 +201,9 @@
|
|||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "18.0.26",
|
"@types/react": "18.0.26",
|
||||||
"@types/react-dom": "18.0.10"
|
"@types/react-dom": "18.0.10",
|
||||||
|
"debug": "4.3.4",
|
||||||
|
"semver": "7.5.4",
|
||||||
|
"xml2js": "0.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
frontend/public/Logos/cmd-terminal.svg
Normal file
1
frontend/public/Logos/cmd-terminal.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 103.53" style="enable-background:new 0 0 122.88 103.53" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;} .st0{fill:#1668dc;} .st1{fill:#FFFFFF;}</style><g><path class="st0" d="M5.47,0h111.93c3.01,0,5.47,2.46,5.47,5.47v92.58c0,3.01-2.46,5.47-5.47,5.47H5.47 c-3.01,0-5.47-2.46-5.47-5.47V5.47C0,2.46,2.46,0,5.47,0L5.47,0z M31.84,38.55l17.79,18.42l2.14,2.13l-2.12,2.16L31.68,80.31 l-5.07-5l15.85-16.15L26.81,43.6L31.84,38.55L31.84,38.55z M94.1,79.41H54.69v-6.84H94.1V79.41L94.1,79.41z M38.19,9.83 c3.19,0,5.78,2.59,5.78,5.78s-2.59,5.78-5.78,5.78c-3.19,0-5.78-2.59-5.78-5.78S35,9.83,38.19,9.83L38.19,9.83z M18.95,9.83 c3.19,0,5.78,2.59,5.78,5.78s-2.59,5.78-5.78,5.78c-3.19,0-5.78-2.59-5.78-5.78S15.75,9.83,18.95,9.83L18.95,9.83z M7.49,5.41 h107.91c1.15,0,2.09,0.94,2.09,2.09v18.32H5.4V7.5C5.4,6.35,6.34,5.41,7.49,5.41L7.49,5.41z"/></g></svg>
|
After Width: | Height: | Size: 1.0 KiB |
1
frontend/public/Logos/docker.svg
Normal file
1
frontend/public/Logos/docker.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 88.17" style="enable-background:new 0 0 122.88 88.17" xml:space="preserve"><style type="text/css">.st0{fill:#0091E2;}</style><g><path class="st0" d="M121.68,33.34c-0.34-0.28-3.42-2.62-10.03-2.62c-1.71,0-3.48,0.17-5.19,0.46c-1.25-8.72-8.49-12.94-8.78-13.16 l-1.77-1.03l-1.14,1.65c-1.42,2.22-2.51,4.73-3.13,7.29c-1.2,4.96-0.46,9.63,2.05,13.62c-3.02,1.71-7.92,2.11-8.95,2.17l-80.93,0 c-2.11,0-3.82,1.71-3.82,3.82c-0.11,7.07,1.08,14.13,3.53,20.8c2.79,7.29,6.95,12.71,12.31,16.01c6.04,3.7,15.9,5.81,27.01,5.81 c5.01,0,10.03-0.46,14.99-1.37c6.9-1.25,13.51-3.65,19.6-7.12c5.02-2.91,9.52-6.61,13.34-10.94c6.44-7.24,10.26-15.33,13.05-22.51 c0.4,0,0.74,0,1.14,0c7.01,0,11.34-2.79,13.73-5.19c1.6-1.48,2.79-3.31,3.65-5.36l0.51-1.48L121.68,33.34L121.68,33.34z M71.59,39.38h10.83c0.51,0,0.97-0.4,0.97-0.97v-9.69c0-0.51-0.4-0.97-0.97-0.97l0,0l-10.83,0c-0.51,0-0.97,0.4-0.97,0.97l0,0v9.69 C70.68,38.98,71.08,39.38,71.59,39.38L71.59,39.38z M56.49,11.63h10.83c0.51,0,0.97-0.4,0.97-0.97V0.97c0-0.51-0.46-0.97-0.97-0.97 L56.49,0c-0.51,0-0.97,0.4-0.97,0.97l0,0v9.69C55.52,11.17,55.97,11.63,56.49,11.63L56.49,11.63z M56.49,25.53h10.83 c0.51,0,0.97-0.46,0.97-0.97v-9.69c0-0.51-0.46-0.97-0.97-0.97H56.49c-0.51,0-0.97,0.4-0.97,0.97l0,0v9.69 C55.52,25.08,55.97,25.53,56.49,25.53L56.49,25.53z M41.5,25.53h10.83c0.51,0,0.97-0.46,0.97-0.97v-9.69c0-0.51-0.4-0.97-0.97-0.97 l0,0H41.5c-0.51,0-0.97,0.4-0.97,0.97l0,0v9.69C40.53,25.08,40.93,25.53,41.5,25.53L41.5,25.53z M26.28,25.53h10.83 c0.51,0,0.97-0.46,0.97-0.97v-9.69c0-0.51-0.4-0.97-0.97-0.97l0,0H26.28c-0.51,0-0.97,0.4-0.97,0.97v9.69 C25.37,25.08,25.77,25.53,26.28,25.53L26.28,25.53z M56.49,39.38h10.83c0.51,0,0.97-0.4,0.97-0.97v-9.69c0-0.51-0.4-0.97-0.97-0.97 l0,0l-10.83,0c-0.51,0-0.97,0.4-0.97,0.97l0,0v9.69C55.52,38.98,55.97,39.38,56.49,39.38L56.49,39.38L56.49,39.38z M41.5,39.38 h10.83c0.51,0,0.97-0.4,0.97-0.97l0,0v-9.69c0-0.51-0.4-0.97-0.97-0.97l0,0l-10.83,0c-0.51,0-0.97,0.4-0.97,0.97l0,0v9.69 C40.53,38.98,40.93,39.38,41.5,39.38L41.5,39.38L41.5,39.38z M26.28,39.38h10.83c0.51,0,0.97-0.4,0.97-0.97l0,0v-9.69 c0-0.51-0.4-0.97-0.97-0.97l0,0l-10.83,0c-0.51,0-0.97,0.4-0.97,0.97v9.69C25.37,38.98,25.77,39.38,26.28,39.38L26.28,39.38z M11.35,39.38h10.83c0.51,0,0.97-0.4,0.97-0.97l0,0v-9.69c0-0.51-0.4-0.97-0.97-0.97l0,0l-10.83,0c-0.51,0-0.97,0.4-0.97,0.97l0,0 v9.69C10.44,38.98,10.84,39.38,11.35,39.38L11.35,39.38L11.35,39.38z"/></g></svg>
|
After Width: | Height: | Size: 2.5 KiB |
1
frontend/public/Logos/kubernetes.svg
Normal file
1
frontend/public/Logos/kubernetes.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.9 KiB |
1
frontend/public/Logos/node-js.svg
Normal file
1
frontend/public/Logos/node-js.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 109 122.88" style="enable-background:new 0 0 109 122.88" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#689F63;}</style><g><path class="st0" d="M68.43,87.08c-19.7,0-23.83-9.04-23.83-16.63c0-0.72,0.58-1.3,1.3-1.3h5.82c0.64,0,1.18,0.47,1.28,1.1 c0.88,5.93,3.49,8.92,15.41,8.92c9.49,0,13.52-2.14,13.52-7.18c0-2.9-1.15-5.05-15.89-6.49c-12.33-1.22-19.95-3.93-19.95-13.8 c0-9.08,7.66-14.49,20.5-14.49c14.42,0,21.56,5,22.46,15.76c0.03,0.37-0.1,0.73-0.35,1c-0.25,0.26-0.6,0.42-0.96,0.42H81.9 c-0.61,0-1.14-0.43-1.26-1.01c-1.41-6.23-4.81-8.23-14.07-8.23c-10.36,0-11.56,3.61-11.56,6.31c0,3.28,1.42,4.24,15.4,6.09 c13.84,1.84,20.41,4.43,20.41,14.16c0,9.81-8.18,15.43-22.45,15.43L68.43,87.08L68.43,87.08z M54.52,122.88 c-1.65,0-3.28-0.43-4.72-1.26l-15.03-8.9c-2.25-1.26-1.15-1.7-0.41-1.96c2.99-1.05,3.6-1.28,6.8-3.1c0.34-0.19,0.78-0.12,1.12,0.08 l11.55,6.85c0.42,0.23,1.01,0.23,1.4,0l45.03-25.99c0.42-0.24,0.69-0.72,0.69-1.22V35.43c0-0.52-0.27-0.98-0.7-1.24L55.23,8.22 c-0.42-0.25-0.97-0.25-1.39,0l-45,25.97c-0.44,0.25-0.71,0.73-0.71,1.23v51.96c0,0.5,0.27,0.97,0.7,1.21l12.33,7.12 c6.69,3.35,10.79-0.6,10.79-4.56V39.86c0-0.73,0.57-1.3,1.31-1.3l5.7,0c0.71,0,1.3,0.56,1.3,1.3v51.31 c0,8.93-4.87,14.05-13.33,14.05c-2.6,0-4.66,0-10.38-2.82L4.72,95.59C1.8,93.9,0,90.75,0,87.38V35.42c0-3.38,1.8-6.54,4.72-8.21 l45.07-26c2.85-1.61,6.64-1.61,9.47,0l45.02,26.01c2.91,1.68,4.72,4.82,4.72,8.21v51.96c0,3.37-1.81,6.51-4.72,8.21l-45.02,26 c-1.44,0.83-3.08,1.26-4.74,1.26L54.52,122.88L54.52,122.88z M54.52,122.88L54.52,122.88L54.52,122.88L54.52,122.88z"/></g></svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
frontend/public/Logos/software-window.svg
Normal file
1
frontend/public/Logos/software-window.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 98.18" style="enable-background:new 0 0 122.88 98.18" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;} .st0{fill:#1668dc;} .st1{fill:#FFFFFF;}</style><g><path class="st0" d="M3.42,0h116.05c1.88,0,3.41,1.54,3.41,3.41v91.36c0,1.88-1.54,3.41-3.41,3.41l-116.05,0 C1.54,98.18,0,96.65,0,94.77L0,3.41C0,1.53,1.54,0,3.42,0L3.42,0L3.42,0z M25.89,8.19c2.05,0,3.72,1.67,3.72,3.72 c0,2.05-1.67,3.72-3.72,3.72c-2.05,0-3.72-1.67-3.72-3.72C22.17,9.85,23.83,8.19,25.89,8.19L25.89,8.19z M103.07,7.69l2.52,2.77 l2.52-2.77l1.97,1.79l-2.69,2.96l2.69,2.96l-1.97,1.79l-2.52-2.77l-2.52,2.77l-1.97-1.79l2.69-2.96l-2.69-2.96L103.07,7.69 L103.07,7.69z M14.52,8.19c2.05,0,3.72,1.67,3.72,3.72c0,2.05-1.67,3.72-3.72,3.72c-2.05,0-3.72-1.67-3.72-3.72 C10.79,9.85,12.46,8.19,14.52,8.19L14.52,8.19z M37.26,8.19c2.05,0,3.72,1.67,3.72,3.72c0,2.05-1.67,3.72-3.72,3.72 c-2.05,0-3.72-1.67-3.72-3.72C33.54,9.85,35.21,8.19,37.26,8.19L37.26,8.19z M14.05,22.75h93.33c1.77,0,3.22,1.49,3.22,3.22v59.2 c0,1.73-1.49,3.22-3.22,3.22l-93.33,0c-1.73,0-3.22-1.45-3.22-3.22v-59.2C10.84,24.2,12.29,22.75,14.05,22.75L14.05,22.75 L14.05,22.75z"/></g></svg>
|
After Width: | Height: | Size: 1.3 KiB |
9
frontend/public/Logos/syslogs.svg
Normal file
9
frontend/public/Logos/syslogs.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.2 KiB |
3
frontend/public/locales/en-GB/explorer.json
Normal file
3
frontend/public/locales/en-GB/explorer.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"name_of_the_view": "Name of the view"
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
"label_orgname": "Organization Name",
|
"label_orgname": "Organization Name",
|
||||||
"placeholder_orgname": "Your Company",
|
"placeholder_orgname": "Your Company",
|
||||||
"prompt_keepme_posted": "Keep me updated on new SigNoz features",
|
"prompt_keepme_posted": "Keep me updated on new SigNoz features",
|
||||||
"prompt_anonymise": "Anonymise my usage date. We collect data to measure product usage",
|
"prompt_anonymise": "Anonymise my usage data. We collect data to measure product usage",
|
||||||
"failed_confirm_password": "Passwords don’t match. Please try again",
|
"failed_confirm_password": "Passwords don’t match. Please try again",
|
||||||
"unexpected_error": "Something went wrong",
|
"unexpected_error": "Something went wrong",
|
||||||
"failed_to_initiate_login": "Signup completed but failed to initiate login",
|
"failed_to_initiate_login": "Signup completed but failed to initiate login",
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"SIGN_UP": "SigNoz | Sign Up",
|
"SIGN_UP": "SigNoz | Sign Up",
|
||||||
"LOGIN": "SigNoz | Login",
|
"LOGIN": "SigNoz | Login",
|
||||||
|
"GET_STARTED": "SigNoz | Get Started",
|
||||||
"SERVICE_METRICS": "SigNoz | Service Metrics",
|
"SERVICE_METRICS": "SigNoz | Service Metrics",
|
||||||
"SERVICE_MAP": "SigNoz | Service Map",
|
"SERVICE_MAP": "SigNoz | Service Map",
|
||||||
"TRACE": "SigNoz | Trace",
|
"TRACE": "SigNoz | Trace",
|
||||||
"TRACE_DETAIL": "SigNoz | Trace Detail",
|
"TRACE_DETAIL": "SigNoz | Trace Detail",
|
||||||
"TRACES_EXPLORER": "SigNoz | Traces Explorer",
|
"TRACES_EXPLORER": "SigNoz | Traces Explorer",
|
||||||
"SETTINGS": "SigNoz | Settings",
|
"SETTINGS": "SigNoz | Settings",
|
||||||
"INSTRUMENTATION": "SigNoz | Get Started",
|
|
||||||
"USAGE_EXPLORER": "SigNoz | Usage Explorer",
|
"USAGE_EXPLORER": "SigNoz | Usage Explorer",
|
||||||
"APPLICATION": "SigNoz | Home",
|
"APPLICATION": "SigNoz | Home",
|
||||||
"ALL_DASHBOARD": "SigNoz | All Dashboards",
|
"ALL_DASHBOARD": "SigNoz | All Dashboards",
|
||||||
|
3
frontend/public/locales/en/explorer.json
Normal file
3
frontend/public/locales/en/explorer.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"name_of_the_view": "Name of the view"
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
"label_orgname": "Organization Name",
|
"label_orgname": "Organization Name",
|
||||||
"placeholder_orgname": "Your Company",
|
"placeholder_orgname": "Your Company",
|
||||||
"prompt_keepme_posted": "Keep me updated on new SigNoz features",
|
"prompt_keepme_posted": "Keep me updated on new SigNoz features",
|
||||||
"prompt_anonymise": "Anonymise my usage date. We collect data to measure product usage",
|
"prompt_anonymise": "Anonymise my usage data. We collect data to measure product usage",
|
||||||
"failed_confirm_password": "Passwords don’t match. Please try again",
|
"failed_confirm_password": "Passwords don’t match. Please try again",
|
||||||
"unexpected_error": "Something went wrong",
|
"unexpected_error": "Something went wrong",
|
||||||
"failed_to_initiate_login": "Signup completed but failed to initiate login",
|
"failed_to_initiate_login": "Signup completed but failed to initiate login",
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
"LOGIN": "SigNoz | Login",
|
"LOGIN": "SigNoz | Login",
|
||||||
"SERVICE_METRICS": "SigNoz | Service Metrics",
|
"SERVICE_METRICS": "SigNoz | Service Metrics",
|
||||||
"SERVICE_MAP": "SigNoz | Service Map",
|
"SERVICE_MAP": "SigNoz | Service Map",
|
||||||
|
"GET_STARTED": "SigNoz | Get Started",
|
||||||
"TRACE": "SigNoz | Trace",
|
"TRACE": "SigNoz | Trace",
|
||||||
"TRACE_DETAIL": "SigNoz | Trace Detail",
|
"TRACE_DETAIL": "SigNoz | Trace Detail",
|
||||||
"TRACES_EXPLORER": "SigNoz | Traces Explorer",
|
"TRACES_EXPLORER": "SigNoz | Traces Explorer",
|
||||||
"SETTINGS": "SigNoz | Settings",
|
"SETTINGS": "SigNoz | Settings",
|
||||||
"INSTRUMENTATION": "SigNoz | Get Started",
|
|
||||||
"USAGE_EXPLORER": "SigNoz | Usage Explorer",
|
"USAGE_EXPLORER": "SigNoz | Usage Explorer",
|
||||||
"APPLICATION": "SigNoz | Home",
|
"APPLICATION": "SigNoz | Home",
|
||||||
"ALL_DASHBOARD": "SigNoz | All Dashboards",
|
"ALL_DASHBOARD": "SigNoz | All Dashboards",
|
||||||
|
@ -1,20 +1,77 @@
|
|||||||
import { ConfigProvider } from 'antd';
|
import { ConfigProvider } from 'antd';
|
||||||
import NotFound from 'components/NotFound';
|
import NotFound from 'components/NotFound';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import AppLayout from 'container/AppLayout';
|
import AppLayout from 'container/AppLayout';
|
||||||
import { useThemeConfig } from 'hooks/useDarkMode';
|
import { useThemeConfig } from 'hooks/useDarkMode';
|
||||||
|
import useGetFeatureFlag from 'hooks/useGetFeatureFlag';
|
||||||
import { NotificationProvider } from 'hooks/useNotifications';
|
import { NotificationProvider } from 'hooks/useNotifications';
|
||||||
import { ResourceProvider } from 'hooks/useResourceAttribute';
|
import { ResourceProvider } from 'hooks/useResourceAttribute';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { QueryBuilderProvider } from 'providers/QueryBuilder';
|
import { QueryBuilderProvider } from 'providers/QueryBuilder';
|
||||||
import { Suspense } from 'react';
|
import { Suspense, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Route, Router, Switch } from 'react-router-dom';
|
import { Route, Router, Switch } from 'react-router-dom';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppActions from 'types/actions';
|
||||||
|
import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app';
|
||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import PrivateRoute from './Private';
|
import PrivateRoute from './Private';
|
||||||
import routes from './routes';
|
import defaultRoutes from './routes';
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
const themeConfig = useThemeConfig();
|
const themeConfig = useThemeConfig();
|
||||||
|
const [routes, setRoutes] = useState(defaultRoutes);
|
||||||
|
const { isLoggedIn: isLoggedInState, user } = useSelector<
|
||||||
|
AppState,
|
||||||
|
AppReducer
|
||||||
|
>((state) => state.app);
|
||||||
|
|
||||||
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
|
const { hostname } = window.location;
|
||||||
|
|
||||||
|
const featureResponse = useGetFeatureFlag((allFlags) => {
|
||||||
|
const isOnboardingEnabled =
|
||||||
|
allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active ||
|
||||||
|
false;
|
||||||
|
|
||||||
|
const isChatSupportEnabled =
|
||||||
|
allFlags.find((flag) => flag.name === FeatureKeys.CHAT_SUPPORT)?.active ||
|
||||||
|
false;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_FEATURE_FLAG_RESPONSE,
|
||||||
|
payload: {
|
||||||
|
featureFlag: allFlags,
|
||||||
|
refetch: featureResponse.refetch,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isOnboardingEnabled ||
|
||||||
|
!(hostname && hostname.endsWith('signoz.cloud'))
|
||||||
|
) {
|
||||||
|
const newRoutes = routes.filter(
|
||||||
|
(route) => route?.path !== ROUTES.GET_STARTED,
|
||||||
|
);
|
||||||
|
|
||||||
|
setRoutes(newRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoggedInState && isChatSupportEnabled) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
window.Intercom('boot', {
|
||||||
|
app_id: process.env.INTERCOM_APP_ID,
|
||||||
|
email: user?.email || '',
|
||||||
|
name: user?.name || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigProvider theme={themeConfig}>
|
<ConfigProvider theme={themeConfig}>
|
||||||
|
@ -44,6 +44,10 @@ export const GettingStarted = Loadable(
|
|||||||
() => import(/* webpackChunkName: "GettingStarted" */ 'pages/GettingStarted'),
|
() => import(/* webpackChunkName: "GettingStarted" */ 'pages/GettingStarted'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const Onboarding = Loadable(
|
||||||
|
() => import(/* webpackChunkName: "Onboarding" */ 'pages/OnboardingPage'),
|
||||||
|
);
|
||||||
|
|
||||||
export const DashboardPage = Loadable(
|
export const DashboardPage = Loadable(
|
||||||
() => import(/* webpackChunkName: "DashboardPage" */ 'pages/Dashboard'),
|
() => import(/* webpackChunkName: "DashboardPage" */ 'pages/Dashboard'),
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import DashboardWidget from 'pages/DashboardWidget';
|
|
||||||
import { RouteProps } from 'react-router-dom';
|
import { RouteProps } from 'react-router-dom';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -8,10 +7,10 @@ import {
|
|||||||
CreateAlertChannelAlerts,
|
CreateAlertChannelAlerts,
|
||||||
CreateNewAlerts,
|
CreateNewAlerts,
|
||||||
DashboardPage,
|
DashboardPage,
|
||||||
|
DashboardWidget,
|
||||||
EditAlertChannelsAlerts,
|
EditAlertChannelsAlerts,
|
||||||
EditRulesPage,
|
EditRulesPage,
|
||||||
ErrorDetails,
|
ErrorDetails,
|
||||||
GettingStarted,
|
|
||||||
LicensePage,
|
LicensePage,
|
||||||
ListAllALertsPage,
|
ListAllALertsPage,
|
||||||
LiveLogs,
|
LiveLogs,
|
||||||
@ -21,6 +20,7 @@ import {
|
|||||||
LogsIndexToFields,
|
LogsIndexToFields,
|
||||||
MySettings,
|
MySettings,
|
||||||
NewDashboardPage,
|
NewDashboardPage,
|
||||||
|
Onboarding,
|
||||||
OrganizationSettings,
|
OrganizationSettings,
|
||||||
PasswordReset,
|
PasswordReset,
|
||||||
PipelinePage,
|
PipelinePage,
|
||||||
@ -46,6 +46,13 @@ const routes: AppRoutes[] = [
|
|||||||
isPrivate: false,
|
isPrivate: false,
|
||||||
key: 'SIGN_UP',
|
key: 'SIGN_UP',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.GET_STARTED,
|
||||||
|
exact: true,
|
||||||
|
component: Onboarding,
|
||||||
|
isPrivate: true,
|
||||||
|
key: 'GET_STARTED',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: LogsIndexToFields,
|
component: LogsIndexToFields,
|
||||||
path: ROUTES.LOGS_INDEX_FIELDS,
|
path: ROUTES.LOGS_INDEX_FIELDS,
|
||||||
@ -95,13 +102,6 @@ const routes: AppRoutes[] = [
|
|||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
key: 'USAGE_EXPLORER',
|
key: 'USAGE_EXPLORER',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: ROUTES.INSTRUMENTATION,
|
|
||||||
exact: true,
|
|
||||||
component: GettingStarted,
|
|
||||||
isPrivate: true,
|
|
||||||
key: 'INSTRUMENTATION',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: ROUTES.ALL_DASHBOARD,
|
path: ROUTES.ALL_DASHBOARD,
|
||||||
exact: true,
|
exact: true,
|
||||||
|
@ -1,24 +1,8 @@
|
|||||||
import axios from 'api';
|
import axios from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ApiResponse } from 'types/api';
|
||||||
import { AxiosError } from 'axios';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
|
||||||
import { PayloadProps } from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
const getAll = async (): Promise<
|
export const getAllDashboardList = (): Promise<Dashboard[]> =>
|
||||||
SuccessResponse<PayloadProps> | ErrorResponse
|
axios
|
||||||
> => {
|
.get<ApiResponse<Dashboard[]>>('/dashboards')
|
||||||
try {
|
.then((res) => res.data.data);
|
||||||
const response = await axios.get('/dashboards');
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
error: null,
|
|
||||||
message: response.data.message,
|
|
||||||
payload: response.data.data,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getAll;
|
|
||||||
|
@ -1,24 +1,10 @@
|
|||||||
import axios from 'api';
|
import axios from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ApiResponse } from 'types/api';
|
||||||
import { AxiosError } from 'axios';
|
import { FeatureFlagProps } from 'types/api/features/getFeaturesFlags';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
|
||||||
import { PayloadProps } from 'types/api/features/getFeaturesFlags';
|
|
||||||
|
|
||||||
const getFeaturesFlags = async (): Promise<
|
const getFeaturesFlags = (): Promise<FeatureFlagProps[]> =>
|
||||||
SuccessResponse<PayloadProps> | ErrorResponse
|
axios
|
||||||
> => {
|
.get<ApiResponse<FeatureFlagProps[]>>(`/featureFlags`)
|
||||||
try {
|
.then((response) => response.data.data);
|
||||||
const response = await axios.get(`/featureFlags`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
error: null,
|
|
||||||
message: response.data.status,
|
|
||||||
payload: response.data.data,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getFeaturesFlags;
|
export default getFeaturesFlags;
|
||||||
|
@ -79,7 +79,6 @@ const interceptorRejected = async (
|
|||||||
|
|
||||||
// when refresh token is expired
|
// when refresh token is expired
|
||||||
if (response.status === 401 && response.config.url === '/login') {
|
if (response.status === 401 && response.config.url === '/login') {
|
||||||
console.log('logging out ');
|
|
||||||
Logout();
|
Logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { AxiosError } from 'axios';
|
|||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { PayloadProps, Props } from 'types/api/logs/addToSelectedFields';
|
import { PayloadProps, Props } from 'types/api/logs/addToSelectedFields';
|
||||||
|
|
||||||
const AddToSelectedFields = async (
|
const addToSelectedFields = async (
|
||||||
props: Props,
|
props: Props,
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
@ -16,8 +16,8 @@ const AddToSelectedFields = async (
|
|||||||
payload: data.data,
|
payload: data.data,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
return Promise.reject(ErrorResponseHandler(error as AxiosError));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddToSelectedFields;
|
export default addToSelectedFields;
|
||||||
|
@ -4,7 +4,7 @@ import { AxiosError } from 'axios';
|
|||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
import { PayloadProps, Props } from 'types/api/logs/addToSelectedFields';
|
import { PayloadProps, Props } from 'types/api/logs/addToSelectedFields';
|
||||||
|
|
||||||
const RemoveSelectedField = async (
|
const removeSelectedField = async (
|
||||||
props: Props,
|
props: Props,
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||||
try {
|
try {
|
||||||
@ -16,8 +16,8 @@ const RemoveSelectedField = async (
|
|||||||
payload: data.data,
|
payload: data.data,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
return Promise.reject(ErrorResponseHandler(error as AxiosError));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RemoveSelectedField;
|
export default removeSelectedField;
|
||||||
|
@ -2,14 +2,12 @@ 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 * as loginPrecheck from 'types/api/user/loginPrecheck';
|
import { PayloadProps } from 'types/api/user/loginPrecheck';
|
||||||
import { Props } from 'types/api/user/signup';
|
import { Props } from 'types/api/user/signup';
|
||||||
|
|
||||||
const signup = async (
|
const signup = async (
|
||||||
props: Props,
|
props: Props,
|
||||||
): Promise<
|
): Promise<SuccessResponse<null | PayloadProps> | ErrorResponse> => {
|
||||||
SuccessResponse<null | loginPrecheck.PayloadProps> | ErrorResponse
|
|
||||||
> => {
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`/register`, {
|
const response = await axios.post(`/register`, {
|
||||||
...props,
|
...props,
|
||||||
|
@ -60,5 +60,9 @@ export const Logout = (): void => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
window.Intercom('shutdown');
|
||||||
|
|
||||||
history.push(ROUTES.LOGIN);
|
history.push(ROUTES.LOGIN);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DownOutlined,
|
|
||||||
MoreOutlined,
|
MoreOutlined,
|
||||||
SaveOutlined,
|
SaveOutlined,
|
||||||
ShareAltOutlined,
|
ShareAltOutlined,
|
||||||
@ -13,13 +12,14 @@ import {
|
|||||||
MenuProps,
|
MenuProps,
|
||||||
Popover,
|
Popover,
|
||||||
Row,
|
Row,
|
||||||
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { querySearchParams } from 'constants/queryBuilderQueryNames';
|
import { QueryParams } from 'constants/query';
|
||||||
import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam';
|
import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useDeleteView } from 'hooks/saveViews/useDeleteView';
|
import { useDeleteView } from 'hooks/saveViews/useDeleteView';
|
||||||
@ -28,7 +28,7 @@ import { useUpdateView } from 'hooks/saveViews/useUpdateView';
|
|||||||
import useErrorNotification from 'hooks/useErrorNotification';
|
import useErrorNotification from 'hooks/useErrorNotification';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
|
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
|
|
||||||
import { ExploreHeaderToolTip, SaveButtonText } from './constants';
|
import { ExploreHeaderToolTip, SaveButtonText } from './constants';
|
||||||
@ -40,7 +40,7 @@ import {
|
|||||||
OffSetCol,
|
OffSetCol,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import { ExplorerCardProps } from './types';
|
import { ExplorerCardProps } from './types';
|
||||||
import { deleteViewHandler, isQueryUpdatedInView } from './utils';
|
import { deleteViewHandler } from './utils';
|
||||||
|
|
||||||
function ExplorerCard({
|
function ExplorerCard({
|
||||||
sourcepage,
|
sourcepage,
|
||||||
@ -48,7 +48,6 @@ function ExplorerCard({
|
|||||||
}: ExplorerCardProps): JSX.Element {
|
}: ExplorerCardProps): JSX.Element {
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
const [, setCopyUrl] = useCopyToClipboard();
|
const [, setCopyUrl] = useCopyToClipboard();
|
||||||
const [isQueryUpdated, setIsQueryUpdated] = useState<boolean>(false);
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
const onCopyUrlHandler = (): void => {
|
const onCopyUrlHandler = (): void => {
|
||||||
@ -59,10 +58,11 @@ function ExplorerCard({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
stagedQuery,
|
|
||||||
currentQuery,
|
currentQuery,
|
||||||
panelType,
|
panelType,
|
||||||
redirectWithQueryBuilderData,
|
redirectWithQueryBuilderData,
|
||||||
|
updateAllQueriesOperators,
|
||||||
|
isStagedQueryUpdated,
|
||||||
} = useQueryBuilder();
|
} = useQueryBuilder();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -75,18 +75,15 @@ function ExplorerCard({
|
|||||||
|
|
||||||
useErrorNotification(error);
|
useErrorNotification(error);
|
||||||
|
|
||||||
const handlePopOverClose = (): void => {
|
const handleOpenChange = (newOpen = false): void => {
|
||||||
setIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenChange = (newOpen: boolean): void => {
|
|
||||||
setIsOpen(newOpen);
|
setIsOpen(newOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewName =
|
const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
|
||||||
useGetSearchQueryParam(querySearchParams.viewName) || 'Query Builder';
|
|
||||||
|
|
||||||
const viewKey = useGetSearchQueryParam(querySearchParams.viewKey) || '';
|
const viewKey = useGetSearchQueryParam(QueryParams.viewKey) || '';
|
||||||
|
|
||||||
|
const isQueryUpdated = isStagedQueryUpdated(viewsData?.data?.data, viewKey);
|
||||||
|
|
||||||
const { mutateAsync: updateViewAsync } = useUpdateView({
|
const { mutateAsync: updateViewAsync } = useUpdateView({
|
||||||
compositeQuery: mapCompositeQueryFromQuery(currentQuery, panelType),
|
compositeQuery: mapCompositeQueryFromQuery(currentQuery, panelType),
|
||||||
@ -104,7 +101,7 @@ function ExplorerCard({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteHandler = useCallback(() => {
|
const onDeleteHandler = (): void =>
|
||||||
deleteViewHandler({
|
deleteViewHandler({
|
||||||
deleteViewAsync,
|
deleteViewAsync,
|
||||||
notifications,
|
notifications,
|
||||||
@ -113,15 +110,9 @@ function ExplorerCard({
|
|||||||
refetchAllView,
|
refetchAllView,
|
||||||
viewId: viewKey,
|
viewId: viewKey,
|
||||||
viewKey,
|
viewKey,
|
||||||
|
updateAllQueriesOperators,
|
||||||
|
sourcePage: sourcepage,
|
||||||
});
|
});
|
||||||
}, [
|
|
||||||
deleteViewAsync,
|
|
||||||
notifications,
|
|
||||||
panelType,
|
|
||||||
redirectWithQueryBuilderData,
|
|
||||||
refetchAllView,
|
|
||||||
viewKey,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const onUpdateQueryHandler = (): void => {
|
const onUpdateQueryHandler = (): void => {
|
||||||
updateViewAsync(
|
updateViewAsync(
|
||||||
@ -134,7 +125,6 @@ function ExplorerCard({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setIsQueryUpdated(false);
|
|
||||||
notifications.success({
|
notifications.success({
|
||||||
message: 'View Updated Successfully',
|
message: 'View Updated Successfully',
|
||||||
});
|
});
|
||||||
@ -147,56 +137,16 @@ function ExplorerCard({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const moreOptionMenu: MenuProps = {
|
||||||
setIsQueryUpdated(
|
items: [
|
||||||
isQueryUpdatedInView({
|
{
|
||||||
data: viewsData?.data?.data,
|
key: 'delete',
|
||||||
stagedQuery,
|
label: <Typography.Text strong>Delete</Typography.Text>,
|
||||||
viewKey,
|
onClick: onDeleteHandler,
|
||||||
currentPanelType: panelType,
|
icon: <DeleteOutlined />,
|
||||||
}),
|
},
|
||||||
);
|
],
|
||||||
}, [
|
};
|
||||||
currentQuery,
|
|
||||||
viewsData?.data?.data,
|
|
||||||
stagedQuery,
|
|
||||||
stagedQuery?.builder.queryData,
|
|
||||||
viewKey,
|
|
||||||
panelType,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const menu = useMemo(
|
|
||||||
(): MenuProps => ({
|
|
||||||
items: viewsData?.data?.data?.map((view) => ({
|
|
||||||
key: view.uuid,
|
|
||||||
label: (
|
|
||||||
<MenuItemGenerator
|
|
||||||
viewName={view.name}
|
|
||||||
viewKey={viewKey}
|
|
||||||
createdBy={view.createdBy}
|
|
||||||
uuid={view.uuid}
|
|
||||||
refetchAllView={refetchAllView}
|
|
||||||
viewData={viewsData.data.data}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
}),
|
|
||||||
[refetchAllView, viewKey, viewsData?.data?.data],
|
|
||||||
);
|
|
||||||
|
|
||||||
const moreOptionMenu = useMemo(
|
|
||||||
(): MenuProps => ({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
key: 'delete',
|
|
||||||
label: <Typography.Text strong>Delete</Typography.Text>,
|
|
||||||
onClick: onDeleteHandler,
|
|
||||||
icon: <DeleteOutlined />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
[onDeleteHandler],
|
|
||||||
);
|
|
||||||
|
|
||||||
const saveButtonType = isQueryUpdated ? 'default' : 'primary';
|
const saveButtonType = isQueryUpdated ? 'default' : 'primary';
|
||||||
const saveButtonIcon = isQueryUpdated ? null : <SaveOutlined />;
|
const saveButtonIcon = isQueryUpdated ? null : <SaveOutlined />;
|
||||||
@ -207,7 +157,7 @@ function ExplorerCard({
|
|||||||
<Row align="middle">
|
<Row align="middle">
|
||||||
<Col span={6}>
|
<Col span={6}>
|
||||||
<Space>
|
<Space>
|
||||||
<Typography>{viewName}</Typography>
|
<Typography>Query Builder</Typography>
|
||||||
<TextToolTip
|
<TextToolTip
|
||||||
url={ExploreHeaderToolTip.url}
|
url={ExploreHeaderToolTip.url}
|
||||||
text={ExploreHeaderToolTip.text}
|
text={ExploreHeaderToolTip.text}
|
||||||
@ -215,20 +165,33 @@ function ExplorerCard({
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
<OffSetCol span={10} offset={8}>
|
<OffSetCol span={18}>
|
||||||
<Space size="large">
|
<Space size="large">
|
||||||
{viewsData?.data.data && viewsData?.data.data.length && (
|
{viewsData?.data.data && viewsData?.data.data.length && (
|
||||||
<Space>
|
<Space>
|
||||||
{/* <Typography.Text>Saved Views</Typography.Text> */}
|
<Select
|
||||||
<Dropdown.Button
|
|
||||||
menu={menu}
|
|
||||||
loading={isLoading || isRefetching}
|
loading={isLoading || isRefetching}
|
||||||
icon={<DownOutlined />}
|
showSearch
|
||||||
trigger={['click']}
|
placeholder="Select a view"
|
||||||
overlayStyle={DropDownOverlay}
|
dropdownStyle={DropDownOverlay}
|
||||||
|
dropdownMatchSelectWidth={false}
|
||||||
|
optionLabelProp="value"
|
||||||
|
value={viewName || undefined}
|
||||||
>
|
>
|
||||||
Select View
|
{viewsData?.data.data.map((view) => (
|
||||||
</Dropdown.Button>
|
<Select.Option key={view.uuid} value={view.name}>
|
||||||
|
<MenuItemGenerator
|
||||||
|
viewName={view.name}
|
||||||
|
viewKey={viewKey}
|
||||||
|
createdBy={view.createdBy}
|
||||||
|
uuid={view.uuid}
|
||||||
|
refetchAllView={refetchAllView}
|
||||||
|
viewData={viewsData.data.data}
|
||||||
|
sourcePage={sourcepage}
|
||||||
|
/>
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
{isQueryUpdated && (
|
{isQueryUpdated && (
|
||||||
@ -246,7 +209,7 @@ function ExplorerCard({
|
|||||||
content={
|
content={
|
||||||
<SaveViewWithName
|
<SaveViewWithName
|
||||||
sourcePage={sourcepage}
|
sourcePage={sourcepage}
|
||||||
handlePopOverClose={handlePopOverClose}
|
handlePopOverClose={handleOpenChange}
|
||||||
refetchAllView={refetchAllView}
|
refetchAllView={refetchAllView}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,15 @@ function MenuItemGenerator({
|
|||||||
uuid,
|
uuid,
|
||||||
viewData,
|
viewData,
|
||||||
refetchAllView,
|
refetchAllView,
|
||||||
|
sourcePage,
|
||||||
}: MenuItemLabelGeneratorProps): JSX.Element {
|
}: MenuItemLabelGeneratorProps): JSX.Element {
|
||||||
const { panelType, redirectWithQueryBuilderData } = useQueryBuilder();
|
const {
|
||||||
|
panelType,
|
||||||
|
redirectWithQueryBuilderData,
|
||||||
|
updateAllQueriesOperators,
|
||||||
|
} = useQueryBuilder();
|
||||||
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
const { mutateAsync: deleteViewAsync } = useDeleteView(uuid);
|
const { mutateAsync: deleteViewAsync } = useDeleteView(uuid);
|
||||||
@ -34,6 +40,8 @@ function MenuItemGenerator({
|
|||||||
refetchAllView,
|
refetchAllView,
|
||||||
viewId: uuid,
|
viewId: uuid,
|
||||||
viewKey,
|
viewKey,
|
||||||
|
updateAllQueriesOperators,
|
||||||
|
sourcePage,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Card, Input, Typography } from 'antd';
|
import { Card, Form, Input, Typography } from 'antd';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useSaveView } from 'hooks/saveViews/useSaveView';
|
import { useSaveView } from 'hooks/saveViews/useSaveView';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
|
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
|
||||||
import { ChangeEvent, useCallback, useState } from 'react';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { SaveButton } from './styles';
|
import { SaveButton } from './styles';
|
||||||
import { SaveViewWithNameProps } from './types';
|
import { SaveViewFormProps, SaveViewWithNameProps } from './types';
|
||||||
import { saveViewHandler } from './utils';
|
import { saveViewHandler } from './utils';
|
||||||
|
|
||||||
function SaveViewWithName({
|
function SaveViewWithName({
|
||||||
@ -15,7 +15,8 @@ function SaveViewWithName({
|
|||||||
handlePopOverClose,
|
handlePopOverClose,
|
||||||
refetchAllView,
|
refetchAllView,
|
||||||
}: SaveViewWithNameProps): JSX.Element {
|
}: SaveViewWithNameProps): JSX.Element {
|
||||||
const [name, setName] = useState('');
|
const [form] = Form.useForm<SaveViewFormProps>();
|
||||||
|
const { t } = useTranslation(['explorer']);
|
||||||
const {
|
const {
|
||||||
currentQuery,
|
currentQuery,
|
||||||
panelType,
|
panelType,
|
||||||
@ -25,19 +26,12 @@ function SaveViewWithName({
|
|||||||
const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType);
|
const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType);
|
||||||
|
|
||||||
const { isLoading, mutateAsync: saveViewAsync } = useSaveView({
|
const { isLoading, mutateAsync: saveViewAsync } = useSaveView({
|
||||||
viewName: name,
|
viewName: form.getFieldValue('viewName'),
|
||||||
compositeQuery,
|
compositeQuery,
|
||||||
sourcePage,
|
sourcePage,
|
||||||
extraData: '',
|
extraData: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const onChangeHandler = useCallback(
|
|
||||||
(e: ChangeEvent<HTMLInputElement>): void => {
|
|
||||||
setName(e.target.value);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onSaveHandler = (): void => {
|
const onSaveHandler = (): void => {
|
||||||
saveViewHandler({
|
saveViewHandler({
|
||||||
compositeQuery,
|
compositeQuery,
|
||||||
@ -49,18 +43,32 @@ function SaveViewWithName({
|
|||||||
refetchAllView,
|
refetchAllView,
|
||||||
saveViewAsync,
|
saveViewAsync,
|
||||||
sourcePage,
|
sourcePage,
|
||||||
viewName: name,
|
viewName: form.getFieldValue('viewName'),
|
||||||
setName,
|
form,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<Typography>Name of the View</Typography>
|
<Typography>{t('name_of_the_view')}</Typography>
|
||||||
<Input placeholder="Enter Name" onChange={onChangeHandler} />
|
<Form form={form} onFinish={onSaveHandler}>
|
||||||
<SaveButton onClick={onSaveHandler} type="primary" loading={isLoading}>
|
<Form.Item
|
||||||
Save
|
name={['viewName']}
|
||||||
</SaveButton>
|
required
|
||||||
|
requiredMark
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Please enter view name',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="Enter Name" />
|
||||||
|
</Form.Item>
|
||||||
|
<SaveButton htmlType="submit" type="primary" loading={isLoading}>
|
||||||
|
Save
|
||||||
|
</SaveButton>
|
||||||
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { QueryParams } from 'constants/query';
|
||||||
|
|
||||||
export const ExploreHeaderToolTip = {
|
export const ExploreHeaderToolTip = {
|
||||||
url:
|
url:
|
||||||
'https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=new-query-builder',
|
'https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=new-query-builder',
|
||||||
@ -8,3 +10,5 @@ export const SaveButtonText = {
|
|||||||
SAVE_AS_NEW_VIEW: 'Save as new view',
|
SAVE_AS_NEW_VIEW: 'Save as new view',
|
||||||
SAVE_VIEW: 'Save view',
|
SAVE_VIEW: 'Save view',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type QuerySearchParamNames = QueryParams.viewName | QueryParams.viewKey;
|
||||||
|
@ -62,15 +62,12 @@ describe('ExplorerCard', () => {
|
|||||||
const screen = render(
|
const screen = render(
|
||||||
<ExplorerCard sourcepage={DataSource.TRACES}>Mock Children</ExplorerCard>,
|
<ExplorerCard sourcepage={DataSource.TRACES}>Mock Children</ExplorerCard>,
|
||||||
);
|
);
|
||||||
const selectButton = screen.getByText('Select View');
|
const selectPlaceholder = screen.getByText('Select a view');
|
||||||
|
|
||||||
fireEvent.click(selectButton);
|
fireEvent.mouseDown(selectPlaceholder);
|
||||||
|
const viewNameText = await screen.getAllByText('View 1');
|
||||||
const spanElement = screen.getByRole('img', {
|
viewNameText.forEach((element) => {
|
||||||
name: 'down',
|
expect(element).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
fireEvent.click(spanElement);
|
|
||||||
const viewNameText = await screen.findByText('View 2');
|
|
||||||
expect(viewNameText).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
|
import MockQueryClientProvider from 'providers/test/MockQueryClientProvider';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { viewMockData } from '../__mock__/viewData';
|
import { viewMockData } from '../__mock__/viewData';
|
||||||
import MenuItemGenerator from '../MenuItemGenerator';
|
import MenuItemGenerator from '../MenuItemGenerator';
|
||||||
@ -12,6 +13,12 @@ jest.mock('react-router-dom', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('antd/es/form/Form', () => ({
|
||||||
|
useForm: jest.fn().mockReturnValue({
|
||||||
|
onFinish: jest.fn(),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('MenuItemGenerator', () => {
|
describe('MenuItemGenerator', () => {
|
||||||
it('should render MenuItemGenerator component', () => {
|
it('should render MenuItemGenerator component', () => {
|
||||||
const screen = render(
|
const screen = render(
|
||||||
@ -23,6 +30,7 @@ describe('MenuItemGenerator', () => {
|
|||||||
uuid={viewMockData[0].uuid}
|
uuid={viewMockData[0].uuid}
|
||||||
refetchAllView={jest.fn()}
|
refetchAllView={jest.fn()}
|
||||||
viewData={viewMockData}
|
viewData={viewMockData}
|
||||||
|
sourcePage={DataSource.TRACES}
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
@ -40,6 +48,7 @@ describe('MenuItemGenerator', () => {
|
|||||||
uuid={viewMockData[0].uuid}
|
uuid={viewMockData[0].uuid}
|
||||||
refetchAllView={jest.fn()}
|
refetchAllView={jest.fn()}
|
||||||
viewData={viewMockData}
|
viewData={viewMockData}
|
||||||
|
sourcePage={DataSource.TRACES}
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
import { FormInstance } from 'antd';
|
||||||
import { NotificationInstance } from 'antd/es/notification/interface';
|
import { NotificationInstance } from 'antd/es/notification/interface';
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { SetStateAction } from 'react';
|
|
||||||
import { UseMutateAsyncFunction } from 'react-query';
|
import { UseMutateAsyncFunction } from 'react-query';
|
||||||
import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery';
|
import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
@ -38,6 +38,10 @@ export interface SaveViewWithNameProps {
|
|||||||
refetchAllView: VoidFunction;
|
refetchAllView: VoidFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SaveViewFormProps {
|
||||||
|
viewName: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MenuItemLabelGeneratorProps {
|
export interface MenuItemLabelGeneratorProps {
|
||||||
viewName: string;
|
viewName: string;
|
||||||
viewKey: string;
|
viewKey: string;
|
||||||
@ -45,6 +49,7 @@ export interface MenuItemLabelGeneratorProps {
|
|||||||
uuid: string;
|
uuid: string;
|
||||||
viewData: ViewProps[];
|
viewData: ViewProps[];
|
||||||
refetchAllView: VoidFunction;
|
refetchAllView: VoidFunction;
|
||||||
|
sourcePage: ExplorerCardProps['sourcepage'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SaveViewHandlerProps {
|
export interface SaveViewHandlerProps {
|
||||||
@ -63,7 +68,7 @@ export interface SaveViewHandlerProps {
|
|||||||
>;
|
>;
|
||||||
handlePopOverClose: SaveViewWithNameProps['handlePopOverClose'];
|
handlePopOverClose: SaveViewWithNameProps['handlePopOverClose'];
|
||||||
redirectWithQueryBuilderData: QueryBuilderContextType['redirectWithQueryBuilderData'];
|
redirectWithQueryBuilderData: QueryBuilderContextType['redirectWithQueryBuilderData'];
|
||||||
setName: (value: SetStateAction<string>) => void;
|
form: FormInstance<SaveViewFormProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteViewHandlerProps {
|
export interface DeleteViewHandlerProps {
|
||||||
@ -74,4 +79,6 @@ export interface DeleteViewHandlerProps {
|
|||||||
panelType: PANEL_TYPES | null;
|
panelType: PANEL_TYPES | null;
|
||||||
viewKey: string;
|
viewKey: string;
|
||||||
viewId: string;
|
viewId: string;
|
||||||
|
updateAllQueriesOperators: QueryBuilderContextType['updateAllQueriesOperators'];
|
||||||
|
sourcePage: ExplorerCardProps['sourcepage'];
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import { NotificationInstance } from 'antd/es/notification/interface';
|
import { NotificationInstance } from 'antd/es/notification/interface';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { initialQueriesMap } from 'constants/queryBuilder';
|
import { QueryParams } from 'constants/query';
|
||||||
import {
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
queryParamNamesMap,
|
|
||||||
querySearchParams,
|
|
||||||
} from 'constants/queryBuilderQueryNames';
|
|
||||||
import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi';
|
import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
|
|
||||||
@ -108,7 +105,7 @@ export const saveViewHandler = ({
|
|||||||
extraData,
|
extraData,
|
||||||
redirectWithQueryBuilderData,
|
redirectWithQueryBuilderData,
|
||||||
panelType,
|
panelType,
|
||||||
setName,
|
form,
|
||||||
}: SaveViewHandlerProps): void => {
|
}: SaveViewHandlerProps): void => {
|
||||||
saveViewAsync(
|
saveViewAsync(
|
||||||
{
|
{
|
||||||
@ -121,9 +118,9 @@ export const saveViewHandler = ({
|
|||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
refetchAllView();
|
refetchAllView();
|
||||||
redirectWithQueryBuilderData(mapQueryDataFromApi(compositeQuery), {
|
redirectWithQueryBuilderData(mapQueryDataFromApi(compositeQuery), {
|
||||||
[queryParamNamesMap.panelTypes]: panelType,
|
[QueryParams.panelTypes]: panelType,
|
||||||
[querySearchParams.viewName]: viewName,
|
[QueryParams.viewName]: viewName,
|
||||||
[querySearchParams.viewKey]: data.data.data,
|
[QueryParams.viewKey]: data.data.data,
|
||||||
});
|
});
|
||||||
notifications.success({
|
notifications.success({
|
||||||
message: 'View Saved Successfully',
|
message: 'View Saved Successfully',
|
||||||
@ -134,7 +131,7 @@ export const saveViewHandler = ({
|
|||||||
},
|
},
|
||||||
onSettled: () => {
|
onSettled: () => {
|
||||||
handlePopOverClose();
|
handlePopOverClose();
|
||||||
setName('');
|
form.resetFields();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -148,15 +145,24 @@ export const deleteViewHandler = ({
|
|||||||
panelType,
|
panelType,
|
||||||
viewKey,
|
viewKey,
|
||||||
viewId,
|
viewId,
|
||||||
|
updateAllQueriesOperators,
|
||||||
|
sourcePage,
|
||||||
}: DeleteViewHandlerProps): void => {
|
}: DeleteViewHandlerProps): void => {
|
||||||
deleteViewAsync(viewKey, {
|
deleteViewAsync(viewKey, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
if (viewId === viewKey) {
|
if (viewId === viewKey) {
|
||||||
redirectWithQueryBuilderData(initialQueriesMap.traces, {
|
redirectWithQueryBuilderData(
|
||||||
[querySearchParams.viewName]: 'Query Builder',
|
updateAllQueriesOperators(
|
||||||
[queryParamNamesMap.panelTypes]: panelType,
|
initialQueriesMap.traces,
|
||||||
[querySearchParams.viewKey]: '',
|
panelType || PANEL_TYPES.LIST,
|
||||||
});
|
sourcePage,
|
||||||
|
),
|
||||||
|
{
|
||||||
|
[QueryParams.viewName]: '',
|
||||||
|
[QueryParams.panelTypes]: panelType,
|
||||||
|
[QueryParams.viewKey]: '',
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
notifications.success({
|
notifications.success({
|
||||||
message: 'View Deleted Successfully',
|
message: 'View Deleted Successfully',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Chart, ChartTypeRegistry, Plugin } from 'chart.js';
|
import { Chart, ChartTypeRegistry, Plugin } from 'chart.js';
|
||||||
import * as ChartHelpers from 'chart.js/helpers';
|
import { getRelativePosition } from 'chart.js/helpers';
|
||||||
|
|
||||||
// utils
|
// utils
|
||||||
import { ChartEventHandler, mergeDefaultOptions } from './utils';
|
import { ChartEventHandler, mergeDefaultOptions } from './utils';
|
||||||
@ -45,7 +45,7 @@ function createMousedownHandler(
|
|||||||
return (ev): void => {
|
return (ev): void => {
|
||||||
const { left, right } = chart.chartArea;
|
const { left, right } = chart.chartArea;
|
||||||
|
|
||||||
let { x: startDragPositionX } = ChartHelpers.getRelativePosition(ev, chart);
|
let { x: startDragPositionX } = getRelativePosition(ev, chart);
|
||||||
|
|
||||||
if (left > startDragPositionX) {
|
if (left > startDragPositionX) {
|
||||||
startDragPositionX = left;
|
startDragPositionX = left;
|
||||||
@ -74,7 +74,7 @@ function createMousemoveHandler(
|
|||||||
|
|
||||||
const { left, right } = chart.chartArea;
|
const { left, right } = chart.chartArea;
|
||||||
|
|
||||||
let { x: dragPositionX } = ChartHelpers.getRelativePosition(ev, chart);
|
let { x: dragPositionX } = getRelativePosition(ev, chart);
|
||||||
|
|
||||||
if (left > dragPositionX) {
|
if (left > dragPositionX) {
|
||||||
dragPositionX = left;
|
dragPositionX = left;
|
||||||
@ -99,7 +99,7 @@ function createMouseupHandler(
|
|||||||
return (ev): void => {
|
return (ev): void => {
|
||||||
const { left, right } = chart.chartArea;
|
const { left, right } = chart.chartArea;
|
||||||
|
|
||||||
let { x: endRelativePostionX } = ChartHelpers.getRelativePosition(ev, chart);
|
let { x: endRelativePostionX } = getRelativePosition(ev, chart);
|
||||||
|
|
||||||
if (left > endRelativePostionX) {
|
if (left > endRelativePostionX) {
|
||||||
endRelativePostionX = left;
|
endRelativePostionX = left;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Chart, ChartEvent, ChartTypeRegistry, Plugin } from 'chart.js';
|
import { Chart, ChartEvent, ChartTypeRegistry, Plugin } from 'chart.js';
|
||||||
import * as ChartHelpers from 'chart.js/helpers';
|
import { getRelativePosition } from 'chart.js/helpers';
|
||||||
|
|
||||||
// utils
|
// utils
|
||||||
import { ChartEventHandler, mergeDefaultOptions } from './utils';
|
import { ChartEventHandler, mergeDefaultOptions } from './utils';
|
||||||
@ -42,7 +42,7 @@ function createMousemoveHandler(
|
|||||||
return (ev: ChartEvent | MouseEvent): void => {
|
return (ev: ChartEvent | MouseEvent): void => {
|
||||||
const { left, right, top, bottom } = chart.chartArea;
|
const { left, right, top, bottom } = chart.chartArea;
|
||||||
|
|
||||||
let { x, y } = ChartHelpers.getRelativePosition(ev, chart);
|
let { x, y } = getRelativePosition(ev, chart);
|
||||||
|
|
||||||
if (left > x) {
|
if (left > x) {
|
||||||
x = left;
|
x = left;
|
||||||
|
@ -1,4 +1,17 @@
|
|||||||
import * as AntD from 'antd';
|
import {
|
||||||
|
Button,
|
||||||
|
ButtonProps,
|
||||||
|
Col,
|
||||||
|
ColProps,
|
||||||
|
Divider,
|
||||||
|
DividerProps,
|
||||||
|
Row,
|
||||||
|
RowProps,
|
||||||
|
Space,
|
||||||
|
SpaceProps,
|
||||||
|
TabsProps,
|
||||||
|
Typography,
|
||||||
|
} from 'antd';
|
||||||
import { TextProps } from 'antd/lib/typography/Text';
|
import { TextProps } from 'antd/lib/typography/Text';
|
||||||
import { TitleProps } from 'antd/lib/typography/Title';
|
import { TitleProps } from 'antd/lib/typography/Title';
|
||||||
import { HTMLAttributes } from 'react';
|
import { HTMLAttributes } from 'react';
|
||||||
@ -9,43 +22,43 @@ import { IStyledClass } from './types';
|
|||||||
const styledClass = (props: IStyledClass): FlattenSimpleInterpolation | null =>
|
const styledClass = (props: IStyledClass): FlattenSimpleInterpolation | null =>
|
||||||
props.styledclass || null;
|
props.styledclass || null;
|
||||||
|
|
||||||
type TStyledCol = AntD.ColProps & IStyledClass;
|
type TStyledCol = ColProps & IStyledClass;
|
||||||
const StyledCol = styled(AntD.Col)<TStyledCol>`
|
const StyledCol = styled(Col)<TStyledCol>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TStyledRow = AntD.RowProps & IStyledClass;
|
type TStyledRow = RowProps & IStyledClass;
|
||||||
const StyledRow = styled(AntD.Row)<TStyledRow>`
|
const StyledRow = styled(Row)<TStyledRow>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TStyledDivider = AntD.DividerProps & IStyledClass;
|
type TStyledDivider = DividerProps & IStyledClass;
|
||||||
const StyledDivider = styled(AntD.Divider)<TStyledDivider>`
|
const StyledDivider = styled(Divider)<TStyledDivider>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TStyledSpace = AntD.SpaceProps & IStyledClass;
|
type TStyledSpace = SpaceProps & IStyledClass;
|
||||||
const StyledSpace = styled(AntD.Space)<TStyledSpace>`
|
const StyledSpace = styled(Space)<TStyledSpace>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TStyledTabs = AntD.TabsProps & IStyledClass;
|
type TStyledTabs = TabsProps & IStyledClass;
|
||||||
const StyledTabs = styled(AntD.Divider)<TStyledTabs>`
|
const StyledTabs = styled(Divider)<TStyledTabs>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type TStyledButton = AntD.ButtonProps & IStyledClass;
|
type TStyledButton = ButtonProps & IStyledClass;
|
||||||
const StyledButton = styled(AntD.Button)<TStyledButton>`
|
const StyledButton = styled(Button)<TStyledButton>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const { Text } = AntD.Typography;
|
const { Text } = Typography;
|
||||||
type TStyledTypographyText = TextProps & IStyledClass;
|
type TStyledTypographyText = TextProps & IStyledClass;
|
||||||
const StyledTypographyText = styled(Text)<TStyledTypographyText>`
|
const StyledTypographyText = styled(Text)<TStyledTypographyText>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const { Title } = AntD.Typography;
|
const { Title } = Typography;
|
||||||
type TStyledTypographyTitle = TitleProps & IStyledClass;
|
type TStyledTypographyTitle = TitleProps & IStyledClass;
|
||||||
const StyledTypographyTitle = styled(Title)<TStyledTypographyTitle>`
|
const StyledTypographyTitle = styled(Title)<TStyledTypographyTitle>`
|
||||||
${styledClass}
|
${styledClass}
|
||||||
|
4
frontend/src/constants/card.ts
Normal file
4
frontend/src/constants/card.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export const CARD_BODY_STYLE = {
|
||||||
|
padding: '0',
|
||||||
|
height: '100%',
|
||||||
|
};
|
@ -17,4 +17,6 @@ export enum FeatureKeys {
|
|||||||
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
||||||
USE_SPAN_METRICS = 'USE_SPAN_METRICS',
|
USE_SPAN_METRICS = 'USE_SPAN_METRICS',
|
||||||
OSS = 'OSS',
|
OSS = 'OSS',
|
||||||
|
ONBOARDING = 'ONBOARDING',
|
||||||
|
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,7 @@ export enum LOCALSTORAGE {
|
|||||||
GRAPH_VISIBILITY_STATES = 'GRAPH_VISIBILITY_STATES',
|
GRAPH_VISIBILITY_STATES = 'GRAPH_VISIBILITY_STATES',
|
||||||
TRACES_LIST_COLUMNS = 'TRACES_LIST_COLUMNS',
|
TRACES_LIST_COLUMNS = 'TRACES_LIST_COLUMNS',
|
||||||
LOGS_LIST_COLUMNS = 'LOGS_LIST_COLUMNS',
|
LOGS_LIST_COLUMNS = 'LOGS_LIST_COLUMNS',
|
||||||
|
LOGGED_IN_USER_NAME = 'LOGGED_IN_USER_NAME',
|
||||||
|
LOGGED_IN_USER_EMAIL = 'LOGGED_IN_USER_EMAIL',
|
||||||
|
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,12 @@ export enum QueryParams {
|
|||||||
q = 'q',
|
q = 'q',
|
||||||
activeLogId = 'activeLogId',
|
activeLogId = 'activeLogId',
|
||||||
timeRange = 'timeRange',
|
timeRange = 'timeRange',
|
||||||
|
compositeQuery = 'compositeQuery',
|
||||||
|
panelTypes = 'panelTypes',
|
||||||
|
pageSize = 'pageSize',
|
||||||
|
viewMode = 'viewMode',
|
||||||
|
selectedFields = 'selectedFields',
|
||||||
|
linesPerRow = 'linesPerRow',
|
||||||
|
viewName = 'viewName',
|
||||||
|
viewKey = 'viewKey',
|
||||||
}
|
}
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
type QueryParamNames =
|
|
||||||
| 'compositeQuery'
|
|
||||||
| 'panelTypes'
|
|
||||||
| 'pageSize'
|
|
||||||
| 'viewMode'
|
|
||||||
| 'selectedFields'
|
|
||||||
| 'linesPerRow';
|
|
||||||
|
|
||||||
export type QuerySearchParamNames = 'viewName' | 'viewKey';
|
|
||||||
|
|
||||||
export const queryParamNamesMap: Record<QueryParamNames, QueryParamNames> = {
|
|
||||||
compositeQuery: 'compositeQuery',
|
|
||||||
panelTypes: 'panelTypes',
|
|
||||||
pageSize: 'pageSize',
|
|
||||||
viewMode: 'viewMode',
|
|
||||||
selectedFields: 'selectedFields',
|
|
||||||
linesPerRow: 'linesPerRow',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const querySearchParams: Record<
|
|
||||||
QuerySearchParamNames,
|
|
||||||
QuerySearchParamNames
|
|
||||||
> = {
|
|
||||||
viewName: 'viewName',
|
|
||||||
viewKey: 'viewKey',
|
|
||||||
};
|
|
@ -2,4 +2,6 @@ export const REACT_QUERY_KEY = {
|
|||||||
GET_ALL_LICENCES: 'GET_ALL_LICENCES',
|
GET_ALL_LICENCES: 'GET_ALL_LICENCES',
|
||||||
GET_QUERY_RANGE: 'GET_QUERY_RANGE',
|
GET_QUERY_RANGE: 'GET_QUERY_RANGE',
|
||||||
GET_ALL_DASHBOARDS: 'GET_ALL_DASHBOARDS',
|
GET_ALL_DASHBOARDS: 'GET_ALL_DASHBOARDS',
|
||||||
|
GET_TRIGGERED_ALERTS: 'GET_TRIGGERED_ALERTS',
|
||||||
|
GET_FEATURES_FLAGS: 'GET_FEATURES_FLAGS',
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,7 @@ const ROUTES = {
|
|||||||
TRACE_DETAIL: '/trace/:id',
|
TRACE_DETAIL: '/trace/:id',
|
||||||
TRACES_EXPLORER: '/traces-explorer',
|
TRACES_EXPLORER: '/traces-explorer',
|
||||||
SETTINGS: '/settings',
|
SETTINGS: '/settings',
|
||||||
INSTRUMENTATION: '/get-started',
|
GET_STARTED: '/get-started',
|
||||||
USAGE_EXPLORER: '/usage-explorer',
|
USAGE_EXPLORER: '/usage-explorer',
|
||||||
APPLICATION: '/services',
|
APPLICATION: '/services',
|
||||||
ALL_DASHBOARD: '/dashboard',
|
ALL_DASHBOARD: '/dashboard',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import getDynamicConfigs from 'api/dynamicConfigs/getDynamicConfigs';
|
import getDynamicConfigs from 'api/dynamicConfigs/getDynamicConfigs';
|
||||||
import getFeaturesFlags from 'api/features/getFeatureFlags';
|
|
||||||
import getUserLatestVersion from 'api/user/getLatestVersion';
|
import getUserLatestVersion from 'api/user/getLatestVersion';
|
||||||
import getUserVersion from 'api/user/getVersion';
|
import getUserVersion from 'api/user/getVersion';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import Header from 'container/Header';
|
import Header from 'container/Header';
|
||||||
import SideNav from 'container/SideNav';
|
import SideNav from 'container/SideNav';
|
||||||
import TopNav from 'container/TopNav';
|
import TopNav from 'container/TopNav';
|
||||||
@ -19,26 +19,25 @@ import {
|
|||||||
UPDATE_CONFIGS,
|
UPDATE_CONFIGS,
|
||||||
UPDATE_CURRENT_ERROR,
|
UPDATE_CURRENT_ERROR,
|
||||||
UPDATE_CURRENT_VERSION,
|
UPDATE_CURRENT_VERSION,
|
||||||
UPDATE_FEATURE_FLAG_RESPONSE,
|
|
||||||
UPDATE_LATEST_VERSION,
|
UPDATE_LATEST_VERSION,
|
||||||
UPDATE_LATEST_VERSION_ERROR,
|
UPDATE_LATEST_VERSION_ERROR,
|
||||||
} from 'types/actions/app';
|
} from 'types/actions/app';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import { ChildrenContainer, Layout } from './styles';
|
import { ChildrenContainer, Layout, LayoutContent } from './styles';
|
||||||
import { getRouteKey } from './utils';
|
import { getRouteKey } from './utils';
|
||||||
|
|
||||||
function AppLayout(props: AppLayoutProps): JSX.Element {
|
function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
const { isLoggedIn, user } = useSelector<AppState, AppReducer>(
|
const { isLoggedIn, user } = useSelector<AppState, AppReducer>(
|
||||||
(state) => state.app,
|
(state) => state.app,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const { t } = useTranslation(['titles']);
|
const { t } = useTranslation(['titles']);
|
||||||
|
|
||||||
const [
|
const [
|
||||||
getUserVersionResponse,
|
getUserVersionResponse,
|
||||||
getUserLatestVersionResponse,
|
getUserLatestVersionResponse,
|
||||||
getFeaturesResponse,
|
|
||||||
getDynamicConfigsResponse,
|
getDynamicConfigsResponse,
|
||||||
] = useQueries([
|
] = useQueries([
|
||||||
{
|
{
|
||||||
@ -51,10 +50,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
queryKey: ['getUserLatestVersion', user?.accessJwt],
|
queryKey: ['getUserLatestVersion', user?.accessJwt],
|
||||||
enabled: isLoggedIn,
|
enabled: isLoggedIn,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
queryFn: getFeaturesFlags,
|
|
||||||
queryKey: ['getFeatureFlags', user?.accessJwt],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
queryFn: getDynamicConfigs,
|
queryFn: getDynamicConfigs,
|
||||||
queryKey: ['getDynamicConfigs', user?.accessJwt],
|
queryKey: ['getDynamicConfigs', user?.accessJwt],
|
||||||
@ -62,10 +57,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (getFeaturesResponse.status === 'idle') {
|
|
||||||
getFeaturesResponse.refetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getUserLatestVersionResponse.status === 'idle' && isLoggedIn) {
|
if (getUserLatestVersionResponse.status === 'idle' && isLoggedIn) {
|
||||||
getUserLatestVersionResponse.refetch();
|
getUserLatestVersionResponse.refetch();
|
||||||
}
|
}
|
||||||
@ -73,14 +64,10 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
if (getUserVersionResponse.status === 'idle' && isLoggedIn) {
|
if (getUserVersionResponse.status === 'idle' && isLoggedIn) {
|
||||||
getUserVersionResponse.refetch();
|
getUserVersionResponse.refetch();
|
||||||
}
|
}
|
||||||
if (getFeaturesResponse.status === 'idle') {
|
|
||||||
getFeaturesResponse.refetch();
|
|
||||||
}
|
|
||||||
if (getDynamicConfigsResponse.status === 'idle') {
|
if (getDynamicConfigsResponse.status === 'idle') {
|
||||||
getDynamicConfigsResponse.refetch();
|
getDynamicConfigsResponse.refetch();
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
getFeaturesResponse,
|
|
||||||
getUserLatestVersionResponse,
|
getUserLatestVersionResponse,
|
||||||
getUserVersionResponse,
|
getUserVersionResponse,
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
@ -194,42 +181,17 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
getUserLatestVersionResponse.isFetched,
|
getUserLatestVersionResponse.isFetched,
|
||||||
getUserVersionResponse.isFetched,
|
getUserVersionResponse.isFetched,
|
||||||
getUserLatestVersionResponse.isSuccess,
|
getUserLatestVersionResponse.isSuccess,
|
||||||
getFeaturesResponse.isFetched,
|
|
||||||
getFeaturesResponse.isSuccess,
|
|
||||||
getFeaturesResponse.data,
|
|
||||||
getDynamicConfigsResponse.data,
|
getDynamicConfigsResponse.data,
|
||||||
getDynamicConfigsResponse.isFetched,
|
getDynamicConfigsResponse.isFetched,
|
||||||
getDynamicConfigsResponse.isSuccess,
|
getDynamicConfigsResponse.isSuccess,
|
||||||
notifications,
|
notifications,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
getFeaturesResponse.isFetched &&
|
|
||||||
getFeaturesResponse.isSuccess &&
|
|
||||||
getFeaturesResponse.data &&
|
|
||||||
getFeaturesResponse.data.payload
|
|
||||||
) {
|
|
||||||
dispatch({
|
|
||||||
type: UPDATE_FEATURE_FLAG_RESPONSE,
|
|
||||||
payload: {
|
|
||||||
featureFlag: getFeaturesResponse.data.payload,
|
|
||||||
refetch: getFeaturesResponse.refetch,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
dispatch,
|
|
||||||
getFeaturesResponse.data,
|
|
||||||
getFeaturesResponse.isFetched,
|
|
||||||
getFeaturesResponse.isSuccess,
|
|
||||||
getFeaturesResponse.refetch,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const isToDisplayLayout = isLoggedIn;
|
const isToDisplayLayout = isLoggedIn;
|
||||||
|
|
||||||
const routeKey = useMemo(() => getRouteKey(pathname), [pathname]);
|
const routeKey = useMemo(() => getRouteKey(pathname), [pathname]);
|
||||||
const pageTitle = t(routeKey);
|
const pageTitle = t(routeKey);
|
||||||
|
const renderFullScreen = pathname === ROUTES.GET_STARTED;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
@ -239,13 +201,13 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
|
|
||||||
{isToDisplayLayout && <Header />}
|
{isToDisplayLayout && <Header />}
|
||||||
<Layout>
|
<Layout>
|
||||||
{isToDisplayLayout && <SideNav />}
|
{isToDisplayLayout && !renderFullScreen && <SideNav />}
|
||||||
<Layout.Content>
|
<LayoutContent>
|
||||||
<ChildrenContainer>
|
<ChildrenContainer>
|
||||||
{isToDisplayLayout && <TopNav />}
|
{isToDisplayLayout && !renderFullScreen && <TopNav />}
|
||||||
{children}
|
{children}
|
||||||
</ChildrenContainer>
|
</ChildrenContainer>
|
||||||
</Layout.Content>
|
</LayoutContent>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
@ -7,9 +7,14 @@ export const Layout = styled(LayoutComponent)`
|
|||||||
position: relative;
|
position: relative;
|
||||||
min-height: calc(100vh - 4rem);
|
min-height: calc(100vh - 4rem);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const LayoutContent = styled(LayoutComponent.Content)`
|
||||||
|
overflow-y: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
export const ChildrenContainer = styled.div`
|
export const ChildrenContainer = styled.div`
|
||||||
margin: 0 1rem;
|
margin: 0 1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
8
frontend/src/container/ErrorDetails/config.ts
Normal file
8
frontend/src/container/ErrorDetails/config.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const keyToExclude = [
|
||||||
|
'exceptionStacktrace',
|
||||||
|
'exceptionType',
|
||||||
|
'errorId',
|
||||||
|
'timestamp',
|
||||||
|
'exceptionMessage',
|
||||||
|
'exceptionEscaped',
|
||||||
|
];
|
@ -5,6 +5,7 @@ import { ResizeTable } from 'components/ResizeTable';
|
|||||||
import { getNanoSeconds } from 'container/AllError/utils';
|
import { getNanoSeconds } from 'container/AllError/utils';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { urlKey } from 'pages/ErrorDetails/utils';
|
import { urlKey } from 'pages/ErrorDetails/utils';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
@ -13,12 +14,13 @@ import { useQuery } from 'react-query';
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { PayloadProps as GetByErrorTypeAndServicePayload } from 'types/api/errors/getByErrorTypeAndService';
|
import { PayloadProps as GetByErrorTypeAndServicePayload } from 'types/api/errors/getByErrorTypeAndService';
|
||||||
|
|
||||||
|
import { keyToExclude } from './config';
|
||||||
import { DashedContainer, EditorContainer, EventContainer } from './styles';
|
import { DashedContainer, EditorContainer, EventContainer } from './styles';
|
||||||
|
|
||||||
function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
|
function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
|
||||||
const { idPayload } = props;
|
const { idPayload } = props;
|
||||||
const { t } = useTranslation(['errorDetails', 'common']);
|
const { t } = useTranslation(['errorDetails', 'common']);
|
||||||
const { search } = useLocation();
|
const { search, pathname } = useLocation();
|
||||||
|
|
||||||
const params = useMemo(() => new URLSearchParams(search), [search]);
|
const params = useMemo(() => new URLSearchParams(search), [search]);
|
||||||
|
|
||||||
@ -69,18 +71,6 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const keyToExclude = useMemo(
|
|
||||||
() => [
|
|
||||||
'exceptionStacktrace',
|
|
||||||
'exceptionType',
|
|
||||||
'errorId',
|
|
||||||
'timestamp',
|
|
||||||
'exceptionMessage',
|
|
||||||
'exceptionEscaped',
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
const onClickErrorIdHandler = async (
|
const onClickErrorIdHandler = async (
|
||||||
@ -95,11 +85,13 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
history.replace(
|
const queryParams = {
|
||||||
`${history.location.pathname}?&groupId=${
|
groupId: idPayload.groupID,
|
||||||
idPayload.groupID
|
timestamp: getNanoSeconds(timestamp),
|
||||||
}×tamp=${getNanoSeconds(timestamp)}&errorId=${id}`,
|
errorId: id,
|
||||||
);
|
};
|
||||||
|
|
||||||
|
history.replace(`${pathname}?${createQueryParams(queryParams)}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: t('something_went_wrong'),
|
message: t('something_went_wrong'),
|
||||||
|
@ -44,10 +44,10 @@ function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
|
|||||||
onError: handleError,
|
onError: handleError,
|
||||||
});
|
});
|
||||||
|
|
||||||
const options = useMemo(() => getSelectOptions(data?.payload || []), [data]);
|
const options = useMemo(() => getSelectOptions(data || []), [data]);
|
||||||
|
|
||||||
const handleExportClick = useCallback((): void => {
|
const handleExportClick = useCallback((): void => {
|
||||||
const currentSelectedDashboard = data?.payload?.find(
|
const currentSelectedDashboard = data?.find(
|
||||||
({ uuid }) => uuid === selectedDashboardId,
|
({ uuid }) => uuid === selectedDashboardId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { AlertOutlined, AreaChartOutlined } from '@ant-design/icons';
|
import { AlertOutlined, AreaChartOutlined } from '@ant-design/icons';
|
||||||
import { Button, Modal, Space } from 'antd';
|
import { Button, Modal, Space } from 'antd';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
@ -22,9 +22,9 @@ function ExportPanel({
|
|||||||
|
|
||||||
const onCreateAlertsHandler = useCallback(() => {
|
const onCreateAlertsHandler = useCallback(() => {
|
||||||
history.push(
|
history.push(
|
||||||
`${ROUTES.ALERTS_NEW}?${
|
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||||
queryParamNamesMap.compositeQuery
|
JSON.stringify(query),
|
||||||
}=${encodeURIComponent(JSON.stringify(query))}`,
|
)}`,
|
||||||
);
|
);
|
||||||
}, [query]);
|
}, [query]);
|
||||||
|
|
||||||
|
@ -20,6 +20,10 @@ export function covertIntoDataFormats({
|
|||||||
sourceUnit,
|
sourceUnit,
|
||||||
targetUnit,
|
targetUnit,
|
||||||
}: IUnit): number {
|
}: IUnit): number {
|
||||||
|
if (sourceUnit === undefined || targetUnit === undefined) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.values(BooleanFormats).includes(sourceUnit as BooleanFormats)) {
|
if (Object.values(BooleanFormats).includes(sourceUnit as BooleanFormats)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { Events } from 'constants/events';
|
|||||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||||
import { useChartMutable } from 'hooks/useChartMutable';
|
import { useChartMutable } from 'hooks/useChartMutable';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { isEmpty, isEqual } from 'lodash-es';
|
import { isEmpty, isEqual } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
@ -18,6 +19,7 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { DeleteWidget } from 'store/actions/dashboard/deleteWidget';
|
import { DeleteWidget } from 'store/actions/dashboard/deleteWidget';
|
||||||
@ -61,6 +63,7 @@ function WidgetGraphComponent({
|
|||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
const { t } = useTranslation(['common']);
|
const { t } = useTranslation(['common']);
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
const { graphVisibilityStates: localstoredVisibilityStates } = useMemo(
|
const { graphVisibilityStates: localstoredVisibilityStates } = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -190,11 +193,11 @@ function WidgetGraphComponent({
|
|||||||
message: 'Panel cloned successfully, redirecting to new copy.',
|
message: 'Panel cloned successfully, redirecting to new copy.',
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
const queryParams = {
|
||||||
history.push(
|
graphType: widget?.panelTypes,
|
||||||
`${history.location.pathname}/new?graphType=${widget?.panelTypes}&widgetId=${uuid}`,
|
widgetId: uuid,
|
||||||
);
|
};
|
||||||
}, 1500);
|
history.push(`${pathname}/new?${createQueryParams(queryParams)}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Dropdown, MenuProps, Tooltip, Typography } from 'antd';
|
import { Dropdown, MenuProps, Tooltip, Typography } from 'antd';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
@ -73,7 +73,7 @@ function WidgetHeader({
|
|||||||
history.push(
|
history.push(
|
||||||
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${
|
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${
|
||||||
widget.panelTypes
|
widget.panelTypes
|
||||||
}&${queryParamNamesMap.compositeQuery}=${encodeURIComponent(
|
}&${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||||
JSON.stringify(widget.query),
|
JSON.stringify(widget.query),
|
||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
@ -81,9 +81,9 @@ function WidgetHeader({
|
|||||||
|
|
||||||
const onCreateAlertsHandler = useCallback(() => {
|
const onCreateAlertsHandler = useCallback(() => {
|
||||||
history.push(
|
history.push(
|
||||||
`${ROUTES.ALERTS_NEW}?${
|
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
|
||||||
queryParamNamesMap.compositeQuery
|
JSON.stringify(widget.query),
|
||||||
}=${encodeURIComponent(JSON.stringify(widget.query))}`,
|
)}`,
|
||||||
);
|
);
|
||||||
}, [widget]);
|
}, [widget]);
|
||||||
|
|
||||||
|
@ -123,7 +123,6 @@ function HeaderContainer(): JSX.Element {
|
|||||||
Try Signoz Cloud
|
Try Signoz Cloud
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Config frontendId="tooltip" />
|
<Config frontendId="tooltip" />
|
||||||
|
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
|
@ -5,7 +5,7 @@ import { ColumnsType } from 'antd/lib/table';
|
|||||||
import saveAlertApi from 'api/alerts/save';
|
import saveAlertApi from 'api/alerts/save';
|
||||||
import { ResizeTable } from 'components/ResizeTable';
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import useInterval from 'hooks/useInterval';
|
import useInterval from 'hooks/useInterval';
|
||||||
@ -76,7 +76,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
|
|
||||||
history.push(
|
history.push(
|
||||||
`${ROUTES.EDIT_ALERTS}?ruleId=${record.id.toString()}&${
|
`${ROUTES.EDIT_ALERTS}?ruleId=${record.id.toString()}&${
|
||||||
queryParamNamesMap.compositeQuery
|
QueryParams.compositeQuery
|
||||||
}=${encodeURIComponent(JSON.stringify(compositeQuery))}`,
|
}=${encodeURIComponent(JSON.stringify(compositeQuery))}`,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -136,6 +136,8 @@ export function OptionsValueResolution(
|
|||||||
options: uniqWith(
|
options: uniqWith(
|
||||||
map(
|
map(
|
||||||
flattenDeep(
|
flattenDeep(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
map(searchData, (searchItem) => searchItem.data.tags).filter(Boolean),
|
map(searchData, (searchItem) => searchItem.data.tags).filter(Boolean),
|
||||||
),
|
),
|
||||||
(tag) => ({ name: tag }),
|
(tag) => ({ name: tag }),
|
||||||
|
@ -10,7 +10,11 @@ import AppActions from 'types/actions';
|
|||||||
import { Data } from '../index';
|
import { Data } from '../index';
|
||||||
import { TableLinkText } from './styles';
|
import { TableLinkText } from './styles';
|
||||||
|
|
||||||
function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
function DeleteButton({
|
||||||
|
deleteDashboard,
|
||||||
|
id,
|
||||||
|
refetchDashboardList,
|
||||||
|
}: DeleteButtonProps): JSX.Element {
|
||||||
const [modal, contextHolder] = Modal.useModal();
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
|
|
||||||
const openConfirmationDialog = useCallback((): void => {
|
const openConfirmationDialog = useCallback((): void => {
|
||||||
@ -20,13 +24,14 @@ function DeleteButton({ deleteDashboard, id }: DeleteButtonProps): JSX.Element {
|
|||||||
onOk() {
|
onOk() {
|
||||||
deleteDashboard({
|
deleteDashboard({
|
||||||
uuid: id,
|
uuid: id,
|
||||||
|
refetch: refetchDashboardList,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
okText: 'Delete',
|
okText: 'Delete',
|
||||||
okButtonProps: { danger: true },
|
okButtonProps: { danger: true },
|
||||||
centered: true,
|
centered: true,
|
||||||
});
|
});
|
||||||
}, [id, modal, deleteDashboard]);
|
}, [modal, deleteDashboard, id, refetchDashboardList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -51,13 +56,22 @@ const mapDispatchToProps = (
|
|||||||
deleteDashboard: bindActionCreators(DeleteDashboard, dispatch),
|
deleteDashboard: bindActionCreators(DeleteDashboard, dispatch),
|
||||||
});
|
});
|
||||||
|
|
||||||
type DeleteButtonProps = Data & DispatchProps;
|
export type DeleteButtonProps = Data & DispatchProps;
|
||||||
|
|
||||||
const WrapperDeleteButton = connect(null, mapDispatchToProps)(DeleteButton);
|
const WrapperDeleteButton = connect(null, mapDispatchToProps)(DeleteButton);
|
||||||
|
|
||||||
// This is to avoid the type collision
|
// This is to avoid the type collision
|
||||||
function Wrapper(props: Data): JSX.Element {
|
function Wrapper(props: Data): JSX.Element {
|
||||||
const { createdBy, description, id, key, lastUpdatedTime, name, tags } = props;
|
const {
|
||||||
|
createdBy,
|
||||||
|
description,
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
refetchDashboardList,
|
||||||
|
lastUpdatedTime,
|
||||||
|
name,
|
||||||
|
tags,
|
||||||
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WrapperDeleteButton
|
<WrapperDeleteButton
|
||||||
@ -69,6 +83,7 @@ function Wrapper(props: Data): JSX.Element {
|
|||||||
lastUpdatedTime,
|
lastUpdatedTime,
|
||||||
name,
|
name,
|
||||||
tags,
|
tags,
|
||||||
|
refetchDashboardList,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -14,6 +14,7 @@ import { ResizeTable } from 'components/ResizeTable';
|
|||||||
import TextToolTip from 'components/TextToolTip';
|
import TextToolTip from 'components/TextToolTip';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import SearchFilter from 'container/ListOfDashboard/SearchFilter';
|
import SearchFilter from 'container/ListOfDashboard/SearchFilter';
|
||||||
|
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
||||||
import useComponentPermission from 'hooks/useComponentPermission';
|
import useComponentPermission from 'hooks/useComponentPermission';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import {
|
import {
|
||||||
@ -25,6 +26,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { UseQueryResult } from 'react-query';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { generatePath } from 'react-router-dom';
|
import { generatePath } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
@ -32,20 +34,24 @@ import AppActions from 'types/actions';
|
|||||||
import { GET_ALL_DASHBOARD_SUCCESS } from 'types/actions/dashboard';
|
import { GET_ALL_DASHBOARD_SUCCESS } from 'types/actions/dashboard';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import DashboardReducer from 'types/reducer/dashboards';
|
|
||||||
|
|
||||||
import ImportJSON from './ImportJSON';
|
import ImportJSON from './ImportJSON';
|
||||||
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
|
import { ButtonContainer, NewDashboardButton, TableContainer } from './styles';
|
||||||
import Createdby from './TableComponents/CreatedBy';
|
import Createdby from './TableComponents/CreatedBy';
|
||||||
import DateComponent from './TableComponents/Date';
|
import DateComponent from './TableComponents/Date';
|
||||||
import DeleteButton from './TableComponents/DeleteButton';
|
import DeleteButton, {
|
||||||
|
DeleteButtonProps,
|
||||||
|
} from './TableComponents/DeleteButton';
|
||||||
import Name from './TableComponents/Name';
|
import Name from './TableComponents/Name';
|
||||||
import Tags from './TableComponents/Tags';
|
import Tags from './TableComponents/Tags';
|
||||||
|
|
||||||
function ListOfAllDashboard(): JSX.Element {
|
function ListOfAllDashboard(): JSX.Element {
|
||||||
const { dashboards, loading } = useSelector<AppState, DashboardReducer>(
|
const {
|
||||||
(state) => state.dashboards,
|
data: dashboardListResponse = [],
|
||||||
);
|
isLoading: isDashboardListLoading,
|
||||||
|
refetch: refetchDashboardList,
|
||||||
|
} = useGetAllDashboard();
|
||||||
|
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
@ -66,8 +72,10 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
const [filteredDashboards, setFilteredDashboards] = useState<Dashboard[]>();
|
const [filteredDashboards, setFilteredDashboards] = useState<Dashboard[]>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFilteredDashboards(dashboards);
|
if (dashboardListResponse.length) {
|
||||||
}, [dashboards]);
|
setFilteredDashboards(dashboardListResponse);
|
||||||
|
}
|
||||||
|
}, [dashboardListResponse]);
|
||||||
|
|
||||||
const [newDashboardState, setNewDashboardState] = useState({
|
const [newDashboardState, setNewDashboardState] = useState({
|
||||||
loading: false,
|
loading: false,
|
||||||
@ -125,22 +133,43 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
title: 'Action',
|
title: 'Action',
|
||||||
dataIndex: '',
|
dataIndex: '',
|
||||||
width: 40,
|
width: 40,
|
||||||
render: DeleteButton,
|
render: ({
|
||||||
|
createdBy,
|
||||||
|
description,
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
lastUpdatedTime,
|
||||||
|
name,
|
||||||
|
tags,
|
||||||
|
}: DeleteButtonProps) => (
|
||||||
|
<DeleteButton
|
||||||
|
description={description}
|
||||||
|
id={id}
|
||||||
|
key={key}
|
||||||
|
lastUpdatedTime={lastUpdatedTime}
|
||||||
|
name={name}
|
||||||
|
tags={tags}
|
||||||
|
createdBy={createdBy}
|
||||||
|
refetchDashboardList={refetchDashboardList}
|
||||||
|
/>
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return tableColumns;
|
return tableColumns;
|
||||||
}, [action]);
|
}, [action, refetchDashboardList]);
|
||||||
|
|
||||||
const data: Data[] = (filteredDashboards || dashboards).map((e) => ({
|
const data: Data[] =
|
||||||
createdBy: e.created_at,
|
filteredDashboards?.map((e) => ({
|
||||||
description: e.data.description || '',
|
createdBy: e.created_at,
|
||||||
id: e.uuid,
|
description: e.data.description || '',
|
||||||
lastUpdatedTime: e.updated_at,
|
id: e.uuid,
|
||||||
name: e.data.title,
|
lastUpdatedTime: e.updated_at,
|
||||||
tags: e.data.tags || [],
|
name: e.data.title,
|
||||||
key: e.uuid,
|
tags: e.data.tags || [],
|
||||||
}));
|
key: e.uuid,
|
||||||
|
refetchDashboardList,
|
||||||
|
})) || [];
|
||||||
|
|
||||||
const onNewDashboardHandler = useCallback(async () => {
|
const onNewDashboardHandler = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@ -209,7 +238,7 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: t('create_dashboard').toString(),
|
key: t('create_dashboard').toString(),
|
||||||
label: t('create_dashboard'),
|
label: t('create_dashboard'),
|
||||||
disabled: loading,
|
disabled: isDashboardListLoading,
|
||||||
onClick: onNewDashboardHandler,
|
onClick: onNewDashboardHandler,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -228,7 +257,7 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return menuItems;
|
return menuItems;
|
||||||
}, [createNewDashboard, loading, onNewDashboardHandler, t]);
|
}, [createNewDashboard, isDashboardListLoading, onNewDashboardHandler, t]);
|
||||||
|
|
||||||
const menu: MenuProps = useMemo(
|
const menu: MenuProps = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@ -250,7 +279,11 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{newDashboard && (
|
{newDashboard && (
|
||||||
<Dropdown disabled={loading} trigger={['click']} menu={menu}>
|
<Dropdown
|
||||||
|
disabled={isDashboardListLoading}
|
||||||
|
trigger={['click']}
|
||||||
|
menu={menu}
|
||||||
|
>
|
||||||
<NewDashboardButton
|
<NewDashboardButton
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -266,7 +299,7 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
),
|
),
|
||||||
[
|
[
|
||||||
newDashboard,
|
newDashboard,
|
||||||
loading,
|
isDashboardListLoading,
|
||||||
menu,
|
menu,
|
||||||
newDashboardState.loading,
|
newDashboardState.loading,
|
||||||
newDashboardState.error,
|
newDashboardState.error,
|
||||||
@ -278,9 +311,9 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
<Card>
|
<Card>
|
||||||
{GetHeader}
|
{GetHeader}
|
||||||
|
|
||||||
{!loading && (
|
{!isDashboardListLoading && (
|
||||||
<SearchFilter
|
<SearchFilter
|
||||||
searchData={dashboards}
|
searchData={dashboardListResponse}
|
||||||
filterDashboards={setFilteredDashboards}
|
filterDashboards={setFilteredDashboards}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -300,7 +333,7 @@ function ListOfAllDashboard(): JSX.Element {
|
|||||||
showHeader
|
showHeader
|
||||||
bordered
|
bordered
|
||||||
sticky
|
sticky
|
||||||
loading={loading}
|
loading={isDashboardListLoading}
|
||||||
dataSource={data}
|
dataSource={data}
|
||||||
showSorterTooltip
|
showSorterTooltip
|
||||||
/>
|
/>
|
||||||
@ -317,6 +350,7 @@ export interface Data {
|
|||||||
createdBy: string;
|
createdBy: string;
|
||||||
lastUpdatedTime: string;
|
lastUpdatedTime: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
refetchDashboardList: UseQueryResult['refetch'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ListOfAllDashboard;
|
export default ListOfAllDashboard;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
import { QueryParams } from 'constants/query';
|
||||||
import {
|
import {
|
||||||
initialQueryBuilderFormValuesMap,
|
initialQueryBuilderFormValuesMap,
|
||||||
PANEL_TYPES,
|
PANEL_TYPES,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
@ -17,7 +17,7 @@ import { constructCompositeQuery } from '../constants';
|
|||||||
function BackButton(): JSX.Element {
|
function BackButton(): JSX.Element {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const { updateAllQueriesOperators, resetQuery } = useQueryBuilder();
|
const { updateAllQueriesOperators } = useQueryBuilder();
|
||||||
|
|
||||||
const compositeQuery = useGetCompositeQueryParam();
|
const compositeQuery = useGetCompositeQueryParam();
|
||||||
|
|
||||||
@ -36,14 +36,12 @@ function BackButton(): JSX.Element {
|
|||||||
DataSource.LOGS,
|
DataSource.LOGS,
|
||||||
);
|
);
|
||||||
|
|
||||||
resetQuery(updatedQuery);
|
|
||||||
|
|
||||||
const JSONCompositeQuery = encodeURIComponent(JSON.stringify(updatedQuery));
|
const JSONCompositeQuery = encodeURIComponent(JSON.stringify(updatedQuery));
|
||||||
|
|
||||||
const path = `${ROUTES.LOGS_EXPLORER}?${queryParamNamesMap.compositeQuery}=${JSONCompositeQuery}`;
|
const path = `${ROUTES.LOGS_EXPLORER}?${QueryParams.compositeQuery}=${JSONCompositeQuery}`;
|
||||||
|
|
||||||
history.push(path);
|
history.push(path);
|
||||||
}, [history, compositeQuery, resetQuery, updateAllQueriesOperators]);
|
}, [history, compositeQuery, updateAllQueriesOperators]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button icon={<ArrowLeftOutlined />} onClick={handleBack}>
|
<Button icon={<ArrowLeftOutlined />} onClick={handleBack}>
|
||||||
|
@ -2,6 +2,7 @@ import { Card, Typography } from 'antd';
|
|||||||
import ListLogView from 'components/Logs/ListLogView';
|
import ListLogView from 'components/Logs/ListLogView';
|
||||||
import RawLogView from 'components/Logs/RawLogView';
|
import RawLogView from 'components/Logs/RawLogView';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
|
import { CARD_BODY_STYLE } from 'constants/card';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import { OptionFormatTypes } from 'constants/optionsFormatTypes';
|
import { OptionFormatTypes } from 'constants/optionsFormatTypes';
|
||||||
import InfinityTableView from 'container/LogsExplorerList/InfinityTableView';
|
import InfinityTableView from 'container/LogsExplorerList/InfinityTableView';
|
||||||
@ -9,7 +10,6 @@ import { InfinityWrapperStyled } from 'container/LogsExplorerList/styles';
|
|||||||
import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils';
|
import { convertKeysToColumnFields } from 'container/LogsExplorerList/utils';
|
||||||
import { Heading } from 'container/LogsTable/styles';
|
import { Heading } from 'container/LogsTable/styles';
|
||||||
import { useOptionsMenu } from 'container/OptionsMenu';
|
import { useOptionsMenu } from 'container/OptionsMenu';
|
||||||
import { contentStyle } from 'container/Trace/Search/config';
|
|
||||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||||
import useFontFaceObserver from 'hooks/useFontObserver';
|
import useFontFaceObserver from 'hooks/useFontObserver';
|
||||||
import { useEventSource } from 'providers/EventSource';
|
import { useEventSource } from 'providers/EventSource';
|
||||||
@ -100,7 +100,7 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element {
|
|||||||
|
|
||||||
{logs.length !== 0 && (
|
{logs.length !== 0 && (
|
||||||
<InfinityWrapperStyled>
|
<InfinityWrapperStyled>
|
||||||
{options.format === 'table' ? (
|
{options.format === OptionFormatTypes.TABLE ? (
|
||||||
<InfinityTableView
|
<InfinityTableView
|
||||||
ref={ref}
|
ref={ref}
|
||||||
isLoading={false}
|
isLoading={false}
|
||||||
@ -112,10 +112,9 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Card style={{ width: '100%' }} bodyStyle={{ ...contentStyle }}>
|
<Card style={{ width: '100%' }} bodyStyle={CARD_BODY_STYLE}>
|
||||||
<Virtuoso
|
<Virtuoso
|
||||||
ref={ref}
|
ref={ref}
|
||||||
useWindowScroll
|
|
||||||
data={logs}
|
data={logs}
|
||||||
totalCount={logs.length}
|
totalCount={logs.length}
|
||||||
itemContent={getItemContent}
|
itemContent={getItemContent}
|
||||||
|
@ -8,7 +8,7 @@ import dayjs from 'dayjs';
|
|||||||
import { Pagination } from 'hooks/queryPagination';
|
import { Pagination } from 'hooks/queryPagination';
|
||||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||||
import { OrderPreferenceItems } from 'pages/Logs/config';
|
import { OrderPreferenceItems } from 'pages/Logs/config';
|
||||||
import * as Papa from 'papaparse';
|
import { unparse } from 'papaparse';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
@ -121,7 +121,7 @@ function LogControls(): JSX.Element | null {
|
|||||||
}, [flattenLogData]);
|
}, [flattenLogData]);
|
||||||
|
|
||||||
const downloadCsvFile = useCallback((): void => {
|
const downloadCsvFile = useCallback((): void => {
|
||||||
const csv = Papa.unparse(flattenLogData);
|
const csv = unparse(flattenLogData);
|
||||||
const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
|
||||||
const csvUrl = URL.createObjectURL(csvBlob);
|
const csvUrl = URL.createObjectURL(csvBlob);
|
||||||
const downloadLink = document.createElement('a');
|
const downloadLink = document.createElement('a');
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import { Button, Form, Input, Space, Tooltip, Typography } from 'antd';
|
import { Button, Form, Input, Space, Tooltip, Typography } from 'antd';
|
||||||
|
import setLocalStorageApi from 'api/browser/localstorage/set';
|
||||||
import getUserVersion from 'api/user/getVersion';
|
import getUserVersion from 'api/user/getVersion';
|
||||||
import loginApi from 'api/user/login';
|
import loginApi from 'api/user/login';
|
||||||
import loginPrecheckApi from 'api/user/loginPrecheck';
|
import loginPrecheckApi from 'api/user/loginPrecheck';
|
||||||
import afterLogin from 'AppRoutes/utils';
|
import afterLogin from 'AppRoutes/utils';
|
||||||
|
import { FeatureKeys } from 'constants/features';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
|
import useFeatureFlag from 'hooks/useFeatureFlag';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
@ -38,6 +42,9 @@ function Login({
|
|||||||
const { t } = useTranslation(['login']);
|
const { t } = useTranslation(['login']);
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
const isChatSupportEnabled: boolean | undefined = useFeatureFlag(
|
||||||
|
FeatureKeys.CHAT_SUPPORT,
|
||||||
|
)?.active;
|
||||||
|
|
||||||
const [precheckResult, setPrecheckResult] = useState<PrecheckResultType>({
|
const [precheckResult, setPrecheckResult] = useState<PrecheckResultType>({
|
||||||
sso: false,
|
sso: false,
|
||||||
@ -158,11 +165,21 @@ function Login({
|
|||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
if (response.statusCode === 200) {
|
if (response.statusCode === 200) {
|
||||||
await afterLogin(
|
const user = await afterLogin(
|
||||||
response.payload.userId,
|
response.payload.userId,
|
||||||
response.payload.accessJwt,
|
response.payload.accessJwt,
|
||||||
response.payload.refreshJwt,
|
response.payload.refreshJwt,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
setLocalStorageApi(LOCALSTORAGE.LOGGED_IN_USER_NAME, user.payload?.name);
|
||||||
|
setLocalStorageApi(LOCALSTORAGE.LOGGED_IN_USER_EMAIL, user.payload?.email);
|
||||||
|
setLocalStorageApi(
|
||||||
|
LOCALSTORAGE.CHAT_SUPPORT,
|
||||||
|
(isChatSupportEnabled || '').toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
history.push(ROUTES.APPLICATION);
|
history.push(ROUTES.APPLICATION);
|
||||||
} else {
|
} else {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
|
@ -6,7 +6,7 @@ import LogsContextList from 'container/LogsContextList';
|
|||||||
import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
|
import { FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
|
||||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import { memo, useCallback, useMemo, useState } from 'react';
|
import { memo, useCallback, useState } from 'react';
|
||||||
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
import { EditButton, LogContainer, TitleWrapper } from './styles';
|
import { EditButton, LogContainer, TitleWrapper } from './styles';
|
||||||
@ -57,11 +57,6 @@ function LogsExplorerContext({
|
|||||||
[contextQuery, filters],
|
[contextQuery, filters],
|
||||||
);
|
);
|
||||||
|
|
||||||
const contextListParams = useMemo(
|
|
||||||
() => ({ log, isEdit, filters, query: contextQuery }),
|
|
||||||
[isEdit, log, filters, contextQuery],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
centered
|
centered
|
||||||
@ -93,8 +88,10 @@ function LogsExplorerContext({
|
|||||||
)}
|
)}
|
||||||
<LogsContextList
|
<LogsContextList
|
||||||
order={FILTERS.ASC}
|
order={FILTERS.ASC}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
filters={filters}
|
||||||
{...contextListParams}
|
isEdit={isEdit}
|
||||||
|
log={log}
|
||||||
|
query={contextQuery}
|
||||||
/>
|
/>
|
||||||
<LogContainer>
|
<LogContainer>
|
||||||
<RawLogView
|
<RawLogView
|
||||||
@ -107,8 +104,10 @@ function LogsExplorerContext({
|
|||||||
</LogContainer>
|
</LogContainer>
|
||||||
<LogsContextList
|
<LogsContextList
|
||||||
order={FILTERS.DESC}
|
order={FILTERS.DESC}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
filters={filters}
|
||||||
{...contextListParams}
|
isEdit={isEdit}
|
||||||
|
log={log}
|
||||||
|
query={contextQuery}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@ -5,6 +5,7 @@ import { LOCALSTORAGE } from 'constants/localStorage';
|
|||||||
import LogsExplorerContext from 'container/LogsExplorerContext';
|
import LogsExplorerContext from 'container/LogsExplorerContext';
|
||||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||||
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import useDragColumns from 'hooks/useDragColumns';
|
import useDragColumns from 'hooks/useDragColumns';
|
||||||
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
|
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
|
||||||
import {
|
import {
|
||||||
@ -40,8 +41,11 @@ const CustomTableRow: TableComponents<ILog>['TableRow'] = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { isHighlighted } = useCopyLogLink(props.item.id);
|
const { isHighlighted } = useCopyLogLink(props.item.id);
|
||||||
|
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRowStyled
|
<TableRowStyled
|
||||||
|
$isDarkMode={isDarkMode}
|
||||||
$isActiveLog={isHighlighted}
|
$isActiveLog={isHighlighted}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...props}
|
{...props}
|
||||||
@ -77,6 +81,8 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
|||||||
Record<string, unknown>
|
Record<string, unknown>
|
||||||
>(LOCALSTORAGE.LOGS_LIST_COLUMNS);
|
>(LOCALSTORAGE.LOGS_LIST_COLUMNS);
|
||||||
|
|
||||||
|
const isDarkMode = useIsDarkMode();
|
||||||
|
|
||||||
const tableColumns = useMemo(
|
const tableColumns = useMemo(
|
||||||
() => getDraggedColumns<Record<string, unknown>>(columns, draggedColumns),
|
() => getDraggedColumns<Record<string, unknown>>(columns, draggedColumns),
|
||||||
[columns, draggedColumns],
|
[columns, draggedColumns],
|
||||||
@ -109,14 +115,18 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
|||||||
const props = elementWithChildren.props as Record<string, unknown>;
|
const props = elementWithChildren.props as Record<string, unknown>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableCellStyled key={column.key}>
|
<TableCellStyled
|
||||||
|
$isDragColumn={false}
|
||||||
|
$isDarkMode={isDarkMode}
|
||||||
|
key={column.key}
|
||||||
|
>
|
||||||
{cloneElement(children, props)}
|
{cloneElement(children, props)}
|
||||||
</TableCellStyled>
|
</TableCellStyled>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[tableColumns],
|
[tableColumns, isDarkMode],
|
||||||
);
|
);
|
||||||
|
|
||||||
const tableHeader = useCallback(
|
const tableHeader = useCallback(
|
||||||
@ -127,7 +137,8 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TableHeaderCellStyled
|
<TableHeaderCellStyled
|
||||||
isDragColumn={isDragColumn}
|
$isDarkMode={isDarkMode}
|
||||||
|
$isDragColumn={isDragColumn}
|
||||||
key={column.key}
|
key={column.key}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...(isDragColumn && { className: 'dragHandler' })}
|
{...(isDragColumn && { className: 'dragHandler' })}
|
||||||
@ -138,13 +149,12 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
|||||||
})}
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
),
|
),
|
||||||
[tableColumns],
|
[tableColumns, isDarkMode],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableVirtuoso
|
<TableVirtuoso
|
||||||
useWindowScroll
|
|
||||||
ref={ref}
|
ref={ref}
|
||||||
style={infinityDefaultStyles}
|
style={infinityDefaultStyles}
|
||||||
data={dataSource}
|
data={dataSource}
|
||||||
|
@ -3,7 +3,8 @@ import styled from 'styled-components';
|
|||||||
import { getActiveLogBackground } from 'utils/logs';
|
import { getActiveLogBackground } from 'utils/logs';
|
||||||
|
|
||||||
interface TableHeaderCellStyledProps {
|
interface TableHeaderCellStyledProps {
|
||||||
isDragColumn: boolean;
|
$isDragColumn: boolean;
|
||||||
|
$isDarkMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TableStyled = styled.table`
|
export const TableStyled = styled.table`
|
||||||
@ -16,15 +17,20 @@ export const TableStyled = styled.table`
|
|||||||
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TableCellStyled = styled.td`
|
export const TableCellStyled = styled.td<TableHeaderCellStyledProps>`
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
||||||
border-top: 1px solid rgba(253, 253, 253, 0.12);
|
border-top: 1px solid rgba(253, 253, 253, 0.12);
|
||||||
background-color: ${themeColors.lightBlack};
|
background-color: ${(props): string =>
|
||||||
|
props.$isDarkMode ? themeColors.black : themeColors.whiteCream};
|
||||||
|
|
||||||
|
color: ${(props): string =>
|
||||||
|
props.$isDarkMode ? themeColors.white : themeColors.bckgGrey};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TableRowStyled = styled.tr<{
|
export const TableRowStyled = styled.tr<{
|
||||||
$isActiveLog: boolean;
|
$isActiveLog: boolean;
|
||||||
|
$isDarkMode: boolean;
|
||||||
}>`
|
}>`
|
||||||
td {
|
td {
|
||||||
${({ $isActiveLog }): string => getActiveLogBackground($isActiveLog)}
|
${({ $isActiveLog }): string => getActiveLogBackground($isActiveLog)}
|
||||||
@ -32,10 +38,12 @@ export const TableRowStyled = styled.tr<{
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
${TableCellStyled} {
|
${TableCellStyled} {
|
||||||
${({ $isActiveLog }): string =>
|
${({ $isActiveLog, $isDarkMode }): string =>
|
||||||
$isActiveLog
|
$isActiveLog
|
||||||
? getActiveLogBackground()
|
? getActiveLogBackground()
|
||||||
: `background-color: ${themeColors.bckgGrey};`}
|
: `background-color: ${
|
||||||
|
!$isDarkMode ? themeColors.lightgrey : themeColors.bckgGrey
|
||||||
|
};`}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@ -43,8 +51,13 @@ export const TableRowStyled = styled.tr<{
|
|||||||
export const TableHeaderCellStyled = styled.th<TableHeaderCellStyledProps>`
|
export const TableHeaderCellStyled = styled.th<TableHeaderCellStyledProps>`
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
border-inline-end: 1px solid rgba(253, 253, 253, 0.12);
|
||||||
background-color: ${themeColors.bckgGrey};
|
background-color: ${(props): string =>
|
||||||
${({ isDragColumn }): string => (isDragColumn ? 'cursor: col-resize;' : '')}
|
!props.$isDarkMode ? themeColors.whiteCream : themeColors.bckgGrey};
|
||||||
|
|
||||||
|
${({ $isDragColumn }): string => ($isDragColumn ? 'cursor: col-resize;' : '')}
|
||||||
|
|
||||||
|
color: ${(props): string =>
|
||||||
|
props.$isDarkMode ? themeColors.white : themeColors.bckgGrey};
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-start-start-radius: 2px;
|
border-start-start-radius: 2px;
|
||||||
|
@ -3,11 +3,11 @@ import { Card, Typography } from 'antd';
|
|||||||
import ListLogView from 'components/Logs/ListLogView';
|
import ListLogView from 'components/Logs/ListLogView';
|
||||||
import RawLogView from 'components/Logs/RawLogView';
|
import RawLogView from 'components/Logs/RawLogView';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
|
import { CARD_BODY_STYLE } from 'constants/card';
|
||||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
import ExplorerControlPanel from 'container/ExplorerControlPanel';
|
import ExplorerControlPanel from 'container/ExplorerControlPanel';
|
||||||
import { Heading } from 'container/LogsTable/styles';
|
import { Heading } from 'container/LogsTable/styles';
|
||||||
import { useOptionsMenu } from 'container/OptionsMenu';
|
import { useOptionsMenu } from 'container/OptionsMenu';
|
||||||
import { contentStyle } from 'container/Trace/Search/config';
|
|
||||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import useFontFaceObserver from 'hooks/useFontObserver';
|
import useFontFaceObserver from 'hooks/useFontObserver';
|
||||||
@ -116,10 +116,9 @@ function LogsExplorerList({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card style={{ width: '100%' }} bodyStyle={{ ...contentStyle }}>
|
<Card style={{ width: '100%' }} bodyStyle={CARD_BODY_STYLE}>
|
||||||
<Virtuoso
|
<Virtuoso
|
||||||
ref={ref}
|
ref={ref}
|
||||||
useWindowScroll
|
|
||||||
data={logs}
|
data={logs}
|
||||||
endReached={onEndReached}
|
endReached={onEndReached}
|
||||||
totalCount={logs.length}
|
totalCount={logs.length}
|
||||||
@ -128,15 +127,7 @@ function LogsExplorerList({
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}, [
|
}, [isLoading, options, logs, onEndReached, getItemContent, selectedFields]);
|
||||||
isLoading,
|
|
||||||
logs,
|
|
||||||
options.format,
|
|
||||||
options.maxLines,
|
|
||||||
onEndReached,
|
|
||||||
getItemContent,
|
|
||||||
selectedFields,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const InfinityWrapperStyled = styled.div`
|
export const InfinityWrapperStyled = styled.div`
|
||||||
min-height: 40rem;
|
height: 40rem !important;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
`;
|
`;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Tabs, TabsProps } from 'antd';
|
import { Tabs, TabsProps } from 'antd';
|
||||||
import TabLabel from 'components/TabLabel';
|
import TabLabel from 'components/TabLabel';
|
||||||
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
||||||
|
import { QueryParams } from 'constants/query';
|
||||||
import {
|
import {
|
||||||
initialFilters,
|
initialFilters,
|
||||||
initialQueriesMap,
|
initialQueriesMap,
|
||||||
initialQueryBuilderFormValues,
|
initialQueryBuilderFormValues,
|
||||||
PANEL_TYPES,
|
PANEL_TYPES,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
|
||||||
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
|
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
|
||||||
import ExportPanel from 'container/ExportPanel';
|
import ExportPanel from 'container/ExportPanel';
|
||||||
import GoToTop from 'container/GoToTop';
|
import GoToTop from 'container/GoToTop';
|
||||||
@ -50,7 +50,7 @@ function LogsExplorerViews(): JSX.Element {
|
|||||||
|
|
||||||
const { activeLogId, timeRange, onTimeRangeChange } = useCopyLogLink();
|
const { activeLogId, timeRange, onTimeRangeChange } = useCopyLogLink();
|
||||||
const { queryData: pageSize } = useUrlQueryData(
|
const { queryData: pageSize } = useUrlQueryData(
|
||||||
queryParamNamesMap.pageSize,
|
QueryParams.pageSize,
|
||||||
DEFAULT_PER_PAGE_VALUE,
|
DEFAULT_PER_PAGE_VALUE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import AddToSelectedFields from 'api/logs/AddToSelectedField';
|
import { message } from 'antd';
|
||||||
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
|
import addToSelectedFields from 'api/logs/AddToSelectedField';
|
||||||
|
import removeSelectedField from 'api/logs/RemoveFromSelectedField';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import {
|
import {
|
||||||
UPDATE_INTERESTING_FIELDS,
|
UPDATE_INTERESTING_FIELDS,
|
||||||
UPDATE_SELECTED_FIELDS,
|
UPDATE_SELECTED_FIELDS,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
|
import { ErrorResponse } from 'types/api';
|
||||||
|
|
||||||
import { RESTRICTED_SELECTED_FIELDS } from './config';
|
import { RESTRICTED_SELECTED_FIELDS } from './config';
|
||||||
import { OnHandleAddInterestProps, OnHandleRemoveInterestProps } from './types';
|
import { OnHandleAddInterestProps, OnHandleRemoveInterestProps } from './types';
|
||||||
@ -24,30 +26,34 @@ export const onHandleAddInterest = async ({
|
|||||||
return [...prevState];
|
return [...prevState];
|
||||||
});
|
});
|
||||||
|
|
||||||
await AddToSelectedFields({
|
try {
|
||||||
...fieldData,
|
await addToSelectedFields({
|
||||||
selected: true,
|
...fieldData,
|
||||||
});
|
selected: true,
|
||||||
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_INTERESTING_FIELDS,
|
type: UPDATE_INTERESTING_FIELDS,
|
||||||
payload: {
|
payload: {
|
||||||
field: interesting.filter((e) => e.name !== fieldData.name),
|
field: interesting.filter((e) => e.name !== fieldData.name),
|
||||||
type: 'selected',
|
type: 'selected',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_SELECTED_FIELDS,
|
type: UPDATE_SELECTED_FIELDS,
|
||||||
payload: {
|
payload: {
|
||||||
field: [...selected, fieldData],
|
field: [...selected, fieldData],
|
||||||
type: 'selected',
|
type: 'selected',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} catch (errRes) {
|
||||||
setInterestingFieldLoading(
|
message.error((errRes as ErrorResponse)?.error);
|
||||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
} finally {
|
||||||
);
|
setInterestingFieldLoading(
|
||||||
|
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onHandleRemoveInterest = async ({
|
export const onHandleRemoveInterest = async ({
|
||||||
@ -67,28 +73,31 @@ export const onHandleRemoveInterest = async ({
|
|||||||
return [...prevState];
|
return [...prevState];
|
||||||
});
|
});
|
||||||
|
|
||||||
await RemoveSelectedField({
|
try {
|
||||||
...fieldData,
|
await removeSelectedField({
|
||||||
selected: false,
|
...fieldData,
|
||||||
});
|
selected: false,
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_SELECTED_FIELDS,
|
||||||
|
payload: {
|
||||||
|
field: selected.filter((e) => e.name !== fieldData.name),
|
||||||
|
type: 'selected',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: UPDATE_SELECTED_FIELDS,
|
type: UPDATE_INTERESTING_FIELDS,
|
||||||
payload: {
|
payload: {
|
||||||
field: selected.filter((e) => e.name !== fieldData.name),
|
field: [...interesting, fieldData],
|
||||||
type: 'selected',
|
type: 'interesting',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} catch (errRes) {
|
||||||
dispatch({
|
message.error((errRes as ErrorResponse)?.error);
|
||||||
type: UPDATE_INTERESTING_FIELDS,
|
} finally {
|
||||||
payload: {
|
setSelectedFieldLoading(
|
||||||
field: [...interesting, fieldData],
|
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
||||||
type: 'interesting',
|
);
|
||||||
},
|
}
|
||||||
});
|
|
||||||
|
|
||||||
setSelectedFieldLoading(
|
|
||||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import './logsTable.styles.scss';
|
||||||
|
|
||||||
import { Card, Typography } from 'antd';
|
import { Card, Typography } from 'antd';
|
||||||
// components
|
// components
|
||||||
import ListLogView from 'components/Logs/ListLogView';
|
import ListLogView from 'components/Logs/ListLogView';
|
||||||
import RawLogView from 'components/Logs/RawLogView';
|
import RawLogView from 'components/Logs/RawLogView';
|
||||||
import LogsTableView from 'components/Logs/TableView';
|
import LogsTableView from 'components/Logs/TableView';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { contentStyle } from 'container/Trace/Search/config';
|
import { CARD_BODY_STYLE } from 'constants/card';
|
||||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||||
import useFontFaceObserver from 'hooks/useFontObserver';
|
import useFontFaceObserver from 'hooks/useFontObserver';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -85,12 +87,8 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card bodyStyle={contentStyle}>
|
<Card className="logs-card" bodyStyle={CARD_BODY_STYLE}>
|
||||||
<Virtuoso
|
<Virtuoso totalCount={logs.length} itemContent={getItemContent} />
|
||||||
useWindowScroll
|
|
||||||
totalCount={logs.length}
|
|
||||||
itemContent={getItemContent}
|
|
||||||
/>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}, [getItemContent, linesPerRow, logs, onSetActiveLog, selected, viewMode]);
|
}, [getItemContent, linesPerRow, logs, onSetActiveLog, selected, viewMode]);
|
||||||
|
3
frontend/src/container/LogsTable/logsTable.styles.scss
Normal file
3
frontend/src/container/LogsTable/logsTable.styles.scss
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.logs-card {
|
||||||
|
flex: 1;
|
||||||
|
}
|
@ -6,6 +6,9 @@ export const Container = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Heading = styled(Card)`
|
export const Heading = styled(Card)`
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { PlayCircleFilled } from '@ant-design/icons';
|
import { PlayCircleFilled } from '@ant-design/icons';
|
||||||
|
import { QueryParams } from 'constants/query';
|
||||||
import {
|
import {
|
||||||
initialQueryBuilderFormValuesMap,
|
initialQueryBuilderFormValuesMap,
|
||||||
PANEL_TYPES,
|
PANEL_TYPES,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
|
||||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
@ -63,7 +63,7 @@ function LogsTopNav(): JSX.Element {
|
|||||||
|
|
||||||
const JSONCompositeQuery = encodeURIComponent(JSON.stringify(compositeQuery));
|
const JSONCompositeQuery = encodeURIComponent(JSON.stringify(compositeQuery));
|
||||||
|
|
||||||
const path = `${ROUTES.LIVE_LOGS}?${queryParamNamesMap.compositeQuery}=${JSONCompositeQuery}`;
|
const path = `${ROUTES.LIVE_LOGS}?${QueryParams.compositeQuery}=${JSONCompositeQuery}`;
|
||||||
|
|
||||||
history.push(path, queryHistoryState);
|
history.push(path, queryHistoryState);
|
||||||
}, [history, panelType, queryClient, stagedQuery]);
|
}, [history, panelType, queryClient, stagedQuery]);
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import { QueryParams } from 'constants/query';
|
||||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { CSSProperties, useCallback } from 'react';
|
import { CSSProperties, useCallback } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { connect, useSelector } from 'react-redux';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import {
|
import {
|
||||||
@ -23,6 +25,8 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
|
|||||||
(state) => state.dashboards,
|
(state) => state.dashboards,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
const [selectedDashboard] = dashboards;
|
const [selectedDashboard] = dashboards;
|
||||||
@ -42,20 +46,20 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
|
|||||||
|
|
||||||
toggleAddWidget(false);
|
toggleAddWidget(false);
|
||||||
|
|
||||||
history.push(
|
const queryParams = {
|
||||||
`${history.location.pathname}/new?graphType=${name}&widgetId=${
|
graphType: name,
|
||||||
emptyLayout.i
|
widgetId: emptyLayout.i,
|
||||||
}&${queryParamNamesMap.compositeQuery}=${encodeURIComponent(
|
[QueryParams.compositeQuery]: JSON.stringify(initialQueriesMap.metrics),
|
||||||
JSON.stringify(initialQueriesMap.metrics),
|
};
|
||||||
)}`,
|
|
||||||
);
|
history.push(`${pathname}/new?${createQueryParams(queryParams)}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error({
|
notifications.error({
|
||||||
message: 'Something went wrong',
|
message: 'Something went wrong',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[data, toggleAddWidget, notifications],
|
[data, toggleAddWidget, notifications, pathname],
|
||||||
);
|
);
|
||||||
const isDarkMode = useIsDarkMode();
|
const isDarkMode = useIsDarkMode();
|
||||||
const fillColor: CSSProperties['color'] = isDarkMode ? 'white' : 'black';
|
const fillColor: CSSProperties['color'] = isDarkMode ? 'white' : 'black';
|
||||||
|
144
frontend/src/container/OnboardingContainer/APM/APM.styles.scss
Normal file
144
frontend/src/container/OnboardingContainer/APM/APM.styles.scss
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
.apm-module-container {
|
||||||
|
padding: 48px 0;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.supported-languages-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supported-language {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 300px;
|
||||||
|
height: 120px;
|
||||||
|
background: #1d1d1d;
|
||||||
|
border: 1px solid #424242;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: #424242;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: #111a2c;
|
||||||
|
border: 0.5px solid #3c89e8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.supported-langauge-img {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-langauage-setup-instructions {
|
||||||
|
padding: 24px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[class*='-setup-instructions-container'] {
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
margin: 16px 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #e5e7eb;
|
||||||
|
gap: 16px;
|
||||||
|
margin: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #292d3e;
|
||||||
|
padding: 8px;
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
code {
|
||||||
|
overflow: auto;
|
||||||
|
text-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container {
|
||||||
|
padding: 24px;
|
||||||
|
margin: 16px 0;
|
||||||
|
background: rgba(29, 29, 29, 1);
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-docs-link {
|
||||||
|
display: flex;
|
||||||
|
margin: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
& .ant-form-item {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$lightModeFontColor: rgb(29, 29, 29);
|
||||||
|
|
||||||
|
.lightMode {
|
||||||
|
.apm-module-container {
|
||||||
|
.header {
|
||||||
|
color: $lightModeFontColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[class*='-setup-instructions-container'] {
|
||||||
|
.header {
|
||||||
|
h1 {
|
||||||
|
color: $lightModeFontColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.framework-selector {
|
||||||
|
.label {
|
||||||
|
color: $lightModeFontColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-name-container {
|
||||||
|
.label {
|
||||||
|
color: $lightModeFontColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
frontend/src/container/OnboardingContainer/APM/APM.tsx
Normal file
95
frontend/src/container/OnboardingContainer/APM/APM.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||||
|
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||||
|
import './APM.styles.scss';
|
||||||
|
|
||||||
|
import cx from 'classnames';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import GoLang from './GoLang/GoLang';
|
||||||
|
import Java from './Java/Java';
|
||||||
|
import Javascript from './Javascript/Javascript';
|
||||||
|
import Python from './Python/Python';
|
||||||
|
|
||||||
|
const supportedLanguages = [
|
||||||
|
{
|
||||||
|
name: 'java',
|
||||||
|
imgURL: `Logos/java.png`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'python',
|
||||||
|
imgURL: `Logos/java.png`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'javascript',
|
||||||
|
imgURL: `Logos/java.png`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'go',
|
||||||
|
imgURL: `Logos/java.png`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function APM({
|
||||||
|
activeStep,
|
||||||
|
}: {
|
||||||
|
activeStep: number;
|
||||||
|
}): JSX.Element {
|
||||||
|
const [selectedLanguage, setSelectedLanguage] = useState('java');
|
||||||
|
|
||||||
|
const renderSelectedLanguageSetupInstructions = (): JSX.Element => {
|
||||||
|
switch (selectedLanguage) {
|
||||||
|
case 'java':
|
||||||
|
return <Java activeStep={activeStep} />;
|
||||||
|
case 'python':
|
||||||
|
return <Python activeStep={activeStep} />;
|
||||||
|
case 'javascript':
|
||||||
|
return <Javascript activeStep={activeStep} />;
|
||||||
|
case 'go':
|
||||||
|
return <GoLang activeStep={activeStep} />;
|
||||||
|
default:
|
||||||
|
return <> </>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="apm-module-container">
|
||||||
|
{activeStep === 2 && (
|
||||||
|
<>
|
||||||
|
<div className="header">
|
||||||
|
<h1>
|
||||||
|
Get Started to instrument your applications and sending data to SigNoz
|
||||||
|
</h1>
|
||||||
|
<h4> Select the data source </h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="supported-languages-container">
|
||||||
|
{supportedLanguages.map((supportedLanguage) => (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'supported-language',
|
||||||
|
selectedLanguage === supportedLanguage.name ? 'selected' : '',
|
||||||
|
)}
|
||||||
|
key={supportedLanguage.name}
|
||||||
|
onClick={(): void => setSelectedLanguage(supportedLanguage.name)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className={cx('supported-langauge-img')}
|
||||||
|
src={`/Logos/${supportedLanguage.name}.png`}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedLanguage && (
|
||||||
|
<div
|
||||||
|
className={cx('selected-langauage-setup-instructions', selectedLanguage)}
|
||||||
|
>
|
||||||
|
{renderSelectedLanguageSetupInstructions()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import './GoLang.styles.scss';
|
||||||
|
|
||||||
|
import { MDXProvider } from '@mdx-js/react';
|
||||||
|
import { Form, Input } from 'antd';
|
||||||
|
import Header from 'container/OnboardingContainer/common/Header/Header';
|
||||||
|
|
||||||
|
import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus';
|
||||||
|
import GoLangDocs from './goLang.md';
|
||||||
|
|
||||||
|
export default function GoLang({
|
||||||
|
activeStep,
|
||||||
|
}: {
|
||||||
|
activeStep: number;
|
||||||
|
}): JSX.Element {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeStep === 2 && (
|
||||||
|
<div className="golang-setup-instructions-container">
|
||||||
|
<Header
|
||||||
|
entity="go"
|
||||||
|
heading="Go OpenTelemetry Instrumentation"
|
||||||
|
imgURL="/Logos/go.png"
|
||||||
|
docsURL="https://signoz.io/docs/instrumentation/golang/"
|
||||||
|
imgClassName="supported-language-img"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="form-container">
|
||||||
|
<div className="service-name-container">
|
||||||
|
<div className="label"> Service Name </div>
|
||||||
|
|
||||||
|
<Form form={form} name="service-name" style={{ minWidth: '300px' }}>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
name="Service Name"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
validateTrigger="onBlur"
|
||||||
|
>
|
||||||
|
<Input autoFocus />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="content-container">
|
||||||
|
<MDXProvider>
|
||||||
|
<GoLangDocs />
|
||||||
|
</MDXProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{activeStep === 3 && (
|
||||||
|
<ConnectionStatus
|
||||||
|
serviceName={form.getFieldValue('Service Name')}
|
||||||
|
framework="go"
|
||||||
|
language="go"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
430
frontend/src/container/OnboardingContainer/APM/GoLang/goLang.md
Normal file
430
frontend/src/container/OnboardingContainer/APM/GoLang/goLang.md
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
## Send Traces to SigNoz Cloud
|
||||||
|
|
||||||
|
### Application on VMs
|
||||||
|
|
||||||
|
From VMs, there are two ways to send data to SigNoz Cloud.
|
||||||
|
|
||||||
|
- [Send traces directly to SigNoz Cloud](#send-traces-directly-to-signoz-cloud)
|
||||||
|
- [Send traces via OTel Collector binary](#send-traces-via-otel-collector-binary) (recommended)
|
||||||
|
|
||||||
|
#### **Send traces directly to SigNoz Cloud**
|
||||||
|
|
||||||
|
1. **Install Dependencies**<br></br>
|
||||||
|
|
||||||
|
Dependencies related to OpenTelemetry exporter and SDK have to be installed first. Note that we are assuming you are using `gin` request router. If you are using other request routers, check out the [corresponding package](#request-routers).
|
||||||
|
|
||||||
|
Run the below commands after navigating to the application source folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get go.opentelemetry.io/otel \
|
||||||
|
go.opentelemetry.io/otel/trace \
|
||||||
|
go.opentelemetry.io/otel/sdk \
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Declare environment variables for configuring OpenTelemetry**<br></br>
|
||||||
|
|
||||||
|
Declare the following global variables in `main.go` which we will use to configure OpenTelemetry:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
var (
|
||||||
|
serviceName = os.Getenv("SERVICE_NAME")
|
||||||
|
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
|
||||||
|
insecure = os.Getenv("INSECURE_MODE")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Instrument your Go application with OpenTelemetry**<br></br>
|
||||||
|
|
||||||
|
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
import (
|
||||||
|
.....
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initTracer() func(context.Context) error {
|
||||||
|
|
||||||
|
var secureOption otlptracegrpc.Option
|
||||||
|
|
||||||
|
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
|
||||||
|
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
|
||||||
|
} else {
|
||||||
|
secureOption = otlptracegrpc.WithInsecure()
|
||||||
|
}
|
||||||
|
|
||||||
|
exporter, err := otlptrace.New(
|
||||||
|
context.Background(),
|
||||||
|
otlptracegrpc.NewClient(
|
||||||
|
secureOption,
|
||||||
|
otlptracegrpc.WithEndpoint(collectorURL),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create exporter: %v", err)
|
||||||
|
}
|
||||||
|
resources, err := resource.New(
|
||||||
|
context.Background(),
|
||||||
|
resource.WithAttributes(
|
||||||
|
attribute.String("service.name", serviceName),
|
||||||
|
attribute.String("library.language", "go"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not set resources: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
otel.SetTracerProvider(
|
||||||
|
sdktrace.NewTracerProvider(
|
||||||
|
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||||
|
sdktrace.WithBatcher(exporter),
|
||||||
|
sdktrace.WithResource(resources),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return exporter.Shutdown
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Initialize the tracer in main.go**<br></br>
|
||||||
|
|
||||||
|
Modify the main function to initialise the tracer in `main.go`. Initiate the tracer at the very beginning of our main function.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
cleanup := initTracer()
|
||||||
|
defer cleanup(context.Background())
|
||||||
|
|
||||||
|
......
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Add the OpenTelemetry Gin middleware**<br></br>
|
||||||
|
|
||||||
|
Configure Gin to use the middleware by adding the following lines in `main.go`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
....
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
......
|
||||||
|
r := gin.Default()
|
||||||
|
r.Use(otelgin.Middleware(serviceName))
|
||||||
|
......
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Set environment variables and run your Go Gin application**<br></br>
|
||||||
|
|
||||||
|
The run command must have some environment variables to send data to SigNoz cloud. The run command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SERVICE_NAME=goApp INSECURE_MODE=false OTEL_EXPORTER_OTLP_HEADERS=signoz-access-token=<SIGNOZ-INGESTION-TOKEN> OTEL_EXPORTER_OTLP_ENDPOINT=ingest.{region}.signoz.cloud:443 go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
We can replace the placeholders based on our environment.
|
||||||
|
|
||||||
|
`SERVICE_NAME`: goGinApp (you can name it whatever you want)
|
||||||
|
|
||||||
|
`OTEL_EXPORTER_OTLP_HEADERS`: `signoz-access-token=<SIGNOZ-INGESTION-TOKEN>`. Update `<SIGNOZ-INGESTION-TOKEN>` with the ingestion token provided by SigNoz
|
||||||
|
|
||||||
|
`OTEL_EXPORTER_OTLP_ENDPOINT`: ingest.{region}.signoz.cloud:443. Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary according to this table.
|
||||||
|
|
||||||
|
|
||||||
|
US - ingest.us.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
IN - ingest.in.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
EU - ingest.eu.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **Send traces via OTel Collector binary**
|
||||||
|
|
||||||
|
OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way.
|
||||||
|
|
||||||
|
You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Golang application.
|
||||||
|
|
||||||
|
1. **Install Dependencies**<br></br>
|
||||||
|
|
||||||
|
Dependencies related to OpenTelemetry exporter and SDK have to be installed first. Note that we are assuming you are using `gin` request router. If you are using other request routers, check out the [corresponding package](#request-routers).
|
||||||
|
|
||||||
|
Run the below commands after navigating to the application source folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get go.opentelemetry.io/otel \
|
||||||
|
go.opentelemetry.io/otel/trace \
|
||||||
|
go.opentelemetry.io/otel/sdk \
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Declare environment variables for configuring OpenTelemetry**<br></br>
|
||||||
|
|
||||||
|
Declare the following global variables in `main.go` which we will use to configure OpenTelemetry:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var (
|
||||||
|
serviceName = os.Getenv("SERVICE_NAME")
|
||||||
|
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
|
||||||
|
insecure = os.Getenv("INSECURE_MODE")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Instrument your Go application with OpenTelemetry**<br></br>
|
||||||
|
|
||||||
|
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
import (
|
||||||
|
.....
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initTracer() func(context.Context) error {
|
||||||
|
|
||||||
|
var secureOption otlptracegrpc.Option
|
||||||
|
|
||||||
|
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
|
||||||
|
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
|
||||||
|
} else {
|
||||||
|
secureOption = otlptracegrpc.WithInsecure()
|
||||||
|
}
|
||||||
|
|
||||||
|
exporter, err := otlptrace.New(
|
||||||
|
context.Background(),
|
||||||
|
otlptracegrpc.NewClient(
|
||||||
|
secureOption,
|
||||||
|
otlptracegrpc.WithEndpoint(collectorURL),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create exporter: %v", err)
|
||||||
|
}
|
||||||
|
resources, err := resource.New(
|
||||||
|
context.Background(),
|
||||||
|
resource.WithAttributes(
|
||||||
|
attribute.String("service.name", serviceName),
|
||||||
|
attribute.String("library.language", "go"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not set resources: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
otel.SetTracerProvider(
|
||||||
|
sdktrace.NewTracerProvider(
|
||||||
|
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||||
|
sdktrace.WithBatcher(exporter),
|
||||||
|
sdktrace.WithResource(resources),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return exporter.Shutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
4. **Initialize the tracer in main.go**<br></br>
|
||||||
|
|
||||||
|
Modify the main function to initialise the tracer in `main.go`. Initiate the tracer at the very beginning of our main function.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
cleanup := initTracer()
|
||||||
|
defer cleanup(context.Background())
|
||||||
|
|
||||||
|
......
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Add the OpenTelemetry Gin middleware**<br></br>
|
||||||
|
|
||||||
|
Configure Gin to use the middleware by adding the following lines in `main.go`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
....
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
......
|
||||||
|
r := gin.Default()
|
||||||
|
r.Use(otelgin.Middleware(serviceName))
|
||||||
|
......
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Set environment variables and run your Go Gin application**<br></br>
|
||||||
|
|
||||||
|
The run command must have some environment variables to send data to SigNoz. The run command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SERVICE_NAME=goGinApp INSECURE_MODE=true OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to update your `service_name`, you can modify the `SERVICE_NAME` variable.<br></br>
|
||||||
|
`SERVICE_NAME`: goGinApp (you can name it whatever you want)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Applications Deployed on Kubernetes
|
||||||
|
|
||||||
|
For Golang application deployed on Kubernetes, you need to install OTel Collector agent in your k8s infra to collect and send traces to SigNoz Cloud. You can find the instructions to install OTel Collector agent [here](/docs/tutorial/kubernetes-infra-metrics/).
|
||||||
|
|
||||||
|
Once you have set up OTel Collector agent, you can proceed with OpenTelemetry Golang instrumentation by following the below steps:
|
||||||
|
|
||||||
|
1. **Install Dependencies**<br></br>
|
||||||
|
|
||||||
|
Dependencies related to OpenTelemetry exporter and SDK have to be installed first. Note that we are assuming you are using `gin` request router. If you are using other request routers, check out the [corresponding package](#request-routers).
|
||||||
|
|
||||||
|
Run the below commands after navigating to the application source folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get go.opentelemetry.io/otel \
|
||||||
|
go.opentelemetry.io/otel/trace \
|
||||||
|
go.opentelemetry.io/otel/sdk \
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin \
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace \
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Declare environment variables for configuring OpenTelemetry**<br></br>
|
||||||
|
|
||||||
|
Declare the following global variables in `main.go` which we will use to configure OpenTelemetry:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var (
|
||||||
|
serviceName = os.Getenv("SERVICE_NAME")
|
||||||
|
collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
|
||||||
|
insecure = os.Getenv("INSECURE_MODE")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Instrument your Go application with OpenTelemetry**<br></br>
|
||||||
|
|
||||||
|
To configure your application to send data we will need a function to initialize OpenTelemetry. Add the following snippet of code in your `main.go` file.
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
import (
|
||||||
|
.....
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initTracer() func(context.Context) error {
|
||||||
|
|
||||||
|
var secureOption otlptracegrpc.Option
|
||||||
|
|
||||||
|
if strings.ToLower(insecure) == "false" || insecure == "0" || strings.ToLower(insecure) == "f" {
|
||||||
|
secureOption = otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, ""))
|
||||||
|
} else {
|
||||||
|
secureOption = otlptracegrpc.WithInsecure()
|
||||||
|
}
|
||||||
|
|
||||||
|
exporter, err := otlptrace.New(
|
||||||
|
context.Background(),
|
||||||
|
otlptracegrpc.NewClient(
|
||||||
|
secureOption,
|
||||||
|
otlptracegrpc.WithEndpoint(collectorURL),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create exporter: %v", err)
|
||||||
|
}
|
||||||
|
resources, err := resource.New(
|
||||||
|
context.Background(),
|
||||||
|
resource.WithAttributes(
|
||||||
|
attribute.String("service.name", serviceName),
|
||||||
|
attribute.String("library.language", "go"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not set resources: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
otel.SetTracerProvider(
|
||||||
|
sdktrace.NewTracerProvider(
|
||||||
|
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||||
|
sdktrace.WithBatcher(exporter),
|
||||||
|
sdktrace.WithResource(resources),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return exporter.Shutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
4. **Initialize the tracer in main.go**<br></br>
|
||||||
|
|
||||||
|
Modify the main function to initialise the tracer in `main.go`. Initiate the tracer at the very beginning of our main function.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
cleanup := initTracer()
|
||||||
|
defer cleanup(context.Background())
|
||||||
|
|
||||||
|
......
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Add the OpenTelemetry Gin middleware**<br></br>
|
||||||
|
|
||||||
|
Configure Gin to use the middleware by adding the following lines in `main.go`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
....
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
......
|
||||||
|
r := gin.Default()
|
||||||
|
r.Use(otelgin.Middleware(serviceName))
|
||||||
|
......
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **Set environment variables and run your Go Gin application**<br></br>
|
||||||
|
|
||||||
|
The run command must have some environment variables to send data to SigNoz. The run command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SERVICE_NAME=goGinApp INSECURE_MODE=true OTEL_EXPORTER_OTLP_ENDPOINT=localhost:4317 go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to update your `service_name`, you can modify the `SERVICE_NAME` variable.<br></br>
|
||||||
|
`SERVICE_NAME`: goGinApp (you can name it whatever you want)
|
@ -0,0 +1,10 @@
|
|||||||
|
.form-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
& .ant-form-item {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
}
|
115
frontend/src/container/OnboardingContainer/APM/Java/Java.tsx
Normal file
115
frontend/src/container/OnboardingContainer/APM/Java/Java.tsx
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import './Java.styles.scss';
|
||||||
|
|
||||||
|
import { MDXProvider } from '@mdx-js/react';
|
||||||
|
import { Form, Input, Select } from 'antd';
|
||||||
|
import Header from 'container/OnboardingContainer/common/Header/Header';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import ConnectionStatus from '../common/ConnectionStatus/ConnectionStatus';
|
||||||
|
import JavaDocs from './md-docs/java.md';
|
||||||
|
import JbossDocs from './md-docs/jboss.md';
|
||||||
|
import SprintBootDocs from './md-docs/spring_boot.md';
|
||||||
|
import TomcatDocs from './md-docs/tomcat.md';
|
||||||
|
|
||||||
|
enum FrameworksMap {
|
||||||
|
tomcat = 'Tomcat',
|
||||||
|
spring_boot = 'Spring Boot',
|
||||||
|
jboss = 'JBoss',
|
||||||
|
other = 'Others',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Java({
|
||||||
|
activeStep,
|
||||||
|
}: {
|
||||||
|
activeStep: number;
|
||||||
|
}): JSX.Element {
|
||||||
|
const [selectedFrameWork, setSelectedFrameWork] = useState('spring_boot');
|
||||||
|
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const renderDocs = (): JSX.Element => {
|
||||||
|
switch (selectedFrameWork) {
|
||||||
|
case 'tomcat':
|
||||||
|
return <TomcatDocs />;
|
||||||
|
case 'spring_boot':
|
||||||
|
return <SprintBootDocs />;
|
||||||
|
case 'jboss':
|
||||||
|
return <JbossDocs />;
|
||||||
|
default:
|
||||||
|
return <JavaDocs />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{activeStep === 2 && (
|
||||||
|
<div className="java-setup-instructions-container">
|
||||||
|
<Header
|
||||||
|
entity="java"
|
||||||
|
heading="Java OpenTelemetry Instrumentation"
|
||||||
|
imgURL="/Logos/java.png"
|
||||||
|
docsURL="https://signoz.io/docs/instrumentation/java/"
|
||||||
|
imgClassName="supported-language-img"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="form-container">
|
||||||
|
<div className="framework-selector">
|
||||||
|
<div className="label"> Select Framework </div>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
defaultValue="spring_boot"
|
||||||
|
style={{ minWidth: 120 }}
|
||||||
|
placeholder="Select Framework"
|
||||||
|
onChange={(value): void => setSelectedFrameWork(value)}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'spring_boot',
|
||||||
|
label: FrameworksMap.spring_boot,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'tomcat',
|
||||||
|
label: FrameworksMap.tomcat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'jboss',
|
||||||
|
label: FrameworksMap.jboss,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'other',
|
||||||
|
label: FrameworksMap.other,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="service-name-container">
|
||||||
|
<div className="label"> Service Name </div>
|
||||||
|
|
||||||
|
<Form form={form} name="service-name" style={{ minWidth: '300px' }}>
|
||||||
|
<Form.Item
|
||||||
|
hasFeedback
|
||||||
|
name="Service Name"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
validateTrigger="onBlur"
|
||||||
|
>
|
||||||
|
<Input autoFocus />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="content-container">
|
||||||
|
<MDXProvider>{renderDocs()}</MDXProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{activeStep === 3 && (
|
||||||
|
<ConnectionStatus
|
||||||
|
serviceName={form.getFieldValue('Service Name')}
|
||||||
|
language="java"
|
||||||
|
framework={(FrameworksMap as any)[selectedFrameWork]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
## Requirements
|
||||||
|
|
||||||
|
Java 8 or higher
|
||||||
|
|
||||||
|
## Send Traces to SigNoz Cloud
|
||||||
|
|
||||||
|
OpenTelemetry provides a handy Java JAR agent that can be attached to any Java 8+ application and dynamically injects bytecode to capture telemetry from a number of popular libraries and frameworks.
|
||||||
|
|
||||||
|
Based on your application environment, you can choose the setup below to send traces to SigNoz Cloud.
|
||||||
|
|
||||||
|
### Application on VMs
|
||||||
|
|
||||||
|
From VMs, there are two ways to send data to SigNoz Cloud.
|
||||||
|
|
||||||
|
- [Send traces directly to SigNoz Cloud](#send-traces-directly-to-signoz-cloud)
|
||||||
|
- [Send traces via OTel Collector binary](#send-traces-via-otel-collector-binary) (recommended)
|
||||||
|
|
||||||
|
#### **Send traces directly to SigNoz Cloud**
|
||||||
|
|
||||||
|
OpenTelemetry Java agent can send traces directly to SigNoz Cloud.
|
||||||
|
|
||||||
|
Step 1. Download otel java binary agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Run your application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OTEL_RESOURCE_ATTRIBUTES=service.name=<app_name> \
|
||||||
|
OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token=SIGNOZ_INGESTION_KEY" \
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{region}.signoz.cloud:443 \
|
||||||
|
java -javaagent:$PWD/opentelemetry-javaagent.jar -jar <my-app>.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<app_name>` is the name for your application
|
||||||
|
- `SIGNOZ_INGESTION_KEY` is the API token provided by SigNoz. You can find your ingestion key from SigNoz cloud account details sent on your email.
|
||||||
|
|
||||||
|
Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary according to this table.
|
||||||
|
|
||||||
|
US - ingest.us.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
IN - ingest.in.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
EU - ingest.eu.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **Send traces via OTel Collector binary**
|
||||||
|
|
||||||
|
OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way.
|
||||||
|
|
||||||
|
You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Java application.
|
||||||
|
|
||||||
|
Step 1. Download OTel java binary agent<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Run your application<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -javaagent:$PWD/opentelemetry-javaagent.jar -jar <myapp>.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<myapp>` is the name of your application jar file
|
||||||
|
- In case you download `opentelemetry-javaagent.jar` file in different directory than that of the project, replace `$PWD` with the path of the otel jar file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Applications Deployed on Kubernetes
|
||||||
|
|
||||||
|
For Java application deployed on Kubernetes, you need to install OTel Collector agent in your k8s infra to collect and send traces to SigNoz Cloud. You can find the instructions to install OTel Collector agent [here](/docs/tutorial/kubernetes-infra-metrics/).
|
||||||
|
|
||||||
|
Once you have set up OTel Collector agent, you can proceed with OpenTelemetry java instrumentation by following the below steps:
|
||||||
|
|
||||||
|
1. Download otel java binary<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run your application<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -javaagent:$PWD/opentelemetry-javaagent.jar -jar <myapp>.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<myapp>` is the name of your application jar file
|
||||||
|
- In case you download `opentelemetry-javaagent.jar` file in different directory than that of the project, replace `$PWD` with the path of the otel jar file.
|
||||||
|
|
||||||
|
3. Make sure to dockerise your application along with OpenTelemetry instrumentation.
|
||||||
|
|
@ -0,0 +1,127 @@
|
|||||||
|
## Requirements
|
||||||
|
|
||||||
|
Java 8 or higher
|
||||||
|
|
||||||
|
## Send Traces to SigNoz Cloud
|
||||||
|
|
||||||
|
OpenTelemetry provides a handy Java JAR agent that can be attached to any Java 8+ application and dynamically injects bytecode to capture telemetry from a number of popular libraries and frameworks.
|
||||||
|
|
||||||
|
Based on your application environment, you can choose the setup below to send traces to SigNoz Cloud.
|
||||||
|
|
||||||
|
### Application on VMs
|
||||||
|
|
||||||
|
From VMs, there are two ways to send data to SigNoz Cloud.
|
||||||
|
|
||||||
|
- [Send traces directly to SigNoz Cloud](#send-traces-directly-to-signoz-cloud)
|
||||||
|
- [Send traces via OTel Collector binary](#send-traces-via-otel-collector-binary) (recommended)
|
||||||
|
|
||||||
|
#### **Send traces directly to SigNoz Cloud**
|
||||||
|
OpenTelemetry Java agent can send traces directly to SigNoz Cloud.
|
||||||
|
|
||||||
|
Step 1. Download otel java binary agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Open the configuration file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vim /opt/jboss-eap-7.1/bin/standalone.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 3. Update `JAVA_OPTS` environment variable
|
||||||
|
|
||||||
|
Update `JAVA_OPTS` environment variable with configurations required to send data to SigNoz cloud in your configuration file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
JAVA_OPTS="-javaagent:/path/opentelemetry-javaagent.jar
|
||||||
|
-Dotel.exporter.otlp.endpoint=https://ingest.{region}.signoz.cloud:443
|
||||||
|
-Dotel.exporter.otlp.headers="signoz-access-token=SIGNOZ_INGESTION_KEY"
|
||||||
|
-Dotel.resource.attributes="service.name=<app_name>""
|
||||||
|
```
|
||||||
|
You need to replace the following things based on your environment:<br></br>
|
||||||
|
|
||||||
|
- `path` - Update it to the path of your downloaded Java JAR agent.<br></br>
|
||||||
|
- `<app_name>` is the name for your application
|
||||||
|
- `SIGNOZ_INGESTION_KEY` is the API token provided by SigNoz. You can find your ingestion key from SigNoz cloud account details sent on your email.
|
||||||
|
|
||||||
|
Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary according to this table.
|
||||||
|
|
||||||
|
|
||||||
|
US - ingest.us.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
IN - ingest.in.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
EU - ingest.eu.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
|
||||||
|
Step 4. [Optional] Write the output/logs of standalone.sh script to a file nohup.out as a background thread
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/opt/jboss-eap-7.1/bin/standalone.sh > /opt/jboss-eap-7.1/bin/nohup.out &
|
||||||
|
```
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **Send traces via OTel Collector binary**
|
||||||
|
|
||||||
|
OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way.
|
||||||
|
|
||||||
|
You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Java application.
|
||||||
|
|
||||||
|
Step 1. Download OTel java binary agent<br></br>
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Step 2. Open the configuration file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vim /opt/jboss-eap-7.1/bin/standalone.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 3. Update `JAVA_OPTS` environment variable
|
||||||
|
|
||||||
|
Update `JAVA_OPTS` environment variable with configurations required to send data to SigNoz cloud in your configuration file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
JAVA_OPTS="-javaagent:/path/opentelemetry-javaagent.jar"
|
||||||
|
```
|
||||||
|
|
||||||
|
where,
|
||||||
|
- `path` - Update it to the path of your downloaded Java JAR agent.<br></br>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Applications Deployed on Kubernetes
|
||||||
|
|
||||||
|
For Java application deployed on Kubernetes, you need to install OTel Collector agent in your k8s infra to collect and send traces to SigNoz Cloud. You can find the instructions to install OTel Collector agent [here](/docs/tutorial/kubernetes-infra-metrics/).
|
||||||
|
|
||||||
|
Once you have set up OTel Collector agent, you can proceed with OpenTelemetry java instrumentation by following the below steps:
|
||||||
|
|
||||||
|
Step 1. Download otel java binary<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Open the configuration file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vim /opt/jboss-eap-7.1/bin/standalone.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 3. Update `JAVA_OPTS` environment variable
|
||||||
|
|
||||||
|
Update `JAVA_OPTS` environment variable with configurations required to send data to SigNoz cloud in your configuration file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
JAVA_OPTS="-javaagent:/path/opentelemetry-javaagent.jar"
|
||||||
|
```
|
||||||
|
|
||||||
|
where,
|
||||||
|
- `path` - Update it to the path of your downloaded Java JAR agent.<br></br>
|
||||||
|
|
||||||
|
|
||||||
|
Step 4. Make sure to dockerise your application along with OpenTelemetry instrumentation.
|
@ -0,0 +1,91 @@
|
|||||||
|
## Requirements
|
||||||
|
|
||||||
|
Java 8 or higher
|
||||||
|
|
||||||
|
## Send Traces to SigNoz Cloud
|
||||||
|
|
||||||
|
OpenTelemetry provides a handy Java JAR agent that can be attached to any Java 8+ application and dynamically injects bytecode to capture telemetry from a number of popular libraries and frameworks.
|
||||||
|
|
||||||
|
Based on your application environment, you can choose the setup below to send traces to SigNoz Cloud.
|
||||||
|
|
||||||
|
### Application on VMs
|
||||||
|
|
||||||
|
From VMs, there are two ways to send data to SigNoz Cloud.
|
||||||
|
|
||||||
|
- [Send traces directly to SigNoz Cloud](#send-traces-directly-to-signoz-cloud)
|
||||||
|
- [Send traces via OTel Collector binary](#send-traces-via-otel-collector-binary) (recommended)
|
||||||
|
|
||||||
|
#### **Send traces directly to SigNoz Cloud**
|
||||||
|
OpenTelemetry Java agent can send traces directly to SigNoz Cloud.
|
||||||
|
|
||||||
|
Step 1. Download otel java binary agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Run your application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
OTEL_RESOURCE_ATTRIBUTES=service.name=<app_name> \
|
||||||
|
OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token=SIGNOZ_INGESTION_KEY" \
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{region}.signoz.cloud:443 \
|
||||||
|
java -javaagent:$PWD/opentelemetry-javaagent.jar -jar <my-app>.jar
|
||||||
|
```
|
||||||
|
- `<app_name>` is the name for your application
|
||||||
|
- `SIGNOZ_INGESTION_KEY` is the API token provided by SigNoz. You can find your ingestion key from SigNoz cloud account details sent on your email.
|
||||||
|
|
||||||
|
Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary according to this table.
|
||||||
|
|
||||||
|
US - ingest.us.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
IN - ingest.in.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
EU - ingest.eu.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **Send traces via OTel Collector binary**
|
||||||
|
|
||||||
|
OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way.
|
||||||
|
|
||||||
|
You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Java application.
|
||||||
|
|
||||||
|
Step 1. Download OTel java binary agent<br></br>
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Run your application<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -javaagent:$PWD/opentelemetry-javaagent.jar -jar <myapp>.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<myapp>` is the name of your application jar file
|
||||||
|
- In case you download `opentelemetry-javaagent.jar` file in different directory than that of the project, replace `$PWD` with the path of the otel jar file.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Applications Deployed on Kubernetes
|
||||||
|
|
||||||
|
For Java application deployed on Kubernetes, you need to install OTel Collector agent in your k8s infra to collect and send traces to SigNoz Cloud. You can find the instructions to install OTel Collector agent [here](/docs/tutorial/kubernetes-infra-metrics/).
|
||||||
|
|
||||||
|
Once you have set up OTel Collector agent, you can proceed with OpenTelemetry java instrumentation by following the below steps:
|
||||||
|
|
||||||
|
1. Download otel java binary<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run your application<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -javaagent:$PWD/opentelemetry-javaagent.jar -jar <myapp>.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<myapp>` is the name of your application jar file
|
||||||
|
- In case you download `opentelemetry-javaagent.jar` file in different directory than that of the project, replace `$PWD` with the path of the otel jar file.
|
||||||
|
|
||||||
|
3. Make sure to dockerise your application along with OpenTelemetry instrumentation.
|
@ -0,0 +1,106 @@
|
|||||||
|
## Requirements
|
||||||
|
|
||||||
|
Java 8 or higher
|
||||||
|
|
||||||
|
## Send Traces to SigNoz Cloud
|
||||||
|
|
||||||
|
OpenTelemetry provides a handy Java JAR agent that can be attached to any Java 8+ application and dynamically injects bytecode to capture telemetry from a number of popular libraries and frameworks.
|
||||||
|
|
||||||
|
Based on your application environment, you can choose the setup below to send traces to SigNoz Cloud.
|
||||||
|
|
||||||
|
### Application on VMs
|
||||||
|
|
||||||
|
From VMs, there are two ways to send data to SigNoz Cloud.
|
||||||
|
|
||||||
|
- [Send traces directly to SigNoz Cloud](#send-traces-directly-to-signoz-cloud)
|
||||||
|
- [Send traces via OTel Collector binary](#send-traces-via-otel-collector-binary) (recommended)
|
||||||
|
|
||||||
|
#### **Send traces directly to SigNoz Cloud**
|
||||||
|
OpenTelemetry Java agent can send traces directly to SigNoz Cloud.
|
||||||
|
|
||||||
|
Step 1. Download otel java binary agent
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Enable the instrumentation agent and run your application
|
||||||
|
|
||||||
|
If you run your `.war` package by putting in `webapps` folder, just add `setenv.sh` in your Tomcat `bin` folder.
|
||||||
|
|
||||||
|
This should set these environment variables and start sending telemetry data to SigNoz Cloud.
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export CATALINA_OPTS="$CATALINA_OPTS -javaagent:/path/to/opentelemetry-javaagent.jar"
|
||||||
|
export OTEL_EXPORTER_OTLP_HEADERS="signoz-access-token=SIGNOZ_INGESTION_KEY"
|
||||||
|
export OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.{region}.signoz.cloud:443
|
||||||
|
export OTEL_RESOURCE_ATTRIBUTES=service.name=<app_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
- `<app_name>` is the name for your application
|
||||||
|
- `SIGNOZ_INGESTION_KEY` is the API token provided by SigNoz. You can find your ingestion key from SigNoz cloud account details sent on your email.
|
||||||
|
|
||||||
|
Depending on the choice of your region for SigNoz cloud, the ingest endpoint will vary according to this table.
|
||||||
|
|
||||||
|
US - ingest.us.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
IN - ingest.in.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
EU - ingest.eu.signoz.cloud:443 <br></br>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **Send traces via OTel Collector binary**
|
||||||
|
|
||||||
|
OTel Collector binary helps to collect logs, hostmetrics, resource and infra attributes. It is recommended to install Otel Collector binary to collect and send traces to SigNoz cloud. You can correlate signals and have rich contextual data through this way.
|
||||||
|
|
||||||
|
You can find instructions to install OTel Collector binary [here](https://signoz.io/docs/tutorial/opentelemetry-binary-usage-in-virtual-machine/) in your VM. Once you are done setting up your OTel Collector binary, you can follow the below steps for instrumenting your Java application.
|
||||||
|
|
||||||
|
Step 1. Download OTel java binary agent<br></br>
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 2. Enable the instrumentation agent and run your application<br></br>
|
||||||
|
|
||||||
|
If you run your `.war` package by putting in `webapps` folder, just add `setenv.sh` in your Tomcat `bin` folder.
|
||||||
|
|
||||||
|
This should set these environment variables and start sending telemetry data to SigNoz Cloud.
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export CATALINA_OPTS="$CATALINA_OPTS -javaagent:/path/to/opentelemetry-javaagent.jar"
|
||||||
|
```
|
||||||
|
|
||||||
|
- path/to - Update it to the path of your downloaded Java JAR agent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Applications Deployed on Kubernetes
|
||||||
|
|
||||||
|
For Java application deployed on Kubernetes, you need to install OTel Collector agent in your k8s infra to collect and send traces to SigNoz Cloud. You can find the instructions to install OTel Collector agent [here](/docs/tutorial/kubernetes-infra-metrics/).
|
||||||
|
|
||||||
|
Once you have set up OTel Collector agent, you can proceed with OpenTelemetry java instrumentation by following the below steps:
|
||||||
|
|
||||||
|
1. Download otel java binary<br></br>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Enable the instrumentation agent and run your application<br></br>
|
||||||
|
|
||||||
|
If you run your `.war` package by putting in `webapps` folder, just add `setenv.sh` in your Tomcat `bin` folder.
|
||||||
|
|
||||||
|
This should set the environment variable and start sending telemetry data to SigNoz Cloud.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export CATALINA_OPTS="$CATALINA_OPTS -javaagent:/path/to/opentelemetry-javaagent.jar"
|
||||||
|
```
|
||||||
|
|
||||||
|
- path/to - Update it to the path of your downloaded Java JAR agent.
|
||||||
|
|
||||||
|
3. Make sure to dockerise your application along with OpenTelemetry instrumentation.
|
||||||
|
|
||||||
|
You can validate if your application is sending traces to SigNoz cloud by following the instructions [here](#validating-instrumentation-by-checking-for-traces).
|
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