Compare commits

..

1 Commits

Author SHA1 Message Date
nikhilmantri0902
b5165d1665 chore: made startNs and endNs a part of the struct 2025-11-06 16:53:59 +05:30
59 changed files with 42501 additions and 34336 deletions

View File

@@ -176,7 +176,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.101.0
image: signoz/signoz:v0.100.1
command:
- --config=/root/config/prometheus.yml
ports:

View File

@@ -117,7 +117,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:v0.101.0
image: signoz/signoz:v0.100.1
command:
- --config=/root/config/prometheus.yml
ports:

View File

@@ -179,7 +179,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.101.0}
image: signoz/signoz:${VERSION:-v0.100.1}
container_name: signoz
command:
- --config=/root/config/prometheus.yml

View File

@@ -111,7 +111,7 @@ services:
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
signoz:
!!merge <<: *db-depend
image: signoz/signoz:${VERSION:-v0.101.0}
image: signoz/signoz:${VERSION:-v0.100.1}
container_name: signoz
command:
- --config=/root/config/prometheus.yml

View File

@@ -274,7 +274,7 @@ function App(): JSX.Element {
chat_settings: {
app_id: process.env.PYLON_APP_ID,
email: user.email,
name: user.displayName || user.email,
name: user.displayName,
},
};
}

View File

@@ -1,4 +1,4 @@
import { LogEventAxiosInstance as axios } from 'api';
import { ApiBaseInstance as axios } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';

View File

