mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-10 02:39:02 +08:00
Merge branch 'develop' into 716-sorting
This commit is contained in:
commit
05371085f9
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -2,5 +2,5 @@
|
|||||||
# Owners are automatically requested for review for PRs that changes code
|
# Owners are automatically requested for review for PRs that changes code
|
||||||
# that they own.
|
# that they own.
|
||||||
* @ankitnayan
|
* @ankitnayan
|
||||||
/frontend/ @palash-signoz
|
/frontend/ @palash-signoz @pranshuchittora
|
||||||
/deploy/ @prashant-shahi
|
/deploy/ @prashant-shahi
|
||||||
|
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -26,6 +26,7 @@ assignees: ''
|
|||||||
* **Signoz version**:
|
* **Signoz version**:
|
||||||
* **Browser version**:
|
* **Browser version**:
|
||||||
* **Your OS and version**:
|
* **Your OS and version**:
|
||||||
|
* **Your CPU Architecture**(ARM/Intel):
|
||||||
|
|
||||||
## Additional context
|
## Additional context
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ tasks:
|
|||||||
- name: Run Docker Images
|
- name: Run Docker Images
|
||||||
init: |
|
init: |
|
||||||
cd ./deploy
|
cd ./deploy
|
||||||
sudo docker-compose --env-file ./docker/clickhouse-setup/env/x86_64.env -f docker/clickhouse-setup/docker-compose.yaml up -d
|
sudo docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d
|
||||||
# command:
|
# command:
|
||||||
|
|
||||||
- name: Run Frontend
|
- name: Run Frontend
|
||||||
@ -22,7 +22,7 @@ tasks:
|
|||||||
yarn dev
|
yarn dev
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
- port: 3000
|
- port: 3301
|
||||||
onOpen: open-browser
|
onOpen: open-browser
|
||||||
- port: 8080
|
- port: 8080
|
||||||
onOpen: ignore
|
onOpen: ignore
|
||||||
|
@ -48,8 +48,15 @@ Need to update [https://github.com/SigNoz/signoz/tree/main/pkg/query-service](ht
|
|||||||
|
|
||||||
- git clone https://github.com/SigNoz/signoz.git
|
- git clone https://github.com/SigNoz/signoz.git
|
||||||
- run `sudo make dev-setup` to configure local setup to run query-service
|
- run `sudo make dev-setup` to configure local setup to run query-service
|
||||||
- comment out frontend service section at `docker/clickhouse-setup/docker-compose.yaml#L59`
|
- comment out frontend service section at `docker/clickhouse-setup/docker-compose.yaml#L45`
|
||||||
- comment out query-service section at `docker/clickhouse-setup/docker-compose.yaml#L38`
|
- comment out query-service section at `docker/clickhouse-setup/docker-compose.yaml#L28`
|
||||||
|
- add below configuration to clickhouse section at `docker/clickhouse-setup/docker-compose.yaml`
|
||||||
|
```
|
||||||
|
expose:
|
||||||
|
- 9000
|
||||||
|
ports:
|
||||||
|
- 9001:9000
|
||||||
|
```
|
||||||
- Install signoz locally without the frontend and query-service
|
- Install signoz locally without the frontend and query-service
|
||||||
- If you are using x86_64 processors (All Intel/AMD processors) run `sudo make run-x86`
|
- If you are using x86_64 processors (All Intel/AMD processors) run `sudo make run-x86`
|
||||||
- If you are on arm64 processors (Apple M1 Macbooks) run `sudo make run-arm`
|
- If you are on arm64 processors (Apple M1 Macbooks) run `sudo make run-arm`
|
||||||
|
@ -23,7 +23,7 @@ services:
|
|||||||
- '--storage.path=/data'
|
- '--storage.path=/data'
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.6.1
|
image: signoz/query-service:0.6.2
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -40,7 +40,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.6.1
|
image: signoz/frontend:0.6.2
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
@ -50,7 +50,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/otelcontribcol:0.5.0
|
image: signoz/otelcontribcol:0.6.0
|
||||||
command: ["--config=/etc/otel-collector-config.yaml", "--mem-ballast-size-mib=683"]
|
command: ["--config=/etc/otel-collector-config.yaml", "--mem-ballast-size-mib=683"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
@ -63,7 +63,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/otelcontribcol:0.5.0
|
image: signoz/otelcontribcol:0.6.0
|
||||||
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--mem-ballast-size-mib=683"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--mem-ballast-size-mib=683"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
|
@ -26,7 +26,7 @@ services:
|
|||||||
|
|
||||||
|
|
||||||
query-service:
|
query-service:
|
||||||
image: signoz/query-service:0.6.1
|
image: signoz/query-service:0.6.2
|
||||||
container_name: query-service
|
container_name: query-service
|
||||||
command: ["-config=/root/config/prometheus.yml"]
|
command: ["-config=/root/config/prometheus.yml"]
|
||||||
volumes:
|
volumes:
|
||||||
@ -43,7 +43,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: signoz/frontend:0.6.1
|
image: signoz/frontend:0.6.2
|
||||||
container_name: frontend
|
container_name: frontend
|
||||||
depends_on:
|
depends_on:
|
||||||
- query-service
|
- query-service
|
||||||
@ -53,7 +53,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/otelcontribcol:0.5.0
|
image: signoz/otelcontribcol:0.6.0
|
||||||
command: ["--config=/etc/otel-collector-config.yaml", "--mem-ballast-size-mib=683"]
|
command: ["--config=/etc/otel-collector-config.yaml", "--mem-ballast-size-mib=683"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
|
||||||
@ -66,7 +66,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
otel-collector-metrics:
|
otel-collector-metrics:
|
||||||
image: signoz/otelcontribcol:0.5.0
|
image: signoz/otelcontribcol:0.6.0
|
||||||
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--mem-ballast-size-mib=683"]
|
command: ["--config=/etc/otel-collector-metrics-config.yaml", "--mem-ballast-size-mib=683"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
|
||||||
|
14
frontend/cypress/CustomFunctions/uncaughtExpection.ts
Normal file
14
frontend/cypress/CustomFunctions/uncaughtExpection.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const resizeObserverLoopErrRe = /ResizeObserver loop limit exceeded/;
|
||||||
|
|
||||||
|
const unCaughtExpection = () => {
|
||||||
|
cy.on('uncaught:exception', (err) => {
|
||||||
|
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||||
|
// returning false here prevents Cypress from
|
||||||
|
// failing the test
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default unCaughtExpection;
|
35
frontend/cypress/fixtures/trace/initialAggregates.json
Normal file
35
frontend/cypress/fixtures/trace/initialAggregates.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"items": {
|
||||||
|
"1644926280000000000": { "timestamp": 1644926280000000000, "value": 787 },
|
||||||
|
"1644926340000000000": { "timestamp": 1644926340000000000, "value": 2798 },
|
||||||
|
"1644926400000000000": { "timestamp": 1644926400000000000, "value": 2828 },
|
||||||
|
"1644926460000000000": { "timestamp": 1644926460000000000, "value": 2926 },
|
||||||
|
"1644926520000000000": { "timestamp": 1644926520000000000, "value": 2932 },
|
||||||
|
"1644926580000000000": { "timestamp": 1644926580000000000, "value": 2842 },
|
||||||
|
"1644926640000000000": { "timestamp": 1644926640000000000, "value": 2966 },
|
||||||
|
"1644926700000000000": { "timestamp": 1644926700000000000, "value": 2782 },
|
||||||
|
"1644926760000000000": { "timestamp": 1644926760000000000, "value": 2843 },
|
||||||
|
"1644926820000000000": { "timestamp": 1644926820000000000, "value": 2864 },
|
||||||
|
"1644926880000000000": { "timestamp": 1644926880000000000, "value": 2777 },
|
||||||
|
"1644926940000000000": { "timestamp": 1644926940000000000, "value": 2820 },
|
||||||
|
"1644927000000000000": { "timestamp": 1644927000000000000, "value": 2579 },
|
||||||
|
"1644927060000000000": { "timestamp": 1644927060000000000, "value": 2681 },
|
||||||
|
"1644927120000000000": { "timestamp": 1644927120000000000, "value": 2828 },
|
||||||
|
"1644927180000000000": { "timestamp": 1644927180000000000, "value": 2975 },
|
||||||
|
"1644927240000000000": { "timestamp": 1644927240000000000, "value": 2934 },
|
||||||
|
"1644927300000000000": { "timestamp": 1644927300000000000, "value": 2793 },
|
||||||
|
"1644927360000000000": { "timestamp": 1644927360000000000, "value": 2913 },
|
||||||
|
"1644927420000000000": { "timestamp": 1644927420000000000, "value": 2621 },
|
||||||
|
"1644927480000000000": { "timestamp": 1644927480000000000, "value": 2631 },
|
||||||
|
"1644927540000000000": { "timestamp": 1644927540000000000, "value": 2924 },
|
||||||
|
"1644927600000000000": { "timestamp": 1644927600000000000, "value": 2576 },
|
||||||
|
"1644927660000000000": { "timestamp": 1644927660000000000, "value": 2878 },
|
||||||
|
"1644927720000000000": { "timestamp": 1644927720000000000, "value": 2737 },
|
||||||
|
"1644927780000000000": { "timestamp": 1644927780000000000, "value": 2621 },
|
||||||
|
"1644927840000000000": { "timestamp": 1644927840000000000, "value": 2823 },
|
||||||
|
"1644927900000000000": { "timestamp": 1644927900000000000, "value": 3081 },
|
||||||
|
"1644927960000000000": { "timestamp": 1644927960000000000, "value": 2883 },
|
||||||
|
"1644928020000000000": { "timestamp": 1644928020000000000, "value": 2823 },
|
||||||
|
"1644928080000000000": { "timestamp": 1644928080000000000, "value": 455 }
|
||||||
|
}
|
||||||
|
}
|
19
frontend/cypress/fixtures/trace/initialSpanFilter.json
Normal file
19
frontend/cypress/fixtures/trace/initialSpanFilter.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"serviceName": {
|
||||||
|
"customer": 1642,
|
||||||
|
"driver": 1642,
|
||||||
|
"frontend": 39408,
|
||||||
|
"mysql": 1642,
|
||||||
|
"redis": 22167,
|
||||||
|
"route": 16420
|
||||||
|
},
|
||||||
|
"status": { "error": 4105, "ok": 78816 },
|
||||||
|
"duration": { "maxDuration": 1253979000, "minDuration": 415000 },
|
||||||
|
"operation": {},
|
||||||
|
"httpCode": {},
|
||||||
|
"httpUrl": {},
|
||||||
|
"httpMethod": {},
|
||||||
|
"httpRoute": {},
|
||||||
|
"httpHost": {},
|
||||||
|
"component": {}
|
||||||
|
}
|
105
frontend/cypress/fixtures/trace/initialSpans.json
Normal file
105
frontend/cypress/fixtures/trace/initialSpans.json
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
"spans": [
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:09.542074Z",
|
||||||
|
"spanID": "303b39065c6f5df5",
|
||||||
|
"traceID": "00000000000000007fc49fab3cb75958",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 313418000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:08.84038Z",
|
||||||
|
"spanID": "557e8303bc802992",
|
||||||
|
"traceID": "000000000000000079310bd1d435a92b",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 318203000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:08.867689Z",
|
||||||
|
"spanID": "347113dd916dd20e",
|
||||||
|
"traceID": "00000000000000004c22c0409cee0f66",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 512810000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:07.060882Z",
|
||||||
|
"spanID": "0a8d07f72aa1339b",
|
||||||
|
"traceID": "0000000000000000488e11a35959de96",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 588705000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:07.134107Z",
|
||||||
|
"spanID": "0acd4ec344675998",
|
||||||
|
"traceID": "00000000000000000292efc7945d9bfa",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 801632000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:06.474095Z",
|
||||||
|
"spanID": "3ae72e433301822a",
|
||||||
|
"traceID": "00000000000000001ac3004ff1b7eefe",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 306650000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:06.996246Z",
|
||||||
|
"spanID": "1d765427af673039",
|
||||||
|
"traceID": "00000000000000002e78f59fabbcdecf",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 311469000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:05.324296Z",
|
||||||
|
"spanID": "0987c90d83298a1d",
|
||||||
|
"traceID": "0000000000000000077bcb960609a350",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 290680000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:02.458221Z",
|
||||||
|
"spanID": "5b0d0d403dd9acf4",
|
||||||
|
"traceID": "00000000000000007ae5b0aa69242556",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 262763000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": "2022-02-15T12:16:00.584939Z",
|
||||||
|
"spanID": "3beafb277a76b9b4",
|
||||||
|
"traceID": "00000000000000000ab44953c2fd949e",
|
||||||
|
"serviceName": "customer",
|
||||||
|
"operation": "HTTP GET /customer",
|
||||||
|
"durationNano": 302851000,
|
||||||
|
"httpCode": "200",
|
||||||
|
"httpMethod": "GET"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"totalSpans": 82921
|
||||||
|
}
|
154
frontend/cypress/integration/trace/index.spec.ts
Normal file
154
frontend/cypress/integration/trace/index.spec.ts
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import { TraceFilterEnum } from 'types/reducer/trace';
|
||||||
|
import TableInitialResponse from '../../fixtures/trace/initialSpans.json';
|
||||||
|
import FilterInitialResponse from '../../fixtures/trace/initialSpanFilter.json';
|
||||||
|
import GraphInitialResponse from '../../fixtures/trace/initialAggregates.json';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
|
||||||
|
describe('Trace', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
window.localStorage.setItem('isLoggedIn', 'yes');
|
||||||
|
|
||||||
|
cy
|
||||||
|
.intercept('POST', '**/aggregates', {
|
||||||
|
fixture: 'trace/initialAggregates',
|
||||||
|
})
|
||||||
|
.as('Graph');
|
||||||
|
|
||||||
|
cy
|
||||||
|
.intercept('POST', '**/getFilteredSpans', {
|
||||||
|
fixture: 'trace/initialSpans',
|
||||||
|
})
|
||||||
|
.as('Table');
|
||||||
|
|
||||||
|
cy
|
||||||
|
.intercept('POST', '**/api/v1/getSpanFilters', {
|
||||||
|
fixture: 'trace/initialSpanFilter',
|
||||||
|
})
|
||||||
|
.as('Filters');
|
||||||
|
|
||||||
|
cy.visit(Cypress.env('baseUrl') + `${ROUTES.TRACE}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('First Initial Load should go with 3 AJAX request', () => {
|
||||||
|
cy.wait(['@Filters', '@Graph', '@Table']).then((e) => {
|
||||||
|
const [filter, graph, table] = e;
|
||||||
|
|
||||||
|
const { body: filterBody } = filter.request;
|
||||||
|
const { body: graphBody } = graph.request;
|
||||||
|
const { body: tableBody } = table.request;
|
||||||
|
|
||||||
|
expect(filterBody.exclude.length).to.equal(0);
|
||||||
|
expect(filterBody.getFilters.length).to.equal(3);
|
||||||
|
filterBody.getFilters.forEach((filter: TraceFilterEnum) => {
|
||||||
|
expect(filter).to.be.oneOf(['duration', 'status', 'serviceName']);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(graphBody.function).to.be.equal('count');
|
||||||
|
expect(graphBody.exclude.length).to.be.equal(0);
|
||||||
|
expect(typeof graphBody.exclude).to.be.equal('object');
|
||||||
|
|
||||||
|
expect(tableBody.tags.length).to.be.equal(0);
|
||||||
|
expect(typeof tableBody.tags).equal('object');
|
||||||
|
|
||||||
|
expect(tableBody.exclude.length).equals(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Render Time Request Response In All 3 Request', () => {
|
||||||
|
cy.wait(['@Filters', '@Graph', '@Table']).then((e) => {
|
||||||
|
const [filter, graph, table] = e;
|
||||||
|
|
||||||
|
expect(filter.response?.body).to.be.not.undefined;
|
||||||
|
expect(filter.response?.body).to.be.not.NaN;
|
||||||
|
|
||||||
|
expect(JSON.stringify(filter.response?.body)).to.be.equals(
|
||||||
|
JSON.stringify(FilterInitialResponse),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(JSON.stringify(graph.response?.body)).to.be.equals(
|
||||||
|
JSON.stringify(GraphInitialResponse),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(JSON.stringify(table.response?.body)).to.be.equals(
|
||||||
|
JSON.stringify(TableInitialResponse),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
cy.get('@Filters.all').should('have.length', 1);
|
||||||
|
cy.get('@Graph.all').should('have.length', 1);
|
||||||
|
cy.get('@Table.all').should('have.length', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Clear All', () => {
|
||||||
|
cy.wait(['@Filters', '@Graph', '@Table']);
|
||||||
|
|
||||||
|
expect(cy.findAllByText('Clear All')).not.to.be.undefined;
|
||||||
|
|
||||||
|
cy
|
||||||
|
.window()
|
||||||
|
.its('store')
|
||||||
|
.invoke('getState')
|
||||||
|
.then((e: AppState) => {
|
||||||
|
const { traces } = e;
|
||||||
|
expect(traces.isFilterExclude.get('status')).to.be.undefined;
|
||||||
|
expect(traces.selectedFilter.size).to.be.equals(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.findAllByText('Clear All').then((e) => {
|
||||||
|
const [firstStatusClear] = e;
|
||||||
|
|
||||||
|
firstStatusClear.click();
|
||||||
|
|
||||||
|
cy.wait(['@Filters', '@Graph', '@Table']);
|
||||||
|
|
||||||
|
// insuring the api get call
|
||||||
|
cy.get('@Filters.all').should('have.length', 2);
|
||||||
|
cy.get('@Graph.all').should('have.length', 2);
|
||||||
|
cy.get('@Table.all').should('have.length', 2);
|
||||||
|
|
||||||
|
cy
|
||||||
|
.window()
|
||||||
|
.its('store')
|
||||||
|
.invoke('getState')
|
||||||
|
.then((e: AppState) => {
|
||||||
|
const { traces } = e;
|
||||||
|
|
||||||
|
expect(traces.isFilterExclude.get('status')).to.be.equals(false);
|
||||||
|
expect(traces.userSelectedFilter.get('status')).to.be.undefined;
|
||||||
|
expect(traces.selectedFilter.size).to.be.equals(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Un Selecting one option from status', () => {
|
||||||
|
cy.wait(['@Filters', '@Graph', '@Table']);
|
||||||
|
|
||||||
|
cy.get('input[type="checkbox"]').then((e) => {
|
||||||
|
const [errorCheckbox] = e;
|
||||||
|
errorCheckbox.click();
|
||||||
|
|
||||||
|
cy.wait(['@Filters', '@Graph', '@Table']).then((e) => {
|
||||||
|
const [filter, graph, table] = e;
|
||||||
|
const filterBody = filter.request.body;
|
||||||
|
const graphBody = graph.request.body;
|
||||||
|
const tableBody = table.request.body;
|
||||||
|
|
||||||
|
expect(filterBody.exclude).not.to.be.undefined;
|
||||||
|
expect(filterBody.exclude.length).not.to.be.equal(0);
|
||||||
|
expect(filterBody.exclude[0] === 'status').to.be.true;
|
||||||
|
|
||||||
|
expect(graphBody.exclude).not.to.be.undefined;
|
||||||
|
expect(graphBody.exclude.length).not.to.be.equal(0);
|
||||||
|
expect(graphBody.exclude[0] === 'status').to.be.true;
|
||||||
|
|
||||||
|
expect(tableBody.exclude).not.to.be.undefined;
|
||||||
|
expect(tableBody.exclude.length).not.to.be.equal(0);
|
||||||
|
expect(tableBody.exclude[0] === 'status').to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.get('@Filters.all').should('have.length', 2);
|
||||||
|
cy.get('@Graph.all').should('have.length', 2);
|
||||||
|
cy.get('@Table.all').should('have.length', 2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
51
frontend/src/components/RouteTab/index.tsx
Normal file
51
frontend/src/components/RouteTab/index.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Tabs, TabsProps } from 'antd';
|
||||||
|
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
import history from 'lib/history';
|
||||||
|
|
||||||
|
const RouteTab = ({
|
||||||
|
routes,
|
||||||
|
activeKey,
|
||||||
|
onChangeHandler,
|
||||||
|
...rest
|
||||||
|
}: RouteTabProps & TabsProps): JSX.Element => {
|
||||||
|
const onChange = (activeRoute: string) => {
|
||||||
|
onChangeHandler && onChangeHandler();
|
||||||
|
|
||||||
|
const selectedRoute = routes.find((e) => e.name === activeRoute);
|
||||||
|
|
||||||
|
if (selectedRoute) {
|
||||||
|
history.push(selectedRoute.route);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs
|
||||||
|
onChange={onChange}
|
||||||
|
destroyInactiveTabPane
|
||||||
|
activeKey={activeKey}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{routes.map(
|
||||||
|
({ Component, name }): JSX.Element => (
|
||||||
|
<TabPane tab={name} key={name}>
|
||||||
|
<Component />
|
||||||
|
</TabPane>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RouteTabProps {
|
||||||
|
routes: {
|
||||||
|
name: string;
|
||||||
|
route: string;
|
||||||
|
Component: () => JSX.Element;
|
||||||
|
}[];
|
||||||
|
activeKey: TabsProps['activeKey'];
|
||||||
|
onChangeHandler?: VoidFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RouteTab;
|
@ -8,7 +8,7 @@ const TextToolTip = ({ text, url }: TextToolTipProps) => (
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{`${text} `}
|
{`${text} `}
|
||||||
<a href={url} target={'_blank'}>
|
<a href={url} rel="noopener noreferrer" target={'_blank'}>
|
||||||
here
|
here
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
import { CloseCircleOutlined, CommentOutlined } from '@ant-design/icons';
|
|
||||||
import { Button, Divider, Form, Input, notification, Typography } from 'antd';
|
|
||||||
import { Callbacks } from 'rc-field-form/lib/interface';
|
|
||||||
import React, { useCallback, useState } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Button as IconButton,
|
|
||||||
ButtonContainer,
|
|
||||||
Card,
|
|
||||||
CenterText,
|
|
||||||
Container,
|
|
||||||
TitleContainer,
|
|
||||||
FormItem,
|
|
||||||
} from './styles';
|
|
||||||
const { Title } = Typography;
|
|
||||||
const { TextArea } = Input;
|
|
||||||
import sendFeedbackApi from 'api/userFeedback/sendFeedback';
|
|
||||||
|
|
||||||
const Feedback = (): JSX.Element => {
|
|
||||||
const [isOpen, setisOpen] = useState<boolean>(false);
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const [notifications, Element] = notification.useNotification();
|
|
||||||
|
|
||||||
const isToggleHandler = useCallback(() => {
|
|
||||||
setisOpen((state) => !state);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onFinishHandler: Callbacks<Feedback>['onFinish'] = async (
|
|
||||||
value: Feedback,
|
|
||||||
): Promise<void> => {
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
const { feedback, email = '' } = value;
|
|
||||||
|
|
||||||
const response = await sendFeedbackApi({
|
|
||||||
email,
|
|
||||||
message: feedback,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response === 200) {
|
|
||||||
notifications.success({
|
|
||||||
message: 'Thanks for your feedback!',
|
|
||||||
description:
|
|
||||||
'We have noted down your feedback and will work on improving SIgNoz based on that!',
|
|
||||||
});
|
|
||||||
|
|
||||||
isToggleHandler();
|
|
||||||
} else {
|
|
||||||
notifications.error({
|
|
||||||
message: 'Error!',
|
|
||||||
description: 'Something went wrong',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
} catch (error) {
|
|
||||||
notifications.error({
|
|
||||||
message: 'Something went wrong',
|
|
||||||
});
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
{!isOpen && (
|
|
||||||
<IconButton onClick={isToggleHandler} type="primary" size="large">
|
|
||||||
<CommentOutlined />
|
|
||||||
</IconButton>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{Element}
|
|
||||||
|
|
||||||
{isOpen && (
|
|
||||||
<Form onFinish={onFinishHandler} form={form}>
|
|
||||||
<Card>
|
|
||||||
<TitleContainer>
|
|
||||||
<Title
|
|
||||||
aria-label="How can we improve SigNoz?"
|
|
||||||
style={{ margin: 0 }}
|
|
||||||
level={5}
|
|
||||||
>
|
|
||||||
How can we improve SigNoz?
|
|
||||||
</Title>
|
|
||||||
<CloseCircleOutlined onClick={isToggleHandler} />
|
|
||||||
</TitleContainer>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<FormItem name="feedback" required>
|
|
||||||
<TextArea
|
|
||||||
required
|
|
||||||
rows={3}
|
|
||||||
placeholder="Share what can we improve ( e.g. Not able to find how to see metrics... )"
|
|
||||||
/>
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<FormItem name="email">
|
|
||||||
<Input type="email" placeholder="Email (optional)" />
|
|
||||||
</FormItem>
|
|
||||||
|
|
||||||
<CenterText>This will just be visible to our maintainers</CenterText>
|
|
||||||
|
|
||||||
<ButtonContainer>
|
|
||||||
<Button
|
|
||||||
disabled={isLoading}
|
|
||||||
loading={isLoading}
|
|
||||||
htmlType="submit"
|
|
||||||
type="primary"
|
|
||||||
>
|
|
||||||
Share
|
|
||||||
</Button>
|
|
||||||
</ButtonContainer>
|
|
||||||
</Card>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Feedback {
|
|
||||||
email?: string;
|
|
||||||
feedback: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Feedback;
|
|
@ -1,64 +0,0 @@
|
|||||||
import {
|
|
||||||
Button as ButtonComponent,
|
|
||||||
Card as CardComponent,
|
|
||||||
Typography,
|
|
||||||
Form,
|
|
||||||
} from 'antd';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
export const Container = styled.div`
|
|
||||||
position: fixed;
|
|
||||||
bottom: 5%;
|
|
||||||
right: 4%;
|
|
||||||
z-index: 999999;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const CenterText = styled(Typography)`
|
|
||||||
&&& {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const TitleContainer = styled.div`
|
|
||||||
&&& {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Card = styled(CardComponent)`
|
|
||||||
&&& {
|
|
||||||
min-width: 400px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const ButtonContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Button = styled(ButtonComponent)`
|
|
||||||
height: 4rem !important;
|
|
||||||
width: 4rem !important;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
border-radius: 25px !important;
|
|
||||||
|
|
||||||
background-color: #65b7f3;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 2rem;
|
|
||||||
height: 2rem;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const FormItem = styled(Form.Item)`
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
`;
|
|
@ -7,7 +7,6 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import Feedback from './FeedBack';
|
|
||||||
import { Content, Footer, Layout } from './styles';
|
import { Content, Footer, Layout } from './styles';
|
||||||
|
|
||||||
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||||
@ -40,8 +39,6 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
|||||||
</Content>
|
</Content>
|
||||||
<Footer>{`SigNoz Inc. © ${currentYear}`}</Footer>
|
<Footer>{`SigNoz Inc. © ${currentYear}`}</Footer>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<Feedback />
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -9,17 +9,10 @@ import FormAlertChannels from 'container/FormAlertChannels';
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { Store } from 'rc-field-form/lib/interface';
|
import { Store } from 'rc-field-form/lib/interface';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { useParams } from 'react-router';
|
import { useParams } from 'react-router';
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
|
||||||
import { ToggleSettingsTab } from 'store/actions';
|
|
||||||
import AppActions from 'types/actions';
|
|
||||||
import { SettingTab } from 'types/reducer/app';
|
|
||||||
|
|
||||||
const EditAlertChannels = ({
|
const EditAlertChannels = ({
|
||||||
initialValue,
|
initialValue,
|
||||||
toggleSettingsTab,
|
|
||||||
}: EditAlertChannelsProps): JSX.Element => {
|
}: EditAlertChannelsProps): JSX.Element => {
|
||||||
const [formInstance] = Form.useForm();
|
const [formInstance] = Form.useForm();
|
||||||
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
const [selectedConfig, setSelectedConfig] = useState<Partial<SlackChannel>>({
|
||||||
@ -52,7 +45,6 @@ const EditAlertChannels = ({
|
|||||||
message: 'Success',
|
message: 'Success',
|
||||||
description: 'Channels Edited Successfully',
|
description: 'Channels Edited Successfully',
|
||||||
});
|
});
|
||||||
toggleSettingsTab('Alert Channels');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
history.replace(ROUTES.SETTINGS);
|
history.replace(ROUTES.SETTINGS);
|
||||||
@ -100,18 +92,8 @@ const EditAlertChannels = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface DispatchProps {
|
interface EditAlertChannelsProps {
|
||||||
toggleSettingsTab: (props: SettingTab) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
|
||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
|
||||||
): DispatchProps => ({
|
|
||||||
toggleSettingsTab: bindActionCreators(ToggleSettingsTab, dispatch),
|
|
||||||
});
|
|
||||||
|
|
||||||
interface EditAlertChannelsProps extends DispatchProps {
|
|
||||||
initialValue: Store;
|
initialValue: Store;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(EditAlertChannels);
|
export default EditAlertChannels;
|
||||||
|
@ -2,14 +2,17 @@ import { Col } from 'antd';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation, matchPath } from 'react-router-dom';
|
||||||
|
|
||||||
import ShowBreadcrumbs from './Breadcrumbs';
|
import ShowBreadcrumbs from './Breadcrumbs';
|
||||||
import DateTimeSelector from './DateTimeSelection';
|
import DateTimeSelector from './DateTimeSelection';
|
||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
const routesToSkip = [ROUTES.SETTINGS, ROUTES.LIST_ALL_ALERT];
|
const routesToSkip = [
|
||||||
|
ROUTES.SETTINGS,
|
||||||
|
ROUTES.LIST_ALL_ALERT,
|
||||||
|
ROUTES.TRACE_GRAPH,
|
||||||
|
];
|
||||||
|
|
||||||
const TopNav = (): JSX.Element | null => {
|
const TopNav = (): JSX.Element | null => {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
@ -18,13 +21,24 @@ const TopNav = (): JSX.Element | null => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkRouteExists = (currentPath: string) => {
|
||||||
|
for (let i = 0; i < routesToSkip.length; ++i) {
|
||||||
|
if (
|
||||||
|
matchPath(currentPath, { path: routesToSkip[i], exact: true, strict: true })
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Col span={16}>
|
<Col span={16}>
|
||||||
<ShowBreadcrumbs />
|
<ShowBreadcrumbs />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
{!routesToSkip.includes(pathname) && (
|
{!checkRouteExists(pathname) && (
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<DateTimeSelector />
|
<DateTimeSelector />
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
import { Tabs } from 'antd';
|
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import history from 'lib/history';
|
|
||||||
import React, { useCallback } from 'react';
|
|
||||||
import { connect, useSelector } from 'react-redux';
|
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
|
||||||
import { ToggleSettingsTab } from 'store/actions';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import AppActions from 'types/actions';
|
|
||||||
import AppReducer, { SettingTab } from 'types/reducer/app';
|
|
||||||
const { TabPane } = Tabs;
|
|
||||||
|
|
||||||
const SettingsWrapper = ({
|
|
||||||
AlertChannels,
|
|
||||||
General,
|
|
||||||
toggleSettingsTab,
|
|
||||||
defaultRoute,
|
|
||||||
}: SettingsWrapperProps): JSX.Element => {
|
|
||||||
const { settingsActiveTab } = useSelector<AppState, AppReducer>(
|
|
||||||
(state) => state.app,
|
|
||||||
);
|
|
||||||
|
|
||||||
const onChangeHandler = useCallback(
|
|
||||||
(value: SettingTab) => {
|
|
||||||
toggleSettingsTab(value);
|
|
||||||
if (value === 'General') {
|
|
||||||
history.push(ROUTES.SETTINGS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'Alert Channels') {
|
|
||||||
history.push(ROUTES.ALL_CHANNELS);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[toggleSettingsTab],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tabs
|
|
||||||
destroyInactiveTabPane
|
|
||||||
onChange={(value): void => onChangeHandler(value as SettingTab)}
|
|
||||||
activeKey={defaultRoute || settingsActiveTab}
|
|
||||||
>
|
|
||||||
<TabPane tab="General" key="General">
|
|
||||||
<General />
|
|
||||||
</TabPane>
|
|
||||||
<TabPane tab="Alert Channels" key="Alert Channels">
|
|
||||||
<AlertChannels />
|
|
||||||
</TabPane>
|
|
||||||
</Tabs>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface DispatchProps {
|
|
||||||
toggleSettingsTab: (props: SettingTab) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
|
||||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
|
||||||
): DispatchProps => ({
|
|
||||||
toggleSettingsTab: bindActionCreators(ToggleSettingsTab, dispatch),
|
|
||||||
});
|
|
||||||
|
|
||||||
interface SettingsWrapperProps extends DispatchProps {
|
|
||||||
General: () => JSX.Element;
|
|
||||||
AlertChannels: () => JSX.Element;
|
|
||||||
defaultRoute?: SettingTab;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(SettingsWrapper);
|
|
48
frontend/src/container/SideNav/Slack.tsx
Normal file
48
frontend/src/container/SideNav/Slack.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Slack = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="28"
|
||||||
|
height="28"
|
||||||
|
viewBox="0 0 28 28"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M6.08886 17.6001C6.08886 19.1778 4.79997 20.4667 3.2222 20.4667C1.64442 20.4667 0.35553 19.1778 0.35553 17.6001C0.35553 16.0223 1.64442 14.7334 3.2222 14.7334H6.08886V17.6001Z"
|
||||||
|
fill="#E01E5A"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M7.53333 17.6001C7.53333 16.0223 8.82221 14.7334 10.4 14.7334C11.9778 14.7334 13.2667 16.0223 13.2667 17.6001V24.7778C13.2667 26.3556 11.9778 27.6445 10.4 27.6445C8.82221 27.6445 7.53333 26.3556 7.53333 24.7778V17.6001Z"
|
||||||
|
fill="#E01E5A"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10.4 6.08892C8.82221 6.08892 7.53333 4.80004 7.53333 3.22226C7.53333 1.64448 8.82221 0.355591 10.4 0.355591C11.9778 0.355591 13.2667 1.64448 13.2667 3.22226V6.08892H10.4Z"
|
||||||
|
fill="#36C5F0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M10.4 7.53333C11.9778 7.53333 13.2666 8.82221 13.2666 10.4C13.2666 11.9778 11.9778 13.2667 10.4 13.2667H3.2222C1.64442 13.2667 0.35553 11.9778 0.35553 10.4C0.35553 8.82221 1.64442 7.53333 3.2222 7.53333H10.4Z"
|
||||||
|
fill="#36C5F0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M21.9111 10.4C21.9111 8.82221 23.2 7.53333 24.7778 7.53333C26.3556 7.53333 27.6445 8.82221 27.6445 10.4C27.6445 11.9778 26.3556 13.2667 24.7778 13.2667H21.9111V10.4Z"
|
||||||
|
fill="#2EB67D"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M20.4667 10.4C20.4667 11.9778 19.1778 13.2667 17.6 13.2667C16.0222 13.2667 14.7333 11.9778 14.7333 10.4V3.22226C14.7333 1.64448 16.0222 0.355591 17.6 0.355591C19.1778 0.355591 20.4667 1.64448 20.4667 3.22226V10.4Z"
|
||||||
|
fill="#2EB67D"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17.6 21.9111C19.1778 21.9111 20.4667 23.2 20.4667 24.7778C20.4667 26.3556 19.1778 27.6445 17.6 27.6445C16.0222 27.6445 14.7333 26.3556 14.7333 24.7778V21.9111H17.6Z"
|
||||||
|
fill="#ECB22E"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17.6 20.4667C16.0222 20.4667 14.7333 19.1778 14.7333 17.6001C14.7333 16.0223 16.0222 14.7334 17.6 14.7334H24.7778C26.3556 14.7334 27.6444 16.0223 27.6444 17.6001C27.6444 19.1778 26.3556 20.4667 24.7778 20.4667H17.6Z"
|
||||||
|
fill="#ECB22E"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Slack;
|
@ -1,5 +1,10 @@
|
|||||||
import { Menu, Typography } from 'antd';
|
import { Menu, Typography } from 'antd';
|
||||||
import { ToggleButton } from './styles';
|
import {
|
||||||
|
MenuItem,
|
||||||
|
SlackButton,
|
||||||
|
SlackMenuItemContainer,
|
||||||
|
ToggleButton,
|
||||||
|
} from './styles';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
@ -17,6 +22,7 @@ import setTheme from 'lib/theme/setTheme';
|
|||||||
|
|
||||||
import menus from './menuItems';
|
import menus from './menuItems';
|
||||||
import { Logo, Sider, ThemeSwitcherWrapper } from './styles';
|
import { Logo, Sider, ThemeSwitcherWrapper } from './styles';
|
||||||
|
import Slack from './Slack';
|
||||||
|
|
||||||
const SideNav = ({ toggleDarkMode }: Props): JSX.Element => {
|
const SideNav = ({ toggleDarkMode }: Props): JSX.Element => {
|
||||||
const [collapsed, setCollapsed] = useState<boolean>(false);
|
const [collapsed, setCollapsed] = useState<boolean>(false);
|
||||||
@ -57,10 +63,18 @@ const SideNav = ({ toggleDarkMode }: Props): JSX.Element => {
|
|||||||
[pathname],
|
[pathname],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onClickSlackHandler = () => {
|
||||||
|
window.open('https://signoz.io/slack', '_blank');
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
|
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
|
||||||
<ThemeSwitcherWrapper>
|
<ThemeSwitcherWrapper>
|
||||||
<ToggleButton checked={isDarkMode} onChange={toggleTheme} defaultChecked={isDarkMode} />
|
<ToggleButton
|
||||||
|
checked={isDarkMode}
|
||||||
|
onChange={toggleTheme}
|
||||||
|
defaultChecked={isDarkMode}
|
||||||
|
/>
|
||||||
</ThemeSwitcherWrapper>
|
</ThemeSwitcherWrapper>
|
||||||
<NavLink to={ROUTES.APPLICATION}>
|
<NavLink to={ROUTES.APPLICATION}>
|
||||||
<Logo src={'/signoz.svg'} alt="SigNoz" collapsed={collapsed} />
|
<Logo src={'/signoz.svg'} alt="SigNoz" collapsed={collapsed} />
|
||||||
@ -81,6 +95,12 @@ const SideNav = ({ toggleDarkMode }: Props): JSX.Element => {
|
|||||||
<Typography>{name}</Typography>
|
<Typography>{name}</Typography>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
<SlackMenuItemContainer collapsed={collapsed}>
|
||||||
|
<MenuItem onClick={onClickSlackHandler} icon={<Slack />}>
|
||||||
|
<SlackButton>Support</SlackButton>
|
||||||
|
</MenuItem>
|
||||||
|
</SlackMenuItemContainer>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Sider>
|
</Sider>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Layout, Switch } from 'antd';
|
import { Layout, Menu, Switch, Typography } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
const { Sider: SiderComponent } = Layout;
|
const { Sider: SiderComponent } = Layout;
|
||||||
|
|
||||||
export const ThemeSwitcherWrapper = styled.div`
|
export const ThemeSwitcherWrapper = styled.div`
|
||||||
@ -23,6 +23,9 @@ export const Sider = styled(SiderComponent)`
|
|||||||
.ant-typography {
|
.ant-typography {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
.ant-layout-sider-trigger {
|
||||||
|
background-color: #1f1f1f;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface DarkModeProps {
|
interface DarkModeProps {
|
||||||
@ -36,3 +39,41 @@ export const ToggleButton = styled(Switch)<DarkModeProps>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const SlackButton = styled(Typography)`
|
||||||
|
&&& {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MenuItem = styled(Menu.Item)`
|
||||||
|
&&& {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 48px;
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
background: #262626;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SlackMenuItemContainer = styled.div<LogoProps>`
|
||||||
|
&&& {
|
||||||
|
li {
|
||||||
|
${({ collapsed }) =>
|
||||||
|
collapsed &&
|
||||||
|
css`
|
||||||
|
padding-left: 24px;
|
||||||
|
`}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-left: ${({ collapsed }) => (collapsed ? '0' : '24px')};
|
||||||
|
|
||||||
|
${({ collapsed }) =>
|
||||||
|
collapsed &&
|
||||||
|
css`
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
`}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -164,7 +164,7 @@ const PanelHeading = (props: PanelHeadingProps): JSX.Element => {
|
|||||||
end: String(global.maxTime),
|
end: String(global.maxTime),
|
||||||
start: String(global.minTime),
|
start: String(global.minTime),
|
||||||
getFilters: filterToFetchData,
|
getFilters: filterToFetchData,
|
||||||
other: Object.fromEntries(preUserSelected),
|
other: Object.fromEntries(updatedFilter),
|
||||||
isFilterExclude: postIsFilterExclude,
|
isFilterExclude: postIsFilterExclude,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ const TraceGraph = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Graph data={ChartData} name="traceGraphph" type="line" />
|
<Graph data={ChartData} name="traceGraph" type="line" />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -56,6 +56,7 @@ const TraceGraphFilter = () => {
|
|||||||
|
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
dropdownMatchSelectWidth
|
dropdownMatchSelectWidth
|
||||||
|
data-testid="selectedFunction"
|
||||||
value={functions.find((e) => selectedFunction === e.key)?.displayValue}
|
value={functions.find((e) => selectedFunction === e.key)?.displayValue}
|
||||||
onChange={onClickSelectedFunctionHandler}
|
onChange={onClickSelectedFunctionHandler}
|
||||||
>
|
>
|
||||||
@ -69,6 +70,7 @@ const TraceGraphFilter = () => {
|
|||||||
<label>Group By</label>
|
<label>Group By</label>
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
dropdownMatchSelectWidth
|
dropdownMatchSelectWidth
|
||||||
|
data-testid="selectedGroupBy"
|
||||||
value={groupBy.find((e) => selectedGroupBy === e.key)?.displayValue}
|
value={groupBy.find((e) => selectedGroupBy === e.key)?.displayValue}
|
||||||
onChange={onClickSelectedGroupByHandler}
|
onChange={onClickSelectedGroupByHandler}
|
||||||
>
|
>
|
||||||
|
@ -43,7 +43,7 @@ const TraceTable = ({ getSpansAggregate }: TraceProps) => {
|
|||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
render: (value: TableType['timestamp']) => {
|
render: (value: TableType['timestamp']) => {
|
||||||
const day = dayjs(value);
|
const day = dayjs(value);
|
||||||
return <div>{day.format('DD/MM/YYYY HH:MM:ss A')}</div>;
|
return <div>{day.format('DD/MM/YYYY hh:mm:ss A')}</div>;
|
||||||
},
|
},
|
||||||
sorter: (a, b) =>
|
sorter: (a, b) =>
|
||||||
dayjs(a.timestamp).toDate().getTime() -
|
dayjs(a.timestamp).toDate().getTime() -
|
||||||
@ -131,6 +131,9 @@ const TraceTable = ({ getSpansAggregate }: TraceProps) => {
|
|||||||
})}
|
})}
|
||||||
size="middle"
|
size="middle"
|
||||||
rowKey={'timestamp'}
|
rowKey={'timestamp'}
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
pagination={{
|
pagination={{
|
||||||
current: spansAggregate.currentPage,
|
current: spansAggregate.currentPage,
|
||||||
pageSize: spansAggregate.pageSize,
|
pageSize: spansAggregate.pageSize,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
|
|
||||||
export interface DebouncedFunc<T extends (...args: any[]) => any> {
|
export interface DebouncedFunc<T extends (...args: any[]) => any> {
|
||||||
@ -26,9 +26,13 @@ const useDebouncedFn = <T extends (...args: any) => any>(
|
|||||||
options: DebounceOptions = defaultOptions,
|
options: DebounceOptions = defaultOptions,
|
||||||
dependencies?: ReadonlyArray<any>,
|
dependencies?: ReadonlyArray<any>,
|
||||||
): DebouncedFunc<T> => {
|
): DebouncedFunc<T> => {
|
||||||
const debounced = debounce(fn, wait, options);
|
const fnRef = useRef(fn);
|
||||||
|
fnRef.current = fn;
|
||||||
|
|
||||||
return useCallback(debounced, dependencies || []);
|
return useMemo(
|
||||||
|
() => debounce(((...args) => fnRef.current(...args)) as T, wait, options),
|
||||||
|
[...(dependencies || [])],
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useDebouncedFn;
|
export default useDebouncedFn;
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
|
import RouteTab from 'components/RouteTab';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
import CreateAlertChannels from 'container/CreateAlertChannels';
|
import CreateAlertChannels from 'container/CreateAlertChannels';
|
||||||
import GeneralSettings from 'container/GeneralSettings';
|
import GeneralSettings from 'container/GeneralSettings';
|
||||||
import SettingsWrapper from 'container/SettingsWrapper';
|
import history from 'lib/history';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const SettingsPage = (): JSX.Element => {
|
const SettingsPage = (): JSX.Element => {
|
||||||
const AlertChannels = (): JSX.Element => {
|
const pathName = history.location.pathname;
|
||||||
return <CreateAlertChannels />;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsWrapper
|
<RouteTab
|
||||||
{...{
|
{...{
|
||||||
AlertChannels,
|
routes: [
|
||||||
General: GeneralSettings,
|
{
|
||||||
|
Component: GeneralSettings,
|
||||||
|
name: 'General Settings',
|
||||||
|
route: ROUTES.SETTINGS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Component: () => {
|
||||||
|
return <CreateAlertChannels />;
|
||||||
|
},
|
||||||
|
name: 'Alert Channels',
|
||||||
|
route: ROUTES.ALL_CHANNELS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
activeKey:
|
||||||
|
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,16 +1,33 @@
|
|||||||
import AlertChannels from 'container/AllAlertChannels';
|
import AlertChannels from 'container/AllAlertChannels';
|
||||||
import GeneralSettings from 'container/GeneralSettings';
|
import GeneralSettings from 'container/GeneralSettings';
|
||||||
import SettingsWrapper from 'container/SettingsWrapper';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import RouteTab from 'components/RouteTab';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import history from 'lib/history';
|
||||||
|
|
||||||
const AllAlertChannels = (): JSX.Element => (
|
const AllAlertChannels = (): JSX.Element => {
|
||||||
<SettingsWrapper
|
const pathName = history.location.pathname;
|
||||||
{...{
|
|
||||||
AlertChannels,
|
return (
|
||||||
General: GeneralSettings,
|
<RouteTab
|
||||||
defaultRoute: 'Alert Channels',
|
{...{
|
||||||
}}
|
routes: [
|
||||||
/>
|
{
|
||||||
);
|
Component: GeneralSettings,
|
||||||
|
name: 'General Settings',
|
||||||
|
route: ROUTES.SETTINGS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Component: AlertChannels,
|
||||||
|
name: 'Alert Channels',
|
||||||
|
route: ROUTES.ALL_CHANNELS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
activeKey:
|
||||||
|
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default AllAlertChannels;
|
export default AllAlertChannels;
|
||||||
|
@ -1,14 +1,30 @@
|
|||||||
import AlertChannels from 'container/AllAlertChannels';
|
import AlertChannels from 'container/AllAlertChannels';
|
||||||
import GeneralSettings from 'container/GeneralSettings';
|
import GeneralSettings from 'container/GeneralSettings';
|
||||||
import SettingsWrapper from 'container/SettingsWrapper';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import RouteTab from 'components/RouteTab';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import history from 'lib/history';
|
||||||
|
|
||||||
const SettingsPage = (): JSX.Element => {
|
const SettingsPage = (): JSX.Element => {
|
||||||
|
const pathName = history.location.pathname;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsWrapper
|
<RouteTab
|
||||||
{...{
|
{...{
|
||||||
AlertChannels,
|
routes: [
|
||||||
General: GeneralSettings,
|
{
|
||||||
|
Component: GeneralSettings,
|
||||||
|
name: 'General Settings',
|
||||||
|
route: ROUTES.SETTINGS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Component: AlertChannels,
|
||||||
|
name: 'Alert Channels',
|
||||||
|
route: ROUTES.ALL_CHANNELS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
activeKey:
|
||||||
|
pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General Settings',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,2 +1 @@
|
|||||||
export * from './toggleDarkMode';
|
export * from './toggleDarkMode';
|
||||||
export * from './toggleSettingsTab';
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import { Dispatch } from 'redux';
|
|
||||||
import AppActions from 'types/actions';
|
|
||||||
import { SettingTab } from 'types/reducer/app';
|
|
||||||
|
|
||||||
export const ToggleSettingsTab = (
|
|
||||||
props: SettingTab,
|
|
||||||
): ((dispatch: Dispatch<AppActions>) => void) => {
|
|
||||||
return (dispatch: Dispatch<AppActions>): void => {
|
|
||||||
dispatch({
|
|
||||||
type: 'TOGGLE_SETTINGS_TABS',
|
|
||||||
payload: {
|
|
||||||
activeTab: props,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,10 +1,5 @@
|
|||||||
import { IS_LOGGED_IN } from 'constants/auth';
|
import { IS_LOGGED_IN } from 'constants/auth';
|
||||||
import {
|
import { AppAction, LOGGED_IN, SWITCH_DARK_MODE } from 'types/actions/app';
|
||||||
AppAction,
|
|
||||||
LOGGED_IN,
|
|
||||||
SWITCH_DARK_MODE,
|
|
||||||
TOGGLE_SETTINGS_TABS,
|
|
||||||
} from 'types/actions/app';
|
|
||||||
import getTheme from 'lib/theme/getTheme';
|
import getTheme from 'lib/theme/getTheme';
|
||||||
import InitialValueTypes from 'types/reducer/app';
|
import InitialValueTypes from 'types/reducer/app';
|
||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
@ -12,7 +7,6 @@ import getLocalStorageKey from 'api/browser/localstorage/get';
|
|||||||
const InitialValue: InitialValueTypes = {
|
const InitialValue: InitialValueTypes = {
|
||||||
isDarkMode: getTheme() === 'darkMode' ? true : false,
|
isDarkMode: getTheme() === 'darkMode' ? true : false,
|
||||||
isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes',
|
isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes',
|
||||||
settingsActiveTab: 'General',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const appReducer = (
|
const appReducer = (
|
||||||
@ -34,13 +28,6 @@ const appReducer = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOGGLE_SETTINGS_TABS: {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
settingsActiveTab: action.payload.activeTab,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { SettingTab } from 'types/reducer/app';
|
|
||||||
|
|
||||||
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
|
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
|
||||||
export const LOGGED_IN = 'LOGGED_IN';
|
export const LOGGED_IN = 'LOGGED_IN';
|
||||||
export const TOGGLE_SETTINGS_TABS = 'TOGGLE_SETTINGS_TABS';
|
|
||||||
|
|
||||||
export interface SwitchDarkMode {
|
export interface SwitchDarkMode {
|
||||||
type: typeof SWITCH_DARK_MODE;
|
type: typeof SWITCH_DARK_MODE;
|
||||||
@ -12,11 +9,4 @@ export interface LoggedInUser {
|
|||||||
type: typeof LOGGED_IN;
|
type: typeof LOGGED_IN;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToggleSettingsTab {
|
export type AppAction = SwitchDarkMode | LoggedInUser;
|
||||||
type: typeof TOGGLE_SETTINGS_TABS;
|
|
||||||
payload: {
|
|
||||||
activeTab: SettingTab;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AppAction = SwitchDarkMode | LoggedInUser | ToggleSettingsTab;
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
export type SettingTab = 'General' | 'Alert Channels';
|
|
||||||
export default interface AppReducer {
|
export default interface AppReducer {
|
||||||
isDarkMode: boolean;
|
isDarkMode: boolean;
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
settingsActiveTab: SettingTab;
|
|
||||||
}
|
}
|
||||||
|
@ -144,8 +144,4 @@ const config = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.env.BUNDLE_ANALYSER === 'true') {
|
|
||||||
config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'server' }));
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
@ -2,27 +2,30 @@
|
|||||||
|
|
||||||
Query service is the interface between forntend and databases. It is written in **Golang**. It will have modules for all supported databases. Query service is responsible to:
|
Query service is the interface between forntend and databases. It is written in **Golang**. It will have modules for all supported databases. Query service is responsible to:
|
||||||
- parse the request from Frontend
|
- parse the request from Frontend
|
||||||
- create relevant Druid queries (and all other supported database queries)
|
- create relevant Clickhouse queries (and all other supported database queries)
|
||||||
- parse response from databases and handle error if any
|
- parse response from databases and handle error if any
|
||||||
- build response in the format accepted by Frontend
|
- clickhouse response in the format accepted by Frontend
|
||||||
|
|
||||||
|
|
||||||
#### Druid Queries
|
|
||||||
Internally we use both native and sql queries to Druid.
|
|
||||||
|
|
||||||
#### Configuration
|
#### Configuration
|
||||||
Query Service needs below `env` variables to run:
|
- Open ./constants/constants.go
|
||||||
|
- Replace ```const RELATIONAL_DATASOURCE_PATH = "/var/lib/signoz/signoz.db"``` \
|
||||||
|
with ```const RELATIONAL_DATASOURCE_PATH = "./signoz.db".```
|
||||||
|
|
||||||
|
- Query Service needs below `env` variables to run:
|
||||||
|
|
||||||
```
|
```
|
||||||
DruidClientUrl: http://signoz-druid-router:8888
|
ClickHouseUrl=tcp://localhost:9001
|
||||||
DruidDatasource: flattened_spans
|
STORAGE=clickhouse
|
||||||
```
|
```
|
||||||
The above values are the default ones used by SigNoz and are kept at `deploy/kubernetes/platform/signoz-charts/query-service/values.yaml`
|
|
||||||
|
<!-- The above values are the default ones used by SigNoz and are kept at `deploy/kubernetes/platform/signoz-charts/query-service/values.yaml` -->
|
||||||
|
|
||||||
#### Build and Run locally
|
#### Build and Run locally
|
||||||
```console
|
```console
|
||||||
cd pkg/query-service
|
cd pkg/query-service
|
||||||
go build -o build/query-service main.go
|
go build -o build/query-service main.go
|
||||||
DruidClientUrl=xxxx DruidDatasource=flattened_spans build/query-service
|
ClickHouseUrl=tcp://localhost:9001 STORAGE=clickhouse build/query-service
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Docker Images
|
#### Docker Images
|
||||||
|
@ -1537,7 +1537,7 @@ func (r *ClickHouseReader) GetSpanFilters(ctx context.Context, queryParams *mode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "status":
|
case "status":
|
||||||
finalQuery := fmt.Sprintf("SELECT COUNT(*) as numErrors FROM %s WHERE timestamp >= ? AND timestamp <= ? AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))", r.indexTable)
|
finalQuery := fmt.Sprintf("SELECT COUNT(*) as numErrors FROM %s WHERE timestamp >= ? AND timestamp <= ? AND hasError = 1", r.indexTable)
|
||||||
finalQuery += query
|
finalQuery += query
|
||||||
var dBResponse []model.DBResponseErrors
|
var dBResponse []model.DBResponseErrors
|
||||||
err := r.db.Select(&dBResponse, finalQuery, args...)
|
err := r.db.Select(&dBResponse, finalQuery, args...)
|
||||||
@ -1582,16 +1582,16 @@ func getStatusFilters(query string, statusParams []string, excludeMap map[string
|
|||||||
if _, ok := excludeMap["status"]; ok {
|
if _, ok := excludeMap["status"]; ok {
|
||||||
if len(statusParams) == 1 {
|
if len(statusParams) == 1 {
|
||||||
if statusParams[0] == "error" {
|
if statusParams[0] == "error" {
|
||||||
query += " AND ((NOT ( has(tags, 'error:true')) AND statusCode<500 AND statusCode!=2))"
|
query += " AND hasError = 0"
|
||||||
} else if statusParams[0] == "ok" {
|
} else if statusParams[0] == "ok" {
|
||||||
query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))"
|
query += " AND hasError = 1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if len(statusParams) == 1 {
|
} else if len(statusParams) == 1 {
|
||||||
if statusParams[0] == "error" {
|
if statusParams[0] == "error" {
|
||||||
query += " AND ( ( has(tags, 'error:true') OR statusCode>=500 OR statusCode=2))"
|
query += " AND hasError = 1"
|
||||||
} else if statusParams[0] == "ok" {
|
} else if statusParams[0] == "ok" {
|
||||||
query += " AND ((NOT ( has(tags, 'error:true')) AND statusCode<500 AND statusCode!=2))"
|
query += " AND hasError = 0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return query
|
return query
|
||||||
@ -1839,6 +1839,79 @@ func excludeTags(ctx context.Context, tags []model.TagFilters) []model.TagFilter
|
|||||||
return newTags
|
return newTags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ClickHouseReader) GetTagValues(ctx context.Context, queryParams *model.TagFilterParams) (*[]model.TagValues, *model.ApiError) {
|
||||||
|
|
||||||
|
excludeMap := make(map[string]struct{})
|
||||||
|
for _, e := range queryParams.Exclude {
|
||||||
|
if e == constants.OperationRequest {
|
||||||
|
excludeMap[constants.OperationDB] = struct{}{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
excludeMap[e] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var query string
|
||||||
|
args := []interface{}{queryParams.TagKey, strconv.FormatInt(queryParams.Start.UnixNano(), 10), strconv.FormatInt(queryParams.End.UnixNano(), 10)}
|
||||||
|
if len(queryParams.ServiceName) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.ServiceName, constants.ServiceName, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.HttpRoute) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpRoute, constants.HttpRoute, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.HttpCode) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpCode, constants.HttpCode, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.HttpHost) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpHost, constants.HttpHost, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.HttpMethod) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpMethod, constants.HttpMethod, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.HttpUrl) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.HttpUrl, constants.HttpUrl, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.Component) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.Component, constants.Component, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.Operation) > 0 {
|
||||||
|
args = buildFilterArrayQuery(ctx, excludeMap, queryParams.Operation, constants.OperationDB, &query, args)
|
||||||
|
}
|
||||||
|
if len(queryParams.MinDuration) != 0 {
|
||||||
|
query = query + " AND durationNano >= ?"
|
||||||
|
args = append(args, queryParams.MinDuration)
|
||||||
|
}
|
||||||
|
if len(queryParams.MaxDuration) != 0 {
|
||||||
|
query = query + " AND durationNano <= ?"
|
||||||
|
args = append(args, queryParams.MaxDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
query = getStatusFilters(query, queryParams.Status, excludeMap)
|
||||||
|
|
||||||
|
tagValues := []model.TagValues{}
|
||||||
|
|
||||||
|
finalQuery := fmt.Sprintf(`SELECT tagMap[?] as tagValues FROM %s WHERE timestamp >= ? AND timestamp <= ?`, r.indexTable)
|
||||||
|
finalQuery += query
|
||||||
|
fmt.Println(finalQuery)
|
||||||
|
finalQuery += "GROUP BY tagMap[?]"
|
||||||
|
args = append(args, queryParams.TagKey)
|
||||||
|
err := r.db.Select(&tagValues, finalQuery, args...)
|
||||||
|
|
||||||
|
zap.S().Info(query)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Debug("Error in processing sql query: ", err)
|
||||||
|
return nil, &model.ApiError{model.ErrorExec, fmt.Errorf("Error in processing sql query")}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanedTagValues := []model.TagValues{}
|
||||||
|
for _, e := range tagValues {
|
||||||
|
if e.TagValues != "" {
|
||||||
|
cleanedTagValues = append(cleanedTagValues, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &cleanedTagValues, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *ClickHouseReader) GetServiceDBOverview(ctx context.Context, queryParams *model.GetServiceOverviewParams) (*[]model.ServiceDBOverviewItem, error) {
|
func (r *ClickHouseReader) GetServiceDBOverview(ctx context.Context, queryParams *model.GetServiceOverviewParams) (*[]model.ServiceDBOverviewItem, error) {
|
||||||
|
|
||||||
var serviceDBOverviewItems []model.ServiceDBOverviewItem
|
var serviceDBOverviewItems []model.ServiceDBOverviewItem
|
||||||
@ -2093,7 +2166,7 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, traceId string) (*[
|
|||||||
|
|
||||||
var searchScanReponses []model.SearchSpanReponseItem
|
var searchScanReponses []model.SearchSpanReponseItem
|
||||||
|
|
||||||
query := fmt.Sprintf("SELECT timestamp, spanID, traceID, serviceName, name, kind, durationNano, tagsKeys, tagsValues, references, events FROM %s WHERE traceID=?", r.indexTable)
|
query := fmt.Sprintf("SELECT timestamp, spanID, traceID, serviceName, name, kind, durationNano, tagsKeys, tagsValues, references, events, hasError FROM %s WHERE traceID=?", r.indexTable)
|
||||||
|
|
||||||
err := r.db.Select(&searchScanReponses, query, traceId)
|
err := r.db.Select(&searchScanReponses, query, traceId)
|
||||||
|
|
||||||
@ -2106,7 +2179,7 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, traceId string) (*[
|
|||||||
|
|
||||||
searchSpansResult := []model.SearchSpansResult{
|
searchSpansResult := []model.SearchSpansResult{
|
||||||
model.SearchSpansResult{
|
model.SearchSpansResult{
|
||||||
Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events"},
|
Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError"},
|
||||||
Events: make([][]interface{}, len(searchScanReponses)),
|
Events: make([][]interface{}, len(searchScanReponses)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,10 @@ func (druid *DruidReader) GetTagFilters(_ context.Context, _ *model.TagFilterPar
|
|||||||
return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting tagFilters")}
|
return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting tagFilters")}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (druid *DruidReader) GetTagValues(_ context.Context, _ *model.TagFilterParams) (*[]model.TagValues, *model.ApiError) {
|
||||||
|
return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting tagValues")}
|
||||||
|
}
|
||||||
|
|
||||||
func (druid *DruidReader) GetFilteredSpans(_ context.Context, _ *model.GetFilteredSpansParams) (*model.GetFilterSpansResponse, *model.ApiError) {
|
func (druid *DruidReader) GetFilteredSpans(_ context.Context, _ *model.GetFilteredSpansParams) (*model.GetFilterSpansResponse, *model.ApiError) {
|
||||||
return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting FilteredSpans")}
|
return nil, &model.ApiError{model.ErrorNotImplemented, fmt.Errorf("druid does not support getting FilteredSpans")}
|
||||||
}
|
}
|
||||||
|
@ -214,6 +214,7 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router) {
|
|||||||
router.HandleFunc("/api/v1/getFilteredSpans", aH.getFilteredSpans).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/getFilteredSpans", aH.getFilteredSpans).Methods(http.MethodPost)
|
||||||
router.HandleFunc("/api/v1/getFilteredSpans/aggregates", aH.getFilteredSpanAggregates).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/getFilteredSpans/aggregates", aH.getFilteredSpanAggregates).Methods(http.MethodPost)
|
||||||
|
|
||||||
|
router.HandleFunc("/api/v1/getTagValues", aH.getTagValues).Methods(http.MethodPost)
|
||||||
router.HandleFunc("/api/v1/errors", aH.getErrors).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/errors", aH.getErrors).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/api/v1/errorWithId", aH.getErrorForId).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/errorWithId", aH.getErrorForId).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/api/v1/errorWithType", aH.getErrorForType).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/errorWithType", aH.getErrorForType).Methods(http.MethodGet)
|
||||||
@ -682,8 +683,9 @@ func (aH *APIHandler) user(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
telemetry.GetInstance().IdentifyUser(user)
|
telemetry.GetInstance().IdentifyUser(user)
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"name": user.Name,
|
"name": user.Name,
|
||||||
"email": user.Email,
|
"email": user.Email,
|
||||||
|
"organizationName": user.OrganizationName,
|
||||||
}
|
}
|
||||||
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER, data)
|
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER, data)
|
||||||
|
|
||||||
@ -1040,6 +1042,22 @@ func (aH *APIHandler) getTagFilters(w http.ResponseWriter, r *http.Request) {
|
|||||||
aH.writeJSON(w, r, result)
|
aH.writeJSON(w, r, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (aH *APIHandler) getTagValues(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
query, err := parseTagValueRequest(r)
|
||||||
|
if aH.handleError(w, err, http.StatusBadRequest) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, apiErr := (*aH.reader).GetTagValues(context.Background(), query)
|
||||||
|
|
||||||
|
if apiErr != nil && aH.handleError(w, apiErr.Err, http.StatusInternalServerError) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
aH.writeJSON(w, r, result)
|
||||||
|
}
|
||||||
|
|
||||||
func (aH *APIHandler) setTTL(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) setTTL(w http.ResponseWriter, r *http.Request) {
|
||||||
ttlParams, err := parseDuration(r)
|
ttlParams, err := parseDuration(r)
|
||||||
if aH.handleError(w, err, http.StatusBadRequest) {
|
if aH.handleError(w, err, http.StatusBadRequest) {
|
||||||
@ -1091,6 +1109,12 @@ func (aH *APIHandler) setUserPreferences(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"hasOptedUpdates": userParams.HasOptedUpdates,
|
||||||
|
"isAnonymous": userParams.IsAnonymous,
|
||||||
|
}
|
||||||
|
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER_PREFERENCES, data)
|
||||||
|
|
||||||
aH.writeJSON(w, r, map[string]string{"data": "user preferences set successfully"})
|
aH.writeJSON(w, r, map[string]string{"data": "user preferences set successfully"})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ type Reader interface {
|
|||||||
GetTTL(ctx context.Context, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError)
|
GetTTL(ctx context.Context, ttlParams *model.GetTTLParams) (*model.GetTTLResponseItem, *model.ApiError)
|
||||||
GetSpanFilters(ctx context.Context, query *model.SpanFilterParams) (*model.SpanFiltersResponse, *model.ApiError)
|
GetSpanFilters(ctx context.Context, query *model.SpanFilterParams) (*model.SpanFiltersResponse, *model.ApiError)
|
||||||
GetTagFilters(ctx context.Context, query *model.TagFilterParams) (*[]model.TagFilters, *model.ApiError)
|
GetTagFilters(ctx context.Context, query *model.TagFilterParams) (*[]model.TagFilters, *model.ApiError)
|
||||||
|
GetTagValues(ctx context.Context, query *model.TagFilterParams) (*[]model.TagValues, *model.ApiError)
|
||||||
GetFilteredSpans(ctx context.Context, query *model.GetFilteredSpansParams) (*model.GetFilterSpansResponse, *model.ApiError)
|
GetFilteredSpans(ctx context.Context, query *model.GetFilteredSpansParams) (*model.GetFilterSpansResponse, *model.ApiError)
|
||||||
GetFilteredSpansAggregates(ctx context.Context, query *model.GetFilteredSpanAggregatesParams) (*model.GetFilteredSpansAggregatesResponse, *model.ApiError)
|
GetFilteredSpansAggregates(ctx context.Context, query *model.GetFilteredSpanAggregatesParams) (*model.GetFilteredSpansAggregatesResponse, *model.ApiError)
|
||||||
|
|
||||||
|
@ -650,6 +650,31 @@ func parseTagFilterRequest(r *http.Request) (*model.TagFilterParams, error) {
|
|||||||
return postData, nil
|
return postData, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTagValueRequest(r *http.Request) (*model.TagFilterParams, error) {
|
||||||
|
var postData *model.TagFilterParams
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&postData)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if postData.TagKey == "" {
|
||||||
|
return nil, fmt.Errorf("%s param missing in query", postData.TagKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
postData.Start, err = parseTimeStr(postData.StartStr, "start")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
postData.End, err = parseTimeMinusBufferStr(postData.EndStr, "end")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return postData, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func parseErrorsRequest(r *http.Request) (*model.GetErrorsParams, error) {
|
func parseErrorsRequest(r *http.Request) (*model.GetErrorsParams, error) {
|
||||||
|
|
||||||
startTime, err := parseTime("start", r)
|
startTime, err := parseTime("start", r)
|
||||||
|
@ -6,8 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
OrganizationName string `json:"organizationName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstantQueryMetricsParams struct {
|
type InstantQueryMetricsParams struct {
|
||||||
@ -201,6 +202,7 @@ type TagFilterParams struct {
|
|||||||
MaxDuration string `json:"maxDuration"`
|
MaxDuration string `json:"maxDuration"`
|
||||||
StartStr string `json:"start"`
|
StartStr string `json:"start"`
|
||||||
EndStr string `json:"end"`
|
EndStr string `json:"end"`
|
||||||
|
TagKey string `json:"tagKey"`
|
||||||
Start *time.Time
|
Start *time.Time
|
||||||
End *time.Time
|
End *time.Time
|
||||||
}
|
}
|
||||||
|
@ -179,6 +179,7 @@ type SearchSpanReponseItem struct {
|
|||||||
TagsKeys []string `db:"tagsKeys"`
|
TagsKeys []string `db:"tagsKeys"`
|
||||||
TagsValues []string `db:"tagsValues"`
|
TagsValues []string `db:"tagsValues"`
|
||||||
Events []string `db:"events"`
|
Events []string `db:"events"`
|
||||||
|
HasError int32 `db:"hasError"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OtelSpanRef struct {
|
type OtelSpanRef struct {
|
||||||
@ -214,7 +215,7 @@ func (item *SearchSpanReponseItem) GetValues() []interface{} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
returnArray := []interface{}{int64(timeObj.UnixNano() / 1000000), item.SpanID, item.TraceID, item.ServiceName, item.Name, strconv.Itoa(int(item.Kind)), strconv.FormatInt(item.DurationNano, 10), item.TagsKeys, item.TagsValues, referencesStringArray, errorEvent}
|
returnArray := []interface{}{int64(timeObj.UnixNano() / 1000000), item.SpanID, item.TraceID, item.ServiceName, item.Name, strconv.Itoa(int(item.Kind)), strconv.FormatInt(item.DurationNano, 10), item.TagsKeys, item.TagsValues, referencesStringArray, errorEvent, item.HasError}
|
||||||
|
|
||||||
return returnArray
|
return returnArray
|
||||||
}
|
}
|
||||||
@ -267,6 +268,10 @@ type TagItem struct {
|
|||||||
type TagFilters struct {
|
type TagFilters struct {
|
||||||
TagKeys string `json:"tagKeys" db:"tagKeys"`
|
TagKeys string `json:"tagKeys" db:"tagKeys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TagValues struct {
|
||||||
|
TagValues string `json:"tagValues" db:"tagValues"`
|
||||||
|
}
|
||||||
type ServiceMapDependencyResponseItem struct {
|
type ServiceMapDependencyResponseItem struct {
|
||||||
Parent string `json:"parent,omitempty" db:"parent,omitempty"`
|
Parent string `json:"parent,omitempty" db:"parent,omitempty"`
|
||||||
Child string `json:"child,omitempty" db:"child,omitempty"`
|
Child string `json:"child,omitempty" db:"child,omitempty"`
|
||||||
|
@ -18,6 +18,7 @@ const (
|
|||||||
TELEMETRY_EVENT_INPRODUCT_FEEDBACK = "InProduct Feeback Submitted"
|
TELEMETRY_EVENT_INPRODUCT_FEEDBACK = "InProduct Feeback Submitted"
|
||||||
TELEMETRY_EVENT_NUMBER_OF_SERVICES = "Number of Services"
|
TELEMETRY_EVENT_NUMBER_OF_SERVICES = "Number of Services"
|
||||||
TELEMETRY_EVENT_HEART_BEAT = "Heart Beat"
|
TELEMETRY_EVENT_HEART_BEAT = "Heart Beat"
|
||||||
|
TELEMETRY_EVENT_USER_PREFERENCES = "User Preferences"
|
||||||
)
|
)
|
||||||
|
|
||||||
const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz"
|
const api_key = "4Gmoa4ixJAUHx2BpJxsjwA1bEfnwEeRz"
|
||||||
@ -61,6 +62,10 @@ func getOutboundIP() string {
|
|||||||
ip := []byte("NA")
|
ip := []byte("NA")
|
||||||
resp, err := http.Get("https://api.ipify.org?format=text")
|
resp, err := http.Get("https://api.ipify.org?format=text")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return string(ip)
|
||||||
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ipBody, err := ioutil.ReadAll(resp.Body)
|
ipBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
@ -50,7 +50,7 @@ spec:
|
|||||||
- all
|
- all
|
||||||
env:
|
env:
|
||||||
- name: JAEGER_ENDPOINT
|
- name: JAEGER_ENDPOINT
|
||||||
value: http://otel-collector.platform.svc.cluster.local:14268/api/traces
|
value: http://my-release-signoz-otel-collector.platform.svc.cluster.local:14268/api/traces
|
||||||
image: jaegertracing/example-hotrod:1.30
|
image: jaegertracing/example-hotrod:1.30
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
name: hotrod
|
name: hotrod
|
||||||
|
Loading…
x
Reference in New Issue
Block a user