Compare commits

...

281 Commits

Author SHA1 Message Date
Prashant Shahi
2c49ba444b Merge branch 'main' into chore/sample-flask 2024-09-02 08:45:26 +00:00
Prashant Shahi
262beef8f9 Merge pull request #5800 from SigNoz/release/v0.53.x
Release/v0.53.x
2024-08-30 15:20:27 +05:30
Prashant Shahi
43cc6dea92 chore(signoz): 📌 pin versions: SigNoz OtelCollector 0.102.7
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-08-30 15:06:52 +05:30
Prashant Shahi
6684640abe Merge branch 'develop' into release/v0.53.x 2024-08-30 12:50:35 +05:30
SagarRajput-7
363fb7bc34 feat: Kafka UI feedbacks (#5801)
* fix: solved kafka feature feedbacks

* fix: changed coming soon text to - join slack community
2024-08-30 12:00:52 +05:30
Srikanth Chekuri
dde4485839 chore: add types for alert type, state, and rule data kind (#5804) 2024-08-30 10:34:11 +05:30
Srikanth Chekuri
44598e304d chore: remove feature usage code from manager (#5803) 2024-08-29 21:53:28 +05:30
Srikanth Chekuri
4295a2756a chore: remove old data migrations (#5802) 2024-08-29 21:44:12 +05:30
Prashant Shahi
0a146910d6 chore(signoz): 📌 pin versions: SigNoz 0.53.0, SigNoz OtelCollector 0.102.6
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-08-29 19:25:34 +05:30
Prashant Shahi
690ed0f7f1 Merge branch 'main' into release/v0.53.x 2024-08-29 19:23:57 +05:30
SagarRajput-7
2f0d98ae51 chore: added trace views test (#5519)
* feat: added trace filter test cases

* feat: added trace filter test cases - initial render

* feat: added test cases - query sync, filter section behaviour etc

* feat: deleted mock-data files

* feat: added test cases of undefined filters and items

* feat: deleted tsconfig

* feat: added clear and rest btn test cases for traces filters

* feat: added collapse and uncollapse test for traces filters

* fix: added test cases for trace - saved view

* chore: code refactor'

* chore: added trace for search and navigation

* chore: used ROUTES enum

* chore: fixed test cases after merge conflict
2024-08-29 17:21:39 +05:30
SagarRajput-7
fb92ddc822 chore: added trace detail tests (#5523)
* feat: added trace filter test cases

* feat: added trace filter test cases - initial render

* feat: added test cases - query sync, filter section behaviour etc

* feat: deleted mock-data files

* feat: added test cases of undefined filters and items

* feat: deleted tsconfig

* feat: added clear and rest btn test cases for traces filters

* feat: added collapse and uncollapse test for traces filters

* chore: added trace detail tests

* chore: added trace detail tests - span selection, focus and reset

* chore: fixed eslint
2024-08-29 16:48:08 +05:30
Yunus M
15b0569b56 fix: show add credit card modal only for cloud users (#5797) 2024-08-29 16:47:27 +05:30
SagarRajput-7
140533b790 feat: added Messaging queue detail page (#5690)
* feat: added Messaging queue detail page

* feat: added MQDetails - tables - consumer, producer & network latency

* feat: added MQConfigOption - with dummy responses

* feat: configured query-range and autocomplete against the staging setup

* feat: added queryparams and linked config options with graph

* feat: added shareable link, cleanup code and connected details table with graph

* feat: fixed comments

* Messaging queue overview (#5782)

* feat: added messaging queue overview page

* feat: added get-started links

* feat: fixed comments

* feat: messaging queue misc tasks (#5785)

* feat: added lightMode styles

* feat: misc fix

* feat: misc fix

* feat: added customer tooltip info text

* feat: removed reset btn until the funcitonality is clear

* feat: fixed comments

* feat: fixed comments and added onDragSelect

* feat: added placeholder doc link for get-started for non-cloud
2024-08-29 16:36:56 +05:30
Prashant Shahi
710d22786d chore(sample-flask): reduce locust user and request rate by half
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-08-29 04:56:14 +00:00
Pranay Prateek
532f274bd6 fix: changing color of beta tag for Service Map & light theme changer (#5731)
* fix: changing color of tag for Service Map in sidepanel

* fix: added geekblue and borderless in beta tag to light theme changer

* fix: removed comments
2024-08-28 18:36:40 +05:30
rahulkeswani101
3200fd054e fix: redirect users to previous page after clicking back on onboarding flow instead of services page (#5685)
Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-08-28 17:48:02 +05:30
Srikanth Chekuri
8468cc863e fix: double encode composite query for explorer links (#5777) 2024-08-28 14:18:15 +05:30
Shivanshu Raj Shrivastava
71911687bf Merge pull request #5652 from shivanshuraj1333/feat/issues/1588
Add network latency (traces-metrics) correlation for kafka
2024-08-27 18:45:31 +05:30
Shivanshu Raj Shrivastava
9644297d28 Merge branch 'develop' into feat/issues/1588 2024-08-27 18:29:50 +05:30
shivanshu
faa6fdfcde feat: bug-fix in ClickHouseFormattedValue to allow strings 2024-08-27 18:28:52 +05:30
shivanshu
aabf364cc6 feat: add partition level granularity 2024-08-27 18:27:44 +05:30
Vikrant Gupta
4b861b2169 fix: remove the checks for aggregate operator in case of metrics v3/v4 (#5775) 2024-08-27 17:00:51 +05:30
shivanshu
8d655bf419 chore: use MinAllowedStepInterval 2024-08-27 14:04:00 +05:30
shivanshu
90cb8ba9a1 chore: modify producer output 2024-08-26 20:35:08 +05:30
shivanshu
f508ee7521 chore: query, response update 2024-08-26 20:28:22 +05:30
shivanshu
413caad0d8 chore: cleanup 2024-08-26 20:28:22 +05:30
shivanshu
666f601ecd feat: change to builder queries 2024-08-26 20:28:22 +05:30
shivanshu
5cdcbef00c feat: add network latency for kafka 2024-08-26 20:28:22 +05:30
Vikrant Gupta
c2f607ab6b chore: clean out the logs time range issues / old logs explorer routes issue (#5590)
* chore: clean out the logs time range async issues

* chore: correct the permissions for old logs explorer
2024-08-26 19:26:34 +05:30
Shivanshu Raj Shrivastava
2ca10bb87c Merge pull request #5769 from shivanshuraj1333/tmp
small patch to fix consumer_group check
2024-08-26 17:47:19 +05:30
Yunus M
6fb2a6d4c9 fix: copy to clipboard not copying complete value in case of numbers (#5770) 2024-08-26 16:48:07 +05:30
Shivanshu Raj Shrivastava
464589e0ca Merge branch 'develop' into tmp 2024-08-26 15:37:22 +05:30
shivanshu
3b94dab3ce chore: small patch to fix consumer_group check 2024-08-26 15:36:18 +05:30
Nityananda Gohain
9f481aacff feat: enable macro (#5760) 2024-08-26 11:57:18 +05:30
Prashant Shahi
9eb6c6201b Merge branch 'main' into chore/sample-flask 2024-08-26 05:57:28 +00:00
Vikrant Gupta
22f2e68db2 fix: colored logs in new logs explorer (#5749)
* fix: colored logs in new logs explorer

* fix: handle escapes better

* fix: handle escapes better

* chore: add code comments

* chore: added back text to copy to the body
2024-08-26 10:41:52 +05:30
Vikrant Gupta
706f967246 chore: add extra safety nets in case of malformed URL (#5767) 2024-08-23 22:29:07 +05:30
Shivanshu Raj Shrivastava
1685f0e74f Merge pull request #5766 from shivanshuraj1333/patch-for-ee
patch for ee
2024-08-23 21:57:09 +05:30
shivanshu
74162456e5 chore: patch for ee 2024-08-23 21:45:09 +05:30
Srikanth Chekuri
b798518aa9 chore: add total count and state filter (#5745) 2024-08-23 21:13:00 +05:30
thesnallygaster
d7fd1d032b feat: Add option to change max_execution_time setting for clickhouse … (#5683) 2024-08-23 20:11:49 +05:30
Vikrant Gupta
a2ac49bfc2 fix: remove same origin check and return proper errors from upgrader function (#5724)
* chore: test websockets

* chore: test websockets

* chore: test websockets

* chore: test websockets

* chore: cleanup PR

* chore: added back check origin function and loggings to check why the mismatch

* chore: the uuid should update on every new request

* chore: experiment with delaying the query deletion

* chore: experiment with delaying the query deletion

* chore: experiment with delaying the query deletion

* chore: upgrade nginx conf

* chore: upgrade nginx conf

* chore: upgrade nginx conf

* chore: experiment with delaying the query deletion

* chore: cleanup PR

* chore: remove print statements
2024-08-23 17:37:56 +05:30
Sudeep MP
33541a2ac0 feat: add view templates option to dashboard menu (#5696)
* feat: add view templates option to dashboard menu

* feat: increase dropdown overlay width
Set the dropdown overlay width to 200px to provide breathing space for the dropdown button.
Added flex to wrap the dropdown button to create space between the right icon and the left elements.

---------

Co-authored-by: Pranay Prateek <pranay@signoz.io>
2024-08-23 15:55:04 +05:30
SagarRajput-7
947b5bdefb fix: handled defaultTraceSelected for traces list view (#5752)
* fix: handled defaultTraceSelected for traces list view

* fix: added metaData id
2024-08-23 15:15:30 +05:30
Vibhu Pandey
bd7d14b1ca feat(render): add render package (#5751)
### Summary

Add `render` package

#### Related Issues / PR's

https://github.com/SigNoz/signoz/pull/5710
2024-08-23 13:07:10 +05:30
Yunus M
43ed49f9d9 fix: dashboard names invisible due to same background color (#5758) 2024-08-23 12:24:06 +05:30
Vikrant Gupta
758b10f1bf fix: raw view css condense fix for line clamp (#5755) 2024-08-23 00:54:30 +05:30
Vikrant Gupta
ab1caf13fc feat: add support for group by attribute in log details (#5753)
* feat: add support for group by attribute in log details

* feat: auto shift to qb from search on adding groupBY

* feat: update icon and styles
2024-08-22 23:59:22 +05:30
Vikrant Gupta
96b81817e0 feat: add support for changing the font size in logs (#5739)
* feat: add support for changing the font size in logs

* fix: build issues and logs context

* chore: fix build issues

* feat: scale all the spaces

* chore: handle light mode designs

* feat: set small as the default
2024-08-22 23:56:51 +05:30
Vibhu Pandey
bfeceb0ed2 feat(web): add web package (#5743)
### Summary

Add a web package for serving frontend

#### Related Issues / PR's

https://github.com/SigNoz/signoz/pull/5710
2024-08-22 20:56:15 +05:30
Vibhu Pandey
c322fc72d9 feat(errors): add errors package (#5741)
### Summary

Add errors package

#### Related Issues / PR's

https://github.com/SigNoz/signoz/pull/5710
2024-08-22 15:19:32 +05:30
Vibhu Pandey
e7b5410c5b feat(packages): add registry and http packages (#5740)
### Summary

Add packages for Registry and HTTP

#### Related Issues / PR's

https://github.com/SigNoz/signoz/pull/5710
2024-08-22 14:24:02 +05:30
Srikanth Chekuri
072693d57d fix: nan and inf values in formula result (#5733) 2024-08-21 17:55:16 +05:30
SagarRajput-7
a20794040a chore: added trace explorer test (#5531)
* feat: added trace filter test cases

* feat: added trace filter test cases - initial render

* feat: added test cases - query sync, filter section behaviour etc

* feat: deleted mock-data files

* feat: added test cases of undefined filters and items

* feat: deleted tsconfig

* feat: added clear and rest btn test cases for traces filters

* feat: added collapse and uncollapse test for traces filters

* chore: added trace explorer tests
2024-08-21 15:04:42 +05:30
Vibhu Pandey
ab4a8dfbea feat(packages): add first dedicated confmap, config, version and instrumentation packages (#5727)
### Summary

A config package based on https://github.com/open-telemetry/opentelemetry-collector/blob/main/confmap/confmap.go for signoz.

#### Related Issues / PR's

This is a part of https://github.com/SigNoz/signoz/pull/5710
2024-08-21 14:18:44 +05:30
Vishal Sharma
fa0a065b95 chore: chat block events (#5725)
Also add go to integration event
2024-08-20 18:41:34 +05:30
Vibhu Pandey
abc8096a39 chore(codeowners): update codeowners to team (#5726) 2024-08-20 18:16:07 +05:30
SagarRajput-7
7cff07333f fix: added onDragSelect to DBCall and External metric app (#5694)
* fix: added onDragSelect to DBCall and External metric app

* fix: handled back navigation
2024-08-20 17:45:22 +05:30
Vikrant Gupta
5796d6cb8c feat: rewrite the query builder search component (#5659)
* feat: make the query builder search extensible

* feat: setup the framework and necessary states needed

* feat: cover the happy path of selects

* chore: forward typing flow handled

* chore: add antd select

* chore: add antd select

* chore: handle forward and backward flows

* feat: added tag properites to the search bar and multi tag partial handling

* feat: handle tag on blur and body contains changes

* feat: handle tag deselect

* feat: multi tag handling

* feat: multi tag handling

* fix: jest test cases

* chore: update the key

* chore: add edit tag not working as expected

* feat: handle cases for exists and nexists

* fix: handle has / nhas operators

* chore: fix usability issues

* chore: remove the usage for the new bar

* fix: flaky build issues

* feat: client changes for consumption and design changes for where clause in logs explorer  (#5712)

* feat: query search new ui

* feat: suggestions changes in v2

* feat: dropdown and tags ui touch up

* feat: added missing keyboard shortcuts

* fix: race condition issues

* feat: remove usage

* fix: operator select fix

* fix: handle example queries click changes

* chore: design sync

* chore: handle boolean selects

* chore: address review comments
2024-08-20 17:09:17 +05:30
Srikanth Chekuri
98367fd054 fix: add missing selected time range variables (#5714) 2024-08-20 15:08:29 +05:30
Raj Kamal Singh
ff8df5dc36 chore: use base prefix of /ws for websocket paths (#5719)
Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-08-20 13:26:34 +05:30
Vikrant Gupta
f0c9f12897 fix: do not use relative URLs for ws connections (#5715)
* fix: do not use relative URLs for ws connections

* fix: handle local env better

* chore: update the websocket endpoint

* chore: handle OSS/Docker installations
2024-08-20 13:17:56 +05:30
Prashant Shahi
5bcf7de440 Merge pull request #5704 from SigNoz/release/v0.52.x
Release/v0.52.x
2024-08-16 21:00:32 +05:30
Vikrant Gupta
79e96e544f chore: added leading slash for for ws URL (#5709) 2024-08-16 18:51:02 +05:30
Vishal Sharma
871e5ada9e chore: dashboard and alert names (#5705)
* chore: dashboard names

* chore: fix panel count
2024-08-16 18:08:59 +05:30
Vikrant Gupta
0401c27dbc chore: remove the base URL from the ws config (#5708) 2024-08-16 17:41:19 +05:30
Vibhu Pandey
57c45f22d6 feat(premium-support): add premium-support feature (#5707) 2024-08-16 16:50:40 +05:30
Srikanth Chekuri
29f1883edd chore: add telemetry for dashboards/alerts with tsv2 table (#5677) 2024-08-16 16:16:12 +05:30
Shaheer Kochai
5d903b5487 NOOP to Count in alert creation from logs (#5464)
* fix: change NOOP to count on creating alert from Logs and traces

* fix: change 'count' back to 'noop' in Traces page, in case there is a single query

* fix: handle the query modification in useGetCompositeQueryParam instead of Filter

* chore: use values StringOperators enum instead of hard coded strings

* Revert "fix: handle the query modification in useGetCompositeQueryParam instead of Filter"

This reverts commit 5bb837ec27.

* Revert "fix: change 'count' back to 'noop' in Traces page, in case there is a single query"

This reverts commit 5e506dbd35.
2024-08-16 14:12:22 +04:30
Vikrant Gupta
1b9683d699 feat: client changes for query stats (#5706)
* feat: changes for the query stats websockets

* chore: remove unwanted files

* fix: work on random id rather than hash

* fix: improve the icons and design

* feat: webpack and docker file changes

* fix: test cases

* chore: format the units

* chore: address review comments

* chore: update the id to uuid package

* fix: build issues

* chore: remove docker file changes

* chore: remove docker file changes
2024-08-16 15:07:06 +05:30
Vikrant Gupta
65280cf4e1 feat: support for attribute key suggestions and example queries in logs explorer query builder (#5608)
* feat: qb-suggestions base setup

* chore: make the dropdown a little similar to the designs

* chore: move out example queries from og and add to renderer

* chore: added the handlers for example queries

* chore: hide the example queries as soon as the user starts typing

* feat: handle changes for cancel query

* chore: remove stupid concept of option group

* chore: show only first 3 items and add option to show all filters

* chore: minor css changes and remove transitions

* feat: integrate suggestions api and control re-renders

* feat: added keyboard shortcuts for the dropdown

* fix: design cleanups and touchups

* fix: build issues and tests

* chore: extra safety check for base64 and fix tests

* fix: qs doesn't handle padding in base64 strings, added client logic

* chore: some code comments

* chore: some code comments

* chore: increase the height of the bar when key is set

* chore: address minor designs

* chore: update the keyboard shortcut to cmd+/

* feat: correct the option render for logs for tooltip

* chore: search bar to not loose focus on btn click

* fix: update the spacing and icon for search bar

* chore: address review comments
2024-08-16 13:11:39 +05:30
Prashant Shahi
703983a5f9 chore(signoz): 📌 pin versions: SigNoz 0.52.0, SigNoz OtelCollector 0.102.3
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-08-15 23:12:36 +05:30
Prashant Shahi
766a2123c5 Merge branch 'main' into release/v0.52.x 2024-08-15 13:42:02 +05:30
Yunus M
1308f0f15f feat: move chat support behind paywall (#5673)
* feat: move chat support behind paywall

* feat: wire up chat support paywall

* feat: move chat support code from app layout to separate component

* feat: add log events
2024-08-14 20:50:35 +05:30
Raj Kamal Singh
6c634b99d0 Feat: QS: query range progress api (#5671)
* feat: get query progress tracker started

* feat: flesh out query progress test some more and get first few assertions passing

* chore: flesh out query tracker tests and impl some more

* feat: add impl for QueryTracker.Subscribe

* feat: send latest update if available on subscription

* feat: broadcast query progress to all subscribers on update

* feat: finish plumbing query tracker happy path

* feat: finish with v0 impl for query progress tracker

* chore: some cleanup of query progress tracker

* feat: hook up query progress tracking for queryRangeV3

* feat: impl for query progress websocket API handler

* feat: implement Hijacker iface for loggingResponseWriter for websocket upgrades

* chore: some cleanup to query progress websocket API handler

* chore: some cleanup

* chore: move query progress impl into its own subpackage

* chore: separate in-memory tracker impl from interface

* chore: some more cleanup of in memory tracker

* chore: some more cleanup of query progress tracker

* chore: some final cleanups
2024-08-14 19:53:36 +05:30
Yunus M
9856335840 fix: hide beta icon in sidebar collapsed view (#5693) 2024-08-14 18:01:29 +05:30
Yunus M
e85b405396 fix: dashboards listing and details page css fixes (#5672)
* fix: dashboards listing and details page css fixes

* chore: remove commented code
2024-08-14 12:22:15 +05:30
Vishal Sharma
e2e965bc7f chore: zeus features (#5686)
* chore: zeus features

* chore: add tests and improve logging
2024-08-14 10:10:33 +05:30
Srikanth Chekuri
7811fdd17a fix: nil pointer dereference for empty payload (#5680) 2024-08-12 21:43:38 +05:30
Yunus M
0dca1237b9 feat: add beta tag for service map and light mode (#5674)
* feat: add beta tag for service map and light mode

* chore: update test case
2024-08-12 16:41:47 +05:30
Vikrant Gupta
f3d73f6d44 feat: use selected columns as pinned attributes (#5601)
* feat: use selected columns as pinned attributes

* chore: handle nested json structs

* chore: refactor and fix build issues

* feat: handle changes for dashboard list panel

* chore: remove console logs
2024-08-12 16:34:43 +05:30
Vikrant Gupta
187927403a fix: clean out the panel type change attribute dependency (#5648)
* fix: clean out the panel type change attribute dependency

* fix: clean out the updater function as well

* fix: issue with rendering list panel as first and then moving around
2024-08-11 16:46:18 +05:30
Srikanth Chekuri
0157b47424 chore: add empty labels to response (#5668) 2024-08-09 18:19:15 +05:30
Srikanth Chekuri
156905afc7 fix: send alert default annotations for missing data alert (#5315) 2024-08-09 15:31:39 +05:30
CheetoDa
a4878f6430 chore: updated k8s instructions (#5665)
Co-authored-by: Prashant Shahi <prashant@signoz.io>
2024-08-09 14:47:31 +05:30
Srikanth Chekuri
4489df6f39 feat: add runningDiff function (#5667) 2024-08-09 14:04:29 +05:30
Srikanth Chekuri
06c075466b chore: add eval tests for threshold rule (#5398) 2024-08-09 12:34:40 +05:30
Srikanth Chekuri
62be3e7c13 chore: enable caching for all panel types in metrics v4 (#5651) 2024-08-09 12:32:11 +05:30
Srikanth Chekuri
bb84960442 chore: add alerts state history query service impl (#5255) 2024-08-09 12:11:05 +05:30
Kobe Cai
52199361d5 chore: fix error message typo on update log field api (#5660) 2024-08-09 10:08:40 +05:30
Srikanth Chekuri
f031845300 chore: make eval delay configurable (#5649) 2024-08-08 17:34:25 +05:30
Shaheer Kochai
6f73bb6eca feat: login flow tests (#5540) 2024-08-08 09:18:23 +04:30
Shaheer Kochai
fe398bcc49 feat: my settings page tests (#5499)
* feat: my settings page tests

* chore: improve mysettings test names

* chore: remove commented code and console.log

* chore: add missing parentheses
2024-08-08 09:17:38 +04:30
Shaheer Kochai
6781c29082 feat: tests for alert channels settings (#5563)
* feat: tests for alert channels settings

* chore: overall improvements to alert channel settings tests

* chore: improve alerts dummy data
2024-08-08 08:52:15 +04:30
Raj Kamal Singh
eb146491f2 Feat: QS: query builder suggestions api v0 (#5634)
* chore: stash initial work with API signature

* chore: put together setup for integration testing filter suggestions

* feat: filter suggestions: suggest attribs using existing autocomplete logic

* chore: filter suggestions test: add expectation for example queries

* feat: filter suggestions: default suggestions when data yet to be received

* feat: finish plumbing basic example queries

* chore: add test for filter suggestions with an existing query

* feat: filter suggestions: don't suggest attribs already included in existing filter

* chore: generate example queries by including existing filter first

* chore: upgrade ClickHouse-go-mock

* chore: some cleanup of reader.GetQBFilterSuggestionsForLogs

* chore: some cleanup of filter suggestion tests

* chore: some cleanup to http handler and request parsing logic for filter suggestions

* chore: remove expectation that attrib suggestions won't contain attribs already used in filter
2024-08-08 09:27:41 +05:30
Vishal Sharma
ae325ec1ca chore: handle traceID search 404 performance issue (#5654)
By setting max and min timestamp filter same as current timestamp when traceIDs are not found
2024-08-08 08:32:11 +05:30
Srikanth Chekuri
fd6f0574f5 fix: make timeshift work with cache (#5646) 2024-08-06 20:24:06 +05:30
rahulkeswani101
b819a90c80 feat: added links to integrations page in onboarding section (#5606)
* feat: added links to integrations page in onboarding section

* feat: removed box shadow for button

* refactor: added routes object to navigate to integrations page

* feat: added new styles for data source name
2024-08-06 19:18:48 +05:30
rahulkeswani101
a6848f6abd fix: added card to show message for deleted alert id (#5565)
* fix: added card to show message for deleted alert id

* refactor: added new constants for handling error message when alert is deleted

* refactor: added error response to error message field

* refactor: removed console statement

* refactor: renamed the variables
2024-08-06 19:09:49 +05:30
Shivanshu Raj Shrivastava
abe65975c9 Merge pull request #5542 from shivanshuraj1333/api-kafka
messaging queue, consumer lag APIs
2024-08-06 17:58:01 +05:30
Shivanshu Raj Shrivastava
5cedd57aa2 Merge branch 'develop' into api-kafka 2024-08-06 16:30:30 +05:30
rahulkeswani101
80a7b9d16d feat: added link for dashboard name (#5544)
* feat: added link for dashboard name

* refactor: added getLink function to get the link of dashboard details page

* refactor: changed the color for dashboard name

* refactor: updated the classname for dashboard name

* fix: update css tokens and light mode design

---------

Co-authored-by: vikrantgupta25 <vikrant.thomso@gmail.com>
2024-08-06 13:33:51 +05:30
Shivanshu Raj Shrivastava
9f7b2542ec Merge branch 'develop' into api-kafka 2024-08-06 10:13:28 +05:30
Srikanth Chekuri
4a4c9f26a2 chore: add Reduce To for pie chart (#5629) 2024-08-05 20:53:52 +05:30
shivanshu
c957c0f757 chore: addressing review comments 2024-08-05 18:14:40 +05:30
shivanshu
3ff0aa4b4b chore: consumer group filtering 2024-08-05 18:09:58 +05:30
shivanshu
063c9adba6 chore: pr-reviews 2024-08-05 18:09:58 +05:30
shivanshu
5c3ce146fa chore: add queue type 2024-08-05 18:09:58 +05:30
shivanshu
481bb6e8b8 feat: add consumer and producer APIs 2024-08-05 18:09:58 +05:30
Yunus M
61e6316736 feat: add 1 month option in time range (#5639) 2024-08-05 16:57:24 +05:30
Vikrant Gupta
f9d1494657 feat: added support for units for formula columns in table panel type (#5638)
* feat: added support for formula columns units

* chore: add unit test cases for query and formula units
2024-08-05 16:54:45 +05:30
dependabot[bot]
0021b4d784 chore(deps): bump fast-loops from 1.1.3 to 1.1.4 in /frontend (#5465)
Bumps [fast-loops](https://github.com/robinweser/fast-loops) from 1.1.3 to 1.1.4.
- [Commits](https://github.com/robinweser/fast-loops/commits)

---
updated-dependencies:
- dependency-name: fast-loops
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-05 10:51:27 +05:30
Yunus M
a5d5800871 feat: enable pagination for service listing,key operations,explorer table and dashboard table (#5625) 2024-08-02 21:51:09 +05:30
Srikanth Chekuri
16dc90bbd1 chore(telemetry): add telemetry for metrics query type and count prom… (#5627) 2024-08-02 18:45:02 +05:30
Prashant Shahi
fff61379fe fix: mount root path in /hostfs for hostmetrics (#5534)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-08-02 16:06:21 +05:30
Vikrant Gupta
08a415032c chore: added service name and time params for top level operations (#5552)
* chore: added service name and time params for top level operations

* fix: build issues

* chore: update the useTopLevelOpertions to send start and end time

* chore: added extra checks to not send the param when undefined

* chore: added extra checks to not send the param when undefined

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-08-01 14:17:00 +05:30
Prashant Shahi
c13b347808 Merge branch 'main' into chore/sample-flask 2024-08-01 08:01:04 +00:00
Raj Kamal Singh
3783ffdd4c feat: show log severity indicator based on severity number if it's available when severity text is unknown (#4971)
* feat: set log sev indicator based on severity number if severity text is unknown

* chore: some cleanup

* chore: some more cleanup

* chore: update log state indicator utils test

* chore: some more cleanup

* fix: priority to severity_number over severity_text and update tests

* fix: made the severity_text check case insensitive and added null checks

---------

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-08-01 12:06:29 +05:30
Vikrant Gupta
a8e4359d95 fix: logs context not working because of incorrect request data (#5595) 2024-08-01 11:29:05 +05:30
UnCool-0x
d9e94a4067 feat: windows onboarding in cloud (#5525)
* feat: windows onboarding in cloud

* fix: missed file instructions

* feat: assigned vars

* feat: windows onboarding minor changes

---------
2024-08-01 09:27:13 +05:30
rahulkeswani101
ae19eaa76a feat: redirect to original page after login (#5604) 2024-08-01 08:49:26 +05:30
SagarRajput-7
fff9954da2 Schedule maintainence release changes (#5585)
* feat: schedule maintenance feedback fixes

* feat: schedule maintenance feedback fixes

* feat: code refactor

* feat: code refactor

* feat: fixed incorrect payload values from start and endTime

* feat: sorted list by updatedAt

* feat: removed dependency on BE response prop - kind

* feat: fixed timezone switching and adding different timezones
2024-07-31 22:30:42 +05:30
Prashant Shahi
a476c68f7e Merge pull request #5618 from SigNoz/release/v0.51.x
Release/v0.51.x
2024-07-31 22:30:09 +05:30
Prashant Shahi
fc15aa6f1c Merge branch 'develop' into release/v0.51.x 2024-07-31 21:29:07 +05:30
Vikrant Gupta
220edd139a fix: do not send query_range api call on every keystroke (#5613) 2024-07-31 21:21:02 +05:30
Prashant Shahi
4192fd573d chore(signoz): 📌 pin versions: SigNoz 0.51.0, SigNoz OtelCollector 0.102.3
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-07-31 20:29:09 +05:30
Prashant Shahi
ca13d80205 Merge branch 'main' into release/v0.51.x 2024-07-31 20:27:51 +05:30
Raj Kamal Singh
59121bd932 chore: nginx integration: add note about adjusting regex if using custom log format (#5615) 2024-07-31 17:52:51 +05:30
Vishal Sharma
aef935a817 feat: faster traceID based filtering (#5607)
* feat: faster traceID based filtering

* chore: add error log
2024-07-31 16:00:57 +05:30
Srikanth Chekuri
f300518d61 chore: add telemetry for channel types (#5602) 2024-07-31 15:15:19 +05:30
Yunus M
18b608a1d8 feat: update logEvent to silently handle errors (#5599) 2024-07-30 18:24:55 +05:30
Yunus M
738d62c9cf fix: show 0 as limit is user has set it to 0 (#5605) 2024-07-30 18:09:29 +05:30
Srikanth Chekuri
38e694cd36 chore: only fetch top level operation from the selected time window (#5404) 2024-07-30 02:02:50 +05:30
Vikrant Gupta
1281330c52 fix: disable the unlock dashboard btn for integration dashboards (#5573)
* fix: disable the unlock dashboard btn for integration dashboards

* chore: added test cases for the integration / non integration dashboards
2024-07-29 15:47:09 +05:30
Yunus M
7b7cca7db7 chore: remove commented code (#5445) 2024-07-29 11:45:03 +05:30
rahulkeswani101
3134e8c1cf feat: removed top nav from new alerts landing page (#5538)
* feat: removed top nav from new alerts landing page

* feat: added new function to check new alerts landing page

---------

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-07-29 11:42:55 +05:30
Vikrant Gupta
d00024b64a feat: preference framework qs changes (#5527)
* feat: query service changes base setup for preferences

* feat: added handlers for user and org preferences

* chore: added base for all user and all org preferences

* feat: added handlers for all user and all org preferences

* feat: register the preference routes and initDB in pkg/query-service

* feat: code refactor

* chore: too much fun code refactor

* chore: little little missing attributes

* fix: handle range queries better

* fix: handle range queries better

* chore: address review comments

* chore: use struct inheritance for the all preferences struct

* chore: address review comments

* chore: address review comments

* chore: correct preference routes

* chore: low hanging optimisations

* chore: address review comments

* chore: address review comments

* chore: added extra validations for the check in allowed values

* fix: better handling for the jwt claims

* fix: better handling for the jwt claims

* chore: move the error to preference apis

* chore: move the error to preference apis

* fix: move the 401 logic to the auth middleware
2024-07-29 09:51:18 +05:30
Prashant Shahi
4360cd0397 fix(saml): handle invalid email domain (#5580)
### Summary

Handle the scenario when email with domain is used for SSO Login which does not match authenticated domains.

Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-07-27 09:52:53 +05:30
Vishal Sharma
a688b6c60e Revert "fix(saml): handle invalid email domain (#5564)" (#5579)
This reverts commit ba7e6fcf23.
2024-07-27 08:47:44 +05:30
Vishal Sharma
522e73b48e chore: move facing issues button in dashboards and disable intercom ping (#5571)
* chore: move facing issues button in dashboards and disable intercom ping

* chore: review comment
2024-07-26 18:51:48 +05:30
Prashant Shahi
ba7e6fcf23 fix(saml): handle invalid email domain (#5564)
### Summary

Handle the scenario when email with domain is used for SSO Login which does not match authenticated domains.

Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-07-26 18:41:39 +05:30
Vibhu Pandey
eefccafa5b feat(gateway): remove feature flag (#5561) 2024-07-26 12:31:33 +05:30
Vikrant Gupta
05bd6d52f1 fix: relative time param from the url not respected (#5545)
* fix: relative time param from the url not respected

* chore: added code comments and the priorities of the params

* fix: added validity checks for the relativeTime in the url
2024-07-25 23:23:01 +05:30
Vikrant Gupta
d60daef171 fix: handle the super set query reset state when changing widgets (#5539) 2024-07-23 20:21:25 +05:30
Vikrant Gupta
d50530f58c fix: retain the step interval while creating alerts from the dashboard panel (#5455)
* fix: use the same step interval as in the dashboard query while creating alerts from panel

* chore: added extra safety checks

* chore: add test cases for the mapQueryDataFromAPI utils

* chore: added functions test cases as well

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-07-23 17:20:31 +05:30
Yunus M
6957bd71ca chore: move from trackEvent to logEvent (#5530)
* chore: move from trackEvent to logEvent

* feat: update test cases
2024-07-23 16:32:45 +05:30
Vikrant Gupta
ef8b50c19e chore: addition of jest test cases for dashboards panels (#5506)
* chore: added value panel wrapper jest tests

* chore: added column units and legends test for table panel wrapper
2024-07-22 21:10:48 +05:30
Nityananda Gohain
1585065fff fix: use proper indexes for full text search (#4787)
* fix: use proper indexes for full text search

* fix: tests updated

* feat: lower support only for body and not attributes

* fix: remove default tolower

* fix: add comment for json key split

* fix: remove ilike only for body searches

* fix: minor fixes

* fix: minor fixes
2024-07-22 17:46:35 +05:30
Yunus M
99c68ddbcd feat: add learn more urls to ingestion settings page (#5526)
* feat: add learn more urls to ingestion settings page

* feat: enable multi ingestion settins for editors, add basic test cases
2024-07-22 16:29:00 +05:30
Vikrant Gupta
b08e859426 fix: do not add select columns when the datasource is logs (#5515)
* fix: do not add select columns when the datasource is logs

* chore: added data test id
2024-07-22 13:43:47 +05:30
SagarRajput-7
89fd3e4f55 chore: added trace filter test cases (#5451)
* feat: added trace filter test cases

* feat: added trace filter test cases - initial render

* feat: added test cases - query sync, filter section behaviour etc

* feat: deleted mock-data files

* feat: added test cases of undefined filters and items

* feat: deleted tsconfig

* feat: added clear and rest btn test cases for traces filters

* feat: added collapse and uncollapse test for traces filters
2024-07-22 11:05:20 +05:30
Vibhu Pandey
a2492b0135 ci(github): change to beta (#5524)
* ci(github): change to beta

* Update testing-deployment.yaml

* ci(staging): bump to beta
2024-07-19 11:59:40 +05:30
Nityananda Gohain
eb8ca5a7ca fix: ignore offset if timestamp is selected in order by (#5520)
* fix: ignore offset if timestamp is selected in order by

* fix: tests updated
2024-07-18 18:03:39 +05:30
Pranay Prateek
80133240ca fix: update community link (#5516)
* update community link

* Update copyright year
2024-07-18 17:25:32 +05:30
Vikrant Gupta
7d7d112f40 fix: the dashboard locked bar should be sticky at the bottom (#5512) 2024-07-18 13:56:18 +05:30
SagarRajput-7
add2d19614 fix: fixed logEvent breaking page due to lack of null checks (#5511)
* fix: fixed logEvent breaking page due to lack of null checks

* fix: fixed logEvent breaking page due to lack of null checks
2024-07-18 13:54:05 +05:30
Vikrant Gupta
adfe20e88a fix: url params should not propagate across pages (#5417)
* fix: dashboards list url query params isolation

* feat: order query param old logs explorer isolation

* feat: added extra checks in place

* fix: refactor the dashboards list page for better performance

* chore: add test cases for the dashboards list page

* fix: added test cases for dashboards list page

* fix: added code comments

* fix: added empty state for dashboards and no search state
2024-07-18 12:25:31 +05:30
Prashant Shahi
610edbb3d1 Merge branch 'main' into chore/sample-flask 2024-07-17 14:55:28 +00:00
Prashant Shahi
8d84ce8f06 Merge pull request #5509 from SigNoz/release/v0.50.x
Release/v0.50.x
2024-07-17 20:16:19 +05:30
Prashant Shahi
09ea7b9eb5 chore(signoz): 📌 pin versions: SigNoz 0.50.0
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-07-17 19:01:03 +05:30
Vishal Sharma
d3b83f5a41 chore: update heartbeat interval logic (#5507)
* chore: update heartbeat interval logic

* chore: address review comment
2024-07-17 17:05:15 +05:30
B Kevin Anderson
77eba9a558 fix: list panel not querying selected columns (#5452)
Added log and traces columns to query for list panels
  Closes #5064

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-07-17 12:19:00 +05:30
Vikrant Gupta
43e73e06fe chore: helpers required for dashboards e2e test cases (#5496)
* chore: helpers required for dashboards e2e test cases

* chore: helpers required for dashboards e2e test cases

* chore: helpers required for dashboards e2e test cases
2024-07-16 18:35:59 +05:30
Vikrant Gupta
840d8b2e49 fix: 404 not found when intercepting the ingestion key calls (#5490) 2024-07-16 14:30:03 +05:30
Vishal Sharma
df751c7f38 chore: add activation events (#5474) 2024-07-16 14:18:59 +05:30
Shaheer Kochai
cd07c743b6 Implement OverlayScrollbars throughout the app for MacOS-like scrolling experience (#5423)
* feat: build overlay scrollbar component for Virtuoso elements

* feat: apply overlay scroll to Virtuoso components

* feat: build overlay scrollbar component for normal scrollable sections

* feat: apply overlay scrollbar to normal scrollable sections

* feat: add dark mode UI support to overlay scrollbars

* chore: rename OverlayScrollbar to OverlayScrollbarForTypicalChildren

* chore: move inline style to scss file

* chore: rename VirtuosoOverlayScrollbar to OverlayScrollbarForVirtuosoChildren

* chore: move OverlayScrollbarForTypicalChildren to components folder

* chore: create a common component for handling Virtuoso and Typical scroll sections

* chore: rename Virtuoso and Typical Overlay Scrollbar components

* fix: fix the overlay scrollbar initialization flickering

* fix: remove calculated height from typical overlay scrollbar + remove the explicit height: 100%
2024-07-16 14:16:13 +05:30
Shaheer Kochai
46e6c34e51 fix: block alert creation if query_range API fails (#5441) 2024-07-16 14:13:25 +05:30
Yunus M
42f7905b3b feat: show status message, status code string, span kind in trace det… (#5428)
* feat: show status message, status code string, span kind in trace details

* chore: update tests

* chore: update snapshots
2024-07-16 11:00:29 +05:30
Vikrant Gupta
a6e68c6519 fix: issue with table sorting when column contains both string and numbers (#5458) 2024-07-15 21:15:37 +05:30
Vikrant Gupta
c7e3e6dc4e fix: retain legends while changing panel types (#5447) 2024-07-15 21:04:49 +05:30
Srikanth Chekuri
9194ab08b6 fix: incorrect response for promql value type panels (#5497) 2024-07-15 18:06:39 +05:30
Srikanth Chekuri
3ecb2e35ef chore: use version v4 for export panel from explorer pages (#5438) 2024-07-12 18:49:24 +05:30
SagarRajput-7
9844dcdfb7 fix: added logic to keep sections uncollapsed for all filtered items (#5371) 2024-07-10 12:43:39 +05:30
SagarRajput-7
ddf5569ce9 fix: added null check on filters obj (#5419)
* fix: added null check on filters obj

* feat: added test cases of undefined filters and items

* feat: added comments
2024-07-10 11:56:11 +05:30
Nityananda Gohain
83455e614e fix: disable removing a selected field (#5457)
* fix: disable removing a selected field

* fix: comment updated with issue link

* fix: remove local db
2024-07-10 11:23:29 +05:30
Srikanth Chekuri
831de18464 fix: concurrent map writes to temporalityMap (#5432) 2024-07-10 11:00:28 +05:30
dependabot[bot]
3b2a811f7b chore(deps): bump google.golang.org/grpc from 1.64.0 to 1.64.1 (#5463)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.64.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.64.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-10 08:11:53 +05:30
Yunus M
2c7a5126fd update project maintainers (#5460) 2024-07-10 00:30:25 +05:30
Prashant Shahi
04991ca4a2 Merge branch 'main' into chore/sample-flask 2024-07-09 18:40:55 +00:00
Shaheer Kochai
87f1597d4e fix: prevent overwriting query expression and queryName on switching between panel types (#5430) 2024-07-09 08:13:35 +04:30
Shaheer Kochai
916663b4d5 fix: fix the explorer toolbar buttons padding (#5443) 2024-07-09 08:12:25 +04:30
Shaheer Kochai
b0e355eb64 fix: properly render \n and \t in log details + apply Geist Mono font to the logs (#5347)
* fix: properly render newline and tab in log details

* fix: change font family and add tab size to properly render \t

* feat: apply Geist Mono font to the logs
2024-07-09 08:11:46 +04:30
Shaheer Kochai
69a39531f0 Merge pull request #5440 from SigNoz/feat--add-react-query-dev-tools-in-dev-env
chore: add react-query devtools in development env
2024-07-08 20:01:21 +04:30
SagarRajput-7
9c9ed741b2 feat: changed name from 'Histogram' to 'Frequency chart' (#5369)
* feat: changed name from 'Histogram' to 'Frequence chart'

* feat: cdoe refactor and test case changes

* feat: added test case for frequency chart
2024-07-08 20:02:10 +05:30
SagarRajput-7
e6eaaa660a feat: added invite team member from onboarding flow (#5410)
* feat: added invite team member from onboarding flow

* feat: removed commented code and added text to strings-translations

* feat: added en-gb strings

* feat: added more text to strings

* feat: removed commented code and app.ts changes

* feat: added test case for onboarding and invite flow

* feat: added invite team member logEvents

* feat: resovled comments

* feat: cdoe refactor and test case changes
2024-07-08 19:50:29 +05:30
Vikrant Gupta
79eef5bb91 fix: clickhouse editor cursor sync issue (#5435) 2024-07-08 19:27:02 +05:30
Vikrant Gupta
4d64f1dede chore: better logging for duplicate keyboard shortcuts (#5425)
* chore: better logging for duplicate keyboard shortcuts

* chore: skip flaky test

* fix: make the shortcut error silent in prod
2024-07-08 19:25:50 +05:30
Vikrant Gupta
bf177882e6 fix: resize observer charts issue in alerts builder (#5436) 2024-07-08 19:24:05 +05:30
SagarRajput-7
f6b29999c9 fix: added right margin to facing issues btn on dashboad detail page (#5365)
* fix: added right padding to facing issues btn on dashboad detail page

* fix: added right margin instead of padding
2024-07-08 19:17:27 +05:30
Shaheer Kochai
75815897b0 Merge branch 'develop' into feat--add-react-query-dev-tools-in-dev-env 2024-07-08 10:53:14 +04:30
SagarRajput-7
c9309eecaa feat: added empty states for list, trace and timeSeried view in traces (#5290)
* feat: added empty states for list, trace and timeSeried view in traces

* feat: test case skip

* feat: fixed import order

* feat: added utm parameter link

* feat: added strings

* feat: resovled comments

* feat: added common doclinks util

* feat: test case updated:
2024-07-08 11:19:07 +05:30
ahmadshaheer1
4264fc0f3a feat: add react-query devtools in development env 2024-07-07 10:46:49 +04:30
Prashant Shahi
ef854910db Merge pull request #5437 from SigNoz/sync/signoz-0.49.1
Sync/signoz 0.49.1
2024-07-05 19:56:34 +05:30
Prashant Shahi
6b8b2ae761 Merge pull request #5429 from SigNoz/release/v0.49.x
Release/v0.49.1
2024-07-04 22:37:04 +05:30
Prashant Shahi
a48340a2ea Merge branch 'main' into release/v0.49.x 2024-07-04 22:27:41 +05:30
Prashant Shahi
e542d2ee09 chore(signoz): 📌 pin versions: SigNoz 0.49.1, SigNoz OtelCollector 0.102.2
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-07-04 22:24:57 +05:30
Prashant Shahi
08431131a9 Merge branch 'develop' into release/v0.49.x 2024-07-04 22:21:07 +05:30
Prashant Shahi
9c7e1a0581 Merge branch 'main' into chore/sample-flask 2024-07-04 14:56:45 +00:00
Nityananda Gohain
1b0ec8ac43 fix: typecase support added for float to int (#5408) 2024-07-04 12:08:42 +05:30
Yunus M
2e0ddc7c7f chore: remove dynamic config invocation (#5416) 2024-07-04 01:07:55 +05:30
Prashant Shahi
858a0cb0de Merge pull request #5418 from SigNoz/release/v0.49.x
Release/v0.49.x
2024-07-03 18:54:14 +05:30
Prashant Shahi
216ad36234 chore(signoz): 📌 pin versions: SigNoz 0.49.0, SigNoz OtelCollector 0.102.1
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-07-03 16:53:11 +05:30
Prashant Shahi
6628abd435 Merge branch 'main' into release/v0.49.x 2024-07-03 16:51:16 +05:30
dependabot[bot]
7c81270ed9 chore(deps): bump ws from 7.5.9 to 7.5.10 in /frontend (#5265)
Bumps [ws](https://github.com/websockets/ws) from 7.5.9 to 7.5.10.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 15:39:28 +05:30
dependabot[bot]
81c3e6fa65 chore(deps): bump braces from 3.0.2 to 3.0.3 in /frontend (#5196)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 12:33:22 +05:30
Yunus M
d215ce09b0 fix: remove pagination from members listing in org settings page (#5400) 2024-07-03 10:30:41 +05:30
Raj Kamal Singh
161a69fbe9 chore: remove workaround for supporting pipeline filters using attribs with . replaced with _ (#5405) 2024-07-02 17:14:08 +05:30
Srikanth Chekuri
3ee51770fd chore: remove rules dependency in CH reader (#5396) 2024-07-02 12:03:01 +05:30
Nityananda Gohain
932b7ddc69 fix: orderby validation and correction in logs old QB (#5399) 2024-07-02 11:53:30 +05:30
Vishal Sharma
6e466df89d chore: update posthog-js (#5382) 2024-07-01 21:11:31 +05:30
Srikanth Chekuri
326dec21fd fix: use the correct formatter for the description (#5388) 2024-07-01 18:34:02 +05:30
Srikanth Chekuri
b0b69c83db fix: use fill gaps only for time series panel types (#5387) 2024-07-01 14:06:28 +05:30
Vikrant Gupta
02106277a6 fix: restructure code to handle loading state for panel type change (#5378)
* fix: restructure code to handle loading state for panel type change

* fix: add inline comments
2024-06-28 13:53:35 +05:30
Vikrant Gupta
b34509215e fix: pie chart panels not rendering (#5376)
* fix: pie chart panels not rendering

* fix: restructure code
2024-06-28 12:10:57 +05:30
Raj Kamal Singh
fd603b8fdf Fix: pipeline alias collisions shouldnt lead to duplicate log processors (#5372)
* chore: add test validating pipeline alias collisions dont lead to bad config recommendations

* chore: emit error log on detecting duplicate processors in generated config

* chore: ensure collector config processor names for pipelines are unique

* chore: minor cleanups
2024-06-28 09:31:21 +05:30
Prashant Shahi
e3484dcfa9 Merge branch 'main' into chore/sample-flask 2024-06-27 18:41:01 +00:00
Prashant Shahi
c6e9eeeee6 Merge pull request #5348 from SigNoz/release/v0.48.1
Release/v0.48.1
2024-06-25 19:54:14 +05:30
Prashant Shahi
97b66741a7 chore(signoz): 📌 pin versions: SigNoz 0.48.1
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-06-25 17:59:37 +05:30
Prashant Shahi
6b234da969 Merge branch 'main' into release/v0.48.1 2024-06-25 17:58:39 +05:30
Prashant Shahi
e6de113ff0 Merge branch 'main' into chore/sample-flask 2024-06-21 04:42:50 +00:00
Prashant Shahi
9dbef080c6 Merge pull request #5288 from SigNoz/release/v0.48.x
Release/v0.48.x
2024-06-20 20:49:47 +05:30
Prashant Shahi
6c192f1242 Merge branch 'develop' into release/v0.48.x 2024-06-20 18:46:14 +05:30
Prashant Shahi
537641000d chore(signoz): 📌 pin versions: SigNoz 0.48.0, SigNoz OtelCollector 0.102.0
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-06-19 21:14:45 +05:30
Prashant Shahi
4916cf5083 Merge branch 'main' into release/v0.48.x 2024-06-19 20:57:30 +05:30
Prashant Shahi
c8045243b5 Merge branch 'main' into chore/sample-flask 2024-06-05 14:24:43 +00:00
Prashant Shahi
b57a24a177 Merge pull request #5151 from SigNoz/release/v0.47.x
Release/v0.47.x
2024-06-05 19:51:47 +05:30
Prashant Shahi
a6e005e3a2 Merge branch 'develop' into release/v0.47.x 2024-06-05 19:43:32 +05:30
Prashant Shahi
4d375e7cc3 chore(signoz): 📌 pin versions: SigNoz 0.47.0, SigNoz OtelCollector 0.88.26
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-06-05 19:12:50 +05:30
Prashant Shahi
4fd862e7ff Merge branch 'main' into chore/sample-flask 2024-05-23 15:34:22 +00:00
Prashant Shahi
950eb99de0 Merge branch 'main' into chore/sample-flask 2024-05-20 03:59:18 +00:00
Prashant Shahi
078aaafc57 Merge branch 'main' into chore/sample-flask 2024-04-15 15:44:24 +00:00
Prashant Shahi
04564f63a8 Merge branch 'main' into chore/sample-flask 2024-03-27 18:42:25 +00:00
Prashant Shahi
862b8b92bd Merge branch 'main' into chore/sample-flask 2024-02-28 15:37:03 +00:00
Prashant Shahi
7b0f5976b4 Merge branch 'main' into chore/sample-flask 2024-02-21 14:25:08 +00:00
Prashant Shahi
b5d01705df Merge branch 'main' into chore/sample-flask 2024-02-14 18:10:09 +00:00
Prashant Shahi
8f1fecc5ba Merge branch 'main' into chore/sample-flask 2024-01-31 17:18:27 +00:00
Prashant Shahi
902eead23c Merge branch 'main' into chore/sample-flask 2024-01-19 22:38:36 +00:00
Prashant Shahi
ffa414fc00 Merge branch 'main' into chore/sample-flask 2023-12-22 13:15:46 +00:00
Prashant Shahi
aa3f1bae62 Merge branch 'main' into chore/sample-flask 2023-12-14 07:25:09 +00:00
Prashant Shahi
16ef43b6ea Merge branch 'main' into chore/sample-flask 2023-12-07 07:35:14 +00:00
Prashant Shahi
e24c1bd400 Merge branch 'main' into chore/sample-flask 2023-11-30 04:32:50 +00:00
Prashant Shahi
6a3054d871 Merge branch 'main' into chore/sample-flask 2023-11-23 17:31:47 +00:00
Prashant Shahi
6bebed62b7 Merge branch 'main' into chore/sample-flask 2023-11-22 17:17:38 +00:00
Prashant Shahi
a7a114cac0 Merge branch 'main' into chore/sample-flask 2023-11-21 13:04:40 +00:00
Prashant Shahi
7ce5fae681 Merge branch 'main' into chore/sample-flask 2023-11-16 20:18:38 +00:00
Prashant Shahi
d1aaa49815 Merge branch 'main' into chore/sample-flask 2023-11-02 19:07:59 +00:00
Prashant Shahi
7a6e0bf87f Merge branch 'main' into chore/sample-flask 2023-11-01 18:24:46 +00:00
Prashant Shahi
a5cfb3abc7 Merge branch 'main' into chore/sample-flask 2023-10-25 18:48:32 +00:00
Prashant Shahi
f732328cf5 Merge branch 'main' into chore/sample-flask 2023-10-23 17:22:34 +00:00
Prashant Shahi
9fa37afab2 Merge branch 'main' into chore/sample-flask 2023-10-13 02:36:12 +00:00
Prashant Shahi
cc35f7db3e Merge branch 'main' into chore/sample-flask 2023-09-27 20:37:56 +00:00
Prashant Shahi
861b0646d6 Merge branch 'main' into chore/sample-flask 2023-09-21 06:35:25 +00:00
Prashant Shahi
cd4682e67e Merge branch 'main' into chore/sample-flask 2023-09-19 17:41:24 +00:00
Prashant Shahi
32baa11beb Merge branch 'main' into chore/sample-flask 2023-09-18 14:48:17 +00:00
Prashant Shahi
8ae04f79fd Merge branch 'main' into chore/sample-flask 2023-09-14 14:20:25 +00:00
Prashant Shahi
ddd1c8e9ff Merge branch 'main' into chore/sample-flask 2023-09-04 12:30:15 +00:00
Prashant Shahi
cb063893b2 Merge branch 'main' into chore/sample-flask 2023-08-24 11:39:32 +00:00
Prashant Shahi
8898f6707f chore: update install.sh script 2023-08-18 13:29:03 +00:00
Prashant Shahi
bd8f16c5fd Merge branch 'main' into chore/sample-flask 2023-08-18 13:28:01 +00:00
Prashant Shahi
1431df9c9d Merge branch 'main' into chore/sample-flask 2023-08-18 03:50:33 +00:00
Prashant Shahi
5df1234892 Merge branch 'main' into chore/sample-flask 2023-08-11 10:30:29 +00:00
Prashant Shahi
0d24f5b428 Merge branch 'main' into chore/sample-flask 2023-08-11 08:22:11 +00:00
Prashant Shahi
55b70ae215 Merge branch 'main' into chore/sample-flask 2023-08-06 16:32:47 +00:00
Prashant Shahi
1ca3957ecb Merge branch 'main' into chore/sample-flask 2023-08-04 13:29:12 +00:00
Prashant Shahi
6be97bcb60 Merge branch 'main' into chore/sample-flask 2023-08-02 19:43:54 +00:00
Prashant Shahi
ac6ea3872c Merge branch 'main' into chore/sample-flask 2023-07-31 02:18:50 +00:00
Prashant Shahi
e33e3f89ac Merge branch 'main' into chore/sample-flask 2023-07-20 11:32:18 +00:00
Prashant Shahi
4bc4e0ed12 Merge branch 'main' into chore/sample-flask 2023-07-18 09:02:02 +00:00
Prashant Shahi
5b29c5989e Merge branch 'main' into chore/sample-flask 2023-07-15 11:14:52 +00:00
Prashant Shahi
2477612eef Merge branch 'main' into chore/sample-flask 2023-07-05 20:12:13 +00:00
Prashant Shahi
5df2c491cf Merge branch 'main' into chore/sample-flask 2023-06-24 06:50:56 +00:00
Prashant Shahi
0639ea8d17 Merge branch 'main' into chore/sample-flask 2023-06-09 12:13:01 +00:00
Prashant Shahi
19ebf81524 Merge branch 'main' into chore/sample-flask 2023-06-08 14:58:26 +00:00
Prashant Shahi
278a19a558 Merge branch 'main' into chore/sample-flask 2023-05-21 18:32:47 +00:00
Prashant Shahi
0b9ee9c3fd Merge branch 'main' into chore/sample-flask 2023-05-04 08:04:35 +00:00
Prashant Shahi
2da99428ae chore: 🔧 reduce locust interval
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2023-04-23 15:09:30 +00:00
Hello SigNoz
e5b02cb8d9 Merge branch 'main' into chore/sample-flask 2023-04-22 12:50:58 +00:00
Prashant Shahi
88c02e713d chore: 🙈 update gitignore file to ignore all pycache
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2023-04-05 11:17:25 +05:45
Prashant Shahi
8a395ce8e5 chore: 🔧 update locustfile to perform CRUD
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2023-04-05 11:16:12 +05:45
Prashant Shahi
8fb3e0ed54 chore: 🔧 sample-flask docker-compose YAML with exception generator
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2023-04-05 01:26:58 +05:45
667 changed files with 35265 additions and 3959 deletions

6
.github/CODEOWNERS vendored
View File

@@ -5,6 +5,6 @@
/frontend/ @YounixM
/frontend/src/container/MetricsApplication @srikanthccv
/frontend/src/container/NewWidget/RightContainer/types.ts @srikanthccv
/deploy/ @prashant-shahi
/sample-apps/ @prashant-shahi
.github @prashant-shahi
/deploy/ @SigNoz/devops
/sample-apps/ @SigNoz/devops
.github @SigNoz/devops

View File

@@ -30,6 +30,7 @@ jobs:
GCP_PROJECT: ${{ secrets.GCP_PROJECT }}
GCP_ZONE: ${{ secrets.GCP_ZONE }}
GCP_INSTANCE: ${{ secrets.GCP_INSTANCE }}
CLOUDSDK_CORE_DISABLE_PROMPTS: 1
run: |
read -r -d '' COMMAND <<EOF || true
echo "GITHUB_BRANCH: ${GITHUB_BRANCH}"
@@ -51,4 +52,4 @@ jobs:
make build-frontend-amd64
make run-testing
EOF
gcloud compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}"
gcloud beta compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}"

View File

@@ -30,6 +30,7 @@ jobs:
GCP_PROJECT: ${{ secrets.GCP_PROJECT }}
GCP_ZONE: ${{ secrets.GCP_ZONE }}
GCP_INSTANCE: ${{ secrets.GCP_INSTANCE }}
CLOUDSDK_CORE_DISABLE_PROMPTS: 1
run: |
read -r -d '' COMMAND <<EOF || true
echo "GITHUB_BRANCH: ${GITHUB_BRANCH}"
@@ -52,4 +53,4 @@ jobs:
make build-frontend-amd64
make run-testing
EOF
gcloud compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}"
gcloud beta compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}"

5
.gitignore vendored
View File

@@ -34,7 +34,7 @@ frontend/src/constants/env.ts
**/.vscode
**/build
**/storage
**/locust-scripts/__pycache__/
**/__pycache__/
**/__debug_bin
.env
@@ -67,3 +67,6 @@ e2e/.auth
# go
vendor/
**/main/**
# git-town
.git-branches.toml

View File

@@ -198,14 +198,14 @@ Not sure how to get started? Just ping us on `#contributing` in our [slack commu
#### Frontend
- [Palash Gupta](https://github.com/palashgdev)
- [Yunus M](https://github.com/YounixM)
- [Rajat Dabade](https://github.com/Rajat-Dabade)
- [Vikrant Gupta](https://github.com/vikrantgupta25)
- [Sagar Rajput](https://github.com/SagarRajput-7)
#### DevOps
- [Prashant Shahi](https://github.com/prashant-shahi)
- [Dhawal Sanghvi](https://github.com/dhawal1248)
- [Vibhu Pandey](https://github.com/grandwizard28)
<br /><br />

View File

@@ -649,12 +649,12 @@
See https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/replication/#creating-replicated-tables
-->
<!--
<macros>
<shard>01</shard>
<replica>example01-01-1</replica>
</macros>
-->
<!-- Reloading interval for embedded dictionaries, in seconds. Default: 3600. -->

View File

@@ -146,7 +146,7 @@ services:
condition: on-failure
query-service:
image: signoz/query-service:0.46.0
image: signoz/query-service:0.53.0
command:
[
"-config=/root/config/prometheus.yml",
@@ -186,7 +186,7 @@ services:
<<: *db-depend
frontend:
image: signoz/frontend:0.46.0
image: signoz/frontend:0.53.0
deploy:
restart_policy:
condition: on-failure
@@ -199,7 +199,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/signoz-otel-collector:0.88.24
image: signoz/signoz-otel-collector:0.102.7
command:
[
"--config=/etc/otel-collector-config.yaml",
@@ -211,6 +211,7 @@ services:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /:/hostfs:ro
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name={{.Node.Hostname}},os.type={{.Node.Platform.OS}},dockerswarm.service.name={{.Service.Name}},dockerswarm.task.name={{.Task.Name}}
- DOCKER_MULTI_NODE_CLUSTER=false
@@ -237,7 +238,7 @@ services:
- query-service
otel-collector-migrator:
image: signoz/signoz-schema-migrator:0.88.24
image: signoz/signoz-schema-migrator:0.102.7
deploy:
restart_policy:
condition: on-failure

View File

@@ -36,6 +36,7 @@ receivers:
# endpoint: 0.0.0.0:6832
hostmetrics:
collection_interval: 30s
root_path: /hostfs
scrapers:
cpu: {}
load: {}

View File

@@ -649,12 +649,12 @@
See https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/replication/#creating-replicated-tables
-->
<!--
<macros>
<shard>01</shard>
<replica>example01-01-1</replica>
</macros>
-->
<!-- Reloading interval for embedded dictionaries, in seconds. Default: 3600. -->

View File

@@ -66,7 +66,7 @@ services:
- --storage.path=/data
otel-collector-migrator:
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.7}
container_name: otel-migrator
command:
- "--dsn=tcp://clickhouse:9000"
@@ -81,7 +81,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`
otel-collector:
container_name: signoz-otel-collector
image: signoz/signoz-otel-collector:0.88.24
image: signoz/signoz-otel-collector:0.102.7
command:
[
"--config=/etc/otel-collector-config.yaml",
@@ -93,6 +93,8 @@ services:
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /:/hostfs:ro
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
ports:

View File

@@ -0,0 +1,42 @@
version: "2.4"
services:
mongodb:
image: "mongo:latest"
container_name: mongodb
hostname: mongodb
restart: always
# environment:
# MONGO_INITDB_ROOT_USERNAME: root
# MONGO_INITDB_ROOT_PASSWORD: example
# ports:
# - 27017:27017
sample-flask:
image: "signoz/sample-flask-app:latest"
container_name: sample-flask
hostname: sample-flask
restart: always
ports:
- 5002:5002
extra_hosts:
- signoz:host-gateway
environment:
MONGO_HOST: mongodb
OTEL_RESOURCE_ATTRIBUTES: service.name=sample-flask
OTEL_EXPORTER_OTLP_ENDPOINT: http://signoz:4317
load-flask:
image: "grubykarol/locust:1.2.3-python3.9-alpine3.12"
container_name: load-flask
hostname: load-flask
restart: always
environment:
ATTACKED_HOST: http://sample-flask:5002
LOCUST_MODE: standalone
NO_PROXY: standalone
TASK_DELAY_FROM: 45
TASK_DELAY_TO: 60
QUIET_MODE: "${QUIET_MODE:-false}"
LOCUST_OPTS: "--headless -u 5 -r 5"
volumes:
- ../common/locust-flask:/locust

View File

@@ -164,7 +164,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:${DOCKER_TAG:-0.46.0}
image: signoz/query-service:${DOCKER_TAG:-0.53.0}
container_name: signoz-query-service
command:
[
@@ -204,7 +204,7 @@ services:
<<: *db-depend
frontend:
image: signoz/frontend:${DOCKER_TAG:-0.46.0}
image: signoz/frontend:${DOCKER_TAG:-0.53.0}
container_name: signoz-frontend
restart: on-failure
depends_on:
@@ -216,7 +216,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector-migrator:
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.7}
container_name: otel-migrator
command:
- "--dsn=tcp://clickhouse:9000"
@@ -230,7 +230,7 @@ services:
otel-collector:
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.7}
container_name: signoz-otel-collector
command:
[
@@ -244,6 +244,7 @@ services:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /:/hostfs:ro
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
- DOCKER_MULTI_NODE_CLUSTER=false

View File

@@ -164,7 +164,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:${DOCKER_TAG:-0.46.0}
image: signoz/query-service:${DOCKER_TAG:-0.53.0}
container_name: signoz-query-service
command:
[
@@ -203,7 +203,7 @@ services:
<<: *db-depend
frontend:
image: signoz/frontend:${DOCKER_TAG:-0.46.0}
image: signoz/frontend:${DOCKER_TAG:-0.53.0}
container_name: signoz-frontend
restart: on-failure
depends_on:
@@ -215,7 +215,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector-migrator:
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.102.7}
container_name: otel-migrator
command:
- "--dsn=tcp://clickhouse:9000"
@@ -229,7 +229,7 @@ services:
otel-collector:
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.102.7}
container_name: signoz-otel-collector
command:
[
@@ -243,6 +243,7 @@ services:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /:/hostfs:ro
environment:
- OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux
- DOCKER_MULTI_NODE_CLUSTER=false

View File

@@ -36,6 +36,7 @@ receivers:
# endpoint: 0.0.0.0:6832
hostmetrics:
collection_interval: 30s
root_path: /hostfs
scrapers:
cpu: {}
load: {}

View File

@@ -0,0 +1,20 @@
from locust import HttpUser, task, between
from uuid import uuid4
class UserTasks(HttpUser):
wait_time = between(30, 60)
@task(1)
def list(self):
self.client.get("/list")
@task(1)
def add_todo(self):
self.client.post("/action", data={"name": "new-todo-"+str(uuid4()), "desc":"new desc", "date": "1990-04-10", "pr":"1"})
@task(1)
def update(self):
self.client.post("/action3", data={"_id":"626682d44bd2839cd80eb079", "name":"todo-"+str(uuid4()), "desc": "update desc", "date": "1990-04-11", "pr":"2"})
@task(1)
def generate_error(self):
self.client.get("/generate-error")

View File

@@ -1,3 +1,8 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 3301;
server_name _;
@@ -42,6 +47,14 @@ server {
proxy_read_timeout 600s;
}
location /ws {
proxy_pass http://query-service:8080/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade "websocket";
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;

View File

@@ -494,7 +494,7 @@ fi
start_docker
# $sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up -d --remove-orphans || true
# $sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml -f ./docker/clickhouse-setup/docker-compose-flask.yaml up -d --remove-orphans || true
echo ""
@@ -506,7 +506,7 @@ echo "🟡 Starting the SigNoz containers. It may take a few minutes ..."
echo
# The docker-compose command does some nasty stuff for the `--detach` functionality. So we add a `|| true` so that the
# script doesn't exit because this command looks like it failed to do it's thing.
$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml up --detach --remove-orphans || true
$sudo_cmd docker-compose -f ./docker/clickhouse-setup/docker-compose.yaml -f ./docker/clickhouse-setup/docker-compose-flask.yaml up --detach --remove-orphans || true
wait_for_containers_start 60
echo ""

View File

@@ -1,7 +1,9 @@
package api
import (
"errors"
"net/http"
"strings"
"github.com/gorilla/mux"
"go.signoz.io/signoz/pkg/query-service/app/dashboards"
@@ -29,6 +31,10 @@ func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request
// Get the dashboard UUID from the request
uuid := mux.Vars(r)["uuid"]
if strings.HasPrefix(uuid,"integration") {
RespondError(w, &model.ApiError{Typ: model.ErrorForbidden, Err: errors.New("dashboards created by integrations cannot be unlocked")}, "You are not authorized to lock/unlock this dashboard")
return
}
dashboard, err := dashboards.GetDashboard(r.Context(), uuid)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error())

View File

@@ -1,17 +1,48 @@
package api
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"
"go.signoz.io/signoz/ee/query-service/constants"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
"go.uber.org/zap"
)
func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
featureSet, err := ah.FF().GetFeatureFlags()
if err != nil {
ah.HandleError(w, err, http.StatusInternalServerError)
return
}
if constants.FetchFeatures == "true" {
zap.L().Debug("fetching license")
license, err := ah.LM().GetRepo().GetActiveLicense(ctx)
if err != nil {
zap.L().Error("failed to fetch license", zap.Error(err))
} else if license == nil {
zap.L().Debug("no active license found")
} else {
licenseKey := license.Key
zap.L().Debug("fetching zeus features")
zeusFeatures, err := fetchZeusFeatures(constants.ZeusFeaturesURL, licenseKey)
if err == nil {
zap.L().Debug("fetched zeus features", zap.Any("features", zeusFeatures))
// merge featureSet and zeusFeatures in featureSet with higher priority to zeusFeatures
featureSet = MergeFeatureSets(zeusFeatures, featureSet)
} else {
zap.L().Error("failed to fetch zeus features", zap.Error(err))
}
}
}
if ah.opts.PreferSpanMetrics {
for idx := range featureSet {
feature := &featureSet[idx]
@@ -20,5 +51,96 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
}
}
}
ah.Respond(w, featureSet)
}
// fetchZeusFeatures makes an HTTP GET request to the /zeusFeatures endpoint
// and returns the FeatureSet.
func fetchZeusFeatures(url, licenseKey string) (basemodel.FeatureSet, error) {
// Check if the URL is empty
if url == "" {
return nil, fmt.Errorf("url is empty")
}
// Check if the licenseKey is empty
if licenseKey == "" {
return nil, fmt.Errorf("licenseKey is empty")
}
// Creating an HTTP client with a timeout for better control
client := &http.Client{
Timeout: 10 * time.Second,
}
// Creating a new GET request
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
// Setting the custom header
req.Header.Set("X-Signoz-Cloud-Api-Key", licenseKey)
// Making the GET request
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to make GET request: %w", err)
}
defer func() {
if resp != nil {
resp.Body.Close()
}
}()
// Check for non-OK status code
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: %d %s", errors.New("received non-OK HTTP status code"), resp.StatusCode, http.StatusText(resp.StatusCode))
}
// Reading and decoding the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
var zeusResponse ZeusFeaturesResponse
if err := json.Unmarshal(body, &zeusResponse); err != nil {
return nil, fmt.Errorf("%w: %v", errors.New("failed to decode response body"), err)
}
if zeusResponse.Status != "success" {
return nil, fmt.Errorf("%w: %s", errors.New("failed to fetch zeus features"), zeusResponse.Status)
}
return zeusResponse.Data, nil
}
type ZeusFeaturesResponse struct {
Status string `json:"status"`
Data basemodel.FeatureSet `json:"data"`
}
// MergeFeatureSets merges two FeatureSet arrays with precedence to zeusFeatures.
func MergeFeatureSets(zeusFeatures, internalFeatures basemodel.FeatureSet) basemodel.FeatureSet {
// Create a map to store the merged features
featureMap := make(map[string]basemodel.Feature)
// Add all features from the otherFeatures set to the map
for _, feature := range internalFeatures {
featureMap[feature.Name] = feature
}
// Add all features from the zeusFeatures set to the map
// If a feature already exists (i.e., same name), the zeusFeature will overwrite it
for _, feature := range zeusFeatures {
featureMap[feature.Name] = feature
}
// Convert the map back to a FeatureSet slice
var mergedFeatures basemodel.FeatureSet
for _, feature := range featureMap {
mergedFeatures = append(mergedFeatures, feature)
}
return mergedFeatures
}

View File

@@ -0,0 +1,88 @@
package api
import (
"testing"
"github.com/stretchr/testify/assert"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
)
func TestMergeFeatureSets(t *testing.T) {
tests := []struct {
name string
zeusFeatures basemodel.FeatureSet
internalFeatures basemodel.FeatureSet
expected basemodel.FeatureSet
}{
{
name: "empty zeusFeatures and internalFeatures",
zeusFeatures: basemodel.FeatureSet{},
internalFeatures: basemodel.FeatureSet{},
expected: basemodel.FeatureSet{},
},
{
name: "non-empty zeusFeatures and empty internalFeatures",
zeusFeatures: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature2", Active: false},
},
internalFeatures: basemodel.FeatureSet{},
expected: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature2", Active: false},
},
},
{
name: "empty zeusFeatures and non-empty internalFeatures",
zeusFeatures: basemodel.FeatureSet{},
internalFeatures: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature2", Active: false},
},
expected: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature2", Active: false},
},
},
{
name: "non-empty zeusFeatures and non-empty internalFeatures with no conflicts",
zeusFeatures: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature3", Active: false},
},
internalFeatures: basemodel.FeatureSet{
{Name: "Feature2", Active: true},
{Name: "Feature4", Active: false},
},
expected: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature2", Active: true},
{Name: "Feature3", Active: false},
{Name: "Feature4", Active: false},
},
},
{
name: "non-empty zeusFeatures and non-empty internalFeatures with conflicts",
zeusFeatures: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature2", Active: false},
},
internalFeatures: basemodel.FeatureSet{
{Name: "Feature1", Active: false},
{Name: "Feature3", Active: true},
},
expected: basemodel.FeatureSet{
{Name: "Feature1", Active: true},
{Name: "Feature2", Active: false},
{Name: "Feature3", Active: true},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := MergeFeatureSets(test.zeusFeatures, test.internalFeatures)
assert.ElementsMatch(t, test.expected, actual)
})
}
}

View File

@@ -1,14 +1,15 @@
package app
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
_ "net/http/pprof" // http profiler
"os"
"regexp"
@@ -28,6 +29,8 @@ import (
"go.signoz.io/signoz/ee/query-service/integrations/gateway"
"go.signoz.io/signoz/ee/query-service/interfaces"
baseauth "go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/migrate"
"go.signoz.io/signoz/pkg/query-service/model"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
licensepkg "go.signoz.io/signoz/ee/query-service/license"
@@ -41,6 +44,7 @@ import (
"go.signoz.io/signoz/pkg/query-service/app/logparsingpipeline"
"go.signoz.io/signoz/pkg/query-service/app/opamp"
opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
"go.signoz.io/signoz/pkg/query-service/app/preferences"
"go.signoz.io/signoz/pkg/query-service/cache"
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/healthcheck"
@@ -110,6 +114,10 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
baseexplorer.InitWithDSN(baseconst.RELATIONAL_DATASOURCE_PATH)
if err := preferences.InitDB(baseconst.RELATIONAL_DATASOURCE_PATH); err != nil {
return nil, err
}
localDB, err := dashboards.InitDB(baseconst.RELATIONAL_DATASOURCE_PATH)
if err != nil {
@@ -118,33 +126,13 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
localDB.SetMaxOpenConns(10)
gatewayFeature := basemodel.Feature{
Name: "GATEWAY",
Active: false,
Usage: 0,
UsageLimit: -1,
Route: "",
}
//Activate this feature if the url is not empty
var gatewayProxy *httputil.ReverseProxy
if serverOptions.GatewayUrl == "" {
gatewayFeature.Active = false
gatewayProxy, err = gateway.NewNoopProxy()
if err != nil {
return nil, err
}
} else {
zap.L().Info("Enabling gateway feature flag ...")
gatewayFeature.Active = true
gatewayProxy, err = gateway.NewProxy(serverOptions.GatewayUrl, gateway.RoutePrefix)
if err != nil {
return nil, err
}
gatewayProxy, err := gateway.NewProxy(serverOptions.GatewayUrl, gateway.RoutePrefix)
if err != nil {
return nil, err
}
// initiate license manager
lm, err := licensepkg.StartManager("sqlite", localDB, gatewayFeature)
lm, err := licensepkg.StartManager("sqlite", localDB)
if err != nil {
return nil, err
}
@@ -193,6 +181,13 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
return nil, err
}
go func() {
err = migrate.ClickHouseMigrate(reader.GetConn(), serverOptions.Cluster)
if err != nil {
zap.L().Error("error while running clickhouse migrations", zap.Error(err))
}
}()
// initiate opamp
_, err = opAmpModel.InitDB(localDB)
if err != nil {
@@ -323,7 +318,7 @@ func (s *Server) createPrivateServer(apiHandler *api.APIHandler) (*http.Server,
// ip here for alert manager
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "DELETE", "POST", "PUT", "PATCH"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "SIGNOZ-API-KEY"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "SIGNOZ-API-KEY", "X-SIGNOZ-QUERY-ID", "Sec-WebSocket-Protocol"},
})
handler := c.Handler(r)
@@ -340,7 +335,17 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, e
// add auth middleware
getUserFromRequest := func(r *http.Request) (*basemodel.UserPayload, error) {
return auth.GetUserFromRequest(r, apiHandler)
user, err := auth.GetUserFromRequest(r, apiHandler)
if err != nil {
return nil, err
}
if user.User.OrgId == "" {
return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims"))
}
return user, nil
}
am := baseapp.NewAuthMiddleware(getUserFromRequest)
@@ -354,11 +359,13 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, e
apiHandler.RegisterIntegrationRoutes(r, am)
apiHandler.RegisterQueryRangeV3Routes(r, am)
apiHandler.RegisterQueryRangeV4Routes(r, am)
apiHandler.RegisterWebSocketPaths(r, am)
apiHandler.RegisterMessagingQueuesRoutes(r, am)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "DELETE", "POST", "PUT", "PATCH", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "cache-control"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "cache-control", "X-SIGNOZ-QUERY-ID", "Sec-WebSocket-Protocol"},
})
handler := c.Handler(r)
@@ -370,6 +377,7 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler) (*http.Server, e
}, nil
}
// TODO(remove): Implemented at pkg/http/middleware/logging.go
// loggingMiddleware is used for logging public api calls
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -381,6 +389,7 @@ func loggingMiddleware(next http.Handler) http.Handler {
})
}
// TODO(remove): Implemented at pkg/http/middleware/logging.go
// loggingMiddlewarePrivate is used for logging private api calls
// from internal services like alert manager
func loggingMiddlewarePrivate(next http.Handler) http.Handler {
@@ -393,27 +402,41 @@ func loggingMiddlewarePrivate(next http.Handler) http.Handler {
})
}
// TODO(remove): Implemented at pkg/http/middleware/logging.go
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
}
// TODO(remove): Implemented at pkg/http/middleware/logging.go
func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
// WriteHeader(int) is not called if our response implicitly returns 200 OK, so
// we default to that status code.
return &loggingResponseWriter{w, http.StatusOK}
}
// TODO(remove): Implemented at pkg/http/middleware/logging.go
func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
// TODO(remove): Implemented at pkg/http/middleware/logging.go
// Flush implements the http.Flush interface.
func (lrw *loggingResponseWriter) Flush() {
lrw.ResponseWriter.(http.Flusher).Flush()
}
// TODO(remove): Implemented at pkg/http/middleware/logging.go
// Support websockets
func (lrw *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
h, ok := lrw.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, errors.New("hijack not supported")
}
return h.Hijack()
}
func extractQueryRangeData(path string, r *http.Request) (map[string]interface{}, bool) {
pathToExtractBodyFromV3 := "/api/v3/query_range"
pathToExtractBodyFromV4 := "/api/v4/query_range"
@@ -550,6 +573,7 @@ func (s *Server) analyticsMiddleware(next http.Handler) http.Handler {
})
}
// TODO(remove): Implemented at pkg/http/middleware/timeout.go
func setTimeoutMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@@ -732,6 +756,7 @@ func makeRulesManager(
DisableRules: disableRules,
FeatureFlags: fm,
Reader: ch,
EvalDelay: baseconst.GetEvalDelay(),
}
// create Manager

View File

@@ -13,6 +13,8 @@ var LicenseAPIKey = GetOrDefaultEnv("SIGNOZ_LICENSE_API_KEY", "")
var SaasSegmentKey = GetOrDefaultEnv("SIGNOZ_SAAS_SEGMENT_KEY", "")
var SpanRenderLimitStr = GetOrDefaultEnv("SPAN_RENDER_LIMIT", "2500")
var MaxSpansInTraceStr = GetOrDefaultEnv("MAX_SPANS_IN_TRACE", "250000")
var FetchFeatures = GetOrDefaultEnv("FETCH_FEATURES", "false")
var ZeusFeaturesURL = GetOrDefaultEnv("ZEUS_FEATURES_URL", "ZeusFeaturesURL")
func GetOrDefaultEnv(key string, fallback string) string {
v := os.Getenv(key)

View File

@@ -20,11 +20,14 @@ import (
func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*basemodel.User, basemodel.BaseApiError) {
// get auth domain from email domain
domain, apierr := m.GetDomainByEmail(ctx, email)
if apierr != nil {
zap.L().Error("failed to get domain from email", zap.Error(apierr))
return nil, model.InternalErrorStr("failed to get domain from email")
}
if domain == nil {
zap.L().Error("email domain does not match any authenticated domain", zap.String("email", email))
return nil, model.InternalErrorStr("email domain does not match any authenticated domain")
}
hash, err := baseauth.PasswordHash(utils.GeneratePassowrd())
if err != nil {

View File

@@ -5,5 +5,5 @@ import (
)
func NewNoopProxy() (*httputil.ReverseProxy, error) {
return nil, nil
return &httputil.ReverseProxy{}, nil
}

View File

@@ -147,7 +147,7 @@ func (lm *Manager) GetLicenses(ctx context.Context) (response []model.License, a
for _, l := range licenses {
l.ParsePlan()
if l.Key == lm.activeLicense.Key {
if lm.activeLicense != nil && l.Key == lm.activeLicense.Key {
l.IsCurrent = true
}

View File

@@ -11,6 +11,8 @@ const Enterprise = "ENTERPRISE_PLAN"
const DisableUpsell = "DISABLE_UPSELL"
const Onboarding = "ONBOARDING"
const ChatSupport = "CHAT_SUPPORT"
const Gateway = "GATEWAY"
const PremiumSupport = "PREMIUM_SUPPORT"
var BasicPlan = basemodel.FeatureSet{
basemodel.Feature{
@@ -111,6 +113,20 @@ var BasicPlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: Gateway,
Active: false,
Usage: 0,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: PremiumSupport,
Active: false,
Usage: 0,
UsageLimit: -1,
Route: "",
},
}
var ProPlan = basemodel.FeatureSet{
@@ -205,6 +221,20 @@ var ProPlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: Gateway,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: PremiumSupport,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
}
var EnterprisePlan = basemodel.FeatureSet{
@@ -313,4 +343,18 @@ var EnterprisePlan = basemodel.FeatureSet{
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: Gateway,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: PremiumSupport,
Active: true,
Usage: 0,
UsageLimit: -1,
Route: "",
},
}

View File

@@ -9,6 +9,7 @@ const config: Config.InitialOptions = {
modulePathIgnorePatterns: ['dist'],
moduleNameMapper: {
'\\.(css|less|scss)$': '<rootDir>/__mocks__/cssMock.ts',
'\\.md$': '<rootDir>/__mocks__/cssMock.ts',
},
globals: {
extensionsToTreatAsEsm: ['.ts'],

View File

@@ -88,7 +88,7 @@
"lucide-react": "0.379.0",
"mini-css-extract-plugin": "2.4.5",
"papaparse": "5.4.1",
"posthog-js": "1.140.1",
"posthog-js": "1.142.1",
"rc-tween-one": "3.0.6",
"react": "18.2.0",
"react-addons-update": "15.6.3",
@@ -110,6 +110,8 @@
"react-syntax-highlighter": "15.5.0",
"react-use": "^17.3.2",
"react-virtuoso": "4.0.3",
"overlayscrollbars-react": "^0.5.6",
"overlayscrollbars": "^2.8.1",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"rehype-raw": "7.0.0",

View File

@@ -0,0 +1 @@
<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#prefix__clip0_4344_1236)" stroke="#C0C1C3" stroke-width="1.167" stroke-linecap="round" stroke-linejoin="round"><path d="M4.667 1.167H2.333c-.644 0-1.166.522-1.166 1.166v2.334c0 .644.522 1.166 1.166 1.166h2.334c.644 0 1.166-.522 1.166-1.166V2.333c0-.644-.522-1.166-1.166-1.166zM8.167 1.167a1.17 1.17 0 011.166 1.166v2.334a1.17 1.17 0 01-1.166 1.166M11.667 1.167a1.17 1.17 0 011.166 1.166v2.334a1.17 1.17 0 01-1.166 1.166M5.833 10.5H2.917c-.992 0-1.75-.758-1.75-1.75v-.583"/><path d="M4.083 12.25l1.75-1.75-1.75-1.75M11.667 8.167H9.333c-.644 0-1.166.522-1.166 1.166v2.334c0 .644.522 1.166 1.166 1.166h2.334c.644 0 1.166-.522 1.166-1.166V9.333c0-.644-.522-1.166-1.166-1.166z"/></g><defs><clipPath id="prefix__clip0_4344_1236"><path fill="#fff" d="M0 0h14v14H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 878 B

View File

@@ -0,0 +1 @@
<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#prefix__clip0_4062_7291)" stroke-width="1.167" stroke-linecap="round" stroke-linejoin="round"><path d="M7 12.833A5.833 5.833 0 107 1.167a5.833 5.833 0 000 11.666z" fill="#E5484D" stroke="#E5484D"/><path d="M8.75 5.25l-3.5 3.5M5.25 5.25l3.5 3.5" stroke="#121317"/></g><defs><clipPath id="prefix__clip0_4062_7291"><path fill="#fff" d="M0 0h14v14H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

View File

@@ -0,0 +1,8 @@
{
"invite_user": "Invite your teammates",
"invite": "Invite",
"skip": "Skip",
"invite_user_helper_text": "Not the right person to get started? No worries! Invite someone who can.",
"select_use_case": "Select a use-case to get started",
"get_started": "Get Started"
}

View File

@@ -6,5 +6,6 @@
"share": "Share",
"save": "Save",
"edit": "Edit",
"logged_in": "Logged In"
"logged_in": "Logged In",
"pending_data_placeholder": "Just a bit of patience, just a little bits enough ⎯ were getting your {{dataSource}}!"
}

View File

@@ -1,6 +1,7 @@
{
"create_dashboard": "Create Dashboard",
"import_json": "Import Dashboard JSON",
"view_template": "View templates",
"import_grafana_json": "Import Grafana JSON",
"copy_to_clipboard": "Copy To ClipBoard",
"download_json": "Download JSON",

View File

@@ -0,0 +1,8 @@
{
"invite_user": "Invite your teammates",
"invite": "Invite",
"skip": "Skip",
"invite_user_helper_text": "Not the right person to get started? No worries! Invite someone who can.",
"select_use_case": "Select a use-case to get started",
"get_started": "Get Started"
}

View File

@@ -49,5 +49,6 @@
"TRACES_SAVE_VIEWS": "SigNoz | Traces Saved Views",
"DEFAULT": "Open source Observability Platform | SigNoz",
"SHORTCUTS": "SigNoz | Shortcuts",
"INTEGRATIONS": "SigNoz | Integrations"
"INTEGRATIONS": "SigNoz | Integrations",
"MESSAGING_QUEUES": "SigNoz | Messaging Queues"
}

View File

@@ -76,9 +76,8 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
isUserFetching: false,
},
});
if (!isLoggedIn) {
history.push(ROUTES.LOGIN);
history.push(ROUTES.LOGIN, { from: pathname });
}
};

View File

@@ -1,6 +1,7 @@
import { ConfigProvider } from 'antd';
import getLocalStorageApi from 'api/browser/localstorage/get';
import setLocalStorageApi from 'api/browser/localstorage/set';
import logEvent from 'api/common/logEvent';
import NotFound from 'components/NotFound';
import Spinner from 'components/Spinner';
import { FeatureKeys } from 'constants/features';
@@ -48,7 +49,7 @@ function App(): JSX.Element {
const dispatch = useDispatch<Dispatch<AppActions>>();
const { trackPageView, trackEvent } = useAnalytics();
const { trackPageView } = useAnalytics();
const { hostname, pathname } = window.location;
@@ -65,6 +66,14 @@ function App(): JSX.Element {
allFlags.find((flag) => flag.name === FeatureKeys.CHAT_SUPPORT)?.active ||
false;
const isPremiumSupportEnabled =
allFlags.find((flag) => flag.name === FeatureKeys.PREMIUM_SUPPORT)?.active ||
false;
const showAddCreditCardModal =
!isPremiumSupportEnabled &&
!licenseData?.payload?.trialConvertedToSubscription;
dispatch({
type: UPDATE_FEATURE_FLAG_RESPONSE,
payload: {
@@ -81,7 +90,7 @@ function App(): JSX.Element {
setRoutes(newRoutes);
}
if (isLoggedInState && isChatSupportEnabled) {
if (isLoggedInState && isChatSupportEnabled && !showAddCreditCardModal) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.Intercom('boot', {
@@ -199,7 +208,7 @@ function App(): JSX.Element {
LOCALSTORAGE.THEME_ANALYTICS_V1,
);
if (!isThemeAnalyticsSent) {
trackEvent('Theme Analytics', {
logEvent('Theme Analytics', {
theme: isDarkMode ? THEME_MODE.DARK : THEME_MODE.LIGHT,
user: pick(user, ['email', 'userId', 'name']),
org,

View File

@@ -204,3 +204,15 @@ export const InstalledIntegrations = Loadable(
/* webpackChunkName: "InstalledIntegrations" */ 'pages/IntegrationsModulePage'
),
);
export const MessagingQueues = Loadable(
() =>
import(/* webpackChunkName: "MessagingQueues" */ 'pages/MessagingQueues'),
);
export const MQDetailPage = Loadable(
() =>
import(
/* webpackChunkName: "MQDetailPage" */ 'pages/MessagingQueues/MQDetailPage'
),
);

View File

@@ -23,6 +23,8 @@ import {
LogsExplorer,
LogsIndexToFields,
LogsSaveViews,
MessagingQueues,
MQDetailPage,
MySettings,
NewDashboardPage,
OldLogsExplorer,
@@ -351,6 +353,20 @@ const routes: AppRoutes[] = [
isPrivate: true,
key: 'INTEGRATIONS',
},
{
path: ROUTES.MESSAGING_QUEUES,
exact: true,
component: MessagingQueues,
key: 'MESSAGING_QUEUES',
isPrivate: true,
},
{
path: ROUTES.MESSAGING_QUEUES_DETAIL,
exact: true,
component: MQDetailPage,
key: 'MESSAGING_QUEUES_DETAIL',
isPrivate: true,
},
];
export const SUPPORT_ROUTE: AppRoutes = {

View File

@@ -9,9 +9,9 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
// making the error status code as standard Error Status Code
const statusCode = response.status as ErrorStatusCode;
if (statusCode >= 400 && statusCode < 500) {
const { data } = response as AxiosResponse;
const { data } = response as AxiosResponse;
if (statusCode >= 400 && statusCode < 500) {
if (statusCode === 404) {
return {
statusCode,
@@ -34,12 +34,11 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
body: JSON.stringify((response.data as any).data),
};
}
return {
statusCode,
payload: null,
error: 'Something went wrong',
message: null,
message: data?.error,
};
}
if (request) {

View File

@@ -3,7 +3,7 @@ const apiV1 = '/api/v1/';
export const apiV2 = '/api/v2/';
export const apiV3 = '/api/v3/';
export const apiV4 = '/api/v4/';
export const gatewayApiV1 = '/api/gateway/v1';
export const apiAlertManager = '/api/alertmanager';
export const gatewayApiV1 = '/api/gateway/v1/';
export const apiAlertManager = '/api/alertmanager/';
export default apiV1;

View File

@@ -0,0 +1,62 @@
import getLocalStorageApi from 'api/browser/localstorage/get';
import { ENVIRONMENT } from 'constants/env';
import { LOCALSTORAGE } from 'constants/localStorage';
import { isEmpty } from 'lodash-es';
export interface WsDataEvent {
read_rows: number;
read_bytes: number;
elapsed_ms: number;
}
interface GetQueryStatsProps {
queryId: string;
setData: React.Dispatch<React.SetStateAction<WsDataEvent | undefined>>;
}
function getURL(baseURL: string, queryId: string): URL | string {
if (baseURL && !isEmpty(baseURL)) {
return `${baseURL}/ws/query_progress?q=${queryId}`;
}
const url = new URL(`/ws/query_progress?q=${queryId}`, window.location.href);
if (window.location.protocol === 'http:') {
url.protocol = 'ws';
} else {
url.protocol = 'wss';
}
return url;
}
export function getQueryStats(props: GetQueryStatsProps): void {
const { queryId, setData } = props;
const token = getLocalStorageApi(LOCALSTORAGE.AUTH_TOKEN) || '';
// https://github.com/whatwg/websockets/issues/20 reason for not using the relative URLs
const url = getURL(ENVIRONMENT.wsURL, queryId);
const socket = new WebSocket(url, token);
socket.addEventListener('message', (event) => {
try {
const parsedData = JSON.parse(event?.data);
setData(parsedData);
} catch {
setData(event?.data);
}
});
socket.addEventListener('error', (event) => {
console.error(event);
});
socket.addEventListener('close', (event) => {
// 1000 is a normal closure status code
if (event.code !== 1000) {
console.error('WebSocket closed with error:', event);
} else {
console.error('WebSocket closed normally.');
}
});
}

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance as axios } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
@@ -21,6 +21,7 @@ const logEvent = async (
payload: response.data.data,
};
} catch (error) {
console.error(error);
return ErrorResponseHandler(error as AxiosError);
}
};

View File

@@ -1,6 +1,8 @@
import { ApiV2Instance as axios } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
import store from 'store';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
Props,
@@ -11,7 +13,26 @@ const dashboardVariablesQuery = async (
props: Props,
): Promise<SuccessResponse<VariableResponseProps> | ErrorResponse> => {
try {
const response = await axios.post(`/variables/query`, props);
const { globalTime } = store.getState();
const { start, end } = getStartEndRangeTime({
type: 'GLOBAL_TIME',
interval: globalTime.selectedTime,
});
const timeVariables: Record<string, number> = {
start_timestamp_ms: parseInt(start, 10) * 1e3,
end_timestamp_ms: parseInt(end, 10) * 1e3,
start_timestamp_nano: parseInt(start, 10) * 1e9,
end_timestamp_nano: parseInt(end, 10) * 1e9,
start_timestamp: parseInt(start, 10),
end_timestamp: parseInt(end, 10),
};
const payload = { ...props };
payload.variables = { ...payload.variables, ...timeVariables };
const response = await axios.post(`/variables/query`, payload);
return {
statusCode: 200,

View File

@@ -96,6 +96,10 @@ const interceptorRejected = async (
}
};
const interceptorRejectedBase = async (
value: AxiosResponse<any>,
): Promise<AxiosResponse<any>> => Promise.reject(value);
const instance = axios.create({
baseURL: `${ENVIRONMENT.baseURL}${apiV1}`,
});
@@ -140,6 +144,18 @@ ApiV4Instance.interceptors.response.use(
ApiV4Instance.interceptors.request.use(interceptorsRequestResponse);
//
// axios Base
export const ApiBaseInstance = axios.create({
baseURL: `${ENVIRONMENT.baseURL}${apiV1}`,
});
ApiBaseInstance.interceptors.response.use(
interceptorsResponse,
interceptorRejectedBase,
);
ApiBaseInstance.interceptors.request.use(interceptorsRequestResponse);
//
// gateway Api V1
export const GatewayApiV1Instance = axios.create({
baseURL: `${ENVIRONMENT.baseURL}${gatewayApiV1}`,

View File

@@ -12,10 +12,13 @@ export const getMetricsQueryRange = async (
props: QueryRangePayload,
version: string,
signal: AbortSignal,
headers?: Record<string, string>,
): Promise<SuccessResponse<MetricRangePayloadV3> | ErrorResponse> => {
try {
if (version && version === ENTITY_VERSION_V4) {
const response = await ApiV4Instance.post('/query_range', props, { signal });
const response = await ApiV4Instance.post('/query_range', props, {
signal,
});
return {
statusCode: 200,
@@ -26,7 +29,10 @@ export const getMetricsQueryRange = async (
};
}
const response = await ApiV3Instance.post('/query_range', props, { signal });
const response = await ApiV3Instance.post('/query_range', props, {
signal,
headers,
});
return {
statusCode: 200,

View File

@@ -1,7 +1,20 @@
import axios from 'api';
import { isNil } from 'lodash-es';
const getTopLevelOperations = async (): Promise<ServiceDataProps> => {
const response = await axios.post(`/service/top_level_operations`);
interface GetTopLevelOperationsProps {
service?: string;
start?: number;
end?: number;
}
const getTopLevelOperations = async (
props: GetTopLevelOperationsProps,
): Promise<ServiceDataProps> => {
const response = await axios.post(`/service/top_level_operations`, {
start: !isNil(props.start) ? `${props.start}` : undefined,
end: !isNil(props.end) ? `${props.end}` : undefined,
service: props.service,
});
return response.data;
};

View File

@@ -1,6 +1,7 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { Dayjs } from 'dayjs';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { Recurrence } from './getAllDowntimeSchedules';
@@ -11,8 +12,8 @@ export interface DowntimeSchedulePayload {
alertIds: string[];
schedule: {
timezone?: string;
startTime?: string;
endTime?: string;
startTime?: string | Dayjs;
endTime?: string | Dayjs;
recurrence?: Recurrence;
};
}

View File

@@ -1,6 +1,6 @@
import axios from 'api';
import { AxiosError, AxiosResponse } from 'axios';
import { Option } from 'container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu';
import { Option } from 'container/PlannedDowntime/PlannedDowntimeutils';
import { useQuery, UseQueryResult } from 'react-query';
export type Recurrence = {
@@ -28,6 +28,7 @@ export interface DowntimeSchedules {
createdBy: string | null;
updatedAt: string | null;
updatedBy: string | null;
kind: string | null;
}
export type PayloadProps = { data: DowntimeSchedules[] };

View File

@@ -0,0 +1,63 @@
import { ApiV3Instance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError, AxiosResponse } from 'axios';
import { baseAutoCompleteIdKeysOrder } from 'constants/queryBuilder';
import { encode } from 'js-base64';
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
import createQueryParams from 'lib/createQueryParams';
import { ErrorResponse, SuccessResponse } from 'types/api';
import {
IGetAttributeSuggestionsPayload,
IGetAttributeSuggestionsSuccessResponse,
} from 'types/api/queryBuilder/getAttributeSuggestions';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
export const getAttributeSuggestions = async ({
searchText,
dataSource,
filters,
}: IGetAttributeSuggestionsPayload): Promise<
SuccessResponse<IGetAttributeSuggestionsSuccessResponse> | ErrorResponse
> => {
try {
let base64EncodedFiltersString;
try {
// the replace function is to remove the padding at the end of base64 encoded string which is auto added to make it a multiple of 4
// why ? because the current working of qs doesn't work well with padding
base64EncodedFiltersString = encode(JSON.stringify(filters)).replace(
/=+$/,
'',
);
} catch {
// default base64 encoded string for empty filters object
base64EncodedFiltersString = 'eyJpdGVtcyI6W10sIm9wIjoiQU5EIn0';
}
const response: AxiosResponse<{
data: IGetAttributeSuggestionsSuccessResponse;
}> = await ApiV3Instance.get(
`/filter_suggestions?${createQueryParams({
searchText,
dataSource,
existingFilter: base64EncodedFiltersString,
})}`,
);
const payload: BaseAutocompleteData[] =
response.data.data.attributes?.map(({ id: _, ...item }) => ({
...item,
id: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
})) || [];
return {
statusCode: 200,
error: null,
message: response.statusText,
payload: {
attributes: payload,
example_queries: response.data.data.example_queries,
},
};
} catch (e) {
return ErrorResponseHandler(e as AxiosError);
}
};

View File

@@ -0,0 +1,27 @@
import { Color } from '@signozhq/design-tokens';
import { useIsDarkMode } from 'hooks/useDarkMode';
function GroupByIcon(): JSX.Element {
const isDarkMode = useIsDarkMode();
return (
<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g
clipPath="url(#prefix__clip0_4344_1236)"
stroke={isDarkMode ? Color.BG_VANILLA_100 : Color.BG_INK_500}
strokeWidth="1.167"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M4.667 1.167H2.333c-.644 0-1.166.522-1.166 1.166v2.334c0 .644.522 1.166 1.166 1.166h2.334c.644 0 1.166-.522 1.166-1.166V2.333c0-.644-.522-1.166-1.166-1.166zM8.167 1.167a1.17 1.17 0 011.166 1.166v2.334a1.17 1.17 0 01-1.166 1.166M11.667 1.167a1.17 1.17 0 011.166 1.166v2.334a1.17 1.17 0 01-1.166 1.166M5.833 10.5H2.917c-.992 0-1.75-.758-1.75-1.75v-.583" />
<path d="M4.083 12.25l1.75-1.75-1.75-1.75M11.667 8.167H9.333c-.644 0-1.166.522-1.166 1.166v2.334c0 .644.522 1.166 1.166 1.166h2.334c.644 0 1.166-.522 1.166-1.166V9.333c0-.644-.522-1.166-1.166-1.166z" />
</g>
<defs>
<clipPath id="prefix__clip0_4344_1236">
<path fill="#fff" d="M0 0h14v14H0z" />
</clipPath>
</defs>
</svg>
);
}
export default GroupByIcon;

View File

@@ -0,0 +1,137 @@
import { Button, Modal, Typography } from 'antd';
import updateCreditCardApi from 'api/billing/checkout';
import logEvent from 'api/common/logEvent';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import useLicense from 'hooks/useLicense';
import { useNotifications } from 'hooks/useNotifications';
import { CreditCard, X } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useMutation } from 'react-query';
import { useLocation } from 'react-router-dom';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout';
import { License } from 'types/api/licenses/def';
export default function ChatSupportGateway(): JSX.Element {
const { notifications } = useNotifications();
const [activeLicense, setActiveLicense] = useState<License | null>(null);
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
false,
);
const { data: licenseData, isFetching } = useLicense();
useEffect(() => {
const activeValidLicense =
licenseData?.payload?.licenses?.find(
(license) => license.isCurrent === true,
) || null;
setActiveLicense(activeValidLicense);
}, [licenseData, isFetching]);
const handleBillingOnSuccess = (
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
): void => {
if (data?.payload?.redirectURL) {
const newTab = document.createElement('a');
newTab.href = data.payload.redirectURL;
newTab.target = '_blank';
newTab.rel = 'noopener noreferrer';
newTab.click();
}
};
const handleBillingOnError = (): void => {
notifications.error({
message: SOMETHING_WENT_WRONG,
});
};
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
updateCreditCardApi,
{
onSuccess: (data) => {
handleBillingOnSuccess(data);
},
onError: handleBillingOnError,
},
);
const { pathname } = useLocation();
const handleAddCreditCard = (): void => {
logEvent('Add Credit card modal: Clicked', {
source: `intercom icon`,
page: pathname,
});
updateCreditCard({
licenseKey: activeLicense?.key || '',
successURL: window.location.href,
cancelURL: window.location.href,
});
};
return (
<>
<div className="chat-support-gateway">
<Button
className="chat-support-gateway-btn"
onClick={(): void => {
logEvent('Disabled Chat Support: Clicked', {
source: `intercom icon`,
page: pathname,
});
setIsAddCreditCardModalOpen(true);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 28 32"
className="chat-support-gateway-btn-icon"
>
<path d="M28 32s-4.714-1.855-8.527-3.34H3.437C1.54 28.66 0 27.026 0 25.013V3.644C0 1.633 1.54 0 3.437 0h21.125c1.898 0 3.437 1.632 3.437 3.645v18.404H28V32zm-4.139-11.982a.88.88 0 00-1.292-.105c-.03.026-3.015 2.681-8.57 2.681-5.486 0-8.517-2.636-8.571-2.684a.88.88 0 00-1.29.107 1.01 1.01 0 00-.219.708.992.992 0 00.318.664c.142.128 3.537 3.15 9.762 3.15 6.226 0 9.621-3.022 9.763-3.15a.992.992 0 00.317-.664 1.01 1.01 0 00-.218-.707z" />
</svg>
</Button>
</div>
{/* Add Credit Card Modal */}
<Modal
className="add-credit-card-modal"
title={<span className="title">Add Credit Card for Chat Support</span>}
open={isAddCreditCardModalOpen}
closable
onCancel={(): void => setIsAddCreditCardModalOpen(false)}
destroyOnClose
footer={[
<Button
key="cancel"
onClick={(): void => setIsAddCreditCardModalOpen(false)}
className="cancel-btn"
icon={<X size={16} />}
>
Cancel
</Button>,
<Button
key="submit"
type="primary"
icon={<CreditCard size={16} />}
size="middle"
loading={isLoadingBilling}
disabled={isLoadingBilling}
onClick={handleAddCreditCard}
className="add-credit-card-btn"
>
Add Credit Card
</Button>,
]}
>
<Typography.Text className="add-credit-card-text">
You&apos;re currently on <span className="highlight-text">Trial plan</span>
. Add a credit card to access SigNoz chat support to your workspace.
</Typography.Text>
</Modal>
</>
);
}

View File

@@ -0,0 +1,191 @@
import './LaunchChatSupport.styles.scss';
import { Button, Modal, Tooltip, Typography } from 'antd';
import updateCreditCardApi from 'api/billing/checkout';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import { FeatureKeys } from 'constants/features';
import useFeatureFlags from 'hooks/useFeatureFlag';
import useLicense from 'hooks/useLicense';
import { useNotifications } from 'hooks/useNotifications';
import { defaultTo } from 'lodash-es';
import { CreditCard, HelpCircle, X } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useMutation } from 'react-query';
import { useLocation } from 'react-router-dom';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout';
import { License } from 'types/api/licenses/def';
import { isCloudUser } from 'utils/app';
export interface LaunchChatSupportProps {
eventName: string;
attributes: Record<string, unknown>;
message?: string;
buttonText?: string;
className?: string;
onHoverText?: string;
intercomMessageDisabled?: boolean;
}
// eslint-disable-next-line sonarjs/cognitive-complexity
function LaunchChatSupport({
attributes,
eventName,
message = '',
buttonText = '',
className = '',
onHoverText = '',
intercomMessageDisabled = false,
}: LaunchChatSupportProps): JSX.Element | null {
const isChatSupportEnabled = useFeatureFlags(FeatureKeys.CHAT_SUPPORT)?.active;
const isCloudUserVal = isCloudUser();
const { notifications } = useNotifications();
const { data: licenseData, isFetching } = useLicense();
const [activeLicense, setActiveLicense] = useState<License | null>(null);
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
false,
);
const { pathname } = useLocation();
const isPremiumChatSupportEnabled =
useFeatureFlags(FeatureKeys.PREMIUM_SUPPORT)?.active || false;
const showAddCreditCardModal =
!isPremiumChatSupportEnabled &&
!licenseData?.payload?.trialConvertedToSubscription;
useEffect(() => {
const activeValidLicense =
licenseData?.payload?.licenses?.find(
(license) => license.isCurrent === true,
) || null;
setActiveLicense(activeValidLicense);
}, [licenseData, isFetching]);
const handleFacingIssuesClick = (): void => {
if (showAddCreditCardModal) {
logEvent('Disabled Chat Support: Clicked', {
source: `facing issues button`,
page: pathname,
...attributes,
});
setIsAddCreditCardModalOpen(true);
} else {
logEvent(eventName, attributes);
if (window.Intercom && !intercomMessageDisabled) {
window.Intercom('showNewMessage', defaultTo(message, ''));
}
}
};
const handleBillingOnSuccess = (
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
): void => {
if (data?.payload?.redirectURL) {
const newTab = document.createElement('a');
newTab.href = data.payload.redirectURL;
newTab.target = '_blank';
newTab.rel = 'noopener noreferrer';
newTab.click();
}
};
const handleBillingOnError = (): void => {
notifications.error({
message: SOMETHING_WENT_WRONG,
});
};
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
updateCreditCardApi,
{
onSuccess: (data) => {
handleBillingOnSuccess(data);
},
onError: handleBillingOnError,
},
);
const handleAddCreditCard = (): void => {
logEvent('Add Credit card modal: Clicked', {
source: `facing issues button`,
page: pathname,
...attributes,
});
updateCreditCard({
licenseKey: activeLicense?.key || '',
successURL: window.location.href,
cancelURL: window.location.href,
});
};
return isCloudUserVal && isChatSupportEnabled ? ( // Note: we would need to move this condition to license based in future
<div className="facing-issue-button">
<Tooltip
title={onHoverText}
autoAdjustOverflow
style={{ padding: 8 }}
overlayClassName="tooltip-overlay"
>
<Button
className={cx('periscope-btn', 'facing-issue-button', className)}
onClick={handleFacingIssuesClick}
icon={<HelpCircle size={14} />}
>
{buttonText || 'Facing issues?'}
</Button>
</Tooltip>
{/* Add Credit Card Modal */}
<Modal
className="add-credit-card-modal"
title={<span className="title">Add Credit Card for Chat Support</span>}
open={isAddCreditCardModalOpen}
closable
onCancel={(): void => setIsAddCreditCardModalOpen(false)}
destroyOnClose
footer={[
<Button
key="cancel"
onClick={(): void => setIsAddCreditCardModalOpen(false)}
className="cancel-btn"
icon={<X size={16} />}
>
Cancel
</Button>,
<Button
key="submit"
type="primary"
icon={<CreditCard size={16} />}
size="middle"
loading={isLoadingBilling}
disabled={isLoadingBilling}
onClick={handleAddCreditCard}
className="add-credit-card-btn"
>
Add Credit Card
</Button>,
]}
>
<Typography.Text className="add-credit-card-text">
You&apos;re currently on <span className="highlight-text">Trial plan</span>
. Add a credit card to access SigNoz chat support to your workspace.
</Typography.Text>
</Modal>
</div>
) : null;
}
LaunchChatSupport.defaultProps = {
message: '',
buttonText: '',
className: '',
onHoverText: '',
intercomMessageDisabled: false,
};
export default LaunchChatSupport;

View File

@@ -41,6 +41,21 @@ I need help with managing alerts.
Thanks`;
export const onboardingHelpMessage = (
dataSourceName: string,
moduleId: string,
): string => `Hi Team,
I am facing issues sending data to SigNoz. Here are my application details
Data Source: ${dataSourceName}
Framework:
Environment:
Module: ${moduleId}
Thanks
`;
export const alertHelpMessage = (
alertDef: AlertDef,
ruleId: number,

View File

@@ -1,14 +1,22 @@
import { DrawerProps } from 'antd';
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
import { ActionItemProps } from 'container/LogDetailedView/ActionItem';
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { VIEWS } from './constants';
export type LogDetailProps = {
log: ILog | null;
selectedTab: VIEWS;
onGroupByAttribute?: (
fieldKey: string,
isJSON?: boolean,
dataType?: DataTypes,
) => Promise<void>;
isListViewPanel?: boolean;
listViewPanelSelectedFields?: IField[] | null;
} & Pick<AddToQueryHOCProps, 'onAddToQuery'> &
Partial<Pick<ActionItemProps, 'onClickActionItem'>> &
Pick<DrawerProps, 'onClose'>;

View File

@@ -52,7 +52,7 @@
.log-body {
font-family: 'SF Mono';
font-family: 'Space Mono', monospace;
font-family: 'Geist Mono';
font-size: var(--font-size-sm);
font-weight: var(--font-weight-normal);

View File

@@ -2,14 +2,23 @@
import './LogDetails.styles.scss';
import { Color, Spacing } from '@signozhq/design-tokens';
import Convert from 'ansi-to-html';
import { Button, Divider, Drawer, Radio, Tooltip, Typography } from 'antd';
import { RadioChangeEvent } from 'antd/lib';
import cx from 'classnames';
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
import { LOCALSTORAGE } from 'constants/localStorage';
import ContextView from 'container/LogDetailedView/ContextView/ContextView';
import JSONView from 'container/LogDetailedView/JsonView';
import Overview from 'container/LogDetailedView/Overview';
import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils';
import {
aggregateAttributesResourcesToString,
removeEscapeCharacters,
unescapeString,
} from 'container/LogDetailedView/utils';
import { useOptionsMenu } from 'container/OptionsMenu';
import dompurify from 'dompurify';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications';
import {
@@ -21,21 +30,27 @@ import {
TextSelect,
X,
} from 'lucide-react';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
import { VIEW_TYPES, VIEWS } from './constants';
import { LogDetailProps } from './LogDetail.interfaces';
import QueryBuilderSearchWrapper from './QueryBuilderSearchWrapper';
const convert = new Convert();
function LogDetail({
log,
onClose,
onAddToQuery,
onGroupByAttribute,
onClickActionItem,
selectedTab,
isListViewPanel = false,
listViewPanelSelectedFields,
}: LogDetailProps): JSX.Element {
const [, copyToClipboard] = useCopyToClipboard();
const [selectedView, setSelectedView] = useState<VIEWS>(selectedTab);
@@ -45,6 +60,19 @@ function LogDetail({
const [contextQuery, setContextQuery] = useState<Query | undefined>();
const [filters, setFilters] = useState<TagFilter | null>(null);
const [isEdit, setIsEdit] = useState<boolean>(false);
const { initialDataSource, stagedQuery } = useQueryBuilder();
const listQuery = useMemo(() => {
if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null;
return stagedQuery.builder.queryData.find((item) => !item.disabled) || null;
}, [stagedQuery]);
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: initialDataSource || DataSource.LOGS,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});
const isDarkMode = useIsDarkMode();
@@ -71,6 +99,17 @@ function LogDetail({
}
};
const htmlBody = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(unescapeString(log?.body || ''), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}),
[log?.body],
);
const handleJSONCopy = (): void => {
copyToClipboard(LogJsonData);
notifications.success({
@@ -108,8 +147,8 @@ function LogDetail({
>
<div className="log-detail-drawer__log">
<Divider type="vertical" className={cx('log-type-indicator', logType)} />
<Tooltip title={log?.body} placement="left">
<Typography.Text className="log-body">{log?.body}</Typography.Text>
<Tooltip title={removeEscapeCharacters(log?.body)} placement="left">
<div className="log-body" dangerouslySetInnerHTML={htmlBody} />
</Tooltip>
<div className="log-overflow-shadow">&nbsp;</div>
@@ -191,7 +230,10 @@ function LogDetail({
logData={log}
onAddToQuery={onAddToQuery}
onClickActionItem={onClickActionItem}
onGroupByAttribute={onGroupByAttribute}
isListViewPanel={isListViewPanel}
selectedOptions={options}
listViewPanelSelectedFields={listViewPanelSelectedFields}
/>
)}
{selectedView === VIEW_TYPES.JSON && <JSONView logData={log} />}

View File

@@ -1,3 +1,16 @@
.addToQueryContainer {
cursor: pointer;
display: flex;
align-items: center;
&.small {
line-height: 16px;
}
&.medium {
line-height: 20px;
}
&.large {
line-height: 24px;
}
}

View File

@@ -1,13 +1,16 @@
import './AddToQueryHOC.styles.scss';
import { Popover } from 'antd';
import cx from 'classnames';
import { OPERATORS } from 'constants/queryBuilder';
import { FontSize } from 'container/OptionsMenu/types';
import { memo, MouseEvent, ReactNode, useMemo } from 'react';
function AddToQueryHOC({
fieldKey,
fieldValue,
onAddToQuery,
fontSize,
children,
}: AddToQueryHOCProps): JSX.Element {
const handleQueryAdd = (event: MouseEvent<HTMLDivElement>): void => {
@@ -21,7 +24,7 @@ function AddToQueryHOC({
return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div className="addToQueryContainer" onClick={handleQueryAdd}>
<div className={cx('addToQueryContainer', fontSize)} onClick={handleQueryAdd}>
<Popover placement="top" content={popOverContent}>
{children}
</Popover>
@@ -33,6 +36,7 @@ export interface AddToQueryHOCProps {
fieldKey: string;
fieldValue: string;
onAddToQuery: (fieldKey: string, fieldValue: string, operator: string) => void;
fontSize: FontSize;
children: ReactNode;
}

View File

@@ -4,6 +4,7 @@ import { ReactNode, useCallback, useEffect } from 'react';
import { useCopyToClipboard } from 'react-use';
function CopyClipboardHOC({
entityKey,
textToCopy,
children,
}: CopyClipboardHOCProps): JSX.Element {
@@ -11,11 +12,15 @@ function CopyClipboardHOC({
const { notifications } = useNotifications();
useEffect(() => {
if (value.value) {
const key = entityKey || '';
const notificationMessage = `${key} copied to clipboard`;
notifications.success({
message: 'Copied to clipboard',
message: notificationMessage,
});
}
}, [value, notifications]);
}, [value, notifications, entityKey]);
const onClick = useCallback((): void => {
setCopy(textToCopy);
@@ -34,6 +39,7 @@ function CopyClipboardHOC({
}
interface CopyClipboardHOCProps {
entityKey: string | undefined;
textToCopy: string;
children: ReactNode;
}

View File

@@ -6,6 +6,21 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
&.small {
font-size: 11px;
line-height: 16px;
}
&.medium {
font-size: 13px;
line-height: 20px;
}
&.large {
font-size: 14px;
line-height: 24px;
}
}
.log-value {
color: var(--text-vanilla-400, #c0c1c3);
@@ -14,6 +29,21 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
&.small {
font-size: 11px;
line-height: 16px;
}
&.medium {
font-size: 13px;
line-height: 20px;
}
&.large {
font-size: 14px;
line-height: 24px;
}
}
.log-line {
display: flex;
@@ -40,6 +70,20 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
&.small {
font-size: 11px;
line-height: 16px;
}
&.medium {
font-size: 13px;
line-height: 20px;
}
&.large {
font-size: 14px;
line-height: 24px;
}
}
.selected-log-value {
@@ -52,12 +96,37 @@
line-height: 18px;
letter-spacing: -0.07px;
font-size: 14px;
&.small {
font-size: 11px;
line-height: 16px;
}
&.medium {
font-size: 13px;
line-height: 20px;
}
&.large {
font-size: 14px;
line-height: 24px;
}
}
.selected-log-kv {
min-height: 24px;
display: flex;
align-items: center;
&.small {
min-height: 16px;
}
&.medium {
min-height: 20px;
}
&.large {
min-height: 24px;
}
}
}

View File

@@ -3,8 +3,11 @@ import './ListLogView.styles.scss';
import { blue } from '@ant-design/colors';
import Convert from 'ansi-to-html';
import { Typography } from 'antd';
import cx from 'classnames';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
import { unescapeString } from 'container/LogDetailedView/utils';
import { FontSize } from 'container/OptionsMenu/types';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
import { useActiveLog } from 'hooks/logs/useActiveLog';
@@ -39,6 +42,7 @@ interface LogFieldProps {
fieldKey: string;
fieldValue: string;
linesPerRow?: number;
fontSize: FontSize;
}
type LogSelectedFieldProps = Omit<LogFieldProps, 'linesPerRow'> &
@@ -48,11 +52,12 @@ function LogGeneralField({
fieldKey,
fieldValue,
linesPerRow = 1,
fontSize,
}: LogFieldProps): JSX.Element {
const html = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(fieldValue, {
dompurify.sanitize(unescapeString(fieldValue), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
@@ -62,12 +67,12 @@ function LogGeneralField({
return (
<TextContainer>
<Text ellipsis type="secondary" className="log-field-key">
<Text ellipsis type="secondary" className={cx('log-field-key', fontSize)}>
{`${fieldKey} : `}
</Text>
<LogText
dangerouslySetInnerHTML={html}
className="log-value"
className={cx('log-value', fontSize)}
linesPerRow={linesPerRow > 1 ? linesPerRow : undefined}
/>
</TextContainer>
@@ -78,6 +83,7 @@ function LogSelectedField({
fieldKey = '',
fieldValue = '',
onAddToQuery,
fontSize,
}: LogSelectedFieldProps): JSX.Element {
return (
<div className="log-selected-fields">
@@ -85,16 +91,22 @@ function LogSelectedField({
fieldKey={fieldKey}
fieldValue={fieldValue}
onAddToQuery={onAddToQuery}
fontSize={fontSize}
>
<Typography.Text>
<span style={{ color: blue[4] }} className="selected-log-field-key">
<span
style={{ color: blue[4] }}
className={cx('selected-log-field-key', fontSize)}
>
{fieldKey}
</span>
</Typography.Text>
</AddToQueryHOC>
<Typography.Text ellipsis className="selected-log-kv">
<span className="selected-log-field-key">{': '}</span>
<span className="selected-log-value">{fieldValue || "''"}</span>
<Typography.Text ellipsis className={cx('selected-log-kv', fontSize)}>
<span className={cx('selected-log-field-key', fontSize)}>{': '}</span>
<span className={cx('selected-log-value', fontSize)}>
{fieldValue || "''"}
</span>
</Typography.Text>
</div>
);
@@ -107,6 +119,7 @@ type ListLogViewProps = {
onAddToQuery: AddToQueryHOCProps['onAddToQuery'];
activeLog?: ILog | null;
linesPerRow: number;
fontSize: FontSize;
};
function ListLogView({
@@ -116,6 +129,7 @@ function ListLogView({
onAddToQuery,
activeLog,
linesPerRow,
fontSize,
}: ListLogViewProps): JSX.Element {
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
@@ -128,6 +142,7 @@ function ListLogView({
onAddToQuery: handleAddToQuery,
onSetActiveLog: handleSetActiveContextLog,
onClearActiveLog: handleClearActiveContextLog,
onGroupByAttribute,
} = useActiveLog();
const isDarkMode = useIsDarkMode();
@@ -185,6 +200,7 @@ function ListLogView({
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={handleDetailedView}
fontSize={fontSize}
>
<div className="log-line">
<LogStateIndicator
@@ -192,18 +208,28 @@ function ListLogView({
isActive={
activeLog?.id === logData.id || activeContextLog?.id === logData.id
}
fontSize={fontSize}
/>
<div>
<LogContainer>
<LogContainer fontSize={fontSize}>
<LogGeneralField
fieldKey="Log"
fieldValue={flattenLogData.body}
linesPerRow={linesPerRow}
fontSize={fontSize}
/>
{flattenLogData.stream && (
<LogGeneralField fieldKey="Stream" fieldValue={flattenLogData.stream} />
<LogGeneralField
fieldKey="Stream"
fieldValue={flattenLogData.stream}
fontSize={fontSize}
/>
)}
<LogGeneralField fieldKey="Timestamp" fieldValue={timestampValue} />
<LogGeneralField
fieldKey="Timestamp"
fieldValue={timestampValue}
fontSize={fontSize}
/>
{updatedSelecedFields.map((field) =>
isValidLogField(flattenLogData[field.name] as never) ? (
@@ -212,6 +238,7 @@ function ListLogView({
fieldKey={field.name}
fieldValue={flattenLogData[field.name] as never}
onAddToQuery={onAddToQuery}
fontSize={fontSize}
/>
) : null,
)}
@@ -232,6 +259,7 @@ function ListLogView({
onAddToQuery={handleAddToQuery}
selectedTab={VIEW_TYPES.CONTEXT}
onClose={handlerClearActiveContextLog}
onGroupByAttribute={onGroupByAttribute}
/>
)}
</>

View File

@@ -1,21 +1,46 @@
/* eslint-disable no-nested-ternary */
import { Color } from '@signozhq/design-tokens';
import { Card, Typography } from 'antd';
import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
interface LogTextProps {
linesPerRow?: number;
}
interface LogContainerProps {
fontSize: FontSize;
}
export const Container = styled(Card)<{
$isActiveLog: boolean;
$isDarkMode: boolean;
fontSize: FontSize;
}>`
width: 100% !important;
margin-bottom: 0.3rem;
${({ fontSize }): string =>
fontSize === FontSize.SMALL
? `margin-bottom:0.1rem;`
: fontSize === FontSize.MEDIUM
? `margin-bottom: 0.2rem;`
: fontSize === FontSize.LARGE
? `margin-bottom:0.3rem;`
: ``}
cursor: pointer;
.ant-card-body {
padding: 0.3rem 0.6rem;
${({ fontSize }): string =>
fontSize === FontSize.SMALL
? `padding:0.1rem 0.6rem;`
: fontSize === FontSize.MEDIUM
? `padding: 0.2rem 0.6rem;`
: fontSize === FontSize.LARGE
? `padding:0.3rem 0.6rem;`
: ``}
${({ $isActiveLog, $isDarkMode }): string =>
$isActiveLog
? `background-color: ${
@@ -38,11 +63,17 @@ export const TextContainer = styled.div`
width: 100%;
`;
export const LogContainer = styled.div`
export const LogContainer = styled.div<LogContainerProps>`
margin-left: 0.5rem;
display: flex;
flex-direction: column;
gap: 6px;
${({ fontSize }): string =>
fontSize === FontSize.SMALL
? `gap: 2px;`
: fontSize === FontSize.MEDIUM
? ` gap:4px;`
: `gap:6px;`}
`;
export const LogText = styled.div<LogTextProps>`

View File

@@ -9,11 +9,24 @@
border-radius: 50px;
background-color: transparent;
&.small {
min-height: 16px;
}
&.medium {
min-height: 20px;
}
&.large {
min-height: 24px;
}
&.INFO {
background-color: var(--bg-slate-400);
}
&.WARNING, &.WARN {
&.WARNING,
&.WARN {
background-color: var(--bg-amber-500);
}

View File

@@ -1,10 +1,13 @@
import { render } from '@testing-library/react';
import { FontSize } from 'container/OptionsMenu/types';
import LogStateIndicator from './LogStateIndicator';
describe('LogStateIndicator', () => {
it('renders correctly with default props', () => {
const { container } = render(<LogStateIndicator type="INFO" />);
const { container } = render(
<LogStateIndicator type="INFO" fontSize={FontSize.MEDIUM} />,
);
const indicator = container.firstChild as HTMLElement;
expect(indicator.classList.contains('log-state-indicator')).toBe(true);
expect(indicator.classList.contains('isActive')).toBe(false);
@@ -15,28 +18,30 @@ describe('LogStateIndicator', () => {
});
it('renders correctly when isActive is true', () => {
const { container } = render(<LogStateIndicator type="INFO" isActive />);
const { container } = render(
<LogStateIndicator type="INFO" isActive fontSize={FontSize.MEDIUM} />,
);
const indicator = container.firstChild as HTMLElement;
expect(indicator.classList.contains('isActive')).toBe(true);
});
it('renders correctly with different types', () => {
const { container: containerInfo } = render(
<LogStateIndicator type="INFO" />,
<LogStateIndicator type="INFO" fontSize={FontSize.MEDIUM} />,
);
expect(containerInfo.querySelector('.line')?.classList.contains('INFO')).toBe(
true,
);
const { container: containerWarning } = render(
<LogStateIndicator type="WARNING" />,
<LogStateIndicator type="WARNING" fontSize={FontSize.MEDIUM} />,
);
expect(
containerWarning.querySelector('.line')?.classList.contains('WARNING'),
).toBe(true);
const { container: containerError } = render(
<LogStateIndicator type="ERROR" />,
<LogStateIndicator type="ERROR" fontSize={FontSize.MEDIUM} />,
);
expect(
containerError.querySelector('.line')?.classList.contains('ERROR'),

View File

@@ -1,6 +1,7 @@
import './LogStateIndicator.styles.scss';
import cx from 'classnames';
import { FontSize } from 'container/OptionsMenu/types';
export const SEVERITY_TEXT_TYPE = {
TRACE: 'TRACE',
@@ -28,24 +29,31 @@ export const SEVERITY_TEXT_TYPE = {
FATAL2: 'FATAL2',
FATAL3: 'FATAL3',
FATAL4: 'FATAL4',
UNKNOWN: 'UNKNOWN',
} as const;
export const LogType = {
TRACE: 'TRACE',
DEBUG: 'DEBUG',
INFO: 'INFO',
WARNING: 'WARNING',
WARN: 'WARN',
ERROR: 'ERROR',
FATAL: 'FATAL',
UNKNOWN: 'UNKNOWN',
} as const;
function LogStateIndicator({
type,
isActive,
fontSize,
}: {
type: string;
fontSize: FontSize;
isActive?: boolean;
}): JSX.Element {
return (
<div className={cx('log-state-indicator', isActive ? 'isActive' : '')}>
<div className={cx('line', type)}> </div>
<div className={cx('line', type, fontSize)}> </div>
</div>
);
}

View File

@@ -1,9 +1,10 @@
/* eslint-disable sonarjs/no-duplicate-string */
import { ILog } from 'types/api/logs/log';
import { getLogIndicatorType, getLogIndicatorTypeForTable } from './utils';
describe('getLogIndicatorType', () => {
it('should return severity type for valid log with severityText', () => {
it('severity_number should be given priority over severity_text', () => {
const log = {
date: '2024-02-29T12:34:46Z',
timestamp: 1646115296,
@@ -20,11 +21,57 @@ describe('getLogIndicatorType', () => {
attributesInt: {},
attributesFloat: {},
severity_text: 'INFO',
severity_number: 2,
};
expect(getLogIndicatorType(log)).toBe('INFO');
// severity_number should get priority over severity_text
expect(getLogIndicatorType(log)).toBe('TRACE');
});
it('should return log level if severityText is missing', () => {
it('severity_text should be used when severity_number is absent ', () => {
const log = {
date: '2024-02-29T12:34:46Z',
timestamp: 1646115296,
id: '123456',
traceId: '987654',
spanId: '54321',
traceFlags: 0,
severityText: 'INFO',
severityNumber: 2,
body: 'Sample log Message',
resources_string: {},
attributesString: {},
attributes_string: {},
attributesInt: {},
attributesFloat: {},
severity_text: 'FATAL',
severity_number: 0,
};
expect(getLogIndicatorType(log)).toBe('FATAL');
});
it('case insensitive severity_text should be valid', () => {
const log = {
date: '2024-02-29T12:34:46Z',
timestamp: 1646115296,
id: '123456',
traceId: '987654',
spanId: '54321',
traceFlags: 0,
severityText: 'INFO',
severityNumber: 2,
body: 'Sample log Message',
resources_string: {},
attributesString: {},
attributes_string: {},
attributesInt: {},
attributesFloat: {},
severity_text: 'fatAl',
severity_number: 0,
};
expect(getLogIndicatorType(log)).toBe('FATAL');
});
it('should return log level if severityText and severityNumber is missing', () => {
const log: ILog = {
date: '2024-02-29T12:34:58Z',
timestamp: 1646115296,
@@ -36,13 +83,16 @@ describe('getLogIndicatorType', () => {
body: 'Sample log',
resources_string: {},
attributesString: {},
attributes_string: {},
attributes_string: {
log_level: 'INFO' as never,
},
attributesInt: {},
attributesFloat: {},
severity_text: 'FATAL',
severity_text: 'some_random',
severityText: '',
severity_number: 0,
};
expect(getLogIndicatorType(log)).toBe('FATAL');
expect(getLogIndicatorType(log)).toBe('INFO');
});
});
@@ -55,6 +105,7 @@ describe('getLogIndicatorTypeForTable', () => {
traceId: '987654',
spanId: '54321',
traceFlags: 0,
severityNumber: 2,
severity_number: 2,
body: 'Sample log message',
resources_string: {},
@@ -64,7 +115,7 @@ describe('getLogIndicatorTypeForTable', () => {
attributesFloat: {},
severity_text: 'WARN',
};
expect(getLogIndicatorTypeForTable(log)).toBe('WARN');
expect(getLogIndicatorTypeForTable(log)).toBe('TRACE');
});
it('should return log level if severityText is missing', () => {
@@ -75,7 +126,8 @@ describe('getLogIndicatorTypeForTable', () => {
traceId: '987654',
spanId: '54321',
traceFlags: 0,
severityNumber: 2,
severityNumber: 0,
severity_number: 0,
body: 'Sample log message',
resources_string: {},
attributesString: {},
@@ -87,3 +139,47 @@ describe('getLogIndicatorTypeForTable', () => {
expect(getLogIndicatorTypeForTable(log)).toBe('INFO');
});
});
describe('logIndicatorBySeverityNumber', () => {
// https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber
const logLevelExpectations = [
{ minSevNumber: 1, maxSevNumber: 4, expectedIndicatorType: 'TRACE' },
{ minSevNumber: 5, maxSevNumber: 8, expectedIndicatorType: 'DEBUG' },
{ minSevNumber: 9, maxSevNumber: 12, expectedIndicatorType: 'INFO' },
{ minSevNumber: 13, maxSevNumber: 16, expectedIndicatorType: 'WARN' },
{ minSevNumber: 17, maxSevNumber: 20, expectedIndicatorType: 'ERROR' },
{ minSevNumber: 21, maxSevNumber: 24, expectedIndicatorType: 'FATAL' },
];
logLevelExpectations.forEach((e) => {
for (let sevNum = e.minSevNumber; sevNum <= e.maxSevNumber; sevNum++) {
const sevText = (Math.random() + 1).toString(36).substring(2);
const log = {
date: '2024-02-29T12:34:46Z',
timestamp: 1646115296,
id: '123456',
traceId: '987654',
spanId: '54321',
traceFlags: 0,
severityText: sevText,
severityNumber: sevNum,
body: 'Sample log Message',
resources_string: {},
attributesString: {},
attributes_string: {},
attributesInt: {},
attributesFloat: {},
severity_text: sevText,
severity_number: sevNum,
};
it(`getLogIndicatorType should return ${e.expectedIndicatorType} for severity_text: ${sevText} and severity_number: ${sevNum}`, () => {
expect(getLogIndicatorType(log)).toBe(e.expectedIndicatorType);
});
it(`getLogIndicatorTypeForTable should return ${e.expectedIndicatorType} for severity_text: ${sevText} and severity_number: ${sevNum}`, () => {
expect(getLogIndicatorTypeForTable(log)).toBe(e.expectedIndicatorType);
});
}
});
});

View File

@@ -2,56 +2,112 @@ import { ILog } from 'types/api/logs/log';
import { LogType, SEVERITY_TEXT_TYPE } from './LogStateIndicator';
const getSeverityType = (severityText: string): string => {
const getLogTypeBySeverityText = (severityText: string): string => {
switch (severityText) {
case SEVERITY_TEXT_TYPE.TRACE:
case SEVERITY_TEXT_TYPE.TRACE2:
case SEVERITY_TEXT_TYPE.TRACE3:
case SEVERITY_TEXT_TYPE.TRACE4:
return SEVERITY_TEXT_TYPE.TRACE;
return LogType.TRACE;
case SEVERITY_TEXT_TYPE.DEBUG:
case SEVERITY_TEXT_TYPE.DEBUG2:
case SEVERITY_TEXT_TYPE.DEBUG3:
case SEVERITY_TEXT_TYPE.DEBUG4:
return SEVERITY_TEXT_TYPE.DEBUG;
return LogType.DEBUG;
case SEVERITY_TEXT_TYPE.INFO:
case SEVERITY_TEXT_TYPE.INFO2:
case SEVERITY_TEXT_TYPE.INFO3:
case SEVERITY_TEXT_TYPE.INFO4:
return SEVERITY_TEXT_TYPE.INFO;
return LogType.INFO;
case SEVERITY_TEXT_TYPE.WARN:
case SEVERITY_TEXT_TYPE.WARN2:
case SEVERITY_TEXT_TYPE.WARN3:
case SEVERITY_TEXT_TYPE.WARN4:
case SEVERITY_TEXT_TYPE.WARNING:
return SEVERITY_TEXT_TYPE.WARN;
return LogType.WARN;
case SEVERITY_TEXT_TYPE.ERROR:
case SEVERITY_TEXT_TYPE.ERROR2:
case SEVERITY_TEXT_TYPE.ERROR3:
case SEVERITY_TEXT_TYPE.ERROR4:
return SEVERITY_TEXT_TYPE.ERROR;
return LogType.ERROR;
case SEVERITY_TEXT_TYPE.FATAL:
case SEVERITY_TEXT_TYPE.FATAL2:
case SEVERITY_TEXT_TYPE.FATAL3:
case SEVERITY_TEXT_TYPE.FATAL4:
return SEVERITY_TEXT_TYPE.FATAL;
return LogType.FATAL;
default:
return SEVERITY_TEXT_TYPE.INFO;
return LogType.UNKNOWN;
}
};
export const getLogIndicatorType = (logData: ILog): string => {
if (logData.severity_text) {
return getSeverityType(logData.severity_text);
// https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber
const getLogTypeBySeverityNumber = (severityNumber: number): string => {
if (severityNumber < 1) {
return LogType.UNKNOWN;
}
return logData.attributes_string?.log_level || LogType.INFO;
if (severityNumber < 5) {
return LogType.TRACE;
}
if (severityNumber < 9) {
return LogType.DEBUG;
}
if (severityNumber < 13) {
return LogType.INFO;
}
if (severityNumber < 17) {
return LogType.WARN;
}
if (severityNumber < 21) {
return LogType.ERROR;
}
if (severityNumber < 25) {
return LogType.FATAL;
}
return LogType.UNKNOWN;
};
const getLogType = (
severityText: string,
severityNumber: number,
defaultType: string,
): string => {
// give priority to the severityNumber
if (severityNumber) {
const logType = getLogTypeBySeverityNumber(severityNumber);
if (logType !== LogType.UNKNOWN) {
return logType;
}
}
// is severityNumber is not present then rely on the severityText
if (severityText) {
const logType = getLogTypeBySeverityText(severityText);
if (logType !== LogType.UNKNOWN) {
return logType;
}
}
return defaultType;
};
export const getLogIndicatorType = (logData: ILog): string => {
const defaultType = logData.attributes_string?.log_level || LogType.INFO;
// convert the severity_text to upper case for the comparison to support case insensitive values
return getLogType(
logData?.severity_text?.toUpperCase(),
logData?.severity_number || 0,
defaultType,
);
};
export const getLogIndicatorTypeForTable = (
log: Record<string, unknown>,
): string => {
if (log.severity_text) {
return getSeverityType(log.severity_text as string);
}
return (log.log_level as string) || LogType.INFO;
const defaultType = (log.log_level as string) || LogType.INFO;
// convert the severity_text to upper case for the comparison to support case insensitive values
return getLogType(
(log?.severity_text as string)?.toUpperCase(),
(log?.severity_number as number) || 0,
defaultType,
);
};

View File

@@ -4,6 +4,7 @@ import Convert from 'ansi-to-html';
import { DrawerProps } from 'antd';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants';
import { unescapeString } from 'container/LogDetailedView/utils';
import LogsExplorerContext from 'container/LogsExplorerContext';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
@@ -39,6 +40,7 @@ function RawLogView({
linesPerRow,
isTextOverflowEllipsisDisabled,
selectedFields = [],
fontSize,
}: RawLogViewProps): JSX.Element {
const { isHighlighted, isLogsExplorerPage, onLogCopy } = useCopyLogLink(
data.id,
@@ -54,6 +56,7 @@ function RawLogView({
onSetActiveLog,
onClearActiveLog,
onAddToQuery,
onGroupByAttribute,
} = useActiveLog();
const [hasActionButtons, setHasActionButtons] = useState<boolean>(false);
@@ -143,7 +146,9 @@ function RawLogView({
const html = useMemo(
() => ({
__html: convert.toHtml(
dompurify.sanitize(text, { FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS] }),
dompurify.sanitize(unescapeString(text), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}),
[text],
@@ -160,6 +165,7 @@ function RawLogView({
$isActiveLog={isActiveLog}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
fontSize={fontSize}
>
<LogStateIndicator
type={logType}
@@ -168,6 +174,7 @@ function RawLogView({
activeContextLog?.id === data.id ||
isActiveLog
}
fontSize={fontSize}
/>
<RawLogContent
@@ -176,6 +183,7 @@ function RawLogView({
$isDarkMode={isDarkMode}
$isTextOverflowEllipsisDisabled={isTextOverflowEllipsisDisabled}
linesPerRow={linesPerRow}
fontSize={fontSize}
dangerouslySetInnerHTML={html}
/>
@@ -199,6 +207,7 @@ function RawLogView({
onClose={handleCloseLogDetail}
onAddToQuery={onAddToQuery}
onClickActionItem={onAddToQuery}
onGroupByAttribute={onGroupByAttribute}
/>
)}
</RawLogViewContainer>

View File

@@ -1,6 +1,8 @@
/* eslint-disable no-nested-ternary */
import { blue } from '@ant-design/colors';
import { Color } from '@signozhq/design-tokens';
import { Col, Row, Space } from 'antd';
import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
import { getActiveLogBackground, getDefaultLogBackground } from 'utils/logs';
@@ -11,6 +13,7 @@ export const RawLogViewContainer = styled(Row)<{
$isReadOnly?: boolean;
$isActiveLog?: boolean;
$isHightlightedLog: boolean;
fontSize: FontSize;
}>`
position: relative;
width: 100%;
@@ -22,6 +25,13 @@ export const RawLogViewContainer = styled(Row)<{
.log-state-indicator {
margin: 4px 0;
${({ fontSize }): string =>
fontSize === FontSize.SMALL
? `margin: 1px 0;`
: fontSize === FontSize.MEDIUM
? `margin: 1px 0;`
: `margin: 2px 0;`}
}
${({ $isActiveLog }): string => getActiveLogBackground($isActiveLog)}
@@ -49,9 +59,9 @@ export const ExpandIconWrapper = styled(Col)`
export const RawLogContent = styled.div<RawLogContentProps>`
margin-bottom: 0;
font-family: 'SF Mono', monospace;
font-family: 'Space Mono', monospace;
font-size: 13px;
font-weight: 400;
font-family: 'Geist Mono';
letter-spacing: -0.07px;
padding: 4px;
text-align: left;
color: ${({ $isDarkMode }): string =>
$isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400};
@@ -66,9 +76,15 @@ export const RawLogContent = styled.div<RawLogContentProps>`
line-clamp: ${linesPerRow};
-webkit-box-orient: vertical;`};
font-size: 13px;
font-weight: 400;
line-height: 24px;
letter-spacing: -0.07px;
padding: 4px;
${({ fontSize }): string =>
fontSize === FontSize.SMALL
? `font-size:11px; line-height:16px; padding:1px;`
: fontSize === FontSize.MEDIUM
? `font-size:13px; line-height:20px; padding:1px;`
: `font-size:14px; line-height:24px; padding:2px;`}
cursor: ${({ $isActiveLog, $isReadOnly }): string =>
$isActiveLog || $isReadOnly ? 'initial' : 'pointer'};

View File

@@ -1,3 +1,4 @@
import { FontSize } from 'container/OptionsMenu/types';
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
@@ -7,11 +8,13 @@ export interface RawLogViewProps {
isTextOverflowEllipsisDisabled?: boolean;
data: ILog;
linesPerRow: number;
fontSize: FontSize;
selectedFields?: IField[];
}
export interface RawLogContentProps {
linesPerRow: number;
fontSize: FontSize;
$isReadOnly?: boolean;
$isActiveLog?: boolean;
$isDarkMode?: boolean;

View File

@@ -1,7 +1,10 @@
/* eslint-disable no-nested-ternary */
import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
interface TableBodyContentProps {
linesPerRow: number;
fontSize: FontSize;
isDarkMode?: boolean;
}
@@ -20,4 +23,10 @@ export const TableBodyContent = styled.div<TableBodyContentProps>`
-webkit-line-clamp: ${(props): number => props.linesPerRow};
line-clamp: ${(props): number => props.linesPerRow};
-webkit-box-orient: vertical;
${({ fontSize }): string =>
fontSize === FontSize.SMALL
? `font-size:11px; line-height:16px;`
: fontSize === FontSize.MEDIUM
? `font-size:13px; line-height:20px;`
: `font-size:14px; line-height:24px;`}
`;

View File

@@ -1,4 +1,5 @@
import { ColumnsType, ColumnType } from 'antd/es/table';
import { FontSize } from 'container/OptionsMenu/types';
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
@@ -10,6 +11,7 @@ export type LogsTableViewProps = {
logs: ILog[];
fields: IField[];
linesPerRow: number;
fontSize: FontSize;
onClickExpand?: (log: ILog) => void;
};

View File

@@ -5,6 +5,21 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
&.small {
font-size: 11px;
line-height: 16px;
}
&.medium {
font-size: 13px;
line-height: 20px;
}
&.large {
font-size: 14px;
line-height: 24px;
}
}
.table-timestamp {
@@ -25,3 +40,21 @@
color: var(--bg-slate-400);
}
}
.paragraph {
padding: 0px !important;
&.small {
font-size: 11px !important;
line-height: 16px !important;
}
&.medium {
font-size: 13px !important;
line-height: 20px !important;
}
&.large {
font-size: 14px !important;
line-height: 24px !important;
}
}

View File

@@ -3,6 +3,8 @@ import './useTableView.styles.scss';
import Convert from 'ansi-to-html';
import { Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import cx from 'classnames';
import { unescapeString } from 'container/LogDetailedView/utils';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
import { useIsDarkMode } from 'hooks/useDarkMode';
@@ -31,6 +33,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
logs,
fields,
linesPerRow,
fontSize,
appendTo = 'center',
activeContextLog,
activeLog,
@@ -57,7 +60,10 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
: getDefaultCellStyle(isDarkMode),
},
children: (
<Typography.Paragraph ellipsis={{ rows: linesPerRow }}>
<Typography.Paragraph
ellipsis={{ rows: linesPerRow }}
className={cx('paragraph', fontSize)}
>
{field}
</Typography.Paragraph>
),
@@ -87,8 +93,9 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
isActive={
activeLog?.id === item.id || activeContextLog?.id === item.id
}
fontSize={fontSize}
/>
<Typography.Paragraph ellipsis className="text">
<Typography.Paragraph ellipsis className={cx('text', fontSize)}>
{date}
</Typography.Paragraph>
</div>
@@ -109,11 +116,12 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
<TableBodyContent
dangerouslySetInnerHTML={{
__html: convert.toHtml(
dompurify.sanitize(field, {
dompurify.sanitize(unescapeString(field), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}}
fontSize={fontSize}
linesPerRow={linesPerRow}
isDarkMode={isDarkMode}
/>
@@ -130,6 +138,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
linesPerRow,
activeLog?.id,
activeContextLog?.id,
fontSize,
]);
return { columns, dataSource: flattenLogData };

View File

@@ -17,17 +17,126 @@
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20px);
.font-size-dropdown {
display: flex;
flex-direction: column;
.back-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 12px;
border: none !important;
box-shadow: none !important;
.icon {
flex-shrink: 0;
}
.text {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: 0.14px;
}
}
.back-btn:hover {
background-color: unset !important;
}
.content {
display: flex;
flex-direction: column;
.option-btn {
display: flex;
align-items: center;
padding: 12px;
border: none !important;
box-shadow: none !important;
justify-content: space-between;
.icon {
flex-shrink: 0;
}
.text {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: normal; /* 142.857% */
letter-spacing: 0.14px;
text-transform: capitalize;
}
.text:hover {
color: var(--bg-vanilla-300);
}
}
.option-btn:hover {
background-color: unset !important;
}
}
}
.font-size-container {
padding: 12px;
display: flex;
flex-direction: column;
gap: 12px;
.title {
color: var(--bg-slate-50);
font-family: Inter;
font-size: 11px;
font-style: normal;
font-weight: 500;
line-height: 18px; /* 163.636% */
letter-spacing: 0.88px;
text-transform: uppercase;
}
.value {
display: flex;
height: 20px;
padding: 4px 0px;
justify-content: space-between;
align-items: center;
border: none !important;
.font-value {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.14px;
text-transform: capitalize;
}
.icon {
}
}
.value:hover {
background-color: unset !important;
}
}
.menu-container {
padding: 12px;
.title {
font-family: Inter;
font-size: 11px;
font-weight: 600;
font-weight: 500;
line-height: 18px;
letter-spacing: 0.08em;
text-align: left;
color: #52575c;
color: var(--bg-slate-50);
}
.menu-items {
@@ -65,11 +174,11 @@
padding: 12px;
.title {
color: #52575c;
color: var(--bg-slate-50);
font-family: Inter;
font-size: 11px;
font-style: normal;
font-weight: 600;
font-weight: 500;
line-height: 18px; /* 163.636% */
letter-spacing: 0.88px;
text-transform: uppercase;
@@ -149,11 +258,11 @@
}
.title {
color: #52575c;
color: var(--bg-slate-50);
font-family: Inter;
font-size: 11px;
font-style: normal;
font-weight: 600;
font-weight: 500;
line-height: 18px; /* 163.636% */
letter-spacing: 0.88px;
text-transform: uppercase;
@@ -299,6 +408,38 @@
box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2);
.font-size-dropdown {
.back-btn {
.text {
color: var(--bg-ink-400);
}
}
.content {
.option-btn {
.text {
color: var(--bg-ink-400);
}
.text:hover {
color: var(--bg-ink-300);
}
}
}
}
.font-size-container {
.title {
color: var(--bg-ink-100);
}
.value {
.font-value {
color: var(--bg-ink-400);
}
}
}
.horizontal-line {
background: var(--bg-vanilla-300);
}

View File

@@ -3,12 +3,12 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
import './LogsFormatOptionsMenu.styles.scss';
import { Divider, Input, InputNumber, Tooltip } from 'antd';
import { Button, Divider, Input, InputNumber, Tooltip, Typography } from 'antd';
import cx from 'classnames';
import { LogViewMode } from 'container/LogsTable';
import { OptionsMenuConfig } from 'container/OptionsMenu/types';
import { FontSize, OptionsMenuConfig } from 'container/OptionsMenu/types';
import useDebouncedFn from 'hooks/useDebouncedFunction';
import { Check, Minus, Plus, X } from 'lucide-react';
import { Check, ChevronLeft, ChevronRight, Minus, Plus, X } from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
interface LogsFormatOptionsMenuProps {
@@ -24,10 +24,16 @@ export default function LogsFormatOptionsMenu({
selectedOptionFormat,
config,
}: LogsFormatOptionsMenuProps): JSX.Element {
const { maxLines, format, addColumn } = config;
const { maxLines, format, addColumn, fontSize } = config;
const [selectedItem, setSelectedItem] = useState(selectedOptionFormat);
const maxLinesNumber = (maxLines?.value as number) || 1;
const [maxLinesPerRow, setMaxLinesPerRow] = useState<number>(maxLinesNumber);
const [fontSizeValue, setFontSizeValue] = useState<FontSize>(
fontSize?.value || FontSize.SMALL,
);
const [isFontSizeOptionsOpen, setIsFontSizeOptionsOpen] = useState<boolean>(
false,
);
const [addNewColumn, setAddNewColumn] = useState(false);
@@ -88,6 +94,12 @@ export default function LogsFormatOptionsMenu({
}
}, [maxLinesPerRow]);
useEffect(() => {
if (fontSizeValue && config && config.fontSize?.onChange) {
config.fontSize.onChange(fontSizeValue);
}
}, [fontSizeValue]);
return (
<div
className={cx('nested-menu-container', addNewColumn ? 'active' : '')}
@@ -96,145 +108,213 @@ export default function LogsFormatOptionsMenu({
event.stopPropagation();
}}
>
<div className="menu-container">
<div className="title"> {title} </div>
<div className="menu-items">
{items.map(
(item: any): JSX.Element => (
<div
className="item"
key={item.label}
onClick={(): void => handleMenuItemClick(item.key)}
>
<div className={cx('item-label')}>
{item.label}
{selectedItem === item.key && <Check size={12} />}
</div>
</div>
),
)}
{isFontSizeOptionsOpen ? (
<div className="font-size-dropdown">
<Button
onClick={(): void => setIsFontSizeOptionsOpen(false)}
className="back-btn"
type="text"
>
<ChevronLeft size={14} className="icon" />
<Typography.Text className="text">Select font size</Typography.Text>
</Button>
<div className="horizontal-line" />
<div className="content">
<Button
onClick={(): void => {
setFontSizeValue(FontSize.SMALL);
}}
className="option-btn"
type="text"
>
<Typography.Text className="text">{FontSize.SMALL}</Typography.Text>
{fontSizeValue === FontSize.SMALL && (
<Check size={14} className="icon" />
)}
</Button>
<Button
onClick={(): void => {
setFontSizeValue(FontSize.MEDIUM);
}}
className="option-btn"
type="text"
>
<Typography.Text className="text">{FontSize.MEDIUM}</Typography.Text>
{fontSizeValue === FontSize.MEDIUM && (
<Check size={14} className="icon" />
)}
</Button>
<Button
onClick={(): void => {
setFontSizeValue(FontSize.LARGE);
}}
className="option-btn"
type="text"
>
<Typography.Text className="text">{FontSize.LARGE}</Typography.Text>
{fontSizeValue === FontSize.LARGE && (
<Check size={14} className="icon" />
)}
</Button>
</div>
</div>
</div>
{selectedItem && (
) : (
<>
<>
<div className="horizontal-line" />
<div className="max-lines-per-row">
<div className="title"> max lines per row </div>
<div className="raw-format max-lines-per-row-input">
<button
type="button"
className="periscope-btn"
onClick={decrementMaxLinesPerRow}
>
{' '}
<Minus size={12} />{' '}
</button>
<InputNumber
min={1}
max={10}
value={maxLinesPerRow}
onChange={handleLinesPerRowChange}
/>
<button
type="button"
className="periscope-btn"
onClick={incrementMaxLinesPerRow}
>
{' '}
<Plus size={12} />{' '}
</button>
</div>
</div>
</>
<div className="font-size-container">
<div className="title">Font Size</div>
<Button
className="value"
type="text"
onClick={(): void => {
setIsFontSizeOptionsOpen(true);
}}
>
<Typography.Text className="font-value">{fontSizeValue}</Typography.Text>
<ChevronRight size={14} className="icon" />
</Button>
</div>
<div className="horizontal-line" />
<div className="menu-container">
<div className="title"> {title} </div>
<div className="selected-item-content-container active">
{!addNewColumn && <div className="horizontal-line" />}
<div className="menu-items">
{items.map(
(item: any): JSX.Element => (
<div
className="item"
key={item.label}
onClick={(): void => handleMenuItemClick(item.key)}
>
<div className={cx('item-label')}>
{item.label}
{addNewColumn && (
<div className="add-new-column-header">
<div className="title">
{' '}
columns
<X size={14} onClick={handleToggleAddNewColumn} />{' '}
</div>
<Input
tabIndex={0}
type="text"
autoFocus
onFocus={addColumn?.onFocus}
onChange={handleSearchValueChange}
placeholder="Search..."
/>
</div>
)}
<div className="item-content">
{!addNewColumn && (
<div className="title">
columns
<Plus size={14} onClick={handleToggleAddNewColumn} />{' '}
</div>
)}
<div className="column-format">
{addColumn?.value?.map(({ key, id }) => (
<div className="column-name" key={id}>
<div className="name">
<Tooltip placement="left" title={key}>
{key}
</Tooltip>
{selectedItem === item.key && <Check size={12} />}
</div>
<X
className="delete-btn"
size={14}
onClick={(): void => addColumn.onRemove(id as string)}
/>
</div>
))}
</div>
{addColumn?.isFetching && (
<div className="loading-container"> Loading ... </div>
)}
{addNewColumn &&
addColumn &&
addColumn.value.length > 0 &&
addColumn.options &&
addColumn?.options?.length > 0 && (
<Divider className="column-divider" />
)}
{addNewColumn && (
<div className="column-format-new-options">
{addColumn?.options?.map(({ label, value }) => (
<div
className="column-name"
key={value}
onClick={(eve): void => {
eve.stopPropagation();
if (addColumn && addColumn?.onSelect) {
addColumn?.onSelect(value, { label, disabled: false });
}
}}
>
<div className="name">
<Tooltip placement="left" title={label}>
{label}
</Tooltip>
</div>
</div>
))}
</div>
),
)}
</div>
</div>
{selectedItem && (
<>
<>
<div className="horizontal-line" />
<div className="max-lines-per-row">
<div className="title"> max lines per row </div>
<div className="raw-format max-lines-per-row-input">
<button
type="button"
className="periscope-btn"
onClick={decrementMaxLinesPerRow}
>
{' '}
<Minus size={12} />{' '}
</button>
<InputNumber
min={1}
max={10}
value={maxLinesPerRow}
onChange={handleLinesPerRowChange}
/>
<button
type="button"
className="periscope-btn"
onClick={incrementMaxLinesPerRow}
>
{' '}
<Plus size={12} />{' '}
</button>
</div>
</div>
</>
<div className="selected-item-content-container active">
{!addNewColumn && <div className="horizontal-line" />}
{addNewColumn && (
<div className="add-new-column-header">
<div className="title">
{' '}
columns
<X size={14} onClick={handleToggleAddNewColumn} />{' '}
</div>
<Input
tabIndex={0}
type="text"
autoFocus
onFocus={addColumn?.onFocus}
onChange={handleSearchValueChange}
placeholder="Search..."
/>
</div>
)}
<div className="item-content">
{!addNewColumn && (
<div className="title">
columns
<Plus size={14} onClick={handleToggleAddNewColumn} />{' '}
</div>
)}
<div className="column-format">
{addColumn?.value?.map(({ key, id }) => (
<div className="column-name" key={id}>
<div className="name">
<Tooltip placement="left" title={key}>
{key}
</Tooltip>
</div>
<X
className="delete-btn"
size={14}
onClick={(): void => addColumn.onRemove(id as string)}
/>
</div>
))}
</div>
{addColumn?.isFetching && (
<div className="loading-container"> Loading ... </div>
)}
{addNewColumn &&
addColumn &&
addColumn.value.length > 0 &&
addColumn.options &&
addColumn?.options?.length > 0 && (
<Divider className="column-divider" />
)}
{addNewColumn && (
<div className="column-format-new-options">
{addColumn?.options?.map(({ label, value }) => (
<div
className="column-name"
key={value}
onClick={(eve): void => {
eve.stopPropagation();
if (addColumn && addColumn?.onSelect) {
addColumn?.onSelect(value, { label, disabled: false });
}
}}
>
<div className="name">
<Tooltip placement="left" title={label}>
{label}
</Tooltip>
</div>
</div>
))}
</div>
)}
</div>
</div>
</>
)}
</>
)}
</div>

View File

@@ -0,0 +1,54 @@
import TypicalOverlayScrollbar from 'components/TypicalOverlayScrollbar/TypicalOverlayScrollbar';
import VirtuosoOverlayScrollbar from 'components/VirtuosoOverlayScrollbar/VirtuosoOverlayScrollbar';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { PartialOptions } from 'overlayscrollbars';
import { CSSProperties, ReactElement, useMemo } from 'react';
type Props = {
children: ReactElement;
isVirtuoso?: boolean;
style?: CSSProperties;
options?: PartialOptions;
};
function OverlayScrollbar({
children,
isVirtuoso,
style,
options: customOptions,
}: Props): any {
const isDarkMode = useIsDarkMode();
const options = useMemo(
() =>
({
scrollbars: {
autoHide: 'scroll',
theme: isDarkMode ? 'os-theme-light' : 'os-theme-dark',
},
...(customOptions || {}),
} as PartialOptions),
[customOptions, isDarkMode],
);
if (isVirtuoso) {
return (
<VirtuosoOverlayScrollbar style={style} options={options}>
{children}
</VirtuosoOverlayScrollbar>
);
}
return (
<TypicalOverlayScrollbar style={style} options={options}>
{children}
</TypicalOverlayScrollbar>
);
}
OverlayScrollbar.defaultProps = {
isVirtuoso: false,
style: {},
options: {},
};
export default OverlayScrollbar;

View File

@@ -5,7 +5,7 @@ import { Button, Dropdown, Flex, MenuProps, Switch } from 'antd';
import { ColumnGroupType, ColumnType } from 'antd/es/table';
import { ColumnsType } from 'antd/lib/table';
import logEvent from 'api/common/logEvent';
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
import { SlidersHorizontal } from 'lucide-react';
import { memo, useEffect, useState } from 'react';
import { popupContainer } from 'utils/selectPopupContainer';
@@ -96,7 +96,7 @@ function DynamicColumnTable({
return (
<div className="DynamicColumnTable">
<Flex justify="flex-end" align="center" gap={8}>
{facingIssueBtn && <FacingIssueBtn {...facingIssueBtn} />}
{facingIssueBtn && <LaunchChatSupport {...facingIssueBtn} />}
{dynamicColumns && (
<Dropdown
getPopupContainer={popupContainer}

View File

@@ -3,6 +3,7 @@
import { Table } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import { dragColumnParams } from 'hooks/useDragColumns/configs';
import { set } from 'lodash-es';
import {
SyntheticEvent,
useCallback,
@@ -59,15 +60,21 @@ function ResizeTable({
[columnsData, onDragColumn, handleResize],
);
const tableParams = useMemo(
() => ({
const tableParams = useMemo(() => {
const props = {
...restProps,
components: { header: { cell: ResizableHeader } },
columns: mergedColumns,
pagination: { ...pagination, hideOnSinglePage: true },
}),
[mergedColumns, pagination, restProps],
);
};
set(
props,
'pagination',
pagination ? { ...pagination, hideOnSinglePage: true } : false,
);
return props;
}, [mergedColumns, pagination, restProps]);
useEffect(() => {
if (columns) {

View File

@@ -2,7 +2,7 @@
import { TableProps } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { ColumnGroupType, ColumnType } from 'antd/lib/table';
import { FacingIssueBtnProps } from 'components/facingIssueBtn/FacingIssueBtn';
import { LaunchChatSupportProps } from 'components/LaunchChatSupport/LaunchChatSupport';
import { TableDataSource } from './contants';
@@ -13,7 +13,7 @@ export interface DynamicColumnTableProps extends TableProps<any> {
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
dynamicColumns: TableProps<any>['columns'];
onDragColumn?: (fromIndex: number, toIndex: number) => void;
facingIssueBtn?: FacingIssueBtnProps;
facingIssueBtn?: LaunchChatSupportProps;
shouldSendAlertsLogEvent?: boolean;
}

View File

@@ -5,7 +5,6 @@ import { Button } from 'antd';
import { Tag } from 'antd/lib';
import Input from 'components/Input';
import { Check, X } from 'lucide-react';
import { TweenOneGroup } from 'rc-tween-one';
import React, { Dispatch, SetStateAction, useState } from 'react';
function Tags({ tags, setTags }: AddTagsProps): JSX.Element {
@@ -46,41 +45,19 @@ function Tags({ tags, setTags }: AddTagsProps): JSX.Element {
func(value);
};
const forMap = (tag: string): React.ReactElement => (
<span key={tag} style={{ display: 'inline-block' }}>
<Tag
closable
onClose={(e): void => {
e.preventDefault();
handleClose(tag);
}}
>
{tag}
</Tag>
</span>
);
const tagChild = tags.map(forMap);
const renderTagsAnimated = (): React.ReactElement => (
<TweenOneGroup
appear={false}
className="tags"
enter={{ scale: 0.8, opacity: 0, type: 'from', duration: 100 }}
leave={{ opacity: 0, width: 0, scale: 0, duration: 200 }}
onEnd={(e): void => {
if (e.type === 'appear' || e.type === 'enter') {
(e.target as any).style = 'display: inline-block';
}
}}
>
{tagChild}
</TweenOneGroup>
);
return (
<div className="tags-container">
{renderTagsAnimated()}
{tags.map<React.ReactNode>((tag) => (
<Tag
key={tag}
closable
style={{ userSelect: 'none' }}
onClose={(): void => handleClose(tag)}
>
<span>{tag}</span>
</Tag>
))}
{inputVisible && (
<div className="add-tag-container">
<Input

View File

@@ -0,0 +1,31 @@
import './typicalOverlayScrollbar.scss';
import { PartialOptions } from 'overlayscrollbars';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { CSSProperties, ReactElement } from 'react';
interface Props {
children: ReactElement;
style?: CSSProperties;
options?: PartialOptions;
}
export default function TypicalOverlayScrollbar({
children,
style,
options,
}: Props): ReturnType<typeof OverlayScrollbarsComponent> {
return (
<OverlayScrollbarsComponent
defer
options={options}
style={style}
className="overlay-scrollbar"
data-overlayscrollbars-initialize
>
{children}
</OverlayScrollbarsComponent>
);
}
TypicalOverlayScrollbar.defaultProps = { style: {}, options: {} };

View File

@@ -0,0 +1,3 @@
.overlay-scrollbar {
height: 100%;
}

View File

@@ -49,7 +49,10 @@ function ValueGraph({
}
>
<Tooltip title={t('this_value_satisfies_multiple_thresholds')}>
<ExclamationCircleFilled className="value-graph-icon" />
<ExclamationCircleFilled
className="value-graph-icon"
data-testid="conflicting-thresholds"
/>
</Tooltip>
</div>
)}

View File

@@ -0,0 +1,37 @@
import './virtuosoOverlayScrollbar.scss';
import useInitializeOverlayScrollbar from 'hooks/useInitializeOverlayScrollbar/useInitializeOverlayScrollbar';
import { PartialOptions } from 'overlayscrollbars';
import React, { CSSProperties, ReactElement } from 'react';
interface VirtuosoOverlayScrollbarProps {
children: ReactElement;
style?: CSSProperties;
options: PartialOptions;
}
export default function VirtuosoOverlayScrollbar({
children,
style,
options,
}: VirtuosoOverlayScrollbarProps): JSX.Element {
const { rootRef, setScroller } = useInitializeOverlayScrollbar(options);
const enhancedChild = React.cloneElement(children, {
scrollerRef: setScroller,
'data-overlayscrollbars-initialize': true,
});
return (
<div
data-overlayscrollbars-initialize
ref={rootRef}
className="overlay-scroll-wrapper"
style={style}
>
{enhancedChild}
</div>
);
}
VirtuosoOverlayScrollbar.defaultProps = { style: {} };

View File

@@ -0,0 +1,5 @@
.overlay-scroll-wrapper {
height: 100%;
width: 100%;
overflow: auto;
}

View File

@@ -1,67 +0,0 @@
import './FacingIssueBtn.style.scss';
import { Button, Tooltip } from 'antd';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';
import { FeatureKeys } from 'constants/features';
import useFeatureFlags from 'hooks/useFeatureFlag';
import { defaultTo } from 'lodash-es';
import { HelpCircle } from 'lucide-react';
import { isCloudUser } from 'utils/app';
export interface FacingIssueBtnProps {
eventName: string;
attributes: Record<string, unknown>;
message?: string;
buttonText?: string;
className?: string;
onHoverText?: string;
}
function FacingIssueBtn({
attributes,
eventName,
message = '',
buttonText = '',
className = '',
onHoverText = '',
}: FacingIssueBtnProps): JSX.Element | null {
const handleFacingIssuesClick = (): void => {
logEvent(eventName, attributes);
if (window.Intercom) {
window.Intercom('showNewMessage', defaultTo(message, ''));
}
};
const isChatSupportEnabled = useFeatureFlags(FeatureKeys.CHAT_SUPPORT)?.active;
const isCloudUserVal = isCloudUser();
return isCloudUserVal && isChatSupportEnabled ? ( // Note: we would need to move this condition to license based in future
<div className="facing-issue-button">
<Tooltip
title={onHoverText}
autoAdjustOverflow
style={{ padding: 8 }}
overlayClassName="tooltip-overlay"
>
<Button
className={cx('periscope-btn', 'facing-issue-button', className)}
onClick={handleFacingIssuesClick}
icon={<HelpCircle size={14} />}
>
{buttonText || 'Facing issues?'}
</Button>
</Tooltip>
</div>
) : null;
}
FacingIssueBtn.defaultProps = {
message: '',
buttonText: '',
className: '',
onHoverText: '',
};
export default FacingIssueBtn;

View File

@@ -3,4 +3,5 @@ export const ENVIRONMENT = {
process?.env?.FRONTEND_API_ENDPOINT ||
process?.env?.GITPOD_WORKSPACE_URL?.replace('://', '://8080-') ||
'',
wsURL: process?.env?.WEBSOCKET_API_ENDPOINT || '',
};

View File

@@ -19,6 +19,6 @@ export enum FeatureKeys {
OSS = 'OSS',
ONBOARDING = 'ONBOARDING',
CHAT_SUPPORT = 'CHAT_SUPPORT',
PLANNED_MAINTENANCE = 'PLANNED_MAINTENANCE',
GATEWAY = 'GATEWAY',
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
}

View File

@@ -32,4 +32,8 @@ export enum QueryParams {
relativeTime = 'relativeTime',
alertType = 'alertType',
ruleId = 'ruleId',
consumerGrp = 'consumerGrp',
topic = 'topic',
partition = 'partition',
selectedTimelineQuery = 'selectedTimelineQuery',
}

View File

@@ -52,7 +52,7 @@ export const selectValueDivider = '__';
export const baseAutoCompleteIdKeysOrder: (keyof Omit<
BaseAutocompleteData,
'id' | 'isJSON'
'id' | 'isJSON' | 'isIndexed'
>)[] = ['key', 'dataType', 'type', 'isColumn'];
export const autocompleteType: Record<AutocompleteType, AutocompleteType> = {
@@ -71,6 +71,7 @@ export const alphabet: string[] = alpha.map((str) => String.fromCharCode(str));
export enum QueryBuilderKeys {
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
GET_ATTRIBUTE_SUGGESTIONS = 'GET_ATTRIBUTE_SUGGESTIONS',
}
export const mapOfOperators = {

View File

@@ -23,6 +23,10 @@ export const metricQueryFunctionOptions: SelectOption<string, string>[] = [
value: QueryFunctionsTypes.ABSOLUTE,
label: 'Absolute',
},
{
value: QueryFunctionsTypes.RUNNING_DIFF,
label: 'Running Diff',
},
{
value: QueryFunctionsTypes.LOG_2,
label: 'Log2',
@@ -103,6 +107,9 @@ export const queryFunctionsTypesConfig: QueryFunctionConfigType = {
absolute: {
showInput: false,
},
runningDiff: {
showInput: false,
},
log2: {
showInput: false,
},

View File

@@ -8,4 +8,5 @@ export const REACT_QUERY_KEY = {
GET_FEATURES_FLAGS: 'GET_FEATURES_FLAGS',
DELETE_DASHBOARD: 'DELETE_DASHBOARD',
LOGS_PIPELINE_PREVIEW: 'LOGS_PIPELINE_PREVIEW',
GET_CONSUMER_LAG_DETAILS: 'GET_CONSUMER_LAG_DETAILS',
};

View File

@@ -54,6 +54,8 @@ const ROUTES = {
WORKSPACE_LOCKED: '/workspace-locked',
SHORTCUTS: '/shortcuts',
INTEGRATIONS: '/integrations',
MESSAGING_QUEUES: '/messaging-queues',
MESSAGING_QUEUES_DETAIL: '/messaging-queues/detail',
} as const;
export default ROUTES;

View File

@@ -9,6 +9,7 @@ export const GlobalShortcuts = {
NavigateToDashboards: 'd+shift',
NavigateToAlerts: 'a+shift',
NavigateToExceptions: 'e+shift',
NavigateToMessagingQueues: 'm+shift',
};
export const GlobalShortcutsName = {
@@ -19,6 +20,7 @@ export const GlobalShortcutsName = {
NavigateToDashboards: 'shift+d',
NavigateToAlerts: 'shift+a',
NavigateToExceptions: 'shift+e',
NavigateToMessagingQueues: 'shift+m',
};
export const GlobalShortcutsDescription = {
@@ -29,4 +31,5 @@ export const GlobalShortcutsDescription = {
NavigateToDashboards: 'Navigate to dashboards page',
NavigateToAlerts: 'Navigate to alerts page',
NavigateToExceptions: 'Navigate to Exceptions page',
NavigateToMessagingQueues: 'Navigate to Messaging Queues page',
};

View File

@@ -4,6 +4,7 @@ const userOS = getUserOperatingSystem();
export const LogsExplorerShortcuts = {
StageAndRunQuery: 'enter+meta',
FocusTheSearchBar: 's',
ShowAllFilters: '/+meta',
};
export const LogsExplorerShortcutsName = {
@@ -11,9 +12,11 @@ export const LogsExplorerShortcutsName = {
userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'
}+enter`,
FocusTheSearchBar: 's',
ShowAllFilters: `${userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'}+/`,
};
export const LogsExplorerShortcutsDescription = {
StageAndRunQuery: 'Stage and Run the current query',
FocusTheSearchBar: 'Shift the focus to the last query filter bar',
ShowAllFilters: 'Toggle all filters in the filters dropdown',
};

View File

@@ -0,0 +1,78 @@
import AlertChannels from 'container/AllAlertChannels';
import { allAlertChannels } from 'mocks-server/__mockdata__/alerts';
import { act, fireEvent, render, screen, waitFor } from 'tests/test-utils';
jest.mock('hooks/useFetch', () => ({
__esModule: true,
default: jest.fn().mockImplementation(() => ({
payload: allAlertChannels,
})),
}));
const successNotification = jest.fn();
jest.mock('hooks/useNotifications', () => ({
__esModule: true,
useNotifications: jest.fn(() => ({
notifications: {
success: successNotification,
error: jest.fn(),
},
})),
}));
describe('Alert Channels Settings List page', () => {
beforeEach(() => {
render(<AlertChannels />);
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('Should display the Alert Channels page properly', () => {
it('Should check if "The alerts will be sent to all the configured channels." is visible ', () => {
expect(screen.getByText('sending_channels_note')).toBeInTheDocument();
});
it('Should check if "New Alert Channel" Button is visble ', () => {
expect(screen.getByText('button_new_channel')).toBeInTheDocument();
});
it('Should check if the help icon is visible and displays "tooltip_notification_channels ', async () => {
const helpIcon = screen.getByLabelText('question-circle');
fireEvent.mouseOver(helpIcon);
await waitFor(() => {
const tooltip = screen.getByText('tooltip_notification_channels');
expect(tooltip).toBeInTheDocument();
});
});
});
describe('Should check if the channels table is properly displayed', () => {
it('Should check if the table columns are properly displayed', () => {
expect(screen.getByText('column_channel_name')).toBeInTheDocument();
expect(screen.getByText('column_channel_type')).toBeInTheDocument();
expect(screen.getByText('column_channel_action')).toBeInTheDocument();
});
it('Should check if the data in the table is displayed properly', () => {
expect(screen.getByText('Dummy-Channel')).toBeInTheDocument();
expect(screen.getAllByText('slack')[0]).toBeInTheDocument();
expect(screen.getAllByText('column_channel_edit')[0]).toBeInTheDocument();
expect(screen.getAllByText('Delete')[0]).toBeInTheDocument();
});
it('Should check if clicking on Delete displays Success Toast "Channel Deleted Successfully"', async () => {
const deleteButton = screen.getAllByRole('button', { name: 'Delete' })[0];
expect(deleteButton).toBeInTheDocument();
act(() => {
fireEvent.click(deleteButton);
});
await waitFor(() => {
expect(successNotification).toBeCalledWith({
message: 'Success',
description: 'channel_delete_success',
});
});
});
});
});

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