diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c37e2f3284..afd70250c4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,5 +2,5 @@ # Owners are automatically requested for review for PRs that changes code # that they own. * @ankitnayan -/frontend/ @palash-signoz -/deploy/ @prashant-shahi \ No newline at end of file +/frontend/ @palash-signoz @pranshuchittora +/deploy/ @prashant-shahi diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5e659eb8e5..1c959c1888 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -26,6 +26,7 @@ assignees: '' * **Signoz version**: * **Browser version**: * **Your OS and version**: +* **Your CPU Architecture**(ARM/Intel): ## Additional context diff --git a/.gitpod.yml b/.gitpod.yml index db4801ba35..1771de8779 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -11,7 +11,7 @@ tasks: - name: Run Docker Images init: | 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: - name: Run Frontend @@ -22,7 +22,7 @@ tasks: yarn dev ports: - - port: 3000 + - port: 3301 onOpen: open-browser - port: 8080 onOpen: ignore diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 412afd5eab..7b63015435 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 - 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 query-service section at `docker/clickhouse-setup/docker-compose.yaml#L38` +- comment out frontend service section at `docker/clickhouse-setup/docker-compose.yaml#L45` +- comment out query-service section at `docker/clickhouse-setup/docker-compose.yaml#L28` +- 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 - 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` diff --git a/deploy/docker/clickhouse-setup/docker-compose.arm.yaml b/deploy/docker/clickhouse-setup/docker-compose.arm.yaml index dba80353da..f666105b32 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.arm.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.arm.yaml @@ -23,7 +23,7 @@ services: - '--storage.path=/data' query-service: - image: signoz/query-service:0.6.1 + image: signoz/query-service:0.6.2 container_name: query-service command: ["-config=/root/config/prometheus.yml"] volumes: @@ -40,7 +40,7 @@ services: condition: service_healthy frontend: - image: signoz/frontend:0.6.1 + image: signoz/frontend:0.6.2 container_name: frontend depends_on: - query-service @@ -50,7 +50,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf 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"] volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml @@ -63,7 +63,7 @@ services: condition: service_healthy 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"] volumes: - ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 13eaa5790e..3c5dbbc64d 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -26,7 +26,7 @@ services: query-service: - image: signoz/query-service:0.6.1 + image: signoz/query-service:0.6.2 container_name: query-service command: ["-config=/root/config/prometheus.yml"] volumes: @@ -43,7 +43,7 @@ services: condition: service_healthy frontend: - image: signoz/frontend:0.6.1 + image: signoz/frontend:0.6.2 container_name: frontend depends_on: - query-service @@ -53,7 +53,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf 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"] volumes: - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml @@ -66,7 +66,7 @@ services: condition: service_healthy 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"] volumes: - ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml diff --git a/frontend/cypress/CustomFunctions/uncaughtExpection.ts b/frontend/cypress/CustomFunctions/uncaughtExpection.ts new file mode 100644 index 0000000000..341ddd8cee --- /dev/null +++ b/frontend/cypress/CustomFunctions/uncaughtExpection.ts @@ -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; diff --git a/frontend/cypress/fixtures/trace/initialAggregates.json b/frontend/cypress/fixtures/trace/initialAggregates.json new file mode 100644 index 0000000000..b029e9de64 --- /dev/null +++ b/frontend/cypress/fixtures/trace/initialAggregates.json @@ -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 } + } +} diff --git a/frontend/cypress/fixtures/trace/initialSpanFilter.json b/frontend/cypress/fixtures/trace/initialSpanFilter.json new file mode 100644 index 0000000000..26bed66ae9 --- /dev/null +++ b/frontend/cypress/fixtures/trace/initialSpanFilter.json @@ -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": {} +} diff --git a/frontend/cypress/fixtures/trace/initialSpans.json b/frontend/cypress/fixtures/trace/initialSpans.json new file mode 100644 index 0000000000..9b15100752 --- /dev/null +++ b/frontend/cypress/fixtures/trace/initialSpans.json @@ -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 +} diff --git a/frontend/cypress/integration/trace/index.spec.ts b/frontend/cypress/integration/trace/index.spec.ts new file mode 100644 index 0000000000..3ca79ecdce --- /dev/null +++ b/frontend/cypress/integration/trace/index.spec.ts @@ -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); + }); + }); +}); diff --git a/frontend/src/components/RouteTab/index.tsx b/frontend/src/components/RouteTab/index.tsx new file mode 100644 index 0000000000..12ce2d2a70 --- /dev/null +++ b/frontend/src/components/RouteTab/index.tsx @@ -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 ( + + {routes.map( + ({ Component, name }): JSX.Element => ( + + + + ), + )} + + ); +}; + +interface RouteTabProps { + routes: { + name: string; + route: string; + Component: () => JSX.Element; + }[]; + activeKey: TabsProps['activeKey']; + onChangeHandler?: VoidFunction; +} + +export default RouteTab; diff --git a/frontend/src/components/TextToolTip/index.tsx b/frontend/src/components/TextToolTip/index.tsx index 83f162fac9..470e61a642 100644 --- a/frontend/src/components/TextToolTip/index.tsx +++ b/frontend/src/components/TextToolTip/index.tsx @@ -8,7 +8,7 @@ const TextToolTip = ({ text, url }: TextToolTipProps) => ( return (
{`${text} `} - + here
diff --git a/frontend/src/container/AppLayout/FeedBack/index.tsx b/frontend/src/container/AppLayout/FeedBack/index.tsx deleted file mode 100644 index 13e0279806..0000000000 --- a/frontend/src/container/AppLayout/FeedBack/index.tsx +++ /dev/null @@ -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(false); - const [form] = Form.useForm(); - const [isLoading, setIsLoading] = useState(false); - - const [notifications, Element] = notification.useNotification(); - - const isToggleHandler = useCallback(() => { - setisOpen((state) => !state); - }, []); - - const onFinishHandler: Callbacks['onFinish'] = async ( - value: Feedback, - ): Promise => { - 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 ( - - {!isOpen && ( - - - - )} - - {Element} - - {isOpen && ( -
- - - - How can we improve SigNoz? - - - - - - - -