mirror of
https://github.com/SigNoz/signoz.git
synced 2026-05-07 02:50:31 +01:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f13b083277 | ||
|
|
efc7aff7a2 | ||
|
|
f04937bbd1 | ||
|
|
fce66a2c5c | ||
|
|
3b4fb3eb80 | ||
|
|
e3ddd0470e | ||
|
|
e88b6e400d | ||
|
|
857a1e9356 | ||
|
|
5c0096623c | ||
|
|
32380d5688 | ||
|
|
55a0cbc3f1 | ||
|
|
d319ce99d6 | ||
|
|
dc74592347 | ||
|
|
5ef07a229d | ||
|
|
17b4e69f8b | ||
|
|
aecd10ef24 | ||
|
|
7f0574061f | ||
|
|
71266836f8 | ||
|
|
4d4cd72ab2 | ||
|
|
b3f1fdefe6 | ||
|
|
5fea20f4e7 | ||
|
|
195035f035 | ||
|
|
5c283a3013 | ||
|
|
b81d3598bc | ||
|
|
01f1bee3cd | ||
|
|
077d23e7b4 | ||
|
|
212a36a233 | ||
|
|
d296ea9574 | ||
|
|
065bc3d55d | ||
|
|
e60f430d66 | ||
|
|
fca51d3d06 | ||
|
|
630bf74e4f | ||
|
|
109e4e0bed | ||
|
|
7b5bb46b84 | ||
|
|
bc5e409c73 | ||
|
|
ccc7cb5b42 | ||
|
|
9423531db1 | ||
|
|
3ec56c12dd | ||
|
|
27b4b98ed0 | ||
|
|
4eb3a4c570 | ||
|
|
cb2aec9139 |
2
.github/workflows/dependency-review.yml
vendored
2
.github/workflows/dependency-review.yml
vendored
@@ -19,4 +19,4 @@ jobs:
|
||||
- name: 'Dependency Review'
|
||||
with:
|
||||
fail-on-severity: high
|
||||
uses: actions/dependency-review-action@v3
|
||||
uses: actions/dependency-review-action@v2
|
||||
|
||||
11
.github/workflows/e2e-k3s.yaml
vendored
11
.github/workflows/e2e-k3s.yaml
vendored
@@ -15,11 +15,6 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup golang
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.21"
|
||||
|
||||
- name: Build query-service image
|
||||
env:
|
||||
DEV_BUILD: 1
|
||||
@@ -70,9 +65,9 @@ jobs:
|
||||
- name: Kick off a sample-app workload
|
||||
run: |
|
||||
# start the locust swarm
|
||||
kubectl --namespace sample-application run strzal --image=djbingham/curl \
|
||||
--restart='OnFailure' -i --tty --rm --command -- curl -X POST -F \
|
||||
'user_count=6' -F 'spawn_rate=2' http://locust-master:8089/swarm
|
||||
kubectl -n sample-application run strzal --image=djbingham/curl \
|
||||
--restart='OnFailure' -i --rm --command -- curl -X POST -F \
|
||||
'locust_count=6' -F 'hatch_rate=2' http://locust-master:8089/swarm
|
||||
|
||||
- name: Get short commit SHA, display tunnel URL and IP Address of the worker node
|
||||
id: get-subdomain
|
||||
|
||||
20
.github/workflows/push.yaml
vendored
20
.github/workflows/push.yaml
vendored
@@ -20,13 +20,13 @@ jobs:
|
||||
with:
|
||||
go-version: "1.21"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: latest
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -64,13 +64,13 @@ jobs:
|
||||
with:
|
||||
go-version: "1.21"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: latest
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -115,11 +115,11 @@ jobs:
|
||||
run: npm run lint
|
||||
continue-on-error: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: latest
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -164,11 +164,11 @@ jobs:
|
||||
run: npm run lint
|
||||
continue-on-error: true
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: latest
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
2
.github/workflows/staging-deployment.yaml
vendored
2
.github/workflows/staging-deployment.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
environment: staging
|
||||
steps:
|
||||
- name: Executing remote ssh commands using ssh key
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
uses: appleboy/ssh-action@v0.1.8
|
||||
env:
|
||||
GITHUB_BRANCH: develop
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
|
||||
7
.github/workflows/testing-deployment.yaml
vendored
7
.github/workflows/testing-deployment.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
if: ${{ github.event.label.name == 'testing-deploy' }}
|
||||
steps:
|
||||
- name: Executing remote ssh commands using ssh key
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
uses: appleboy/ssh-action@v0.1.8
|
||||
env:
|
||||
GITHUB_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
GITHUB_SHA: ${{ github.sha }}
|
||||
@@ -33,11 +33,8 @@ jobs:
|
||||
git add .
|
||||
git stash push -m "stashed on $(date --iso-8601=seconds)"
|
||||
git fetch origin
|
||||
git checkout develop
|
||||
git checkout ${GITHUB_BRANCH}
|
||||
git pull
|
||||
# This is added to include the scenerio when new commit in PR is force-pushed
|
||||
git branch -D ${GITHUB_BRANCH}
|
||||
git checkout --track origin/${GITHUB_BRANCH}
|
||||
make build-ee-query-service-amd64
|
||||
make build-frontend-amd64
|
||||
make run-signoz
|
||||
@@ -108,7 +108,7 @@ We support [OpenTelemetry](https://opentelemetry.io) as the library which you ca
|
||||
|
||||
- Java
|
||||
- Python
|
||||
- Node.js
|
||||
- NodeJS
|
||||
- Go
|
||||
- PHP
|
||||
- .NET
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.9"
|
||||
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
image: clickhouse/clickhouse-server:24.1.2-alpine
|
||||
image: clickhouse/clickhouse-server:23.11.1-alpine
|
||||
tty: true
|
||||
deploy:
|
||||
restart_policy:
|
||||
@@ -146,7 +146,7 @@ services:
|
||||
condition: on-failure
|
||||
|
||||
query-service:
|
||||
image: signoz/query-service:0.39.0
|
||||
image: signoz/query-service:0.37.1
|
||||
command:
|
||||
[
|
||||
"-config=/root/config/prometheus.yml",
|
||||
@@ -186,7 +186,7 @@ services:
|
||||
<<: *db-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:0.39.0
|
||||
image: signoz/frontend:0.37.1
|
||||
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.12
|
||||
image: signoz/signoz-otel-collector:0.88.8
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-config.yaml",
|
||||
@@ -237,7 +237,7 @@ services:
|
||||
- query-service
|
||||
|
||||
otel-collector-migrator:
|
||||
image: signoz/signoz-schema-migrator:0.88.12
|
||||
image: signoz/signoz-schema-migrator:0.88.8
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
||||
@@ -123,7 +123,15 @@ exporters:
|
||||
clickhouselogsexporter:
|
||||
dsn: tcp://clickhouse:9000/
|
||||
docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER}
|
||||
timeout: 10s
|
||||
timeout: 5s
|
||||
sending_queue:
|
||||
queue_size: 100
|
||||
retry_on_failure:
|
||||
enabled: true
|
||||
initial_interval: 5s
|
||||
max_interval: 30s
|
||||
max_elapsed_time: 300s
|
||||
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 0.0.0.0:13133
|
||||
|
||||
@@ -19,7 +19,7 @@ services:
|
||||
- ZOO_AUTOPURGE_INTERVAL=1
|
||||
|
||||
clickhouse:
|
||||
image: clickhouse/clickhouse-server:24.1.2-alpine
|
||||
image: clickhouse/clickhouse-server:23.7.3-alpine
|
||||
container_name: signoz-clickhouse
|
||||
# ports:
|
||||
# - "9000:9000"
|
||||
@@ -66,7 +66,7 @@ services:
|
||||
- --storage.path=/data
|
||||
|
||||
otel-collector-migrator:
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.12}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.8}
|
||||
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.12
|
||||
image: signoz/signoz-otel-collector:0.88.8
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-config.yaml",
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "2.4"
|
||||
x-clickhouse-defaults: &clickhouse-defaults
|
||||
restart: on-failure
|
||||
# addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab
|
||||
image: clickhouse/clickhouse-server:24.1.2-alpine
|
||||
image: clickhouse/clickhouse-server:23.11.1-alpine
|
||||
tty: true
|
||||
depends_on:
|
||||
- zookeeper-1
|
||||
@@ -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.39.0}
|
||||
image: signoz/query-service:${DOCKER_TAG:-0.37.1}
|
||||
container_name: signoz-query-service
|
||||
command:
|
||||
[
|
||||
@@ -203,7 +203,7 @@ services:
|
||||
<<: *db-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.39.0}
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.37.1}
|
||||
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.12}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.8}
|
||||
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.12}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.8}
|
||||
container_name: signoz-otel-collector
|
||||
command:
|
||||
[
|
||||
|
||||
@@ -135,7 +135,14 @@ exporters:
|
||||
clickhouselogsexporter:
|
||||
dsn: tcp://clickhouse:9000/
|
||||
docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER}
|
||||
timeout: 10s
|
||||
timeout: 5s
|
||||
sending_queue:
|
||||
queue_size: 100
|
||||
retry_on_failure:
|
||||
enabled: true
|
||||
initial_interval: 5s
|
||||
max_interval: 30s
|
||||
max_elapsed_time: 300s
|
||||
|
||||
service:
|
||||
telemetry:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# use a minimal alpine image
|
||||
FROM alpine:3.18.6
|
||||
FROM alpine:3.18.5
|
||||
|
||||
# Add Maintainer Info
|
||||
LABEL maintainer="signoz"
|
||||
|
||||
@@ -139,13 +139,17 @@ func (lm *Manager) UploadUsage() {
|
||||
|
||||
zap.S().Info("uploading usage data")
|
||||
|
||||
// Try to get the org name
|
||||
orgName := ""
|
||||
orgNames, orgError := lm.modelDao.GetOrgs(ctx)
|
||||
if orgError != nil {
|
||||
zap.S().Errorf("failed to get org data: %v", zap.Error(orgError))
|
||||
}
|
||||
if len(orgNames) == 1 {
|
||||
orgName = orgNames[0].Name
|
||||
orgNames, err := lm.modelDao.GetOrgs(ctx)
|
||||
if err != nil {
|
||||
zap.S().Errorf("failed to get org data: %v", zap.Error(err))
|
||||
} else {
|
||||
if len(orgNames) != 1 {
|
||||
zap.S().Errorf("expected one org but got %d orgs", len(orgNames))
|
||||
} else {
|
||||
orgName = orgNames[0].Name
|
||||
}
|
||||
}
|
||||
|
||||
usagesPayload := []model.Usage{}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^10.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-react": "0.288.0",
|
||||
"lucide-react": "0.321.0",
|
||||
"mini-css-extract-plugin": "2.4.5",
|
||||
"papaparse": "5.4.1",
|
||||
"react": "18.2.0",
|
||||
@@ -212,8 +212,7 @@
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.(js|jsx|ts|tsx)": [
|
||||
"eslint --fix",
|
||||
"sh scripts/typecheck-staged.sh"
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c1 2.538 2.5 2.962 3.5 3.808.942.78 1.481 1.845 1.5 2.961 0 1.122-.527 2.198-1.464 2.992C14.598 12.554 13.326 13 12 13s-2.598-.446-3.536-1.24C7.527 10.968 7 9.892 7 8.77c0-.255 0-.508.1-.762.085.25.236.48.443.673.207.193.463.342.75.437a2.334 2.334 0 001.767-.128c.263-.135.485-.32.65-.539.166-.22.269-.468.301-.727a1.452 1.452 0 00-.11-.765 1.699 1.699 0 00-.501-.644C8 4.115 11 2 12 2zM17 16l-5 6-5-6h10z" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
|
Before Width: | Height: | Size: 581 B |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
@@ -3,6 +3,7 @@
|
||||
"alert_channels": "Alert Channels",
|
||||
"organization_settings": "Organization Settings",
|
||||
"ingestion_settings": "Ingestion Settings",
|
||||
"api_keys": "API Keys",
|
||||
"my_settings": "My Settings",
|
||||
"overview_metrics": "Overview Metrics",
|
||||
"dbcall_metrics": "Database Calls",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"MY_SETTINGS": "SigNoz | My Settings",
|
||||
"ORG_SETTINGS": "SigNoz | Organization Settings",
|
||||
"INGESTION_SETTINGS": "SigNoz | Ingestion Settings",
|
||||
"API_KEYS": "SigNoz | API Keys",
|
||||
"SOMETHING_WENT_WRONG": "SigNoz | Something Went Wrong",
|
||||
"UN_AUTHORIZED": "SigNoz | Unauthorized",
|
||||
"NOT_FOUND": "SigNoz | Page Not Found",
|
||||
|
||||
3
frontend/public/locales/en/apiKeys.json
Normal file
3
frontend/public/locales/en/apiKeys.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"delete_confirm_message": "Are you sure you want to delete {{keyName}} key? Deleting a key is irreversible and cannot be undone."
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
"alert_channels": "Alert Channels",
|
||||
"organization_settings": "Organization Settings",
|
||||
"ingestion_settings": "Ingestion Settings",
|
||||
"api_keys": "API Keys",
|
||||
"my_settings": "My Settings",
|
||||
"overview_metrics": "Overview Metrics",
|
||||
"dbcall_metrics": "Database Calls",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"MY_SETTINGS": "SigNoz | My Settings",
|
||||
"ORG_SETTINGS": "SigNoz | Organization Settings",
|
||||
"INGESTION_SETTINGS": "SigNoz | Ingestion Settings",
|
||||
"API_KEYS": "SigNoz | API Keys",
|
||||
"SOMETHING_WENT_WRONG": "SigNoz | Something Went Wrong",
|
||||
"UN_AUTHORIZED": "SigNoz | Unauthorized",
|
||||
"NOT_FOUND": "SigNoz | Page Not Found",
|
||||
@@ -41,6 +42,5 @@
|
||||
"SUPPORT": "SigNoz | Support",
|
||||
"LOGS_SAVE_VIEWS": "SigNoz | Logs Save Views",
|
||||
"TRACES_SAVE_VIEWS": "SigNoz | Traces Save Views",
|
||||
"DEFAULT": "Open source Observability Platform | SigNoz",
|
||||
"SHORTCUTS": "SigNoz | Shortcuts"
|
||||
"DEFAULT": "Open source Observability Platform | SigNoz"
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
files="";
|
||||
|
||||
# lint-staged will pass all files in $1 $2 $3 etc. iterate and concat.
|
||||
for var in "$@"
|
||||
do
|
||||
files="$files \"$var\","
|
||||
done
|
||||
|
||||
# create temporary tsconfig which includes only passed files
|
||||
str="{
|
||||
\"extends\": \"./tsconfig.json\",
|
||||
\"include\": [\"src/types/global.d.ts\",\"src/typings/window.ts\", \"src/typings/chartjs-adapter-date-fns.d.ts\", \"src/typings/environment.ts\" ,$files]
|
||||
}"
|
||||
echo $str > tsconfig.tmp
|
||||
|
||||
# run typecheck using temp config
|
||||
tsc -p ./tsconfig.tmp
|
||||
|
||||
# capture exit code of tsc
|
||||
code=$?
|
||||
|
||||
# delete temp config
|
||||
rm ./tsconfig.tmp
|
||||
|
||||
exit $code
|
||||
@@ -98,6 +98,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
|
||||
if (
|
||||
userResponse &&
|
||||
route &&
|
||||
route.find((e) => e === userResponse.payload.role) === undefined
|
||||
) {
|
||||
history.push(ROUTES.UN_AUTHORIZED);
|
||||
|
||||
@@ -8,7 +8,6 @@ import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import ROUTES from 'constants/routes';
|
||||
import AppLayout from 'container/AppLayout';
|
||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||
import { KeyboardHotkeysProvider } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||
import { useThemeConfig } from 'hooks/useDarkMode';
|
||||
import useGetFeatureFlag from 'hooks/useGetFeatureFlag';
|
||||
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
|
||||
@@ -178,24 +177,22 @@ function App(): JSX.Element {
|
||||
<ResourceProvider>
|
||||
<QueryBuilderProvider>
|
||||
<DashboardProvider>
|
||||
<KeyboardHotkeysProvider>
|
||||
<AppLayout>
|
||||
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
|
||||
<Switch>
|
||||
{routes.map(({ path, component, exact }) => (
|
||||
<Route
|
||||
key={`${path}`}
|
||||
exact={exact}
|
||||
path={path}
|
||||
component={component}
|
||||
/>
|
||||
))}
|
||||
<AppLayout>
|
||||
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
|
||||
<Switch>
|
||||
{routes.map(({ path, component, exact }) => (
|
||||
<Route
|
||||
key={`${path}`}
|
||||
exact={exact}
|
||||
path={path}
|
||||
component={component}
|
||||
/>
|
||||
))}
|
||||
|
||||
<Route path="*" component={NotFound} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</AppLayout>
|
||||
</KeyboardHotkeysProvider>
|
||||
<Route path="*" component={NotFound} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</AppLayout>
|
||||
</DashboardProvider>
|
||||
</QueryBuilderProvider>
|
||||
</ResourceProvider>
|
||||
|
||||
@@ -118,6 +118,10 @@ export const IngestionSettings = Loadable(
|
||||
() => import(/* webpackChunkName: "Ingestion Settings" */ 'pages/Settings'),
|
||||
);
|
||||
|
||||
export const APIKeys = Loadable(
|
||||
() => import(/* webpackChunkName: "All Settings" */ 'pages/Settings'),
|
||||
);
|
||||
|
||||
export const MySettings = Loadable(
|
||||
() => import(/* webpackChunkName: "All MySettings" */ 'pages/MySettings'),
|
||||
);
|
||||
@@ -182,7 +186,3 @@ export const WorkspaceBlocked = Loadable(
|
||||
() =>
|
||||
import(/* webpackChunkName: "WorkspaceLocked" */ 'pages/WorkspaceLocked'),
|
||||
);
|
||||
|
||||
export const ShortcutsPage = Loadable(
|
||||
() => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Shortcuts'),
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import ROUTES from 'constants/routes';
|
||||
import Shortcuts from 'pages/Shortcuts/Shortcuts';
|
||||
import WorkspaceBlocked from 'pages/WorkspaceLocked';
|
||||
import { RouteProps } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
AllAlertChannels,
|
||||
AllErrors,
|
||||
APIKeys,
|
||||
BillingPage,
|
||||
CreateAlertChannelAlerts,
|
||||
CreateNewAlerts,
|
||||
@@ -236,6 +236,13 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: true,
|
||||
key: 'INGESTION_SETTINGS',
|
||||
},
|
||||
{
|
||||
path: ROUTES.API_KEYS,
|
||||
exact: true,
|
||||
component: APIKeys,
|
||||
isPrivate: true,
|
||||
key: 'API_KEYS',
|
||||
},
|
||||
{
|
||||
path: ROUTES.MY_SETTINGS,
|
||||
exact: true,
|
||||
@@ -320,13 +327,6 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: true,
|
||||
key: 'WORKSPACE_LOCKED',
|
||||
},
|
||||
{
|
||||
path: ROUTES.SHORTCUTS,
|
||||
exact: true,
|
||||
component: Shortcuts,
|
||||
isPrivate: true,
|
||||
key: 'SHORTCUTS',
|
||||
},
|
||||
];
|
||||
|
||||
export const SUPPORT_ROUTE: AppRoutes = {
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { popupContainer } from 'utils/selectPopupContainer';
|
||||
|
||||
import CustomTimePickerPopoverContent from './CustomTimePickerPopoverContent';
|
||||
@@ -69,7 +68,6 @@ function CustomTimePicker({
|
||||
const [inputErrorMessage, setInputErrorMessage] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const location = useLocation();
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
|
||||
const getSelectedTimeRangeLabel = (
|
||||
@@ -154,7 +152,7 @@ function CustomTimePicker({
|
||||
break;
|
||||
}
|
||||
|
||||
if (minTime && (!minTime.isValid() || minTime < maxAllowedMinTime)) {
|
||||
if (minTime && minTime < maxAllowedMinTime) {
|
||||
setInputStatus('error');
|
||||
onError(true);
|
||||
setInputErrorMessage('Please enter time less than 6 months');
|
||||
@@ -224,15 +222,6 @@ function CustomTimePicker({
|
||||
setIsInputFocused(false);
|
||||
};
|
||||
|
||||
// this is required as TopNav component wraps the components and we need to clear the state on path change
|
||||
useEffect(() => {
|
||||
setInputStatus('');
|
||||
onError(false);
|
||||
setInputErrorMessage(null);
|
||||
setInputValue('');
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location.pathname]);
|
||||
|
||||
return (
|
||||
<div className="custom-time-picker">
|
||||
<Popover
|
||||
|
||||
@@ -151,12 +151,6 @@
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
height: 46px;
|
||||
padding: var(--padding-1) var(--padding-2);
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ant-drawer-close {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.addToQueryContainer {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import './AddToQueryHOC.styles.scss';
|
||||
|
||||
import { Popover } from 'antd';
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import { memo, ReactNode, useCallback, useMemo } from 'react';
|
||||
|
||||
import { ButtonContainer } from './styles';
|
||||
|
||||
function AddToQueryHOC({
|
||||
fieldKey,
|
||||
fieldValue,
|
||||
@@ -19,12 +19,11 @@ 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}>
|
||||
<ButtonContainer size="small" type="text" onClick={handleQueryAdd}>
|
||||
<Popover placement="top" content={popOverContent}>
|
||||
{children}
|
||||
</Popover>
|
||||
</div>
|
||||
</ButtonContainer>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function LogLinesActionButtons({
|
||||
}: LogLinesActionButtonsProps): JSX.Element {
|
||||
return (
|
||||
<div className={`log-line-action-buttons ${customClassName}`}>
|
||||
<Tooltip title="Show in Context">
|
||||
<Tooltip title="Show Context">
|
||||
<Button
|
||||
size="small"
|
||||
icon={<TextSelect size={14} />}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||
// hooks
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||
import { isEmpty, isNumber, isUndefined } from 'lodash-es';
|
||||
import { isEmpty, isUndefined } from 'lodash-es';
|
||||
import {
|
||||
KeyboardEvent,
|
||||
MouseEvent,
|
||||
@@ -73,14 +73,7 @@ function RawLogView({
|
||||
|
||||
const attributesValues = updatedSelecedFields
|
||||
.map((field) => flattenLogData[field.name])
|
||||
.filter((attribute) => {
|
||||
// loadash isEmpty doesnot work with numbers
|
||||
if (isNumber(attribute)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isUndefined(attribute) && !isEmpty(attribute);
|
||||
});
|
||||
.filter((attribute) => !isUndefined(attribute) && !isEmpty(attribute));
|
||||
|
||||
let attributesText = attributesValues.join(' | ');
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
.menu-items {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
gap: 12px;
|
||||
flex-direction: column;
|
||||
margin-top: 12px;
|
||||
}
|
||||
@@ -49,7 +49,6 @@
|
||||
display: flex;
|
||||
color: var(--bg-vanilla-400, #c0c1c3);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
@@ -89,6 +88,8 @@
|
||||
.max-lines-per-row-input {
|
||||
display: flex;
|
||||
|
||||
border: 1px solid var(--bg-slate-400, #1d212d);
|
||||
|
||||
.ant-input-number-handler-wrap {
|
||||
display: none;
|
||||
}
|
||||
@@ -96,10 +97,7 @@
|
||||
.ant-input-number {
|
||||
min-width: 36px;
|
||||
width: auto;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
border-top: 1px solid var(--bg-slate-400);
|
||||
border-bottom: 1px solid var(--bg-slate-400);
|
||||
border: 0px;
|
||||
text-align: center;
|
||||
height: 26px;
|
||||
border-radius: 0;
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
.DynamicColumnTable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
.dynamicColumnTable-button {
|
||||
align-self: flex-end;
|
||||
margin: 10px 0;
|
||||
|
||||
&.filter-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.dynamicColumnTable-button {
|
||||
align-self: flex-end;
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dynamicColumnsTable-items {
|
||||
display: flex;
|
||||
width: 10.625rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 10.625rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.dynamicColumnsTable-items {
|
||||
flex-direction: column;
|
||||
width: auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.dynamicColumnsTable-items {
|
||||
flex-direction: column;
|
||||
width: auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import './DynamicColumnTable.syles.scss';
|
||||
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { Button, Dropdown, MenuProps, Switch } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { SlidersHorizontal } from 'lucide-react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { popupContainer } from 'utils/selectPopupContainer';
|
||||
|
||||
@@ -90,9 +90,9 @@ function DynamicColumnTable({
|
||||
trigger={['click']}
|
||||
>
|
||||
<Button
|
||||
className="dynamicColumnTable-button filter-btn"
|
||||
className="dynamicColumnTable-button"
|
||||
size="middle"
|
||||
icon={<SlidersHorizontal size={14} />}
|
||||
icon={<SettingOutlined />}
|
||||
/>
|
||||
</Dropdown>
|
||||
)}
|
||||
|
||||
@@ -73,12 +73,21 @@ function ResizeTable({
|
||||
}
|
||||
}, [columns]);
|
||||
|
||||
const paginationConfig = tableParams.pagination
|
||||
? {
|
||||
hideOnSinglePage: true,
|
||||
showTotal: (total: number, range: number[]): string =>
|
||||
`${range[0]}-${range[1]} of ${total} items`,
|
||||
...tableParams.pagination,
|
||||
}
|
||||
: tableParams.pagination;
|
||||
|
||||
return onDragColumn ? (
|
||||
<ReactDragListView.DragColumn {...dragColumnParams} onDragEnd={onDragColumn}>
|
||||
<Table {...tableParams} />
|
||||
<Table {...tableParams} pagination={paginationConfig} />
|
||||
</ReactDragListView.DragColumn>
|
||||
) : (
|
||||
<Table {...tableParams} />
|
||||
<Table {...tableParams} pagination={paginationConfig} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,5 +15,4 @@ export enum LOCALSTORAGE {
|
||||
LOGGED_IN_USER_EMAIL = 'LOGGED_IN_USER_EMAIL',
|
||||
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
||||
IS_IDENTIFIED_USER = 'IS_IDENTIFIED_USER',
|
||||
DASHBOARD_VARIABLES = 'DASHBOARD_VARIABLES',
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ const ROUTES = {
|
||||
MY_SETTINGS: '/my-settings',
|
||||
SETTINGS: '/settings',
|
||||
ORG_SETTINGS: '/settings/org-settings',
|
||||
API_KEYS: '/settings/api-keys',
|
||||
INGESTION_SETTINGS: '/settings/ingestion-settings',
|
||||
SOMETHING_WENT_WRONG: '/something-went-wrong',
|
||||
UN_AUTHORIZED: '/un-authorized',
|
||||
@@ -44,7 +45,6 @@ const ROUTES = {
|
||||
LOGS_SAVE_VIEWS: '/logs-save-views',
|
||||
TRACES_SAVE_VIEWS: '/traces-save-views',
|
||||
WORKSPACE_LOCKED: '/workspace-locked',
|
||||
SHORTCUTS: '/shortcuts',
|
||||
} as const;
|
||||
|
||||
export default ROUTES;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { getUserOperatingSystem, UserOperatingSystem } from 'utils/getUserOS';
|
||||
|
||||
const userOS = getUserOperatingSystem();
|
||||
export const GlobalShortcuts = {
|
||||
SidebarCollapse: '\\+meta',
|
||||
NavigateToServices: 's+shift',
|
||||
NavigateToTraces: 't+shift',
|
||||
NavigateToLogs: 'l+shift',
|
||||
NavigateToDashboards: 'd+shift',
|
||||
NavigateToAlerts: 'a+shift',
|
||||
NavigateToExceptions: 'e+shift',
|
||||
};
|
||||
|
||||
export const GlobalShortcutsName = {
|
||||
SidebarCollapse: `${userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'}+\\`,
|
||||
NavigateToServices: 'shift+s',
|
||||
NavigateToTraces: 'shift+t',
|
||||
NavigateToLogs: 'shift+l',
|
||||
NavigateToDashboards: 'shift+d',
|
||||
NavigateToAlerts: 'shift+a',
|
||||
NavigateToExceptions: 'shift+e',
|
||||
};
|
||||
|
||||
export const GlobalShortcutsDescription = {
|
||||
SidebarCollapse: 'Collpase the sidebar',
|
||||
NavigateToServices: 'Navigate to Services page',
|
||||
NavigateToTraces: 'Navigate to Traces page',
|
||||
NavigateToLogs: 'Navigate to logs page',
|
||||
NavigateToDashboards: 'Navigate to dashboards page',
|
||||
NavigateToAlerts: 'Navigate to alerts page',
|
||||
NavigateToExceptions: 'Navigate to Exceptions page',
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
import { getUserOperatingSystem, UserOperatingSystem } from 'utils/getUserOS';
|
||||
|
||||
const userOS = getUserOperatingSystem();
|
||||
export const LogsExplorerShortcuts = {
|
||||
StageAndRunQuery: 'enter+meta',
|
||||
FocusTheSearchBar: 's',
|
||||
};
|
||||
|
||||
export const LogsExplorerShortcutsName = {
|
||||
StageAndRunQuery: `${
|
||||
userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'
|
||||
}+enter`,
|
||||
FocusTheSearchBar: 's',
|
||||
};
|
||||
|
||||
export const LogsExplorerShortcutsDescription = {
|
||||
StageAndRunQuery: 'Stage and Run the current query',
|
||||
FocusTheSearchBar: 'Shift the focus to the last query filter bar',
|
||||
};
|
||||
447
frontend/src/container/APIKeys/APIKeys.styles.scss
Normal file
447
frontend/src/container/APIKeys/APIKeys.styles.scss
Normal file
@@ -0,0 +1,447 @@
|
||||
.api-key-container {
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
|
||||
.api-key-content {
|
||||
width: calc(100% - 30px);
|
||||
max-width: 736px;
|
||||
|
||||
.title {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-size: var(--font-size-lg);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: 28px; /* 155.556% */
|
||||
letter-spacing: -0.09px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: var(---bg-vanilla-400);
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.api-keys-search-add-new {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
padding: 16px 0;
|
||||
|
||||
.add-new-api-key-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-table-row {
|
||||
.ant-table-cell {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: var(--bg-ink-500);
|
||||
}
|
||||
.column-render {
|
||||
margin: 8px 0 !important;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
|
||||
.title-with-action {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
|
||||
.api-key-data {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
|
||||
.api-key-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
.ant-typography {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
|
||||
.api-key-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
border-radius: 20px;
|
||||
padding: 0px 12px;
|
||||
|
||||
background: var(--bg-ink-200, #23262e);
|
||||
|
||||
.ant-typography {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-size: var(--font-size-xs);
|
||||
font-family: 'Space Mono', monospace;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.copy-key-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.visibility-btn {
|
||||
border: 1px solid rgba(113, 144, 249, 0.2);
|
||||
background: rgba(113, 144, 249, 0.1);
|
||||
}
|
||||
}
|
||||
.api-key-details {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--bg-slate-500, #161922);
|
||||
padding: 8px;
|
||||
|
||||
.api-key-tag {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50px;
|
||||
background: var(--bg-slate-300);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.tag-text {
|
||||
color: var(--bg-vanilla-400);
|
||||
leading-trim: both;
|
||||
text-edge: cap;
|
||||
font-size: 10px;
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: normal;
|
||||
letter-spacing: -0.05px;
|
||||
}
|
||||
}
|
||||
|
||||
.api-key-created-by {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.api-key-created-at {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.ant-typography {
|
||||
margin-left: 6px;
|
||||
color: var(--bg-vanilla-400);
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: 18px; /* 128.571% */
|
||||
letter-spacing: -0.07px;
|
||||
font-variant-numeric: lining-nums tabular-nums stacked-fractions
|
||||
slashed-zero;
|
||||
font-feature-settings: 'dlig' on, 'salt' on, 'cpsp' on, 'case' on;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-pagination-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
> a {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-variant-numeric: lining-nums tabular-nums slashed-zero;
|
||||
font-feature-settings: 'dlig' on, 'salt' on, 'case' on, 'cpsp' on;
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
}
|
||||
|
||||
.ant-pagination-item-active {
|
||||
background-color: var(--bg-robin-500);
|
||||
> a {
|
||||
color: var(--bg-ink-500) !important;
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.api-key-modal {
|
||||
.ant-modal-content {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--Slate-500, #161922);
|
||||
background: var(--Ink-400, #121317);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
padding: 0;
|
||||
|
||||
.ant-modal-header {
|
||||
background: none;
|
||||
border-bottom: 1px solid var(--Slate-500, #161922);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.ant-modal-close-x {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
padding: 16px;
|
||||
margin-top: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.api-key-access-role {
|
||||
.ant-radio-button-wrapper {
|
||||
font-size: 12px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.tab {
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
|
||||
&::before {
|
||||
background: var(--bg-slate-400);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: var(--Slate-400, #1d212d);
|
||||
}
|
||||
}
|
||||
|
||||
.role {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-api-key-modal {
|
||||
width: calc(100% - 30px) !important; /* Adjust the 20px as needed */
|
||||
max-width: 384px;
|
||||
.ant-modal-content {
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
background: var(--bg-ink-400);
|
||||
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
|
||||
.ant-modal-header {
|
||||
padding: 16px;
|
||||
background: var(--bg-ink-400);
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: 0px 16px 28px 16px;
|
||||
|
||||
.ant-typography {
|
||||
color: var(--bg-vanilla-400);
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
line-height: 20px;
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.api-key-input {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ant-color-picker-trigger {
|
||||
padding: 6px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: var(--bg-ink-300);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
.ant-color-picker-color-block {
|
||||
border-radius: 50px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.ant-color-picker-color-block-inner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 16px;
|
||||
margin: 0;
|
||||
|
||||
.cancel-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-slate-500);
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-cherry-500);
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
color: var(--bg-vanilla-100);
|
||||
background: var(--bg-cherry-600);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
color: var(--bg-vanilla-100);
|
||||
font-size: var(--font-size-sm);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: 20px; /* 142.857% */
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.api-key-container {
|
||||
.api-key-content {
|
||||
.title {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
|
||||
.ant-table-row {
|
||||
.ant-table-cell {
|
||||
background: var(--bg-vanilla-200);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.ant-table-cell {
|
||||
background: var(--bg-vanilla-200) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.column-render {
|
||||
border: 1px solid var(--bg-vanilla-200);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.title-with-action {
|
||||
.api-key-title {
|
||||
.ant-typography {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
.ant-typography {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.api-key-details {
|
||||
.api-key-tag {
|
||||
background: var(--bg-vanilla-200);
|
||||
.tag-text {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
|
||||
.api-key-created-by {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
|
||||
.api-key-created-at {
|
||||
.ant-typography {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delete-api-key-modal {
|
||||
.ant-modal-content {
|
||||
border: 1px solid var(--bg-vanilla-200);
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.ant-modal-header {
|
||||
background: var(--bg-vanilla-100);
|
||||
|
||||
.title {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
.ant-typography {
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
|
||||
.api-key-input {
|
||||
.ant-input {
|
||||
background: var(--bg-vanilla-200);
|
||||
color: var(--bg-ink-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-footer {
|
||||
.cancel-btn {
|
||||
background: var(--bg-vanilla-300);
|
||||
color: var(--bg-ink-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
412
frontend/src/container/APIKeys/APIKeys.tsx
Normal file
412
frontend/src/container/APIKeys/APIKeys.tsx
Normal file
@@ -0,0 +1,412 @@
|
||||
import './APIKeys.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Radio,
|
||||
Table,
|
||||
TableProps,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { getRandomColor } from 'container/ExplorerOptions/utils';
|
||||
import { useGetAllViews } from 'hooks/saveViews/useGetAllViews';
|
||||
import useErrorNotification from 'hooks/useErrorNotification';
|
||||
import {
|
||||
CalendarClock,
|
||||
Check,
|
||||
ClipboardEdit,
|
||||
Contact2,
|
||||
Copy,
|
||||
Eye,
|
||||
PenLine,
|
||||
Plus,
|
||||
Search,
|
||||
Trash2,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery';
|
||||
import { ViewProps } from 'types/api/saveViews/types';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
|
||||
function APIKeys(): JSX.Element {
|
||||
const sourcepage = 'traces';
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [activeViewKey, setActiveViewKey] = useState<string>('');
|
||||
const [newViewName, setNewViewName] = useState<string>('');
|
||||
const [color, setColor] = useState(Color.BG_SIENNA_500);
|
||||
|
||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [activeViewName, setActiveViewName] = useState<string>('');
|
||||
const [
|
||||
activeCompositeQuery,
|
||||
setActiveCompositeQuery,
|
||||
] = useState<ICompositeMetricQuery | null>(null);
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const [dataSource, setDataSource] = useState<ViewProps[]>([]);
|
||||
const { t } = useTranslation(['apiKeys']);
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const hideDeleteViewModal = (): void => {
|
||||
setIsDeleteModalOpen(false);
|
||||
};
|
||||
|
||||
const handleDeleteModelOpen = (uuid: string, name: string): void => {
|
||||
setActiveViewKey(uuid);
|
||||
setActiveViewName(name);
|
||||
setIsDeleteModalOpen(true);
|
||||
};
|
||||
|
||||
const hideEditViewModal = (): void => {
|
||||
setIsEditModalOpen(false);
|
||||
};
|
||||
|
||||
const hideAddViewModal = (): void => {
|
||||
setIsAddModalOpen(false);
|
||||
};
|
||||
|
||||
const handleEditModelOpen = (view: ViewProps, color: string): void => {
|
||||
setActiveViewKey(view.uuid);
|
||||
setColor(color);
|
||||
setActiveViewName(view.name);
|
||||
setNewViewName(view.name);
|
||||
setActiveCompositeQuery(view.compositeQuery);
|
||||
setIsEditModalOpen(true);
|
||||
};
|
||||
|
||||
const handleAddModelOpen = (): void => {
|
||||
setIsAddModalOpen(true);
|
||||
};
|
||||
|
||||
const { data: viewsData, isLoading, error, isRefetching } = useGetAllViews(
|
||||
sourcepage as DataSource,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setDataSource(viewsData?.data.data || []);
|
||||
}, [viewsData?.data.data]);
|
||||
|
||||
useErrorNotification(error);
|
||||
|
||||
const handleSearch = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
setSearchValue(e.target.value);
|
||||
const filteredData = viewsData?.data.data.filter((view) =>
|
||||
view.name.toLowerCase().includes(e.target.value.toLowerCase()),
|
||||
);
|
||||
setDataSource(filteredData || []);
|
||||
};
|
||||
|
||||
const clearSearch = (): void => {
|
||||
setSearchValue('');
|
||||
};
|
||||
|
||||
const onDeleteHandler = (): void => {
|
||||
console.log('on delete handler');
|
||||
clearSearch();
|
||||
};
|
||||
|
||||
const onUpdateApiKey = (): void => {
|
||||
console.log('update key');
|
||||
};
|
||||
|
||||
const columns: TableProps<ViewProps>['columns'] = [
|
||||
{
|
||||
title: 'API Key',
|
||||
key: 'api-key',
|
||||
render: (view: ViewProps): JSX.Element => {
|
||||
const extraData = view.extraData !== '' ? JSON.parse(view.extraData) : '';
|
||||
let bgColor = getRandomColor();
|
||||
if (extraData !== '') {
|
||||
bgColor = extraData.color;
|
||||
}
|
||||
|
||||
const timeOptions: Intl.DateTimeFormatOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
};
|
||||
const formattedTime = new Date(view.createdAt).toLocaleTimeString(
|
||||
'en-US',
|
||||
timeOptions,
|
||||
);
|
||||
|
||||
const dateOptions: Intl.DateTimeFormatOptions = {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
};
|
||||
|
||||
const formattedDate = new Date(view.createdAt).toLocaleDateString(
|
||||
'en-US',
|
||||
dateOptions,
|
||||
);
|
||||
|
||||
// Combine time and date
|
||||
const formattedDateAndTime = `${formattedDate} ${formattedTime} `;
|
||||
return (
|
||||
<div className="column-render">
|
||||
<div className="title-with-action">
|
||||
<div className="api-key-data">
|
||||
<div className="api-key-title">
|
||||
<Typography.Text>{view.name}</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div className="api-key-value">
|
||||
<Typography.Text>
|
||||
{view.name.substring(0, 2)}********
|
||||
{view.name.substring(view.name.length - 2).trim()}
|
||||
</Typography.Text>
|
||||
|
||||
<Copy className="copy-key-btn" size={12} />
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="small"
|
||||
className="periscope-btn primary visibility-btn"
|
||||
shape="circle"
|
||||
icon={<Eye size={12} color={Color.BG_ROBIN_400} />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="action-btn">
|
||||
<PenLine
|
||||
size={14}
|
||||
onClick={(): void => handleEditModelOpen(view, bgColor)}
|
||||
/>
|
||||
|
||||
<Trash2
|
||||
size={14}
|
||||
color={Color.BG_CHERRY_500}
|
||||
onClick={(): void => handleDeleteModelOpen(view.uuid, view.name)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="api-key-details">
|
||||
<div className="api-key-created-at">
|
||||
<CalendarClock size={14} />
|
||||
Last used
|
||||
<Typography.Text>{formattedDateAndTime}</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="api-key-container">
|
||||
<div className="api-key-content">
|
||||
<header>
|
||||
<Typography.Title className="title">API Keys</Typography.Title>
|
||||
<Typography.Text className="subtitle">
|
||||
Create and manage access keys for the SigNoz API
|
||||
</Typography.Text>
|
||||
</header>
|
||||
|
||||
<div className="api-keys-search-add-new">
|
||||
<Input
|
||||
placeholder="Search for keys..."
|
||||
prefix={<Search size={12} color={Color.BG_VANILLA_400} />}
|
||||
value={searchValue}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="add-new-api-key-btn"
|
||||
type="primary"
|
||||
onClick={handleAddModelOpen}
|
||||
>
|
||||
{' '}
|
||||
<Plus size={14} /> New Key{' '}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
loading={isLoading || isRefetching}
|
||||
showHeader={false}
|
||||
pagination={{ pageSize: 5 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
className="delete-api-key-modal"
|
||||
title={<span className="title">Delete key</span>}
|
||||
open={isDeleteModalOpen}
|
||||
closable={false}
|
||||
onCancel={hideDeleteViewModal}
|
||||
footer={[
|
||||
<Button
|
||||
key="cancel"
|
||||
onClick={hideDeleteViewModal}
|
||||
className="cancel-btn"
|
||||
icon={<X size={16} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button
|
||||
key="submit"
|
||||
icon={<Trash2 size={16} />}
|
||||
onClick={onDeleteHandler}
|
||||
className="delete-btn"
|
||||
>
|
||||
Delete key
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Typography.Text className="delete-text">
|
||||
{t('delete_confirm_message', {
|
||||
keyName: activeViewName,
|
||||
})}
|
||||
</Typography.Text>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
className="api-key-modal"
|
||||
title="Edit key"
|
||||
open={isEditModalOpen}
|
||||
closable
|
||||
onCancel={hideEditViewModal}
|
||||
footer={[
|
||||
<Button
|
||||
className="periscope-btn primary"
|
||||
key="submit"
|
||||
type="primary"
|
||||
icon={<Check size={14} />}
|
||||
onClick={onUpdateApiKey}
|
||||
>
|
||||
Update key
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form form={form} layout="vertical" autoComplete="off">
|
||||
<Form.Item
|
||||
name="label"
|
||||
label="Label"
|
||||
rules={[
|
||||
{ required: true },
|
||||
{ type: 'url', warningOnly: true },
|
||||
{ type: 'string', min: 6 },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="Top Secret" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="role" label="Role">
|
||||
<Flex vertical gap="middle">
|
||||
<Radio.Group buttonStyle="solid" className="api-key-access-role">
|
||||
<Radio.Button value={USER_ROLES.ADMIN} className="tab">
|
||||
<div className="role">
|
||||
<Contact2 size={14} /> Admin
|
||||
</div>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={USER_ROLES.EDITOR} className="tab selected">
|
||||
<div className="role">
|
||||
{' '}
|
||||
<ClipboardEdit size={14} /> Editor
|
||||
</div>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={USER_ROLES.EDITOR} className="tab">
|
||||
<div className="role">
|
||||
{' '}
|
||||
<Eye size={14} /> Viewer
|
||||
</div>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
className="api-key-modal"
|
||||
title="Create new key"
|
||||
open={isAddModalOpen}
|
||||
closable
|
||||
onCancel={hideAddViewModal}
|
||||
footer={[
|
||||
<Button
|
||||
className="periscope-btn primary"
|
||||
key="submit"
|
||||
type="primary"
|
||||
icon={<Check size={14} />}
|
||||
onClick={onUpdateApiKey}
|
||||
>
|
||||
Create new key
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
initialValues={{
|
||||
role: USER_ROLES.ADMIN,
|
||||
}}
|
||||
layout="vertical"
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item
|
||||
name="label"
|
||||
label="Label"
|
||||
rules={[{ required: true }, { type: 'string', min: 6 }]}
|
||||
>
|
||||
<Input placeholder="Top Secret" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="role" label="Role">
|
||||
<Flex vertical gap="middle">
|
||||
<Radio.Group buttonStyle="solid" className="api-key-access-role">
|
||||
<Radio.Button
|
||||
value={USER_ROLES.ADMIN}
|
||||
className={cx(
|
||||
'tab',
|
||||
form.getFieldValue('role') === USER_ROLES.ADMIN ? 'selected' : '',
|
||||
)}
|
||||
>
|
||||
<div className="role">
|
||||
<Contact2 size={14} /> Admin
|
||||
</div>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={USER_ROLES.VIEWER} className="tab">
|
||||
<div className="role">
|
||||
{' '}
|
||||
<ClipboardEdit size={14} /> Editor
|
||||
</div>
|
||||
</Radio.Button>
|
||||
<Radio.Button value={USER_ROLES.EDITOR} className="tab">
|
||||
<div className="role">
|
||||
{' '}
|
||||
<Eye size={14} /> Viewer
|
||||
</div>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="expiration"
|
||||
label="Expiration"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<InputNumber min={1} max={100} defaultValue={30} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default APIKeys;
|
||||
@@ -64,9 +64,7 @@ function AlertChannels({ allChannels }: AlertChannelsProps): JSX.Element {
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ResizeTable columns={columns} dataSource={channels} rowKey="id" bordered />
|
||||
);
|
||||
return <ResizeTable columns={columns} dataSource={channels} rowKey="id" />;
|
||||
}
|
||||
|
||||
interface AlertChannelsProps {
|
||||
|
||||
@@ -29,7 +29,7 @@ import useErrorNotification from 'hooks/useErrorNotification';
|
||||
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
|
||||
import { Check, ConciergeBell, Disc3, Plus, X, XCircle } from 'lucide-react';
|
||||
import { Check, ConciergeBell, Disc3, Plus, X } from 'lucide-react';
|
||||
import { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
@@ -107,7 +107,7 @@ function ExplorerOptions({
|
||||
const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
|
||||
const viewKey = useGetSearchQueryParam(QueryParams.viewKey) || '';
|
||||
|
||||
const extraData = viewsData?.data?.data?.find((view) => view.uuid === viewKey)
|
||||
const extraData = viewsData?.data.data.find((view) => view.uuid === viewKey)
|
||||
?.extraData;
|
||||
|
||||
const extraDataColor = extraData ? JSON.parse(extraData).color : '';
|
||||
@@ -134,7 +134,7 @@ function ExplorerOptions({
|
||||
};
|
||||
|
||||
const onUpdateQueryHandler = (): void => {
|
||||
const extraData = viewsData?.data?.data?.find((view) => view.uuid === viewKey)
|
||||
const extraData = viewsData?.data.data.find((view) => view.uuid === viewKey)
|
||||
?.extraData;
|
||||
updateViewAsync(
|
||||
{
|
||||
@@ -166,7 +166,7 @@ function ExplorerOptions({
|
||||
({ key }: { key: string }): void => {
|
||||
const currentViewDetails = getViewDetailsUsingViewKey(
|
||||
key,
|
||||
viewsData?.data?.data,
|
||||
viewsData?.data.data,
|
||||
);
|
||||
if (!currentViewDetails) return;
|
||||
const {
|
||||
@@ -290,13 +290,11 @@ function ExplorerOptions({
|
||||
}}
|
||||
dropdownStyle={dropdownStyle}
|
||||
className="views-dropdown"
|
||||
allowClear={{
|
||||
clearIcon: <XCircle size={16} style={{ marginTop: '-3px' }} />,
|
||||
}}
|
||||
allowClear
|
||||
onClear={handleClearSelect}
|
||||
ref={ref}
|
||||
>
|
||||
{viewsData?.data?.data?.map((view) => {
|
||||
{viewsData?.data.data.map((view) => {
|
||||
const extraData =
|
||||
view.extraData !== '' ? JSON.parse(view.extraData) : '';
|
||||
let bgColor = getRandomColor();
|
||||
@@ -332,21 +330,13 @@ function ExplorerOptions({
|
||||
<hr />
|
||||
|
||||
<div className="actions">
|
||||
<Tooltip title="Create Alerts">
|
||||
<Button
|
||||
disabled={disabled}
|
||||
shape="circle"
|
||||
onClick={onCreateAlertsHandler}
|
||||
>
|
||||
<ConciergeBell size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button disabled={disabled} shape="circle" onClick={onCreateAlertsHandler}>
|
||||
<ConciergeBell size={16} />
|
||||
</Button>
|
||||
|
||||
<Tooltip title="Add to Dashboard">
|
||||
<Button disabled={disabled} shape="circle" onClick={onAddToDashboard}>
|
||||
<Plus size={16} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button disabled={disabled} shape="circle" onClick={onAddToDashboard}>
|
||||
<Plus size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,11 +8,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.prom-ql-icon {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
.ant-btn-default {
|
||||
border-color: transparent;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import './QuerySection.styles.scss';
|
||||
|
||||
import { Button, Tabs, Tooltip } from 'antd';
|
||||
import { Button, Tabs } from 'antd';
|
||||
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { Atom, Play, Terminal } from 'lucide-react';
|
||||
import { Atom, LucideAccessibility, Play, Terminal } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -55,21 +55,17 @@ function QuerySection({
|
||||
const tabs = [
|
||||
{
|
||||
label: (
|
||||
<Tooltip title="Query Builder">
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
),
|
||||
key: EQueryType.QUERY_BUILDER,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Tooltip title="ClickHouse">
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
</Button>
|
||||
),
|
||||
key: EQueryType.CLICKHOUSE,
|
||||
},
|
||||
@@ -79,31 +75,25 @@ function QuerySection({
|
||||
() => [
|
||||
{
|
||||
label: (
|
||||
<Tooltip title="Query Builder">
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
),
|
||||
key: EQueryType.QUERY_BUILDER,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Tooltip title="ClickHouse">
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
</Button>
|
||||
),
|
||||
key: EQueryType.CLICKHOUSE,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<Tooltip title="PromQL">
|
||||
<Button className="nav-btns">
|
||||
<img src="/Icons/promQL.svg" alt="Prom Ql" className="prom-ql-icon" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<LucideAccessibility size={14} />
|
||||
</Button>
|
||||
),
|
||||
key: EQueryType.PROM,
|
||||
},
|
||||
|
||||
@@ -83,7 +83,7 @@ function RuleOptions({
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
const renderMatchOpts = (): JSX.Element => (
|
||||
const renderThresholdMatchOpts = (): JSX.Element => (
|
||||
<InlineSelect
|
||||
getPopupContainer={popupContainer}
|
||||
defaultValue={defaultMatchType}
|
||||
@@ -98,13 +98,17 @@ function RuleOptions({
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
const onChangeEvalWindow = (value: string | unknown): void => {
|
||||
const ew = (value as string) || alertDef.evalWindow;
|
||||
setAlertDef({
|
||||
...alertDef,
|
||||
evalWindow: ew,
|
||||
});
|
||||
};
|
||||
const renderPromMatchOpts = (): JSX.Element => (
|
||||
<InlineSelect
|
||||
getPopupContainer={popupContainer}
|
||||
defaultValue={defaultMatchType}
|
||||
style={{ minWidth: '130px' }}
|
||||
value={alertDef.condition?.matchType}
|
||||
onChange={(value: string | unknown): void => handleMatchOptChange(value)}
|
||||
>
|
||||
<Select.Option value="1">{t('option_atleastonce')}</Select.Option>
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
const renderEvalWindows = (): JSX.Element => (
|
||||
<InlineSelect
|
||||
@@ -112,7 +116,13 @@ function RuleOptions({
|
||||
defaultValue={defaultEvalWindow}
|
||||
style={{ minWidth: '120px' }}
|
||||
value={alertDef.evalWindow}
|
||||
onChange={onChangeEvalWindow}
|
||||
onChange={(value: string | unknown): void => {
|
||||
const ew = (value as string) || alertDef.evalWindow;
|
||||
setAlertDef({
|
||||
...alertDef,
|
||||
evalWindow: ew,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Select.Option value="5m0s">{t('option_5min')}</Select.Option>
|
||||
<Select.Option value="10m0s">{t('option_10min')}</Select.Option>
|
||||
@@ -123,20 +133,6 @@ function RuleOptions({
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
const renderPromEvalWindows = (): JSX.Element => (
|
||||
<InlineSelect
|
||||
getPopupContainer={popupContainer}
|
||||
defaultValue={defaultEvalWindow}
|
||||
style={{ minWidth: '120px' }}
|
||||
value={alertDef.evalWindow}
|
||||
onChange={onChangeEvalWindow}
|
||||
>
|
||||
<Select.Option value="5m0s">{t('option_5min')}</Select.Option>
|
||||
<Select.Option value="10m0s">{t('option_10min')}</Select.Option>
|
||||
<Select.Option value="15m0s">{t('option_15min')}</Select.Option>
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
const renderThresholdRuleOpts = (): JSX.Element => (
|
||||
<Form.Item>
|
||||
<Typography.Text>
|
||||
@@ -151,7 +147,7 @@ function RuleOptions({
|
||||
onChange={onChangeSelectedQueryName}
|
||||
/>
|
||||
<Typography.Text>is</Typography.Text>
|
||||
{renderCompareOps()} {t('text_condition2')} {renderMatchOpts()}{' '}
|
||||
{renderCompareOps()} {t('text_condition2')} {renderThresholdMatchOpts()}{' '}
|
||||
{t('text_condition3')} {renderEvalWindows()}
|
||||
</Typography.Text>
|
||||
</Form.Item>
|
||||
@@ -171,8 +167,7 @@ function RuleOptions({
|
||||
onChange={onChangeSelectedQueryName}
|
||||
/>
|
||||
<Typography.Text>is</Typography.Text>
|
||||
{renderCompareOps()} {t('text_condition2')} {renderMatchOpts()}
|
||||
{t('text_condition3')} {renderPromEvalWindows()}
|
||||
{renderCompareOps()} {t('text_condition2')} {renderPromMatchOpts()}
|
||||
</Typography.Text>
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Col, Divider, Modal, Row, Spin, Typography } from 'antd';
|
||||
import setRetentionApi from 'api/settings/setRetention';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import GeneralSettingsCloud from 'container/GeneralSettingsCloud';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import find from 'lodash-es/find';
|
||||
@@ -25,7 +24,6 @@ import {
|
||||
PayloadPropsTraces as GetRetentionPeriodTracesPayload,
|
||||
} from 'types/api/settings/getRetention';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
|
||||
import Retention from './Retention';
|
||||
import StatusMessage from './StatusMessage';
|
||||
@@ -396,8 +394,6 @@ function GeneralSettings({
|
||||
onModalToggleHandler(type);
|
||||
};
|
||||
|
||||
const isCloudUserVal = isCloudUser();
|
||||
|
||||
const renderConfig = [
|
||||
{
|
||||
name: 'Metrics',
|
||||
@@ -525,7 +521,7 @@ function GeneralSettings({
|
||||
return (
|
||||
<Fragment key={category.name}>
|
||||
<Col xs={22} xl={11} key={category.name} style={{ margin: '0.5rem' }}>
|
||||
<Card style={{ height: '100%' }}>
|
||||
<Card style={{ height: '100%', minHeight: 300 }}>
|
||||
<Typography.Title style={{ margin: 0 }} level={3}>
|
||||
{category.name}
|
||||
</Typography.Title>
|
||||
@@ -546,43 +542,38 @@ function GeneralSettings({
|
||||
hide={!!retentionField.hide}
|
||||
/>
|
||||
))}
|
||||
|
||||
{!isCloudUserVal && (
|
||||
<>
|
||||
<ActionItemsContainer>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={category.save.modalOpen}
|
||||
disabled={category.save.isDisabled}
|
||||
>
|
||||
{category.save.saveButtonText}
|
||||
</Button>
|
||||
{category.statusComponent}
|
||||
</ActionItemsContainer>
|
||||
<Modal
|
||||
title={t('retention_confirmation')}
|
||||
focusTriggerAfterClose
|
||||
forceRender
|
||||
destroyOnClose
|
||||
closable
|
||||
onCancel={(): void =>
|
||||
onModalToggleHandler(category.name.toLowerCase() as TTTLType)
|
||||
}
|
||||
onOk={(): Promise<void> =>
|
||||
onOkHandler(category.name.toLowerCase() as TTTLType)
|
||||
}
|
||||
centered
|
||||
open={category.save.modal}
|
||||
confirmLoading={category.save.apiLoading}
|
||||
>
|
||||
<Typography>
|
||||
{t('retention_confirmation_description', {
|
||||
name: category.name.toLowerCase(),
|
||||
})}
|
||||
</Typography>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
<ActionItemsContainer>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={category.save.modalOpen}
|
||||
disabled={category.save.isDisabled}
|
||||
>
|
||||
{category.save.saveButtonText}
|
||||
</Button>
|
||||
{category.statusComponent}
|
||||
</ActionItemsContainer>
|
||||
<Modal
|
||||
title={t('retention_confirmation')}
|
||||
focusTriggerAfterClose
|
||||
forceRender
|
||||
destroyOnClose
|
||||
closable
|
||||
onCancel={(): void =>
|
||||
onModalToggleHandler(category.name.toLowerCase() as TTTLType)
|
||||
}
|
||||
onOk={(): Promise<void> =>
|
||||
onOkHandler(category.name.toLowerCase() as TTTLType)
|
||||
}
|
||||
centered
|
||||
open={category.save.modal}
|
||||
confirmLoading={category.save.apiLoading}
|
||||
>
|
||||
<Typography>
|
||||
{t('retention_confirmation_description', {
|
||||
name: category.name.toLowerCase(),
|
||||
})}
|
||||
</Typography>
|
||||
</Modal>
|
||||
</Card>
|
||||
</Col>
|
||||
</Fragment>
|
||||
@@ -596,20 +587,16 @@ function GeneralSettings({
|
||||
{Element}
|
||||
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
|
||||
<ErrorTextContainer>
|
||||
{!isCloudUserVal && (
|
||||
<TextToolTip
|
||||
{...{
|
||||
text: `More details on how to set retention period`,
|
||||
url: 'https://signoz.io/docs/userguide/retention-period/',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<TextToolTip
|
||||
{...{
|
||||
text: `More details on how to set retention period`,
|
||||
url: 'https://signoz.io/docs/userguide/retention-period/',
|
||||
}}
|
||||
/>
|
||||
{errorText && <ErrorText>{errorText}</ErrorText>}
|
||||
</ErrorTextContainer>
|
||||
|
||||
<Row justify="start">{renderConfig}</Row>
|
||||
|
||||
{isCloudUserVal && <GeneralSettingsCloud />}
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
|
||||
import {
|
||||
Input,
|
||||
@@ -86,13 +85,9 @@ function Retention({
|
||||
func(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (hide) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isCloudUserVal = isCloudUser();
|
||||
|
||||
return (
|
||||
<RetentionContainer>
|
||||
<Row justify="space-between">
|
||||
@@ -103,14 +98,12 @@ function Retention({
|
||||
<RetentionFieldInputContainer>
|
||||
<Input
|
||||
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
|
||||
disabled={isCloudUserVal}
|
||||
onChange={(e): void => onChangeHandler(e, setSelectedValue)}
|
||||
style={{ width: 75 }}
|
||||
/>
|
||||
<Select
|
||||
value={selectedTimeUnit}
|
||||
onChange={currentSelectedOption}
|
||||
disabled={isCloudUserVal}
|
||||
style={{ width: 100 }}
|
||||
>
|
||||
{menuItems}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { green, orange, volcano } from '@ant-design/colors';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Card, Col } from 'antd';
|
||||
import { Card, Col, Row } from 'antd';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TStatus } from 'types/api/settings/getRetention';
|
||||
@@ -43,16 +43,9 @@ function StatusMessage({
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '1rem',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Row style={{ gap: '1rem', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Col xs={1}>
|
||||
<InfoCircleOutlined style={{ fontSize: '1rem' }} />
|
||||
<InfoCircleOutlined style={{ fontSize: '1.5rem' }} />
|
||||
</Col>
|
||||
|
||||
<Col
|
||||
@@ -63,7 +56,7 @@ function StatusMessage({
|
||||
>
|
||||
{statusMessage}
|
||||
</Col>
|
||||
</div>
|
||||
</Row>
|
||||
</Card>
|
||||
) : null;
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
.general-settings-container {
|
||||
margin: 16px 8px;
|
||||
|
||||
.ant-card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 8px;
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import './GeneralSettingsCloud.styles.scss';
|
||||
|
||||
import { Card, Typography } from 'antd';
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
export default function GeneralSettingsCloud(): JSX.Element {
|
||||
return (
|
||||
<Card className="general-settings-container">
|
||||
<Info size={16} />
|
||||
<Typography.Text>
|
||||
Please <a href="mailto:cloud-support@signoz.io"> email us </a> or connect
|
||||
with us via intercom support to change the retention period.
|
||||
</Typography.Text>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import GeneralSettingsCloud from './GeneralSettingsCloud';
|
||||
|
||||
export default GeneralSettingsCloud;
|
||||
@@ -84,7 +84,6 @@ export default function IngestionSettings(): JSX.Element {
|
||||
pagination={false}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
bordered
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
.field-renderer-container {
|
||||
display: flex !important;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
|
||||
.label {
|
||||
color: var(--text-robin-400);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import './FieldRenderer.styles.scss';
|
||||
|
||||
import { Divider, Tooltip, Typography } from 'antd';
|
||||
import { Divider } from 'antd';
|
||||
|
||||
import { TagContainer, TagLabel, TagValue } from './FieldRenderer.styles';
|
||||
import { FieldRendererProps } from './LogDetailedView.types';
|
||||
@@ -13,11 +13,7 @@ function FieldRenderer({ field }: FieldRendererProps): JSX.Element {
|
||||
<span className="field-renderer-container">
|
||||
{dataType && newField && logType ? (
|
||||
<>
|
||||
<Tooltip placement="left" title={newField}>
|
||||
<Typography.Text ellipsis className="label">
|
||||
{newField}{' '}
|
||||
</Typography.Text>
|
||||
</Tooltip>
|
||||
<div className="label">{newField} </div>
|
||||
|
||||
<div className="tags">
|
||||
<TagContainer>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--bg-slate-500);
|
||||
|
||||
.wrap-word-switch {
|
||||
display: flex;
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
} from 'antd';
|
||||
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { ChevronDown, ChevronRight, Search } from 'lucide-react';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { Search } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
|
||||
import { ActionItemProps } from './ActionItem';
|
||||
@@ -99,10 +99,6 @@ function Overview({
|
||||
<div className="overview-container">
|
||||
<Collapse
|
||||
defaultActiveKey={['1']}
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
expandIcon={(props): ReactNode =>
|
||||
props.isActive ? <ChevronDown size={14} /> : <ChevronRight size={14} />
|
||||
}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
@@ -151,10 +147,6 @@ function Overview({
|
||||
className="attribute-table"
|
||||
defaultActiveKey={['1']}
|
||||
bordered={false}
|
||||
// eslint-disable-next-line react/no-unstable-nested-components
|
||||
expandIcon={(props): ReactNode =>
|
||||
props.isActive ? <ChevronDown size={14} /> : <ChevronRight size={14} />
|
||||
}
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
|
||||
@@ -2,7 +2,7 @@ import './TableView.styles.scss';
|
||||
|
||||
import { LinkOutlined } from '@ant-design/icons';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Space, Spin, Tooltip, Tree, Typography } from 'antd';
|
||||
import { Button, Space, Spin, Tooltip, Tree } from 'antd';
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import AddToQueryHOC, {
|
||||
AddToQueryHOCProps,
|
||||
@@ -144,7 +144,7 @@ function TableView({
|
||||
|
||||
return (
|
||||
<Space size="middle" className="log-attribute">
|
||||
<Typography.Text>{renderedField}</Typography.Text>
|
||||
{renderedField}
|
||||
|
||||
{traceId && (
|
||||
<Tooltip title="Inspect in Trace">
|
||||
|
||||
@@ -14,10 +14,7 @@ import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQ
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import {
|
||||
prepareQueryWithDefaultTimestamp,
|
||||
SELECTED_VIEWS,
|
||||
} from 'pages/LogsExplorer/utils';
|
||||
import { prepareQueryWithDefaultTimestamp } from 'pages/LogsExplorer/utils';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
@@ -25,7 +22,7 @@ import { DataSource } from 'types/common/queryBuilder';
|
||||
function LogExplorerQuerySection({
|
||||
selectedView,
|
||||
}: {
|
||||
selectedView: SELECTED_VIEWS;
|
||||
selectedView: string;
|
||||
}): JSX.Element {
|
||||
const { currentQuery, updateAllQueriesOperators } = useQueryBuilder();
|
||||
|
||||
@@ -87,7 +84,7 @@ function LogExplorerQuerySection({
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedView === SELECTED_VIEWS.SEARCH && (
|
||||
{selectedView === 'search' && (
|
||||
<div className="qb-search-view-container">
|
||||
<QueryBuilderSearch
|
||||
query={query}
|
||||
@@ -97,7 +94,7 @@ function LogExplorerQuerySection({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedView === SELECTED_VIEWS.QUERY_BUILDER && (
|
||||
{selectedView === 'query-builder' && (
|
||||
<QueryBuilder
|
||||
panelType={panelTypes}
|
||||
config={{ initialDataSource: DataSource.LOGS, queryVariant: 'static' }}
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import Graph from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import getChartData, { GetChartDataProps } from 'lib/getChartData';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import { colors } from 'lib/getRandomColor';
|
||||
import getTimeString from 'lib/getTimeString';
|
||||
import history from 'lib/history';
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
|
||||
import { LogsExplorerChartProps } from './LogsExplorerChart.interfaces';
|
||||
import { CardStyled } from './LogsExplorerChart.styled';
|
||||
@@ -22,9 +14,6 @@ function LogsExplorerChart({
|
||||
isLabelEnabled = true,
|
||||
className,
|
||||
}: LogsExplorerChartProps): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const urlQuery = useUrlQuery();
|
||||
const location = useLocation();
|
||||
const handleCreateDatasets: Required<GetChartDataProps>['createDataset'] = useCallback(
|
||||
(element, index, allLabels) => ({
|
||||
data: element,
|
||||
@@ -39,52 +28,6 @@ function LogsExplorerChart({
|
||||
[isLabelEnabled],
|
||||
);
|
||||
|
||||
const onDragSelect = useCallback(
|
||||
(start: number, end: number): void => {
|
||||
const startTimestamp = Math.trunc(start);
|
||||
const endTimestamp = Math.trunc(end);
|
||||
|
||||
if (startTimestamp !== endTimestamp) {
|
||||
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
|
||||
}
|
||||
|
||||
const { maxTime, minTime } = GetMinMax('custom', [
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
]);
|
||||
|
||||
urlQuery.set(QueryParams.startTime, minTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.push(generatedUrl);
|
||||
},
|
||||
[dispatch, location.pathname, urlQuery],
|
||||
);
|
||||
|
||||
const handleBackNavigation = (): void => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const startTime = searchParams.get(QueryParams.startTime);
|
||||
const endTime = searchParams.get(QueryParams.endTime);
|
||||
|
||||
if (startTime && endTime && startTime !== endTime) {
|
||||
dispatch(
|
||||
UpdateTimeInterval('custom', [
|
||||
parseInt(getTimeString(startTime), 10),
|
||||
parseInt(getTimeString(endTime), 10),
|
||||
]),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('popstate', handleBackNavigation);
|
||||
|
||||
return (): void => {
|
||||
window.removeEventListener('popstate', handleBackNavigation);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const graphData = useMemo(
|
||||
() =>
|
||||
getChartData({
|
||||
@@ -103,13 +46,7 @@ function LogsExplorerChart({
|
||||
{isLoading ? (
|
||||
<Spinner size="default" height="100%" />
|
||||
) : (
|
||||
<Graph
|
||||
name="logsExplorerChart"
|
||||
data={graphData.data}
|
||||
type="bar"
|
||||
animate
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
<Graph name="logsExplorerChart" data={graphData.data} type="bar" animate />
|
||||
)}
|
||||
</CardStyled>
|
||||
);
|
||||
|
||||
0
frontend/src/container/LogsExplorerList/Error.tsx
Normal file
0
frontend/src/container/LogsExplorerList/Error.tsx
Normal file
@@ -25,10 +25,6 @@
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
.ant-btn {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tab {
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
width: 114px;
|
||||
@@ -64,7 +60,7 @@
|
||||
align-items: center;
|
||||
|
||||
.ant-btn {
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.format-options-container {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import './LogsExplorerViews.styles.scss';
|
||||
|
||||
import { Button } from 'antd';
|
||||
import { Button, Radio } from 'antd';
|
||||
import { RadioChangeEvent } from 'antd/lib';
|
||||
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
|
||||
@@ -25,7 +26,6 @@ import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
||||
import { LogTimeRange } from 'hooks/logs/types';
|
||||
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import useAxiosError from 'hooks/useAxiosError';
|
||||
import useClickOutside from 'hooks/useClickOutside';
|
||||
@@ -33,9 +33,8 @@ import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
|
||||
import { defaultTo, isEmpty } from 'lodash-es';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { Sliders } from 'lucide-react';
|
||||
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
@@ -61,15 +60,12 @@ function LogsExplorerViews({
|
||||
selectedView,
|
||||
showHistogram,
|
||||
}: {
|
||||
selectedView: SELECTED_VIEWS;
|
||||
selectedView: string;
|
||||
showHistogram: boolean;
|
||||
}): JSX.Element {
|
||||
const { notifications } = useNotifications();
|
||||
const history = useHistory();
|
||||
|
||||
// this is to respect the panel type present in the URL rather than defaulting it to list always.
|
||||
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
|
||||
|
||||
const { activeLogId, timeRange, onTimeRangeChange } = useCopyLogLink();
|
||||
|
||||
const { queryData: pageSize } = useUrlQueryData(
|
||||
@@ -90,7 +86,6 @@ function LogsExplorerViews({
|
||||
stagedQuery,
|
||||
panelType,
|
||||
updateAllQueriesOperators,
|
||||
handleSetConfig,
|
||||
} = useQueryBuilder();
|
||||
|
||||
const [selectedPanelType, setSelectedPanelType] = useState<PANEL_TYPES>(
|
||||
@@ -176,13 +171,9 @@ function LogsExplorerViews({
|
||||
[currentQuery, updateAllQueriesOperators],
|
||||
);
|
||||
|
||||
const handleModeChange = (panelType: PANEL_TYPES): void => {
|
||||
if (selectedView === SELECTED_VIEWS.SEARCH) {
|
||||
handleSetConfig(panelType, DataSource.LOGS);
|
||||
}
|
||||
|
||||
const handleModeChange = (e: RadioChangeEvent): void => {
|
||||
setShowFormatMenuItems(false);
|
||||
handleExplorerTabChange(panelType);
|
||||
handleExplorerTabChange(e.target.value);
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -366,8 +357,7 @@ function LogsExplorerViews({
|
||||
|
||||
useEffect(() => {
|
||||
const shouldChangeView =
|
||||
(isMultipleQueries || isGroupByExist) &&
|
||||
selectedView !== SELECTED_VIEWS.SEARCH;
|
||||
(isMultipleQueries || isGroupByExist) && selectedView !== 'search';
|
||||
|
||||
if (selectedPanelType === PANEL_TYPES.LIST && shouldChangeView) {
|
||||
handleExplorerTabChange(PANEL_TYPES.TIME_SERIES);
|
||||
@@ -386,17 +376,6 @@ function LogsExplorerViews({
|
||||
panelType,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
selectedView &&
|
||||
selectedView === SELECTED_VIEWS.SEARCH &&
|
||||
handleSetConfig
|
||||
) {
|
||||
handleSetConfig(defaultTo(panelTypes, PANEL_TYPES.LIST), DataSource.LOGS);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const currentParams = data?.params as Omit<LogTimeRange, 'pageSize'>;
|
||||
const currentData = data?.payload.data.newResult.data.result || [];
|
||||
@@ -435,6 +414,10 @@ function LogsExplorerViews({
|
||||
setPage(1);
|
||||
setRequestData(newRequestData);
|
||||
currentMinTimeRef.current = minTime;
|
||||
|
||||
if (!activeLogId) {
|
||||
onTimeRangeChange(null);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
stagedQuery,
|
||||
@@ -447,7 +430,6 @@ function LogsExplorerViews({
|
||||
activeLogId,
|
||||
onTimeRangeChange,
|
||||
panelType,
|
||||
selectedView,
|
||||
]);
|
||||
|
||||
const { options, config } = useOptionsMenu({
|
||||
@@ -521,15 +503,24 @@ function LogsExplorerViews({
|
||||
{showHistogram && (
|
||||
<LogsExplorerChart
|
||||
className="logs-histogram"
|
||||
isLoading={isFetchingListChartData || isLoadingListChartData}
|
||||
isLoading={
|
||||
isFetchingListChartData ||
|
||||
isLoadingListChartData ||
|
||||
isLoading ||
|
||||
isFetching
|
||||
}
|
||||
data={chartData}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="logs-explorer-views-types">
|
||||
<div className="views-tabs-container">
|
||||
<Button.Group className="views-tabs">
|
||||
<Button
|
||||
<Radio.Group
|
||||
className="views-tabs"
|
||||
onChange={handleModeChange}
|
||||
value={selectedPanelType}
|
||||
>
|
||||
<Radio.Button
|
||||
value={PANEL_TYPES.LIST}
|
||||
className={
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
@@ -538,11 +529,10 @@ function LogsExplorerViews({
|
||||
disabled={
|
||||
(isMultipleQueries || isGroupByExist) && selectedView !== 'search'
|
||||
}
|
||||
onClick={(): void => handleModeChange(PANEL_TYPES.LIST)}
|
||||
>
|
||||
List view
|
||||
</Button>
|
||||
<Button
|
||||
</Radio.Button>
|
||||
<Radio.Button
|
||||
value={PANEL_TYPES.TIME_SERIES}
|
||||
className={
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
@@ -550,21 +540,20 @@ function LogsExplorerViews({
|
||||
? 'selected_view tab'
|
||||
: 'tab'
|
||||
}
|
||||
onClick={(): void => handleModeChange(PANEL_TYPES.TIME_SERIES)}
|
||||
>
|
||||
Time series
|
||||
</Button>
|
||||
<Button
|
||||
</Radio.Button>
|
||||
<Radio.Button
|
||||
value={PANEL_TYPES.TABLE}
|
||||
className={
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
selectedPanelType === PANEL_TYPES.TABLE ? 'selected_view tab' : 'tab'
|
||||
}
|
||||
onClick={(): void => handleModeChange(PANEL_TYPES.TABLE)}
|
||||
>
|
||||
Table
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
|
||||
<div className="logs-actions-container">
|
||||
{selectedPanelType === PANEL_TYPES.LIST && (
|
||||
<div className="tab-options">
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
import { Row } from 'antd';
|
||||
import { useDashboardVariablesFromLocalStorage } from 'hooks/dashboard/useDashboardFromLocalStorage';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
|
||||
import { convertVariablesToDbFormat } from './util';
|
||||
import VariableItem from './VariableItem';
|
||||
|
||||
function DashboardVariableSelection(): JSX.Element | null {
|
||||
const {
|
||||
selectedDashboard,
|
||||
setSelectedDashboard,
|
||||
dashboardId,
|
||||
} = useDashboard();
|
||||
|
||||
const {
|
||||
updateLocalStorageDashboardVariables,
|
||||
} = useDashboardVariablesFromLocalStorage(dashboardId);
|
||||
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||
|
||||
const { data } = selectedDashboard || {};
|
||||
|
||||
@@ -27,6 +23,8 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
|
||||
const [variablesTableData, setVariablesTableData] = useState<any>([]);
|
||||
|
||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
useEffect(() => {
|
||||
if (variables) {
|
||||
const tableRowData = [];
|
||||
@@ -54,6 +52,40 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
setUpdate(!update);
|
||||
};
|
||||
|
||||
const updateMutation = useUpdateDashboard();
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const updateVariables = (
|
||||
name: string,
|
||||
updatedVariablesData: Dashboard['data']['variables'],
|
||||
): void => {
|
||||
if (!selectedDashboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateMutation.mutateAsync(
|
||||
{
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
...selectedDashboard.data,
|
||||
variables: updatedVariablesData,
|
||||
},
|
||||
},
|
||||
{
|
||||
onSuccess: (updatedDashboard) => {
|
||||
if (updatedDashboard.payload) {
|
||||
setSelectedDashboard(updatedDashboard.payload);
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
notifications.error({
|
||||
message: `Error updating ${name} variable`,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const onValueUpdate = (
|
||||
name: string,
|
||||
id: string,
|
||||
@@ -73,22 +105,12 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
return variableCopy;
|
||||
},
|
||||
);
|
||||
updateLocalStorageDashboardVariables(name, value, allSelected);
|
||||
|
||||
const variables = convertVariablesToDbFormat(newVariablesArr);
|
||||
|
||||
if (selectedDashboard) {
|
||||
setSelectedDashboard({
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
...selectedDashboard?.data,
|
||||
variables: {
|
||||
...variables,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (role !== 'VIEWER' && selectedDashboard) {
|
||||
updateVariables(name, variables);
|
||||
}
|
||||
|
||||
onVarChanged(name);
|
||||
|
||||
setUpdate(!update);
|
||||
|
||||
@@ -36,6 +36,7 @@ function NewExplorerCTA(): JSX.Element | null {
|
||||
danger
|
||||
data-testid="newExplorerCTA"
|
||||
type="primary"
|
||||
size="small"
|
||||
>
|
||||
{buttonText[location.pathname]}
|
||||
</Button>
|
||||
|
||||
@@ -8,11 +8,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.prom-ql-icon {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
.ant-btn-default {
|
||||
border-color: transparent;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import './QuerySection.styles.scss';
|
||||
|
||||
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
||||
import { Button, Tabs, Typography } from 'antd';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||
@@ -11,7 +11,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { Atom, Play, Terminal } from 'lucide-react';
|
||||
import { Atom, LucideAccessibility, Play, Terminal } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import {
|
||||
getNextWidgets,
|
||||
@@ -138,11 +138,9 @@ function QuerySection({
|
||||
{
|
||||
key: EQueryType.QUERY_BUILDER,
|
||||
label: (
|
||||
<Tooltip title="Query Builder">
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Atom size={14} />
|
||||
</Button>
|
||||
),
|
||||
tab: <Typography>Query Builder</Typography>,
|
||||
children: (
|
||||
@@ -152,11 +150,9 @@ function QuerySection({
|
||||
{
|
||||
key: EQueryType.CLICKHOUSE,
|
||||
label: (
|
||||
<Tooltip title="ClickHouse">
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<Terminal size={14} />
|
||||
</Button>
|
||||
),
|
||||
tab: <Typography>ClickHouse Query</Typography>,
|
||||
children: <ClickHouseQueryContainer />,
|
||||
@@ -164,11 +160,9 @@ function QuerySection({
|
||||
{
|
||||
key: EQueryType.PROM,
|
||||
label: (
|
||||
<Tooltip title="PromQL">
|
||||
<Button className="nav-btns">
|
||||
<img src="/Icons/promQL.svg" alt="Prom Ql" className="prom-ql-icon" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button className="nav-btns">
|
||||
<LucideAccessibility size={14} />
|
||||
</Button>
|
||||
),
|
||||
tab: <Typography>PromQL</Typography>,
|
||||
children: <PromQLQueryContainer />,
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
## Install otel-collector in your Kubernetes infra
|
||||
|
||||
|
||||
Add the SigNoz Helm Chart repository
|
||||
```bash
|
||||
helm repo add signoz https://charts.signoz.io
|
||||
```
|
||||
|
||||
|
||||
If the chart is already present, update the chart to the latest using:
|
||||
```bash
|
||||
helm repo update
|
||||
```
|
||||
|
||||
|
||||
Install the Kubernetes Infrastructure chart provided by SigNoz
|
||||
```bash
|
||||
helm install my-release signoz/k8s-infra \
|
||||
--set otelCollectorEndpoint=ingest.{{REGION}}.signoz.cloud:443 \
|
||||
--set otelInsecure=false \
|
||||
--set signozApiKey={{SIGNOZ_INGESTION_KEY}} \
|
||||
--set global.clusterName=<CLUSTER_NAME>
|
||||
```
|
||||
- Replace `<CLUSTER_NAME>` with the name of the Kubernetes cluster or a unique identifier of the cluster.
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
|
||||
After setting up the Otel collector agent, follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter:
|
||||
{:opentelemetry_exporter,
|
||||
%{endpoints: ["http://localhost:4318"]}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
### Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
|
||||
Follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter: {
|
||||
:opentelemetry_exporter,
|
||||
%{
|
||||
endpoints: ["https://ingest.{{REGION}}.signoz.cloud:443"],
|
||||
headers: [
|
||||
{"signoz-access-token", {{SIGNOZ_ACCESS_TOKEN}} }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
### Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
@@ -1,96 +0,0 @@
|
||||
## Setup OpenTelemetry Binary as an agent
|
||||
|
||||
|
||||
### Step 1: Download otel-collector tar.gz
|
||||
```bash
|
||||
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_linux_amd64.tar.gz
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
|
||||
```bash
|
||||
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_linux_amd64.tar.gz -C otelcol-contrib
|
||||
```
|
||||
|
||||
|
||||
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
|
||||
```bash
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
hostmetrics:
|
||||
collection_interval: 60s
|
||||
scrapers:
|
||||
cpu: {}
|
||||
disk: {}
|
||||
load: {}
|
||||
filesystem: {}
|
||||
memory: {}
|
||||
network: {}
|
||||
paging: {}
|
||||
process:
|
||||
mute_process_name_error: true
|
||||
mute_process_exe_error: true
|
||||
mute_process_io_error: true
|
||||
processes: {}
|
||||
prometheus:
|
||||
config:
|
||||
global:
|
||||
scrape_interval: 60s
|
||||
scrape_configs:
|
||||
- job_name: otel-collector-binary
|
||||
static_configs:
|
||||
- targets:
|
||||
# - localhost:8888
|
||||
processors:
|
||||
batch:
|
||||
send_batch_size: 1000
|
||||
timeout: 10s
|
||||
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
|
||||
resourcedetection:
|
||||
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
|
||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||
timeout: 2s
|
||||
system:
|
||||
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
|
||||
extensions:
|
||||
health_check: {}
|
||||
zpages: {}
|
||||
exporters:
|
||||
otlp:
|
||||
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
|
||||
tls:
|
||||
insecure: false
|
||||
headers:
|
||||
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
|
||||
logging:
|
||||
verbosity: normal
|
||||
service:
|
||||
telemetry:
|
||||
metrics:
|
||||
address: 0.0.0.0:8888
|
||||
extensions: [health_check, zpages]
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
metrics/internal:
|
||||
receivers: [prometheus, hostmetrics]
|
||||
processors: [resourcedetection, batch]
|
||||
exporters: [otlp]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
|
||||
After setting up the Otel collector agent, follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter:
|
||||
{:opentelemetry_exporter,
|
||||
%{endpoints: ["http://localhost:4318"]}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
|
||||
### Step 1: Run OTel Collector
|
||||
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
|
||||
|
||||
```bash
|
||||
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
|
||||
```
|
||||
|
||||
|
||||
#### (Optional Step): View last 50 lines of `otelcol` logs
|
||||
```bash
|
||||
tail -f -n 50 otelcol-output.log
|
||||
```
|
||||
|
||||
#### (Optional Step): Stop `otelcol`
|
||||
```bash
|
||||
kill "$(< otel-pid)"
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Running your Elixir application
|
||||
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
```
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
|
||||
Follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter: {
|
||||
:opentelemetry_exporter,
|
||||
%{
|
||||
endpoints: ["https://ingest.{{REGION}}.signoz.cloud:443"],
|
||||
headers: [
|
||||
{"signoz-access-token", {{SIGNOZ_ACCESS_TOKEN}} }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
### Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
@@ -1,96 +0,0 @@
|
||||
## Setup OpenTelemetry Binary as an agent
|
||||
|
||||
|
||||
### Step 1: Download otel-collector tar.gz
|
||||
```bash
|
||||
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_linux_arm64.tar.gz
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
|
||||
```bash
|
||||
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_linux_arm64.tar.gz -C otelcol-contrib
|
||||
```
|
||||
|
||||
|
||||
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
|
||||
```bash
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
hostmetrics:
|
||||
collection_interval: 60s
|
||||
scrapers:
|
||||
cpu: {}
|
||||
disk: {}
|
||||
load: {}
|
||||
filesystem: {}
|
||||
memory: {}
|
||||
network: {}
|
||||
paging: {}
|
||||
process:
|
||||
mute_process_name_error: true
|
||||
mute_process_exe_error: true
|
||||
mute_process_io_error: true
|
||||
processes: {}
|
||||
prometheus:
|
||||
config:
|
||||
global:
|
||||
scrape_interval: 60s
|
||||
scrape_configs:
|
||||
- job_name: otel-collector-binary
|
||||
static_configs:
|
||||
- targets:
|
||||
# - localhost:8888
|
||||
processors:
|
||||
batch:
|
||||
send_batch_size: 1000
|
||||
timeout: 10s
|
||||
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
|
||||
resourcedetection:
|
||||
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
|
||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||
timeout: 2s
|
||||
system:
|
||||
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
|
||||
extensions:
|
||||
health_check: {}
|
||||
zpages: {}
|
||||
exporters:
|
||||
otlp:
|
||||
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
|
||||
tls:
|
||||
insecure: false
|
||||
headers:
|
||||
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
|
||||
logging:
|
||||
verbosity: normal
|
||||
service:
|
||||
telemetry:
|
||||
metrics:
|
||||
address: 0.0.0.0:8888
|
||||
extensions: [health_check, zpages]
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
metrics/internal:
|
||||
receivers: [prometheus, hostmetrics]
|
||||
processors: [resourcedetection, batch]
|
||||
exporters: [otlp]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
|
||||
After setting up the Otel collector agent, follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter:
|
||||
{:opentelemetry_exporter,
|
||||
%{endpoints: ["http://localhost:4318"]}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
|
||||
### Step 1: Run OTel Collector
|
||||
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
|
||||
|
||||
```bash
|
||||
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
|
||||
```
|
||||
|
||||
|
||||
#### (Optional Step): View last 50 lines of `otelcol` logs
|
||||
```bash
|
||||
tail -f -n 50 otelcol-output.log
|
||||
```
|
||||
|
||||
#### (Optional Step): Stop `otelcol`
|
||||
```bash
|
||||
kill "$(< otel-pid)"
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
```
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
|
||||
Follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter: {
|
||||
:opentelemetry_exporter,
|
||||
%{
|
||||
endpoints: ["https://ingest.{{REGION}}.signoz.cloud:443"],
|
||||
headers: [
|
||||
{"signoz-access-token", {{SIGNOZ_ACCESS_TOKEN}} }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
### Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
@@ -1,96 +0,0 @@
|
||||
### Setup OpenTelemetry Binary as an agent
|
||||
|
||||
|
||||
### Step 1: Download otel-collector tar.gz
|
||||
```bash
|
||||
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_darwin_amd64.tar.gz
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
|
||||
```bash
|
||||
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_darwin_amd64.tar.gz -C otelcol-contrib
|
||||
```
|
||||
|
||||
|
||||
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
|
||||
```bash
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
hostmetrics:
|
||||
collection_interval: 60s
|
||||
scrapers:
|
||||
cpu: {}
|
||||
disk: {}
|
||||
load: {}
|
||||
filesystem: {}
|
||||
memory: {}
|
||||
network: {}
|
||||
paging: {}
|
||||
process:
|
||||
mute_process_name_error: true
|
||||
mute_process_exe_error: true
|
||||
mute_process_io_error: true
|
||||
processes: {}
|
||||
prometheus:
|
||||
config:
|
||||
global:
|
||||
scrape_interval: 60s
|
||||
scrape_configs:
|
||||
- job_name: otel-collector-binary
|
||||
static_configs:
|
||||
- targets:
|
||||
# - localhost:8888
|
||||
processors:
|
||||
batch:
|
||||
send_batch_size: 1000
|
||||
timeout: 10s
|
||||
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
|
||||
resourcedetection:
|
||||
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
|
||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||
timeout: 2s
|
||||
system:
|
||||
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
|
||||
extensions:
|
||||
health_check: {}
|
||||
zpages: {}
|
||||
exporters:
|
||||
otlp:
|
||||
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
|
||||
tls:
|
||||
insecure: false
|
||||
headers:
|
||||
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
|
||||
logging:
|
||||
verbosity: normal
|
||||
service:
|
||||
telemetry:
|
||||
metrics:
|
||||
address: 0.0.0.0:8888
|
||||
extensions: [health_check, zpages]
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
metrics/internal:
|
||||
receivers: [prometheus, hostmetrics]
|
||||
processors: [resourcedetection, batch]
|
||||
exporters: [otlp]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
|
||||
After setting up the Otel collector agent, follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter:
|
||||
{:opentelemetry_exporter,
|
||||
%{endpoints: ["http://localhost:4318"]}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
|
||||
### Step 1: Run OTel Collector
|
||||
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
|
||||
|
||||
```bash
|
||||
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
|
||||
```
|
||||
|
||||
|
||||
#### (Optional Step): View last 50 lines of `otelcol` logs
|
||||
```bash
|
||||
tail -f -n 50 otelcol-output.log
|
||||
```
|
||||
|
||||
#### (Optional Step): Stop `otelcol`
|
||||
```bash
|
||||
kill "$(< otel-pid)"
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
```
|
||||
@@ -1,62 +0,0 @@
|
||||
|
||||
|
||||
Follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter: {
|
||||
:opentelemetry_exporter,
|
||||
%{
|
||||
endpoints: ["https://ingest.{{REGION}}.signoz.cloud:443"],
|
||||
headers: [
|
||||
{"signoz-access-token", {{SIGNOZ_ACCESS_TOKEN}} }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
### Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
@@ -1,96 +0,0 @@
|
||||
## Setup OpenTelemetry Binary as an agent
|
||||
|
||||
|
||||
### Step 1: Download otel-collector tar.gz
|
||||
```bash
|
||||
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.79.0/otelcol-contrib_0.79.0_darwin_arm64.tar.gz
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Extract otel-collector tar.gz to the `otelcol-contrib` folder
|
||||
```bash
|
||||
mkdir otelcol-contrib && tar xvzf otelcol-contrib_0.79.0_darwin_arm64.tar.gz -C otelcol-contrib
|
||||
```
|
||||
|
||||
|
||||
### Step 3: Create config.yaml in folder otelcol-contrib with the below content in it
|
||||
```bash
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
hostmetrics:
|
||||
collection_interval: 60s
|
||||
scrapers:
|
||||
cpu: {}
|
||||
disk: {}
|
||||
load: {}
|
||||
filesystem: {}
|
||||
memory: {}
|
||||
network: {}
|
||||
paging: {}
|
||||
process:
|
||||
mute_process_name_error: true
|
||||
mute_process_exe_error: true
|
||||
mute_process_io_error: true
|
||||
processes: {}
|
||||
prometheus:
|
||||
config:
|
||||
global:
|
||||
scrape_interval: 60s
|
||||
scrape_configs:
|
||||
- job_name: otel-collector-binary
|
||||
static_configs:
|
||||
- targets:
|
||||
# - localhost:8888
|
||||
processors:
|
||||
batch:
|
||||
send_batch_size: 1000
|
||||
timeout: 10s
|
||||
# Ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md
|
||||
resourcedetection:
|
||||
detectors: [env, system] # Before system detector, include ec2 for AWS, gcp for GCP and azure for Azure.
|
||||
# Using OTEL_RESOURCE_ATTRIBUTES envvar, env detector adds custom labels.
|
||||
timeout: 2s
|
||||
system:
|
||||
hostname_sources: [os] # alternatively, use [dns,os] for setting FQDN as host.name and os as fallback
|
||||
extensions:
|
||||
health_check: {}
|
||||
zpages: {}
|
||||
exporters:
|
||||
otlp:
|
||||
endpoint: "ingest.{{REGION}}.signoz.cloud:443"
|
||||
tls:
|
||||
insecure: false
|
||||
headers:
|
||||
"signoz-access-token": "{{SIGNOZ_INGESTION_KEY}}"
|
||||
logging:
|
||||
verbosity: normal
|
||||
service:
|
||||
telemetry:
|
||||
metrics:
|
||||
address: 0.0.0.0:8888
|
||||
extensions: [health_check, zpages]
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
metrics/internal:
|
||||
receivers: [prometheus, hostmetrics]
|
||||
processors: [resourcedetection, batch]
|
||||
exporters: [otlp]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [otlp]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
|
||||
|
||||
After setting up the Otel collector agent, follow the steps below to instrument your Elixir (Phoenix + Ecto) Application
|
||||
|
||||
### Step 1: Add dependencies
|
||||
Install dependencies related to OpenTelemetry by adding them to `mix.exs` file
|
||||
|
||||
```bash
|
||||
{:opentelemetry_exporter, "~> 1.6"},
|
||||
{:opentelemetry_api, "~> 1.2"},
|
||||
{:opentelemetry, "~> 1.3"},
|
||||
{:opentelemetry_semantic_conventions, "~> 0.2"},
|
||||
{:opentelemetry_cowboy, "~> 0.2.1"},
|
||||
{:opentelemetry_phoenix, "~> 1.1"},
|
||||
{:opentelemetry_ecto, "~> 1.1"}
|
||||
```
|
||||
|
||||
|
||||
In your application start, usually the `application.ex` file, setup the telemetry handlers
|
||||
|
||||
```bash
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:{{MYAPP}}, :repo])
|
||||
```
|
||||
|
||||
|
||||
As an example, this is how you can setup the handlers in your application.ex file for an application called demo :
|
||||
|
||||
```bash
|
||||
# application.ex
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
:opentelemetry_cowboy.setup()
|
||||
OpentelemetryPhoenix.setup(adapter: :cowboy2)
|
||||
OpentelemetryEcto.setup([:demo, :repo])
|
||||
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Step 2: Configure Application
|
||||
You need to configure your application to send telemetry data by adding the following config to your `runtime.exs` file:
|
||||
|
||||
```bash
|
||||
config :opentelemetry, :resource, service: %{name: "{{MYAPP}}"}
|
||||
|
||||
config :opentelemetry, :processors,
|
||||
otel_batch_processor: %{
|
||||
exporter:
|
||||
{:opentelemetry_exporter,
|
||||
%{endpoints: ["http://localhost:4318"]}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
|
||||
|
||||
### Step 1: Run OTel Collector
|
||||
Run this command inside the `otelcol-contrib` directory that you created in the install Otel Collector step
|
||||
|
||||
```bash
|
||||
./otelcol-contrib --config ./config.yaml &> otelcol-output.log & echo "$!" > otel-pid
|
||||
```
|
||||
|
||||
|
||||
#### (Optional Step): View last 50 lines of `otelcol` logs
|
||||
```bash
|
||||
tail -f -n 50 otelcol-output.log
|
||||
```
|
||||
|
||||
#### (Optional Step): Stop `otelcol`
|
||||
```bash
|
||||
kill "$(< otel-pid)"
|
||||
```
|
||||
|
||||
|
||||
### Step 2: Running your Elixir application
|
||||
Once you are done instrumenting your Elixir (Phoenix + Ecto) application with OpenTelemetry, you should install the dependencies needed to run your application and run it as you normally would.
|
||||
|
||||
|
||||
|
||||
To see some examples for instrumented applications, you can checkout [this link](https://signoz.io/docs/instrumentation/elixir/#sample-examples)
|
||||
```
|
||||
@@ -20,8 +20,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ This will create and activate a virtual environment named `.venv`
|
||||
### Step 2 : Install the OpenTelemetry dependencies
|
||||
|
||||
```bash
|
||||
pip install opentelemetry-distro==0.43b0
|
||||
pip install opentelemetry-exporter-otlp==1.22.0
|
||||
pip install opentelemetry-distro==0.38b0
|
||||
pip install opentelemetry-exporter-otlp==1.17.0
|
||||
```
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user