@@ -1,11 +1,13 @@
/* eslint-disable sonarjs/no-duplicate-string */
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { getFieldKeys } from '../getFieldKeys';
// Mock the API instance
jest.mock('api', () => ({
get: jest.fn(),
ApiBaseInstance: {
get: jest.fn(),
},
}));
describe('getFieldKeys API', () => {
@@ -29,33 +31,33 @@ describe('getFieldKeys API', () => {
it('should call API with correct parameters when no args provided', async () => {
// Mock successful API response
(axios.get as jest.Mock).mockResolvedValueOnce(mockSuccessResponse);
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce(mockSuccessResponse);
// Call function with no parameters
await getFieldKeys();
// Verify API was called correctly with empty params object
expect(axios.get).toHaveBeenCalledWith('/fields/keys', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/keys', {
params: {},
});
});
it('should call API with signal parameter when provided', async () => {
// Mock successful API response
(axios.get as jest.Mock).mockResolvedValueOnce(mockSuccessResponse);
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce(mockSuccessResponse);
// Call function with signal parameter
await getFieldKeys('traces');
// Verify API was called with signal parameter
expect(axios.get).toHaveBeenCalledWith('/fields/keys', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/keys', {
params: { signal: 'traces' },
});
});
it('should call API with name parameter when provided', async () => {
// Mock successful API response
(axios.get as jest.Mock).mockResolvedValueOnce({
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce({
status: 200,
data: {
status: 'success',
@@ -70,14 +72,14 @@ describe('getFieldKeys API', () => {
await getFieldKeys(undefined, 'service');
// Verify API was called with name parameter
expect(axios.get).toHaveBeenCalledWith('/fields/keys', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/keys', {
params: { name: 'service' },
});
});
it('should call API with both signal and name when provided', async () => {
// Mock successful API response
(axios.get as jest.Mock).mockResolvedValueOnce({
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce({
status: 200,
data: {
status: 'success',
@@ -92,14 +94,14 @@ describe('getFieldKeys API', () => {
await getFieldKeys('logs', 'service');
// Verify API was called with both parameters
expect(axios.get).toHaveBeenCalledWith('/fields/keys', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/keys', {
params: { signal: 'logs', name: 'service' },
});
});
it('should return properly formatted response', async () => {
// Mock API to return our response
(axios.get as jest.Mock).mockResolvedValueOnce(mockSuccessResponse);
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce(mockSuccessResponse);
// Call the function
const result = await getFieldKeys('traces');

View File

@@ -1,11 +1,13 @@
/* eslint-disable sonarjs/no-duplicate-string */
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { getFieldValues } from '../getFieldValues';
// Mock the API instance
jest.mock('api', () => ({
get: jest.fn(),
ApiBaseInstance: {
get: jest.fn(),
},
}));
describe('getFieldValues API', () => {
@@ -15,7 +17,7 @@ describe('getFieldValues API', () => {
it('should call the API with correct parameters (no options)', async () => {
// Mock API response
(axios.get as jest.Mock).mockResolvedValueOnce({
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce({
status: 200,
data: {
status: 'success',
@@ -32,14 +34,14 @@ describe('getFieldValues API', () => {
await getFieldValues();
// Verify API was called correctly with empty params
expect(axios.get).toHaveBeenCalledWith('/fields/values', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/values', {
params: {},
});
});
it('should call the API with signal parameter', async () => {
// Mock API response
(axios.get as jest.Mock).mockResolvedValueOnce({
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce({
status: 200,
data: {
status: 'success',
@@ -56,14 +58,14 @@ describe('getFieldValues API', () => {
await getFieldValues('traces');
// Verify API was called with signal parameter
expect(axios.get).toHaveBeenCalledWith('/fields/values', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/values', {
params: { signal: 'traces' },
});
});
it('should call the API with name parameter', async () => {
// Mock API response
(axios.get as jest.Mock).mockResolvedValueOnce({
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce({
status: 200,
data: {
status: 'success',
@@ -80,14 +82,14 @@ describe('getFieldValues API', () => {
await getFieldValues(undefined, 'service.name');
// Verify API was called with name parameter
expect(axios.get).toHaveBeenCalledWith('/fields/values', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/values', {
params: { name: 'service.name' },
});
});
it('should call the API with value parameter', async () => {
// Mock API response
(axios.get as jest.Mock).mockResolvedValueOnce({
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce({
status: 200,
data: {
status: 'success',
@@ -104,14 +106,14 @@ describe('getFieldValues API', () => {
await getFieldValues(undefined, 'service.name', 'front');
// Verify API was called with value parameter
expect(axios.get).toHaveBeenCalledWith('/fields/values', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/values', {
params: { name: 'service.name', searchText: 'front' },
});
});
it('should call the API with time range parameters', async () => {
// Mock API response
(axios.get as jest.Mock).mockResolvedValueOnce({
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce({
status: 200,
data: {
status: 'success',
@@ -136,7 +138,7 @@ describe('getFieldValues API', () => {
);
// Verify API was called with time range parameters (converted to milliseconds)
expect(axios.get).toHaveBeenCalledWith('/fields/values', {
expect(ApiBaseInstance.get).toHaveBeenCalledWith('/fields/values', {
params: {
signal: 'logs',
name: 'service.name',
@@ -163,7 +165,7 @@ describe('getFieldValues API', () => {
},
};
(axios.get as jest.Mock).mockResolvedValueOnce(mockResponse);
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce(mockResponse);
// Call the function
const result = await getFieldValues('traces', 'mixed.values');
@@ -194,7 +196,7 @@ describe('getFieldValues API', () => {
};
// Mock API to return our response
(axios.get as jest.Mock).mockResolvedValueOnce(mockApiResponse);
(ApiBaseInstance.get as jest.Mock).mockResolvedValueOnce(mockApiResponse);
// Call the function
const result = await getFieldValues('traces', 'service.name');

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
@@ -24,7 +24,7 @@ export const getFieldKeys = async (
}
try {
const response = await axios.get('/fields/keys', { params });
const response = await ApiBaseInstance.get('/fields/keys', { params });
return {
httpStatusCode: response.status,

View File

@@ -1,5 +1,5 @@
/* eslint-disable sonarjs/cognitive-complexity */
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
@@ -47,7 +47,7 @@ export const getFieldValues = async (
}
try {
const response = await axios.get('/fields/values', { params });
const response = await ApiBaseInstance.get('/fields/values', { params });
// Normalize values from different types (stringValues, boolValues, etc.)
if (response.data?.data?.values) {

View File

@@ -86,9 +86,8 @@ const interceptorRejected = async (
if (
response.status === 401 &&
// if the session rotate call or the create session errors out with 401 or the delete sessions call returns 401 then we do not retry!
// if the session rotate call errors out with 401 or the delete sessions call returns 401 then we do not retry!
response.config.url !== '/sessions/rotate' &&
response.config.url !== '/sessions/email_password' &&
!(
response.config.url === '/sessions' && response.config.method === 'delete'
)
@@ -200,15 +199,15 @@ ApiV5Instance.interceptors.request.use(interceptorsRequestResponse);
//
// axios Base
export const LogEventAxiosInstance = axios.create({
export const ApiBaseInstance = axios.create({
baseURL: `${ENVIRONMENT.baseURL}${apiV1}`,
});
LogEventAxiosInstance.interceptors.response.use(
ApiBaseInstance.interceptors.response.use(
interceptorsResponse,
interceptorRejectedBase,
);
LogEventAxiosInstance.interceptors.request.use(interceptorsRequestResponse);
ApiBaseInstance.interceptors.request.use(interceptorsRequestResponse);
//
// gateway Api V1

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError, AxiosResponse } from 'axios';
import { baseAutoCompleteIdKeysOrder } from 'constants/queryBuilder';
@@ -17,7 +17,7 @@ export const getHostAttributeKeys = async (
try {
const response: AxiosResponse<{
data: IQueryAutocompleteResponse;
}> = await axios.get(
}> = await ApiBaseInstance.get(
`/${entity}/attribute_keys?dataSource=metrics&searchText=${searchText}`,
{
params: {

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { SOMETHING_WENT_WRONG } from 'constants/api';
@@ -20,7 +20,7 @@ const getOnboardingStatus = async (props: {
}): Promise<SuccessResponse<OnboardingStatusResponse> | ErrorResponse> => {
const { endpointService, ...rest } = props;
try {
const response = await axios.post(
const response = await ApiBaseInstance.post(
`/messaging-queues/kafka/onboarding/${endpointService || 'consumers'}`,
rest,
);

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
@@ -9,7 +9,7 @@ const getCustomFilters = async (
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
const { signal } = props;
try {
const response = await axios.get(`/orgs/me/filters/${signal}`);
const response = await ApiBaseInstance.get(`orgs/me/filters/${signal}`);
return {
statusCode: 200,

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { AxiosError } from 'axios';
import { SuccessResponse } from 'types/api';
import { UpdateCustomFiltersProps } from 'types/api/quickFilters/updateCustomFilters';
@@ -6,7 +6,7 @@ import { UpdateCustomFiltersProps } from 'types/api/quickFilters/updateCustomFil
const updateCustomFiltersAPI = async (
props: UpdateCustomFiltersProps,
): Promise<SuccessResponse<void> | AxiosError> =>
axios.put(`/orgs/me/filters`, {
ApiBaseInstance.put(`orgs/me/filters`, {
...props.data,
});

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
@@ -9,12 +9,15 @@ const listOverview = async (
): Promise<SuccessResponseV2<PayloadProps>> => {
const { start, end, show_ip: showIp, filter } = props;
try {
const response = await axios.post(`/third-party-apis/overview/list`, {
start,
end,
show_ip: showIp,
filter,
});
const response = await ApiBaseInstance.post(
`/third-party-apis/overview/list`,
{
start,
end,
show_ip: showIp,
filter,
},
);
return {
httpStatusCode: response.status,

View File

@@ -1,4 +1,4 @@
import axios from 'api';
import { ApiBaseInstance } from 'api';
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
import { AxiosError } from 'axios';
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
@@ -11,7 +11,7 @@ const getSpanPercentiles = async (
props: GetSpanPercentilesProps,
): Promise<SuccessResponseV2<GetSpanPercentilesResponseDataProps>> => {
try {
const response = await axios.post('/span_percentile', {
const response = await ApiBaseInstance.post('/span_percentile', {
...props,
});

View File

@@ -836,7 +836,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
})}
data-overlayscrollbars-initialize
>
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />} key={pathname}>
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
<LayoutContent data-overlayscrollbars-initialize>
<OverlayScrollbar>
<ChildrenContainer>

View File

@@ -17,6 +17,12 @@ export const Card = styled(CardComponent)<CardProps>`
overflow: hidden;
border-radius: 3px;
border: 1px solid var(--bg-slate-500);
background: linear-gradient(
0deg,
rgba(171, 189, 255, 0) 0%,
rgba(171, 189, 255, 0) 100%
),
#0b0c0e;
${({ isDarkMode }): StyledCSS =>
!isDarkMode &&

View File

@@ -90,9 +90,8 @@ export function QueryTable({
column: any,
tableColumns: any,
): void => {
e.stopPropagation();
if (isQueryTypeBuilder && enableDrillDown) {
e.stopPropagation();
onClick({ x: e.clientX, y: e.clientY }, { record, column, tableColumns });
}
},

View File

@@ -245,81 +245,5 @@ describe('useQueryBuilderOperations - Empty Aggregate Attribute Type', () => {
}),
);
});
it('should reset operators when going from gauge -> empty -> gauge', () => {
// Start with a gauge metric
const gaugeQuery: IBuilderQuery = {
...defaultMockQuery,
aggregateAttribute: {
key: 'original_gauge',
dataType: DataTypes.Float64,
type: ATTRIBUTE_TYPES.GAUGE,
} as BaseAutocompleteData,
aggregations: [
{
timeAggregation: MetricAggregateOperator.COUNT_DISTINCT,
metricName: 'original_gauge',
temporality: '',
spaceAggregation: '',
},
],
};
const { result, rerender } = renderHook(
({ query }) =>
useQueryOperations({
query,
index: 0,
entityVersion: ENTITY_VERSION_V5,
}),
{
initialProps: { query: gaugeQuery },
},
);
// Re-render with empty attribute
const emptyAttribute: BaseAutocompleteData = {
key: '',
dataType: DataTypes.Float64,
type: '',
};
const emptyQuery: IBuilderQuery = {
...defaultMockQuery,
aggregateAttribute: emptyAttribute,
aggregations: [
{
timeAggregation: MetricAggregateOperator.COUNT,
metricName: '',
temporality: '',
spaceAggregation: MetricAggregateOperator.SUM,
},
],
};
rerender({ query: emptyQuery });
// Change to a new gauge metric
const newGaugeAttribute: BaseAutocompleteData = {
key: 'new_gauge',
dataType: DataTypes.Float64,
type: ATTRIBUTE_TYPES.GAUGE,
};
act(() => {
result.current.handleChangeAggregatorAttribute(newGaugeAttribute);
});
expect(mockHandleSetQueryData).toHaveBeenLastCalledWith(
0,
expect.objectContaining({
aggregateAttribute: newGaugeAttribute,
aggregations: [
{
timeAggregation: MetricAggregateOperator.AVG,
metricName: 'new_gauge',
temporality: '',
spaceAggregation: '',
},
],
}),
);
});
});
});

View File

@@ -89,8 +89,6 @@ export const useQueryOperations: UseQueryOperations = ({
name: metricName,
type: metricType,
});
} else {
setPreviousMetricInfo(null);
}
}
}, [query]);
@@ -297,6 +295,7 @@ export const useQueryOperations: UseQueryOperations = ({
if (!isEditMode) {
// Get current metric info
const currentMetricName = newQuery.aggregateAttribute?.key || '';
const currentMetricType = newQuery.aggregateAttribute?.type || '';
const prevMetricType = previousMetricInfo?.type
@@ -379,6 +378,14 @@ export const useQueryOperations: UseQueryOperations = ({
];
}
}
// Update the tracked metric info for next comparison only if we have valid data
if (currentMetricName && currentMetricType) {
setPreviousMetricInfo({
name: currentMetricName,
type: currentMetricType,
});
}
}
}

View File

@@ -16,20 +16,8 @@
// https://tobyzerner.github.io/placement.js/dist/index.js
/**
* Positions an element (tooltip/popover) relative to a reference element.
* Automatically flips to the opposite side if there's insufficient space.
*
* @param element - The HTMLElement to position
* @param reference - Reference element/Range or bounding rect
* @param side - Preferred side: 'top', 'bottom', 'left', 'right' (default: 'bottom')
* @param align - Alignment: 'start', 'center', 'end' (default: 'center')
* @param options - Optional bounds for constraining the element
* - bound: Custom boundary rect/element
* - followCursor: { x, y } - If provided, tooltip follows cursor with smart positioning
*/
export const placement = (function () {
const AXIS_PROPS = {
const e = {
size: ['height', 'width'],
clientSize: ['clientHeight', 'clientWidth'],
offsetSize: ['offsetHeight', 'offsetWidth'],
@@ -40,241 +28,87 @@ export const placement = (function () {
marginAfter: ['marginBottom', 'marginRight'],
scrollOffset: ['pageYOffset', 'pageXOffset'],
};
function extractRect(source) {
return {
top: source.top,
bottom: source.bottom,
left: source.left,
right: source.right,
};
function t(e) {
return { top: e.top, bottom: e.bottom, left: e.left, right: e.right };
}
return function (element, reference, side, align, options) {
// Default parameters
void 0 === side && (side = 'bottom');
void 0 === align && (align = 'center');
void 0 === options && (options = {});
// Handle cursor following mode
if (options.followCursor) {
const cursorX = options.followCursor.x;
const cursorY = options.followCursor.y;
const offset = options.followCursor.offset || 10; // Default 10px offset from cursor
element.style.position = 'absolute';
element.style.maxWidth = '';
element.style.maxHeight = '';
const elementWidth = element.offsetWidth;
const elementHeight = element.offsetHeight;
// Use viewport bounds for cursor following (not chart bounds)
const viewportBounds = {
top: 0,
left: 0,
bottom: window.innerHeight,
right: window.innerWidth,
};
// Vertical positioning: follow cursor Y with offset, clamped to viewport
const topPosition = cursorY + offset;
const clampedTop = Math.max(
viewportBounds.top,
Math.min(topPosition, viewportBounds.bottom - elementHeight),
);
element.style.top = `${clampedTop}px`;
element.style.bottom = 'auto';
// Horizontal positioning: auto-detect left or right based on available space
const spaceOnRight = viewportBounds.right - cursorX;
const spaceOnLeft = cursorX - viewportBounds.left;
if (spaceOnRight >= elementWidth + offset) {
// Enough space on the right
element.style.left = `${cursorX + offset}px`;
element.style.right = 'auto';
element.dataset.side = 'right';
} else if (spaceOnLeft >= elementWidth + offset) {
// Not enough space on right, use left
element.style.left = `${cursorX - elementWidth - offset}px`;
element.style.right = 'auto';
element.dataset.side = 'left';
} else if (spaceOnRight > spaceOnLeft) {
// Not enough space on either side, pick the side with more space
const leftPos = cursorX + offset;
const clampedLeft = Math.max(
viewportBounds.left,
Math.min(leftPos, viewportBounds.right - elementWidth),
);
element.style.left = `${clampedLeft}px`;
element.style.right = 'auto';
element.dataset.side = 'right';
} else {
const leftPos = cursorX - elementWidth - offset;
const clampedLeft = Math.max(
viewportBounds.left,
Math.min(leftPos, viewportBounds.right - elementWidth),
);
element.style.left = `${clampedLeft}px`;
element.style.right = 'auto';
element.dataset.side = 'left';
}
element.dataset.align = 'cursor';
return; // Exit early, don't run normal positioning logic
}
// Normalize reference to rect object
(reference instanceof Element || reference instanceof Range) &&
(reference = extractRect(reference.getBoundingClientRect()));
// Create anchor rect with swapped opposite edges for positioning
const anchorRect = {
top: reference.bottom,
bottom: reference.top,
left: reference.right,
right: reference.left,
...reference,
return function (o, r, f, a, i) {
void 0 === f && (f = 'bottom'),
void 0 === a && (a = 'center'),
void 0 === i && (i = {}),
(r instanceof Element || r instanceof Range) &&
(r = t(r.getBoundingClientRect()));
const n = {
top: r.bottom,
bottom: r.top,
left: r.right,
right: r.left,
...r,
};
// Viewport bounds (can be overridden via options.bound)
const bounds = {
const s = {
top: 0,
left: 0,
bottom: window.innerHeight,
right: window.innerWidth,
};
options.bound &&
((options.bound instanceof Element || options.bound instanceof Range) &&
(options.bound = extractRect(options.bound.getBoundingClientRect())),
Object.assign(bounds, options.bound));
const styles = getComputedStyle(element);
const isVertical = side === 'top' || side === 'bottom';
// Build axis property maps based on orientation
const mainAxis = {}; // Properties for the main positioning axis
const crossAxis = {}; // Properties for the perpendicular axis
for (const prop in AXIS_PROPS) {
mainAxis[prop] = AXIS_PROPS[prop][isVertical ? 0 : 1];
crossAxis[prop] = AXIS_PROPS[prop][isVertical ? 1 : 0];
}
// Reset element positioning
element.style.position = 'absolute';
element.style.maxWidth = '';
element.style.maxHeight = '';
// Cross-axis: calculate and apply max size constraint
const crossMarginBefore = parseInt(styles[crossAxis.marginBefore]);
const crossMarginAfter = parseInt(styles[crossAxis.marginAfter]);
const crossMarginTotal = crossMarginBefore + crossMarginAfter;
const crossAvailableSpace =
bounds[crossAxis.after] - bounds[crossAxis.before] - crossMarginTotal;
const crossMaxSize = parseInt(styles[crossAxis.maxSize]);
(!crossMaxSize || crossAvailableSpace < crossMaxSize) &&
(element.style[crossAxis.maxSize] = `${crossAvailableSpace}px`);
// Main-axis: calculate space on both sides
const mainMarginTotal =
parseInt(styles[mainAxis.marginBefore]) +
parseInt(styles[mainAxis.marginAfter]);
const spaceBefore =
anchorRect[mainAxis.before] - bounds[mainAxis.before] - mainMarginTotal;
const spaceAfter =
bounds[mainAxis.after] - anchorRect[mainAxis.after] - mainMarginTotal;
// Auto-flip to the side with more space if needed
((side === mainAxis.before && element[mainAxis.offsetSize] > spaceBefore) ||
(side === mainAxis.after && element[mainAxis.offsetSize] > spaceAfter)) &&
(side = spaceBefore > spaceAfter ? mainAxis.before : mainAxis.after);
// Apply main-axis max size constraint
const mainAvailableSpace =
side === mainAxis.before ? spaceBefore : spaceAfter;
const mainMaxSize = parseInt(styles[mainAxis.maxSize]);
(!mainMaxSize || mainAvailableSpace < mainMaxSize) &&
(element.style[mainAxis.maxSize] = `${mainAvailableSpace}px`);
// Position on main axis
const mainScrollOffset = window[mainAxis.scrollOffset];
const clampMainPosition = function (pos) {
return Math.max(
bounds[mainAxis.before],
Math.min(
pos,
bounds[mainAxis.after] - element[mainAxis.offsetSize] - mainMarginTotal,
),
);
i.bound &&
((i.bound instanceof Element || i.bound instanceof Range) &&
(i.bound = t(i.bound.getBoundingClientRect())),
Object.assign(s, i.bound));
const l = getComputedStyle(o);
const m = {};
const b = {};
for (const g in e)
(m[g] = e[g][f === 'top' || f === 'bottom' ? 0 : 1]),
(b[g] = e[g][f === 'top' || f === 'bottom' ? 1 : 0]);
(o.style.position = 'absolute'),
(o.style.maxWidth = ''),
(o.style.maxHeight = '');
const d = parseInt(l[b.marginBefore]);
const c = parseInt(l[b.marginAfter]);
const u = d + c;
const p = s[b.after] - s[b.before] - u;
const h = parseInt(l[b.maxSize]);
(!h || p < h) && (o.style[b.maxSize] = `${p}px`);
const x = parseInt(l[m.marginBefore]) + parseInt(l[m.marginAfter]);
const y = n[m.before] - s[m.before] - x;
const z = s[m.after] - n[m.after] - x;
((f === m.before && o[m.offsetSize] > y) ||
(f === m.after && o[m.offsetSize] > z)) &&
(f = y > z ? m.before : m.after);
const S = f === m.before ? y : z;
const v = parseInt(l[m.maxSize]);
(!v || S < v) && (o.style[m.maxSize] = `${S}px`);
const w = window[m.scrollOffset];
const O = function (e) {
return Math.max(s[m.before], Math.min(e, s[m.after] - o[m.offsetSize] - x));
};
side === mainAxis.before
? ((element.style[mainAxis.before] = `${
mainScrollOffset +
clampMainPosition(
anchorRect[mainAxis.before] -
element[mainAxis.offsetSize] -
mainMarginTotal,
)
}px`),
(element.style[mainAxis.after] = 'auto'))
: ((element.style[mainAxis.before] = `${
mainScrollOffset + clampMainPosition(anchorRect[mainAxis.after])
}px`),
(element.style[mainAxis.after] = 'auto'));
// Position on cross axis based on alignment
const crossScrollOffset = window[crossAxis.scrollOffset];
const clampCrossPosition = function (pos) {
return Math.max(
bounds[crossAxis.before],
Math.min(
pos,
bounds[crossAxis.after] - element[crossAxis.offsetSize] - crossMarginTotal,
),
);
f === m.before
? ((o.style[m.before] = `${w + O(n[m.before] - o[m.offsetSize] - x)}px`),
(o.style[m.after] = 'auto'))
: ((o.style[m.before] = `${w + O(n[m.after])}px`),
(o.style[m.after] = 'auto'));
const B = window[b.scrollOffset];
const I = function (e) {
return Math.max(s[b.before], Math.min(e, s[b.after] - o[b.offsetSize] - u));
};
switch (align) {
switch (a) {
case 'start':
(element.style[crossAxis.before] = `${
crossScrollOffset +
clampCrossPosition(anchorRect[crossAxis.before] - crossMarginBefore)
}px`),
(element.style[crossAxis.after] = 'auto');
(o.style[b.before] = `${B + I(n[b.before] - d)}px`),
(o.style[b.after] = 'auto');
break;
case 'end':
(element.style[crossAxis.before] = 'auto'),
(element.style[crossAxis.after] = `${
crossScrollOffset +
clampCrossPosition(
document.documentElement[crossAxis.clientSize] -
anchorRect[crossAxis.after] -
crossMarginAfter,
)
(o.style[b.before] = 'auto'),
(o.style[b.after] = `${
B + I(document.documentElement[b.clientSize] - n[b.after] - c)
}px`);
break;
default:
// 'center'
var crossSize = anchorRect[crossAxis.after] - anchorRect[crossAxis.before];
(element.style[crossAxis.before] = `${
crossScrollOffset +
clampCrossPosition(
anchorRect[crossAxis.before] +
crossSize / 2 -
element[crossAxis.offsetSize] / 2 -
crossMarginBefore,
)
var H = n[b.after] - n[b.before];
(o.style[b.before] = `${
B + I(n[b.before] + H / 2 - o[b.offsetSize] / 2 - d)
}px`),
(element.style[crossAxis.after] = 'auto');
(o.style[b.after] = 'auto');
}
// Store final placement as data attributes
(element.dataset.side = side), (element.dataset.align = align);
(o.dataset.side = f), (o.dataset.align = a);
};
})();

View File

@@ -3,71 +3,7 @@ import { themeColors } from 'constants/theme';
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
function isSeriesValueValid(seriesValue: number | undefined | null): boolean {
return (
seriesValue !== undefined &&
seriesValue !== null &&
!Number.isNaN(seriesValue)
);
}
// Helper function to get the focused/highlighted series at a specific position
function resolveSeriesColor(series: uPlot.Series, index: number): string {
let color = '#000000';
if (typeof series.stroke === 'string') {
color = series.stroke;
} else if (typeof series.fill === 'string') {
color = series.fill;
} else {
const seriesLabel = series.label || `Series ${index}`;
const isDarkMode = !document.body.classList.contains('lightMode');
color = generateColor(
seriesLabel,
isDarkMode ? themeColors.chartcolors : themeColors.lightModeColor,
);
}
return color;
}
function getPreferredSeriesIndex(
u: uPlot,
timestampIndex: number,
e: MouseEvent,
): number {
const bbox = u.over.getBoundingClientRect();
const top = e.clientY - bbox.top;
// Prefer series explicitly marked as focused
for (let i = 1; i < u.series.length; i++) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const isSeriesFocused = u.series[i]?._focus === true;
const isSeriesShown = u.series[i].show !== false;
const seriesValue = u.data[i]?.[timestampIndex];
if (isSeriesFocused && isSeriesShown && isSeriesValueValid(seriesValue)) {
return i;
}
}
// Fallback: choose series with Y closest to mouse position
let focusedSeriesIndex = -1;
let closestPixelDiff = Infinity;
for (let i = 1; i < u.series.length; i++) {
const series = u.data[i];
const seriesValue = series?.[timestampIndex];
if (isSeriesValueValid(seriesValue) && u.series[i].show !== false) {
const yPx = u.valToPos(seriesValue as number, 'y');
const diff = Math.abs(yPx - top);
if (diff < closestPixelDiff) {
closestPixelDiff = diff;
focusedSeriesIndex = i;
}
}
}
return focusedSeriesIndex;
}
export const getFocusedSeriesAtPosition = (
e: MouseEvent,
u: uPlot,
@@ -81,28 +17,74 @@ export const getFocusedSeriesAtPosition = (
} | null => {
const bbox = u.over.getBoundingClientRect();
const left = e.clientX - bbox.left;
const top = e.clientY - bbox.top;
const timestampIndex = u.posToIdx(left);
const preferredIndex = getPreferredSeriesIndex(u, timestampIndex, e);
let focusedSeriesIndex = -1;
let closestPixelDiff = Infinity;
// Check all series (skip index 0 which is the x-axis)
for (let i = 1; i < u.data.length; i++) {
const series = u.data[i];
const seriesValue = series[timestampIndex];
if (
seriesValue !== undefined &&
seriesValue !== null &&
!Number.isNaN(seriesValue)
) {
const seriesYPx = u.valToPos(seriesValue, 'y');
const pixelDiff = Math.abs(seriesYPx - top);
if (pixelDiff < closestPixelDiff) {
closestPixelDiff = pixelDiff;
focusedSeriesIndex = i;
}
}
}
// If we found a focused series, return its data
if (focusedSeriesIndex > 0) {
const series = u.series[focusedSeriesIndex];
const seriesValue = u.data[focusedSeriesIndex][timestampIndex];
// Ensure we have a valid value
if (
seriesValue !== undefined &&
seriesValue !== null &&
!Number.isNaN(seriesValue)
) {
// Get color - try series stroke first, then generate based on label
let color = '#000000';
if (typeof series.stroke === 'string') {
color = series.stroke;
} else if (typeof series.fill === 'string') {
color = series.fill;
} else {
// Generate color based on series label (like the tooltip plugin does)
const seriesLabel = series.label || `Series ${focusedSeriesIndex}`;
// Detect theme mode by checking body class
const isDarkMode = !document.body.classList.contains('lightMode');
color = generateColor(
seriesLabel,
isDarkMode ? themeColors.chartcolors : themeColors.lightModeColor,
);
}
if (preferredIndex > 0) {
const series = u.series[preferredIndex];
const seriesValue = u.data[preferredIndex][timestampIndex];
if (isSeriesValueValid(seriesValue)) {
const color = resolveSeriesColor(series, preferredIndex);
return {
seriesIndex: preferredIndex,
seriesName: series.label || `Series ${preferredIndex}`,
seriesIndex: focusedSeriesIndex,
seriesName: series.label || `Series ${focusedSeriesIndex}`,
value: seriesValue as number,
color,
show: series.show !== false,
isFocused: true,
isFocused: true, // This indicates it's the highlighted/bold one
};
}
}
return null;
};
export interface OnClickPluginOpts {
onClick: (
xValue: number,
@@ -155,31 +137,50 @@ function onClickPlugin(opts: OnClickPluginOpts): uPlot.Plugin {
const yValue = u.posToVal(event.offsetY, 'y');
// Get the focused/highlighted series (the one that would be bold in hover)
const focusedSeriesData = getFocusedSeriesAtPosition(event, u);
const focusedSeries = getFocusedSeriesAtPosition(event, u);
let metric = {};
const { series } = u;
const apiResult = opts.apiResponse?.data?.result || [];
const outputMetric = {
queryName: '',
inFocusOrNot: false,
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (
focusedSeriesData &&
focusedSeriesData.seriesIndex <= apiResult.length
) {
const { metric: focusedMetric, queryName } =
apiResult[focusedSeriesData.seriesIndex - 1] || {};
metric = focusedMetric;
outputMetric.queryName = queryName;
outputMetric.inFocusOrNot = true;
// this is to get the metric value of the focused series
if (Array.isArray(series) && series.length > 0) {
series.forEach((item, index) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (item?.show && item?._focus) {
const { metric: focusedMetric, queryName } = apiResult[index - 1] || [];
metric = focusedMetric;
outputMetric.queryName = queryName;
outputMetric.inFocusOrNot = true;
}
});
}
if (!outputMetric.queryName) {
// Get the focused series data
const focusedSeriesData = getFocusedSeriesAtPosition(event, u);
// If we found a valid focused series, get its data
if (
focusedSeriesData &&
focusedSeriesData.seriesIndex <= apiResult.length
) {
const { metric: focusedMetric, queryName } =
apiResult[focusedSeriesData.seriesIndex - 1] || [];
metric = focusedMetric;
outputMetric.queryName = queryName;
outputMetric.inFocusOrNot = true;
}
}
// Get the actual data point timestamp from the focused series
let actualDataTimestamp = xValue; // fallback to click position timestamp
if (focusedSeriesData) {
if (focusedSeries) {
// Get the data index from the focused series
const dataIndex = u.posToIdx(event.offsetX);
// Get the actual timestamp from the x-axis data (u.data[0])
@@ -208,7 +209,7 @@ function onClickPlugin(opts: OnClickPluginOpts): uPlot.Plugin {
absoluteMouseX,
absoluteMouseY,
axesData,
focusedSeriesData,
focusedSeries,
);
};
u.over.addEventListener('click', handleClick);

View File

@@ -415,11 +415,7 @@ ToolTipPluginProps): any => {
}
// Clear and set new content in one operation
overlay.replaceChildren(content);
placement(overlay, anchor, 'right', 'start', {
bound,
followCursor: { x: anchor.left, y: anchor.top, offset: 4 },
});
placement(overlay, anchor, 'right', 'start', { bound });
showOverlay();
} else {
hideOverlay();

View File

@@ -16,6 +16,6 @@ export const topTracesTableColumns = [
title: 'STEP TRANSITION DURATION',
dataIndex: 'duration_ms',
key: 'duration_ms',
render: (value: string): string => getYAxisFormattedValue(`${value}`, 'ms'),
render: (value: string): string => getYAxisFormattedValue(value, 'ms'),
},
];

View File

@@ -401,14 +401,14 @@ body {
font-size: 12px;
position: absolute;
margin: 0.5rem;
background: var(--bg-ink-300);
background: rgba(0, 0, 0);
-webkit-font-smoothing: antialiased;
color: var(--bg-vanilla-100);
color: #fff;
z-index: 10000;
// pointer-events: none;
overflow: auto;
max-height: 480px !important;
max-width: 300px !important;
max-width: 240px !important;
border-radius: 5px;
border: 1px solid rgba(255, 255, 255, 0.1);
@@ -571,12 +571,6 @@ body {
}
.lightMode {
#overlay {
color: var(--bg-ink-500);
background: var(--bg-vanilla-100);
border: 1px solid var(--bg-vanilla-300);
}
.ant-dropdown-menu {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);

View File

@@ -42,4 +42,4 @@ type URLShareableOptions struct {
SelectColumns []v3.AttributeKey `json:"selectColumns"`
}
var PredefinedAlertLabels = []string{ruletypes.LabelThresholdName, ruletypes.LabelSeverityName, ruletypes.LabelLastSeen}
var PredefinedAlertLabels = []string{ruletypes.LabelThresholdName}

View File

@@ -94,8 +94,6 @@ type SigNozAgentConfig struct {
IngestionKey string `json:"ingestion_key"`
SigNozAPIUrl string `json:"signoz_api_url"`
SigNozAPIKey string `json:"signoz_api_key"`
Version string `json:"version,omitempty"`
}
type GenerateConnectionUrlResponse struct {
@@ -116,10 +114,8 @@ func (c *Controller) GenerateConnectionUrl(ctx context.Context, orgId string, cl
return nil, model.WrapApiError(apiErr, "couldn't upsert cloud account")
}
agentVersion := "v0.0.6"
if req.AgentConfig.Version != "" {
agentVersion = req.AgentConfig.Version
}
// TODO(Raj): parameterized this in follow up changes
agentVersion := "v0.0.5"
connectionUrl := fmt.Sprintf(
"https://%s.console.aws.amazon.com/cloudformation/home?region=%s#/stacks/quickcreate?",

View File

@@ -1,6 +1,6 @@
{
"description": "This dashboard provides a high-level overview of your MongoDB. It includes read/write performance, most-used replicas, collection metrics etc...",
"id": "mongo-overview",
"description": "This dashboard provides a high-level overview of your MongoDB. It includes read/write performance, most-used replicas, collection metrics etc...",
"layout": [
{
"h": 3,
@@ -92,7 +92,6 @@
"type": "QUERY"
}
},
"version": "v5",
"widgets": [
{
"description": "Total number of operations",
@@ -106,20 +105,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb_operation_count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_operation_count--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_operation_count",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "a468a30b",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -131,13 +147,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -181,20 +196,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb_operation_time",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_operation_time--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_operation_time",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "31be3166",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -206,13 +238,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -256,20 +287,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb_cache_operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_cache_operations--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_cache_operations",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "01b45814",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -281,13 +329,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "{{type}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -331,29 +378,58 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb_operation_latency_time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_operation_latency_time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_operation_latency_time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'read' AND host_name IN $host_name)"
"filters": {
"items": [
{
"id": "2e165319",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "read"
},
{
"id": "888e920b",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -397,29 +473,58 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb_operation_latency_time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_operation_latency_time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_operation_latency_time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host_name IN $host_name AND operation = 'write')"
"filters": {
"items": [
{
"id": "53b37ca7",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
},
{
"id": "9862c46c",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "write"
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -463,29 +568,58 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb_operation_latency_time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_operation_latency_time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_operation_latency_time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host_name IN $host_name AND operation = 'command')"
"filters": {
"items": [
{
"id": "c33ad4b6",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
},
{
"id": "c70ecfd0",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "command"
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -529,20 +663,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb_network_io_receive",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_network_io_receive--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_network_io_receive",
"type": "Sum"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "5c9d7fe3",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -554,30 +705,46 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Bytes received :: {{host_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
},
{
"aggregations": [
{
"metricName": "mongodb_network_io_transmit",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb_network_io_transmit--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb_network_io_transmit",
"type": "Sum"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "B",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "96520885",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -589,13 +756,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Bytes transmitted :: {{host_name}}",
"limit": null,
"orderBy": [],
"queryName": "B",
"reduceTo": "sum",
"stepInterval": 60
}
],

View File

@@ -1,6 +1,6 @@
{
"description": "This dashboard provides a high-level overview of your MongoDB. It includes read/write performance, most-used replicas, collection metrics etc...",
"id": "mongo-overview",
"description": "This dashboard provides a high-level overview of your MongoDB. It includes read/write performance, most-used replicas, collection metrics etc...",
"layout": [
{
"h": 3,
@@ -92,7 +92,6 @@
"type": "QUERY"
}
},
"version": "v5",
"widgets": [
{
"description": "Total number of operations",
@@ -106,20 +105,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.count--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.count",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "a468a30b",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -131,13 +147,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -181,20 +196,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.time",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.time--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.time",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "31be3166",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -206,13 +238,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -256,20 +287,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.cache.operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.cache.operations--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.cache.operations",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "01b45814",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -281,13 +329,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "{{type}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -331,29 +378,58 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.latency.time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.latency.time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'read' AND host.name IN $host.name)"
"filters": {
"items": [
{
"id": "2e165319",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "read"
},
{
"id": "888e920b",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -397,29 +473,58 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.latency.time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.latency.time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host.name IN $host.name AND operation = 'write')"
"filters": {
"items": [
{
"id": "53b37ca7",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
},
{
"id": "9862c46c",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "write"
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -463,29 +568,58 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.operation.latency.time--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.operation.latency.time",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host.name IN $host.name AND operation = 'command')"
"filters": {
"items": [
{
"id": "c33ad4b6",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
},
{
"id": "c70ecfd0",
"key": {
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
},
"op": "=",
"value": "command"
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -529,20 +663,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.network.io.receive",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.network.io.receive--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.network.io.receive",
"type": "Sum"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "5c9d7fe3",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -554,30 +705,46 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Bytes received :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
},
{
"aggregations": [
{
"metricName": "mongodb.network.io.transmit",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "mongodb.network.io.transmit--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "mongodb.network.io.transmit",
"type": "Sum"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "B",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "96520885",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -589,13 +756,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Bytes transmitted :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "B",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -628,4 +794,4 @@
"yAxisUnit": "bytes"
}
]
}
}

View File

@@ -1,6 +1,6 @@
{
"description": "This dashboard shows the Redis instance overview. It includes latency, hit/miss rate, connections, and memory information.\n",
"id": "redis-overview",
"description": "This dashboard shows the Redis instance overview. It includes latency, hit/miss rate, connections, and memory information.\n",
"layout": [
{
"h": 3,
@@ -111,7 +111,6 @@
"type": "QUERY"
}
},
"version": "v5",
"widgets": [
{
"description": "Rate successful lookup of keys in the main dictionary",
@@ -125,29 +124,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_keyspace_hits",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_keyspace_hits--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis_keyspace_hits",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "e99669ea",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Hit/s across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -191,29 +206,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_clients_blocked",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_clients_blocked--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis_clients_blocked",
"type": "Sum"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "97247f25",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Blocked clients across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -257,26 +288,28 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_db_keys",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "",
"id": "redis_db_keys------false",
"isColumn": false,
"key": "redis_db_keys",
"type": ""
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"groupBy": [],
"having": {
"expression": ""
"filters": {
"items": [],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -320,29 +353,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_rdb_changes_since_last_save",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_rdb_changes_since_last_save--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis_rdb_changes_since_last_save",
"type": "Sum"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "d4aef346",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Number of unsaved changes",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -386,29 +435,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_commands",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_commands--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis_commands",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "458dc402",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "ops/s",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -452,20 +517,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_memory_used",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_memory_used--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis_memory_used",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "394a537e",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -477,30 +559,46 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Used::{{host_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
},
{
"aggregations": [
{
"metricName": "redis_maxmemory",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_maxmemory--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis_maxmemory",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "B",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "0c0754da",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -512,13 +610,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Max::{{host_name}}",
"limit": null,
"orderBy": [],
"queryName": "B",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -562,20 +659,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_memory_rss",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_memory_rss--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis_memory_rss",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "4dc9ae49",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -587,13 +701,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Rss::{{host_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -637,20 +750,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_memory_fragmentation_ratio",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_memory_fragmentation_ratio--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis_memory_fragmentation_ratio",
"type": "Gauge"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "79dc25f3",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -662,13 +792,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Rss::{{host_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -712,20 +841,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis_keys_evicted",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis_keys_evicted--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis_keys_evicted",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"filters": {
"items": [
{
"id": "53d189ac",
"key": {
"dataType": "string",
"id": "host_name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host_name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -737,13 +883,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Rss::{{host_name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],

View File

@@ -1,6 +1,6 @@
{
"description": "This dashboard shows the Redis instance overview. It includes latency, hit/miss rate, connections, and memory information.\n",
"id": "redis-overview",
"description": "This dashboard shows the Redis instance overview. It includes latency, hit/miss rate, connections, and memory information.\n",
"layout": [
{
"h": 3,
@@ -111,7 +111,6 @@
"type": "QUERY"
}
},
"version": "v5",
"widgets": [
{
"description": "Rate successful lookup of keys in the main dictionary",
@@ -125,29 +124,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.keyspace.hits",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.keyspace.hits--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.keyspace.hits",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "e99669ea",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Hit/s across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -191,29 +206,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.clients.blocked",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.clients.blocked--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.clients.blocked",
"type": "Sum"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "97247f25",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Blocked clients across all hosts",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -257,26 +288,28 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.db.keys",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "",
"id": "redis.db.keys------false",
"isColumn": false,
"key": "redis.db.keys",
"type": ""
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"groupBy": [],
"having": {
"expression": ""
"filters": {
"items": [],
"op": "AND"
},
"groupBy": [],
"having": [],
"legend": "",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -320,29 +353,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.rdb.changes_since_last_save",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.rdb.changes_since_last_save--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.rdb.changes_since_last_save",
"type": "Sum"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "d4aef346",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "Number of unsaved changes",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -386,29 +435,45 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.commands",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.commands--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.commands",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "458dc402",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [],
"having": {
"expression": ""
},
"having": [],
"legend": "ops/s",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -452,20 +517,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.memory.used",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.memory.used--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.memory.used",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "394a537e",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -477,30 +559,46 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Used::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
},
{
"aggregations": [
{
"metricName": "redis.maxmemory",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.maxmemory--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.maxmemory",
"type": "Gauge"
},
"aggregateOperator": "max",
"dataSource": "metrics",
"disabled": false,
"expression": "B",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "0c0754da",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -512,13 +610,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Max::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "B",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -562,20 +659,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.memory.rss",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "sum"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.memory.rss--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.memory.rss",
"type": "Gauge"
},
"aggregateOperator": "sum",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "4dc9ae49",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -587,13 +701,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Rss::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -637,20 +750,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.memory.fragmentation_ratio",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.memory.fragmentation_ratio--float64--Gauge--true",
"isColumn": true,
"isJSON": false,
"key": "redis.memory.fragmentation_ratio",
"type": "Gauge"
},
"aggregateOperator": "avg",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "79dc25f3",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -662,13 +792,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Rss::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -712,20 +841,37 @@
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "redis.keys.evicted",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"aggregateAttribute": {
"dataType": "float64",
"id": "redis.keys.evicted--float64--Sum--true",
"isColumn": true,
"isJSON": false,
"key": "redis.keys.evicted",
"type": "Sum"
},
"aggregateOperator": "sum_rate",
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
"filters": {
"items": [
{
"id": "53d189ac",
"key": {
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
},
"op": "in",
"value": [
"{{.host.name}}"
]
}
],
"op": "AND"
},
"groupBy": [
{
@@ -737,13 +883,12 @@
"type": "tag"
}
],
"having": {
"expression": ""
},
"having": [],
"legend": "Rss::{{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"reduceTo": "sum",
"stepInterval": 60
}
],
@@ -776,4 +921,4 @@
"yAxisUnit": "short"
}
]
}
}

View File

@@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes"
"math"
"net/http"
"sort"
@@ -14,8 +15,6 @@ import (
"text/template"
"time"
"github.com/SigNoz/signoz/pkg/types/thirdpartyapitypes"
"github.com/SigNoz/govaluate"
"github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
queues2 "github.com/SigNoz/signoz/pkg/query-service/app/integrations/messagingQueues/queues"

View File

@@ -467,7 +467,7 @@ func (r *ThresholdRule) buildAndRunQuery(ctx context.Context, orgID valuer.UUID,
r.logger.InfoContext(ctx, "no data found for rule condition", "rule_id", r.ID())
lbls := labels.NewBuilder(labels.Labels{})
if !r.lastTimestampWithDatapoints.IsZero() {
lbls.Set(ruletypes.LabelLastSeen, r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat))
lbls.Set("lastSeen", r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat))
}
resultVector = append(resultVector, ruletypes.Sample{
Metric: lbls.Labels(),
@@ -544,7 +544,7 @@ func (r *ThresholdRule) buildAndRunQueryV5(ctx context.Context, orgID valuer.UUI
r.logger.InfoContext(ctx, "no data found for rule condition", "rule_id", r.ID())
lbls := labels.NewBuilder(labels.Labels{})
if !r.lastTimestampWithDatapoints.IsZero() {
lbls.Set(ruletypes.LabelLastSeen, r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat))
lbls.Set("lastSeen", r.lastTimestampWithDatapoints.Format(constants.AlertTimeFormat))
}
resultVector = append(resultVector, ruletypes.Sample{
Metric: lbls.Labels(),

View File

@@ -54,6 +54,8 @@ func (r *aggExprRewriter) Rewrite(
expr string,
rateInterval uint64,
keys map[string][]*telemetrytypes.TelemetryFieldKey,
startNs uint64,
endNs uint64,
) (string, []any, error) {
wrapped := fmt.Sprintf("SELECT %s", expr)
@@ -83,6 +85,8 @@ func (r *aggExprRewriter) Rewrite(
r.conditionBuilder,
r.jsonBodyPrefix,
r.jsonKeyToKey,
startNs,
endNs,
)
// Rewrite the first select item (our expression)
if err := sel.SelectItems[0].Accept(visitor); err != nil {
@@ -101,12 +105,14 @@ func (r *aggExprRewriter) RewriteMulti(
exprs []string,
rateInterval uint64,
keys map[string][]*telemetrytypes.TelemetryFieldKey,
startNs uint64,
endNs uint64,
) ([]string, [][]any, error) {
out := make([]string, len(exprs))
var errs []error
var chArgsList [][]any
for i, e := range exprs {
w, chArgs, err := r.Rewrite(ctx, e, rateInterval, keys)
w, chArgs, err := r.Rewrite(ctx, e, rateInterval, keys, startNs, endNs)
if err != nil {
errs = append(errs, err)
out[i] = e
@@ -134,6 +140,8 @@ type exprVisitor struct {
Modified bool
chArgs []any
isRate bool
startNs uint64
endNs uint64
}
func newExprVisitor(
@@ -144,6 +152,8 @@ func newExprVisitor(
conditionBuilder qbtypes.ConditionBuilder,
jsonBodyPrefix string,
jsonKeyToKey qbtypes.JsonKeyToFieldFunc,
startNs uint64,
endNs uint64,
) *exprVisitor {
return &exprVisitor{
logger: logger,
@@ -153,6 +163,8 @@ func newExprVisitor(
conditionBuilder: conditionBuilder,
jsonBodyPrefix: jsonBodyPrefix,
jsonKeyToKey: jsonKeyToKey,
startNs: startNs,
endNs: endNs,
}
}
@@ -190,7 +202,7 @@ func (v *exprVisitor) VisitFunctionExpr(fn *chparser.FunctionExpr) error {
if aggFunc.FuncCombinator {
// Map the predicate (last argument)
origPred := args[len(args)-1].String()
whereClause, err := PrepareWhereClause(
whereClause, err := PrepareWhereClause(
origPred,
FilterExprVisitorOpts{
Logger: v.logger,
@@ -200,7 +212,7 @@ func (v *exprVisitor) VisitFunctionExpr(fn *chparser.FunctionExpr) error {
FullTextColumn: v.fullTextColumn,
JsonBodyPrefix: v.jsonBodyPrefix,
JsonKeyToKey: v.jsonKeyToKey,
}, 0, 0,
}, v.startNs, v.endNs,
)
if err != nil {
return err

View File

@@ -350,6 +350,8 @@ func (b *logQueryStatementBuilder) buildTimeSeriesQuery(
ctx, agg.Expression,
uint64(query.StepInterval.Seconds()),
keys,
start,
end,
)
if err != nil {
return nil, err
@@ -499,6 +501,8 @@ func (b *logQueryStatementBuilder) buildScalarQuery(
ctx, aggExpr.Expression,
rateInterval,
keys,
start,
end,
)
if err != nil {
return nil, err
@@ -592,7 +596,7 @@ func (b *logQueryStatementBuilder) addFilterCondition(
JsonBodyPrefix: b.jsonBodyPrefix,
JsonKeyToKey: b.jsonKeyToKey,
Variables: variables,
}, start, end)
}, start, end)
if err != nil {
return nil, err

View File

@@ -512,6 +512,8 @@ func (b *traceQueryStatementBuilder) buildTimeSeriesQuery(
ctx, agg.Expression,
uint64(query.StepInterval.Seconds()),
keys,
start,
end,
)
if err != nil {
return nil, err
@@ -657,6 +659,8 @@ func (b *traceQueryStatementBuilder) buildScalarQuery(
ctx, aggExpr.Expression,
rateInterval,
keys,
start,
end,
)
if err != nil {
return nil, err
@@ -746,7 +750,7 @@ func (b *traceQueryStatementBuilder) addFilterCondition(
FieldKeys: keys,
SkipResourceFilter: true,
Variables: variables,
}, start, end)
}, start, end)
if err != nil {
return nil, err

View File

@@ -237,7 +237,7 @@ func (b *traceOperatorCTEBuilder) buildQueryCTE(ctx context.Context, queryName s
ConditionBuilder: b.stmtBuilder.cb,
FieldKeys: keys,
SkipResourceFilter: true,
}, b.start, b.end,
}, b.start, b.end,
)
if err != nil {
b.stmtBuilder.logger.ErrorContext(ctx, "Failed to prepare where clause", "error", err, "filter", query.Filter.Expression)
@@ -575,6 +575,8 @@ func (b *traceOperatorCTEBuilder) buildTimeSeriesQuery(ctx context.Context, sele
agg.Expression,
uint64(b.operator.StepInterval.Seconds()),
keys,
b.start,
b.end,
)
if err != nil {
return nil, errors.NewInvalidInputf(
@@ -687,6 +689,8 @@ func (b *traceOperatorCTEBuilder) buildTraceQuery(ctx context.Context, selectFro
agg.Expression,
rateInterval,
keys,
b.start,
b.end,
)
if err != nil {
return nil, errors.NewInvalidInputf(
@@ -825,6 +829,8 @@ func (b *traceOperatorCTEBuilder) buildScalarQuery(ctx context.Context, selectFr
agg.Expression,
uint64((b.end-b.start)/querybuilder.NsToSeconds),
keys,
b.start,
b.end,
)
if err != nil {
return nil, errors.NewInvalidInputf(

View File

@@ -93,13 +93,10 @@ func NewConfigFromStoreableConfig(sc *StoreableConfig) (*Config, error) {
}
func NewDefaultConfig(globalConfig GlobalConfig, routeConfig RouteConfig, orgID string) (*Config, error) {
// Mergo treats an explicit false as zero-value and overwrites it to true, so we save it in smtpRequireTLS and restore the user-specified value.
smtpRequireTLS := globalConfig.SMTPRequireTLS
err := mergo.Merge(&globalConfig, config.DefaultGlobalConfig())
if err != nil {
return nil, err
}
globalConfig.SMTPRequireTLS = smtpRequireTLS
route, err := NewRouteFromRouteConfig(nil, routeConfig)
if err != nil {
@@ -170,13 +167,10 @@ func (c *Config) CopyWithReset() (*Config, error) {
}
func (c *Config) SetGlobalConfig(globalConfig GlobalConfig) error {
// Mergo treats an explicit false as zero-value and overwrites it to true, so we save it in smtpRequireTLS and restore the user-specified value.
smtpRequireTLS := globalConfig.SMTPRequireTLS
err := mergo.Merge(&globalConfig, config.DefaultGlobalConfig())
if err != nil {
return err
}
globalConfig.SMTPRequireTLS = smtpRequireTLS
c.alertmanagerConfig.Global = &globalConfig
c.storeableConfig.Config = string(newRawFromConfig(c.alertmanagerConfig))

View File

@@ -282,50 +282,3 @@ func TestUTF8Validation(t *testing.T) {
})
}
}
func TestNewDefaultConfigPreservesSMTPRequireTLS(t *testing.T) {
testCases := []struct {
name string
globalConfig GlobalConfig
expect bool
}{
{"False", GlobalConfig{SMTPRequireTLS: false}, false},
{"True", GlobalConfig{SMTPRequireTLS: true}, true},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
global := tt.globalConfig
route := RouteConfig{
GroupInterval: time.Minute,
GroupWait: time.Minute,
RepeatInterval: time.Minute,
}
cfg, err := NewDefaultConfig(global, route, "1")
require.NoError(t, err)
assert.Equal(t, tt.expect, cfg.alertmanagerConfig.Global.SMTPRequireTLS)
})
}
}
func TestSetGlobalConfigPreservesSMTPRequireTLS(t *testing.T) {
testCases := []struct {
name string
globalConfig GlobalConfig
expect bool
}{
{"False", GlobalConfig{SMTPRequireTLS: false}, false},
{"True", GlobalConfig{SMTPRequireTLS: true}, true},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
c := NewConfig(&config.Config{}, "1")
global := tt.globalConfig
err := c.SetGlobalConfig(global)
require.NoError(t, err)
assert.Equal(t, tt.expect, c.alertmanagerConfig.Global.SMTPRequireTLS)
})
}
}

View File

@@ -37,8 +37,8 @@ type ConditionBuilder interface {
type AggExprRewriter interface {
// Rewrite rewrites the aggregation expression to be used in the query.
Rewrite(ctx context.Context, expr string, rateInterval uint64, keys map[string][]*telemetrytypes.TelemetryFieldKey) (string, []any, error)
RewriteMulti(ctx context.Context, exprs []string, rateInterval uint64, keys map[string][]*telemetrytypes.TelemetryFieldKey) ([]string, [][]any, error)
Rewrite(ctx context.Context, expr string, rateInterval uint64, keys map[string][]*telemetrytypes.TelemetryFieldKey, startNs uint64, endNs uint64) (string, []any, error)
RewriteMulti(ctx context.Context, exprs []string, rateInterval uint64, keys map[string][]*telemetrytypes.TelemetryFieldKey, startNs uint64, endNs uint64) ([]string, [][]any, error)
}
type Statement struct {

View File

@@ -1,12 +1,5 @@
package ruletypes
const (
CriticalThresholdName = "critical"
ErrorThresholdName = "error"
WarningThresholdName = "warning"
InfoThresholdName = "info"
LabelThresholdName = "threshold.name"
LabelSeverityName = "severity"
LabelLastSeen = "lastSeen"
LabelRuleId = "ruleId"
)
const CriticalThresholdName = "CRITICAL"
const LabelThresholdName = "threshold.name"
const LabelRuleId = "ruleId"

View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"math"
"sort"
"strings"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/query-service/converter"
@@ -199,10 +198,7 @@ func (b BasicRuleThreshold) shouldAlert(series v3.Series, ruleUnit string) (Samp
target := b.target(ruleUnit)
// TODO(srikanthccv): is it better to move the logic to notifier instead of
// adding two labels?
lbls = append(lbls, labels.Label{Name: LabelThresholdName, Value: b.Name})
lbls = append(lbls, labels.Label{Name: LabelSeverityName, Value: strings.ToLower(b.Name)})
series.Points = removeGroupinSetPoints(series)

View File

@@ -1,7 +1,6 @@
package ruletypes
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -22,7 +21,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "milliseconds to seconds conversion - should alert",
threshold: BasicRuleThreshold{
Name: CriticalThresholdName,
Name: "test",
TargetValue: &target, // 100ms
TargetUnit: "ms",
MatchType: AtleastOnce,
@@ -40,7 +39,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "milliseconds to seconds conversion - should not alert",
threshold: BasicRuleThreshold{
Name: WarningThresholdName,
Name: "test",
TargetValue: &target, // 100ms
TargetUnit: "ms",
MatchType: AtleastOnce,
@@ -58,7 +57,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "seconds to milliseconds conversion - should alert",
threshold: BasicRuleThreshold{
Name: CriticalThresholdName,
Name: "test",
TargetValue: &target, // 100s
TargetUnit: "s",
MatchType: AtleastOnce,
@@ -77,7 +76,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "bytes to kibibytes conversion - should alert",
threshold: BasicRuleThreshold{
Name: InfoThresholdName,
Name: "test",
TargetValue: &target, // 100 bytes
TargetUnit: "bytes",
MatchType: AtleastOnce,
@@ -95,7 +94,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "kibibytes to mebibytes conversion - should alert",
threshold: BasicRuleThreshold{
Name: ErrorThresholdName,
Name: "test",
TargetValue: &target, // 100KiB
TargetUnit: "kbytes",
MatchType: AtleastOnce,
@@ -114,7 +113,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "milliseconds to seconds with ValueIsBelow - should alert",
threshold: BasicRuleThreshold{
Name: WarningThresholdName,
Name: "test",
TargetValue: &target, // 100ms
TargetUnit: "ms",
MatchType: AtleastOnce,
@@ -132,7 +131,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "milliseconds to seconds with OnAverage - should alert",
threshold: BasicRuleThreshold{
Name: CriticalThresholdName,
Name: "test",
TargetValue: &target, // 100ms
TargetUnit: "ms",
MatchType: OnAverage,
@@ -152,7 +151,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "decimal megabytes to gigabytes with InTotal - should alert",
threshold: BasicRuleThreshold{
Name: WarningThresholdName,
Name: "test",
TargetValue: &target, // 100MB
TargetUnit: "decmbytes",
MatchType: InTotal,
@@ -172,7 +171,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "milliseconds to seconds with AllTheTimes - should alert",
threshold: BasicRuleThreshold{
Name: InfoThresholdName,
Name: "test",
TargetValue: &target, // 100ms
TargetUnit: "ms",
MatchType: AllTheTimes,
@@ -192,7 +191,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "kilobytes to megabytes with Last - should not alert",
threshold: BasicRuleThreshold{
Name: ErrorThresholdName,
Name: "test",
TargetValue: &target, // 100kB
TargetUnit: "deckbytes",
MatchType: Last,
@@ -212,7 +211,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "bytes per second to kilobytes per second - should alert",
threshold: BasicRuleThreshold{
Name: CriticalThresholdName,
Name: "test",
TargetValue: &target, // 100 bytes/s
TargetUnit: "Bps",
MatchType: AtleastOnce,
@@ -231,7 +230,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "same unit - no conversion needed - should alert",
threshold: BasicRuleThreshold{
Name: InfoThresholdName,
Name: "test",
TargetValue: &target, // 100ms
TargetUnit: "ms",
MatchType: AtleastOnce,
@@ -250,7 +249,7 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
{
name: "empty unit - no conversion - should alert",
threshold: BasicRuleThreshold{
Name: ErrorThresholdName,
Name: "test",
TargetValue: &target, // 100 (unitless)
TargetUnit: "",
MatchType: AtleastOnce,
@@ -281,20 +280,12 @@ func TestBasicRuleThresholdShouldAlert_UnitConversion(t *testing.T) {
hasThresholdLabel := false
for _, label := range sample.Metric {
if label.Name == LabelThresholdName && label.Value == tt.threshold.Name {
if label.Name == LabelThresholdName && label.Value == "test" {
hasThresholdLabel = true
break
}
}
assert.True(t, hasThresholdLabel)
hasSeverityLabel := false
for _, label := range sample.Metric {
if label.Name == LabelSeverityName && label.Value == strings.ToLower(tt.threshold.Name) {
hasSeverityLabel = true
break
}
}
assert.True(t, hasSeverityLabel)
assert.Equal(t, *tt.threshold.TargetValue, sample.Target)
assert.Equal(t, tt.threshold.TargetUnit, sample.TargetUnit)
}