Merge pull request #3549 from SigNoz/release/v0.29.0

Release/v0.29.0
This commit is contained in:
Prashant Shahi 2023-09-14 18:05:19 +05:30 committed by GitHub
commit 9c8125ffc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
239 changed files with 12497 additions and 3027 deletions

View File

@ -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:

View File

@ -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
View File

@ -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/

View File

@ -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/...

View File

@ -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",

View File

@ -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",

View File

@ -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"

View File

@ -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:
[ [

View File

@ -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"

View File

@ -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,
} }

View File

@ -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)
} }

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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"`
} }

View File

@ -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: "",
},
} }

View File

@ -1,5 +1,4 @@
node_modules node_modules
.vscode .vscode
build build
.env
.git .git

7
frontend/example.env Normal file
View 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"

View File

@ -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"
} }
} }

View 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

View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.9 KiB

View 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

View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -0,0 +1,3 @@
{
"name_of_the_view": "Name of the view"
}

View File

@ -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 dont match. Please try again", "failed_confirm_password": "Passwords dont 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",

View File

@ -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",

View File

@ -0,0 +1,3 @@
{
"name_of_the_view": "Name of the view"
}

View File

@ -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 dont match. Please try again", "failed_confirm_password": "Passwords dont 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",

View File

@ -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",

View File

@ -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}>

View File

@ -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'),
); );

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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();
} }
} }

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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);
}; };

View File

@ -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}
/> />
} }

View File

@ -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,
}); });
}; };

View File

@ -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>
); );
} }

View File

@ -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;

View File

@ -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();
}); });
}); });

View File

@ -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>,
); );

View File

@ -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'];
} }

View File

@ -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',

View File

@ -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;

View File

@ -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;

View File

@ -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}

View File

@ -0,0 +1,4 @@
export const CARD_BODY_STYLE = {
padding: '0',
height: '100%',
};

View File

@ -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',
} }

View File

@ -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',
} }

View File

@ -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',
} }

View File

@ -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',
};

View File

@ -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',
}; };

View File

@ -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',

View File

@ -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>
); );

View File

@ -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;

View File

@ -0,0 +1,8 @@
export const keyToExclude = [
'exceptionStacktrace',
'exceptionType',
'errorId',
'timestamp',
'exceptionMessage',
'exceptionEscaped',
];

View File

@ -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),
}&timestamp=${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'),

View File

@ -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,
); );

View File

@ -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]);

View File

@ -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;
} }

View File

@ -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)}`);
}); });
} }
}; };

View File

@ -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]);

View File

@ -123,7 +123,6 @@ function HeaderContainer(): JSX.Element {
Try Signoz Cloud Try Signoz Cloud
</Button> </Button>
)} )}
<Config frontendId="tooltip" /> <Config frontendId="tooltip" />
<ToggleButton <ToggleButton

View File

@ -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))}`,
); );
}) })

View File

@ -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 }),

View File

@ -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,
}} }}
/> />
); );

View File

@ -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;

View File

@ -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}>

View File

@ -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}

View File

@ -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');

View File

@ -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({

View File

@ -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>
); );

View File

@ -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}

View File

@ -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;

View File

@ -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 (
<> <>

View File

@ -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%;
`; `;

View File

@ -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,
); );

View File

@ -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),
);
}; };

View File

@ -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]);

View File

@ -0,0 +1,3 @@
.logs-card {
flex: 1;
}

View File

@ -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)`

View File

@ -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]);

View File

@ -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';

View 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;
}
}
}
}
}

View 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>
);
}

View File

@ -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"
/>
)}
</>
);
}

View 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)

View File

@ -0,0 +1,10 @@
.form-container {
display: flex;
align-items: flex-start;
width: 100%;
gap: 16px;
& .ant-form-item {
margin-bottom: 0px;
}
}

View 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]}
/>
)}
</>
);
}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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