Compare commits

...

161 Commits

Author SHA1 Message Date
Prashant Shahi
ed2bbc5035 chore(release): 📌 pin versions: SigNoz-Otel-Collector 0.55.1
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-09-14 00:03:07 +05:30
Prashant Shahi
f1fdf78dc5 chore(release): 📌 pin versions: SigNoz 0.11.1
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-09-13 22:46:00 +05:30
Prashant Shahi
745fd07bd8 fix(lint): 🚨 format prometheus config YAML and remove trailing spaces
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-09-13 22:45:30 +05:30
Prashant Shahi
05de0ccba5 chore: 🔧 Add 414 issue fix for Frontend default.conf and Swarm
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-09-13 22:42:14 +05:30
palashgdev
c9139c5236 feat: height is updated (#1563) 2022-09-13 21:19:01 +05:30
palashgdev
0806397816 feat: webpack chunk name is updated (#1562) 2022-09-13 12:00:09 +05:30
Pranshu Chittora
c43dabdb0b fix: dashboard variable getting deleted on edit instances (#1561) 2022-09-12 23:24:45 +05:30
Vishal Sharma
eaadc3bb95 feat: introduce search trace ID component (#1551)
* feat: searchTraceID checkpoint
* feat: filter spans using TraceID from trace filter page

Co-authored-by: palashgdev <palashgdev@gmail.com>
Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2022-09-12 19:35:31 +05:30
Pranshu Chittora
1ec9248975 feat: getting started page (#1560)
Co-authored-by: palashgdev <palashgdev@gmail.com>
2022-09-12 16:26:01 +05:30
Srikanth Chekuri
0ccd7777bf fix: make widget plot work with missing data points (#1559) 2022-09-12 16:11:55 +05:30
Srikanth Chekuri
ac86d840f9 fix: reuse the query engine and storage for alerts pqlEngine (#1558) 2022-09-12 12:30:36 +05:30
Srikanth Chekuri
8556c87d46 feat: add support for dashboard variables (#1557) 2022-09-11 03:34:02 +05:30
Pranshu Chittora
461a15d52d feat: dashboard variables (#1552)
* feat: dashboard variables
* fix: variable wipe on few instances
* feat: error handling states
* feat: eslint and tsc fixes
2022-09-09 17:43:25 +05:30
Ankit Nayan
9e6d9019f7 chore: added group analytics 2022-09-08 21:05:54 +05:30
Ankit Nayan
578dafd1ff chore: fixed random number generation to match to maxRandInt 2022-09-07 15:20:56 +05:30
Ankit Nayan
99c0c97c1e chore: added sampling in analytics 2022-09-06 19:55:01 +05:30
Ankit Nayan
4875652ecb chore: added group analytics 2022-09-06 19:29:07 +05:30
Ankit Nayan
d170515d4d Merge pull request #1532 from SigNoz/release/v0.11.0
Release/v0.11.0
2022-08-24 20:11:36 +05:30
Prashant Shahi
73b00f405b Merge branch 'develop' into release/v0.11.0 2022-08-24 19:01:33 +05:30
Nityananda Gohain
ea8bd7047f Fix case mismatch in static fields. (#1537)
* case mismatch fix
* fix: undefined handled in flattened data
2022-08-24 18:59:44 +05:30
Prashant Shahi
596daefa7e compose changes: add default env and remove otel memory limit (#1536)
* chore(docker-compose): 🔥 remove memory limit

* chore: 🔧 include envs: ALERTMANAGER_API_PREFIX and SIGNOZ_LOCAL_DB_PATH
2022-08-24 18:58:44 +05:30
Pranshu Chittora
7081e4ffce fix: recursive url reloading (#1535) 2022-08-24 17:46:31 +05:30
Prashant Shahi
07dcdb51f7 chore: 🔧 enable logs capturing by default (#1534)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-08-24 15:11:38 +05:30
Prashant Shahi
d2e990ebf4 chore: 📌 pin versions: SigNoz 0.11.0, OtelCollector distribution 0.55.0
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-08-24 00:00:19 +05:30
Prashant Shahi
c2f95dc727 Merge branch 'develop' into release/v0.11.0 2022-08-23 22:22:49 +05:30
Aditya Kumar Praharaj
37dedc8b87 feat(devbox): splitting docker-compose.yaml into core and prod / local for no-edit local setup (#1528) 2022-08-23 15:23:37 +05:30
Srikanth Chekuri
9cd1be6553 Allow search by service name in services list page (#1520)
* feat: add search by service name
* chore: allow clear
* chore: table search icon and review comments
* chore: fix lint
* chore: address review comments
* chore: fix types
* chore: tweak user experience
* chore: antd color enum
2022-08-23 13:05:19 +05:30
Pranshu Chittora
5e0eb05a9c feat: support for legend in query builderformulas (#1530) 2022-08-23 11:17:49 +05:30
Pranshu Chittora
f48a884f90 fix: eslint and tsc fixes for logs (#1527)
* fix: eslint and tsc fixes for logs

* chore: remove package-lock file
2022-08-19 17:16:04 +05:30
Srikanth Chekuri
32fba00aa8 fix: do not show services without any data for select interval (#1521) 2022-08-17 15:11:08 +05:30
Pranshu Chittora
166e5612eb feat: restrict timestamp from adding it to query (#1517)
Co-authored-by: Palash Gupta <palashgdev@gmail.com>
2022-08-16 18:53:34 +05:30
Ankit Nayan
b23d63cb2b Merge pull request #1515 from pranshuchittora/pranshuchittora/fix/sse-event-polyfill
fix: live tail sse prod issue
2022-08-16 13:33:45 +05:30
Pranshu Chittora
5e0ed6f5f5 chore: removed unused SSE libs 2022-08-16 13:07:05 +05:30
Pranshu Chittora
74f947a028 fix: live tail sse prod issue 2022-08-16 12:59:07 +05:30
Ankit Nayan
cca74e5926 Merge branch 'main' into develop 2022-08-11 18:53:38 +05:30
Ankit Nayan
1865d75df6 Merge pull request #1512 from SigNoz/release/v0.10.2
Release/v0.10.2
2022-08-11 18:52:34 +05:30
Prashant Shahi
d4b0013900 chore(prerelease): 📌 pin versions: SigNoz 0.11.0-rc.1
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-08-11 15:08:12 +05:30
Prashant Shahi
55c9eb733d chore(release): 📌 pin versions: SigNoz 0.10.2
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-08-11 14:58:36 +05:30
Amol Umbark
54cc363752 Alerts/edit rule issue 676 (#1505)
* fix: resolved issue with editing of rules

(cherry picked from commit a3015d1077)
2022-08-11 14:43:50 +05:30
Ankit Nayan
07d013a716 chore: added analytics for logs 2022-08-11 14:27:19 +05:30
Amol Umbark
a3015d1077 Alerts/edit rule issue 676 (#1505)
* fix: resolved issue with editing of rules
2022-08-11 13:54:17 +05:30
Nityananda Gohain
66b67a08a0 alias for timstamp interval changed in sql query (#1509) 2022-08-11 13:53:33 +05:30
Pranshu Chittora
7a4750a882 Logs UI (#1436)
* feat: logs routing
* feat: add redux for logs
* feat: logs filter ui
* feat: logsql parser integration
* feat: logs table initial version
* feat: logs aggregated view
* feat: add log detail
* feat: log live tail
* feat: Logs TTL UI
2022-08-11 11:45:28 +05:30
Nityananda Gohain
6d623c5d45 single otlp receiver (#1506) 2022-08-11 11:43:10 +05:30
Srikanth Chekuri
6e899175a0 fix: escape and encode operations regex for overview details (#1502)
* fix: escape and encode operations regex for overview details
* chore: use back tick with escaping
2022-08-11 11:41:32 +05:30
Amol Umbark
aecf3ef93e fix: added cache bursting for translations using file hash (#1478)
* fix: added cache bursting for translations using file hash


Co-authored-by: Palash Gupta <palashgdev@gmail.com>
2022-08-10 23:09:34 +05:30
Nityananda Gohain
b6afc9315b clickhouse logs exporter added to deployment file (#1500)
* clickhouse logs exporter added to deployment file
* updated to latest otel collector
2022-08-10 23:00:05 +05:30
Srikanth Chekuri
dda82474ae feat: add more options in service map time dropdown (#1501) 2022-08-10 21:09:02 +05:30
Srikanth Chekuri
998e72374f fix: escape and encode operations regex for overview details (#1499)
* fix: interval should be 1d=24h (#1482) (#1483)

* fix: escape and encode operations regex for overview details

Co-authored-by: Ankit Nayan <ankit@signoz.io>
Co-authored-by: zedongh <248348907@qq.com>
2022-08-10 21:04:12 +05:30
Ankit Nayan
a1f6f09ae1 Merge pull request #1379 from nityanandagohain/feat/logs
Support for Logs
2022-08-10 15:29:03 +05:30
nityanandagohain
7a1cbdb0bb Merge remote-tracking branch 'origin/feat/logs' into feat/logs 2022-08-10 14:28:11 +05:30
nityanandagohain
eb28ece680 parser updated for pagination 2022-08-10 14:27:46 +05:30
Vishal Sharma
fda6e4472a Merge branch 'develop' into feat/logs 2022-08-09 10:45:23 +05:30
zedongh
9de99d1872 fix: interval should be 1d=24h (#1482) (#1483) 2022-08-09 09:52:55 +05:30
Ankit Nayan
8f9d0f2403 Merge pull request #1480 from SigNoz/release/v0.10.1
Release/v0.10.1
2022-08-07 15:35:29 +05:30
Prashant Shahi
04cf1b2697 Merge branch 'develop' into release/v0.10.1 2022-08-07 15:27:33 +05:30
Amol Umbark
8bdc41bef0 fix: resolves issue for migrated promql (#1481) 2022-08-06 13:40:41 +05:30
Prashant Shahi
616da88790 chore(release): 📌 pin versions: SigNoz 0.10.1, OtelCollector 0.45.1-1.3, Alertmanager 0.23.0-0.2
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-08-05 22:30:55 +05:30
Srikanth Chekuri
143a5b65f9 Merge branch 'develop' into feat/logs 2022-08-04 20:08:58 +05:30
nityanandagohain
0807a0ae26 Merge remote-tracking branch 'upstream/develop' into feat/logs 2022-08-04 17:32:45 +05:30
Amol Umbark
1ebf64589f Alerts: Test Notifications in Rules 2022-08-04 17:24:15 +05:30
Amol Umbark
80c96af5a4 feat: added user selected filtering of channels in alerts (#1459) 2022-08-04 15:31:21 +05:30
nityanandagohain
61ebd3aded logs ttl support added in ttl api 2022-08-04 14:28:10 +05:30
Vishal Sharma
425b732370 fix: add defaultDependencyGraphTable to getTTL status check (#1474) 2022-08-04 13:41:25 +05:30
Vishal Sharma
a742c9aee1 feat: use materialized view for usage explorer API (#1466)
* feat: use materialized view for usage explorer API

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2022-08-04 12:55:21 +05:30
Srikanth Chekuri
3968f11b3d feat: improve service map (#1467)
* feat: improve service map
2022-08-04 12:38:53 +05:30
Srikanth Chekuri
5bfc2af51b feat: show messaging/cron/browser services in listing page (#1455)
* feat: show messaging/cron/browser services in listing page

* chore: issue maximum of ten queries to clickhouse

Co-authored-by: Palash Gupta <palashgdev@gmail.com>
2022-08-04 11:57:05 +05:30
Amol Umbark
8146da52af feat: Disable Alerts Feature (Backend) (#1443)
* feat: added patch rule api

* feat: added backend api for patching rule status

* fix: improved patchRule and also editRule


Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2022-08-04 11:55:54 +05:30
Amol Umbark
5dc6d28f2e feat: disable alerts feature (UI) (#1445)
* feat: added enable disable feature for rules

* fix: resolved type issue in getTriggered

Co-authored-by: Palash Gupta <palashgdev@gmail.com>
2022-08-04 11:55:09 +05:30
Amol Umbark
a6ed6c03c1 Alerts/607 test notifications UI (#1469)
* feat: added test alert feature

* fix: solved the lint issues

Co-authored-by: Palash Gupta <palashgdev@gmail.com>
2022-08-03 19:40:20 +05:30
Vishal Sharma
cca4db602c Remove query-service code owners (#1461)
* Remove query-service code owners
2022-08-03 16:55:51 +05:30
Amol Umbark
7ff49ba47c feat: added rule url to the title link in slack message (#1421)
* feat: added rule url to the title link in slack message

* fix: corrected duplication of code for generator url in rules engine

* fix: removed unnecessary import in rules engine
2022-08-03 15:08:14 +05:30
nityanandagohain
9dcf913a74 severity_number type changed to int8 2022-08-03 12:23:00 +05:30
Prashant Shahi
68194d7e07 fix(query-service): 🚀 embed copy of timezone data (#1462)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-08-02 10:05:16 +05:30
Amol Umbark
7881aee350 feat: added user preferred channel filters in alerts (#1458)
* feat: added user preferred channel filters in alerts

* fix: resolved minor translation issue
2022-08-02 09:54:24 +05:30
nityanandagohain
594bfc256c fulltext validation updated 2022-08-01 13:02:00 +05:30
nityanandagohain
5894acdb2d OR support added with contains 2022-08-01 12:30:11 +05:30
nityanandagohain
6eb9389e81 parser updated to include or as well 2022-08-01 12:17:15 +05:30
Srikanth Chekuri
39be8201aa Merge pull request #1450 from SigNoz/bump-json-iterator
chore: bump json-iterator version to v1.1.12
2022-08-01 12:10:07 +05:30
Srikanth Chekuri
6778163a07 Merge branch 'develop' into bump-json-iterator 2022-08-01 09:13:04 +05:30
Amol Umbark
56a2047560 fix: remove 'default channel' note from channels page (#1446) 2022-07-31 09:54:58 +05:30
Srikanth Chekuri
023ef66035 Merge branch 'develop' into bump-json-iterator 2022-07-29 08:37:46 +05:30
Prashant Shahi
22b8572495 feat(swarm): 🚀 scraping multiple otel-collector (#1438)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-07-28 14:05:35 +05:30
jshiwam
e39d2f799d Used Prepared Statements for GetChannel in clickhousereader (#1414)
* feat: used db.Preparex
2022-07-28 10:14:27 +05:30
nityanandagohain
5b28fe1c9d Merge remote-tracking branch 'origin/feat/logs' into feat/logs 2022-07-27 15:59:24 +05:30
nityanandagohain
d15f9a1709 log statement corrected 2022-07-27 15:58:58 +05:30
Srikanth Chekuri
2c383528bc Merge branch 'develop' into bump-json-iterator 2022-07-27 14:41:50 +05:30
Srikanth Chekuri
0378dfd12f chore: bump json-iterator version to v1.1.12 2022-07-27 14:40:45 +05:30
Srikanth Chekuri
002ccc3975 Merge branch 'develop' into feat/logs 2022-07-27 12:53:32 +05:30
Prashant Shahi
f8f903848e fix: 🚀 disables TTL moves on insert and only run in background (#1448)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-07-27 11:53:10 +05:30
nityanandagohain
047227ad18 use ticker for polling db in live tail. 2022-07-27 11:47:35 +05:30
nityanandagohain
7b6a086b37 consistant query formatting 2022-07-27 10:46:33 +05:30
Nityananda Gohain
baf72610d6 Update pkg/query-service/app/clickhouseReader/reader.go
Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2022-07-27 10:39:08 +05:30
nityanandagohain
a5388d357c Merge remote-tracking branch 'origin/feat/logs' into feat/logs 2022-07-26 14:50:55 +05:30
nityanandagohain
294d527a0e parser updated to support more than one contains 2022-07-26 14:45:20 +05:30
Srikanth Chekuri
a23788852a Merge branch 'develop' into feat/logs 2022-07-26 11:55:18 +05:30
jshiwam
a771c3b9a6 docs: added documentation for query-service local setup (#1426)
* docs: added documentation for query-service local setup

* fix: updated clickhouse setup link

* Use Env Var for the alertmanager endpoint
2022-07-26 09:59:31 +05:30
nityanandagohain
0fe4327877 live tail fetch only recent 100 logs every 10s 2022-07-25 14:42:58 +05:30
nityanandagohain
4825ed6e5f dataType constant strings 2022-07-22 17:19:55 +05:30
nityanandagohain
2f17898390 primitive type pointers removed 2022-07-22 16:49:40 +05:30
nityanandagohain
373cbbc375 logs select statement converted to a const 2022-07-22 16:07:19 +05:30
nityanandagohain
f8be4a6d5b livetail timestamp correction 2022-07-22 15:49:50 +05:30
nityanandagohain
420d46ab01 tail function updated to use values instead of pointers 2022-07-22 15:44:07 +05:30
nityanandagohain
bdb6901c74 generateSql returns value insted of pointer 2022-07-22 15:39:43 +05:30
nityanandagohain
94cde11164 consistant response value instead of pointer 2022-07-22 15:27:52 +05:30
nityanandagohain
448e14b32f parser updated to support contians operator for other fields 2022-07-22 15:17:46 +05:30
nityanandagohain
6ac7cb1022 parser updated 2022-07-21 18:32:11 +05:30
nityanandagohain
2132d1059c live tail api excluded from timeout middleware 2022-07-21 17:55:08 +05:30
Palash Gupta
ff9c41464b test: error and error details case is added (#1420) 2022-07-21 11:25:54 +05:30
Akshay Awate
acb3721815 refactor: start_docker() (#1410)
* refactor: start_docker()

Co-authored-by: akshay <akshay.awate@infracloud.io>
2022-07-20 23:30:21 +05:30
nityanandagohain
5912d3a4a0 observed timestamp removed 2022-07-20 14:52:16 +05:30
nityanandagohain
a527c33c7d timestamp in ns from ms 2022-07-20 13:05:24 +05:30
nityanandagohain
c24bdfc8cf aggregate function added 2022-07-20 12:11:03 +05:30
nityanandagohain
051f640100 correct var names in live tail 2022-07-19 16:38:28 +05:30
nityanandagohain
b5c8764605 changes added for live tail api 2022-07-19 16:34:33 +05:30
Amol Umbark
475c44a000 fix: increased debounce to 1000 (from 500). also reduced networkcalls in getquery range (#1423) 2022-07-19 11:30:56 +05:30
Amol Umbark
1b6597b974 fix: resolved issue with promql rule creation (#1422) 2022-07-19 11:29:32 +05:30
nityanandagohain
8e4fbbe770 parsing logic and test updated 2022-07-19 10:40:19 +05:30
nityanandagohain
2450fff34d live tail v1 2022-07-18 18:55:52 +05:30
nityanandagohain
df17d4ca54 Merge remote-tracking branch 'upstream/develop' into feat/logs 2022-07-18 16:49:04 +05:30
nityanandagohain
33e7252645 Merge remote-tracking branch 'origin/develop' into feat/logs 2022-07-18 16:44:27 +05:30
nityanandagohain
2e9affa80c Filtering logic updated 2022-07-18 16:37:46 +05:30
Ankit Nayan
bf2f3f8f5e Merge pull request #1402 from zriyansh/develop
update contributing.md file
2022-07-16 14:29:21 +05:30
Amol Umbark
d92aad38df fix: removed background color from sections in alert ui (#1413) 2022-07-16 12:08:17 +05:30
Pranay Prateek
78d13c94ae Merge branch 'develop' into develop 2022-07-15 23:43:04 +05:30
Ankit Nayan
73e699080d Merge pull request #1406 from SigNoz/release/v0.10.0
Release/v0.10.0
2022-07-15 22:19:13 +05:30
Prashant Shahi
b6aa378fae chore(release): 📌 pin versions: SigNoz 0.10.0, OtelCollector 0.45.1-1.1
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-07-15 21:46:32 +05:30
Amol Umbark
b5fe3d7fa1 fix: changed translation file for rules (#1405) 2022-07-15 21:33:30 +05:30
Amol Umbark
7c14a75c68 feat: Right side panel in create/edit alert UI (#1404)
* feat: metrics builder

* feat: tag key selection

* feat: metrics builder

* poc version

* added more changes to query builder

* added types for composite queries

* (feat): added edit rules and create rules forms

* added label key value input item

* (chore): added hidden labels for labelinput

* (chore): resolved some merge conflicts from develop

* (chore): added translations

* (chore): removed some old files in metric builder

* (chore): restored some of the files from develop branch

* (chore): restored env.ts

* (fix): solved empty builder queries errors

* (fix): changed queryIndex and formulaIndex type to string|number from number

* (feat): added chart preview for alert metric ui

* (feat): added threshold in chart, translations in alert form and a few fixes

* (fix): restoring env.ts

* (fix): placed threshold on horizontal line

* fix: resolved review comments

* fix: resolved label remove issue

* fix: removed console log

* fix: resolved issue with edit rule - old state values shown after update of threshold

* fix: resolved issue with match condition dropdown in alert ui

* fix: increased size of timeframe drop down

* fix: fixed label key value field and chart auto update when eval window changes

* feat: added a link for alert name in list alerts page and source for each rule update

* fix: resolved review coments in querysection of alerts ui

* feat: adding panel user guide in alerting form

* feat: added user guide panel in the alert form

* feat: added more help icon in user guide and fixed the sizing issue

Co-authored-by: Pranshu Chittora <pranshu@signoz.io>
2022-07-15 17:48:16 +05:30
Prashant Shahi
10c6325e46 chore(clickhouse): 🔊 update logging level to info (#1401)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-07-15 17:10:27 +05:30
Prashant Shahi
e4883495c3 fix(exceptions-page): 🚑 unix nanoseconds operations (#1403)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-07-15 16:40:45 +05:30
Palash
b9d63d6b8f feat: text is now ellipsed (#1392)
* feat: text is now ellipsed
2022-07-15 14:17:29 +05:30
Priyansh Khodiyar
65804f245c Merge branch 'SigNoz:develop' into develop 2022-07-15 13:38:54 +05:30
Ankit Anand
964b819f20 Update CONTRIBUTING.md (#1) 2022-07-15 13:38:39 +05:30
Palash
e22be60a9e Create dependency-review.yml (#1360)
* Create dependency-review.yml
2022-07-15 13:01:29 +05:30
Palash
b6a6833a64 test: utils unit case is updated (#1396) 2022-07-15 12:46:57 +05:30
Vishal Sharma
c90e9ffa34 fix: remove requirement of exceptionType and serviceName from errorDetail page URL (#1400)
* fix: remove requirement of exceptionType and serviceName from errorDetail page URL

* chore: id is updated

* chore: commented code is removed

* chore: eslint error is fixed

Co-authored-by: Palash <palashgdev@gmail.com>
2022-07-15 12:35:15 +05:30
Srikanth Chekuri
c5c7fb238f fix: update the error rate percentage text and scale (#1399) 2022-07-15 09:55:43 +05:30
Priyansh Khodiyar
4ad79bee18 add images 2022-07-14 22:51:51 +05:30
Priyansh Khodiyar
bebfaa1c4c Update CONTRIBUTING.md 2022-07-14 22:41:11 +05:30
Prashant Shahi
6fb7e34dbc chore: 🔧 otel-collector config changes (#1388)
* chore: 🔧 otel-collector config changes

* chore: 🗑️  remove redundant users.xml

* chore: 🔧 otel-config changes

- seperate scraper job for otel-collector and otel-collector-metrcs internal metrics
- use resourcedetection only for hostmetrics
- add swarm service name and task name in resource attributes env

Signed-off-by: Prashant Shahi <prashant@signoz.io>
2022-07-14 19:36:19 +05:30
Amol Umbark
a2e1c41343 fix: edit form shows incorrect eval window when 24hours is saved (#1393)
* fix: edit form shows incorrect eval window when 24hours is saved

* fix: edit form shows incorrect eval window when 24hours is saved

* feat: added 4 hour window to alert ui

Co-authored-by: Palash <palashgdev@gmail.com>
2022-07-14 18:23:02 +05:30
Palash
46f258747a Merge pull request #1395 from mindhash/amol/alert-0-10-0-fix3
feat: remove global time selection on alerts pages
2022-07-14 18:15:33 +05:30
Amol Umbark
11c352741d Merge branch 'develop' into amol/alert-0-10-0-fix3 2022-07-14 17:34:50 +05:30
Amol
a63267cf90 feat: remove global time selection on alerts pages 2022-07-14 17:28:30 +05:30
Palash
3200248e98 fix: error page is updated (#1394) 2022-07-14 17:14:13 +05:30
Priyansh Khodiyar
7da69f6a75 Update CONTRIBUTING.md 2022-07-14 00:21:59 +05:30
Priyansh Khodiyar
3ea36092f6 Update CONTRIBUTING.md 2022-07-13 23:44:25 +05:30
Priyansh Khodiyar
8db4793ad6 change main branch to develop branch for contribution 2022-07-13 23:33:41 +05:30
Priyansh Khodiyar
83f3180641 Update CONTRIBUTING.md 2022-07-13 23:28:13 +05:30
Priyansh Khodiyar
3e2a6df200 Update CONTRIBUTING.md 2022-07-13 18:08:02 +05:30
Priyansh Khodiyar
3dc1dc970f v6 2022-07-13 18:00:46 +05:30
Priyansh Khodiyar
0ceaa56679 v6 2022-07-13 17:46:48 +05:30
Priyansh Khodiyar
ab52538e91 v5 2022-07-13 17:43:23 +05:30
Priyansh Khodiyar
ef69505bf9 remove arm version of docker-compose file 2022-07-13 17:22:31 +05:30
Priyansh Khodiyar
61b79742dc v4 2022-07-13 17:17:37 +05:30
nityanandagohain
ed5d217c76 API for filtering and paginating logs added 2022-07-13 15:42:13 +05:30
Priyansh Khodiyar
54e09e1292 v3 2022-07-13 13:48:18 +05:30
Priyansh Khodiyar
8477aebc8e V2 2022-07-13 12:27:28 +05:30
Priyansh Khodiyar
d7f7f20520 1st iteration 2022-07-13 02:23:06 +05:30
nityanandagohain
ef141d2cee API for fields added 2022-07-12 16:38:26 +05:30
327 changed files with 13333 additions and 4788 deletions

2
.github/CODEOWNERS vendored
View File

@@ -4,4 +4,4 @@
* @ankitnayan
/frontend/ @palashgdev @pranshuchittora
/deploy/ @prashant-shahi
/pkg/query-service/ @srikanthccv @makeavish @nityanandagohain
/pkg/query-service/ @srikanthccv

22
.github/workflows/dependency-review.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
with:
fail-on-severity: high
uses: actions/dependency-review-action@v2

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@ package.json
deploy/docker/environment_tiny/common_test
frontend/node_modules
frontend/.pnp
frontend/i18n-translations-hash.json
*.pnp.js
# testing

View File

@@ -1,122 +1,331 @@
# How to Contribute
# Contributing Guidelines
There are primarily 2 areas in which you can contribute in SigNoz
## Welcome to SigNoz Contributing section 🎉
- Frontend ( written in Typescript, React)
- Backend - ( Query Service - written in Go)
Hi there! We're thrilled that you'd like to contribute to this project, thank you for your interest. Whether it's a bug report, new feature, correction, or additional documentation, we greatly value feedback and contributions from our community.
Depending upon your area of expertise & interest, you can chose one or more to contribute. Below are detailed instructions to contribute in each area
Please read through this document before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution.
> Please note: If you want to work on an issue, please ask the maintainers to assign the issue to you before starting work on it. This would help us understand who is working on an issue and prevent duplicate work. 🙏🏻
- We accept contributions made to the [SigNoz `develop` branch]()
- Find all SigNoz Docker Hub images here
- [signoz/frontend](https://hub.docker.com/r/signoz/frontend)
- [signoz/query-service](https://hub.docker.com/r/signoz/query-service)
- [signoz/otelcontribcol](https://hub.docker.com/r/signoz/otelcontribcol)
> If you just raise a PR, without the corresponding issue being assigned to you - it may not be accepted.
## Finding contributions to work on 💬
# Develop Frontend
Looking at the existing issues is a great way to find something to contribute on.
Also, have a look at these [good first issues label](https://github.com/SigNoz/signoz/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to start with.
Need to update [https://github.com/SigNoz/signoz/tree/main/frontend](https://github.com/SigNoz/signoz/tree/main/frontend)
### Contribute to Frontend with Docker installation of SigNoz
## Sections:
- [General Instructions](#1-general-instructions-)
- [For Creating Issue(s)](#11-for-creating-issues)
- [For Pull Requests(s)](#12-for-pull-requests)
- [How to Contribute](#2-how-to-contribute-%EF%B8%8F)
- [Develop Frontend](#3-develop-frontend-)
- [Contribute to Frontend with Docker installation of SigNoz](#31-contribute-to-frontend-with-docker-installation-of-signoz)
- [Contribute to Frontend without installing SigNoz backend](#32-contribute-to-frontend-without-installing-signoz-backend)
- [Contribute to Backend (Query-Service)](#4-contribute-to-backend-query-service-)
- [To run ClickHouse setup](#41-to-run-clickhouse-setup-recommended-for-local-development)
- [Contribute to SigNoz Helm Chart](#5-contribute-to-signoz-helm-chart-)
- [To run helm chart for local development](#51-to-run-helm-chart-for-local-development)
- [Other Ways to Contribute](#other-ways-to-contribute)
- `git clone https://github.com/SigNoz/signoz.git && cd signoz`
- comment out frontend service section at `deploy/docker/clickhouse-setup/docker-compose.yaml#L62`
- run `cd deploy` to move to deploy directory
- Install signoz locally without the frontend
- Add below configuration to query-service section at `docker/clickhouse-setup/docker-compose.yaml#L38`
# 1. General Instructions 📝
```docker
## 1.1 For Creating Issue(s)
Before making any significant changes and before filing a new issue, please check [existing open](https://github.com/SigNoz/signoz/issues?q=is%3Aopen+is%3Aissue), or [recently closed](https://github.com/SigNoz/signoz/issues?q=is%3Aissue+is%3Aclosed) issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can.
**Issue Types** - [Bug Report](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=bug_report.md&title=) | [Feature Request](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=feature_request.md&title=) | [Performance Issue Report](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=performance-issue-report.md&title=) | [Report a Security Vulnerability](https://github.com/SigNoz/signoz/security/policy)
#### Details like these are incredibly useful:
- **Requirement** - what kind of use case are you trying to solve?
- **Proposal** - what do you suggest to solve the problem or improve the existing
situation?
- Any open questions to address❓
#### If you are reporting a bug, details like these are incredibly useful:
- A reproducible test case or series of steps.
- The version of our code being used.
- Any modifications you've made relevant to the bug🐞.
- Anything unusual about your environment or deployment.
Discussing your proposed changes ahead of time will make the contribution
process smooth for everyone 🙌.
**[`^top^`](#)**
<hr>
## 1.2 For Pull Request(s)
Contributions via pull requests are much appreciated. Once the approach is agreed upon ✅, make your changes and open a Pull Request(s).
Before sending us a pull request, please ensure that,
- Fork the SigNoz repo on GitHub, clone it on your machine.
- Create a branch with your changes.
- You are working against the latest source on the `develop` branch.
- Modify the source; please focus only on the specific change you are contributing.
- Ensure local tests pass.
- Commit to your fork using clear commit messages.
- Send us a pull request, answering any default questions in the pull request interface.
- Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation
- Once you've pushed your commits to GitHub, make sure that your branch can be auto-merged (there are no merge conflicts). If not, on your computer, merge main into your branch, resolve any merge conflicts, make sure everything still runs correctly and passes all the tests, and then push up those changes.
- Once the change has been approved and merged, we will inform you in a comment.
GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
**Note:** Unless your change is small, **please** consider submitting different Pull Rrequest(s):
* 1⃣ First PR should include the overall structure of the new component:
* Readme, configuration, interfaces or base classes, etc...
* This PR is usually trivial to review, so the size limit does not apply to
it.
* 2⃣ Second PR should include the concrete implementation of the component. If the
size of this PR is larger than the recommended size, consider **splitting** ⚔️ it into
multiple PRs.
* If there are multiple sub-component then ideally each one should be implemented as
a **separate** pull request.
* Last PR should include changes to **any user-facing documentation.** And should include
end-to-end tests if applicable. The component must be enabled
only after sufficient testing, and there is enough confidence in the
stability and quality of the component.
You can always reach out to `ankit@signoz.io` to understand more about the repo and product. We are very responsive over email and [SLACK](https://signoz.io/slack).
### Pointers:
- If you find any **bugs** → please create an [**issue.**](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=bug_report.md&title=)
- If you find anything **missing** in documentation → you can create an issue with the label **`documentation`**.
- If you want to build any **new feature** → please create an [issue with the label **`enhancement`**.](https://github.com/SigNoz/signoz/issues/new?assignees=&labels=&template=feature_request.md&title=)
- If you want to **discuss** something about the product, start a new [**discussion**.](https://github.com/SigNoz/signoz/discussions)
<hr>
### Conventions to follow when submitting Commits and Pull Request(s).
We try to follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), more specifically the commits and PRs **should have type specifiers** prefixed in the name. [This](https://www.conventionalcommits.org/en/v1.0.0/#specification) should give you a better idea.
e.g. If you are submitting a fix for an issue in frontend, the PR name should be prefixed with **`fix(FE):`**
- Follow [GitHub Flow](https://guides.github.com/introduction/flow/) guidelines for your contribution flows.
- Feel free to ping us on [`#contributing`](https://signoz-community.slack.com/archives/C01LWQ8KS7M) or [`#contributing-frontend`](https://signoz-community.slack.com/archives/C027134DM8B) on our slack community if you need any help on this :)
**[`^top^`](#)**
<hr>
# 2. How to Contribute 🙋🏻‍♂️
#### There are primarily 2 areas in which you can contribute to SigNoz
- [**Frontend**](#3-develop-frontend-) (Written in Typescript, React)
- [**Backend**](#4-contribute-to-backend-query-service-) (Query Service, written in Go)
Depending upon your area of expertise & interest, you can choose one or more to contribute. Below are detailed instructions to contribute in each area.
**Please note:** If you want to work on an issue, please ask the maintainers to assign the issue to you before starting work on it. This would help us understand who is working on an issue and prevent duplicate work. 🙏🏻
⚠️ If you just raise a PR, without the corresponding issue being assigned to you - it may not be accepted.
**[`^top^`](#)**
<hr>
# 3. Develop Frontend 🌚
**Need to Update: [https://github.com/SigNoz/signoz/tree/develop/frontend](https://github.com/SigNoz/signoz/tree/develop/frontend)**
Also, have a look at [Frontend README.md](https://github.com/SigNoz/signoz/blob/develop/frontend/README.md) sections for more info on how to setup SigNoz frontend locally (with and without Docker).
## 3.1 Contribute to Frontend with Docker installation of SigNoz
- Clone the SigNoz repository and cd into signoz directory,
```
git clone https://github.com/SigNoz/signoz.git && cd signoz
```
- Comment out `frontend` service section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L68`](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L68)
![develop-frontend](https://user-images.githubusercontent.com/52788043/179009217-6692616b-17dc-4d27-b587-9d007098d739.jpeg)
- run `cd deploy` to move to deploy directory,
- Install signoz locally **without** the frontend,
- Add / Uncomment the below configuration to query-service section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L47`](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L47)
```
ports:
- "8080:8080"
```
- If you are using x86_64 processors (All Intel/AMD processors) run `sudo docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d`
- If you are on arm64 processors (Apple M1 Macbooks) run `sudo docker-compose -f docker/clickhouse-setup/docker-compose.arm.yaml up -d`
- `cd ../frontend` and change baseURL to `http://localhost:8080` in file `src/constants/env.ts`
- `yarn install`
- `yarn dev`
<img width="869" alt="query service" src="https://user-images.githubusercontent.com/52788043/179010251-8489be31-04ca-42f8-b30d-ef0bb6accb6b.png">
- Next run,
```
sudo docker-compose -f docker/clickhouse-setup/docker-compose.yaml up -d
```
- `cd ../frontend` and change baseURL in file [`frontend/src/constants/env.ts#L2`](https://github.com/SigNoz/signoz/blob/develop/frontend/src/constants/env.ts#L2) and for that, you need to create a `.env` file in the `frontend` directory with the following environment variable (`FRONTEND_API_ENDPOINT`) matching your configuration.
> Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh`
If you have backend api exposed via frontend nginx:
```
FRONTEND_API_ENDPOINT=http://localhost:3301
```
If not:
```
FRONTEND_API_ENDPOINT=http://localhost:8080
```
### Contribute to Frontend without installing SigNoz backend
- Next,
```
yarn install
yarn dev
```
If you don't want to install SigNoz backend just for doing frontend development, we can provide you with test environments which you can use as the backend. Please ping us in #contributing channel in our [slack community](https://signoz.io/slack) and we will DM you with `<test environment URL>`
### Important Notes:
The Maintainers / Contributors who will change Line Numbers of `Frontend` & `Query-Section`, please update line numbers in [`/.scripts/commentLinesForSetup.sh`](https://github.com/SigNoz/signoz/blob/develop/.scripts/commentLinesForSetup.sh)
- `git clone https://github.com/SigNoz/signoz.git && cd signoz/frontend`
- Create a file `.env` with `FRONTEND_API_ENDPOINT=<test environment URL>`
- `yarn install`
- `yarn dev`
**[`^top^`](#)**
**_Frontend should now be accessible at `http://localhost:3301/application`_**
## 3.2 Contribute to Frontend without installing SigNoz backend
# Contribute to Query-Service
If you don't want to install the SigNoz backend just for doing frontend development, we can provide you with test environments that you can use as the backend.
Need to update [https://github.com/SigNoz/signoz/tree/main/pkg/query-service](https://github.com/SigNoz/signoz/tree/main/pkg/query-service)
- Clone the SigNoz repository and cd into signoz/frontend directory,
```
git clone https://github.com/SigNoz/signoz.git && cd signoz/frontend
````
- Create a file `.env` in the `frontend` directory with `FRONTEND_API_ENDPOINT=<test environment URL>`
- Next,
```
yarn install
yarn dev
```
### To run ClickHouse setup (recommended for local development)
Please ping us in the [`#contributing`](https://signoz-community.slack.com/archives/C01LWQ8KS7M) channel or ask `@Prashant Shahi` in our [Slack Community](https://signoz.io/slack) and we will DM you with `<test environment URL>`.
- git clone https://github.com/SigNoz/signoz.git
- run `cd signoz` to move to signoz directory
- run `sudo make dev-setup` to configure local setup to run query-service
- comment out frontend service section at `docker/clickhouse-setup/docker-compose.yaml`
- comment out query-service section at `docker/clickhouse-setup/docker-compose.yaml`
- add below configuration to clickhouse section at `docker/clickhouse-setup/docker-compose.yaml`
```docker
expose:
- 9000
ports:
- 9001:9000
**Frontend should now be accessible at** [`http://localhost:3301/application`](http://localhost:3301/application)
**[`^top^`](#)**
<hr>
# 4. Contribute to Backend (Query-Service) 🌑
[**https://github.com/SigNoz/signoz/tree/develop/pkg/query-service**](https://github.com/SigNoz/signoz/tree/develop/pkg/query-service)
## 4.1 To run ClickHouse setup (recommended for local development)
- Clone the SigNoz repository and cd into signoz directory,
```
git clone https://github.com/SigNoz/signoz.git && cd signoz
```
- run `sudo make dev-setup` to configure local setup to run query-service,
- Comment out `frontend` service section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L68`](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L68)
<img width="982" alt="develop-frontend" src="https://user-images.githubusercontent.com/52788043/179043977-012be8b0-a2ed-40d1-b2e6-2ab72d7989c0.png">
- Comment out `query-service` section at [`deploy/docker/clickhouse-setup/docker-compose.yaml#L41`,](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml#L41)
<img width="1068" alt="Screenshot 2022-07-14 at 22 48 07" src="https://user-images.githubusercontent.com/52788043/179044151-a65ba571-db0b-4a16-b64b-ca3fadcf3af0.png">
- add below configuration to `clickhouse` section at [`deploy/docker/clickhouse-setup/docker-compose.yaml`,](https://github.com/SigNoz/signoz/blob/develop/deploy/docker/clickhouse-setup/docker-compose.yaml)
```
ports:
- 9001:9000
```
<img width="1013" alt="Screenshot 2022-07-14 at 22 50 37" src="https://user-images.githubusercontent.com/52788043/179044544-a293d3bc-4c4f-49ea-a276-505a381de67d.png">
- run `cd pkg/query-service/` to move to `query-service` directory,
- Then, you need to create a `.env` file with the following environment variable
```
SIGNOZ_LOCAL_DB_PATH="./signoz.db"
```
to set your local environment with the right `RELATIONAL_DATASOURCE_PATH` as mentioned in [`./constants/constants.go#L38`,](https://github.com/SigNoz/signoz/blob/develop/pkg/query-service/constants/constants.go#L38)
- Now, 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 Macs) run `sudo make run-arm`
#### Run locally,
```
- run `cd pkg/query-service/` to move to query-service directory
- Open ./constants/constants.go
- Replace ```const RELATIONAL_DATASOURCE_PATH = "/var/lib/signoz/signoz.db"``` \
with ```const RELATIONAL_DATASOURCE_PATH = "./signoz.db".```
- Install signoz locally without the frontend and query-service
- 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`
#### Run locally
```console
ClickHouseUrl=tcp://localhost:9001 STORAGE=clickhouse go run main.go
```
> Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh`
#### Build and Run locally
```
cd pkg/query-service
go build -o build/query-service main.go
ClickHouseUrl=tcp://localhost:9001 STORAGE=clickhouse build/query-service
```
**_Query Service should now be available at `http://localhost:8080`_**
#### Docker Images
The docker images of query-service is available at https://hub.docker.com/r/signoz/query-service
> If you want to see how, frontend plays with query service, you can run frontend also in you local env with the baseURL changed to `http://localhost:8080` in file `src/constants/env.ts` as the query-service is now running at port `8080`
```
docker pull signoz/query-service
```
```
docker pull signoz/query-service:latest
```
```
docker pull signoz/query-service:develop
```
### Important Note:
The Maintainers / Contributors who will change Line Numbers of `Frontend` & `Query-Section`, please update line numbers in [`/.scripts/commentLinesForSetup.sh`](https://github.com/SigNoz/signoz/blob/develop/.scripts/commentLinesForSetup.sh)
**Query Service should now be available at** [`http://localhost:8080`](http://localhost:8080)
If you want to see how the frontend plays with query service, you can run the frontend also in your local env with the baseURL changed to `http://localhost:8080` in file [`frontend/src/constants/env.ts`](https://github.com/SigNoz/signoz/blob/develop/frontend/src/constants/env.ts) as the `query-service` is now running at port `8080`.
---
<!-- Instead of configuring a local setup, you can also use [Gitpod](https://www.gitpod.io/), a VSCode-based Web IDE.
Click the button below. A workspace with all required environments will be created.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/SigNoz/signoz)
> To use it on your forked repo, edit the 'Open in Gitpod' button url to `https://gitpod.io/#https://github.com/<your-github-username>/signoz` -->
> To use it on your forked repo, edit the 'Open in Gitpod' button URL to `https://gitpod.io/#https://github.com/<your-github-username>/signoz` -->
# Contribute to SigNoz Helm Chart
**[`^top^`](#)**
<hr>
Need to update [https://github.com/SigNoz/charts](https://github.com/SigNoz/charts).
# 5. Contribute to SigNoz Helm Chart 📊
### To run helm chart for local development
**Need to Update: [https://github.com/SigNoz/charts](https://github.com/SigNoz/charts).**
- run `git clone https://github.com/SigNoz/charts.git` followed by `cd charts`
- it is recommended to use lightweight kubernetes (k8s) cluster for local development:
## 5.1 To run helm chart for local development
- Clone the SigNoz repository and cd into charts directory,
```
git clone https://github.com/SigNoz/charts.git && cd charts
```
- It is recommended to use lightweight kubernetes (k8s) cluster for local development:
- [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation)
- [k3d](https://k3d.io/#installation)
- [minikube](https://minikube.sigs.k8s.io/docs/start/)
- create a k8s cluster and make sure `kubectl` points to the locally created k8s cluster
- run `make dev-install` to install SigNoz chart with `my-release` release name in `platform` namespace.
- run `kubectl -n platform port-forward svc/my-release-signoz-frontend 3301:3301` to make SigNoz UI available at [localhost:3301](http://localhost:3301)
- create a k8s cluster and make sure `kubectl` points to the locally created k8s cluster,
- run `make dev-install` to install SigNoz chart with `my-release` release name in `platform` namespace,
- next run,
```
kubectl -n platform port-forward svc/my-release-signoz-frontend 3301:3301
```
to make SigNoz UI available at [localhost:3301](http://localhost:3301)
**To install HotROD sample app:**
**5.1.1 To install the HotROD sample app:**
```bash
curl -sL https://github.com/SigNoz/signoz/raw/main/sample-apps/hotrod/hotrod-install.sh \
| HELM_RELEASE=my-release SIGNOZ_NAMESPACE=platform bash
```
**To load data with HotROD sample app:**
**5.1.2 To load data with the HotROD sample app:**
```bash
kubectl -n sample-application run strzal --image=djbingham/curl \
@@ -124,7 +333,7 @@ kubectl -n sample-application run strzal --image=djbingham/curl \
'locust_count=6' -F 'hatch_rate=2' http://locust-master:8089/swarm
```
**To stop the load generation:**
**5.1.3 To stop the load generation:**
```bash
kubectl -n sample-application run strzal --image=djbingham/curl \
@@ -132,59 +341,32 @@ kubectl -n sample-application run strzal --image=djbingham/curl \
http://locust-master:8089/stop
```
**To delete HotROD sample app:**
**5.1.4 To delete the HotROD sample app:**
```bash
curl -sL https://github.com/SigNoz/signoz/raw/main/sample-apps/hotrod/hotrod-delete.sh \
| HOTROD_NAMESPACE=sample-application bash
```
**[`^top^`](#)**
---
## General Instructions
## Other Ways to Contribute
**Before making any significant changes, please open an issue**. Each issue
should describe the following:
There are many other ways to get involved with the community and to participate in this project:
* Requirement - what kind of use case are you trying to solve?
* Proposal - what do you suggest to solve the problem or improve the existing
situation?
* Any open questions to address
Discussing your proposed changes ahead of time will make the contribution
process smooth for everyone. Once the approach is agreed upon, make your changes
and open a pull request(s). Unless your change is small, Please consider submitting different PRs:
* First PR should include the overall structure of the new component:
* Readme, configuration, interfaces or base classes etc...
* This PR is usually trivial to review, so the size limit does not apply to
it.
* Second PR should include the concrete implementation of the component. If the
size of this PR is larger than the recommended size consider splitting it in
multiple PRs.
* If there are multiple sub-component then ideally each one should be implemented as
a separate pull request.
* Last PR should include changes to any user facing documentation. And should include
end to end tests if applicable. The component must be enabled
only after sufficient testing, and there is enough confidence in the
stability and quality of the component.
- Use the product, submitting GitHub issues when a problem is found.
- Help code review pull requests and participate in issue threads.
- Submit a new feature request as an issue.
- Help answer questions on forums such as Stack Overflow and [SigNoz Community Slack Channel](https://signoz.io/slack).
- Tell others about the project on Twitter, your blog, etc.
You can always reach out to `ankit@signoz.io` to understand more about the repo and product. We are very responsive over email and [slack](https://signoz.io/slack).
## License
- If you find any bugs, please create an issue
- If you find anything missing in documentation, you can create an issue with label **documentation**
- If you want to build any new feature, please create an issue with label `enhancement`
- If you want to discuss something about the product, start a new [discussion](https://github.com/SigNoz/signoz/discussions)
By contributing to SigNoz, you agree that your contributions will be licensed under its MIT license.
### Conventions to follow when submitting commits, PRs
Again, Feel free to ping us on [`#contributing`](https://signoz-community.slack.com/archives/C01LWQ8KS7M) or [`#contributing-frontend`](https://signoz-community.slack.com/archives/C027134DM8B) on our slack community if you need any help on this :)
1. We try to follow https://www.conventionalcommits.org/en/v1.0.0/
More specifically the commits and PRs should have type specifiers prefixed in the name. [This](https://www.conventionalcommits.org/en/v1.0.0/#specification) should give you a better idea.
e.g. If you are submitting a fix for an issue in frontend - PR name should be prefixed with `fix(FE):`
2. Follow [GitHub Flow](https://guides.github.com/introduction/flow/) guidelines for your contribution flows
3. Feel free to ping us on `#contributing` or `#contributing-frontend` on our slack community if you need any help on this :)
Thank You!

View File

@@ -13,6 +13,8 @@ FRONTEND_DIRECTORY ?= frontend
QUERY_SERVICE_DIRECTORY ?= pkg/query-service
STANDALONE_DIRECTORY ?= deploy/docker/clickhouse-setup
SWARM_DIRECTORY ?= deploy/docker-swarm/clickhouse-setup
LOCAL_GOOS ?= $(shell go env GOOS)
LOCAL_GOARCH ?= $(shell go env GOARCH)
REPONAME ?= signoz
DOCKER_TAG ?= latest
@@ -79,11 +81,25 @@ dev-setup:
@echo "--> Local Setup completed"
@echo "------------------"
run-local:
@LOCAL_GOOS=$(LOCAL_GOOS) LOCAL_GOARCH=$(LOCAL_GOARCH) docker-compose -f \
$(STANDALONE_DIRECTORY)/docker-compose-core.yaml -f $(STANDALONE_DIRECTORY)/docker-compose-local.yaml \
up --build -d
down-local:
@docker-compose -f \
$(STANDALONE_DIRECTORY)/docker-compose-core.yaml -f $(STANDALONE_DIRECTORY)/docker-compose-local.yaml \
down -v
run-x86:
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml up -d
@docker-compose -f \
$(STANDALONE_DIRECTORY)/docker-compose-core.yaml -f $(STANDALONE_DIRECTORY)/docker-compose-prod.yaml \
up --build -d
down-x86:
@docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml down -v
@docker-compose -f \
$(STANDALONE_DIRECTORY)/docker-compose-core.yaml -f $(STANDALONE_DIRECTORY)/docker-compose-prod.yaml \
down -v
clear-standalone-data:
@docker run --rm -v "$(PWD)/$(STANDALONE_DIRECTORY)/data:/pwd" busybox \

View File

@@ -22,7 +22,7 @@
[1]: https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/Logger.h#L105-L114
-->
<level>trace</level>
<level>information</level>
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
<errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
<!-- Rotation policy

View File

@@ -20,6 +20,7 @@
</default>
<s3>
<disk>s3</disk>
<perform_ttl_move_on_insert>0</perform_ttl_move_on_insert>
</s3>
</volumes>
</tiered>

View File

@@ -27,7 +27,7 @@ services:
retries: 3
alertmanager:
image: signoz/alertmanager:0.23.0-0.1
image: signoz/alertmanager:0.23.0-0.2
volumes:
- ./data/alertmanager:/data
command:
@@ -40,7 +40,7 @@ services:
condition: on-failure
query-service:
image: signoz/query-service:0.9.2
image: signoz/query-service:0.11.1
command: ["-config=/root/config/prometheus.yml"]
# ports:
# - "6060:6060" # pprof port
@@ -51,10 +51,12 @@ services:
- ./data/signoz/:/var/lib/signoz/
environment:
- ClickHouseUrl=tcp://clickhouse:9000/?database=signoz_traces
- ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/
- STORAGE=clickhouse
- GODEBUG=netdns=go
- TELEMETRY_ENABLED=true
- DEPLOYMENT_TYPE=docker-swarm
- SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"]
@@ -68,7 +70,7 @@ services:
- clickhouse
frontend:
image: signoz/frontend:0.9.2
image: signoz/frontend:0.11.1
deploy:
restart_policy:
condition: on-failure
@@ -81,36 +83,43 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/otelcontribcol:0.45.1-1.0
image: signoz-otel-collector:0.55.1
command: ["--config=/etc/otel-collector-config.yaml"]
user: root # required for reading docker container logs
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}},dockerswarm.service.name={{.Service.Name}},dockerswarm.task.name={{.Task.Name}}
ports:
# - "1777:1777" # pprof extension
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
# - "8889:8889" # Prometheus metrics exposed by the agent
# - "13133:13133" # health_check
# - "14268:14268" # Jaeger receiver
# - "8888:8888" # OtelCollector internal metrics
# - "8889:8889" # signoz spanmetrics exposed by the agent
# - "9411:9411" # Zipkin port
# - "13133:13133" # Health check extension
# - "14250:14250" # Jaeger gRPC
# - "14268:14268" # Jaeger thrift HTTP
# - "55678:55678" # OpenCensus receiver
# - "55679:55679" # zpages extension
# - "55680:55680" # OTLP gRPC legacy receiver
# - "55681:55681" # OTLP HTTP legacy receiver
# - "55679:55679" # zPages extension
deploy:
mode: replicated
replicas: 3
mode: global
restart_policy:
condition: on-failure
resources:
limits:
memory: 2000m
depends_on:
- clickhouse
otel-collector-metrics:
image: signoz/otelcontribcol:0.45.1-1.0
image: signoz-otel-collector:0.55.1
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
volumes:
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
# ports:
# - "1777:1777" # pprof extension
# - "8888:8888" # OtelCollector internal metrics
# - "13133:13133" # Health check extension
# - "55679:55679" # zPages extension
deploy:
restart_policy:
condition: on-failure

View File

@@ -1,30 +1,71 @@
receivers:
filelog/dockercontainers:
include: [ "/var/lib/docker/containers/*/*.log" ]
start_at: end
include_file_path: true
include_file_name: false
operators:
- type: json_parser
id: parser-docker
output: extract_metadata_from_filepath
timestamp:
parse_from: attributes.time
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
- type: regex_parser
id: extract_metadata_from_filepath
regex: '^.*containers/(?P<container_id>[^_]+)/.*log$'
parse_from: attributes["log.file.path"]
output: parse_body
- type: move
id: parse_body
from: attributes.log
to: body
output: time
- type: remove
id: time
field: attributes.time
opencensus:
endpoint: 0.0.0.0:55678
otlp/spanmetrics:
protocols:
grpc:
endpoint: "localhost:12345"
endpoint: localhost:12345
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
jaeger:
protocols:
grpc:
endpoint: 0.0.0.0:14250
thrift_http:
endpoint: 0.0.0.0:14268
# thrift_compact:
# endpoint: 0.0.0.0:6831
# thrift_binary:
# endpoint: 0.0.0.0:6832
hostmetrics:
collection_interval: 60s
scrapers:
cpu:
load:
memory:
disk:
filesystem:
network:
cpu: {}
load: {}
memory: {}
disk: {}
filesystem: {}
network: {}
processors:
batch:
send_batch_size: 10000
send_batch_max_size: 11000
timeout: 10s
resourcedetection:
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
detectors: [env, system] # include ec2 for AWS, gce for GCP and azure for Azure.
timeout: 2s
override: false
signozspanmetrics/prometheus:
metrics_exporter: prometheus
latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s ]
@@ -49,9 +90,7 @@ processors:
# num_workers: 4
# queue_size: 100
# retry_on_failure: true
extensions:
health_check: {}
zpages: {}
exporters:
clickhousetraces:
datasource: tcp://clickhouse:9000/?database=signoz_traces
@@ -60,18 +99,49 @@ exporters:
resource_to_telemetry_conversion:
enabled: true
prometheus:
endpoint: "0.0.0.0:8889"
endpoint: 0.0.0.0:8889
# logging: {}
clickhouselogsexporter:
dsn: tcp://clickhouse:9000/
timeout: 5s
sending_queue:
queue_size: 100
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 300s
extensions:
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
pprof:
endpoint: 0.0.0.0:1777
service:
extensions: [health_check, zpages]
telemetry:
metrics:
address: 0.0.0.0:8888
extensions: [health_check, zpages, pprof]
pipelines:
traces:
receivers: [jaeger, otlp]
processors: [signozspanmetrics/prometheus, batch]
exporters: [clickhousetraces]
metrics:
receivers: [otlp, hostmetrics]
receivers: [otlp]
processors: [batch]
exporters: [clickhousemetricswrite]
metrics/hostmetrics:
receivers: [hostmetrics]
processors: [resourcedetection, batch]
exporters: [clickhousemetricswrite]
metrics/spanmetrics:
receivers: [otlp/spanmetrics]
exporters: [prometheus]
exporters: [prometheus]
logs:
receivers: [otlp, filelog/dockercontainers]
processors: [batch]
exporters: [clickhouselogsexporter]

View File

@@ -1,17 +1,30 @@
receivers:
otlp:
protocols:
grpc:
http:
# Data sources: metrics
prometheus:
config:
scrape_configs:
# otel-collector internal metrics
- job_name: "otel-collector"
scrape_interval: 60s
dns_sd_configs:
- names:
- 'tasks.otel-collector'
type: 'A'
port: 8888
# otel-collector-metrics internal metrics
- job_name: "otel-collector-metrics"
scrape_interval: 60s
static_configs:
- targets: ["otel-collector:8889"]
- targets:
- localhost:8888
# SigNoz span metrics
- job_name: "signozspanmetrics-collector"
scrape_interval: 60s
dns_sd_configs:
- names:
- 'tasks.otel-collector'
type: 'A'
port: 8889
processors:
batch:
send_batch_size: 10000
@@ -32,17 +45,26 @@ processors:
# num_workers: 4
# queue_size: 100
# retry_on_failure: true
extensions:
health_check: {}
zpages: {}
exporters:
clickhousemetricswrite:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
extensions:
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
pprof:
endpoint: 0.0.0.0:1777
service:
extensions: [health_check, zpages]
telemetry:
metrics:
address: 0.0.0.0:8888
extensions: [health_check, zpages, pprof]
pipelines:
metrics:
receivers: [otlp, prometheus]
receivers: [prometheus]
processors: [batch]
exporters: [clickhousemetricswrite]
exporters: [clickhousemetricswrite]

View File

@@ -19,8 +19,7 @@ rule_files:
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
scrape_configs: []
remote_read:
- url: tcp://clickhouse:9000/?database=signoz_metrics

View File

@@ -3,13 +3,17 @@ server {
server_name _;
gzip on;
gzip_static on;
gzip_static on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_http_version 1.1;
# to handle uri issue 414 from nginx
client_max_body_size 24M;
large_client_header_buffers 8 16k;
location / {
if ( $uri = '/index.html' ) {

View File

@@ -22,7 +22,7 @@
[1]: https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/Logger.h#L105-L114
-->
<level>trace</level>
<level>information</level>
<log>/var/log/clickhouse-server/clickhouse-server.log</log>
<errorlog>/var/log/clickhouse-server/clickhouse-server.err.log</errorlog>
<!-- Rotation policy

View File

@@ -20,6 +20,7 @@
</default>
<s3>
<disk>s3</disk>
<perform_ttl_move_on_insert>0</perform_ttl_move_on_insert>
</s3>
</volumes>
</tiered>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
version: "2.4"
services:
clickhouse:
image: clickhouse/clickhouse-server:22.4.5-alpine
container_name: clickhouse
# ports:
# - "9000:9000"
# - "8123:8123"
tty: true
volumes:
- ./clickhouse-config.xml:/etc/clickhouse-server/config.xml
- ./clickhouse-users.xml:/etc/clickhouse-server/users.xml
# - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml
- ./data/clickhouse/:/var/lib/clickhouse/
restart: on-failure
logging:
options:
max-size: 50m
max-file: "3"
healthcheck:
# "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'"
test: ["CMD", "wget", "--spider", "-q", "localhost:8123/ping"]
interval: 30s
timeout: 5s
retries: 3
alertmanager:
container_name: alertmanager
image: signoz/alertmanager:0.23.0-0.2
volumes:
- ./data/alertmanager:/data
depends_on:
query-service:
condition: service_healthy
restart: on-failure
command:
- --queryService.url=http://query-service:8085
- --storage.path=/data
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
otel-collector:
container_name: otel-collector
image: signoz/signoz-otel-collector:0.55.1
command: ["--config=/etc/otel-collector-config.yaml"]
# user: root # required for reading docker container logs
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
ports:
# - "1777:1777" # pprof extension
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
# - "8888:8888" # OtelCollector internal metrics
# - "8889:8889" # signoz spanmetrics exposed by the agent
# - "9411:9411" # Zipkin port
# - "13133:13133" # health check extension
# - "14250:14250" # Jaeger gRPC
# - "14268:14268" # Jaeger thrift HTTP
# - "55678:55678" # OpenCensus receiver
# - "55679:55679" # zPages extension
restart: on-failure
depends_on:
clickhouse:
condition: service_healthy
otel-collector-metrics:
container_name: otel-collector-metrics
image: signoz/signoz-otel-collector:0.55.1
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
volumes:
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
# ports:
# - "1777:1777" # pprof extension
# - "8888:8888" # OtelCollector internal metrics
# - "13133:13133" # Health check extension
# - "55679:55679" # zPages extension
restart: on-failure
depends_on:
clickhouse:
condition: service_healthy
hotrod:
image: jaegertracing/example-hotrod:1.30
container_name: hotrod
logging:
options:
max-size: 50m
max-file: "3"
command: ["all"]
environment:
- JAEGER_ENDPOINT=http://otel-collector:14268/api/traces
load-hotrod:
image: "grubykarol/locust:1.2.3-python3.9-alpine3.12"
container_name: load-hotrod
hostname: load-hotrod
environment:
ATTACKED_HOST: http://hotrod:8080
LOCUST_MODE: standalone
NO_PROXY: standalone
TASK_DELAY_FROM: 5
TASK_DELAY_TO: 30
QUIET_MODE: "${QUIET_MODE:-false}"
LOCUST_OPTS: "--headless -u 10 -r 1"
volumes:
- ../common/locust-scripts:/locust

View File

@@ -0,0 +1,55 @@
version: "2.4"
services:
query-service:
hostname: query-service
build:
context: "../../../pkg/query-service"
dockerfile: "./Dockerfile"
args:
LDFLAGS: ""
TARGETPLATFORM: "${LOCAL_GOOS}/${LOCAL_GOARCH}"
container_name: query-service
environment:
- ClickHouseUrl=tcp://clickhouse:9000
- ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/
- STORAGE=clickhouse
- GODEBUG=netdns=go
- TELEMETRY_ENABLED=true
- SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db
volumes:
- ./prometheus.yml:/root/config/prometheus.yml
- ../dashboards:/root/config/dashboards
- ./data/signoz/:/var/lib/signoz/
command: ["-config=/root/config/prometheus.yml"]
ports:
- "6060:6060"
- "8080:8080"
restart: on-failure
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"]
interval: 30s
timeout: 5s
retries: 3
depends_on:
clickhouse:
condition: service_healthy
frontend:
build:
context: "../../../frontend"
dockerfile: "./Dockerfile"
args:
TARGETOS: "${LOCAL_GOOS}"
TARGETPLATFORM: "${LOCAL_GOARCH}"
container_name: frontend
environment:
- FRONTEND_API_ENDPOINT=http://query-service:8080
restart: on-failure
depends_on:
- alertmanager
- query-service
ports:
- "3301:3301"
volumes:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf

View File

@@ -0,0 +1,43 @@
version: "2.4"
services:
query-service:
image: signoz/query-service:0.11.1
container_name: query-service
command: ["-config=/root/config/prometheus.yml"]
# ports:
# - "6060:6060" # pprof port
# - "8080:8080" # query-service port
volumes:
- ./prometheus.yml:/root/config/prometheus.yml
- ../dashboards:/root/config/dashboards
- ./data/signoz/:/var/lib/signoz/
environment:
- ClickHouseUrl=tcp://clickhouse:9000/?database=signoz_traces
- ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/
- STORAGE=clickhouse
- GODEBUG=netdns=go
- TELEMETRY_ENABLED=true
- DEPLOYMENT_TYPE=docker-standalone-amd
- SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db
restart: on-failure
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "localhost:8080/api/v1/version"]
interval: 30s
timeout: 5s
retries: 3
depends_on:
clickhouse:
condition: service_healthy
frontend:
image: signoz/frontend:0.11.1
container_name: frontend
restart: on-failure
depends_on:
- alertmanager
- query-service
ports:
- "3301:3301"
volumes:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf

View File

@@ -25,7 +25,7 @@ services:
retries: 3
alertmanager:
image: signoz/alertmanager:0.23.0-0.1
image: signoz/alertmanager:0.23.0-0.2
volumes:
- ./data/alertmanager:/data
depends_on:
@@ -39,7 +39,7 @@ services:
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
query-service:
image: signoz/query-service:0.9.2
image: signoz/query-service:0.11.1
container_name: query-service
command: ["-config=/root/config/prometheus.yml"]
# ports:
@@ -51,6 +51,8 @@ services:
- ./data/signoz/:/var/lib/signoz/
environment:
- ClickHouseUrl=tcp://clickhouse:9000/?database=signoz_traces
- ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/
- SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db
- STORAGE=clickhouse
- GODEBUG=netdns=go
- TELEMETRY_ENABLED=true
@@ -66,7 +68,7 @@ services:
condition: service_healthy
frontend:
image: signoz/frontend:0.9.2
image: signoz/frontend:0.11.1
container_name: frontend
restart: on-failure
depends_on:
@@ -78,31 +80,41 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/otelcontribcol:0.45.1-1.0
image: signoz/signoz-otel-collector:0.55.1
command: ["--config=/etc/otel-collector-config.yaml"]
user: root # required for reading docker container logs
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
ports:
# - "1777:1777" # pprof extension
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
# - "8889:8889" # Prometheus metrics exposed by the agent
# - "13133:13133" # health_check
# - "14268:14268" # Jaeger receiver
# - "8888:8888" # OtelCollector internal metrics
# - "8889:8889" # signoz spanmetrics exposed by the agent
# - "9411:9411" # Zipkin port
# - "13133:13133" # health check extension
# - "14250:14250" # Jaeger gRPC
# - "14268:14268" # Jaeger thrift HTTP
# - "55678:55678" # OpenCensus receiver
# - "55679:55679" # zpages extension
# - "55680:55680" # OTLP gRPC legacy receiver
# - "55681:55681" # OTLP HTTP legacy receiver
mem_limit: 2000m
# - "55679:55679" # zPages extension
restart: on-failure
depends_on:
clickhouse:
condition: service_healthy
otel-collector-metrics:
image: signoz/otelcontribcol:0.45.1-1.0
image: signoz/signoz-otel-collector:0.55.1
command: ["--config=/etc/otel-collector-metrics-config.yaml"]
volumes:
- ./otel-collector-metrics-config.yaml:/etc/otel-collector-metrics-config.yaml
# ports:
# - "1777:1777" # pprof extension
# - "8888:8888" # OtelCollector internal metrics
# - "13133:13133" # Health check extension
# - "55679:55679" # zPages extension
restart: on-failure
depends_on:
clickhouse:

View File

@@ -1,25 +1,61 @@
receivers:
filelog/dockercontainers:
include: [ "/var/lib/docker/containers/*/*.log" ]
start_at: end
include_file_path: true
include_file_name: false
operators:
- type: json_parser
id: parser-docker
output: extract_metadata_from_filepath
timestamp:
parse_from: attributes.time
layout: '%Y-%m-%dT%H:%M:%S.%LZ'
- type: regex_parser
id: extract_metadata_from_filepath
regex: '^.*containers/(?P<container_id>[^_]+)/.*log$'
parse_from: attributes["log.file.path"]
output: parse_body
- type: move
id: parse_body
from: attributes.log
to: body
output: time
- type: remove
id: time
field: attributes.time
opencensus:
endpoint: 0.0.0.0:55678
otlp/spanmetrics:
protocols:
grpc:
endpoint: "localhost:12345"
endpoint: localhost:12345
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
jaeger:
protocols:
grpc:
endpoint: 0.0.0.0:14250
thrift_http:
endpoint: 0.0.0.0:14268
# thrift_compact:
# endpoint: 0.0.0.0:6831
# thrift_binary:
# endpoint: 0.0.0.0:6832
hostmetrics:
collection_interval: 60s
scrapers:
cpu:
load:
memory:
disk:
filesystem:
network:
cpu: {}
load: {}
memory: {}
disk: {}
filesystem: {}
network: {}
processors:
batch:
send_batch_size: 10000
@@ -49,9 +85,20 @@ processors:
# num_workers: 4
# queue_size: 100
# retry_on_failure: true
resourcedetection:
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
detectors: [env, system] # include ec2 for AWS, gce for GCP and azure for Azure.
timeout: 2s
override: false
extensions:
health_check: {}
zpages: {}
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
pprof:
endpoint: 0.0.0.0:1777
exporters:
clickhousetraces:
datasource: tcp://clickhouse:9000/?database=signoz_traces
@@ -60,18 +107,45 @@ exporters:
resource_to_telemetry_conversion:
enabled: true
prometheus:
endpoint: "0.0.0.0:8889"
endpoint: 0.0.0.0:8889
# logging: {}
clickhouselogsexporter:
dsn: tcp://clickhouse:9000/
timeout: 5s
sending_queue:
queue_size: 100
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 300s
service:
extensions: [health_check, zpages]
telemetry:
metrics:
address: 0.0.0.0:8888
extensions:
- health_check
- zpages
- pprof
pipelines:
traces:
receivers: [jaeger, otlp]
processors: [signozspanmetrics/prometheus, batch]
exporters: [clickhousetraces]
metrics:
receivers: [otlp, hostmetrics]
receivers: [otlp]
processors: [batch]
exporters: [clickhousemetricswrite]
metrics/hostmetrics:
receivers: [hostmetrics]
processors: [resourcedetection, batch]
exporters: [clickhousemetricswrite]
metrics/spanmetrics:
receivers: [otlp/spanmetrics]
exporters: [prometheus]
logs:
receivers: [otlp, filelog/dockercontainers]
processors: [batch]
exporters: [clickhouselogsexporter]

View File

@@ -3,15 +3,28 @@ receivers:
protocols:
grpc:
http:
# Data sources: metrics
prometheus:
config:
scrape_configs:
# otel-collector internal metrics
- job_name: "otel-collector"
scrape_interval: 60s
static_configs:
- targets: ["otel-collector:8889"]
- targets:
- otel-collector:8888
# otel-collector-metrics internal metrics
- job_name: "otel-collector-metrics"
scrape_interval: 60s
static_configs:
- targets:
- localhost:8888
# SigNoz span metrics
- job_name: "signozspanmetrics-collector"
scrape_interval: 60s
static_configs:
- targets:
- otel-collector:8889
processors:
batch:
send_batch_size: 10000
@@ -32,17 +45,29 @@ processors:
# num_workers: 4
# queue_size: 100
# retry_on_failure: true
extensions:
health_check: {}
zpages: {}
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
pprof:
endpoint: 0.0.0.0:1777
exporters:
clickhousemetricswrite:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
service:
extensions: [health_check, zpages]
telemetry:
metrics:
address: 0.0.0.0:8888
extensions:
- health_check
- zpages
- pprof
pipelines:
metrics:
receivers: [otlp, prometheus]
receivers: [prometheus]
processors: [batch]
exporters: [clickhousemetricswrite]

View File

@@ -19,8 +19,7 @@ rule_files:
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
scrape_configs: []
remote_read:
- url: tcp://clickhouse:9000/?database=signoz_metrics

View File

@@ -1,123 +0,0 @@
<?xml version="1.0"?>
<clickhouse>
<!-- See also the files in users.d directory where the settings can be overridden. -->
<!-- Profiles of settings. -->
<profiles>
<!-- Default settings. -->
<default>
<!-- Maximum memory usage for processing single query, in bytes. -->
<max_memory_usage>10000000000</max_memory_usage>
<!-- How to choose between replicas during distributed query processing.
random - choose random replica from set of replicas with minimum number of errors
nearest_hostname - from set of replicas with minimum number of errors, choose replica
with minimum number of different symbols between replica's hostname and local hostname
(Hamming distance).
in_order - first live replica is chosen in specified order.
first_or_random - if first replica one has higher number of errors, pick a random one from replicas with minimum number of errors.
-->
<load_balancing>random</load_balancing>
</default>
<!-- Profile that allows only read queries. -->
<readonly>
<readonly>1</readonly>
</readonly>
</profiles>
<!-- Users and ACL. -->
<users>
<!-- If user name was not specified, 'default' user is used. -->
<default>
<!-- See also the files in users.d directory where the password can be overridden.
Password could be specified in plaintext or in SHA256 (in hex format).
If you want to specify password in plaintext (not recommended), place it in 'password' element.
Example: <password>qwerty</password>.
Password could be empty.
If you want to specify SHA256, place it in 'password_sha256_hex' element.
Example: <password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>
Restrictions of SHA256: impossibility to connect to ClickHouse using MySQL JS client (as of July 2019).
If you want to specify double SHA1, place it in 'password_double_sha1_hex' element.
Example: <password_double_sha1_hex>e395796d6546b1b65db9d665cd43f0e858dd4303</password_double_sha1_hex>
If you want to specify a previously defined LDAP server (see 'ldap_servers' in the main config) for authentication,
place its name in 'server' element inside 'ldap' element.
Example: <ldap><server>my_ldap_server</server></ldap>
If you want to authenticate the user via Kerberos (assuming Kerberos is enabled, see 'kerberos' in the main config),
place 'kerberos' element instead of 'password' (and similar) elements.
The name part of the canonical principal name of the initiator must match the user name for authentication to succeed.
You can also place 'realm' element inside 'kerberos' element to further restrict authentication to only those requests
whose initiator's realm matches it.
Example: <kerberos />
Example: <kerberos><realm>EXAMPLE.COM</realm></kerberos>
How to generate decent password:
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha256sum | tr -d '-'
In first line will be password and in second - corresponding SHA256.
How to generate double SHA1:
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha1sum | tr -d '-' | xxd -r -p | sha1sum | tr -d '-'
In first line will be password and in second - corresponding double SHA1.
-->
<password></password>
<!-- List of networks with open access.
To open access from everywhere, specify:
<ip>::/0</ip>
To open access only from localhost, specify:
<ip>::1</ip>
<ip>127.0.0.1</ip>
Each element of list has one of the following forms:
<ip> IP-address or network mask. Examples: 213.180.204.3 or 10.0.0.1/8 or 10.0.0.1/255.255.255.0
2a02:6b8::3 or 2a02:6b8::3/64 or 2a02:6b8::3/ffff:ffff:ffff:ffff::.
<host> Hostname. Example: server01.clickhouse.com.
To check access, DNS query is performed, and all received addresses compared to peer address.
<host_regexp> Regular expression for host names. Example, ^server\d\d-\d\d-\d\.clickhouse\.com$
To check access, DNS PTR query is performed for peer address and then regexp is applied.
Then, for result of PTR query, another DNS query is performed and all received addresses compared to peer address.
Strongly recommended that regexp is ends with $
All results of DNS requests are cached till server restart.
-->
<networks>
<ip>::/0</ip>
</networks>
<!-- Settings profile for user. -->
<profile>default</profile>
<!-- Quota for user. -->
<quota>default</quota>
<!-- User can create other users and grant rights to them. -->
<!-- <access_management>1</access_management> -->
</default>
</users>
<!-- Quotas. -->
<quotas>
<!-- Name of quota. -->
<default>
<!-- Limits for time interval. You could specify many intervals with different limits. -->
<interval>
<!-- Length of interval. -->
<duration>3600</duration>
<!-- No limits. Just calculate resource usage for time interval. -->
<queries>0</queries>
<errors>0</errors>
<result_rows>0</result_rows>
<read_rows>0</read_rows>
<execution_time>0</execution_time>
</interval>
</default>
</quotas>
</clickhouse>

View File

@@ -3,7 +3,7 @@ server {
server_name _;
gzip on;
gzip_static on;
gzip_static on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_proxied any;
gzip_vary on;
@@ -13,7 +13,6 @@ server {
# to handle uri issue 414 from nginx
client_max_body_size 24M;
large_client_header_buffers 8 16k;
location / {

View File

@@ -204,9 +204,14 @@ start_docker() {
echo "Starting docker service"
$sudo_cmd systemctl start docker.service
fi
# if [[ -z $sudo_cmd ]]; then
# docker ps > /dev/null && true
# if [[ $? -ne 0 ]]; then
# request_sudo
# fi
# fi
if [[ -z $sudo_cmd ]]; then
docker ps > /dev/null && true
if [[ $? -ne 0 ]]; then
if ! docker ps > /dev/null && true; then
request_sudo
fi
fi
@@ -268,8 +273,12 @@ request_sudo() {
if (( $EUID != 0 )); then
sudo_cmd="sudo"
echo -e "Please enter your sudo password, if prompt."
$sudo_cmd -l | grep -e "NOPASSWD: ALL" > /dev/null
if [[ $? -ne 0 ]] && ! $sudo_cmd -v; then
# $sudo_cmd -l | grep -e "NOPASSWD: ALL" > /dev/null
# if [[ $? -ne 0 ]] && ! $sudo_cmd -v; then
# echo "Need sudo privileges to proceed with the installation."
# exit 1;
# fi
if ! $sudo_cmd -l | grep -e "NOPASSWD: ALL" > /dev/null && ! $sudo_cmd -v; then
echo "Need sudo privileges to proceed with the installation."
exit 1;
fi
@@ -303,8 +312,13 @@ echo -e "🌏 Detecting your OS ...\n"
check_os
# Obtain unique installation id
sysinfo="$(uname -a)"
if [[ $? -ne 0 ]]; then
# sysinfo="$(uname -a)"
# if [[ $? -ne 0 ]]; then
# uuid="$(uuidgen)"
# uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
# sysinfo="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
# fi
if ! sysinfo="$(uname -a)"; then
uuid="$(uuidgen)"
uuid="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"
sysinfo="${uuid:-$(cat /proc/sys/kernel/random/uuid)}"

View File

@@ -1,3 +1,4 @@
node_modules
build
*.typegen.ts
i18-generate-hash.js

View File

@@ -13,7 +13,7 @@ WORKDIR /frontend
COPY package.json ./
# Install the dependencies and make the folder
RUN CI=1 yarn install
RUN CI=1 yarn install
COPY . .

View File

@@ -1,15 +1,19 @@
server {
listen 3301;
server_name _;
gzip on;
gzip_static on;
gzip_static on;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_http_version 1.1;
# to handle uri issue 414 from nginx
client_max_body_size 24M;
large_client_header_buffers 8 16k;
location / {
root /usr/share/nginx/html;

View File

@@ -0,0 +1,24 @@
/* eslint-disable */
// @ts-ignore
// @ts-nocheck
const crypto = require('crypto');
const fs = require('fs');
const glob = require('glob');
function generateChecksum(str, algorithm, encoding) {
return crypto
.createHash(algorithm || 'md5')
.update(str, 'utf8')
.digest(encoding || 'hex');
}
const result = {};
glob.sync(`public/locales/**/*.json`).forEach(path => {
const [_, lang] = path.split('public/locales');
const content = fs.readFileSync(path, { encoding: 'utf-8' });
result[lang.replace('.json', '')] = generateChecksum(content);
});
fs.writeFileSync('./i18n-translations-hash.json', JSON.stringify(result));

View File

@@ -4,18 +4,20 @@
"description": "",
"main": "webpack.config.js",
"scripts": {
"dev": "cross-env NODE_ENV=development webpack serve --progress",
"build": "webpack --config=webpack.config.prod.js --progress",
"i18n:generate-hash": "node ./i18-generate-hash.js",
"dev": "npm run i18n:generate-hash && cross-env NODE_ENV=development webpack serve --progress",
"build": "npm run i18n:generate-hash && webpack --config=webpack.config.prod.js --progress",
"prettify": "prettier --write .",
"lint": "eslint ./src",
"lint:fix": "eslint ./src --fix",
"lint": "npm run i18n:generate-hash && eslint ./src",
"lint:fix": "npm run i18n:generate-hash && eslint ./src --fix",
"jest": "jest",
"jest:coverage": "jest --coverage",
"jest:watch": "jest --watch",
"postinstall": "is-ci || yarn husky:configure",
"playwright": "NODE_ENV=testing playwright test --config=./playwright.config.ts",
"playwright": "npm run i18n:generate-hash && NODE_ENV=testing playwright test --config=./playwright.config.ts",
"playwright:local:debug": "PWDEBUG=console yarn playwright --headed --browser=chromium",
"playwright:codegen:local":"playwright codegen http://localhost:3301",
"playwright:codegen:local": "playwright codegen http://localhost:3301",
"playwright:codegen:local:auth": "yarn playwright:codegen:local --load-storage=tests/auth.json",
"husky:configure": "cd .. && husky install frontend/.husky && cd frontend && chmod ug+x .husky/*",
"commitlint": "commitlint --edit $1"
},
@@ -54,7 +56,9 @@
"d3-tip": "^0.9.1",
"dayjs": "^1.10.7",
"dotenv": "8.2.0",
"event-source-polyfill": "1.0.31",
"file-loader": "6.1.1",
"flat": "^5.0.2",
"history": "4.10.1",
"html-webpack-plugin": "5.1.0",
"i18next": "^21.6.12",
@@ -122,6 +126,8 @@
"@types/copy-webpack-plugin": "^8.0.1",
"@types/d3": "^6.2.0",
"@types/d3-tip": "^3.5.5",
"@types/event-source-polyfill": "^1.0.0",
"@types/flat": "^5.0.2",
"@types/jest": "^27.5.1",
"@types/lodash-es": "^4.17.4",
"@types/mini-css-extract-plugin": "^2.5.1",

View File

@@ -14,8 +14,8 @@ const config: PlaywrightTestConfig = {
baseURL: process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:3301',
},
updateSnapshots: 'all',
fullyParallel: false,
quiet: true,
fullyParallel: !!process.env.CI,
quiet: false,
testMatch: ['**/*.spec.ts'],
reporter: process.env.CI ? 'github' : 'list',
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,92 @@
{
"target_missing": "Please enter a threshold to proceed",
"rule_test_fired": "Test notification sent successfully",
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
"button_testrule": "Test Notification",
"label_channel_select": "Notification Channels",
"placeholder_channel_select": "select one or more channels",
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
"preview_chart_threshold_label": "Threshold",
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
"button_yes": "Yes",
"button_no": "No",
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
"remove_label_success": "Labels cleared",
"alert_form_step1": "Step 1 - Define the metric",
"alert_form_step2": "Step 2 - Define Alert Conditions",
"alert_form_step3": "Step 3 - Alert Configuration",
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
"confirm_save_title": "Save Changes",
"confirm_save_content_part1": "Your alert built with",
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
"rule_created": "Rule created successfully",
"rule_edited": "Rule edited successfully",
"expression_missing": "expression is missing in {{where}}",
"metricname_missing": "metric name is missing in {{where}}",
"condition_required": "at least one metric condition is required",
"alertname_required": "alert name is required",
"promql_required": "promql expression is required when query format is set to PromQL",
"button_savechanges": "Save Rule",
"button_createrule": "Create Rule",
"button_returntorules": "Return to rules",
"button_cancelchanges": "Cancel",
"button_discard": "Discard",
"text_condition1": "Send a notification when the metric is",
"text_condition2": "the threshold",
"text_condition3": "during the last",
"option_5min": "5 mins",
"option_10min": "10 mins",
"option_15min": "15 mins",
"option_60min": "60 mins",
"option_4hours": "4 hours",
"option_24hours": "24 hours",
"field_threshold": "Alert Threshold",
"option_allthetimes": "all the times",
"option_atleastonce": "at least once",
"option_onaverage": "on average",
"option_intotal": "in total",
"option_above": "above",
"option_below": "below",
"option_equal": "is equal to",
"option_notequal": "not equal to",
"button_query": "Query",
"button_formula": "Formula",
"tab_qb": "Query Builder",
"tab_promql": "PromQL",
"title_confirm": "Confirm",
"button_ok": "Yes",
"button_cancel": "No",
"field_promql_expr": "PromQL Expression",
"field_alert_name": "Alert Name",
"field_alert_desc": "Alert Description",
"field_labels": "Labels",
"field_severity": "Severity",
"option_critical": "Critical",
"option_error": "Error",
"option_warning": "Warning",
"option_info": "Info",
"user_guide_headline": "Steps to create an Alert",
"user_guide_qb_step1": "Step 1 - Define the metric",
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
"user_guide_qb_step2b": "Enter the Alert threshold",
"user_guide_qb_step3": "Step 3 -Alert Configuration",
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
"user_guide_pql_step1": "Step 1 - Define the metric",
"user_guide_pql_step1a": "Write a PromQL query for the metric",
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
"user_guide_pql_step2b": "Enter the Alert threshold",
"user_guide_pql_step3": "Step 3 -Alert Configuration",
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
"user_tooltip_more_help": "More details on how to create alerts"
}

View File

@@ -1,4 +1,14 @@
{
"channel_delete_unexp_error": "Something went wrong",
"channel_delete_success": "Channel Deleted Successfully",
"column_channel_name": "Name",
"column_channel_type": "Type",
"column_channel_action": "Action",
"column_channel_edit": "Edit",
"button_new_channel": "New Alert Channel",
"tooltip_notification_channels": "More details on how to setting notification channels",
"sending_channels_note": "The alerts will be sent to all the configured channels.",
"loading_channels_message": "Loading Channels..",
"page_title_create": "New Notification Channels",
"page_title_edit": "Edit Notification Channels",
"button_save_channel": "Save",

View File

@@ -33,6 +33,7 @@
"option_10min": "10 mins",
"option_15min": "15 mins",
"option_60min": "60 mins",
"option_4hours": "4 hours",
"option_24hours": "24 hours",
"field_threshold": "Alert Threshold",
"option_allthetimes": "all the times",
@@ -58,5 +59,27 @@
"option_critical": "Critical",
"option_error": "Error",
"option_warning": "Warning",
"option_info": "Info"
"option_info": "Info",
"user_guide_headline": "Steps to create an Alert",
"user_guide_qb_step1": "Step 1 - Define the metric",
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
"user_guide_qb_step2b": "Enter the Alert threshold",
"user_guide_qb_step3": "Step 3 -Alert Configuration",
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
"user_guide_pql_step1": "Step 1 - Define the metric",
"user_guide_pql_step1a": "Write a PromQL query for the metric",
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
"user_guide_pql_step2b": "Enter the Alert threshold",
"user_guide_pql_step3": "Step 3 -Alert Configuration",
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
"user_tooltip_more_help": "More details on how to create alerts"
}

View File

@@ -0,0 +1,92 @@
{
"target_missing": "Please enter a threshold to proceed",
"rule_test_fired": "Test notification sent successfully",
"no_alerts_found": "No alerts found during the evaluation. This happens when rule condition is unsatisfied. You may adjust the rule threshold and retry.",
"button_testrule": "Test Notification",
"label_channel_select": "Notification Channels",
"placeholder_channel_select": "select one or more channels",
"channel_select_tooltip": "Leave empty to send this alert on all the configured channels",
"preview_chart_unexpected_error": "An unexpeced error occurred updating the chart, please check your query.",
"preview_chart_threshold_label": "Threshold",
"placeholder_label_key_pair": "Click here to enter a label (key value pairs)",
"button_yes": "Yes",
"button_no": "No",
"remove_label_confirm": "This action will remove all the labels. Do you want to proceed?",
"remove_label_success": "Labels cleared",
"alert_form_step1": "Step 1 - Define the metric",
"alert_form_step2": "Step 2 - Define Alert Conditions",
"alert_form_step3": "Step 3 - Alert Configuration",
"metric_query_max_limit": "Can not create query. You can create maximum of 5 queries",
"confirm_save_title": "Save Changes",
"confirm_save_content_part1": "Your alert built with",
"confirm_save_content_part2": "query will be saved. Press OK to confirm.",
"unexpected_error": "Sorry, an unexpected error occurred. Please contact your admin",
"rule_created": "Rule created successfully",
"rule_edited": "Rule edited successfully",
"expression_missing": "expression is missing in {{where}}",
"metricname_missing": "metric name is missing in {{where}}",
"condition_required": "at least one metric condition is required",
"alertname_required": "alert name is required",
"promql_required": "promql expression is required when query format is set to PromQL",
"button_savechanges": "Save Rule",
"button_createrule": "Create Rule",
"button_returntorules": "Return to rules",
"button_cancelchanges": "Cancel",
"button_discard": "Discard",
"text_condition1": "Send a notification when the metric is",
"text_condition2": "the threshold",
"text_condition3": "during the last",
"option_5min": "5 mins",
"option_10min": "10 mins",
"option_15min": "15 mins",
"option_60min": "60 mins",
"option_4hours": "4 hours",
"option_24hours": "24 hours",
"field_threshold": "Alert Threshold",
"option_allthetimes": "all the times",
"option_atleastonce": "at least once",
"option_onaverage": "on average",
"option_intotal": "in total",
"option_above": "above",
"option_below": "below",
"option_equal": "is equal to",
"option_notequal": "not equal to",
"button_query": "Query",
"button_formula": "Formula",
"tab_qb": "Query Builder",
"tab_promql": "PromQL",
"title_confirm": "Confirm",
"button_ok": "Yes",
"button_cancel": "No",
"field_promql_expr": "PromQL Expression",
"field_alert_name": "Alert Name",
"field_alert_desc": "Alert Description",
"field_labels": "Labels",
"field_severity": "Severity",
"option_critical": "Critical",
"option_error": "Error",
"option_warning": "Warning",
"option_info": "Info",
"user_guide_headline": "Steps to create an Alert",
"user_guide_qb_step1": "Step 1 - Define the metric",
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
"user_guide_qb_step2b": "Enter the Alert threshold",
"user_guide_qb_step3": "Step 3 -Alert Configuration",
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
"user_guide_pql_step1": "Step 1 - Define the metric",
"user_guide_pql_step1a": "Write a PromQL query for the metric",
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
"user_guide_pql_step2b": "Enter the Alert threshold",
"user_guide_pql_step3": "Step 3 -Alert Configuration",
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
"user_tooltip_more_help": "More details on how to create alerts"
}

View File

@@ -1,4 +1,14 @@
{
"channel_delete_unexp_error": "Something went wrong",
"channel_delete_success": "Channel Deleted Successfully",
"column_channel_name": "Name",
"column_channel_type": "Type",
"column_channel_action": "Action",
"column_channel_edit": "Edit",
"button_new_channel": "New Alert Channel",
"tooltip_notification_channels": "More details on how to setting notification channels",
"sending_channels_note": "The alerts will be sent to all the configured channels.",
"loading_channels_message": "Loading Channels..",
"page_title_create": "New Notification Channels",
"page_title_edit": "Edit Notification Channels",
"button_save_channel": "Save",

View File

@@ -33,6 +33,7 @@
"option_10min": "10 mins",
"option_15min": "15 mins",
"option_60min": "60 mins",
"option_4hours": "4 hours",
"option_24hours": "24 hours",
"field_threshold": "Alert Threshold",
"option_allthetimes": "all the times",
@@ -58,5 +59,27 @@
"option_critical": "Critical",
"option_error": "Error",
"option_warning": "Warning",
"option_info": "Info"
"option_info": "Info",
"user_guide_headline": "Steps to create an Alert",
"user_guide_qb_step1": "Step 1 - Define the metric",
"user_guide_qb_step1a": "Choose a metric which you want to create an alert on",
"user_guide_qb_step1b": "Filter it based on WHERE field or GROUPBY if needed",
"user_guide_qb_step1c": "Apply an aggregatiion function like COUNT, SUM, etc. or choose NOOP to plot the raw metric",
"user_guide_qb_step1d": "Create a formula based on Queries if needed",
"user_guide_qb_step2": "Step 2 - Define Alert Conditions",
"user_guide_qb_step2a": "Select the evaluation interval, threshold type and whether you want to alert above/below a value",
"user_guide_qb_step2b": "Enter the Alert threshold",
"user_guide_qb_step3": "Step 3 -Alert Configuration",
"user_guide_qb_step3a": "Set alert severity, name and descriptions",
"user_guide_qb_step3b": "Add tags to the alert in the Label field if needed",
"user_guide_pql_step1": "Step 1 - Define the metric",
"user_guide_pql_step1a": "Write a PromQL query for the metric",
"user_guide_pql_step1b": "Format the legends based on labels you want to highlight",
"user_guide_pql_step2": "Step 2 - Define Alert Conditions",
"user_guide_pql_step2a": "Select the threshold type and whether you want to alert above/below a value",
"user_guide_pql_step2b": "Enter the Alert threshold",
"user_guide_pql_step3": "Step 3 -Alert Configuration",
"user_guide_pql_step3a": "Set alert severity, name and descriptions",
"user_guide_pql_step3b": "Add tags to the alert in the Label field if needed",
"user_tooltip_more_help": "More details on how to create alerts"
}

View File

@@ -35,11 +35,8 @@ export const SettingsPage = Loadable(
() => import(/* webpackChunkName: "SettingsPage" */ 'pages/Settings'),
);
export const InstrumentationPage = Loadable(
() =>
import(
/* webpackChunkName: "InstrumentationPage" */ 'pages/AddInstrumentation'
),
export const GettingStarted = Loadable(
() => import(/* webpackChunkName: "GettingStarted" */ 'pages/GettingStarted'),
);
export const DashboardPage = Loadable(
@@ -100,6 +97,10 @@ export const MySettings = Loadable(
() => import(/* webpackChunkName: "All MySettings" */ 'pages/MySettings'),
);
export const Logs = Loadable(
() => import(/* webpackChunkName: "Logs" */ 'pages/Logs'),
);
export const Login = Loadable(
() => import(/* webpackChunkName: "Login" */ 'pages/Login'),
);

View File

@@ -11,9 +11,10 @@ import {
EditAlertChannelsAlerts,
EditRulesPage,
ErrorDetails,
InstrumentationPage,
GettingStarted,
ListAllALertsPage,
Login,
Logs,
MySettings,
NewDashboardPage,
OrganizationSettings,
@@ -84,7 +85,7 @@ const routes: AppRoutes[] = [
{
path: ROUTES.INSTRUMENTATION,
exact: true,
component: InstrumentationPage,
component: GettingStarted,
isPrivate: true,
key: 'INSTRUMENTATION',
},
@@ -193,6 +194,13 @@ const routes: AppRoutes[] = [
isPrivate: true,
key: 'MY_SETTINGS',
},
{
path: ROUTES.LOGS,
exact: true,
component: Logs,
key: 'LOGS',
isPrivate: true,
},
{
path: ROUTES.LOGIN,
exact: true,

View File

@@ -3,6 +3,8 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
import cacheBursting from '../../i18n-translations-hash.json';
i18n
// load translation using http -> see /public/locales
.use(Backend)
@@ -17,7 +19,14 @@ i18n
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
backend: {
loadPath: (language, namespace) => {
const ns = namespace[0];
const pathkey = `/${language}/${ns}`;
const hash = cacheBursting[pathkey as keyof typeof cacheBursting] || '';
return `/locales/${language}/${namespace}.json?h=${hash}`;
},
},
react: {
useSuspense: false,
},

View File

@@ -0,0 +1,26 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/alerts/patch';
const patch = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.patch(`/rules/${props.id}`, {
...props.data,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default patch;

View File

@@ -0,0 +1,26 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/alerts/testAlert';
const testAlert = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post('/testRule', {
...props.data,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default testAlert;

View File

@@ -0,0 +1,26 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/dashboard/variables/query';
const query = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.get(
`/variables/query?query=${encodeURIComponent(props.query)}`,
);
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default query;

View File

@@ -0,0 +1,23 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/logs/addToSelectedFields';
const AddToSelectedFields = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const data = await axios.post(`/logs/fields`, props);
return {
statusCode: 200,
error: null,
message: '',
payload: data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default AddToSelectedFields;

View File

@@ -0,0 +1,26 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/logs/getLogs';
const GetLogs = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const data = await axios.get(`/logs`, {
params: props,
});
return {
statusCode: 200,
error: null,
message: '',
payload: data.data.results,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default GetLogs;

View File

@@ -0,0 +1,26 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/logs/getLogsAggregate';
const GetLogsAggregate = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const data = await axios.get(`/logs/aggregate`, {
params: props,
});
return {
statusCode: 200,
error: null,
message: '',
payload: data.data.items,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default GetLogsAggregate;

View File

@@ -0,0 +1,24 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps } from 'types/api/logs/getSearchFields';
const GetSearchFields = async (): Promise<
SuccessResponse<PayloadProps> | ErrorResponse
> => {
try {
const data = await axios.get(`/logs/fields`);
return {
statusCode: 200,
error: null,
message: '',
payload: data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default GetSearchFields;

View File

@@ -0,0 +1,23 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/logs/addToSelectedFields';
const RemoveSelectedField = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const data = await axios.post(`/logs/fields`, props);
return {
statusCode: 200,
error: null,
message: '',
payload: data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default RemoveSelectedField;

View File

@@ -0,0 +1,17 @@
import apiV1 from 'api/apiV1';
import getLocalStorageKey from 'api/browser/localstorage/get';
import { ENVIRONMENT } from 'constants/env';
import { LOCALSTORAGE } from 'constants/localStorage';
import { EventSourcePolyfill } from 'event-source-polyfill';
export const LiveTail = (queryParams: string): EventSourcePolyfill => {
const dict = {
headers: {
Authorization: `Bearer ${getLocalStorageKey(LOCALSTORAGE.AUTH_TOKEN)}`,
},
};
return new EventSourcePolyfill(
`${ENVIRONMENT.baseURL}${apiV1}logs/tail?${queryParams}`,
dict,
);
};

View File

@@ -0,0 +1,24 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/metrics/getTopLevelOperations';
const getTopLevelOperations = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post(`/service/top_level_operations`);
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data[props.service],
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default getTopLevelOperations;

View File

@@ -2,13 +2,13 @@ import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/metrics/getTopEndPoints';
import { PayloadProps, Props } from 'types/api/metrics/getTopOperations';
const getTopEndPoints = async (
const getTopOperations = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post(`/service/top_endpoints`, {
const response = await axios.post(`/service/top_operations`, {
start: `${props.start}`,
end: `${props.end}`,
service: props.service,
@@ -26,4 +26,4 @@ const getTopEndPoints = async (
}
};
export default getTopEndPoints;
export default getTopOperations;

View File

@@ -65,6 +65,7 @@ function Graph({
yAxisUnit = 'short',
forceReRender,
staticLine,
containerHeight,
}: GraphProps): JSX.Element {
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const chartRef = useRef<HTMLCanvasElement>(null);
@@ -241,7 +242,7 @@ function Graph({
}, [buildChart, forceReRender]);
return (
<div style={{ height: '85%' }}>
<div style={{ height: containerHeight }}>
<canvas ref={chartRef} />
<LegendsContainer id={name} />
</div>
@@ -259,6 +260,7 @@ interface GraphProps {
yAxisUnit?: string;
forceReRender?: boolean | null | number;
staticLine?: StaticLineProps | undefined;
containerHeight?: string | number;
}
export interface StaticLineProps {
@@ -285,5 +287,6 @@ Graph.defaultProps = {
yAxisUnit: undefined,
forceReRender: undefined,
staticLine: undefined,
containerHeight: '85%',
};
export default Graph;

View File

@@ -0,0 +1,147 @@
import { Button, Popover } from 'antd';
import getStep from 'lib/getStep';
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
import React, { memo, useCallback, useMemo } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { getLogs } from 'store/actions/logs/getLogs';
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { SET_SEARCH_QUERY_STRING, TOGGLE_LIVE_TAIL } from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime';
import { ILogsReducer } from 'types/reducer/logs';
interface AddToQueryHOCProps {
fieldKey: string;
fieldValue: string;
children: React.ReactNode;
getLogs: (props: Parameters<typeof getLogs>[0]) => ReturnType<typeof getLogs>;
getLogsAggregate: (
props: Parameters<typeof getLogsAggregate>[0],
) => ReturnType<typeof getLogsAggregate>;
}
function AddToQueryHOC({
fieldKey,
fieldValue,
children,
getLogs,
getLogsAggregate,
}: AddToQueryHOCProps): JSX.Element {
const {
searchFilter: { queryString },
logLinesPerPage,
idStart,
idEnd,
liveTail,
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
const dispatch = useDispatch();
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const generatedQuery = useMemo(
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
[fieldKey, fieldValue],
);
const handleQueryAdd = useCallback(() => {
let updatedQueryString = queryString || '';
if (updatedQueryString.length === 0) {
updatedQueryString += `${generatedQuery}`;
} else {
updatedQueryString += ` AND ${generatedQuery}`;
}
dispatch({
type: SET_SEARCH_QUERY_STRING,
payload: updatedQueryString,
});
if (liveTail === 'STOPPED') {
getLogs({
q: updatedQueryString,
limit: logLinesPerPage,
orderBy: 'timestamp',
order: 'desc',
timestampStart: minTime,
timestampEnd: maxTime,
...(idStart ? { idGt: idStart } : {}),
...(idEnd ? { idLt: idEnd } : {}),
});
getLogsAggregate({
timestampStart: minTime,
timestampEnd: maxTime,
step: getStep({
start: minTime,
end: maxTime,
inputFormat: 'ns',
}),
q: updatedQueryString,
...(idStart ? { idGt: idStart } : {}),
...(idEnd ? { idLt: idEnd } : {}),
});
} else if (liveTail === 'PLAYING') {
dispatch({
type: TOGGLE_LIVE_TAIL,
payload: 'PAUSED',
});
setTimeout(
() =>
dispatch({
type: TOGGLE_LIVE_TAIL,
payload: liveTail,
}),
0,
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
dispatch,
generatedQuery,
getLogs,
idEnd,
idStart,
logLinesPerPage,
maxTime,
minTime,
queryString,
]);
const popOverContent = (
<span style={{ fontSize: '0.9rem' }}>Add to query: {fieldKey}</span>
);
return (
<Button
size="small"
type="text"
style={{
margin: 0,
padding: 0,
}}
onClick={handleQueryAdd}
>
<Popover placement="top" content={popOverContent}>
{children}
</Popover>
</Button>
);
}
interface DispatchProps {
getLogs: (
props: Parameters<typeof getLogs>[0],
) => (dispatch: Dispatch<AppActions>) => void;
getLogsAggregate: (
props: Parameters<typeof getLogsAggregate>[0],
) => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
): DispatchProps => ({
getLogs: bindActionCreators(getLogs, dispatch),
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
});
export default connect(null, mapDispatchToProps)(memo(AddToQueryHOC));

View File

@@ -0,0 +1,12 @@
import React from 'react';
import { CategoryHeadingText } from './styles';
interface ICategoryHeadingProps {
children: React.ReactNode;
}
function CategoryHeading({ children }: ICategoryHeadingProps): JSX.Element {
return <CategoryHeadingText type="secondary">{children}</CategoryHeadingText>;
}
export default CategoryHeading;

View File

@@ -0,0 +1,6 @@
import { Typography } from 'antd';
import styled from 'styled-components';
export const CategoryHeadingText = styled(Typography.Text)`
font-size: 0.8rem;
`;

View File

@@ -0,0 +1,37 @@
import { Popover } from 'antd';
import React from 'react';
import { useCopyToClipboard } from 'react-use';
interface CopyClipboardHOCProps {
textToCopy: string;
children: React.ReactNode;
}
function CopyClipboardHOC({
textToCopy,
children,
}: CopyClipboardHOCProps): JSX.Element {
const [, setCopy] = useCopyToClipboard();
return (
<span
style={{
margin: 0,
padding: 0,
cursor: 'pointer',
}}
onClick={(): void => setCopy(textToCopy)}
onKeyDown={(): void => setCopy(textToCopy)}
role="button"
tabIndex={0}
>
<Popover
placement="top"
content={<span style={{ fontSize: '0.9rem' }}>Copy to clipboard</span>}
>
{children}
</Popover>
</span>
);
}
export default CopyClipboardHOC;

View File

@@ -0,0 +1,157 @@
import { blue, grey, orange } from '@ant-design/colors';
import { CopyFilled, ExpandAltOutlined } from '@ant-design/icons';
import { Button, Divider, Row, Typography } from 'antd';
import { map } from 'd3';
import dayjs from 'dayjs';
import { FlatLogData } from 'lib/logs/flatLogData';
import React, { useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useCopyToClipboard } from 'react-use';
import { AppState } from 'store/reducers';
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
import { ILog } from 'types/api/logs/log';
import { ILogsReducer } from 'types/reducer/logs';
import AddToQueryHOC from '../AddToQueryHOC';
import CopyClipboardHOC from '../CopyClipboardHOC';
import { Container } from './styles';
import { isValidLogField } from './util';
interface LogFieldProps {
fieldKey: string;
fieldValue: string;
}
function LogGeneralField({ fieldKey, fieldValue }: LogFieldProps): JSX.Element {
return (
<div
style={{
display: 'flex',
overflow: 'hidden',
width: '100%',
}}
>
<Typography.Text type="secondary">{fieldKey}</Typography.Text>
<CopyClipboardHOC textToCopy={fieldValue}>
<Typography.Text ellipsis>
{': '}
{fieldValue}
</Typography.Text>
</CopyClipboardHOC>
</div>
);
}
function LogSelectedField({
fieldKey = '',
fieldValue = '',
}: LogFieldProps): JSX.Element {
return (
<div
style={{
display: 'flex',
overflow: 'hidden',
width: '100%',
}}
>
<AddToQueryHOC fieldKey={fieldKey} fieldValue={fieldValue}>
<Typography.Text>
{`"`}
<span style={{ color: blue[4] }}>{fieldKey}</span>
{`"`}
</Typography.Text>
</AddToQueryHOC>
<CopyClipboardHOC textToCopy={fieldValue}>
<Typography.Text ellipsis>
<span>
{': '}
{typeof fieldValue === 'string' && `"`}
</span>
<span style={{ color: orange[6] }}>{fieldValue}</span>
{typeof fieldValue === 'string' && `"`}
</Typography.Text>
</CopyClipboardHOC>
</div>
);
}
interface LogItemProps {
logData: ILog;
}
function LogItem({ logData }: LogItemProps): JSX.Element {
const {
fields: { selected },
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
const dispatch = useDispatch();
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
const [, setCopy] = useCopyToClipboard();
const handleDetailedView = useCallback(() => {
dispatch({
type: SET_DETAILED_LOG_DATA,
payload: logData,
});
}, [dispatch, logData]);
const handleCopyJSON = (): void => {
setCopy(JSON.stringify(logData, null, 2));
};
return (
<Container>
<div style={{ maxWidth: '100%' }}>
<div>
{'{'}
<div style={{ marginLeft: '0.5rem' }}>
<LogGeneralField
fieldKey="log"
fieldValue={flattenLogData.body as never}
/>
{flattenLogData.stream && (
<LogGeneralField
fieldKey="stream"
fieldValue={flattenLogData.stream as never}
/>
)}
<LogGeneralField
fieldKey="timestamp"
fieldValue={dayjs((flattenLogData.timestamp as never) / 1e6).format()}
/>
</div>
{'}'}
</div>
<div>
{map(selected, (field) => {
return isValidLogField(flattenLogData[field.name] as never) ? (
<LogSelectedField
key={field.name}
fieldKey={field.name}
fieldValue={flattenLogData[field.name] as never}
/>
) : null;
})}
</div>
</div>
<Divider style={{ padding: 0, margin: '0.4rem 0', opacity: 0.5 }} />
<Row>
<Button
size="small"
type="text"
onClick={handleDetailedView}
style={{ color: blue[5], padding: 0, margin: 0 }}
>
{' '}
<ExpandAltOutlined /> View Details
</Button>
<Button
size="small"
type="text"
onClick={handleCopyJSON}
style={{ padding: 0, margin: 0, color: grey[1] }}
>
{' '}
<CopyFilled /> Copy JSON
</Button>
</Row>
</Container>
);
}
export default LogItem;

View File

@@ -0,0 +1,18 @@
import { Card } from 'antd';
import styled, { keyframes } from 'styled-components';
const fadeInAnimation = keyframes`
0% { opacity: 0; }
100% { opacity: 1;}
`;
export const Container = styled(Card)`
width: 100% !important;
margin-bottom: 0.3rem;
.ant-card-body {
padding: 0.3rem 0.6rem;
}
animation-name: ${fadeInAnimation};
animation-duration: 0.2s;
animation-timing-function: ease-in;
`;

View File

@@ -0,0 +1 @@
export const isValidLogField = (value: never): boolean => value !== undefined;

View File

@@ -30,4 +30,5 @@ export const QueryBuilderQueryTemplate = {
export const QueryBuilderFormulaTemplate = {
expression: '',
disabled: false,
legend: '',
};

View File

@@ -6,7 +6,7 @@ const ROUTES = {
TRACE: '/trace',
TRACE_DETAIL: '/trace/:id',
SETTINGS: '/settings',
INSTRUMENTATION: '/add-instrumentation',
INSTRUMENTATION: '/get-started',
USAGE_EXPLORER: '/usage-explorer',
APPLICATION: '/application',
ALL_DASHBOARD: '/dashboard',
@@ -26,6 +26,7 @@ const ROUTES = {
SOMETHING_WENT_WRONG: '/something-went-wrong',
UN_AUTHORIZED: '/un-authorized',
NOT_FOUND: '/not-found',
LOGS: '/logs',
HOME_PAGE: '/',
PASSWORD_RESET: '/password-reset',
};

View File

@@ -5,6 +5,7 @@ import ROUTES from 'constants/routes';
import useComponentPermission from 'hooks/useComponentPermission';
import history from 'lib/history';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { generatePath } from 'react-router-dom';
import { AppState } from 'store/reducers';
@@ -14,6 +15,7 @@ import AppReducer from 'types/reducer/app';
import Delete from './Delete';
function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element {
const { t } = useTranslation(['channels']);
const [notifications, Element] = notification.useNotification();
const [channels, setChannels] = useState<Channels[]>(allChannels);
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
@@ -29,12 +31,12 @@ function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element {
const columns: ColumnsType<Channels> = [
{
title: 'Name',
title: t('column_channel_name'),
dataIndex: 'name',
key: 'name',
},
{
title: 'Type',
title: t('column_channel_type'),
dataIndex: 'type',
key: 'type',
},
@@ -42,14 +44,14 @@ function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element {
if (action) {
columns.push({
title: 'Action',
title: t('column_channel_action'),
dataIndex: 'id',
key: 'action',
align: 'center',
render: (id: string): JSX.Element => (
<>
<Button onClick={(): void => onClickEditHandler(id)} type="link">
Edit
{t('column_channel_edit')}
</Button>
<Delete id={id} setChannels={setChannels} notifications={notifications} />
</>

View File

@@ -1,29 +1,31 @@
import { Button } from 'antd';
import { NotificationInstance } from 'antd/lib/notification';
import deleteAlert from 'api/channels/delete';
import deleteChannel from 'api/channels/delete';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Channels } from 'types/api/channels/getAll';
function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element {
const { t } = useTranslation(['channels']);
const [loading, setLoading] = useState(false);
const onClickHandler = async (): Promise<void> => {
try {
setLoading(true);
const response = await deleteAlert({
const response = await deleteChannel({
id,
});
if (response.statusCode === 200) {
notifications.success({
message: 'Success',
description: 'Channel Deleted Successfully',
description: t('channel_delete_success'),
});
setChannels((preChannels) => preChannels.filter((e) => e.id !== id));
} else {
notifications.error({
message: 'Error',
description: response.error || 'Something went wrong',
description: response.error || t('channel_delete_unexp_error'),
});
}
setLoading(false);
@@ -31,7 +33,9 @@ function Delete({ notifications, setChannels, id }: DeleteProps): JSX.Element {
notifications.error({
message: 'Error',
description:
error instanceof Error ? error.toString() : 'Something went wrong',
error instanceof Error
? error.toString()
: t('channel_delete_unexp_error'),
});
setLoading(false);
}

View File

@@ -8,16 +8,18 @@ import useComponentPermission from 'hooks/useComponentPermission';
import useFetch from 'hooks/useFetch';
import history from 'lib/history';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
import AlertChannelsComponent from './AlertChannels';
import { Button, ButtonContainer } from './styles';
import { Button, ButtonContainer, RightActionContainer } from './styles';
const { Paragraph } = Typography;
function AlertChannels(): JSX.Element {
const { t } = useTranslation(['channels']);
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
const [addNewChannelPermission] = useComponentPermission(
['add_new_channel'],
@@ -34,28 +36,28 @@ function AlertChannels(): JSX.Element {
}
if (loading || payload === undefined) {
return <Spinner tip="Loading Channels.." height="90vh" />;
return <Spinner tip={t('loading_channels_message')} height="90vh" />;
}
return (
<>
<ButtonContainer>
<Paragraph ellipsis type="secondary">
The latest added channel is used as the default channel for sending alerts
{t('sending_channels_note')}
</Paragraph>
<div>
<RightActionContainer>
<TextToolTip
text="More details on how to setting notification channels"
text={t('tooltip_notification_channels')}
url="https://signoz.io/docs/userguide/alerts-management/#setting-notification-channel"
/>
{addNewChannelPermission && (
<Button onClick={onToggleHandler} icon={<PlusOutlined />}>
New Alert Channel
{t('button_new_channel')}
</Button>
)}
</div>
</RightActionContainer>
</ButtonContainer>
<AlertChannelsComponent allChannels={payload} />

View File

@@ -1,6 +1,13 @@
import { Button as ButtonComponent } from 'antd';
import styled from 'styled-components';
export const RightActionContainer = styled.div`
&&& {
display: flex;
align-items: center;
}
`;
export const ButtonContainer = styled.div`
&&& {
display: flex;

View File

@@ -101,9 +101,7 @@ function AllErrors(): JSX.Element {
render: (value, record): JSX.Element => (
<Tooltip overlay={(): JSX.Element => value}>
<Link
to={`${ROUTES.ERROR_DETAIL}?serviceName=${
record.serviceName
}&exceptionType=${record.exceptionType}&groupId=${
to={`${ROUTES.ERROR_DETAIL}?groupId=${
record.groupID
}&timestamp=${getNanoSeconds(record.lastSeen)}`}
>
@@ -188,14 +186,14 @@ function AllErrors(): JSX.Element {
sorter,
) => {
if (!Array.isArray(sorter)) {
const { current = 0, pageSize = 0 } = paginations;
const { pageSize = 0, current = 0 } = paginations;
const { columnKey = '', order } = sorter;
const updatedOrder = order === 'ascend' ? 'ascending' : 'descending';
history.replace(
`${pathname}?${createQueryParams({
order: updatedOrder,
offset: current - 1,
offset: (current - 1) * pageSize,
orderParam: columnKey,
pageSize,
})}`,
@@ -213,7 +211,7 @@ function AllErrors(): JSX.Element {
pagination={{
pageSize: getUpdatedPageSize,
responsive: true,
current: getUpdatedOffset + 1,
current: getUpdatedOffset / 10 + 1,
position: ['bottomLeft'],
total: errorCountResponse.data?.payload || 0,
}}

View File

@@ -1,4 +1,15 @@
import { isOrder, isOrderParams } from './utils';
import { Order, OrderBy } from 'types/api/errors/getAll';
import {
getDefaultOrder,
getLimit,
getOffSet,
getOrder,
getOrderParams,
getUpdatePageSize,
isOrder,
isOrderParams,
} from './utils';
describe('Error utils', () => {
test('Valid OrderBy Params', () => {
@@ -25,4 +36,74 @@ describe('Error utils', () => {
expect(isOrder(null)).toBe(false);
expect(isOrder('')).toBe(false);
});
test('Default Order', () => {
const OrderBy: OrderBy[] = [
'exceptionCount',
'exceptionType',
'firstSeen',
'lastSeen',
'serviceName',
];
const order: Order[] = ['ascending', 'descending'];
const ascOrd = order[0];
const desOrd = order[1];
OrderBy.forEach((order) => {
expect(getDefaultOrder(order, ascOrd, order)).toBe('ascend');
expect(getDefaultOrder(order, desOrd, order)).toBe('descend');
});
});
test('Limit', () => {
expect(getLimit(null)).toBe(10);
expect(getLimit('')).toBe(10);
expect(getLimit('0')).toBe(0);
expect(getLimit('1')).toBe(1);
expect(getLimit('10')).toBe(10);
expect(getLimit('11')).toBe(11);
expect(getLimit('100')).toBe(100);
expect(getLimit('101')).toBe(101);
});
test('Update Page Size', () => {
expect(getUpdatePageSize(null)).toBe(10);
expect(getUpdatePageSize('')).toBe(10);
expect(getUpdatePageSize('0')).toBe(0);
expect(getUpdatePageSize('1')).toBe(1);
expect(getUpdatePageSize('10')).toBe(10);
expect(getUpdatePageSize('11')).toBe(11);
expect(getUpdatePageSize('100')).toBe(100);
expect(getUpdatePageSize('101')).toBe(101);
});
test('Order Params', () => {
expect(getOrderParams(null)).toBe('serviceName');
expect(getOrderParams('')).toBe('serviceName');
expect(getOrderParams('serviceName')).toBe('serviceName');
expect(getOrderParams('exceptionCount')).toBe('exceptionCount');
expect(getOrderParams('lastSeen')).toBe('lastSeen');
expect(getOrderParams('firstSeen')).toBe('firstSeen');
expect(getOrderParams('exceptionType')).toBe('exceptionType');
});
test('OffSet', () => {
expect(getOffSet(null)).toBe(0);
expect(getOffSet('')).toBe(0);
expect(getOffSet('0')).toBe(0);
expect(getOffSet('1')).toBe(1);
expect(getOffSet('10')).toBe(10);
expect(getOffSet('11')).toBe(11);
expect(getOffSet('100')).toBe(100);
expect(getOffSet('101')).toBe(101);
});
test('Order', () => {
expect(getOrder(null)).toBe('ascending');
expect(getOrder('')).toBe('ascending');
expect(getOrder('ascending')).toBe('ascending');
expect(getOrder('descending')).toBe('descending');
});
});

View File

@@ -74,10 +74,10 @@ export const getDefaultOrder = (
return undefined;
};
export const getNanoSeconds = (date: string): number => {
export const getNanoSeconds = (date: string): string => {
return (
parseInt((new Date(date).getTime() / 1e3).toString(), 10) * 1e9 +
Timestamp.fromString(date).getNano()
Math.floor(new Date(date).getTime() / 1e3).toString() +
Timestamp.fromString(date).getNano().toString()
);
};

View File

@@ -3,7 +3,7 @@ import styled from 'styled-components';
export const Layout = styled(LayoutComponent)`
&&& {
min-height: 91vh;
min-height: 92vh;
display: flex;
position: relative;
}

View File

@@ -40,7 +40,7 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
getNextPrevId({
errorID: errorId || idPayload.errorId,
groupID: idPayload.groupID,
timestamp: timestamp || getNanoSeconds(idPayload.timestamp).toString(),
timestamp: timestamp || getNanoSeconds(idPayload.timestamp),
}),
},
);
@@ -79,7 +79,7 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
const onClickErrorIdHandler = async (
id: string,
timespamp: string,
timestamp: string,
): Promise<void> => {
try {
if (id.length === 0) {
@@ -90,11 +90,9 @@ function ErrorDetails(props: ErrorDetailsProps): JSX.Element {
}
history.replace(
`${history.location.pathname}?${urlKey.serviceName}=${serviceName}&${
urlKey.exceptionType
}=${errorType}&groupId=${idPayload.groupID}&timestamp=${getNanoSeconds(
timespamp,
)}&errorId=${id}`,
`${history.location.pathname}?&groupId=${
idPayload.groupID
}&timestamp=${getNanoSeconds(timestamp)}&errorId=${id}`,
);
} catch (error) {
notification.error({

View File

@@ -4,9 +4,12 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { AlertDef, Labels } from 'types/api/alerts/def';
import ChannelSelect from './ChannelSelect';
import LabelSelect from './labels';
import {
ChannelSelectTip,
FormContainer,
FormItemMedium,
InputSmall,
SeveritySelect,
StepHeading,
@@ -22,7 +25,7 @@ interface BasicInfoProps {
function BasicInfo({ alertDef, setAlertDef }: BasicInfoProps): JSX.Element {
// init namespace for translations
const { t } = useTranslation('rules');
const { t } = useTranslation('alerts');
return (
<>
@@ -80,7 +83,7 @@ function BasicInfo({ alertDef, setAlertDef }: BasicInfoProps): JSX.Element {
}}
/>
</FormItem>
<FormItem label={t('field_labels')}>
<FormItemMedium label={t('field_labels')}>
<LabelSelect
onSetLabels={(l: Labels): void => {
setAlertDef({
@@ -92,7 +95,19 @@ function BasicInfo({ alertDef, setAlertDef }: BasicInfoProps): JSX.Element {
}}
initialValues={alertDef.labels}
/>
</FormItem>
</FormItemMedium>
<FormItemMedium label="Notification Channels">
<ChannelSelect
currentValue={alertDef.preferredChannels}
onSelectChannels={(s: string[]): void => {
setAlertDef({
...alertDef,
preferredChannels: s,
});
}}
/>
<ChannelSelectTip> {t('channel_select_tooltip')}</ChannelSelectTip>
</FormItemMedium>
</FormContainer>
</>
);

View File

@@ -0,0 +1,70 @@
import { notification, Select } from 'antd';
import getChannels from 'api/channels/getAll';
import useFetch from 'hooks/useFetch';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { StyledSelect } from './styles';
export interface ChannelSelectProps {
currentValue?: string[];
onSelectChannels: (s: string[]) => void;
}
function ChannelSelect({
currentValue,
onSelectChannels,
}: ChannelSelectProps): JSX.Element | null {
// init namespace for translations
const { t } = useTranslation('alerts');
const { loading, payload, error, errorMessage } = useFetch(getChannels);
const handleChange = (value: string[]): void => {
onSelectChannels(value);
};
if (error && errorMessage !== '') {
notification.error({
message: 'Error',
description: errorMessage,
});
}
const renderOptions = (): React.ReactNode[] => {
const children: React.ReactNode[] = [];
if (loading || payload === undefined || payload.length === 0) {
return children;
}
payload.forEach((o) => {
children.push(
<Select.Option key={o.id} value={o.name}>
{o.name}
</Select.Option>,
);
});
return children;
};
return (
<StyledSelect
status={error ? 'error' : ''}
mode="multiple"
style={{ width: '100%' }}
placeholder={t('placeholder_channel_select')}
value={currentValue}
onChange={(value): void => {
handleChange(value as string[]);
}}
optionLabelProp="label"
>
{renderOptions()}
</StyledSelect>
);
}
ChannelSelect.defaultProps = {
currentValue: [],
};
export default ChannelSelect;

View File

@@ -0,0 +1,6 @@
import { Select } from 'antd';
import styled from 'styled-components';
export const StyledSelect = styled(Select)`
border-radius: 4px;
`;

View File

@@ -21,7 +21,7 @@ export interface ChartPreviewProps {
selectedTime?: timePreferenceType;
selectedInterval?: Time;
headline?: JSX.Element;
threshold?: number;
threshold?: number | undefined;
}
function ChartPreview({
@@ -33,9 +33,9 @@ function ChartPreview({
headline,
threshold,
}: ChartPreviewProps): JSX.Element | null {
const { t } = useTranslation('rules');
const { t } = useTranslation('alerts');
const staticLine: StaticLineProps | undefined =
threshold && threshold > 0
threshold !== undefined
? {
yMin: threshold,
yMax: threshold,
@@ -66,8 +66,12 @@ function ChartPreview({
}),
enabled:
query != null &&
(query.queryType !== EQueryType.PROM ||
(query.promQL?.length > 0 && query.promQL[0].query !== '')),
((query.queryType === EQueryType.PROM &&
query.promQL?.length > 0 &&
query.promQL[0].query !== '') ||
(query.queryType === EQueryType.QUERY_BUILDER &&
query.metricsBuilder?.queryBuilder?.length > 0 &&
query.metricsBuilder?.queryBuilder[0].metricName !== '')),
});
const chartDataSet = queryResponse.isError
@@ -113,7 +117,7 @@ ChartPreview.defaultProps = {
selectedTime: 'GLOBAL_TIME',
selectedInterval: '5min',
headline: undefined,
threshold: 0,
threshold: undefined,
};
export default ChartPreview;

View File

@@ -31,7 +31,7 @@ function QuerySection({
setPromQueries,
}: QuerySectionProps): JSX.Element {
// init namespace for translations
const { t } = useTranslation('rules');
const { t } = useTranslation('alerts');
const handleQueryCategoryChange = (s: string): void => {
if (
@@ -68,15 +68,20 @@ function QuerySection({
const handleFormulaChange = ({
formulaIndex,
expression,
legend,
toggleDisable,
toggleDelete,
}: IQueryBuilderFormulaHandleChange): void => {
const allFormulas = formulaQueries;
const current = allFormulas[formulaIndex];
if (expression) {
if (expression !== undefined) {
current.expression = expression;
}
if (legend !== undefined) {
current.legend = legend;
}
if (toggleDisable) {
current.disabled = !current.disabled;
}
@@ -179,6 +184,7 @@ function QuerySection({
formulaOnly: true,
expression: 'A',
disabled: false,
legend: '',
};
setFormulaQueries({ ...formulas });

View File

@@ -25,7 +25,7 @@ function RuleOptions({
queryCategory,
}: RuleOptionsProps): JSX.Element {
// init namespace for translations
const { t } = useTranslation('rules');
const { t } = useTranslation('alerts');
const handleMatchOptChange = (value: string | unknown): void => {
const m = (value as string) || alertDef.condition?.matchType;
@@ -112,7 +112,8 @@ function RuleOptions({
<Option value="10m0s">{t('option_10min')}</Option>
<Option value="15m0s">{t('option_15min')}</Option>
<Option value="60m0s">{t('option_60min')}</Option>
<Option value="1440m0s">{t('option_24hours')}</Option>
<Option value="4h0m0s">{t('option_4hours')}</Option>
<Option value="24h0m0s">{t('option_24hours')}</Option>
</InlineSelect>
);
};
@@ -155,7 +156,9 @@ function RuleOptions({
...alertDef,
condition: {
...alertDef.condition,
target: (value as number) || undefined,
op: alertDef.condition?.op || defaultCompareOp,
matchType: alertDef.condition?.matchType || defaultMatchType,
target: value as number,
},
});
}}

View File

@@ -0,0 +1,132 @@
import { Col, Row, Typography } from 'antd';
import TextToolTip from 'components/TextToolTip';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { EQueryType } from 'types/common/dashboard';
import {
StyledList,
StyledListItem,
StyledMainContainer,
StyledTopic,
} from './styles';
function UserGuide({ queryType }: UserGuideProps): JSX.Element {
// init namespace for translations
const { t } = useTranslation('alerts');
const renderStep1QB = (): JSX.Element => {
return (
<>
<StyledTopic>{t('user_guide_qb_step1')}</StyledTopic>
<StyledList>
<StyledListItem>{t('user_guide_qb_step1a')}</StyledListItem>
<StyledListItem>{t('user_guide_qb_step1b')}</StyledListItem>
<StyledListItem>{t('user_guide_qb_step1c')}</StyledListItem>
<StyledListItem>{t('user_guide_qb_step1d')}</StyledListItem>
</StyledList>
</>
);
};
const renderStep2QB = (): JSX.Element => {
return (
<>
<StyledTopic>{t('user_guide_qb_step2')}</StyledTopic>
<StyledList>
<StyledListItem>{t('user_guide_qb_step2a')}</StyledListItem>
<StyledListItem>{t('user_guide_qb_step2b')}</StyledListItem>
</StyledList>
</>
);
};
const renderStep3QB = (): JSX.Element => {
return (
<>
<StyledTopic>{t('user_guide_qb_step3')}</StyledTopic>
<StyledList>
<StyledListItem>{t('user_guide_qb_step3a')}</StyledListItem>
<StyledListItem>{t('user_guide_qb_step3b')}</StyledListItem>
</StyledList>
</>
);
};
const renderGuideForQB = (): JSX.Element => {
return (
<>
{renderStep1QB()}
{renderStep2QB()}
{renderStep3QB()}
</>
);
};
const renderStep1PQL = (): JSX.Element => {
return (
<>
<StyledTopic>{t('user_guide_pql_step1')}</StyledTopic>
<StyledList>
<StyledListItem>{t('user_guide_pql_step1a')}</StyledListItem>
<StyledListItem>{t('user_guide_pql_step1b')}</StyledListItem>
</StyledList>
</>
);
};
const renderStep2PQL = (): JSX.Element => {
return (
<>
<StyledTopic>{t('user_guide_pql_step2')}</StyledTopic>
<StyledList>
<StyledListItem>{t('user_guide_pql_step2a')}</StyledListItem>
<StyledListItem>{t('user_guide_pql_step2b')}</StyledListItem>
</StyledList>
</>
);
};
const renderStep3PQL = (): JSX.Element => {
return (
<>
<StyledTopic>{t('user_guide_pql_step3')}</StyledTopic>
<StyledList>
<StyledListItem>{t('user_guide_pql_step3a')}</StyledListItem>
<StyledListItem>{t('user_guide_pql_step3b')}</StyledListItem>
</StyledList>
</>
);
};
const renderGuideForPQL = (): JSX.Element => {
return (
<>
{renderStep1PQL()}
{renderStep2PQL()}
{renderStep3PQL()}
</>
);
};
return (
<StyledMainContainer>
<Row>
<Col flex="auto">
<Typography.Paragraph> {t('user_guide_headline')} </Typography.Paragraph>
</Col>
<Col flex="none">
<TextToolTip
text={t('user_tooltip_more_help')}
url="https://signoz.io/docs/userguide/alerts-management/#create-alert-rules"
/>
</Col>
</Row>
{queryType === EQueryType.QUERY_BUILDER && renderGuideForQB()}
{queryType === EQueryType.PROM && renderGuideForPQL()}
</StyledMainContainer>
);
}
interface UserGuideProps {
queryType: EQueryType;
}
export default UserGuide;

View File

@@ -0,0 +1,17 @@
import { Card, Typography } from 'antd';
import styled from 'styled-components';
export const StyledMainContainer = styled(Card)``;
export const StyledTopic = styled(Typography.Paragraph)`
font-weight: 600;
`;
export const StyledList = styled.ul`
padding-left: 18px;
`;
export const StyledListItem = styled.li`
font-style: italic;
padding-bottom: 0.5rem;
`;

View File

@@ -1,6 +1,7 @@
import { ExclamationCircleOutlined, SaveOutlined } from '@ant-design/icons';
import { FormInstance, Modal, notification, Typography } from 'antd';
import saveAlertApi from 'api/alerts/save';
import testAlertApi from 'api/alerts/testAlert';
import ROUTES from 'constants/routes';
import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag';
import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag';
@@ -25,8 +26,16 @@ import BasicInfo from './BasicInfo';
import ChartPreview from './ChartPreview';
import QuerySection from './QuerySection';
import RuleOptions from './RuleOptions';
import { ActionButton, ButtonContainer, MainFormContainer } from './styles';
import {
ActionButton,
ButtonContainer,
MainFormContainer,
PanelContainer,
StyledLeftContainer,
StyledRightContainer,
} from './styles';
import useDebounce from './useDebounce';
import UserGuide from './UserGuide';
import {
prepareBuilderQueries,
prepareStagedQuery,
@@ -41,7 +50,7 @@ function FormAlertRules({
ruleId,
}: FormAlertRuleProps): JSX.Element {
// init namespace for translations
const { t } = useTranslation('rules');
const { t } = useTranslation('alerts');
// use query client
const ruleCache = useQueryClient();
@@ -75,7 +84,7 @@ function FormAlertRules({
// staged query is used to display chart preview
const [stagedQuery, setStagedQuery] = useState<StagedQuery>();
const debouncedStagedQuery = useDebounce(stagedQuery, 500);
const debouncedStagedQuery = useDebounce(stagedQuery, 1000);
// this use effect initiates staged query and
// other queries based on server data.
@@ -135,10 +144,74 @@ function FormAlertRules({
});
}
};
const validatePromParams = useCallback((): boolean => {
let retval = true;
if (queryCategory !== EQueryType.PROM) return retval;
if (!promQueries || Object.keys(promQueries).length === 0) {
notification.error({
message: 'Error',
description: t('promql_required'),
});
return false;
}
Object.keys(promQueries).forEach((key) => {
if (promQueries[key].query === '') {
notification.error({
message: 'Error',
description: t('promql_required'),
});
retval = false;
}
});
return retval;
}, [t, promQueries, queryCategory]);
const validateQBParams = useCallback((): boolean => {
let retval = true;
if (queryCategory !== EQueryType.QUERY_BUILDER) return true;
if (!metricQueries || Object.keys(metricQueries).length === 0) {
notification.error({
message: 'Error',
description: t('condition_required'),
});
return false;
}
if (!alertDef.condition?.target) {
notification.error({
message: 'Error',
description: t('target_missing'),
});
return false;
}
Object.keys(metricQueries).forEach((key) => {
if (metricQueries[key].metricName === '') {
notification.error({
message: 'Error',
description: t('metricname_missing', { where: metricQueries[key].name }),
});
retval = false;
}
});
Object.keys(formulaQueries).forEach((key) => {
if (formulaQueries[key].expression === '') {
notification.error({
message: 'Error',
description: t('expression_missing', formulaQueries[key].name),
});
retval = false;
}
});
return retval;
}, [t, alertDef, queryCategory, metricQueries, formulaQueries]);
const isFormValid = useCallback((): boolean => {
let retval = true;
if (!alertDef.alert || alertDef.alert === '') {
notification.error({
message: 'Error',
@@ -147,56 +220,14 @@ function FormAlertRules({
return false;
}
if (
queryCategory === EQueryType.PROM &&
(!promQueries || Object.keys(promQueries).length === 0)
) {
notification.error({
message: 'Error',
description: t('promql_required'),
});
if (!validatePromParams()) {
return false;
}
if (
(queryCategory === EQueryType.QUERY_BUILDER && !metricQueries) ||
Object.keys(metricQueries).length === 0
) {
notification.error({
message: 'Error',
description: t('condition_required'),
});
return false;
}
Object.keys(metricQueries).forEach((key) => {
if (metricQueries[key].metricName === '') {
retval = false;
notification.error({
message: 'Error',
description: t('metricname_missing', { where: metricQueries[key].name }),
});
}
});
Object.keys(formulaQueries).forEach((key) => {
if (formulaQueries[key].expression === '') {
retval = false;
notification.error({
message: 'Error',
description: t('expression_missing', formulaQueries[key].name),
});
}
});
return retval;
}, [t, alertDef, queryCategory, metricQueries, formulaQueries, promQueries]);
const saveRule = useCallback(async () => {
if (!isFormValid()) {
return;
}
return validateQBParams();
}, [t, validateQBParams, alertDef, validatePromParams]);
const preparePostData = (): AlertDef => {
const postableAlert: AlertDef = {
...alertDef,
source: window?.location.toString(),
@@ -211,6 +242,22 @@ function FormAlertRules({
},
},
};
return postableAlert;
};
const memoizedPreparePostData = useCallback(preparePostData, [
queryCategory,
alertDef,
metricQueries,
formulaQueries,
promQueries,
]);
const saveRule = useCallback(async () => {
if (!isFormValid()) {
return;
}
const postableAlert = memoizedPreparePostData();
setLoading(true);
try {
@@ -227,7 +274,7 @@ function FormAlertRules({
description:
!ruleId || ruleId === 0 ? t('rule_created') : t('rule_edited'),
});
console.log('invalidting cache');
// invalidate rule in cache
ruleCache.invalidateQueries(['ruleId', ruleId]);
@@ -241,24 +288,13 @@ function FormAlertRules({
});
}
} catch (e) {
console.log('save alert api failed:', e);
notification.error({
message: 'Error',
description: t('unexpected_error'),
});
}
setLoading(false);
}, [
t,
isFormValid,
queryCategory,
ruleId,
alertDef,
metricQueries,
formulaQueries,
promQueries,
ruleCache,
]);
}, [t, isFormValid, ruleId, ruleCache, memoizedPreparePostData]);
const onSaveHandler = useCallback(async () => {
const content = (
@@ -279,6 +315,44 @@ function FormAlertRules({
});
}, [t, saveRule, queryCategory]);
const onTestRuleHandler = useCallback(async () => {
if (!isFormValid()) {
return;
}
const postableAlert = memoizedPreparePostData();
setLoading(true);
try {
const response = await testAlertApi({ data: postableAlert });
if (response.statusCode === 200) {
const { payload } = response;
if (payload?.alertCount === 0) {
notification.error({
message: 'Error',
description: t('no_alerts_found'),
});
} else {
notification.success({
message: 'Success',
description: t('rule_test_fired'),
});
}
} else {
notification.error({
message: 'Error',
description: response.error || t('unexpected_error'),
});
}
} catch (e) {
notification.error({
message: 'Error',
description: t('unexpected_error'),
});
}
setLoading(false);
}, [t, isFormValid, memoizedPreparePostData]);
const renderBasicInfo = (): JSX.Element => (
<BasicInfo alertDef={alertDef} setAlertDef={setAlertDef} />
);
@@ -309,50 +383,65 @@ function FormAlertRules({
return (
<>
{Element}
<MainFormContainer
initialValues={initialValue}
layout="vertical"
form={formInstance}
>
{queryCategory === EQueryType.QUERY_BUILDER && renderQBChartPreview()}
{queryCategory === EQueryType.PROM && renderPromChartPreview()}
<QuerySection
queryCategory={queryCategory}
setQueryCategory={onQueryCategoryChange}
metricQueries={metricQueries}
setMetricQueries={setMetricQueries}
formulaQueries={formulaQueries}
setFormulaQueries={setFormulaQueries}
promQueries={promQueries}
setPromQueries={setPromQueries}
/>
<RuleOptions
queryCategory={queryCategory}
alertDef={alertDef}
setAlertDef={setAlertDef}
/>
{renderBasicInfo()}
<ButtonContainer>
<ActionButton
loading={loading || false}
type="primary"
onClick={onSaveHandler}
icon={<SaveOutlined />}
<PanelContainer>
<StyledLeftContainer flex="5 1 600px">
<MainFormContainer
initialValues={initialValue}
layout="vertical"
form={formInstance}
>
{ruleId > 0 ? t('button_savechanges') : t('button_createrule')}
</ActionButton>
<ActionButton
disabled={loading || false}
type="default"
onClick={onCancelHandler}
>
{ruleId === 0 && t('button_cancelchanges')}
{ruleId > 0 && t('button_discard')}
</ActionButton>
</ButtonContainer>
</MainFormContainer>
{queryCategory === EQueryType.QUERY_BUILDER && renderQBChartPreview()}
{queryCategory === EQueryType.PROM && renderPromChartPreview()}
<QuerySection
queryCategory={queryCategory}
setQueryCategory={onQueryCategoryChange}
metricQueries={metricQueries}
setMetricQueries={setMetricQueries}
formulaQueries={formulaQueries}
setFormulaQueries={setFormulaQueries}
promQueries={promQueries}
setPromQueries={setPromQueries}
/>
<RuleOptions
queryCategory={queryCategory}
alertDef={alertDef}
setAlertDef={setAlertDef}
/>
{renderBasicInfo()}
<ButtonContainer>
<ActionButton
loading={loading || false}
type="primary"
onClick={onSaveHandler}
icon={<SaveOutlined />}
>
{ruleId > 0 ? t('button_savechanges') : t('button_createrule')}
</ActionButton>
<ActionButton
loading={loading || false}
type="default"
onClick={onTestRuleHandler}
>
{' '}
{t('button_testrule')}
</ActionButton>
<ActionButton
disabled={loading || false}
type="default"
onClick={onCancelHandler}
>
{ruleId === 0 && t('button_cancelchanges')}
{ruleId > 0 && t('button_discard')}
</ActionButton>
</ButtonContainer>
</MainFormContainer>
</StyledLeftContainer>
<StyledRightContainer flex="1 1 300px">
<UserGuide queryType={queryCategory} />
</StyledRightContainer>
</PanelContainer>
</>
);
}

View File

@@ -28,7 +28,7 @@ function LabelSelect({
onSetLabels,
initialValues,
}: LabelSelectProps): JSX.Element | null {
const { t } = useTranslation('rules');
const { t } = useTranslation('alerts');
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const [currentVal, setCurrentVal] = useState('');
const [staging, setStaging] = useState<string[]>([]);

View File

@@ -8,8 +8,7 @@ interface SearchContainerProps {
}
export const SearchContainer = styled.div<SearchContainerProps>`
width: 70%;
border-radisu: 4px;
border-radius: 4px;
background: ${({ isDarkMode }): string => (isDarkMode ? '#000' : '#fff')};
flex: 1;
display: flex;

View File

@@ -1,11 +1,35 @@
import { Button, Card, Form, Input, InputNumber, Select } from 'antd';
import {
Button,
Card,
Col,
Form,
Input,
InputNumber,
Row,
Select,
Typography,
} from 'antd';
import FormItem from 'antd/lib/form/FormItem';
import TextArea from 'antd/lib/input/TextArea';
import styled from 'styled-components';
export const MainFormContainer = styled(Form)`
max-width: 900px;
export const PanelContainer = styled(Row)`
flex-wrap: nowrap;
`;
export const StyledRightContainer = styled(Col)`
&&& {
}
`;
export const StyledLeftContainer = styled(Col)`
&&& {
margin-right: 1rem;
}
`;
export const MainFormContainer = styled(Form)``;
export const ButtonContainer = styled.div`
&&& {
display: flex;
@@ -54,21 +78,19 @@ export const InlineSelect = styled(Select)`
`;
export const SeveritySelect = styled(Select)`
width: 15% !important;
width: 25% !important;
`;
export const InputSmall = styled(Input)`
width: 40% !important;
`;
export const FormContainer = styled.div`
export const FormContainer = styled(Card)`
padding: 2em;
margin-top: 1rem;
display: flex;
flex-direction: column;
background: #141414;
border-radius: 4px;
border: 1px solid #303030;
`;
export const ThresholdInput = styled(InputNumber)`
@@ -88,3 +110,11 @@ export const ThresholdInput = styled(InputNumber)`
export const TextareaMedium = styled(TextArea)`
width: 70%;
`;
export const FormItemMedium = styled(FormItem)`
width: 70%;
`;
export const ChannelSelectTip = styled(Typography.Text)`
color: hsla(0, 0%, 100%, 0.3);
`;

View File

@@ -126,7 +126,9 @@ export const toChartInterval = (evalWindow: string | undefined): Time => {
return '30min';
case '60m0s':
return '30min';
case '1440m0s':
case '4h0m0s':
return '4hr';
case '24h0m0s':
return '1day';
default:
return '5min';

View File

@@ -1,6 +1,8 @@
/* eslint-disable sonarjs/no-duplicate-string */
import { LoadingOutlined } from '@ant-design/icons';
import {
Button,
Card,
Col,
Divider,
Modal,
@@ -26,6 +28,7 @@ import {
} from 'types/api/disks/getDisks';
import { TTTLType } from 'types/api/settings/common';
import {
PayloadPropsLogs as GetRetentionPeriodLogsPayload,
PayloadPropsMetrics as GetRetentionPeriodMetricsPayload,
PayloadPropsTraces as GetRetentionPeriodTracesPayload,
} from 'types/api/settings/getRetention';
@@ -40,19 +43,25 @@ type NumberOrNull = number | null;
function GeneralSettings({
metricsTtlValuesPayload,
tracesTtlValuesPayload,
logsTtlValuesPayload,
getAvailableDiskPayload,
metricsTtlValuesRefetch,
tracesTtlValuesRefetch,
logsTtlValuesRefetch,
}: GeneralSettingsProps): JSX.Element {
const { t } = useTranslation(['generalSettings']);
const [modalMetrics, setModalMetrics] = useState<boolean>(false);
const [modalTraces, setModalTraces] = useState<boolean>(false);
const [modalLogs, setModalLogs] = useState<boolean>(false);
const [postApiLoadingMetrics, setPostApiLoadingMetrics] = useState<boolean>(
false,
);
const [postApiLoadingTraces, setPostApiLoadingTraces] = useState<boolean>(
false,
);
const [modalTraces, setModalTraces] = useState<boolean>(false);
const [postApiLoadingLogs, setPostApiLoadingLogs] = useState<boolean>(false);
const [availableDisks] = useState<IDiskType[]>(getAvailableDiskPayload);
const [metricsCurrentTTLValues, setMetricsCurrentTTLValues] = useState(
@@ -62,6 +71,10 @@ function GeneralSettings({
tracesTtlValuesPayload,
);
const [logsCurrentTTLValues, setLogsCurrentTTLValues] = useState(
logsTtlValuesPayload,
);
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
const [setRetentionPermission] = useComponentPermission(
@@ -86,6 +99,15 @@ function GeneralSettings({
setTracesS3RetentionPeriod,
] = useState<NumberOrNull>(null);
const [
logsTotalRetentionPeriod,
setLogsTotalRetentionPeriod,
] = useState<NumberOrNull>(null);
const [
logsS3RetentionPeriod,
setLogsS3RetentionPeriod,
] = useState<NumberOrNull>(null);
useEffect(() => {
if (metricsCurrentTTLValues) {
setMetricsTotalRetentionPeriod(
@@ -112,6 +134,17 @@ function GeneralSettings({
}
}, [tracesCurrentTTLValues]);
useEffect(() => {
if (logsCurrentTTLValues) {
setLogsTotalRetentionPeriod(logsCurrentTTLValues.logs_ttl_duration_hrs);
setLogsS3RetentionPeriod(
logsCurrentTTLValues.logs_move_ttl_duration_hrs
? logsCurrentTTLValues.logs_move_ttl_duration_hrs
: null,
);
}
}, [logsCurrentTTLValues]);
useInterval(
async (): Promise<void> => {
if (metricsTtlValuesPayload.status === 'pending') {
@@ -130,13 +163,24 @@ function GeneralSettings({
tracesTtlValuesPayload.status === 'pending' ? 1000 : null,
);
useInterval(
async (): Promise<void> => {
if (logsTtlValuesPayload.status === 'pending') {
logsTtlValuesRefetch();
}
},
logsTtlValuesPayload.status === 'pending' ? 1000 : null,
);
const onModalToggleHandler = (type: TTTLType): void => {
if (type === 'metrics') setModalMetrics((modal) => !modal);
if (type === 'traces') setModalTraces((modal) => !modal);
if (type === 'logs') setModalLogs((modal) => !modal);
};
const onPostApiLoadingHandler = (type: TTTLType): void => {
if (type === 'metrics') setPostApiLoadingMetrics((modal) => !modal);
if (type === 'traces') setPostApiLoadingTraces((modal) => !modal);
if (type === 'logs') setPostApiLoadingLogs((modal) => !modal);
};
const onClickSaveHandler = useCallback(
@@ -157,7 +201,13 @@ function GeneralSettings({
[availableDisks],
);
const [isMetricsSaveDisabled, isTracesSaveDisabled, errorText] = useMemo((): [
const [
isMetricsSaveDisabled,
isTracesSaveDisabled,
isLogsSaveDisabled,
errorText,
] = useMemo((): [
boolean,
boolean,
boolean,
string,
@@ -174,6 +224,7 @@ function GeneralSettings({
// Defaults to button not disabled and empty error message text.
let isMetricsSaveDisabled = false;
let isTracesSaveDisabled = false;
let isLogsSaveDisabled = false;
let errorText = '';
if (s3Enabled) {
@@ -189,18 +240,35 @@ function GeneralSettings({
) {
isTracesSaveDisabled = true;
errorText = messages.compareError('traces');
} else if (
(logsTotalRetentionPeriod || logsS3RetentionPeriod) &&
Number(logsTotalRetentionPeriod) <= Number(logsS3RetentionPeriod)
) {
isLogsSaveDisabled = true;
errorText = messages.compareError('logs');
}
}
if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) {
if (
!metricsTotalRetentionPeriod ||
!tracesTotalRetentionPeriod ||
!logsTotalRetentionPeriod
) {
isMetricsSaveDisabled = true;
isTracesSaveDisabled = true;
if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) {
errorText = messages.nullValueError('metrics and traces');
isLogsSaveDisabled = true;
if (
!metricsTotalRetentionPeriod &&
!tracesTotalRetentionPeriod &&
!logsTotalRetentionPeriod
) {
errorText = messages.nullValueError('metrics, traces and logs');
} else if (!metricsTotalRetentionPeriod) {
errorText = messages.nullValueError('metrics');
} else if (!tracesTotalRetentionPeriod) {
errorText = messages.nullValueError('traces');
} else if (!logsTotalRetentionPeriod) {
errorText = messages.nullValueError('logs');
}
}
if (
@@ -219,8 +287,23 @@ function GeneralSettings({
)
isTracesSaveDisabled = true;
return [isMetricsSaveDisabled, isTracesSaveDisabled, errorText];
if (
logsCurrentTTLValues.logs_ttl_duration_hrs === logsTotalRetentionPeriod &&
logsCurrentTTLValues.logs_move_ttl_duration_hrs === logsS3RetentionPeriod
)
isLogsSaveDisabled = true;
return [
isMetricsSaveDisabled,
isTracesSaveDisabled,
isLogsSaveDisabled,
errorText,
];
}, [
logsCurrentTTLValues.logs_move_ttl_duration_hrs,
logsCurrentTTLValues.logs_ttl_duration_hrs,
logsS3RetentionPeriod,
logsTotalRetentionPeriod,
metricsCurrentTTLValues.metrics_move_ttl_duration_hrs,
metricsCurrentTTLValues?.metrics_ttl_duration_hrs,
metricsS3RetentionPeriod,
@@ -235,21 +318,36 @@ function GeneralSettings({
// eslint-disable-next-line sonarjs/cognitive-complexity
const onOkHandler = async (type: TTTLType): Promise<void> => {
let apiCallTotalRetention;
let apiCallS3Retention;
switch (type) {
case 'metrics': {
apiCallTotalRetention = metricsTotalRetentionPeriod;
apiCallS3Retention = metricsS3RetentionPeriod;
break;
}
case 'traces': {
apiCallTotalRetention = tracesTotalRetentionPeriod;
apiCallS3Retention = tracesS3RetentionPeriod;
break;
}
case 'logs': {
apiCallTotalRetention = logsTotalRetentionPeriod;
apiCallS3Retention = logsS3RetentionPeriod;
break;
}
default: {
break;
}
}
try {
onPostApiLoadingHandler(type);
const setTTLResponse = await setRetentionApi({
type,
totalDuration: `${
(type === 'metrics'
? metricsTotalRetentionPeriod
: tracesTotalRetentionPeriod) || -1
}h`,
totalDuration: `${apiCallTotalRetention || -1}h`,
coldStorage: s3Enabled ? 's3' : null,
toColdDuration: `${
(type === 'metrics'
? metricsS3RetentionPeriod
: tracesS3RetentionPeriod) || -1
}h`,
toColdDuration: `${apiCallS3Retention || -1}h`,
});
let hasSetTTLFailed = false;
if (setTTLResponse.statusCode === 409) {
@@ -281,6 +379,15 @@ function GeneralSettings({
traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1,
status: '',
});
} else if (type === 'logs') {
logsTtlValuesRefetch();
if (!hasSetTTLFailed)
// Updates the currentTTL Values in order to avoid pushing the same values.
setLogsCurrentTTLValues({
logs_ttl_duration_hrs: logsTotalRetentionPeriod || -1,
logs_move_ttl_duration_hrs: logsS3RetentionPeriod || -1,
status: '',
});
}
} catch (error) {
notification.error({
@@ -375,63 +482,107 @@ function GeneralSettings({
/>
),
},
].map((category, idx, renderArr): JSX.Element | null => {
{
name: 'Logs',
retentionFields: [
{
name: t('total_retention_period'),
value: logsTotalRetentionPeriod,
setValue: setLogsTotalRetentionPeriod,
},
{
name: t('move_to_s3'),
value: logsS3RetentionPeriod,
setValue: setLogsS3RetentionPeriod,
hide: !s3Enabled,
},
],
save: {
modal: modalLogs,
modalOpen: (): void => onClickSaveHandler('logs'),
apiLoading: postApiLoadingLogs,
saveButtonText:
logsTtlValuesPayload.status === 'pending' ? (
<span>
<Spin spinning size="small" indicator={<LoadingOutlined spin />} />{' '}
{t('retention_save_button.pending', { name: 'logs' })}
</span>
) : (
<span>{t('retention_save_button.success')}</span>
),
isDisabled: logsTtlValuesPayload.status === 'pending' || isLogsSaveDisabled,
},
statusComponent: (
<StatusMessage
total_retention={logsTtlValuesPayload.expected_logs_ttl_duration_hrs}
status={logsTtlValuesPayload.status}
s3_retention={logsTtlValuesPayload.expected_logs_move_ttl_duration_hrs}
/>
),
},
].map((category): JSX.Element | null => {
if (
Array.isArray(category.retentionFields) &&
category.retentionFields.length > 0
) {
return (
<React.Fragment key={category.name}>
<Col xs={22} xl={11} key={category.name}>
<Typography.Title level={3}>{category.name}</Typography.Title>
{category.retentionFields.map((retentionField) => (
<Retention
key={retentionField.name}
text={retentionField.name}
retentionValue={retentionField.value}
setRetentionValue={retentionField.setValue}
hide={!!retentionField.hide}
<Col xs={22} xl={11} key={category.name} style={{ margin: '0.5rem' }}>
<Card style={{ height: '100%', minHeight: 300 }}>
<Typography.Title style={{ margin: 0 }} level={3}>
{category.name}
</Typography.Title>
<Divider
style={{
margin: '0.5rem 0',
padding: 0,
opacity: 0.5,
marginBottom: '1rem',
}}
/>
))}
<ActionItemsContainer>
<Button
type="primary"
onClick={category.save.modalOpen}
disabled={category.save.isDisabled}
{category.retentionFields.map((retentionField) => (
<Retention
key={retentionField.name}
text={retentionField.name}
retentionValue={retentionField.value}
setRetentionValue={retentionField.setValue}
hide={!!retentionField.hide}
/>
))}
<ActionItemsContainer>
<Button
type="primary"
onClick={category.save.modalOpen}
disabled={category.save.isDisabled}
>
{category.save.saveButtonText}
</Button>
{category.statusComponent}
</ActionItemsContainer>
<Modal
title={t('retention_confirmation')}
focusTriggerAfterClose
forceRender
destroyOnClose
closable
onCancel={(): void =>
onModalToggleHandler(category.name.toLowerCase() as TTTLType)
}
onOk={(): Promise<void> =>
onOkHandler(category.name.toLowerCase() as TTTLType)
}
centered
visible={category.save.modal}
confirmLoading={category.save.apiLoading}
>
{category.save.saveButtonText}
</Button>
{category.statusComponent}
</ActionItemsContainer>
<Modal
title={t('retention_confirmation')}
focusTriggerAfterClose
forceRender
destroyOnClose
closable
onCancel={(): void =>
onModalToggleHandler(category.name.toLowerCase() as TTTLType)
}
onOk={(): Promise<void> =>
onOkHandler(category.name.toLowerCase() as TTTLType)
}
centered
visible={category.save.modal}
confirmLoading={category.save.apiLoading}
>
<Typography>
{t('retention_confirmation_description', {
name: category.name.toLowerCase(),
})}
</Typography>
</Modal>
<Typography>
{t('retention_confirmation_description', {
name: category.name.toLowerCase(),
})}
</Typography>
</Modal>
</Card>
</Col>
{idx < renderArr.length && (
<Col xs={0} xl={1} style={{ textAlign: 'center' }}>
<Divider type="vertical" dashed style={{ height: '100%' }} />
</Col>
)}
</React.Fragment>
);
}
@@ -451,7 +602,7 @@ function GeneralSettings({
{errorText && <ErrorText>{errorText}</ErrorText>}
</ErrorTextContainer>
<Row justify="space-around">{renderConfig}</Row>
<Row justify="start">{renderConfig}</Row>
</Col>
);
}
@@ -460,12 +611,16 @@ interface GeneralSettingsProps {
getAvailableDiskPayload: GetDisksPayload;
metricsTtlValuesPayload: GetRetentionPeriodMetricsPayload;
tracesTtlValuesPayload: GetRetentionPeriodTracesPayload;
logsTtlValuesPayload: GetRetentionPeriodLogsPayload;
metricsTtlValuesRefetch: UseQueryResult<
ErrorResponse | SuccessResponse<GetRetentionPeriodMetricsPayload>
>['refetch'];
tracesTtlValuesRefetch: UseQueryResult<
ErrorResponse | SuccessResponse<GetRetentionPeriodTracesPayload>
>['refetch'];
logsTtlValuesRefetch: UseQueryResult<
ErrorResponse | SuccessResponse<GetRetentionPeriodLogsPayload>
>['refetch'];
}
export default GeneralSettings;

View File

@@ -84,10 +84,10 @@ function Retention({
return (
<RetentionContainer>
<Row justify="space-between">
<Col flex={1} style={{ display: 'flex' }}>
<Col span={12} style={{ display: 'flex' }}>
<RetentionFieldLabel>{text}</RetentionFieldLabel>
</Col>
<Col flex="150px">
<Row justify="end">
<RetentionFieldInputContainer>
<Input
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
@@ -102,7 +102,7 @@ function Retention({
{menuItems}
</Select>
</RetentionFieldInputContainer>
</Col>
</Row>
</Row>
</RetentionContainer>
);

View File

@@ -21,6 +21,7 @@ function GeneralSettings(): JSX.Element {
const [
getRetentionPeriodMetricsApiResponse,
getRetentionPeriodTracesApiResponse,
getRetentionPeriodLogsApiResponse,
getDisksResponse,
] = useQueries([
{
@@ -33,6 +34,10 @@ function GeneralSettings(): JSX.Element {
getRetentionPeriodApi('traces'),
queryKey: 'getRetentionPeriodApiTraces',
},
{
queryFn: (): TRetentionAPIReturn<'logs'> => getRetentionPeriodApi('logs'),
queryKey: 'getRetentionPeriodApiLogs',
},
{
queryFn: getDisks,
queryKey: 'getDisks',
@@ -60,17 +65,27 @@ function GeneralSettings(): JSX.Element {
</Typography>
);
}
// Error State - When RetentionPeriodLogsApi or getDiskApi gets errored out.
if (getRetentionPeriodLogsApiResponse.isError || getDisksResponse.isError) {
return (
<Typography>
{getRetentionPeriodLogsApiResponse.data?.error ||
getDisksResponse.data?.error ||
t('something_went_wrong')}
</Typography>
);
}
// Loading State - When Metrics, Traces and Disk API are in progress and the promise has not been resolved/reject.
if (
getRetentionPeriodMetricsApiResponse.isLoading ||
getDisksResponse.isLoading ||
!getDisksResponse.data?.payload ||
getRetentionPeriodMetricsApiResponse.isLoading ||
!getRetentionPeriodMetricsApiResponse.data?.payload ||
getRetentionPeriodTracesApiResponse.isLoading ||
getDisksResponse.isLoading ||
!getDisksResponse.data?.payload ||
!getRetentionPeriodTracesApiResponse.data?.payload
!getRetentionPeriodTracesApiResponse.data?.payload ||
getRetentionPeriodLogsApiResponse.isLoading ||
!getRetentionPeriodLogsApiResponse.data?.payload
) {
return <Spinner tip="Loading.." height="70vh" />;
}
@@ -83,6 +98,8 @@ function GeneralSettings(): JSX.Element {
metricsTtlValuesRefetch: getRetentionPeriodMetricsApiResponse.refetch,
tracesTtlValuesPayload: getRetentionPeriodTracesApiResponse.data?.payload,
tracesTtlValuesRefetch: getRetentionPeriodTracesApiResponse.refetch,
logsTtlValuesPayload: getRetentionPeriodLogsApiResponse.data?.payload,
logsTtlValuesRefetch: getRetentionPeriodLogsApiResponse.refetch,
}}
/>
);

View File

@@ -7,6 +7,7 @@ import {
timeItems,
timePreferance,
} from 'container/NewWidget/RightContainer/timeItems';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import getChartData from 'lib/getChartData';
import React, { useCallback, useState } from 'react';
import { useQuery } from 'react-query';
@@ -52,6 +53,7 @@ function FullView({
graphType: widget.panelTypes,
query: widget.query,
globalSelectedInterval: globalSelectedTime,
variables: getDashboardVariables(),
}),
);

View File

@@ -3,6 +3,7 @@ import { AxiosError } from 'axios';
import { ChartData } from 'chart.js';
import Spinner from 'components/Spinner';
import GridGraphComponent from 'container/GridGraphComponent';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import getChartData from 'lib/getChartData';
import isEmpty from 'lodash-es/isEmpty';
import React, { memo, useCallback, useEffect, useState } from 'react';
@@ -104,11 +105,18 @@ function GridCardGraph({
useEffect(() => {
(async (): Promise<void> => {
try {
setState((state) => ({
...state,
error: false,
errorMessage: '',
loading: true,
}));
const response = await GetMetricQueryRange({
selectedTime: widget.timePreferance,
graphType: widget.panelTypes,
query: widget.query,
globalSelectedInterval,
variables: getDashboardVariables(),
});
const isError = response.error;
@@ -144,6 +152,11 @@ function GridCardGraph({
errorMessage: (error as AxiosError).toString(),
loading: false,
}));
} finally {
setState((state) => ({
...state,
loading: false,
}));
}
})();
}, [widget, maxTime, minTime, globalSelectedInterval]);

View File

@@ -121,6 +121,7 @@ function GridGraph(props: Props): JSX.Element {
name: data.name,
tags: data.tags,
widgets: data.widgets,
variables: data.variables,
layout,
},
uuid: selectedDashboard.uuid,
@@ -157,6 +158,7 @@ function GridGraph(props: Props): JSX.Element {
data.name,
data.tags,
data.title,
data.variables,
data.widgets,
dispatch,
saveLayoutPermission,

View File

@@ -27,6 +27,7 @@ export const UpdateDashboard = async ({
description: data.description,
name: data.name,
tags: data.tags,
variables: data.variables,
widgets: [
...(data.widgets || []),
{

View File

@@ -1,10 +1,11 @@
import { Button } from 'antd';
import { NotificationInstance } from 'antd/lib/notification/index';
import deleteAlerts from 'api/alerts/delete';
import { State } from 'hooks/useFetch';
import React, { useState } from 'react';
import { PayloadProps as DeleteAlertPayloadProps } from 'types/api/alerts/delete';
import { Alerts } from 'types/api/alerts/getAll';
import { GettableAlert } from 'types/api/alerts/get';
import { ColumnButton } from './styles';
function DeleteAlert({
id,
@@ -72,20 +73,20 @@ function DeleteAlert({
};
return (
<Button
<ColumnButton
disabled={deleteAlertState.loading || false}
loading={deleteAlertState.loading || false}
onClick={(): Promise<void> => onDeleteHandler(id)}
type="link"
>
Delete
</Button>
</ColumnButton>
);
}
interface DeleteAlertProps {
id: Alerts['id'];
setData: React.Dispatch<React.SetStateAction<Alerts[]>>;
id: GettableAlert['id'];
setData: React.Dispatch<React.SetStateAction<GettableAlert[]>>;
notifications: NotificationInstance;
}

Some files were not shown because too many files have changed in this diff Show More