mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-11 12:04:27 +00:00
Compare commits
10 Commits
unit-testi
...
v0.36.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ead935886 | ||
|
|
fd9a502012 | ||
|
|
6530873994 | ||
|
|
c9c0bd38be | ||
|
|
9ac22fcb10 | ||
|
|
86ff865842 | ||
|
|
e792c48f6d | ||
|
|
8ee92516ca | ||
|
|
79c05d8fa8 | ||
|
|
019bc8c1df |
@@ -146,7 +146,7 @@ services:
|
||||
condition: on-failure
|
||||
|
||||
query-service:
|
||||
image: signoz/query-service:0.36.0
|
||||
image: signoz/query-service:0.36.2
|
||||
command:
|
||||
[
|
||||
"-config=/root/config/prometheus.yml",
|
||||
@@ -186,7 +186,7 @@ services:
|
||||
<<: *db-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:0.36.0
|
||||
image: signoz/frontend:0.36.2
|
||||
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.4
|
||||
image: signoz/signoz-otel-collector:0.88.6
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-config.yaml",
|
||||
@@ -237,7 +237,7 @@ services:
|
||||
- query-service
|
||||
|
||||
otel-collector-migrator:
|
||||
image: signoz/signoz-schema-migrator:0.88.4
|
||||
image: signoz/signoz-schema-migrator:0.88.6
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@@ -250,7 +250,7 @@ services:
|
||||
# - clickhouse-3
|
||||
|
||||
otel-collector-metrics:
|
||||
image: signoz/signoz-otel-collector:0.88.4
|
||||
image: signoz/signoz-otel-collector:0.88.6
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||
|
||||
@@ -66,7 +66,7 @@ services:
|
||||
- --storage.path=/data
|
||||
|
||||
otel-collector-migrator:
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.4}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.6}
|
||||
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.4
|
||||
image: signoz/signoz-otel-collector:0.88.6
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-config.yaml",
|
||||
@@ -118,7 +118,7 @@ services:
|
||||
|
||||
otel-collector-metrics:
|
||||
container_name: signoz-otel-collector-metrics
|
||||
image: signoz/signoz-otel-collector:0.88.4
|
||||
image: signoz/signoz-otel-collector:0.88.6
|
||||
command:
|
||||
[
|
||||
"--config=/etc/otel-collector-metrics-config.yaml",
|
||||
|
||||
@@ -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.36.0}
|
||||
image: signoz/query-service:${DOCKER_TAG:-0.36.2}
|
||||
container_name: signoz-query-service
|
||||
command:
|
||||
[
|
||||
@@ -203,7 +203,7 @@ services:
|
||||
<<: *db-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.36.0}
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.36.2}
|
||||
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.4}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.6}
|
||||
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.4}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.6}
|
||||
container_name: signoz-otel-collector
|
||||
command:
|
||||
[
|
||||
@@ -269,7 +269,7 @@ services:
|
||||
condition: service_healthy
|
||||
|
||||
otel-collector-metrics:
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.4}
|
||||
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.6}
|
||||
container_name: signoz-otel-collector-metrics
|
||||
command:
|
||||
[
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const WrapperStyled = styled.div`
|
||||
height: 100%;
|
||||
height: 95%;
|
||||
overflow: hidden;
|
||||
|
||||
& .ant-table-wrapper {
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { fireEvent, render, screen, within } from 'tests/test-utils';
|
||||
|
||||
import ListAlertRules from '.';
|
||||
|
||||
describe('ListAlertRules', () => {
|
||||
test('Should render the table', async () => {
|
||||
act(() => {
|
||||
render(<ListAlertRules />);
|
||||
});
|
||||
|
||||
const newAlert = await screen.findByRole('button', {
|
||||
name: /plus new alert/i,
|
||||
});
|
||||
|
||||
expect(newAlert).toBeInTheDocument();
|
||||
|
||||
const status = await screen.findByText(/status/i);
|
||||
expect(status).toBeInTheDocument();
|
||||
|
||||
const alertName = await screen.findByText(/alert name/i);
|
||||
expect(alertName).toBeInTheDocument();
|
||||
|
||||
const severity = await screen.findByText(/severity/i);
|
||||
expect(severity).toBeInTheDocument();
|
||||
|
||||
const label = await screen.findByText(/label/i);
|
||||
expect(label).toBeInTheDocument();
|
||||
|
||||
const action = await screen.findByText(/action/i);
|
||||
expect(action).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Should render the table data', async () => {
|
||||
act(() => {
|
||||
render(<ListAlertRules />);
|
||||
});
|
||||
|
||||
const status = await screen.findByText(/status/i);
|
||||
expect(status).toBeInTheDocument();
|
||||
|
||||
const disabledRow = await screen.findByRole('row', {
|
||||
name: /disabled Test Rule 1 warning details: https:\/\/stagi\.\.\. hello: world region: us \+1 ellipsis/i,
|
||||
});
|
||||
expect(disabledRow).toBeInTheDocument();
|
||||
|
||||
const actionButton = within(disabledRow).getByRole('button', {
|
||||
name: /ellipsis/i,
|
||||
});
|
||||
expect(actionButton).toBeInTheDocument();
|
||||
|
||||
fireEvent.mouseOver(actionButton);
|
||||
|
||||
const enabled = await screen.findByRole('menuitem', {
|
||||
name: /enable/i,
|
||||
});
|
||||
expect(enabled).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Should render enabled for disabled alert in menu', async () => {
|
||||
act(() => {
|
||||
render(<ListAlertRules />);
|
||||
});
|
||||
|
||||
const disabledRow = await screen.findByRole('row', {
|
||||
name: /disabled Test Rule 1 warning details: https:\/\/stagi\.\.\. hello: world region: us \+1 ellipsis/i,
|
||||
});
|
||||
expect(disabledRow).toBeInTheDocument();
|
||||
|
||||
const actionButton = within(disabledRow).getByRole('button', {
|
||||
name: /ellipsis/i,
|
||||
});
|
||||
expect(actionButton).toBeInTheDocument();
|
||||
|
||||
fireEvent.mouseOver(actionButton);
|
||||
|
||||
const enabled = await screen.findByRole('menuitem', {
|
||||
name: /enable/i,
|
||||
});
|
||||
expect(enabled).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Should render disabled for Ok alert in menu', async () => {
|
||||
act(() => {
|
||||
render(<ListAlertRules />);
|
||||
});
|
||||
|
||||
const enabledRow = await screen.findByRole('row', {
|
||||
name: /ok test rule 2 warning - ellipsis/i,
|
||||
});
|
||||
|
||||
expect(enabledRow).toBeInTheDocument();
|
||||
|
||||
const actionButton = within(enabledRow).getByRole('button', {
|
||||
name: /ellipsis/i,
|
||||
});
|
||||
expect(actionButton).toBeInTheDocument();
|
||||
|
||||
fireEvent.mouseOver(actionButton);
|
||||
|
||||
const disabled = await screen.findByRole('menuitem', {
|
||||
name: /disable/i,
|
||||
});
|
||||
expect(disabled).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -12,9 +12,9 @@ export const Container = styled(Card)<Props>`
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
padding: 8px;
|
||||
padding: ${({ $panelType }): string =>
|
||||
$panelType === PANEL_TYPES.TABLE ? '0 0' : '1.5rem 0'};
|
||||
height: 57vh;
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ import CustomColor from './CustomColor';
|
||||
import ShowCaseValue from './ShowCaseValue';
|
||||
import { ThresholdProps } from './types';
|
||||
|
||||
const wrapStyle = {
|
||||
flexWrap: 'wrap',
|
||||
} as React.CSSProperties;
|
||||
|
||||
function Threshold({
|
||||
index,
|
||||
thresholdOperator = '>',
|
||||
@@ -220,7 +224,7 @@ function Threshold({
|
||||
}
|
||||
>
|
||||
{selectedGraph === PANEL_TYPES.TIME_SERIES && (
|
||||
<>
|
||||
<Space style={wrapStyle}>
|
||||
<Typography.Text>Label</Typography.Text>
|
||||
{isEditMode ? (
|
||||
<Input
|
||||
@@ -232,7 +236,7 @@ function Threshold({
|
||||
) : (
|
||||
<ShowCaseValue width="180px" value={label || 'none'} />
|
||||
)}
|
||||
</>
|
||||
</Space>
|
||||
)}
|
||||
{(selectedGraph === PANEL_TYPES.VALUE ||
|
||||
selectedGraph === PANEL_TYPES.TABLE) && (
|
||||
@@ -243,7 +247,7 @@ function Threshold({
|
||||
{isEditMode ? (
|
||||
<>
|
||||
{selectedGraph === PANEL_TYPES.TABLE && (
|
||||
<Space>
|
||||
<Space style={wrapStyle}>
|
||||
<Select
|
||||
style={{
|
||||
minWidth: '150px',
|
||||
@@ -270,7 +274,7 @@ function Threshold({
|
||||
) : (
|
||||
<>
|
||||
{selectedGraph === PANEL_TYPES.TABLE && (
|
||||
<Space>
|
||||
<Space style={wrapStyle}>
|
||||
<ShowCaseValue width="150px" value={tableSelectedOption} />
|
||||
<Typography.Text>is</Typography.Text>
|
||||
</Space>
|
||||
@@ -283,7 +287,7 @@ function Threshold({
|
||||
</Space>
|
||||
</div>
|
||||
<div className="threshold-units-selector">
|
||||
<Space>
|
||||
<Space style={wrapStyle}>
|
||||
{isEditMode ? (
|
||||
<InputNumber
|
||||
style={{ backgroundColor }}
|
||||
@@ -311,7 +315,7 @@ function Threshold({
|
||||
<div>
|
||||
<Space direction="vertical">
|
||||
<Typography.Text>Show with</Typography.Text>
|
||||
<Space>
|
||||
<Space style={wrapStyle}>
|
||||
{isEditMode ? (
|
||||
<>
|
||||
<ColorSelector setColor={setColor} thresholdColor={color} />
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { render, screen } from 'tests/test-utils';
|
||||
import { Alerts } from 'types/api/alerts/getTriggered';
|
||||
|
||||
import ExapandableRow from './ExapandableRow';
|
||||
|
||||
jest.mock('lib/convertDateToAmAndPm', () => jest.fn(() => '12:00 PM'));
|
||||
jest.mock('lib/getFormatedDate', () => jest.fn(() => '2023-12-05'));
|
||||
|
||||
describe('ExapandableRow component', () => {
|
||||
const allAlerts: Alerts[] = [
|
||||
{
|
||||
id: 1,
|
||||
annotations: { description: 'Description 1', summary: 'Summary 1' },
|
||||
state: 'active',
|
||||
name: 'Alert 1',
|
||||
labels: {
|
||||
alertname: 'Critical Alert',
|
||||
severity: 'critical',
|
||||
tag1: 'value1',
|
||||
tag2: 'value2',
|
||||
},
|
||||
status: { inhibitedBy: [], silencedBy: [], state: 'active' },
|
||||
startsAt: '2023-12-05T11:00:00Z',
|
||||
fingerprint: 'fingerprint1',
|
||||
endsAt: '2023-12-05T12:00:00Z',
|
||||
generatorURL: 'generatorURL1',
|
||||
receivers: [],
|
||||
updatedAt: '2023-12-05T11:30:00Z',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
annotations: { description: 'Description 2', summary: 'Summary 2' },
|
||||
state: 'inactive',
|
||||
name: 'Alert 2',
|
||||
labels: {
|
||||
alertname: 'Warning Alert',
|
||||
severity: 'warning',
|
||||
tag1: 'value3',
|
||||
tag2: 'value4',
|
||||
tag3: 'value5',
|
||||
},
|
||||
status: { inhibitedBy: [], silencedBy: [], state: 'inactive' },
|
||||
startsAt: '2023-12-05T13:00:00Z',
|
||||
fingerprint: 'fingerprint2',
|
||||
endsAt: '2023-12-05T14:00:00Z',
|
||||
generatorURL: 'generatorURL2',
|
||||
receivers: [],
|
||||
updatedAt: '2023-12-05T13:30:00Z',
|
||||
},
|
||||
];
|
||||
|
||||
test('should render correct content for each alert', () => {
|
||||
render(<ExapandableRow allAlerts={allAlerts} />);
|
||||
|
||||
expect(screen.getByText('Critical Alert')).toBeInTheDocument();
|
||||
expect(screen.getByText('critical')).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Warning Alert')).toBeInTheDocument();
|
||||
expect(screen.getByText('warning')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Should render the unknown status if tag is not corrently mentioned', () => {
|
||||
render(<ExapandableRow allAlerts={allAlerts} />);
|
||||
const unknowStatus = screen.getByText('Unknown Status');
|
||||
expect(unknowStatus).toBeInTheDocument();
|
||||
screen.debug();
|
||||
});
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
// FilteredTable.test.tsx
|
||||
|
||||
import { render } from 'tests/test-utils';
|
||||
import { Alerts } from 'types/api/alerts/getTriggered';
|
||||
|
||||
import FilteredTable from '.';
|
||||
|
||||
describe('FilteredTable component', () => {
|
||||
const selectedGroup = [{ value: 'group1' }, { value: 'group2' }];
|
||||
const allAlerts: Alerts[] = [
|
||||
{
|
||||
labels: { group1: 'value1', group2: 'value2' },
|
||||
annotations: { description: 'Description 1', summary: 'Summary 1' },
|
||||
state: 'active',
|
||||
name: 'Alert 1',
|
||||
id: 1,
|
||||
endsAt: '2023-12-05T12:00:00Z',
|
||||
fingerprint: 'fingerprint1',
|
||||
generatorURL: 'generatorURL1',
|
||||
receivers: [],
|
||||
startsAt: '2023-12-05T11:00:00Z',
|
||||
status: { inhibitedBy: [], silencedBy: [], state: 'active' },
|
||||
updatedAt: '2023-12-05T11:30:00Z',
|
||||
},
|
||||
];
|
||||
const selectedFilter = [{ value: 'severity:critical' }];
|
||||
|
||||
it('should render table headers', () => {
|
||||
const { getByText } = render(
|
||||
<FilteredTable
|
||||
selectedGroup={selectedGroup}
|
||||
allAlerts={allAlerts}
|
||||
selectedFilter={selectedFilter}
|
||||
/>,
|
||||
);
|
||||
|
||||
// Assert that each header is present
|
||||
expect(getByText('Status')).toBeInTheDocument();
|
||||
expect(getByText('Alert Name')).toBeInTheDocument();
|
||||
expect(getByText('Severity')).toBeInTheDocument();
|
||||
expect(getByText('Firing Since')).toBeInTheDocument();
|
||||
expect(getByText('Tags')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,68 +0,0 @@
|
||||
import { render, screen } from 'tests/test-utils';
|
||||
import { Alerts } from 'types/api/alerts/getTriggered';
|
||||
|
||||
import TableRowComponent from './TableRow';
|
||||
|
||||
jest.mock('types/api/alerts/getTriggered', () => ({}));
|
||||
|
||||
describe('TableRowComponent component', () => {
|
||||
const tags = ['tag1', 'tag2'];
|
||||
const tagsAlerts: Alerts[] = [
|
||||
{
|
||||
labels: {
|
||||
alertname: 'Critical Alert',
|
||||
severity: 'critical',
|
||||
tag1: 'value1',
|
||||
tag2: 'value2',
|
||||
},
|
||||
annotations: {
|
||||
description: 'Description 1',
|
||||
summary: 'Summary 1',
|
||||
customProperty: 'Custom Value 1',
|
||||
},
|
||||
state: 'active',
|
||||
name: 'Alert 1',
|
||||
id: 1,
|
||||
endsAt: '2023-12-05T12:00:00Z',
|
||||
fingerprint: 'fingerprint1',
|
||||
generatorURL: 'generatorURL1',
|
||||
receivers: [],
|
||||
startsAt: '2023-12-05T11:00:00Z',
|
||||
status: { inhibitedBy: [], silencedBy: [], state: 'active' },
|
||||
updatedAt: '2023-12-05T11:30:00Z',
|
||||
},
|
||||
{
|
||||
labels: {
|
||||
alertname: 'Warning Alert',
|
||||
severity: 'warning',
|
||||
tag1: 'value3',
|
||||
tag2: 'value4',
|
||||
tag3: 'value5',
|
||||
},
|
||||
annotations: {
|
||||
description: 'Description 2',
|
||||
summary: 'Summary 2',
|
||||
customProperty: 'Custom Value 2',
|
||||
},
|
||||
state: 'inactive',
|
||||
name: 'Alert 2',
|
||||
id: 2,
|
||||
endsAt: '2023-12-05T13:00:00Z',
|
||||
fingerprint: 'fingerprint2',
|
||||
generatorURL: 'generatorURL2',
|
||||
receivers: [],
|
||||
startsAt: '2023-12-05T12:30:00Z',
|
||||
status: { inhibitedBy: [], silencedBy: [], state: 'inactive' },
|
||||
updatedAt: '2023-12-05T12:45:00Z',
|
||||
},
|
||||
// Add more test alerts as needed
|
||||
];
|
||||
|
||||
test('should render tags and expandable row when clicked', () => {
|
||||
render(<TableRowComponent tags={tags} tagsAlert={tagsAlerts} />);
|
||||
expect(screen.getByText('tag1')).toBeInTheDocument();
|
||||
expect(screen.getByText('tag2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Add more test cases as needed
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Alerts } from 'types/api/alerts/getTriggered';
|
||||
|
||||
import { Value } from './Filter';
|
||||
import { FilterAlerts } from './utils';
|
||||
|
||||
describe('FilterAlerts function', () => {
|
||||
const alerts: Alerts[] = [
|
||||
{
|
||||
labels: { severity: 'critical', app: 'myApp' },
|
||||
annotations: { description: 'Alert description', summary: 'Alert summary' },
|
||||
state: 'active',
|
||||
name: 'Alert 1',
|
||||
id: 1,
|
||||
endsAt: '2023-12-05T12:00:00Z',
|
||||
fingerprint: 'fingerprint1',
|
||||
generatorURL: 'generatorURL1',
|
||||
receivers: [],
|
||||
startsAt: '2023-12-05T11:00:00Z',
|
||||
status: { inhibitedBy: [], silencedBy: [], state: 'active' },
|
||||
updatedAt: '2023-12-05T11:30:00Z',
|
||||
},
|
||||
];
|
||||
|
||||
const selectedFilter: Value[] = [
|
||||
{ value: 'severity:critical' },
|
||||
{ value: 'app:myApp' },
|
||||
];
|
||||
|
||||
it('should filter alerts based on the selected filter', () => {
|
||||
const filteredAlerts = FilterAlerts(alerts, selectedFilter);
|
||||
expect(filteredAlerts).toHaveLength(1);
|
||||
expect(filteredAlerts[0].fingerprint).toEqual('fingerprint1');
|
||||
});
|
||||
|
||||
it('should return all alerts when no filter is selected', () => {
|
||||
const allAlerts = FilterAlerts(alerts, []);
|
||||
expect(allAlerts).toHaveLength(alerts.length);
|
||||
});
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
import { render } from 'tests/test-utils';
|
||||
|
||||
import Severity from './AlertStatus';
|
||||
|
||||
describe('Severity component', () => {
|
||||
it('should render UnProcessed tag for severity "unprocessed"', () => {
|
||||
const { getByText } = render(<Severity severity="unprocessed" />);
|
||||
const tagElement = getByText('UnProcessed');
|
||||
|
||||
expect(tagElement).toBeInTheDocument();
|
||||
expect(tagElement).toHaveClass('ant-tag-green');
|
||||
});
|
||||
|
||||
it('should render Firing tag for severity "active"', () => {
|
||||
const { getByText } = render(<Severity severity="active" />);
|
||||
const tagElement = getByText('Firing');
|
||||
|
||||
expect(tagElement).toBeInTheDocument();
|
||||
expect(tagElement).toHaveClass('ant-tag-red');
|
||||
});
|
||||
|
||||
it('should render Suppressed tag for severity "suppressed"', () => {
|
||||
const { getByText } = render(<Severity severity="suppressed" />);
|
||||
const tagElement = getByText('Suppressed');
|
||||
|
||||
expect(tagElement).toBeInTheDocument();
|
||||
expect(tagElement).toHaveClass('ant-tag-red');
|
||||
});
|
||||
|
||||
it('should render Unknown Status tag for unknown severity', () => {
|
||||
const { getByText } = render(<Severity severity="unknown" />);
|
||||
const tagElement = getByText('Unknown Status');
|
||||
|
||||
expect(tagElement).toBeInTheDocument();
|
||||
expect(tagElement).toHaveClass('ant-tag-default');
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, screen } from 'tests/test-utils';
|
||||
|
||||
import TriggeredAlerts from '.';
|
||||
|
||||
describe('TriggeredAlerts', () => {
|
||||
test('Should render the table', async () => {
|
||||
act(() => {
|
||||
render(<TriggeredAlerts />);
|
||||
});
|
||||
|
||||
const status = await screen.findByText('Status');
|
||||
expect(status).toBeInTheDocument();
|
||||
|
||||
const alertName = await screen.findByText('Alert Name');
|
||||
expect(alertName).toBeInTheDocument();
|
||||
|
||||
const severity = await screen.findByText('Severity');
|
||||
expect(severity).toBeInTheDocument();
|
||||
|
||||
const tags = await screen.findByText('Tags');
|
||||
expect(tags).toBeInTheDocument();
|
||||
|
||||
const firedSince = await screen.findByText('Firing Since');
|
||||
expect(firedSince).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// test('Should render the table data in triggeredAlert', async () => {
|
||||
// act(() => {
|
||||
// render(<TriggeredAlerts />);
|
||||
// });
|
||||
|
||||
// const row = await screen.findByRole('row', {
|
||||
// name: /firing above 400ms alertname: above 400ms component: net\/http details: https:\/\/demo\.\.\.\. \+2 warning 11\/30\/2023 10:04:19 am/i,
|
||||
// });
|
||||
// expect(row).toBeInTheDocument();
|
||||
// });
|
||||
});
|
||||
@@ -1,116 +0,0 @@
|
||||
import {
|
||||
NumTypeQueryOperators,
|
||||
QueryOperatorsMultiVal,
|
||||
QueryTypes,
|
||||
StringTypeQueryOperators,
|
||||
ValidTypeSequence,
|
||||
ValidTypeValue,
|
||||
} from './tokens';
|
||||
|
||||
describe('ValidTypeValue', () => {
|
||||
test('should return true for valid numeric values with number operators', () => {
|
||||
expect(ValidTypeValue(NumTypeQueryOperators.GTE, '42')).toBe(true);
|
||||
expect(ValidTypeValue(NumTypeQueryOperators.LT, '3.14')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for invalid numeric values with number operators', () => {
|
||||
expect(ValidTypeValue(NumTypeQueryOperators.GTE, 'abc')).toBe(false);
|
||||
expect(ValidTypeValue(NumTypeQueryOperators.LT, '12xyz')).toBe(false);
|
||||
});
|
||||
|
||||
test('should return true for string values with string operators', () => {
|
||||
expect(ValidTypeValue(StringTypeQueryOperators.CONTAINS, 'example')).toBe(
|
||||
true,
|
||||
);
|
||||
expect(ValidTypeValue(StringTypeQueryOperators.NCONTAINS, 'test')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true for any value with other operators', () => {
|
||||
expect(ValidTypeValue('anything', 'whatever')).toBe(true);
|
||||
expect(ValidTypeValue(QueryOperatorsMultiVal.IN, ['1', '2', '3'])).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false if value is array', () => {
|
||||
expect(ValidTypeValue(NumTypeQueryOperators.GTE, ['1', '2', '3'])).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ValidTypeSequence', () => {
|
||||
test('should return true for valid type sequences', () => {
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
undefined,
|
||||
QueryTypes.QUERY_KEY,
|
||||
QueryTypes.CONDITIONAL_OPERATOR,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.QUERY_KEY,
|
||||
QueryTypes.QUERY_OPERATOR,
|
||||
QueryTypes.QUERY_VALUE,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.QUERY_OPERATOR,
|
||||
QueryTypes.QUERY_VALUE,
|
||||
undefined,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false for invalid type sequences', () => {
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
undefined,
|
||||
QueryTypes.QUERY_OPERATOR,
|
||||
QueryTypes.QUERY_VALUE,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.QUERY_KEY,
|
||||
QueryTypes.QUERY_VALUE,
|
||||
QueryTypes.QUERY_OPERATOR,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.QUERY_OPERATOR,
|
||||
QueryTypes.QUERY_KEY,
|
||||
QueryTypes.QUERY_VALUE,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.QUERY_VALUE,
|
||||
QueryTypes.QUERY_OPERATOR,
|
||||
undefined,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.CONDITIONAL_OPERATOR,
|
||||
QueryTypes.QUERY_OPERATOR,
|
||||
QueryTypes.QUERY_KEY,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.CONDITIONAL_OPERATOR,
|
||||
undefined,
|
||||
QueryTypes.QUERY_KEY,
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
ValidTypeSequence(
|
||||
QueryTypes.QUERY_KEY,
|
||||
QueryTypes.CONDITIONAL_OPERATOR,
|
||||
undefined,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -1,163 +0,0 @@
|
||||
export const rulesSuccessResponse = {
|
||||
status: 'success',
|
||||
data: {
|
||||
rules: [
|
||||
{
|
||||
id: '5',
|
||||
state: 'disabled',
|
||||
alert: 'Test Rule 1',
|
||||
alertType: 'LOGS_BASED_ALERT',
|
||||
ruleType: 'threshold_rule',
|
||||
evalWindow: '1h0m0s',
|
||||
frequency: '1m0s',
|
||||
condition: {
|
||||
compositeQuery: {
|
||||
builderQueries: {
|
||||
A: {
|
||||
queryName: 'A',
|
||||
stepInterval: 60,
|
||||
dataSource: 'metrics',
|
||||
aggregateOperator: 'noop',
|
||||
aggregateAttribute: {
|
||||
key: '',
|
||||
dataType: 'float64',
|
||||
type: '',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
filters: {
|
||||
op: 'AND',
|
||||
items: null,
|
||||
},
|
||||
expression: 'A',
|
||||
disabled: false,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
pageSize: 0,
|
||||
reduceTo: 'last',
|
||||
},
|
||||
},
|
||||
chQueries: {
|
||||
A: {
|
||||
query:
|
||||
'select \ntoStartOfInterval(fromUnixTimestamp64Nano(timestamp), INTERVAL 1 MINUTE) AS interval, \ntoFloat64(count()) as value \nFROM signoz_logs.distributed_logs \nWHERE timestamp BETWEEN {{.start_timestamp_nano}} AND {{.end_timestamp_nano}}\n\nGROUP BY interval;\n\n-- available variables:\n-- \t{{.start_timestamp_nano}}\n-- \t{{.end_timestamp_nano}}\n\n-- required columns (or alias):\n-- \tvalue\n-- \tinterval',
|
||||
disabled: false,
|
||||
},
|
||||
},
|
||||
promQueries: {
|
||||
A: {
|
||||
query: '',
|
||||
disabled: false,
|
||||
},
|
||||
},
|
||||
panelType: 'graph',
|
||||
queryType: 'clickhouse_sql',
|
||||
},
|
||||
op: '1',
|
||||
target: 2000,
|
||||
matchType: '1',
|
||||
},
|
||||
labels: {
|
||||
details: 'https://stagingapp.signoz.io/logs',
|
||||
hello: 'world',
|
||||
region: 'us',
|
||||
severity: 'warning',
|
||||
type: 'test',
|
||||
},
|
||||
annotations: {
|
||||
description: 'description',
|
||||
summary: 'summary',
|
||||
},
|
||||
disabled: true,
|
||||
source:
|
||||
'https://stagingapp.signoz.io/alerts/edit?ruleId=5\u0026compositeQuery=%7B%22builder%22%3A%7B%22queryData%22%3A%5B%7B%22dataSource%22%3A%22metrics%22%2C%22queryName%22%3A%22A%22%2C%22aggregateOperator%22%3A%22noop%22%2C%22aggregateAttribute%22%3A%7B%22key%22%3A%22%22%2C%22dataType%22%3A%22float64%22%2C%22type%22%3A%22%22%2C%22isColumn%22%3Atrue%2C%22isJSON%22%3Afalse%7D%2C%22filters%22%3A%7B%22op%22%3A%22AND%22%2C%22items%22%3Anull%7D%2C%22expression%22%3A%22A%22%2C%22disabled%22%3Afalse%2C%22having%22%3A%5B%5D%2C%22stepInterval%22%3A60%2C%22limit%22%3A0%2C%22orderBy%22%3A%5B%5D%2C%22groupBy%22%3A%5B%5D%2C%22legend%22%3A%22%22%2C%22reduceTo%22%3A%22last%22%2C%22offset%22%3A0%2C%22pageSize%22%3A0%7D%5D%2C%22queryFormulas%22%3A%5B%5D%7D%2C%22promql%22%3A%5B%7B%22query%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22name%22%3A%22A%22%7D%5D%2C%22clickhouse_sql%22%3A%5B%7B%22query%22%3A%22select%20%5CntoStartOfInterval(fromUnixTimestamp64Nano(timestamp)%2C%20INTERVAL%201%20MINUTE)%20AS%20interval%2C%20%5CntoFloat64(count())%20as%20value%20%5CnFROM%20signoz_logs.distributed_logs%20%20%5CnWHERE%20timestamp%20BETWEEN%20%7B%7B.start_timestamp_nano%7D%7D%20AND%20%7B%7B.end_timestamp_nano%7D%7D%5Cn%5CnGROUP%20BY%20interval%3B%5Cn%5Cn--%20available%20variables%3A%5Cn--%20%5Ct%7B%7B.start_timestamp_nano%7D%7D%5Cn--%20%5Ct%7B%7B.end_timestamp_nano%7D%7D%5Cn%5Cn--%20required%20columns%20(or%20alias)%3A%5Cn--%20%5Ctvalue%5Cn--%20%5Ctinterval%22%2C%22disabled%22%3Afalse%2C%22name%22%3A%22A%22%7D%5D%2C%22queryType%22%3A%22clickhouse_sql%22%2C%22id%22%3A%22f17cf0cd-f479-4452-aded-e426aeda45ff%22%7D',
|
||||
preferredChannels: ['webhook-site'],
|
||||
createAt: null,
|
||||
createBy: null,
|
||||
updateAt: '2023-10-27T14:03:49.79371099Z',
|
||||
updateBy: 'ankit@signoz.io',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
state: 'inactive',
|
||||
alert: 'Test Rule 2',
|
||||
alertType: 'METRIC_BASED_ALERT',
|
||||
ruleType: 'threshold_rule',
|
||||
evalWindow: '5m0s',
|
||||
frequency: '1m0s',
|
||||
condition: {
|
||||
compositeQuery: {
|
||||
builderQueries: {
|
||||
A: {
|
||||
queryName: 'A',
|
||||
stepInterval: 60,
|
||||
dataSource: 'metrics',
|
||||
aggregateOperator: 'sum_rate',
|
||||
aggregateAttribute: {
|
||||
key: 'signoz_calls_total',
|
||||
dataType: 'float64',
|
||||
type: '',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
},
|
||||
filters: {
|
||||
op: 'AND',
|
||||
items: [],
|
||||
},
|
||||
groupBy: [
|
||||
{
|
||||
key: 'service_name',
|
||||
dataType: 'string',
|
||||
type: 'tag',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
},
|
||||
],
|
||||
expression: 'A',
|
||||
disabled: false,
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
pageSize: 0,
|
||||
reduceTo: 'sum',
|
||||
},
|
||||
},
|
||||
chQueries: {
|
||||
A: {
|
||||
query: '',
|
||||
disabled: false,
|
||||
},
|
||||
},
|
||||
promQueries: {
|
||||
A: {
|
||||
query: '',
|
||||
disabled: false,
|
||||
},
|
||||
},
|
||||
panelType: 'graph',
|
||||
queryType: 'builder',
|
||||
},
|
||||
op: '1',
|
||||
target: 20,
|
||||
matchType: '1',
|
||||
},
|
||||
labels: {
|
||||
severity: 'warning',
|
||||
},
|
||||
annotations: {
|
||||
description:
|
||||
'This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})',
|
||||
summary:
|
||||
'The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}',
|
||||
},
|
||||
disabled: false,
|
||||
source:
|
||||
'http://localhost:3301/alerts/edit?ruleId=6\u0026compositeQuery=%7B%22builder%22%3A%7B%22queryData%22%3A%5B%7B%22dataSource%22%3A%22metrics%22%2C%22queryName%22%3A%22A%22%2C%22aggregateOperator%22%3A%22sum_rate%22%2C%22aggregateAttribute%22%3A%7B%22key%22%3A%22signoz_calls_total%22%2C%22dataType%22%3A%22float64%22%2C%22type%22%3A%22%22%2C%22isColumn%22%3Atrue%7D%2C%22filters%22%3A%7B%22op%22%3A%22AND%22%2C%22items%22%3A%5B%5D%7D%2C%22expression%22%3A%22A%22%2C%22disabled%22%3Afalse%2C%22having%22%3A%5B%5D%2C%22stepInterval%22%3A60%2C%22limit%22%3A0%2C%22orderBy%22%3A%5B%5D%2C%22groupBy%22%3A%5B%7B%22key%22%3A%22service_name%22%2C%22dataType%22%3A%22string%22%2C%22type%22%3A%22tag%22%2C%22isColumn%22%3Afalse%7D%5D%2C%22legend%22%3A%22%22%2C%22reduceTo%22%3A%22sum%22%2C%22offset%22%3A0%2C%22pageSize%22%3A0%7D%5D%2C%22queryFormulas%22%3A%5B%5D%7D%2C%22promql%22%3A%5B%7B%22query%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22name%22%3A%22A%22%7D%5D%2C%22clickhouse_sql%22%3A%5B%7B%22query%22%3A%22%22%2C%22disabled%22%3Afalse%2C%22name%22%3A%22A%22%7D%5D%2C%22queryType%22%3A%22builder%22%2C%22id%22%3A%22c6486149-69b9-4e75-92ab-dde3282e558f%22%7D',
|
||||
preferredChannels: ['Slack-Discord-Compatible', 'Discord-webhook'],
|
||||
createAt: null,
|
||||
createBy: null,
|
||||
updateAt: '2023-10-06T09:48:07.047188664Z',
|
||||
updateBy: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,10 +1,8 @@
|
||||
import { rest } from 'msw';
|
||||
|
||||
import { alertsSuccessResponse } from './__mockdata__/alerts';
|
||||
import { billingSuccessResponse } from './__mockdata__/billing';
|
||||
import { licensesSuccessResponse } from './__mockdata__/licenses';
|
||||
import { queryRangeSuccessResponse } from './__mockdata__/query_range';
|
||||
import { rulesSuccessResponse } from './__mockdata__/rules';
|
||||
import { serviceSuccessResponse } from './__mockdata__/services';
|
||||
import { topLevelOperationSuccessResponse } from './__mockdata__/top_level_operations';
|
||||
|
||||
@@ -83,12 +81,4 @@ export const handlers = [
|
||||
rest.get('http://localhost/api/v1/billing', (req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(billingSuccessResponse)),
|
||||
),
|
||||
|
||||
rest.get('http://localhost/api/v1/rules', (req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(rulesSuccessResponse)),
|
||||
),
|
||||
|
||||
rest.get('http://localhost/api/v1/alerts', (req, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(alertsSuccessResponse)),
|
||||
),
|
||||
];
|
||||
|
||||
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.21
|
||||
require (
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.15.0
|
||||
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.4
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.6
|
||||
github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974
|
||||
github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974
|
||||
github.com/antonmedv/expr v1.15.3
|
||||
|
||||
4
go.sum
4
go.sum
@@ -98,8 +98,8 @@ github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb h1:bneLSKPf9YUSFm
|
||||
github.com/SigNoz/govaluate v0.0.0-20220522085550-d19c08c206cb/go.mod h1:JznGDNg9x1cujDKa22RaQOimOvvEfy3nxzDGd8XDgmA=
|
||||
github.com/SigNoz/prometheus v1.9.78 h1:bB3yuDrRzi/Mv00kWayR9DZbyjTuGfendSqISyDcXiY=
|
||||
github.com/SigNoz/prometheus v1.9.78/go.mod h1:MffmFu2qFILQrOHehx3D0XjYtaZMVfI+Ppeiv98x4Ww=
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.4 h1:vwyr26Iz1IkjTJQvcTk6E0StSg8pZGoz6aqXCAJjU8w=
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.4/go.mod h1:AkN5EPLaFn9TRS5303LzOEjiApld7TBoMImiRTXAvs8=
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.6 h1:rvXm9bz4b9GsYeT8c3+F/g56DHPf0IN8mK8tUfZfnw8=
|
||||
github.com/SigNoz/signoz-otel-collector v0.88.6/go.mod h1:6lR8Uy99zBd0JGPg9zt0aEBW4A4GpblUtpcbszGmg8E=
|
||||
github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc=
|
||||
github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo=
|
||||
github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY=
|
||||
|
||||
@@ -352,6 +352,13 @@ type logFieldsInExprExtractor struct {
|
||||
func (v *logFieldsInExprExtractor) Visit(node *ast.Node) {
|
||||
if n, ok := (*node).(*ast.MemberNode); ok {
|
||||
memberRef := n.String()
|
||||
|
||||
// coalesce ops end up as MemberNode right now for some reason.
|
||||
// ignore such member nodes.
|
||||
if strings.Contains(memberRef, "??") {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(memberRef, "attributes") || strings.HasPrefix(memberRef, "resource") {
|
||||
v.referencedFields = append(v.referencedFields, memberRef)
|
||||
}
|
||||
|
||||
@@ -698,6 +698,13 @@ func TestMembershipOpInProcessorFieldExpressions(t *testing.T) {
|
||||
Name: "add3",
|
||||
Field: `attributes["attrs.test.value"]`,
|
||||
Value: `EXPR(attributes.test?.value)`,
|
||||
}, {
|
||||
ID: "add4",
|
||||
Type: "add",
|
||||
Enabled: true,
|
||||
Name: "add4",
|
||||
Field: `attributes["attrs.test.value"]`,
|
||||
Value: `EXPR((attributes.temp?.request_context?.scraper ?? [nil])[0])`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ func (agent *Agent) processStatusUpdate(
|
||||
// If remote config is changed and different from what the Agent has then
|
||||
// send the new remote config to the Agent.
|
||||
if configChanged ||
|
||||
(agent.Status.RemoteConfigStatus != nil &&
|
||||
(agent.Status.RemoteConfigStatus != nil && agent.remoteConfig != nil &&
|
||||
!bytes.Equal(agent.Status.RemoteConfigStatus.LastRemoteConfigHash, agent.remoteConfig.ConfigHash)) {
|
||||
// The new status resulted in a change in the config of the Agent or the Agent
|
||||
// does not have this config (hash is different). Send the new config the Agent.
|
||||
@@ -276,7 +276,7 @@ func (agent *Agent) processStatusUpdate(
|
||||
func (agent *Agent) updateRemoteConfig(configProvider AgentConfigProvider) bool {
|
||||
recommendedConfig, confId, err := configProvider.RecommendAgentConfig([]byte(agent.EffectiveConfig))
|
||||
if err != nil {
|
||||
zap.S().Errorf("could not generate config recommendation for agent %d: %w", agent.ID, err)
|
||||
zap.S().Error("could not generate config recommendation for agent:", agent.ID, err)
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ services:
|
||||
<<: *db-depend
|
||||
|
||||
otel-collector-migrator:
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.4}
|
||||
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.6}
|
||||
container_name: otel-migrator
|
||||
command:
|
||||
- "--dsn=tcp://clickhouse:9000"
|
||||
@@ -205,7 +205,7 @@ services:
|
||||
# condition: service_healthy
|
||||
|
||||
otel-collector:
|
||||
image: signoz/signoz-otel-collector:0.88.4
|
||||
image: signoz/signoz-otel-collector:0.88.6
|
||||
container_name: signoz-otel-collector
|
||||
command:
|
||||
[
|
||||
@@ -245,7 +245,7 @@ services:
|
||||
condition: service_healthy
|
||||
|
||||
otel-collector-metrics:
|
||||
image: signoz/signoz-otel-collector:0.88.4
|
||||
image: signoz/signoz-otel-collector:0.88.6
|
||||
container_name: signoz-otel-collector-metrics
|
||||
command:
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user