mirror of
https://github.com/SigNoz/signoz.git
synced 2026-08-07 21:50:39 +01:00
Compare commits
1 Commits
chore/remo
...
issue-4293
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ab6eafb88 |
15
.github/workflows/goci.yaml
vendored
15
.github/workflows/goci.yaml
vendored
@@ -53,21 +53,6 @@ jobs:
|
||||
with:
|
||||
PRIMUS_REF: main
|
||||
GO_VERSION: 1.24
|
||||
semconv-generated:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: self-checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: go-install
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: check-semconv-generated-files
|
||||
run: go run ./scripts/semconv -check
|
||||
build:
|
||||
if: |
|
||||
github.event_name == 'merge_group' ||
|
||||
|
||||
1
.github/workflows/integrationci.yaml
vendored
1
.github/workflows/integrationci.yaml
vendored
@@ -58,6 +58,7 @@ jobs:
|
||||
- querierai
|
||||
- rawexportdata
|
||||
- promqlconformance
|
||||
- promapiconformance
|
||||
- querierauthz
|
||||
- role
|
||||
- rootuser
|
||||
|
||||
4
Makefile
4
Makefile
@@ -233,10 +233,6 @@ py-clean: ## Clear all pycache and pytest cache from tests directory recursively
|
||||
##############################################################
|
||||
# generate commands
|
||||
##############################################################
|
||||
.PHONY: semconv-generate
|
||||
semconv-generate: ## Regenerate semantic-convention families for Go and TypeScript
|
||||
@go run ./scripts/semconv
|
||||
|
||||
.PHONY: gen-mocks
|
||||
gen-mocks:
|
||||
@echo ">> Generating mocks"
|
||||
|
||||
@@ -98,6 +98,14 @@ func (ah *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
Route: "",
|
||||
})
|
||||
|
||||
if constants.IsDotMetricsEnabled {
|
||||
for idx, feature := range featureSet {
|
||||
if feature.Name == licensetypes.DotMetricsEnabled {
|
||||
featureSet[idx].Active = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ah.Respond(w, featureSet)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,15 @@ func GetOrDefaultEnv(key string, fallback string) string {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// constant functions that override env vars
|
||||
|
||||
const DotMetricsEnabled = "DOT_METRICS_ENABLED"
|
||||
|
||||
var IsDotMetricsEnabled = false
|
||||
|
||||
func init() {
|
||||
if GetOrDefaultEnv(DotMetricsEnabled, "true") == "true" {
|
||||
IsDotMetricsEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,3 +24,19 @@ export const Logout = async (): Promise<void> => {
|
||||
window.dispatchEvent(new CustomEvent('LOGOUT'));
|
||||
history.push(ROUTES.LOGIN);
|
||||
};
|
||||
|
||||
export const UnderscoreToDotMap: Record<string, string> = {
|
||||
k8s_cluster_name: 'k8s.cluster.name',
|
||||
k8s_cluster_uid: 'k8s.cluster.uid',
|
||||
k8s_namespace_name: 'k8s.namespace.name',
|
||||
k8s_node_name: 'k8s.node.name',
|
||||
k8s_node_uid: 'k8s.node.uid',
|
||||
k8s_pod_name: 'k8s.pod.name',
|
||||
k8s_pod_uid: 'k8s.pod.uid',
|
||||
k8s_deployment_name: 'k8s.deployment.name',
|
||||
k8s_daemonset_name: 'k8s.daemonset.name',
|
||||
k8s_statefulset_name: 'k8s.statefulset.name',
|
||||
k8s_cronjob_name: 'k8s.cronjob.name',
|
||||
k8s_job_name: 'k8s.job.name',
|
||||
k8s_persistentvolumeclaim_name: 'k8s.persistentvolumeclaim.name',
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ export enum FeatureKeys {
|
||||
GATEWAY = 'gateway',
|
||||
PREMIUM_SUPPORT = 'premium_support',
|
||||
ANOMALY_DETECTION = 'anomaly_detection',
|
||||
DOT_METRICS_ENABLED = 'dot_metrics_enabled',
|
||||
USE_JSON_BODY = 'use_json_body',
|
||||
ENABLE_AI_OBSERVABILITY = 'enable_ai_observability',
|
||||
ENABLE_METRICS_REDUCTION = 'enable_metrics_reduction',
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Code generated by scripts/semconv. DO NOT EDIT.
|
||||
|
||||
export type SemconvFamily = {
|
||||
readonly current: string;
|
||||
readonly old: readonly string[];
|
||||
readonly kind: 'attribute' | 'metric';
|
||||
readonly contexts: readonly string[];
|
||||
readonly signals: readonly string[];
|
||||
readonly applyToMetrics: readonly string[];
|
||||
readonly valueMap: Readonly<Record<string, string>>;
|
||||
};
|
||||
|
||||
export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [
|
||||
{
|
||||
current: 'db.system.name',
|
||||
old: ['db.system'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
{
|
||||
current: 'deployment.environment.name',
|
||||
old: ['deployment.environment'],
|
||||
kind: 'attribute',
|
||||
contexts: [],
|
||||
signals: [],
|
||||
applyToMetrics: [],
|
||||
valueMap: {},
|
||||
},
|
||||
] as const;
|
||||
@@ -37,6 +37,8 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { Exception, PayloadProps } from 'types/api/errors/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../constants/features';
|
||||
import { useAppContext } from '../../providers/App/App';
|
||||
import { FilterDropdownExtendsProps } from './types';
|
||||
import {
|
||||
extractFilterValues,
|
||||
@@ -416,6 +418,11 @@ function AllErrors(): JSX.Element {
|
||||
},
|
||||
];
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const onChangeHandler: TableProps<Exception>['onChange'] = useCallback(
|
||||
(
|
||||
paginations: TablePaginationConfig,
|
||||
@@ -451,7 +458,7 @@ function AllErrors(): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (!isUndefined(errorCountResponse.data?.payload)) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(),
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('Exception: List page visited', {
|
||||
|
||||
@@ -35,6 +35,7 @@ import { openInNewTab } from 'utils/navigation';
|
||||
|
||||
import triangleRulerUrl from '@/assets/Icons/triangle-ruler.svg';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { DOCS_LINKS } from '../constants';
|
||||
import { columns, TIME_PICKER_OPTIONS } from './constants';
|
||||
|
||||
@@ -211,13 +212,19 @@ function ServiceMetrics({
|
||||
|
||||
const topLevelOperations = useMemo(() => Object.entries(data || {}), [data]);
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const queryRangeRequestData = useMemo(
|
||||
() =>
|
||||
getQueryRangeRequestData({
|
||||
topLevelOperations,
|
||||
globalSelectedInterval,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
[globalSelectedInterval, topLevelOperations],
|
||||
[globalSelectedInterval, topLevelOperations, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const dataQueries = useGetQueriesRange(
|
||||
|
||||
@@ -82,7 +82,7 @@ export function getHostMetricsQueryPayload(
|
||||
start: number,
|
||||
end: number,
|
||||
): ReturnType<typeof getHostQueryPayload> {
|
||||
return getHostQueryPayload(host.hostName, start, end);
|
||||
return getHostQueryPayload(host.hostName, start, end, true);
|
||||
}
|
||||
|
||||
export { hostWidgetInfo };
|
||||
|
||||
@@ -121,6 +121,12 @@ jest.spyOn(appContextHooks, 'useAppContext').mockReturnValue({
|
||||
plan_version: 'test-plan-version',
|
||||
},
|
||||
},
|
||||
featureFlags: [
|
||||
{
|
||||
name: 'DOT_METRICS_ENABLED',
|
||||
active: false,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
|
||||
const mockEntity = {
|
||||
|
||||
@@ -17,6 +17,8 @@ import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import {
|
||||
getHostQueryPayload,
|
||||
getNodeQueryPayload,
|
||||
@@ -51,12 +53,23 @@ function NodeMetrics({
|
||||
};
|
||||
}, [timestamp]);
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const queryPayloads = useMemo(() => {
|
||||
if (nodeName) {
|
||||
return getNodeQueryPayload(clusterName, nodeName, start, end);
|
||||
return getNodeQueryPayload(
|
||||
clusterName,
|
||||
nodeName,
|
||||
start,
|
||||
end,
|
||||
dotMetricsEnabled,
|
||||
);
|
||||
}
|
||||
return getHostQueryPayload(hostName, start, end);
|
||||
}, [nodeName, hostName, clusterName, start, end]);
|
||||
return getHostQueryPayload(hostName, start, end, dotMetricsEnabled);
|
||||
}, [nodeName, hostName, clusterName, start, end, dotMetricsEnabled]);
|
||||
|
||||
const widgetInfo = nodeName ? nodeWidgetInfo : hostWidgetInfo;
|
||||
const queries = useQueries(
|
||||
|
||||
@@ -12,11 +12,13 @@ import { useResizeObserver } from 'hooks/useDimensions';
|
||||
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
|
||||
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
||||
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { getPodQueryPayload, podWidgetInfo } from './constants';
|
||||
|
||||
function PodMetrics({
|
||||
@@ -52,9 +54,14 @@ function PodMetrics({
|
||||
scrollLeft: 0,
|
||||
});
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const queryPayloads = useMemo(
|
||||
() => getPodQueryPayload(clusterName, podName, start, end),
|
||||
[clusterName, end, podName, start],
|
||||
() => getPodQueryPayload(clusterName, podName, start, end, dotMetricsEnabled),
|
||||
[clusterName, end, podName, start, dotMetricsEnabled],
|
||||
);
|
||||
const queries = useQueries(
|
||||
queryPayloads.map((payload) => ({
|
||||
|
||||
@@ -9,21 +9,48 @@ export const getPodQueryPayload = (
|
||||
podName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
dotMetricsEnabled: boolean,
|
||||
): GetQueryResultsProps[] => {
|
||||
const k8sClusterNameKey = 'k8s.cluster.name';
|
||||
const k8sPodNameKey = 'k8s.pod.name';
|
||||
const containerCpuUtilKey = 'container.cpu.usage';
|
||||
const containerMemUsageKey = 'container.memory.usage';
|
||||
const k8sContainerCpuReqKey = 'k8s.container.cpu_request';
|
||||
const k8sContainerCpuLimitKey = 'k8s.container.cpu_limit';
|
||||
const k8sContainerMemReqKey = 'k8s.container.memory_request';
|
||||
const k8sContainerMemLimitKey = 'k8s.container.memory_limit';
|
||||
const k8sPodFsAvailKey = 'k8s.pod.filesystem.available';
|
||||
const k8sPodFsCapKey = 'k8s.pod.filesystem.capacity';
|
||||
const k8sPodNetIoKey = 'k8s.pod.network.io';
|
||||
const podLegendTemplate = '{{k8s.pod.name}}';
|
||||
const podLegendUsage = 'usage - {{k8s.pod.name}}';
|
||||
const podLegendLimit = 'limit - {{k8s.pod.name}}';
|
||||
const k8sClusterNameKey = dotMetricsEnabled
|
||||
? 'k8s.cluster.name'
|
||||
: 'k8s_cluster_name';
|
||||
const k8sPodNameKey = dotMetricsEnabled ? 'k8s.pod.name' : 'k8s_pod_name';
|
||||
const containerCpuUtilKey = dotMetricsEnabled
|
||||
? 'container.cpu.usage'
|
||||
: 'container_cpu_usage';
|
||||
const containerMemUsageKey = dotMetricsEnabled
|
||||
? 'container.memory.usage'
|
||||
: 'container_memory_usage';
|
||||
const k8sContainerCpuReqKey = dotMetricsEnabled
|
||||
? 'k8s.container.cpu_request'
|
||||
: 'k8s_container_cpu_request';
|
||||
const k8sContainerCpuLimitKey = dotMetricsEnabled
|
||||
? 'k8s.container.cpu_limit'
|
||||
: 'k8s_container_cpu_limit';
|
||||
const k8sContainerMemReqKey = dotMetricsEnabled
|
||||
? 'k8s.container.memory_request'
|
||||
: 'k8s_container_memory_request';
|
||||
const k8sContainerMemLimitKey = dotMetricsEnabled
|
||||
? 'k8s.container.memory_limit'
|
||||
: 'k8s_container_memory_limit';
|
||||
const k8sPodFsAvailKey = dotMetricsEnabled
|
||||
? 'k8s.pod.filesystem.available'
|
||||
: 'k8s_pod_filesystem_available';
|
||||
const k8sPodFsCapKey = dotMetricsEnabled
|
||||
? 'k8s.pod.filesystem.capacity'
|
||||
: 'k8s_pod_filesystem_capacity';
|
||||
const k8sPodNetIoKey = dotMetricsEnabled
|
||||
? 'k8s.pod.network.io'
|
||||
: 'k8s_pod_network_io';
|
||||
const podLegendTemplate = dotMetricsEnabled
|
||||
? '{{k8s.pod.name}}'
|
||||
: '{{k8s_pod_name}}';
|
||||
const podLegendUsage = dotMetricsEnabled
|
||||
? 'usage - {{k8s.pod.name}}'
|
||||
: 'usage - {{k8s_pod_name}}';
|
||||
const podLegendLimit = dotMetricsEnabled
|
||||
? 'limit - {{k8s.pod.name}}'
|
||||
: 'limit - {{k8s_pod_name}}';
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -1000,17 +1027,36 @@ export const getNodeQueryPayload = (
|
||||
nodeName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
dotMetricsEnabled: boolean,
|
||||
): GetQueryResultsProps[] => {
|
||||
const k8sClusterNameKey = 'k8s.cluster.name';
|
||||
const k8sNodeNameKey = 'k8s.node.name';
|
||||
const k8sNodeCpuTimeKey = 'k8s.node.cpu.time';
|
||||
const k8sNodeAllocCpuKey = 'k8s.node.allocatable_cpu';
|
||||
const k8sNodeMemWsKey = 'k8s.node.memory.working_set';
|
||||
const k8sNodeAllocMemKey = 'k8s.node.allocatable_memory';
|
||||
const k8sNodeNetIoKey = 'k8s.node.network.io';
|
||||
const k8sNodeFsAvailKey = 'k8s.node.filesystem.available';
|
||||
const k8sNodeFsCapKey = 'k8s.node.filesystem.capacity';
|
||||
const podLegend = '{{k8s.node.name}}';
|
||||
const k8sClusterNameKey = dotMetricsEnabled
|
||||
? 'k8s.cluster.name'
|
||||
: 'k8s_cluster_name';
|
||||
const k8sNodeNameKey = dotMetricsEnabled ? 'k8s.node.name' : 'k8s_node_name';
|
||||
const k8sNodeCpuTimeKey = dotMetricsEnabled
|
||||
? 'k8s.node.cpu.time'
|
||||
: 'k8s_node_cpu_time';
|
||||
const k8sNodeAllocCpuKey = dotMetricsEnabled
|
||||
? 'k8s.node.allocatable_cpu'
|
||||
: 'k8s_node_allocatable_cpu';
|
||||
const k8sNodeMemWsKey = dotMetricsEnabled
|
||||
? 'k8s.node.memory.working_set'
|
||||
: 'k8s_node_memory_working_set';
|
||||
const k8sNodeAllocMemKey = dotMetricsEnabled
|
||||
? 'k8s.node.allocatable_memory'
|
||||
: 'k8s_node_allocatable_memory';
|
||||
const k8sNodeNetIoKey = dotMetricsEnabled
|
||||
? 'k8s.node.network.io'
|
||||
: 'k8s_node_network_io';
|
||||
const k8sNodeFsAvailKey = dotMetricsEnabled
|
||||
? 'k8s.node.filesystem.available'
|
||||
: 'k8s_node_filesystem_available';
|
||||
const k8sNodeFsCapKey = dotMetricsEnabled
|
||||
? 'k8s.node.filesystem.capacity'
|
||||
: 'k8s_node_filesystem_capacity';
|
||||
const podLegend = dotMetricsEnabled
|
||||
? '{{k8s.node.name}}'
|
||||
: '{{k8s_node_name}}';
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -1540,23 +1586,48 @@ export const getHostQueryPayload = (
|
||||
hostName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
dotMetricsEnabled: boolean,
|
||||
): GetQueryResultsProps[] => {
|
||||
const hostNameKey = 'host.name';
|
||||
const cpuTimeKey = 'system.cpu.time';
|
||||
const memUsageKey = 'system.memory.usage';
|
||||
const load1mKey = 'system.cpu.load_average.1m';
|
||||
const load5mKey = 'system.cpu.load_average.5m';
|
||||
const load15mKey = 'system.cpu.load_average.15m';
|
||||
const netIoKey = 'system.network.io';
|
||||
const netPktsKey = 'system.network.packets';
|
||||
const netErrKey = 'system.network.errors';
|
||||
const netDropKey = 'system.network.dropped';
|
||||
const netConnKey = 'system.network.connections';
|
||||
const diskIoKey = 'system.disk.io';
|
||||
const diskOpTimeKey = 'system.disk.operation_time';
|
||||
const diskOpsKey = 'system.disk.operations';
|
||||
const diskPendingKey = 'system.disk.pending_operations';
|
||||
const fsUsageKey = 'system.filesystem.usage';
|
||||
const hostNameKey = dotMetricsEnabled ? 'host.name' : 'host_name';
|
||||
const cpuTimeKey = dotMetricsEnabled ? 'system.cpu.time' : 'system_cpu_time';
|
||||
const memUsageKey = dotMetricsEnabled
|
||||
? 'system.memory.usage'
|
||||
: 'system_memory_usage';
|
||||
const load1mKey = dotMetricsEnabled
|
||||
? 'system.cpu.load_average.1m'
|
||||
: 'system_cpu_load_average_1m';
|
||||
const load5mKey = dotMetricsEnabled
|
||||
? 'system.cpu.load_average.5m'
|
||||
: 'system_cpu_load_average_5m';
|
||||
const load15mKey = dotMetricsEnabled
|
||||
? 'system.cpu.load_average.15m'
|
||||
: 'system_cpu_load_average_15m';
|
||||
const netIoKey = dotMetricsEnabled ? 'system.network.io' : 'system_network_io';
|
||||
const netPktsKey = dotMetricsEnabled
|
||||
? 'system.network.packets'
|
||||
: 'system_network_packets';
|
||||
const netErrKey = dotMetricsEnabled
|
||||
? 'system.network.errors'
|
||||
: 'system_network_errors';
|
||||
const netDropKey = dotMetricsEnabled
|
||||
? 'system.network.dropped'
|
||||
: 'system_network_dropped';
|
||||
const netConnKey = dotMetricsEnabled
|
||||
? 'system.network.connections'
|
||||
: 'system_network_connections';
|
||||
const diskIoKey = dotMetricsEnabled ? 'system.disk.io' : 'system_disk_io';
|
||||
const diskOpTimeKey = dotMetricsEnabled
|
||||
? 'system.disk.operation_time'
|
||||
: 'system_disk_operation_time';
|
||||
const diskOpsKey = dotMetricsEnabled
|
||||
? 'system.disk.operations'
|
||||
: 'system_disk_operations';
|
||||
const diskPendingKey = dotMetricsEnabled
|
||||
? 'system.disk.pending_operations'
|
||||
: 'system_disk_pending_operations';
|
||||
const fsUsageKey = dotMetricsEnabled
|
||||
? 'system.filesystem.usage'
|
||||
: 'system_filesystem_usage';
|
||||
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ export const databaseCallsRPS = ({
|
||||
servicename,
|
||||
legend,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}: DatabaseCallsRPSProps): QueryBuilderData => {
|
||||
const autocompleteData: BaseAutocompleteData[] = [
|
||||
{
|
||||
@@ -32,7 +33,7 @@ export const databaseCallsRPS = ({
|
||||
const groupBy: BaseAutocompleteData[] = [
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.DbSystem,
|
||||
key: dotMetricsEnabled ? WidgetKeys.Db_system : WidgetKeys.Db_system_norm,
|
||||
type: 'tag',
|
||||
},
|
||||
];
|
||||
@@ -41,7 +42,9 @@ export const databaseCallsRPS = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
@@ -72,6 +75,7 @@ export const databaseCallsRPS = ({
|
||||
export const databaseCallsAvgDuration = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}: DatabaseCallProps): QueryBuilderData => {
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozDbLatencySum,
|
||||
@@ -88,7 +92,9 @@ export const databaseCallsAvgDuration = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@ export const externalCallErrorPercent = ({
|
||||
servicename,
|
||||
legend,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}: ExternalCallDurationByAddressProps): QueryBuilderData => {
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozExternalCallLatencyCount,
|
||||
@@ -48,7 +49,9 @@ export const externalCallErrorPercent = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
@@ -58,7 +61,7 @@ export const externalCallErrorPercent = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.StatusCode,
|
||||
key: dotMetricsEnabled ? WidgetKeys.StatusCode : WidgetKeys.StatusCodeNorm,
|
||||
dataType: DataTypes.Int64,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
@@ -71,7 +74,9 @@ export const externalCallErrorPercent = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
@@ -115,6 +120,7 @@ export const externalCallErrorPercent = ({
|
||||
export const externalCallDuration = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}: ExternalCallProps): QueryBuilderData => {
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
dataType: DataTypes.Float64,
|
||||
@@ -135,7 +141,9 @@ export const externalCallDuration = ({
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -175,6 +183,7 @@ export const externalCallRpsByAddress = ({
|
||||
servicename,
|
||||
legend,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}: ExternalCallDurationByAddressProps): QueryBuilderData => {
|
||||
const autocompleteData: BaseAutocompleteData[] = [
|
||||
{
|
||||
@@ -189,7 +198,9 @@ export const externalCallRpsByAddress = ({
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -220,6 +231,7 @@ export const externalCallDurationByAddress = ({
|
||||
servicename,
|
||||
legend,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}: ExternalCallDurationByAddressProps): QueryBuilderData => {
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
dataType: DataTypes.Float64,
|
||||
@@ -239,7 +251,9 @@ export const externalCallDurationByAddress = ({
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
|
||||
@@ -37,10 +37,15 @@ export const latency = ({
|
||||
tagFilterItems,
|
||||
isSpanMetricEnable = false,
|
||||
topLevelOperationsRoute,
|
||||
dotMetricsEnabled,
|
||||
}: LatencyProps): QueryBuilderData => {
|
||||
const signozLatencyBucketMetrics = WidgetKeys.SignozLatencyBucket;
|
||||
const signozLatencyBucketMetrics = dotMetricsEnabled
|
||||
? WidgetKeys.Signoz_latency_bucket
|
||||
: WidgetKeys.Signoz_latency_bucket_norm;
|
||||
|
||||
const signozMetricsServiceName = WidgetKeys.OTelServiceName;
|
||||
const signozMetricsServiceName = dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm;
|
||||
const newAutoCompleteData: BaseAutocompleteData = {
|
||||
key: isSpanMetricEnable
|
||||
? signozLatencyBucketMetrics
|
||||
@@ -282,21 +287,28 @@ export const apDexMetricsQueryBuilderQueries = ({
|
||||
threashold,
|
||||
delta,
|
||||
metricsBuckets,
|
||||
dotMetricsEnabled,
|
||||
}: ApDexMetricsQueryBuilderQueriesProps): QueryBuilderData => {
|
||||
const autoCompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozLatencyCount,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.SignozLatencyCount
|
||||
: WidgetKeys.SignozLatencyCountNorm,
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
};
|
||||
|
||||
const autoCompleteDataB: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozLatencyBucket,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Signoz_latency_bucket
|
||||
: WidgetKeys.Signoz_latency_bucket_norm,
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
};
|
||||
|
||||
const autoCompleteDataC: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozLatencyBucket,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Signoz_latency_bucket
|
||||
: WidgetKeys.Signoz_latency_bucket_norm,
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
};
|
||||
@@ -305,7 +317,9 @@ export const apDexMetricsQueryBuilderQueries = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
@@ -329,7 +343,7 @@ export const apDexMetricsQueryBuilderQueries = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.StatusCode,
|
||||
key: dotMetricsEnabled ? WidgetKeys.StatusCode : WidgetKeys.StatusCodeNorm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
@@ -349,7 +363,9 @@ export const apDexMetricsQueryBuilderQueries = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
@@ -383,7 +399,7 @@ export const apDexMetricsQueryBuilderQueries = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.StatusCode,
|
||||
key: dotMetricsEnabled ? WidgetKeys.StatusCode : WidgetKeys.StatusCodeNorm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
@@ -393,7 +409,9 @@ export const apDexMetricsQueryBuilderQueries = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
@@ -456,10 +474,13 @@ export const operationPerSec = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations,
|
||||
dotMetricsEnabled,
|
||||
}: OperationPerSecProps): QueryBuilderData => {
|
||||
const autocompleteData: BaseAutocompleteData[] = [
|
||||
{
|
||||
key: WidgetKeys.SignozLatencyCount,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.SignozLatencyCount
|
||||
: WidgetKeys.SignozLatencyCountNorm,
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
},
|
||||
@@ -470,7 +491,9 @@ export const operationPerSec = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
@@ -511,6 +534,7 @@ export const errorPercentage = ({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations,
|
||||
dotMetricsEnabled,
|
||||
}: OperationPerSecProps): QueryBuilderData => {
|
||||
const autocompleteDataA: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozCallsTotal,
|
||||
@@ -529,7 +553,9 @@ export const errorPercentage = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
@@ -549,7 +575,7 @@ export const errorPercentage = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.StatusCode,
|
||||
key: dotMetricsEnabled ? WidgetKeys.StatusCode : WidgetKeys.StatusCodeNorm,
|
||||
dataType: DataTypes.Int64,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
@@ -563,7 +589,9 @@ export const errorPercentage = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
|
||||
@@ -21,9 +21,12 @@ import { getQueryBuilderQuerieswithFormula } from './MetricsPageQueriesFactory';
|
||||
|
||||
export const topOperationQueries = ({
|
||||
servicename,
|
||||
dotMetricsEnabled,
|
||||
}: TopOperationQueryFactoryProps): QueryBuilderData => {
|
||||
const latencyAutoCompleteData: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozLatencyBucket,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Signoz_latency_bucket
|
||||
: WidgetKeys.Signoz_latency_bucket_norm,
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
};
|
||||
@@ -35,7 +38,9 @@ export const topOperationQueries = ({
|
||||
};
|
||||
|
||||
const numOfCallAutoCompleteData: BaseAutocompleteData = {
|
||||
key: WidgetKeys.SignozLatencyCount,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.SignozLatencyCount
|
||||
: WidgetKeys.SignozLatencyCountNorm,
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
};
|
||||
@@ -44,7 +49,9 @@ export const topOperationQueries = ({
|
||||
{
|
||||
id: '',
|
||||
key: {
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
dataType: DataTypes.String,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
@@ -58,7 +65,9 @@ export const topOperationQueries = ({
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -68,7 +77,7 @@ export const topOperationQueries = ({
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.Int64,
|
||||
key: WidgetKeys.StatusCode,
|
||||
key: dotMetricsEnabled ? WidgetKeys.StatusCode : WidgetKeys.StatusCodeNorm,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
|
||||
@@ -28,6 +28,8 @@ import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import {
|
||||
GraphTitle,
|
||||
MENU_ITEMS,
|
||||
@@ -87,7 +89,12 @@ function DBCall(): JSX.Element {
|
||||
[queries],
|
||||
);
|
||||
|
||||
const legend = '{{db.system}}';
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const legend = dotMetricsEnabled ? '{{db.system}}' : '{{db_system}}';
|
||||
|
||||
const databaseCallsRPSWidget = useMemo(
|
||||
() =>
|
||||
@@ -99,6 +106,7 @@ function DBCall(): JSX.Element {
|
||||
servicename,
|
||||
legend,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -109,7 +117,7 @@ function DBCall(): JSX.Element {
|
||||
id: SERVICE_CHART_ID.dbCallsRPS,
|
||||
fillSpans: false,
|
||||
}),
|
||||
[servicename, tagFilterItems, legend],
|
||||
[servicename, tagFilterItems, dotMetricsEnabled, legend],
|
||||
);
|
||||
const databaseCallsAverageDurationWidget = useMemo(
|
||||
() =>
|
||||
@@ -120,6 +128,7 @@ function DBCall(): JSX.Element {
|
||||
builder: databaseCallsAvgDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -130,7 +139,7 @@ function DBCall(): JSX.Element {
|
||||
id: GraphTitle.DATABASE_CALLS_AVG_DURATION,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
[servicename, tagFilterItems, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const stepInterval = useMemo(
|
||||
@@ -148,7 +157,7 @@ function DBCall(): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(),
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('APM: Service detail page visited', {
|
||||
|
||||
@@ -30,6 +30,8 @@ import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import {
|
||||
GraphTitle,
|
||||
legend,
|
||||
@@ -82,6 +84,10 @@ function External(): JSX.Element {
|
||||
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
|
||||
[queries],
|
||||
);
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const externalCallErrorWidget = useMemo(
|
||||
() =>
|
||||
@@ -93,6 +99,7 @@ function External(): JSX.Element {
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -102,7 +109,7 @@ function External(): JSX.Element {
|
||||
yAxisUnit: '%',
|
||||
id: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
[servicename, tagFilterItems, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const selectedTraceTags = useMemo(
|
||||
@@ -119,6 +126,7 @@ function External(): JSX.Element {
|
||||
builder: externalCallDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -129,7 +137,7 @@ function External(): JSX.Element {
|
||||
id: GraphTitle.EXTERNAL_CALL_DURATION,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
[servicename, tagFilterItems, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const errorApmToTraceQuery = useGetAPMToTracesQueries({
|
||||
@@ -163,7 +171,7 @@ function External(): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(),
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('APM: Service detail page visited', {
|
||||
@@ -186,6 +194,7 @@ function External(): JSX.Element {
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -196,7 +205,7 @@ function External(): JSX.Element {
|
||||
id: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
[servicename, tagFilterItems, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const externalCallDurationAddressWidget = useMemo(
|
||||
@@ -209,6 +218,7 @@ function External(): JSX.Element {
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
tagFilterItems,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -219,7 +229,7 @@ function External(): JSX.Element {
|
||||
id: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
[servicename, tagFilterItems, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const apmToTraceQuery = useGetAPMToTracesQueries({
|
||||
|
||||
@@ -93,12 +93,15 @@ function Application(): JSX.Element {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[handleSetTimeStamp],
|
||||
);
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const logEventCalledRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(),
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
)?.tagValue;
|
||||
|
||||
logEvent('APM: Service detail page visited', {
|
||||
@@ -156,6 +159,7 @@ function Application(): JSX.Element {
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperationsRoute,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -165,7 +169,7 @@ function Application(): JSX.Element {
|
||||
yAxisUnit: 'ops',
|
||||
id: SERVICE_CHART_ID.rps,
|
||||
}),
|
||||
[servicename, tagFilterItems, topLevelOperationsRoute],
|
||||
[servicename, tagFilterItems, topLevelOperationsRoute, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const errorPercentageWidget = useMemo(
|
||||
@@ -178,6 +182,7 @@ function Application(): JSX.Element {
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperationsRoute,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -188,7 +193,7 @@ function Application(): JSX.Element {
|
||||
id: SERVICE_CHART_ID.errorPercentage,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems, topLevelOperationsRoute],
|
||||
[servicename, tagFilterItems, topLevelOperationsRoute, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const stepInterval = useMemo(
|
||||
|
||||
@@ -22,6 +22,8 @@ import { apDexMetricsQueryBuilderQueries } from 'container/MetricsApplication/Me
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../../../../constants/features';
|
||||
import { useAppContext } from '../../../../../providers/App/App';
|
||||
import { IServiceName } from '../../types';
|
||||
import { ApDexMetricsProps } from './types';
|
||||
|
||||
@@ -36,6 +38,10 @@ function ApDexMetrics({
|
||||
}: ApDexMetricsProps): JSX.Element {
|
||||
const { servicename: encodedServiceName } = useParams<IServiceName>();
|
||||
const servicename = decodeURIComponent(encodedServiceName);
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
const apDexMetricsWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
@@ -49,6 +55,7 @@ function ApDexMetrics({
|
||||
threashold: thresholdValue || 0,
|
||||
delta: delta || false,
|
||||
metricsBuckets: metricsBuckets || [],
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -74,6 +81,7 @@ function ApDexMetrics({
|
||||
tagFilterItems,
|
||||
thresholdValue,
|
||||
topLevelOperationsRoute,
|
||||
dotMetricsEnabled,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import Spinner from 'components/Spinner';
|
||||
import { useGetMetricMeta } from 'hooks/apDex/useGetMetricMeta';
|
||||
import useErrorNotification from 'hooks/useErrorNotification';
|
||||
|
||||
import { FeatureKeys } from '../../../../../constants/features';
|
||||
import { useAppContext } from '../../../../../providers/App/App';
|
||||
import { WidgetKeys } from '../../../constant';
|
||||
import { IServiceName } from '../../types';
|
||||
import ApDexMetrics from './ApDexMetrics';
|
||||
@@ -18,8 +20,17 @@ function ApDexMetricsApplication({
|
||||
const { servicename: encodedServiceName } = useParams<IServiceName>();
|
||||
const servicename = decodeURIComponent(encodedServiceName);
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const signozLatencyBucketMetrics = dotMetricsEnabled
|
||||
? WidgetKeys.Signoz_latency_bucket
|
||||
: WidgetKeys.Signoz_latency_bucket_norm;
|
||||
|
||||
const { data, isLoading, error } = useGetMetricMeta(
|
||||
WidgetKeys.SignozLatencyBucket,
|
||||
signozLatencyBucketMetrics,
|
||||
servicename,
|
||||
);
|
||||
useErrorNotification(error);
|
||||
|
||||
@@ -56,6 +56,10 @@ function ServiceOverview({
|
||||
[isSpanMetricEnable, queries],
|
||||
);
|
||||
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const latencyWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
@@ -67,6 +71,7 @@ function ServiceOverview({
|
||||
tagFilterItems,
|
||||
isSpanMetricEnable,
|
||||
topLevelOperationsRoute,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
@@ -76,7 +81,13 @@ function ServiceOverview({
|
||||
yAxisUnit: 'ns',
|
||||
id: SERVICE_CHART_ID.latency,
|
||||
}),
|
||||
[isSpanMetricEnable, servicename, tagFilterItems, topLevelOperationsRoute],
|
||||
[
|
||||
isSpanMetricEnable,
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperationsRoute,
|
||||
dotMetricsEnabled,
|
||||
],
|
||||
);
|
||||
|
||||
const isQueryEnabled =
|
||||
|
||||
@@ -19,6 +19,8 @@ import { EQueryType } from 'types/common/dashboard';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../../../constants/features';
|
||||
import { useAppContext } from '../../../../providers/App/App';
|
||||
import { IServiceName } from '../types';
|
||||
import { title } from './config';
|
||||
import ColumnWithLink from './TableRenderer/ColumnWithLink';
|
||||
@@ -42,6 +44,11 @@ function TopOperationMetrics(): JSX.Element {
|
||||
convertRawQueriesToTraceSelectedTags(queries) || [],
|
||||
);
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const keyOperationWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
@@ -50,13 +57,14 @@ function TopOperationMetrics(): JSX.Element {
|
||||
promql: [],
|
||||
builder: topOperationQueries({
|
||||
servicename,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
panelTypes: PANEL_TYPES.TABLE,
|
||||
}),
|
||||
[servicename],
|
||||
[servicename, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const updatedQuery = updateStepInterval(keyOperationWidget.query);
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface IServiceName {
|
||||
|
||||
export interface TopOperationQueryFactoryProps {
|
||||
servicename: IServiceName['servicename'];
|
||||
dotMetricsEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface ExternalCallDurationByAddressProps extends ExternalCallProps {
|
||||
@@ -19,6 +20,7 @@ export interface ExternalCallDurationByAddressProps extends ExternalCallProps {
|
||||
export interface ExternalCallProps {
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
dotMetricsEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface BuilderQueriesProps {
|
||||
@@ -50,6 +52,7 @@ export interface OperationPerSecProps {
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
topLevelOperations: string[];
|
||||
dotMetricsEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface LatencyProps {
|
||||
@@ -57,6 +60,7 @@ export interface LatencyProps {
|
||||
tagFilterItems: TagFilterItem[];
|
||||
isSpanMetricEnable?: boolean;
|
||||
topLevelOperationsRoute: string[];
|
||||
dotMetricsEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface ApDexProps {
|
||||
@@ -74,4 +78,5 @@ export interface TableRendererProps {
|
||||
export interface ApDexMetricsQueryBuilderQueriesProps extends ApDexProps {
|
||||
delta: boolean;
|
||||
metricsBuckets: number[];
|
||||
dotMetricsEnabled: boolean;
|
||||
}
|
||||
|
||||
@@ -85,11 +85,14 @@ export enum WidgetKeys {
|
||||
HasError = 'hasError',
|
||||
Address = 'address',
|
||||
DurationNano = 'durationNano',
|
||||
StatusCodeNorm = 'status_code',
|
||||
StatusCode = 'status.code',
|
||||
Operation = 'operation',
|
||||
OperationName = 'operationName',
|
||||
OTelServiceName = 'service.name',
|
||||
Service_name_norm = 'service_name',
|
||||
Service_name = 'service.name',
|
||||
ServiceName = 'serviceName',
|
||||
SignozLatencyCountNorm = 'signoz_latency_count',
|
||||
SignozLatencyCount = 'signoz_latency.count',
|
||||
SignozDBLatencyCount = 'signoz_db_latency_count',
|
||||
DatabaseCallCount = 'signoz_database_call_count',
|
||||
@@ -98,8 +101,10 @@ export enum WidgetKeys {
|
||||
SignozCallsTotal = 'signoz_calls_total',
|
||||
SignozExternalCallLatencyCount = 'signoz_external_call_latency_count',
|
||||
SignozExternalCallLatencySum = 'signoz_external_call_latency_sum',
|
||||
SignozLatencyBucket = 'signoz_latency.bucket',
|
||||
DbSystem = 'db.system',
|
||||
Signoz_latency_bucket_norm = 'signoz_latency_bucket',
|
||||
Signoz_latency_bucket = 'signoz_latency.bucket',
|
||||
Db_system = 'db.system',
|
||||
Db_system_norm = 'db_system',
|
||||
}
|
||||
|
||||
export const topOperationMetricsDownloadOptions: DownloadOptions = {
|
||||
|
||||
@@ -32,4 +32,5 @@ export interface DatabaseCallsRPSProps extends DatabaseCallProps {
|
||||
export interface DatabaseCallProps {
|
||||
servicename: IServiceName['servicename'];
|
||||
tagFilterItems: TagFilterItem[];
|
||||
dotMetricsEnabled: boolean;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ import { getUserOperatingSystem, UserOperatingSystem } from 'utils/getUserOS';
|
||||
import { useSelectPopupContainer } from 'utils/selectPopupContainer';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../../../constants/features';
|
||||
import { useAppContext } from '../../../../providers/App/App';
|
||||
import { selectStyle } from './config';
|
||||
import { PLACEHOLDER } from './constant';
|
||||
import ExampleQueriesRendererForLogs from './ExampleQueriesRendererForLogs';
|
||||
@@ -102,6 +104,11 @@ function QueryBuilderSearch({
|
||||
|
||||
const [isEditingTag, setIsEditingTag] = useState(false);
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const {
|
||||
updateTag,
|
||||
handleClearTag,
|
||||
@@ -121,6 +128,7 @@ function QueryBuilderSearch({
|
||||
exampleQueries,
|
||||
} = useAutoComplete(
|
||||
query,
|
||||
dotMetricsEnabled,
|
||||
whereClauseConfig,
|
||||
isLogsExplorerPage,
|
||||
isInfraMonitoring,
|
||||
@@ -138,6 +146,7 @@ function QueryBuilderSearch({
|
||||
const { sourceKeys, handleRemoveSourceKey } = useFetchKeysAndValues(
|
||||
searchValue,
|
||||
query,
|
||||
dotMetricsEnabled,
|
||||
searchKey,
|
||||
isLogsExplorerPage,
|
||||
isInfraMonitoring,
|
||||
|
||||
@@ -14,6 +14,8 @@ import { SelectOption } from 'types/common/select';
|
||||
import { popupContainer } from 'utils/selectPopupContainer';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { FeatureKeys } from '../../constants/features';
|
||||
import { useAppContext } from '../../providers/App/App';
|
||||
import QueryChip from './components/QueryChip';
|
||||
import { QueryChipItem, SearchContainer } from './styles';
|
||||
|
||||
@@ -40,7 +42,12 @@ function ResourceAttributesFilter({
|
||||
SelectOption<string, string>[]
|
||||
>([]);
|
||||
|
||||
const resourceDeploymentKey = getResourceDeploymentKeys();
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const resourceDeploymentKey = getResourceDeploymentKeys(dotMetricsEnabled);
|
||||
|
||||
const [selectedEnvironments, setSelectedEnvironments] = useState<string[]>([]);
|
||||
|
||||
@@ -66,14 +73,14 @@ function ResourceAttributesFilter({
|
||||
}, [queries, resourceDeploymentKey]);
|
||||
|
||||
useEffect(() => {
|
||||
getEnvironmentTagKeys().then((tagKeys) => {
|
||||
getEnvironmentTagKeys(dotMetricsEnabled).then((tagKeys) => {
|
||||
if (tagKeys && Array.isArray(tagKeys) && tagKeys.length > 0) {
|
||||
getEnvironmentTagValues().then((tagValues) => {
|
||||
getEnvironmentTagValues(dotMetricsEnabled).then((tagValues) => {
|
||||
setEnvironments(tagValues);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
}, [dotMetricsEnabled]);
|
||||
|
||||
return (
|
||||
<div className="resourceAttributesFilter-container">
|
||||
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
getResourceDeploymentKeys,
|
||||
} from 'hooks/useResourceAttribute/utils';
|
||||
|
||||
import { FeatureKeys } from '../../../../constants/features';
|
||||
import { useAppContext } from '../../../../providers/App/App';
|
||||
import { QueryChipContainer, QueryChipItem } from '../../styles';
|
||||
import { IQueryChipProps } from './types';
|
||||
|
||||
@@ -11,7 +13,13 @@ function QueryChip({ queryData, onClose }: IQueryChipProps): JSX.Element {
|
||||
onClose(queryData.id);
|
||||
};
|
||||
|
||||
const isClosable = queryData.tagKey !== getResourceDeploymentKeys();
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const isClosable =
|
||||
queryData.tagKey !== getResourceDeploymentKeys(dotMetricsEnabled);
|
||||
|
||||
return (
|
||||
<QueryChipContainer>
|
||||
|
||||
@@ -4,6 +4,8 @@ import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { ServiceMetricsProps } from '../types';
|
||||
import { getQueryRangeRequestData } from '../utils';
|
||||
import ServiceMetricTable from './ServiceMetricTable';
|
||||
@@ -16,13 +18,19 @@ function ServiceMetricsApplication({
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const queryRangeRequestData = useMemo(
|
||||
() =>
|
||||
getQueryRangeRequestData({
|
||||
topLevelOperations,
|
||||
globalSelectedInterval,
|
||||
dotMetricsEnabled,
|
||||
}),
|
||||
[globalSelectedInterval, topLevelOperations],
|
||||
[globalSelectedInterval, topLevelOperations, dotMetricsEnabled],
|
||||
);
|
||||
return (
|
||||
<ServiceMetricTable
|
||||
|
||||
@@ -19,10 +19,13 @@ import {
|
||||
|
||||
export const serviceMetricsQuery = (
|
||||
topLevelOperation: [keyof ServiceDataProps, string[]],
|
||||
dotMetricsEnabled: boolean,
|
||||
): QueryBuilderData => {
|
||||
const p99AutoCompleteData: BaseAutocompleteData = {
|
||||
dataType: DataTypes.Float64,
|
||||
key: WidgetKeys.SignozLatencyBucket,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Signoz_latency_bucket
|
||||
: WidgetKeys.Signoz_latency_bucket_norm,
|
||||
type: '',
|
||||
};
|
||||
|
||||
@@ -50,7 +53,9 @@ export const serviceMetricsQuery = (
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -73,7 +78,9 @@ export const serviceMetricsQuery = (
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -83,7 +90,7 @@ export const serviceMetricsQuery = (
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.Int64,
|
||||
key: WidgetKeys.StatusCode,
|
||||
key: dotMetricsEnabled ? WidgetKeys.StatusCode : WidgetKeys.StatusCodeNorm,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -106,7 +113,9 @@ export const serviceMetricsQuery = (
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -129,7 +138,9 @@ export const serviceMetricsQuery = (
|
||||
id: '',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Resource,
|
||||
},
|
||||
op: OPERATORS.IN,
|
||||
@@ -182,7 +193,9 @@ export const serviceMetricsQuery = (
|
||||
const groupBy: BaseAutocompleteData[] = [
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
key: WidgetKeys.OTelServiceName,
|
||||
key: dotMetricsEnabled
|
||||
? WidgetKeys.Service_name
|
||||
: WidgetKeys.Service_name_norm,
|
||||
type: MetricsType.Tag,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -17,6 +17,8 @@ import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { Tags } from 'types/reducer/trace';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import SkipOnBoardingModal from '../SkipOnBoardModal';
|
||||
import ServiceTraceTable from './ServiceTracesTable';
|
||||
|
||||
@@ -38,6 +40,11 @@ function ServiceTraces(): JSX.Element {
|
||||
selectedTags,
|
||||
});
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
useErrorNotification(error);
|
||||
|
||||
const services = data || [];
|
||||
@@ -55,7 +62,7 @@ function ServiceTraces(): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (!logEventCalledRef.current && !isUndefined(data)) {
|
||||
const selectedEnvironments = queries.find(
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(),
|
||||
(val) => val.tagKey === getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
)?.tagValue;
|
||||
|
||||
const rps = data.reduce((total, service) => total + service.callRate, 0);
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface ServiceMetricsTableProps {
|
||||
export interface GetQueryRangeRequestDataProps {
|
||||
topLevelOperations: [keyof ServiceDataProps, string[]][];
|
||||
globalSelectedInterval: Time | CustomTimeType;
|
||||
dotMetricsEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface GetServiceListFromQueryProps {
|
||||
|
||||
@@ -26,6 +26,7 @@ export function getSeriesValue(
|
||||
export const getQueryRangeRequestData = ({
|
||||
topLevelOperations,
|
||||
globalSelectedInterval,
|
||||
dotMetricsEnabled,
|
||||
}: GetQueryRangeRequestDataProps): GetQueryResultsProps[] => {
|
||||
const requestData: GetQueryResultsProps[] = [];
|
||||
topLevelOperations.forEach((operation) => {
|
||||
@@ -33,7 +34,7 @@ export const getQueryRangeRequestData = ({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: serviceMetricsQuery(operation),
|
||||
builder: serviceMetricsQuery(operation, dotMetricsEnabled),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
|
||||
@@ -27,6 +27,7 @@ export type WhereClauseConfig = {
|
||||
|
||||
export const useAutoComplete = (
|
||||
query: IBuilderQuery,
|
||||
dotMetricsEnabled: boolean,
|
||||
whereClauseConfig?: WhereClauseConfig,
|
||||
shouldUseSuggestions?: boolean,
|
||||
isInfraMonitoring?: boolean,
|
||||
@@ -39,6 +40,7 @@ export const useAutoComplete = (
|
||||
const { keys, results, isFetching, exampleQueries } = useFetchKeysAndValues(
|
||||
searchValue,
|
||||
query,
|
||||
dotMetricsEnabled,
|
||||
searchKey,
|
||||
shouldUseSuggestions,
|
||||
isInfraMonitoring,
|
||||
|
||||
@@ -48,6 +48,7 @@ type IuseFetchKeysAndValues = {
|
||||
export const useFetchKeysAndValues = (
|
||||
searchValue: string,
|
||||
query: IBuilderQuery,
|
||||
dotMetricsEnabled: boolean,
|
||||
searchKey: string,
|
||||
shouldUseSuggestions?: boolean,
|
||||
isInfraMonitoring?: boolean,
|
||||
|
||||
@@ -6,6 +6,8 @@ import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { encode } from 'js-base64';
|
||||
|
||||
import { FeatureKeys } from '../../constants/features';
|
||||
import { useAppContext } from '../../providers/App/App';
|
||||
import { whilelistedKeys } from './config';
|
||||
import { ResourceContext } from './context';
|
||||
import {
|
||||
@@ -56,6 +58,11 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
}
|
||||
};
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const dispatchQueries = useCallback(
|
||||
(queries: IResourceAttribute[]): void => {
|
||||
urlQuery.set(
|
||||
@@ -71,7 +78,7 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
|
||||
const loadTagKeys = (): void => {
|
||||
handleLoading(true);
|
||||
GetTagKeys()
|
||||
GetTagKeys(dotMetricsEnabled)
|
||||
.then((tagKeys) => {
|
||||
const options = mappingWithRoutesAndKeys(pathname, tagKeys);
|
||||
setOptionsData({ options, mode: undefined });
|
||||
@@ -154,15 +161,15 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
|
||||
setSelectedQueries([...value]);
|
||||
},
|
||||
[optionsData.mode, step, staging, pathname],
|
||||
[optionsData.mode, step, staging, dotMetricsEnabled, pathname],
|
||||
);
|
||||
|
||||
const handleEnvironmentChange = useCallback(
|
||||
(environments: string[]): void => {
|
||||
const staging = [getResourceDeploymentKeys(), 'IN'];
|
||||
const staging = [getResourceDeploymentKeys(dotMetricsEnabled), 'IN'];
|
||||
|
||||
const queriesCopy = queries.filter(
|
||||
(query) => query.tagKey !== getResourceDeploymentKeys(),
|
||||
(query) => query.tagKey !== getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
);
|
||||
|
||||
if (environments && Array.isArray(environments) && environments.length > 0) {
|
||||
@@ -177,7 +184,7 @@ function ResourceProvider({ children }: Props): JSX.Element {
|
||||
|
||||
setStep('Idle');
|
||||
},
|
||||
[dispatchQueries, queries],
|
||||
[dispatchQueries, dotMetricsEnabled, queries],
|
||||
);
|
||||
|
||||
const handleClose = useCallback(
|
||||
|
||||
@@ -2,9 +2,13 @@ import { ReactNode } from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { createMemoryHistory, MemoryHistory } from 'history';
|
||||
import { encode } from 'js-base64';
|
||||
import { AppContext } from 'providers/App/App';
|
||||
import { IAppContext } from 'providers/App/types';
|
||||
import { getAppContextMock } from 'tests/test-utils';
|
||||
|
||||
import ResourceProvider from '../ResourceProvider';
|
||||
import useResourceAttribute from '../useResourceAttribute';
|
||||
@@ -51,8 +55,10 @@ const mockTagValues = getResourceAttributesTagValues as jest.MockedFunction<
|
||||
|
||||
function createWrapper({
|
||||
routerHistory,
|
||||
appContextOverrides,
|
||||
}: {
|
||||
routerHistory: MemoryHistory;
|
||||
appContextOverrides?: Partial<IAppContext>;
|
||||
}): ({ children }: { children: ReactNode }) => JSX.Element {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
@@ -60,9 +66,13 @@ function createWrapper({
|
||||
return function Wrapper({ children }: { children: ReactNode }): JSX.Element {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Router history={routerHistory}>
|
||||
<ResourceProvider>{children}</ResourceProvider>
|
||||
</Router>
|
||||
<AppContext.Provider
|
||||
value={getAppContextMock('ADMIN', appContextOverrides)}
|
||||
>
|
||||
<Router history={routerHistory}>
|
||||
<ResourceProvider>{children}</ResourceProvider>
|
||||
</Router>
|
||||
</AppContext.Provider>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
@@ -401,7 +411,7 @@ describe('ResourceProvider', () => {
|
||||
});
|
||||
|
||||
describe('handleEnvironmentChange', () => {
|
||||
it('adds a dotted environment query when envs are provided', async () => {
|
||||
it('adds an environment query when envs are provided', async () => {
|
||||
const routerHistory = createMemoryHistory({ initialEntries: ['/'] });
|
||||
const { result } = renderHook(() => useResourceAttribute(), {
|
||||
wrapper: createWrapper({ routerHistory }),
|
||||
@@ -414,7 +424,7 @@ describe('ResourceProvider', () => {
|
||||
await waitFor(() => {
|
||||
expect(result.current.queries).toHaveLength(1);
|
||||
expect(result.current.queries[0]).toMatchObject({
|
||||
tagKey: 'resource_deployment.environment',
|
||||
tagKey: 'resource_deployment_environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
});
|
||||
@@ -425,7 +435,7 @@ describe('ResourceProvider', () => {
|
||||
const seeded = [
|
||||
{
|
||||
id: 'env',
|
||||
tagKey: 'resource_deployment.environment',
|
||||
tagKey: 'resource_deployment_environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
},
|
||||
@@ -449,7 +459,7 @@ describe('ResourceProvider', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
const tagKeys = result.current.queries.map((q) => q.tagKey);
|
||||
expect(tagKeys).not.toContain('resource_deployment.environment');
|
||||
expect(tagKeys).not.toContain('resource_deployment_environment');
|
||||
expect(tagKeys).toContain('resource_service_name');
|
||||
});
|
||||
});
|
||||
@@ -458,7 +468,7 @@ describe('ResourceProvider', () => {
|
||||
const seeded = [
|
||||
{
|
||||
id: 'env',
|
||||
tagKey: 'resource_deployment.environment',
|
||||
tagKey: 'resource_deployment_environment',
|
||||
operator: 'IN',
|
||||
tagValue: ['production'],
|
||||
},
|
||||
@@ -476,13 +486,43 @@ describe('ResourceProvider', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
const envQueries = result.current.queries.filter(
|
||||
(q) => q.tagKey === 'resource_deployment.environment',
|
||||
(q) => q.tagKey === 'resource_deployment_environment',
|
||||
);
|
||||
expect(envQueries).toHaveLength(1);
|
||||
expect(envQueries[0].tagValue).toStrictEqual(['staging']);
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the dotted deployment env key when DOT_METRICS_ENABLED is active', async () => {
|
||||
const routerHistory = createMemoryHistory({ initialEntries: ['/'] });
|
||||
const { result } = renderHook(() => useResourceAttribute(), {
|
||||
wrapper: createWrapper({
|
||||
routerHistory,
|
||||
appContextOverrides: {
|
||||
featureFlags: [
|
||||
{
|
||||
name: FeatureKeys.DOT_METRICS_ENABLED,
|
||||
active: true,
|
||||
usage: 0,
|
||||
usage_limit: -1,
|
||||
route: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.handleEnvironmentChange(['production']);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.queries[0].tagKey).toBe(
|
||||
'resource_deployment.environment',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves unrelated query params when dispatching', async () => {
|
||||
const routerHistory = createMemoryHistory({
|
||||
initialEntries: ['/?tab=overview'],
|
||||
|
||||
@@ -5,13 +5,13 @@ import { mappingWithRoutesAndKeys } from '../utils';
|
||||
|
||||
describe('useResourceAttribute config', () => {
|
||||
describe('whilelistedKeys', () => {
|
||||
it('should include underscore-notation keys', () => {
|
||||
it('should include underscore-notation keys (DOT_METRICS_ENABLED=false)', () => {
|
||||
expect(whilelistedKeys).toContain('resource_deployment_environment');
|
||||
expect(whilelistedKeys).toContain('resource_k8s_cluster_name');
|
||||
expect(whilelistedKeys).toContain('resource_k8s_cluster_namespace');
|
||||
});
|
||||
|
||||
it('should include dot-notation keys', () => {
|
||||
it('should include dot-notation keys (DOT_METRICS_ENABLED=true)', () => {
|
||||
expect(whilelistedKeys).toContain('resource_deployment.environment');
|
||||
expect(whilelistedKeys).toContain('resource_k8s.cluster.name');
|
||||
expect(whilelistedKeys).toContain('resource_k8s.cluster.namespace');
|
||||
|
||||
@@ -144,11 +144,19 @@ export const OperatorSchema: IOption[] = OperatorConversions.map(
|
||||
}),
|
||||
);
|
||||
|
||||
export const getResourceDeploymentKeys = (): string =>
|
||||
'resource_deployment.environment';
|
||||
export const getResourceDeploymentKeys = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): string => {
|
||||
if (dotMetricsEnabled) {
|
||||
return 'resource_deployment.environment';
|
||||
}
|
||||
return 'resource_deployment_environment';
|
||||
};
|
||||
|
||||
export const GetTagKeys = async (): Promise<IOption[]> => {
|
||||
const resourceDeploymentKey = getResourceDeploymentKeys();
|
||||
export const GetTagKeys = async (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Promise<IOption[]> => {
|
||||
const resourceDeploymentKey = getResourceDeploymentKeys(dotMetricsEnabled);
|
||||
const { payload } = await getResourceAttributesTagKeys({
|
||||
metricName: 'signoz_calls_total',
|
||||
match: 'resource_',
|
||||
@@ -168,10 +176,12 @@ export const GetTagKeys = async (): Promise<IOption[]> => {
|
||||
}));
|
||||
};
|
||||
|
||||
export const getEnvironmentTagKeys = async (): Promise<IOption[]> => {
|
||||
export const getEnvironmentTagKeys = async (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Promise<IOption[]> => {
|
||||
const { payload } = await getResourceAttributesTagKeys({
|
||||
metricName: 'signoz_calls_total',
|
||||
match: getResourceDeploymentKeys(),
|
||||
match: getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
});
|
||||
if (!payload || !payload?.data) {
|
||||
return [];
|
||||
@@ -184,9 +194,11 @@ export const getEnvironmentTagKeys = async (): Promise<IOption[]> => {
|
||||
}));
|
||||
};
|
||||
|
||||
export const getEnvironmentTagValues = async (): Promise<IOption[]> => {
|
||||
export const getEnvironmentTagValues = async (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Promise<IOption[]> => {
|
||||
const { payload } = await getResourceAttributesTagValues({
|
||||
tagKey: getResourceDeploymentKeys(),
|
||||
tagKey: getResourceDeploymentKeys(dotMetricsEnabled),
|
||||
metricName: 'signoz_calls_total',
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import { CardContainer } from 'container/GridCardLayout/styles';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { FeatureKeys } from '../../../../constants/features';
|
||||
import { useAppContext } from '../../../../providers/App/App';
|
||||
import MetricPageGridGraph from './MetricPageGraph';
|
||||
import {
|
||||
getAverageRequestLatencyWidgetData,
|
||||
@@ -71,15 +73,20 @@ function MetricColumnGraphs({
|
||||
}): JSX.Element {
|
||||
const { t } = useTranslation('messagingQueues');
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const metricsData = [
|
||||
{
|
||||
title: t('metricGraphCategory.brokerMetrics.title'),
|
||||
description: t('metricGraphCategory.brokerMetrics.description'),
|
||||
graphCount: [
|
||||
getBrokerCountWidgetData(),
|
||||
getRequestTimesWidgetData(),
|
||||
getProducerFetchRequestPurgatoryWidgetData(),
|
||||
getBrokerNetworkThroughputWidgetData(),
|
||||
getBrokerCountWidgetData(dotMetricsEnabled),
|
||||
getRequestTimesWidgetData(dotMetricsEnabled),
|
||||
getProducerFetchRequestPurgatoryWidgetData(dotMetricsEnabled),
|
||||
getBrokerNetworkThroughputWidgetData(dotMetricsEnabled),
|
||||
],
|
||||
id: 'broker-metrics',
|
||||
},
|
||||
@@ -87,11 +94,11 @@ function MetricColumnGraphs({
|
||||
title: t('metricGraphCategory.producerMetrics.title'),
|
||||
description: t('metricGraphCategory.producerMetrics.description'),
|
||||
graphCount: [
|
||||
getIoWaitTimeWidgetData(),
|
||||
getRequestResponseWidgetData(),
|
||||
getAverageRequestLatencyWidgetData(),
|
||||
getKafkaProducerByteRateWidgetData(),
|
||||
getBytesConsumedWidgetData(),
|
||||
getIoWaitTimeWidgetData(dotMetricsEnabled),
|
||||
getRequestResponseWidgetData(dotMetricsEnabled),
|
||||
getAverageRequestLatencyWidgetData(dotMetricsEnabled),
|
||||
getKafkaProducerByteRateWidgetData(dotMetricsEnabled),
|
||||
getBytesConsumedWidgetData(dotMetricsEnabled),
|
||||
],
|
||||
id: 'producer-metrics',
|
||||
},
|
||||
@@ -99,11 +106,11 @@ function MetricColumnGraphs({
|
||||
title: t('metricGraphCategory.consumerMetrics.title'),
|
||||
description: t('metricGraphCategory.consumerMetrics.description'),
|
||||
graphCount: [
|
||||
getConsumerOffsetWidgetData(),
|
||||
getConsumerGroupMemberWidgetData(),
|
||||
getConsumerLagByGroupWidgetData(),
|
||||
getConsumerFetchRateWidgetData(),
|
||||
getMessagesConsumedWidgetData(),
|
||||
getConsumerOffsetWidgetData(dotMetricsEnabled),
|
||||
getConsumerGroupMemberWidgetData(dotMetricsEnabled),
|
||||
getConsumerLagByGroupWidgetData(dotMetricsEnabled),
|
||||
getConsumerFetchRateWidgetData(dotMetricsEnabled),
|
||||
getMessagesConsumedWidgetData(dotMetricsEnabled),
|
||||
],
|
||||
id: 'consumer-metrics',
|
||||
},
|
||||
|
||||
@@ -8,6 +8,8 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { ChevronDown, ChevronUp } from '@signozhq/icons';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { FeatureKeys } from '../../../../constants/features';
|
||||
import { useAppContext } from '../../../../providers/App/App';
|
||||
import MetricColumnGraphs from './MetricColumnGraphs';
|
||||
import MetricPageGridGraph from './MetricPageGraph';
|
||||
import {
|
||||
@@ -95,6 +97,11 @@ function MetricPage(): JSX.Element {
|
||||
}));
|
||||
};
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const { t } = useTranslation('messagingQueues');
|
||||
|
||||
const metricSections = [
|
||||
@@ -103,10 +110,10 @@ function MetricPage(): JSX.Element {
|
||||
title: t('metricGraphCategory.brokerJVMMetrics.title'),
|
||||
description: t('metricGraphCategory.brokerJVMMetrics.description'),
|
||||
graphCount: [
|
||||
getJvmGCCountWidgetData(),
|
||||
getJvmGcCollectionsElapsedWidgetData(),
|
||||
getCpuRecentUtilizationWidgetData(),
|
||||
getJvmMemoryHeapWidgetData(),
|
||||
getJvmGCCountWidgetData(dotMetricsEnabled),
|
||||
getJvmGcCollectionsElapsedWidgetData(dotMetricsEnabled),
|
||||
getCpuRecentUtilizationWidgetData(dotMetricsEnabled),
|
||||
getJvmMemoryHeapWidgetData(dotMetricsEnabled),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -114,10 +121,10 @@ function MetricPage(): JSX.Element {
|
||||
title: t('metricGraphCategory.partitionMetrics.title'),
|
||||
description: t('metricGraphCategory.partitionMetrics.description'),
|
||||
graphCount: [
|
||||
getPartitionCountPerTopicWidgetData(),
|
||||
getCurrentOffsetPartitionWidgetData(),
|
||||
getOldestOffsetWidgetData(),
|
||||
getInsyncReplicasWidgetData(),
|
||||
getPartitionCountPerTopicWidgetData(dotMetricsEnabled),
|
||||
getCurrentOffsetPartitionWidgetData(dotMetricsEnabled),
|
||||
getOldestOffsetWidgetData(dotMetricsEnabled),
|
||||
getInsyncReplicasWidgetData(dotMetricsEnabled),
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -131,7 +138,7 @@ function MetricPage(): JSX.Element {
|
||||
|
||||
// Only log when first graph has rendered and we haven't logged yet
|
||||
if (renderedGraphCountRef.current === 1 && !hasLoggedRef.current) {
|
||||
void logEvent('MQ Kafka: Metric view', {
|
||||
logEvent('MQ Kafka: Metric view', {
|
||||
graphRendered: true,
|
||||
});
|
||||
hasLoggedRef.current = true;
|
||||
|
||||
@@ -78,15 +78,21 @@ export function getWidgetQuery(
|
||||
};
|
||||
}
|
||||
|
||||
export const getRequestTimesWidgetData = (): Widgets =>
|
||||
export const getRequestTimesWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.request.time.avg',
|
||||
id: 'kafka.request.time.avg--float64--Gauge--true',
|
||||
// choose key based on flag
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.request.time.avg'
|
||||
: 'kafka_request_time_avg',
|
||||
// mirror into the id as well
|
||||
id: 'kafka_request_time_avg--float64--Gauge--true',
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -116,15 +122,15 @@ export const getRequestTimesWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getBrokerCountWidgetData = (): Widgets =>
|
||||
export const getBrokerCountWidgetData = (dotMetricsEnabled: boolean): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.brokers',
|
||||
id: 'kafka.brokers--float64--Gauge--true',
|
||||
key: dotMetricsEnabled ? 'kafka.brokers' : 'kafka_brokers',
|
||||
id: 'kafka_brokers--float64--Gauge--true',
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'sum',
|
||||
@@ -150,15 +156,20 @@ export const getBrokerCountWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getProducerFetchRequestPurgatoryWidgetData = (): Widgets =>
|
||||
export const getProducerFetchRequestPurgatoryWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.purgatory.size',
|
||||
id: 'kafka.purgatory.size--float64--Gauge--true',
|
||||
// inline ternary based on dotMetricsEnabled
|
||||
key: dotMetricsEnabled ? 'kafka.purgatory.size' : 'kafka_purgatory_size',
|
||||
id: `${
|
||||
dotMetricsEnabled ? 'kafka.purgatory.size' : 'kafka_purgatory_size'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -185,15 +196,24 @@ export const getProducerFetchRequestPurgatoryWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getBrokerNetworkThroughputWidgetData = (): Widgets =>
|
||||
export const getBrokerNetworkThroughputWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka_server_brokertopicmetrics_total_replicationbytesinpersec_oneminuterate',
|
||||
id: 'kafka_server_brokertopicmetrics_total_replicationbytesinpersec_oneminuterate--float64--Gauge--true',
|
||||
// inline ternary based on dotMetricsEnabled
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka_server_brokertopicmetrics_total_replicationbytesinpersec_oneminuterate'
|
||||
: 'kafka_server_brokertopicmetrics_bytesoutpersec_oneminuterate',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka_server_brokertopicmetrics_total_replicationbytesinpersec_oneminuterate'
|
||||
: 'kafka_server_brokertopicmetrics_bytesoutpersec_oneminuterate'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -220,15 +240,22 @@ export const getBrokerNetworkThroughputWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getIoWaitTimeWidgetData = (): Widgets =>
|
||||
export const getIoWaitTimeWidgetData = (dotMetricsEnabled: boolean): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.producer.io_waittime_total',
|
||||
id: 'kafka.producer.io_waittime_total--float64--Sum--true',
|
||||
// inline ternary based on dotMetricsEnabled
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.producer.io_waittime_total'
|
||||
: 'kafka_producer_io_waittime_total',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.producer.io_waittime_total'
|
||||
: 'kafka_producer_io_waittime_total'
|
||||
}--float64--Sum--true`,
|
||||
type: 'Sum',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
@@ -255,15 +282,23 @@ export const getIoWaitTimeWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getRequestResponseWidgetData = (): Widgets =>
|
||||
export const getRequestResponseWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.producer.request_rate',
|
||||
id: 'kafka.producer.request_rate--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.producer.request_rate'
|
||||
: 'kafka_producer_request_rate',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.producer.request_rate'
|
||||
: 'kafka_producer_request_rate'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -286,8 +321,14 @@ export const getRequestResponseWidgetData = (): Widgets =>
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.producer.response_rate',
|
||||
id: 'kafka.producer.response_rate--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.producer.response_rate'
|
||||
: 'kafka_producer_response_rate',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.producer.response_rate'
|
||||
: 'kafka_producer_response_rate'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -314,15 +355,23 @@ export const getRequestResponseWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getAverageRequestLatencyWidgetData = (): Widgets =>
|
||||
export const getAverageRequestLatencyWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.producer.request_latency_avg',
|
||||
id: 'kafka.producer.request_latency_avg--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.producer.request_latency_avg'
|
||||
: 'kafka_producer_request_latency_avg',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.producer.request_latency_avg'
|
||||
: 'kafka_producer_request_latency_avg'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -349,15 +398,23 @@ export const getAverageRequestLatencyWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getKafkaProducerByteRateWidgetData = (): Widgets =>
|
||||
export const getKafkaProducerByteRateWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.producer.byte_rate',
|
||||
id: 'kafka.producer.byte_rate--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.producer.byte_rate'
|
||||
: 'kafka_producer_byte_rate',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.producer.byte_rate'
|
||||
: 'kafka_producer_byte_rate'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -385,21 +442,31 @@ export const getKafkaProducerByteRateWidgetData = (): Widgets =>
|
||||
timeAggregation: 'avg',
|
||||
},
|
||||
],
|
||||
title: 'kafka.producer.byte_rate',
|
||||
title: dotMetricsEnabled
|
||||
? 'kafka.producer.byte_rate'
|
||||
: 'kafka_producer_byte_rate',
|
||||
description:
|
||||
'Helps measure the data output rate from the producer, indicating the load a producer is placing on Kafka brokers.',
|
||||
}),
|
||||
);
|
||||
|
||||
export const getBytesConsumedWidgetData = (): Widgets =>
|
||||
export const getBytesConsumedWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.consumer.bytes_consumed_rate',
|
||||
id: 'kafka.consumer.bytes_consumed_rate--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.consumer.bytes_consumed_rate'
|
||||
: 'kafka_consumer_bytes_consumed_rate',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.consumer.bytes_consumed_rate'
|
||||
: 'kafka_consumer_bytes_consumed_rate'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -427,15 +494,23 @@ export const getBytesConsumedWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getConsumerOffsetWidgetData = (): Widgets =>
|
||||
export const getConsumerOffsetWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.consumer_group.offset',
|
||||
id: 'kafka.consumer_group.offset--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.consumer_group.offset'
|
||||
: 'kafka_consumer_group_offset',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.consumer_group.offset'
|
||||
: 'kafka_consumer_group_offset'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -481,15 +556,23 @@ export const getConsumerOffsetWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getConsumerGroupMemberWidgetData = (): Widgets =>
|
||||
export const getConsumerGroupMemberWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.consumer_group.members',
|
||||
id: 'kafka.consumer_group.members--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.consumer_group.members'
|
||||
: 'kafka_consumer_group_members',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.consumer_group.members'
|
||||
: 'kafka_consumer_group_members'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'sum',
|
||||
@@ -522,15 +605,23 @@ export const getConsumerGroupMemberWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getConsumerLagByGroupWidgetData = (): Widgets =>
|
||||
export const getConsumerLagByGroupWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.consumer_group.lag',
|
||||
id: 'kafka.consumer_group.lag--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.consumer_group.lag'
|
||||
: 'kafka_consumer_group_lag',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.consumer_group.lag'
|
||||
: 'kafka_consumer_group_lag'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -576,15 +667,23 @@ export const getConsumerLagByGroupWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getConsumerFetchRateWidgetData = (): Widgets =>
|
||||
export const getConsumerFetchRateWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.consumer.fetch_rate',
|
||||
id: 'kafka.consumer.fetch_rate--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.consumer.fetch_rate'
|
||||
: 'kafka_consumer_fetch_rate',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.consumer.fetch_rate'
|
||||
: 'kafka_consumer_fetch_rate'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -597,7 +696,7 @@ export const getConsumerFetchRateWidgetData = (): Widgets =>
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'service_name--string--tag--false',
|
||||
key: 'service.name',
|
||||
key: dotMetricsEnabled ? 'service.name' : 'service_name',
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
@@ -618,15 +717,23 @@ export const getConsumerFetchRateWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getMessagesConsumedWidgetData = (): Widgets =>
|
||||
export const getMessagesConsumedWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.consumer.records_consumed_rate',
|
||||
id: 'kafka.consumer.records_consumed_rate--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.consumer.records_consumed_rate'
|
||||
: 'kafka_consumer_records_consumed_rate',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.consumer.records_consumed_rate'
|
||||
: 'kafka_consumer_records_consumed_rate'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -653,15 +760,21 @@ export const getMessagesConsumedWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getJvmGCCountWidgetData = (): Widgets =>
|
||||
export const getJvmGCCountWidgetData = (dotMetricsEnabled: boolean): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'jvm.gc.collections.count',
|
||||
id: 'jvm.gc.collections.count--float64--Sum--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'jvm.gc.collections.count'
|
||||
: 'jvm_gc_collections_count',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'jvm.gc.collections.count'
|
||||
: 'jvm_gc_collections_count'
|
||||
}--float64--Sum--true`,
|
||||
type: 'Sum',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
@@ -688,15 +801,23 @@ export const getJvmGCCountWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getJvmGcCollectionsElapsedWidgetData = (): Widgets =>
|
||||
export const getJvmGcCollectionsElapsedWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'jvm.gc.collections.elapsed',
|
||||
id: 'jvm.gc.collections.elapsed--float64--Sum--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'jvm.gc.collections.elapsed'
|
||||
: 'jvm_gc_collections_elapsed',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'jvm.gc.collections.elapsed'
|
||||
: 'jvm_gc_collections_elapsed'
|
||||
}--float64--Sum--true`,
|
||||
type: 'Sum',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
@@ -717,21 +838,31 @@ export const getJvmGcCollectionsElapsedWidgetData = (): Widgets =>
|
||||
timeAggregation: 'rate',
|
||||
},
|
||||
],
|
||||
title: 'jvm.gc.collections.elapsed',
|
||||
title: dotMetricsEnabled
|
||||
? 'jvm.gc.collections.elapsed'
|
||||
: 'jvm_gc_collections_elapsed',
|
||||
description:
|
||||
'Measures the total time (usually in milliseconds) spent on garbage collection (GC) events in the Java Virtual Machine (JVM).',
|
||||
}),
|
||||
);
|
||||
|
||||
export const getCpuRecentUtilizationWidgetData = (): Widgets =>
|
||||
export const getCpuRecentUtilizationWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'jvm.cpu.recent_utilization',
|
||||
id: 'jvm.cpu.recent_utilization--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'jvm.cpu.recent_utilization'
|
||||
: 'jvm_cpu_recent_utilization',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'jvm.cpu.recent_utilization'
|
||||
: 'jvm_cpu_recent_utilization'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -758,15 +889,19 @@ export const getCpuRecentUtilizationWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getJvmMemoryHeapWidgetData = (): Widgets =>
|
||||
export const getJvmMemoryHeapWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'jvm.memory.heap.max',
|
||||
id: 'jvm.memory.heap.max--float64--Gauge--true',
|
||||
key: dotMetricsEnabled ? 'jvm.memory.heap.max' : 'jvm_memory_heap_max',
|
||||
id: `${
|
||||
dotMetricsEnabled ? 'jvm.memory.heap.max' : 'jvm_memory_heap_max'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -793,15 +928,21 @@ export const getJvmMemoryHeapWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getPartitionCountPerTopicWidgetData = (): Widgets =>
|
||||
export const getPartitionCountPerTopicWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.topic.partitions',
|
||||
id: 'kafka.topic.partitions--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.topic.partitions'
|
||||
: 'kafka_topic_partitions',
|
||||
id: `${
|
||||
dotMetricsEnabled ? 'kafka.topic.partitions' : 'kafka_topic_partitions'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'sum',
|
||||
@@ -834,15 +975,23 @@ export const getPartitionCountPerTopicWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getCurrentOffsetPartitionWidgetData = (): Widgets =>
|
||||
export const getCurrentOffsetPartitionWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.partition.current_offset',
|
||||
id: 'kafka.partition.current_offset--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.partition.current_offset'
|
||||
: 'kafka_partition_current_offset',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.partition.current_offset'
|
||||
: 'kafka_partition_current_offset'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -882,15 +1031,23 @@ export const getCurrentOffsetPartitionWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getOldestOffsetWidgetData = (): Widgets =>
|
||||
export const getOldestOffsetWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.partition.oldest_offset',
|
||||
id: 'kafka.partition.oldest_offset--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.partition.oldest_offset'
|
||||
: 'kafka_partition_oldest_offset',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.partition.oldest_offset'
|
||||
: 'kafka_partition_oldest_offset'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
@@ -930,15 +1087,23 @@ export const getOldestOffsetWidgetData = (): Widgets =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const getInsyncReplicasWidgetData = (): Widgets =>
|
||||
export const getInsyncReplicasWidgetData = (
|
||||
dotMetricsEnabled: boolean,
|
||||
): Widgets =>
|
||||
getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
key: 'kafka.partition.replicas_in_sync',
|
||||
id: 'kafka.partition.replicas_in_sync--float64--Gauge--true',
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.partition.replicas_in_sync'
|
||||
: 'kafka_partition_replicas_in_sync',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.partition.replicas_in_sync'
|
||||
: 'kafka_partition_replicas_in_sync'
|
||||
}--float64--Gauge--true`,
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
|
||||
@@ -11,6 +11,8 @@ import useDebouncedFn from 'hooks/useDebouncedFunction';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { Check, Share2 } from '@signozhq/icons';
|
||||
|
||||
import { FeatureKeys } from '../../../constants/features';
|
||||
import { useAppContext } from '../../../providers/App/App';
|
||||
import { useGetAllConfigOptions } from './useGetAllConfigOptions';
|
||||
|
||||
import './MQConfigOptions.styles.scss';
|
||||
@@ -38,11 +40,19 @@ const useConfigOptions = (
|
||||
isFetching: boolean;
|
||||
options: DefaultOptionType[];
|
||||
} => {
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const { isFetching, options } = useGetAllConfigOptions({
|
||||
attributeKey: type,
|
||||
searchText,
|
||||
});
|
||||
const { isFetching, options } = useGetAllConfigOptions(
|
||||
{
|
||||
attributeKey: type,
|
||||
searchText,
|
||||
},
|
||||
dotMetricsEnabled,
|
||||
);
|
||||
const handleDebouncedSearch = useDebouncedFn((searchText): void => {
|
||||
setSearchText(searchText as string);
|
||||
}, 500);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useCallback, useMemo, useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { ViewMenuAction } from 'container/GridCardLayout/config';
|
||||
@@ -11,6 +12,7 @@ import { Card } from 'container/GridCardLayout/styles';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
|
||||
import {
|
||||
@@ -32,9 +34,15 @@ function MessagingQueuesGraph(): JSX.Element {
|
||||
[consumerGrp, topic, partition],
|
||||
);
|
||||
|
||||
const { featureFlags } = useAppContext();
|
||||
const dotMetricsEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.DOT_METRICS_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const widgetData = useMemo(
|
||||
() => getWidgetQueryBuilder(getWidgetQuery({ filterItems })),
|
||||
[filterItems],
|
||||
() =>
|
||||
getWidgetQueryBuilder(getWidgetQuery({ filterItems, dotMetricsEnabled })),
|
||||
[filterItems, dotMetricsEnabled],
|
||||
);
|
||||
|
||||
const history = useHistory();
|
||||
@@ -73,7 +81,7 @@ function MessagingQueuesGraph(): JSX.Element {
|
||||
const checkIfDataExists = (isDataAvailable: boolean): void => {
|
||||
if (!isLogEventCalled.current) {
|
||||
isLogEventCalled.current = true;
|
||||
void logEvent('Messaging Queues: Graph data fetched', {
|
||||
logEvent('Messaging Queues: Graph data fetched', {
|
||||
isDataAvailable,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface GetAllConfigOptionsResponse {
|
||||
|
||||
export function useGetAllConfigOptions(
|
||||
props: ConfigOptions,
|
||||
dotMetricsEnabled: boolean,
|
||||
): GetAllConfigOptionsResponse {
|
||||
const { attributeKey, searchText } = props;
|
||||
|
||||
@@ -25,7 +26,9 @@ export function useGetAllConfigOptions(
|
||||
const { payload } = await getAttributesValues({
|
||||
aggregateOperator: 'avg',
|
||||
dataSource: DataSource.METRICS,
|
||||
aggregateAttribute: 'kafka.consumer_group.lag',
|
||||
aggregateAttribute: dotMetricsEnabled
|
||||
? 'kafka.consumer_group.lag'
|
||||
: 'kafka_consumer_group_lag',
|
||||
attributeKey,
|
||||
searchText: searchText ?? '',
|
||||
filterAttributeKeyDataType: DataTypes.String,
|
||||
|
||||
@@ -94,8 +94,10 @@ export function getFiltersFromConfigOptions(
|
||||
|
||||
export function getWidgetQuery({
|
||||
filterItems,
|
||||
dotMetricsEnabled,
|
||||
}: {
|
||||
filterItems: TagFilterItem[];
|
||||
dotMetricsEnabled: boolean;
|
||||
}): GetWidgetQueryBuilderProps {
|
||||
return {
|
||||
title: 'Consumer Lag',
|
||||
@@ -110,8 +112,14 @@ export function getWidgetQuery({
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id: 'kafka.consumer_group.lag--float64--Gauge--true',
|
||||
key: 'kafka.consumer_group.lag',
|
||||
id: `${
|
||||
dotMetricsEnabled
|
||||
? 'kafka.consumer_group.lag'
|
||||
: 'kafka_consumer_group_lag'
|
||||
}--float64--Gauge--true`,
|
||||
key: dotMetricsEnabled
|
||||
? 'kafka.consumer_group.lag'
|
||||
: 'kafka_consumer_group_lag',
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'max',
|
||||
|
||||
9
pkg/prometheus/handler.go
Normal file
9
pkg/prometheus/handler.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package prometheus
|
||||
|
||||
import "net/http"
|
||||
|
||||
type Handler interface {
|
||||
Query(http.ResponseWriter, *http.Request)
|
||||
|
||||
QueryRange(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
259
pkg/prometheus/promapi/handler.go
Normal file
259
pkg/prometheus/promapi/handler.go
Normal file
@@ -0,0 +1,259 @@
|
||||
// Package promapi serves the Prometheus HTTP query API over a
|
||||
// prometheus.Prometheus provider: /query and /query_range in the shape of
|
||||
// Prometheus' /api/v1 endpoints (https://prometheus.io/docs/prometheus/latest/querying/api/),
|
||||
// intended to be mounted under a distinguishing prefix (/prometheus/api/v1)
|
||||
// so PromQL-only endpoints are separate from the SigNoz query APIs. The
|
||||
// request and response contracts follow Prometheus: form-encoded GET/POST
|
||||
// params, {"status":"success","data":{resultType,result}} on success and
|
||||
// {"status":"error","errorType","error"} with Prometheus' status codes on
|
||||
// failure — so Prometheus-compatible clients can point at the prefix.
|
||||
package promapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
promModel "github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
"github.com/prometheus/prometheus/util/stats"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
logger *slog.Logger
|
||||
prom prometheus.Prometheus
|
||||
}
|
||||
|
||||
func NewHandler(logger *slog.Logger, prom prometheus.Prometheus) prometheus.Handler {
|
||||
return &handler{logger: logger, prom: prom}
|
||||
}
|
||||
|
||||
type errorType string
|
||||
|
||||
const (
|
||||
errBadData errorType = "bad_data"
|
||||
errExec errorType = "execution"
|
||||
errCanceled errorType = "canceled"
|
||||
errTimeout errorType = "timeout"
|
||||
errInternal errorType = "internal"
|
||||
)
|
||||
|
||||
type queryData struct {
|
||||
ResultType parser.ValueType `json:"resultType"`
|
||||
Result parser.Value `json:"result"`
|
||||
Stats stats.QueryStats `json:"stats,omitempty"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Status string `json:"status"`
|
||||
Data *queryData `json:"data,omitempty"`
|
||||
ErrorType errorType `json:"errorType,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
Infos []string `json:"infos,omitempty"`
|
||||
}
|
||||
|
||||
// QueryRange evaluates an expression over a grid: query, start, end, step,
|
||||
// and optional timeout/stats params, all in Prometheus' formats.
|
||||
func (h *handler) QueryRange(w http.ResponseWriter, r *http.Request) {
|
||||
start, err := parseTime(r.FormValue("start"))
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
end, err := parseTime(r.FormValue("end"))
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
if end.Before(start) {
|
||||
h.respondError(r.Context(), w, errBadData, errors.NewInvalidInputf(errors.CodeInvalidInput, "end timestamp must not be before start time"))
|
||||
return
|
||||
}
|
||||
step, err := parseDuration(r.FormValue("step"))
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
if step <= 0 {
|
||||
h.respondError(r.Context(), w, errBadData, errors.NewInvalidInputf(errors.CodeInvalidInput, "zero or negative query resolution step widths are not accepted. Try a positive integer"))
|
||||
return
|
||||
}
|
||||
// The engine materializes every point of every series; an unbounded
|
||||
// grid is an unbounded allocation. 11,000 points covers 60s resolution
|
||||
// for a week or 1h resolution for a year.
|
||||
if end.Sub(start)/step > 11000 {
|
||||
h.respondError(r.Context(), w, errBadData, errors.NewInvalidInputf(errors.CodeInvalidInput, "exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel, err := h.contextWithTimeout(r)
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
if h.tryRangeExecutor(ctx, w, r, start, end, step) {
|
||||
return
|
||||
}
|
||||
|
||||
qry, err := h.prom.Engine().NewRangeQuery(ctx, h.prom.Storage(), nil, r.FormValue("query"), start, end, step)
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
h.exec(ctx, w, r, qry)
|
||||
}
|
||||
|
||||
// tryRangeExecutor serves the query the way a RangeExecutor provider is
|
||||
// designed to serve: evaluated inside the datastore when the shape allows.
|
||||
// It reports whether the response was written.
|
||||
func (h *handler) tryRangeExecutor(ctx context.Context, w http.ResponseWriter, r *http.Request, start, end time.Time, step time.Duration) bool {
|
||||
re, ok := h.prom.(prometheus.RangeExecutor)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
matrix, served, err := re.TryExecuteRange(ctx, r.FormValue("query"), start, end, step)
|
||||
if err != nil {
|
||||
h.respondError(ctx, w, errExec, err)
|
||||
return true
|
||||
}
|
||||
if !served {
|
||||
return false
|
||||
}
|
||||
h.respond(ctx, w, &queryData{ResultType: matrix.Type(), Result: matrix}, nil, nil)
|
||||
return true
|
||||
}
|
||||
|
||||
// Query evaluates an expression at a single instant: query and optional
|
||||
// time/timeout/stats params. A missing time evaluates at the server's now,
|
||||
// as in Prometheus.
|
||||
func (h *handler) Query(w http.ResponseWriter, r *http.Request) {
|
||||
ts := time.Now()
|
||||
if t := r.FormValue("time"); t != "" {
|
||||
var err error
|
||||
ts, err = parseTime(t)
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel, err := h.contextWithTimeout(r)
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
qry, err := h.prom.Engine().NewInstantQuery(ctx, h.prom.Storage(), nil, r.FormValue("query"), ts)
|
||||
if err != nil {
|
||||
h.respondError(r.Context(), w, errBadData, err)
|
||||
return
|
||||
}
|
||||
h.exec(ctx, w, r, qry)
|
||||
}
|
||||
|
||||
func (h *handler) exec(ctx context.Context, w http.ResponseWriter, r *http.Request, qry promql.Query) {
|
||||
defer qry.Close()
|
||||
res := qry.Exec(ctx)
|
||||
if res.Err != nil {
|
||||
h.logger.ErrorContext(ctx, "error evaluating promql query", errors.Attr(res.Err))
|
||||
switch res.Err.(type) {
|
||||
case promql.ErrQueryCanceled:
|
||||
h.respondError(ctx, w, errCanceled, res.Err)
|
||||
case promql.ErrQueryTimeout:
|
||||
h.respondError(ctx, w, errTimeout, res.Err)
|
||||
case promql.ErrStorage:
|
||||
h.respondError(ctx, w, errInternal, res.Err)
|
||||
default:
|
||||
h.respondError(ctx, w, errExec, res.Err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data := &queryData{ResultType: res.Value.Type(), Result: res.Value}
|
||||
if r.FormValue("stats") != "" {
|
||||
data.Stats = stats.NewQueryStats(qry.Stats())
|
||||
}
|
||||
warnings, infos := res.Warnings.AsStrings(r.FormValue("query"), 10, 10)
|
||||
h.respond(ctx, w, data, warnings, infos)
|
||||
}
|
||||
|
||||
func (h *handler) contextWithTimeout(r *http.Request) (context.Context, context.CancelFunc, error) {
|
||||
ctx := r.Context()
|
||||
if to := r.FormValue("timeout"); to != "" {
|
||||
timeout, err := parseDuration(to)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
return ctx, cancel, nil
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return ctx, cancel, nil
|
||||
}
|
||||
|
||||
func (h *handler) respond(ctx context.Context, w http.ResponseWriter, data *queryData, warnings, infos []string) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if err := json.NewEncoder(w).Encode(&response{Status: "success", Data: data, Warnings: warnings, Infos: infos}); err != nil {
|
||||
h.logger.ErrorContext(ctx, "error writing prometheus api response", errors.Attr(err))
|
||||
}
|
||||
}
|
||||
|
||||
// respondError follows Prometheus' status-code mapping: bad_data 400,
|
||||
// execution 422, canceled/timeout 503, internal 500.
|
||||
func (h *handler) respondError(ctx context.Context, w http.ResponseWriter, typ errorType, err error) {
|
||||
code := http.StatusInternalServerError
|
||||
switch typ {
|
||||
case errBadData:
|
||||
code = http.StatusBadRequest
|
||||
case errExec:
|
||||
code = http.StatusUnprocessableEntity
|
||||
case errCanceled, errTimeout:
|
||||
code = http.StatusServiceUnavailable
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
if encErr := json.NewEncoder(w).Encode(&response{Status: "error", ErrorType: typ, Error: err.Error()}); encErr != nil {
|
||||
h.logger.ErrorContext(ctx, "error writing prometheus api error response", errors.Attr(encErr))
|
||||
}
|
||||
}
|
||||
|
||||
// parseTime accepts Prometheus' time formats: float unix seconds or RFC3339.
|
||||
func parseTime(s string) (time.Time, error) {
|
||||
if t, err := strconv.ParseFloat(s, 64); err == nil {
|
||||
sec, ns := math.Modf(t)
|
||||
return time.Unix(int64(sec), int64(ns*float64(time.Second))), nil
|
||||
}
|
||||
if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
return time.Time{}, errors.NewInvalidInputf(errors.CodeInvalidInput, "cannot parse %q to a valid timestamp", s)
|
||||
}
|
||||
|
||||
// parseDuration accepts Prometheus' duration formats: float seconds or a
|
||||
// duration string like 5m.
|
||||
func parseDuration(s string) (time.Duration, error) {
|
||||
if d, err := strconv.ParseFloat(s, 64); err == nil {
|
||||
ts := d * float64(time.Second)
|
||||
if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
|
||||
return 0, errors.NewInvalidInputf(errors.CodeInvalidInput, "cannot parse %q to a valid duration. It overflows int64", s)
|
||||
}
|
||||
return time.Duration(ts), nil
|
||||
}
|
||||
if d, err := promModel.ParseDuration(s); err == nil {
|
||||
return time.Duration(d), nil
|
||||
}
|
||||
return 0, errors.NewInvalidInputf(errors.CodeInvalidInput, "cannot parse %q to a valid duration", s)
|
||||
}
|
||||
@@ -3190,6 +3190,11 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, orgID v
|
||||
var rows driver.Rows
|
||||
var attributeValues v3.FilterAttributeValueResponse
|
||||
|
||||
normalized := true
|
||||
if constants.IsDotMetricsEnabled {
|
||||
normalized = false
|
||||
}
|
||||
|
||||
reductionEnabled := r.fl.BooleanOrEmpty(ctx, flagger.FeatureEnableMetricsReduction, featuretypes.NewFlaggerEvaluationContext(orgID))
|
||||
|
||||
if reductionEnabled {
|
||||
@@ -3201,7 +3206,7 @@ func (r *ClickHouseReader) GetMetricAttributeValues(ctx context.Context, orgID v
|
||||
query = query + fmt.Sprintf(" LIMIT %d;", req.Limit)
|
||||
}
|
||||
names := []string{req.AggregateAttribute}
|
||||
names = append(names, metrics.GetTransitionedMetric(req.AggregateAttribute))
|
||||
names = append(names, metrics.GetTransitionedMetric(req.AggregateAttribute, normalized))
|
||||
|
||||
rows, err = r.db.Query(ctx, query, req.FilterAttributeKey, names, req.FilterAttributeKey, fmt.Sprintf("%%%s%%", req.SearchText), common.PastDayRoundOff())
|
||||
|
||||
@@ -5443,3 +5448,112 @@ func (r *ClickHouseReader) SearchTraces(ctx context.Context, params *model.Searc
|
||||
|
||||
return &searchSpansResult, nil
|
||||
}
|
||||
|
||||
func (r *ClickHouseReader) GetNormalizedStatus(
|
||||
ctx context.Context,
|
||||
orgID valuer.UUID,
|
||||
metricNames []string,
|
||||
) (map[string]bool, error) {
|
||||
|
||||
ctx = ctxtypes.NewContextWithCommentVals(ctx, map[string]string{
|
||||
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentationtypes.CodeNamespace: "clickhouse-reader",
|
||||
instrumentationtypes.CodeFunctionName: "GetNormalizedStatus",
|
||||
})
|
||||
if len(metricNames) == 0 {
|
||||
return map[string]bool{}, nil
|
||||
}
|
||||
|
||||
result := make(map[string]bool, len(metricNames))
|
||||
buildKey := func(name string) string {
|
||||
return constants.NormalizedMetricsMapCacheKey + ":" + name
|
||||
}
|
||||
|
||||
uncached := make([]string, 0, len(metricNames))
|
||||
for _, m := range metricNames {
|
||||
var status model.MetricsNormalizedMap
|
||||
if err := r.cache.Get(ctx, orgID, buildKey(m), &status); err == nil {
|
||||
result[m] = status.IsUnNormalized
|
||||
} else {
|
||||
uncached = append(uncached, m)
|
||||
}
|
||||
}
|
||||
if len(uncached) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
placeholders := "'" + strings.Join(uncached, "', '") + "'"
|
||||
|
||||
reductionEnabled := r.fl.BooleanOrEmpty(ctx, flagger.FeatureEnableMetricsReduction, featuretypes.NewFlaggerEvaluationContext(orgID))
|
||||
|
||||
var q string
|
||||
if reductionEnabled {
|
||||
q = fmt.Sprintf(
|
||||
`SELECT metric_name, toUInt8(__normalized)
|
||||
FROM (
|
||||
SELECT metric_name, __normalized FROM %s.%s WHERE metric_name IN (%s)
|
||||
UNION ALL
|
||||
SELECT metric_name, __normalized FROM %s.%s WHERE metric_name IN (%s)
|
||||
)
|
||||
GROUP BY metric_name, __normalized`,
|
||||
signozMetricDBName, signozTSTableNameV41Day, placeholders,
|
||||
signozMetricDBName, signozTSTableNameV4Reduced, placeholders,
|
||||
)
|
||||
} else {
|
||||
q = fmt.Sprintf(
|
||||
`SELECT metric_name, toUInt8(__normalized)
|
||||
FROM %s.%s
|
||||
WHERE metric_name IN (%s)
|
||||
GROUP BY metric_name, __normalized`,
|
||||
signozMetricDBName, signozTSTableNameV41Day, placeholders,
|
||||
)
|
||||
}
|
||||
|
||||
rows, err := r.db.Query(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// tmp[m] collects the set {0,1} for a metric name, truth table
|
||||
tmp := make(map[string]map[uint8]struct{}, len(uncached))
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
name string
|
||||
normalized uint8
|
||||
)
|
||||
if err := rows.Scan(&name, &normalized); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := tmp[name]; !ok {
|
||||
tmp[name] = make(map[uint8]struct{}, 2)
|
||||
}
|
||||
tmp[name][normalized] = struct{}{}
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, m := range uncached {
|
||||
set := tmp[m]
|
||||
switch {
|
||||
case len(set) == 0:
|
||||
return nil, fmt.Errorf("metric %q not found in ClickHouse", m)
|
||||
|
||||
case len(set) == 2:
|
||||
result[m] = true
|
||||
|
||||
default:
|
||||
_, hasUnnorm := set[0]
|
||||
result[m] = hasUnnorm
|
||||
}
|
||||
status := model.MetricsNormalizedMap{
|
||||
MetricName: m,
|
||||
IsUnNormalized: result[m],
|
||||
}
|
||||
_ = r.cache.Set(ctx, orgID, buildKey(m), &status, 0)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/app/queryBuilder"
|
||||
tracesV3 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v3"
|
||||
tracesV4 "github.com/SigNoz/signoz/pkg/query-service/app/traces/v4"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/postprocess"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
@@ -484,6 +485,9 @@ func (aH *APIHandler) Respond(w http.ResponseWriter, data interface{}) {
|
||||
func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *middleware.AuthZ) {
|
||||
router.HandleFunc("/api/v1/query_range", am.ViewAccess(aH.queryRangeMetrics)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/query", am.ViewAccess(aH.queryMetrics)).Methods(http.MethodGet)
|
||||
|
||||
router.HandleFunc("/prometheus/api/v1/query_range", am.ViewAccess(aH.Signoz.Handlers.PrometheusHandler.QueryRange)).Methods(http.MethodGet, http.MethodPost)
|
||||
router.HandleFunc("/prometheus/api/v1/query", am.ViewAccess(aH.Signoz.Handlers.PrometheusHandler.Query)).Methods(http.MethodGet, http.MethodPost)
|
||||
router.HandleFunc("/api/v1/rules", am.ViewAccess(aH.listRules)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/rules/{id}", am.ViewAccess(aH.getRule)).Methods(http.MethodGet)
|
||||
router.HandleFunc("/api/v1/rules", am.EditAccess(aH.createRule)).Methods(http.MethodPost)
|
||||
@@ -1051,6 +1055,10 @@ func prepareQuery(r *http.Request) (string, error) {
|
||||
return "", tmplErr
|
||||
}
|
||||
|
||||
if !constants.IsDotMetricsEnabled {
|
||||
return queryBuf.String(), nil
|
||||
}
|
||||
|
||||
query = queryBuf.String()
|
||||
|
||||
// Now handle $var replacements (simple string replace)
|
||||
@@ -1603,6 +1611,13 @@ func (aH *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
Route: "",
|
||||
})
|
||||
|
||||
if constants.IsDotMetricsEnabled {
|
||||
for idx, feature := range featureSet {
|
||||
if feature.Name == licensetypes.DotMetricsEnabled {
|
||||
featureSet[idx].Active = true
|
||||
}
|
||||
}
|
||||
}
|
||||
aH.Respond(w, featureSet)
|
||||
}
|
||||
|
||||
@@ -2043,8 +2058,12 @@ func (aH *APIHandler) onboardKafka(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
var kafkaConsumerFetchLatencyAvg string = "kafka.consumer.fetch_latency_avg"
|
||||
var kafkaConsumerLag string = "kafka.consumer_group.lag"
|
||||
var kafkaConsumerFetchLatencyAvg string = "kafka_consumer_fetch_latency_avg"
|
||||
var kafkaConsumerLag string = "kafka_consumer_group_lag"
|
||||
if constants.IsDotMetricsEnabled {
|
||||
kafkaConsumerLag = "kafka.consumer_group.lag"
|
||||
kafkaConsumerFetchLatencyAvg = "kafka.consumer.fetch_latency_avg"
|
||||
}
|
||||
|
||||
if !fetchLatencyState && !consumerLagState {
|
||||
entries = append(entries, kafka.OnboardingResponse{
|
||||
|
||||
@@ -18,12 +18,12 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForClusters = "k8s.node.cpu.usage"
|
||||
metricToUseForClusters = GetDotMetrics("k8s_node_cpu_usage")
|
||||
|
||||
clusterAttrsToEnrich = []string{"k8s.cluster.name"}
|
||||
clusterAttrsToEnrich = []string{GetDotMetrics("k8s_cluster_name")}
|
||||
|
||||
// TODO(srikanthccv): change this to k8s_cluster_uid after showing the missing data banner
|
||||
k8sClusterUIDAttrKey = "k8s.cluster.name"
|
||||
k8sClusterUIDAttrKey = GetDotMetrics("k8s_cluster_name")
|
||||
|
||||
queryNamesForClusters = map[string][]string{
|
||||
"cpu": {"A"},
|
||||
|
||||
@@ -9,6 +9,250 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
)
|
||||
|
||||
var dotMetricMap = map[string]string{
|
||||
"system_uptime": "system.uptime",
|
||||
"system_cpu_physical_count": "system.cpu.physical.count",
|
||||
"system_cpu_logical_count": "system.cpu.logical.count",
|
||||
"system_cpu_time": "system.cpu.time",
|
||||
"system_cpu_frequency": "system.cpu.frequency",
|
||||
"system_cpu_utilization": "system.cpu.utilization",
|
||||
"system_cpu_load_average_15m": "system.cpu.load_average.15m",
|
||||
"system_memory_usage": "system.memory.usage",
|
||||
"system_memory_limit": "system.memory.limit",
|
||||
"system_memory_utilization": "system.memory.utilization",
|
||||
"system_memory_linux_available": "system.memory.linux.available",
|
||||
"system_memory_linux_shared": "system.memory.linux.shared",
|
||||
"system_memory_linux_slab_usage": "system.memory.linux.slab.usage",
|
||||
"system_paging_usage": "system.paging.usage",
|
||||
"system_paging_utilization": "system.paging.utilization",
|
||||
"system_paging_faults": "system.paging.faults",
|
||||
"system_paging_operations": "system.paging.operations",
|
||||
"system_disk_io": "system.disk.io",
|
||||
"system_disk_operations": "system.disk.operations",
|
||||
"system_disk_io_time": "system.disk.io_time",
|
||||
"system_disk_operation_time": "system.disk.operation_time",
|
||||
"system_disk_merged": "system.disk.merged",
|
||||
"system_disk_limit": "system.disk.limit",
|
||||
"system_filesystem_usage": "system.filesystem.usage",
|
||||
"system_filesystem_utilization": "system.filesystem.utilization",
|
||||
"system_filesystem_limit": "system.filesystem.limit",
|
||||
"system_network_errors": "system.network.errors",
|
||||
"system_network_io": "system.network.io",
|
||||
"system_network_connections": "system.network.connections",
|
||||
"system_network_dropped": "system.network.dropped",
|
||||
"system_network_packets": "system.network.packets",
|
||||
"system_processes_count": "system.processes.count",
|
||||
"system_processes_created": "system.processes.created",
|
||||
"system_disk_pending_operations": "system.disk.pending_operations",
|
||||
"system_disk_weighted_io_time": "system.disk.weighted_io_time",
|
||||
"system_filesystem_inodes_usage": "system.filesystem.inodes.usage",
|
||||
"system_network_conntrack_count": "system.network.conntrack.count",
|
||||
"system_network_conntrack_max": "system.network.conntrack.max",
|
||||
"system_cpu_load_average_1m": "system.cpu.load_average.1m",
|
||||
"system_cpu_load_average_5m": "system.cpu.load_average.5m",
|
||||
|
||||
"host_name": "host.name",
|
||||
"k8s_cluster_name": "k8s.cluster.name",
|
||||
"k8s_node_name": "k8s.node.name",
|
||||
"k8s_pod_memory_usage": "k8s.pod.memory.usage",
|
||||
"k8s_pod_cpu_request_utilization": "k8s.pod.cpu_request_utilization",
|
||||
"k8s_pod_memory_request_utilization": "k8s.pod.memory_request_utilization",
|
||||
"k8s_pod_cpu_limit_utilization": "k8s.pod.cpu_limit_utilization",
|
||||
"k8s_pod_memory_limit_utilization": "k8s.pod.memory_limit_utilization",
|
||||
"k8s_container_restarts": "k8s.container.restarts",
|
||||
"k8s_pod_phase": "k8s.pod.phase",
|
||||
"k8s_node_allocatable_cpu": "k8s.node.allocatable_cpu",
|
||||
"k8s_node_allocatable_memory": "k8s.node.allocatable_memory",
|
||||
"k8s_node_memory_usage": "k8s.node.memory.usage",
|
||||
"k8s_node_condition_ready": "k8s.node.condition_ready",
|
||||
"k8s_daemonset_desired_scheduled_nodes": "k8s.daemonset.desired_scheduled_nodes",
|
||||
"k8s_daemonset_current_scheduled_nodes": "k8s.daemonset.current_scheduled_nodes",
|
||||
"k8s_deployment_desired": "k8s.deployment.desired",
|
||||
"k8s_deployment_available": "k8s.deployment.available",
|
||||
"k8s_job_desired_successful_pods": "k8s.job.desired_successful_pods",
|
||||
"k8s_job_active_pods": "k8s.job.active_pods",
|
||||
"k8s_job_failed_pods": "k8s.job.failed_pods",
|
||||
"k8s_job_successful_pods": "k8s.job.successful_pods",
|
||||
"k8s_statefulset_desired_pods": "k8s.statefulset.desired_pods",
|
||||
"k8s_statefulset_current_pods": "k8s.statefulset.current_pods",
|
||||
"k8s_namespace_name": "k8s.namespace.name",
|
||||
"k8s_deployment_name": "k8s.deployment.name",
|
||||
"k8s_cronjob_name": "k8s.cronjob.name",
|
||||
"k8s_job_name": "k8s.job.name",
|
||||
"k8s_daemonset_name": "k8s.daemonset.name",
|
||||
"os_type": "os.type",
|
||||
"process_cgroup": "process.cgroup",
|
||||
"process_pid": "process.pid",
|
||||
"process_parent_pid": "process.parent_pid",
|
||||
"process_owner": "process.owner",
|
||||
"process_executable_path": "process.executable.path",
|
||||
"process_executable_name": "process.executable.name",
|
||||
"process_command_line": "process.command_line",
|
||||
"process_command": "process.command",
|
||||
"process_memory_usage": "process.memory.usage",
|
||||
"process_memory_virtual": "process.memory.virtual",
|
||||
"process_cpu_time": "process.cpu.time",
|
||||
"process_disk_io": "process.disk.io",
|
||||
"nfs_client_net_count": "nfs.client.net.count",
|
||||
"nfs_client_net_tcp_connection_accepted": "nfs.client.net.tcp.connection.accepted",
|
||||
"nfs_client_operation_count": "nfs.client.operation.count",
|
||||
"nfs_client_procedure_count": "nfs.client.procedure.count",
|
||||
"nfs_client_rpc_authrefresh_count": "nfs.client.rpc.authrefresh.count",
|
||||
"nfs_client_rpc_count": "nfs.client.rpc.count",
|
||||
"nfs_client_rpc_retransmit_count": "nfs.client.rpc.retransmit.count",
|
||||
"nfs_server_fh_stale_count": "nfs.server.fh.stale.count",
|
||||
"nfs_server_io": "nfs.server.io",
|
||||
"nfs_server_net_count": "nfs.server.net.count",
|
||||
"nfs_server_net_tcp_connection_accepted": "nfs.server.net.tcp.connection.accepted",
|
||||
"nfs_server_operation_count": "nfs.server.operation.count",
|
||||
"nfs_server_procedure_count": "nfs.server.procedure.count",
|
||||
"nfs_server_repcache_requests": "nfs.server.repcache.requests",
|
||||
"nfs_server_rpc_count": "nfs.server.rpc.count",
|
||||
"nfs_server_thread_count": "nfs.server.thread.count",
|
||||
"k8s_persistentvolumeclaim_name": "k8s.persistentvolumeclaim.name",
|
||||
"k8s_volume_available": "k8s.volume.available",
|
||||
"k8s_volume_capacity": "k8s.volume.capacity",
|
||||
"k8s_volume_inodes": "k8s.volume.inodes",
|
||||
"k8s_volume_inodes_free": "k8s.volume.inodes.free",
|
||||
|
||||
"k8s_pod_uid": "k8s.pod.uid",
|
||||
"k8s_pod_name": "k8s.pod.name",
|
||||
"k8s_container_name": "k8s.container.name",
|
||||
"container_id": "container.id",
|
||||
"k8s_volume_name": "k8s.volume.name",
|
||||
"k8s_volume_type": "k8s.volume.type",
|
||||
"aws_volume_id": "aws.volume.id",
|
||||
"fs_type": "fs.type",
|
||||
"partition": "partition",
|
||||
"gce_pd_name": "gce.pd.name",
|
||||
"glusterfs_endpoints_name": "glusterfs.endpoints.name",
|
||||
"glusterfs_path": "glusterfs.path",
|
||||
"interface": "interface",
|
||||
"direction": "direction",
|
||||
|
||||
"k8s_node_cpu_usage": "k8s.node.cpu.usage",
|
||||
"k8s_node_cpu_time": "k8s.node.cpu.time",
|
||||
"k8s_node_memory_available": "k8s.node.memory.available",
|
||||
"k8s_node_memory_rss": "k8s.node.memory.rss",
|
||||
"k8s_node_memory_working_set": "k8s.node.memory.working_set",
|
||||
"k8s_node_memory_page_faults": "k8s.node.memory.page_faults",
|
||||
"k8s_node_memory_major_page_faults": "k8s.node.memory.major_page_faults",
|
||||
"k8s_node_filesystem_available": "k8s.node.filesystem.available",
|
||||
"k8s_node_filesystem_capacity": "k8s.node.filesystem.capacity",
|
||||
"k8s_node_filesystem_usage": "k8s.node.filesystem.usage",
|
||||
"k8s_node_network_io": "k8s.node.network.io",
|
||||
"k8s_node_network_errors": "k8s.node.network.errors",
|
||||
"k8s_node_uptime": "k8s.node.uptime",
|
||||
|
||||
"k8s_pod_cpu_usage": "k8s.pod.cpu.usage",
|
||||
"k8s_pod_cpu_time": "k8s.pod.cpu.time",
|
||||
"k8s_pod_memory_available": "k8s.pod.memory.available",
|
||||
"k8s_pod_cpu_node_utilization": "k8s.pod.cpu.node.utilization",
|
||||
"k8s_pod_memory_node_utilization": "k8s.pod.memory.node.utilization",
|
||||
"k8s_pod_memory_rss": "k8s.pod.memory.rss",
|
||||
"k8s_pod_memory_working_set": "k8s.pod.memory.working_set",
|
||||
"k8s_pod_memory_page_faults": "k8s.pod.memory.page_faults",
|
||||
"k8s_pod_memory_major_page_faults": "k8s.pod.memory.major_page_faults",
|
||||
"k8s_pod_filesystem_available": "k8s.pod.filesystem.available",
|
||||
"k8s_pod_filesystem_capacity": "k8s.pod.filesystem.capacity",
|
||||
"k8s_pod_filesystem_usage": "k8s.pod.filesystem.usage",
|
||||
"k8s_pod_network_io": "k8s.pod.network.io",
|
||||
"k8s_pod_network_errors": "k8s.pod.network.errors",
|
||||
"k8s_pod_uptime": "k8s.pod.uptime",
|
||||
|
||||
"container_cpu_usage": "container.cpu.usage",
|
||||
"container_cpu_time": "container.cpu.time",
|
||||
"container_memory_available": "container.memory.available",
|
||||
"container_memory_usage": "container.memory.usage",
|
||||
"k8s_container_cpu_node_utilization": "k8s.container.cpu.node.utilization",
|
||||
"k8s_container_cpu_limit_utilization": "k8s.container.cpu_limit_utilization",
|
||||
"k8s_container_cpu_request_utilization": "k8s.container.cpu_request_utilization",
|
||||
"k8s_container_memory_node_utilization": "k8s.container.memory.node.utilization",
|
||||
"k8s_container_memory_limit_utilization": "k8s.container.memory_limit_utilization",
|
||||
"k8s_container_memory_request_utilization": "k8s.container.memory_request_utilization",
|
||||
"container_memory_rss": "container.memory.rss",
|
||||
"container_memory_working_set": "container.memory.working_set",
|
||||
"container_memory_page_faults": "container.memory.page_faults",
|
||||
"container_memory_major_page_faults": "container.memory.major_page_faults",
|
||||
"container_filesystem_available": "container.filesystem.available",
|
||||
"container_filesystem_capacity": "container.filesystem.capacity",
|
||||
"container_filesystem_usage": "container.filesystem.usage",
|
||||
"container_uptime": "container.uptime",
|
||||
|
||||
"k8s_volume_inodes_used": "k8s.volume.inodes.used",
|
||||
|
||||
"k8s_namespace_uid": "k8s.namespace.uid",
|
||||
"container_image_name": "container.image.name",
|
||||
"container_image_tag": "container.image.tag",
|
||||
"k8s_pod_qos_class": "k8s.pod.qos_class",
|
||||
"k8s_replicaset_name": "k8s.replicaset.name",
|
||||
"k8s_replicaset_uid": "k8s.replicaset.uid",
|
||||
"k8s_replicationcontroller_name": "k8s.replicationcontroller.name",
|
||||
"k8s_replicationcontroller_uid": "k8s.replicationcontroller.uid",
|
||||
"k8s_resourcequota_uid": "k8s.resourcequota.uid",
|
||||
"k8s_resourcequota_name": "k8s.resourcequota.name",
|
||||
"k8s_statefulset_uid": "k8s.statefulset.uid",
|
||||
"k8s_statefulset_name": "k8s.statefulset.name",
|
||||
"k8s_deployment_uid": "k8s.deployment.uid",
|
||||
"k8s_cronjob_uid": "k8s.cronjob.uid",
|
||||
"k8s_daemonset_uid": "k8s.daemonset.uid",
|
||||
"k8s_hpa_uid": "k8s.hpa.uid",
|
||||
"k8s_hpa_name": "k8s.hpa.name",
|
||||
"k8s_hpa_scaletargetref_kind": "k8s.hpa.scaletargetref.kind",
|
||||
"k8s_hpa_scaletargetref_name": "k8s.hpa.scaletargetref.name",
|
||||
"k8s_hpa_scaletargetref_apiversion": "k8s.hpa.scaletargetref.apiversion",
|
||||
"k8s_job_uid": "k8s.job.uid",
|
||||
"k8s_kubelet_version": "k8s.kubelet.version",
|
||||
"container_runtime": "container.runtime",
|
||||
"container_runtime_version": "container.runtime.version",
|
||||
"os_description": "os.description",
|
||||
"openshift_clusterquota_uid": "openshift.clusterquota.uid",
|
||||
"openshift_clusterquota_name": "openshift.clusterquota.name",
|
||||
"k8s_container_status_last_terminated_reason": "k8s.container.status.last_terminated_reason",
|
||||
|
||||
"resource": "resource",
|
||||
"condition": "condition",
|
||||
|
||||
"k8s_container_cpu_request": "k8s.container.cpu_request",
|
||||
"k8s_container_cpu_limit": "k8s.container.cpu_limit",
|
||||
"k8s_container_memory_request": "k8s.container.memory_request",
|
||||
"k8s_container_memory_limit": "k8s.container.memory_limit",
|
||||
"k8s_container_storage_request": "k8s.container.storage_request",
|
||||
"k8s_container_storage_limit": "k8s.container.storage_limit",
|
||||
"k8s_container_ephemeralstorage_request": "k8s.container.ephemeralstorage_request",
|
||||
"k8s_container_ephemeralstorage_limit": "k8s.container.ephemeralstorage_limit",
|
||||
"k8s_container_ready": "k8s.container.ready",
|
||||
|
||||
"k8s_pod_status_reason": "k8s.pod.status_reason",
|
||||
|
||||
"k8s_cronjob_active_jobs": "k8s.cronjob.active_jobs",
|
||||
|
||||
"k8s_daemonset_misscheduled_nodes": "k8s.daemonset.misscheduled_nodes",
|
||||
"k8s_daemonset_ready_nodes": "k8s.daemonset.ready_nodes",
|
||||
|
||||
"k8s_hpa_max_replicas": "k8s.hpa.max_replicas",
|
||||
"k8s_hpa_min_replicas": "k8s.hpa.min_replicas",
|
||||
"k8s_hpa_current_replicas": "k8s.hpa.current_replicas",
|
||||
"k8s_hpa_desired_replicas": "k8s.hpa.desired_replicas",
|
||||
|
||||
"k8s_job_max_parallel_pods": "k8s.job.max_parallel_pods",
|
||||
|
||||
"k8s_namespace_phase": "k8s.namespace.phase",
|
||||
|
||||
"k8s_replicaset_desired": "k8s.replicaset.desired",
|
||||
"k8s_replicaset_available": "k8s.replicaset.available",
|
||||
|
||||
"k8s_replication_controller_desired": "k8s.replication_controller.desired",
|
||||
"k8s_replication_controller_available": "k8s.replication_controller.available",
|
||||
|
||||
"k8s_resource_quota_hard_limit": "k8s.resource_quota.hard_limit",
|
||||
"k8s_resource_quota_used": "k8s.resource_quota.used",
|
||||
|
||||
"k8s_statefulset_updated_pods": "k8s.statefulset.updated_pods",
|
||||
|
||||
"k8s_node_condition": "k8s.node.condition",
|
||||
}
|
||||
|
||||
const fromWhereQuery = `
|
||||
FROM %s.%s
|
||||
WHERE metric_name IN (%s)
|
||||
@@ -18,39 +262,39 @@ WHERE metric_name IN (%s)
|
||||
var (
|
||||
// TODO(srikanthccv): import metadata yaml from receivers and use generated files to check the metrics
|
||||
podMetricNamesToCheck = []string{
|
||||
"k8s.pod.cpu.usage",
|
||||
"k8s.pod.memory.working_set",
|
||||
"k8s.pod.cpu_request_utilization",
|
||||
"k8s.pod.memory_request_utilization",
|
||||
"k8s.pod.cpu_limit_utilization",
|
||||
"k8s.pod.memory_limit_utilization",
|
||||
"k8s.container.restarts",
|
||||
"k8s.pod.phase",
|
||||
GetDotMetrics("k8s_pod_cpu_usage"),
|
||||
GetDotMetrics("k8s_pod_memory_working_set"),
|
||||
GetDotMetrics("k8s_pod_cpu_request_utilization"),
|
||||
GetDotMetrics("k8s_pod_memory_request_utilization"),
|
||||
GetDotMetrics("k8s_pod_cpu_limit_utilization"),
|
||||
GetDotMetrics("k8s_pod_memory_limit_utilization"),
|
||||
GetDotMetrics("k8s_container_restarts"),
|
||||
GetDotMetrics("k8s_pod_phase"),
|
||||
}
|
||||
nodeMetricNamesToCheck = []string{
|
||||
"k8s.node.cpu.usage",
|
||||
"k8s.node.allocatable_cpu",
|
||||
"k8s.node.memory.working_set",
|
||||
"k8s.node.allocatable_memory",
|
||||
"k8s.node.condition_ready",
|
||||
GetDotMetrics("k8s_node_cpu_usage"),
|
||||
GetDotMetrics("k8s_node_allocatable_cpu"),
|
||||
GetDotMetrics("k8s_node_memory_working_set"),
|
||||
GetDotMetrics("k8s_node_allocatable_memory"),
|
||||
GetDotMetrics("k8s_node_condition_ready"),
|
||||
}
|
||||
clusterMetricNamesToCheck = []string{
|
||||
"k8s.daemonset.desired_scheduled_nodes",
|
||||
"k8s.daemonset.current_scheduled_nodes",
|
||||
"k8s.deployment.desired",
|
||||
"k8s.deployment.available",
|
||||
"k8s.job.desired_successful_pods",
|
||||
"k8s.job.active_pods",
|
||||
"k8s.job.failed_pods",
|
||||
"k8s.job.successful_pods",
|
||||
"k8s.statefulset.desired_pods",
|
||||
"k8s.statefulset.current_pods",
|
||||
GetDotMetrics("k8s_daemonset_desired_scheduled_nodes"),
|
||||
GetDotMetrics("k8s_daemonset_current_scheduled_nodes"),
|
||||
GetDotMetrics("k8s_deployment_desired"),
|
||||
GetDotMetrics("k8s_deployment_available"),
|
||||
GetDotMetrics("k8s_job_desired_successful_pods"),
|
||||
GetDotMetrics("k8s_job_active_pods"),
|
||||
GetDotMetrics("k8s_job_failed_pods"),
|
||||
GetDotMetrics("k8s_job_successful_pods"),
|
||||
GetDotMetrics("k8s_statefulset_desired_pods"),
|
||||
GetDotMetrics("k8s_statefulset_current_pods"),
|
||||
}
|
||||
optionalPodMetricNamesToCheck = []string{
|
||||
"k8s.pod.cpu_request_utilization",
|
||||
"k8s.pod.memory_request_utilization",
|
||||
"k8s.pod.cpu_limit_utilization",
|
||||
"k8s.pod.memory_limit_utilization",
|
||||
GetDotMetrics("k8s_pod_cpu_request_utilization"),
|
||||
GetDotMetrics("k8s_pod_memory_request_utilization"),
|
||||
GetDotMetrics("k8s_pod_cpu_limit_utilization"),
|
||||
GetDotMetrics("k8s_pod_memory_limit_utilization"),
|
||||
}
|
||||
|
||||
// did they ever send _any_ pod metrics?
|
||||
@@ -88,15 +332,15 @@ SELECT
|
||||
any(JSONExtractString(labels, '%s')) as k8s_job_name,
|
||||
JSONExtractString(labels, '%s') as k8s_pod_name
|
||||
`,
|
||||
"k8s.cluster.name",
|
||||
"k8s.node.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.deployment.name",
|
||||
"k8s.statefulset.name",
|
||||
"k8s.daemonset.name",
|
||||
"k8s.cronjob.name",
|
||||
"k8s.job.name",
|
||||
"k8s.pod.name",
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
GetDotMetrics("k8s_node_name"),
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_deployment_name"),
|
||||
GetDotMetrics("k8s_statefulset_name"),
|
||||
GetDotMetrics("k8s_daemonset_name"),
|
||||
GetDotMetrics("k8s_cronjob_name"),
|
||||
GetDotMetrics("k8s_job_name"),
|
||||
GetDotMetrics("k8s_pod_name"),
|
||||
)
|
||||
|
||||
filterGroupQuery = fmt.Sprintf(`
|
||||
@@ -105,7 +349,7 @@ AND JSONExtractString(labels, '%s')
|
||||
GROUP BY k8s_pod_name
|
||||
LIMIT 1 BY k8s_cluster_name, k8s_node_name, k8s_namespace_name
|
||||
`,
|
||||
"k8s.namespace.name",
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
)
|
||||
|
||||
isSendingRequiredMetadataQuery = selectQuery + fromWhereQuery + filterGroupQuery
|
||||
@@ -206,3 +450,12 @@ func getParamsForTopVolumes(req model.VolumeListRequest) (int64, string, string)
|
||||
func localQueryToDistributedQuery(query string) string {
|
||||
return strings.Replace(query, ".time_series_v4", ".distributed_time_series_v4", 1)
|
||||
}
|
||||
|
||||
func GetDotMetrics(key string) string {
|
||||
if constants.IsDotMetricsEnabled {
|
||||
if _, ok := dotMetricMap[key]; ok {
|
||||
return dotMetricMap[key]
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
@@ -18,18 +18,18 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForDaemonSets = "k8s.pod.cpu.usage"
|
||||
k8sDaemonSetNameAttrKey = "k8s.daemonset.name"
|
||||
metricToUseForDaemonSets = GetDotMetrics("k8s_pod_cpu_usage")
|
||||
k8sDaemonSetNameAttrKey = GetDotMetrics("k8s_daemonset_name")
|
||||
|
||||
metricNamesForDaemonSets = map[string]string{
|
||||
"desired_nodes": "k8s.daemonset.desired_scheduled_nodes",
|
||||
"available_nodes": "k8s.daemonset.current_scheduled_nodes",
|
||||
"desired_nodes": GetDotMetrics("k8s_daemonset_desired_scheduled_nodes"),
|
||||
"available_nodes": GetDotMetrics("k8s_daemonset_current_scheduled_nodes"),
|
||||
}
|
||||
|
||||
daemonSetAttrsToEnrich = []string{
|
||||
"k8s.daemonset.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.cluster.name",
|
||||
GetDotMetrics("k8s_daemonset_name"),
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
}
|
||||
|
||||
queryNamesForDaemonSets = map[string][]string{
|
||||
|
||||
@@ -18,18 +18,18 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForDeployments = "k8s.pod.cpu.usage"
|
||||
k8sDeploymentNameAttrKey = "k8s.deployment.name"
|
||||
metricToUseForDeployments = GetDotMetrics("k8s_pod_cpu_usage")
|
||||
k8sDeploymentNameAttrKey = GetDotMetrics("k8s_deployment_name")
|
||||
|
||||
metricNamesForDeployments = map[string]string{
|
||||
"desired_pods": "k8s.deployment.desired",
|
||||
"available_pods": "k8s.deployment.available",
|
||||
"desired_pods": GetDotMetrics("k8s_deployment_desired"),
|
||||
"available_pods": GetDotMetrics("k8s_deployment_available"),
|
||||
}
|
||||
|
||||
deploymentAttrsToEnrich = []string{
|
||||
"k8s.deployment.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.cluster.name",
|
||||
GetDotMetrics("k8s_deployment_name"),
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
}
|
||||
|
||||
queryNamesForDeployments = map[string][]string{
|
||||
|
||||
@@ -45,15 +45,15 @@ var (
|
||||
"mode",
|
||||
"mountpoint",
|
||||
"type",
|
||||
"os.type",
|
||||
"process.cgroup",
|
||||
"process.command",
|
||||
"process.command_line",
|
||||
"process.executable.name",
|
||||
"process.executable.path",
|
||||
"process.owner",
|
||||
"process.parent_pid",
|
||||
"process.pid",
|
||||
GetDotMetrics("os_type"),
|
||||
GetDotMetrics("process_cgroup"),
|
||||
GetDotMetrics("process_command"),
|
||||
GetDotMetrics("process_command_line"),
|
||||
GetDotMetrics("process_executable_name"),
|
||||
GetDotMetrics("process_executable_path"),
|
||||
GetDotMetrics("process_owner"),
|
||||
GetDotMetrics("process_parent_pid"),
|
||||
GetDotMetrics("process_pid"),
|
||||
}
|
||||
|
||||
queryNamesForTopHosts = map[string][]string{
|
||||
@@ -64,65 +64,65 @@ var (
|
||||
}
|
||||
|
||||
// TODO(srikanthccv): remove hardcoded metric name and support keys from any system metric
|
||||
metricToUseForHostAttributes = "system.cpu.load_average.15m"
|
||||
hostNameAttrKey = "host.name"
|
||||
metricToUseForHostAttributes = GetDotMetrics("system_cpu_load_average_15m")
|
||||
hostNameAttrKey = GetDotMetrics("host_name")
|
||||
agentNameToIgnore = "k8s-infra-otel-agent"
|
||||
hostAttrsToEnrich = []string{
|
||||
"os.type",
|
||||
GetDotMetrics("os_type"),
|
||||
}
|
||||
metricNamesForHosts = map[string]string{
|
||||
"filesystem": "system.filesystem.usage",
|
||||
"cpu": "system.cpu.time",
|
||||
"memory": "system.memory.usage",
|
||||
"load15": "system.cpu.load_average.15m",
|
||||
"wait": "system.cpu.time",
|
||||
"filesystem": GetDotMetrics("system_filesystem_usage"),
|
||||
"cpu": GetDotMetrics("system_cpu_time"),
|
||||
"memory": GetDotMetrics("system_memory_usage"),
|
||||
"load15": GetDotMetrics("system_cpu_load_average_15m"),
|
||||
"wait": GetDotMetrics("system_cpu_time"),
|
||||
}
|
||||
uniqueMetricNamesForHosts = []string{
|
||||
"system.uptime",
|
||||
"system.cpu.time",
|
||||
"system.cpu.load_average.1m",
|
||||
"system.cpu.load_average.5m",
|
||||
"system.cpu.load_average.15m",
|
||||
"system.memory.usage",
|
||||
"system.paging.usage",
|
||||
"system.paging.faults",
|
||||
"system.paging.operations",
|
||||
"system.disk.io",
|
||||
"system.disk.operations",
|
||||
"system.disk.io_time",
|
||||
"system.disk.operation_time",
|
||||
"system.disk.merged",
|
||||
"system.disk.pending_operations",
|
||||
"system.disk.weighted_io_time",
|
||||
"system.filesystem.usage",
|
||||
"system.filesystem.inodes.usage",
|
||||
"system.network.io",
|
||||
"system.network.errors",
|
||||
"system.network.connections",
|
||||
"system.network.dropped",
|
||||
"system.network.packets",
|
||||
"system.processes.count",
|
||||
"system.processes.created",
|
||||
"process.cpu.time",
|
||||
"process.disk.io",
|
||||
"process.memory.usage",
|
||||
"process.memory.virtual",
|
||||
"nfs.client.net.count",
|
||||
"nfs.client.net.tcp.connection.accepted",
|
||||
"nfs.client.operation.count",
|
||||
"nfs.client.procedure.count",
|
||||
"nfs.client.rpc.authrefresh.count",
|
||||
"nfs.client.rpc.count",
|
||||
"nfs.client.rpc.retransmit.count",
|
||||
"nfs.server.fh.stale.count",
|
||||
"nfs.server.io",
|
||||
"nfs.server.net.count",
|
||||
"nfs.server.net.tcp.connection.accepted",
|
||||
"nfs.server.operation.count",
|
||||
"nfs.server.procedure.count",
|
||||
"nfs.server.repcache.requests",
|
||||
"nfs.server.rpc.count",
|
||||
"nfs.server.thread.count",
|
||||
GetDotMetrics("system_uptime"),
|
||||
GetDotMetrics("system_cpu_time"),
|
||||
GetDotMetrics("system_cpu_load_average_1m"),
|
||||
GetDotMetrics("system_cpu_load_average_5m"),
|
||||
GetDotMetrics("system_cpu_load_average_15m"),
|
||||
GetDotMetrics("system_memory_usage"),
|
||||
GetDotMetrics("system_paging_usage"),
|
||||
GetDotMetrics("system_paging_faults"),
|
||||
GetDotMetrics("system_paging_operations"),
|
||||
GetDotMetrics("system_disk_io"),
|
||||
GetDotMetrics("system_disk_operations"),
|
||||
GetDotMetrics("system_disk_io_time"),
|
||||
GetDotMetrics("system_disk_operation_time"),
|
||||
GetDotMetrics("system_disk_merged"),
|
||||
GetDotMetrics("system_disk_pending_operations"),
|
||||
GetDotMetrics("system_disk_weighted_io_time"),
|
||||
GetDotMetrics("system_filesystem_usage"),
|
||||
GetDotMetrics("system_filesystem_inodes_usage"),
|
||||
GetDotMetrics("system_network_io"),
|
||||
GetDotMetrics("system_network_errors"),
|
||||
GetDotMetrics("system_network_connections"),
|
||||
GetDotMetrics("system_network_dropped"),
|
||||
GetDotMetrics("system_network_packets"),
|
||||
GetDotMetrics("system_processes_count"),
|
||||
GetDotMetrics("system_processes_created"),
|
||||
GetDotMetrics("process_cpu_time"),
|
||||
GetDotMetrics("process_disk_io"),
|
||||
GetDotMetrics("process_memory_usage"),
|
||||
GetDotMetrics("process_memory_virtual"),
|
||||
GetDotMetrics("nfs_client_net_count"),
|
||||
GetDotMetrics("nfs_client_net_tcp_connection_accepted"),
|
||||
GetDotMetrics("nfs_client_operation_count"),
|
||||
GetDotMetrics("nfs_client_procedure_count"),
|
||||
GetDotMetrics("nfs_client_rpc_authrefresh_count"),
|
||||
GetDotMetrics("nfs_client_rpc_count"),
|
||||
GetDotMetrics("nfs_client_rpc_retransmit_count"),
|
||||
GetDotMetrics("nfs_server_fh_stale_count"),
|
||||
GetDotMetrics("nfs_server_io"),
|
||||
GetDotMetrics("nfs_server_net_count"),
|
||||
GetDotMetrics("nfs_server_net_tcp_connection_accepted"),
|
||||
GetDotMetrics("nfs_server_operation_count"),
|
||||
GetDotMetrics("nfs_server_procedure_count"),
|
||||
GetDotMetrics("nfs_server_repcache_requests"),
|
||||
GetDotMetrics("nfs_server_rpc_count"),
|
||||
GetDotMetrics("nfs_server_thread_count"),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -351,8 +351,8 @@ func (h *HostsRepo) IsSendingK8SAgentMetrics(ctx context.Context, req model.Host
|
||||
AND unix_milli >= toUnixTimestamp(now() - INTERVAL 60 MINUTE) * 1000
|
||||
AND JSONExtractString(labels, '%s') LIKE '%%-otel-agent%%'
|
||||
AND fingerprint GLOBAL IN (%s)`,
|
||||
"k8s.cluster.name", "k8s.node.name",
|
||||
constants.SIGNOZ_METRIC_DBNAME, constants.SIGNOZ_TIMESERIES_V4_TABLENAME, namesStr, "host.name", queryForRecentFingerprints)
|
||||
GetDotMetrics("k8s_cluster_name"), GetDotMetrics("k8s_node_name"),
|
||||
constants.SIGNOZ_METRIC_DBNAME, constants.SIGNOZ_TIMESERIES_V4_TABLENAME, namesStr, GetDotMetrics("host_name"), queryForRecentFingerprints)
|
||||
|
||||
result, err := h.reader.GetListResultV3(ctx, query)
|
||||
if err != nil {
|
||||
@@ -363,13 +363,13 @@ func (h *HostsRepo) IsSendingK8SAgentMetrics(ctx context.Context, req model.Host
|
||||
nodeNames := make(map[string]struct{})
|
||||
|
||||
for _, row := range result {
|
||||
switch v := row.Data["k8s.cluster.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_cluster_name")].(type) {
|
||||
case string:
|
||||
clusterNames[v] = struct{}{}
|
||||
case *string:
|
||||
clusterNames[*v] = struct{}{}
|
||||
}
|
||||
switch v := row.Data["k8s.node.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_node_name")].(type) {
|
||||
case string:
|
||||
nodeNames[v] = struct{}{}
|
||||
case *string:
|
||||
@@ -535,7 +535,7 @@ func (h *HostsRepo) GetHostList(ctx context.Context, orgID valuer.UUID, req mode
|
||||
if _, ok := hostAttrs[record.HostName]; ok {
|
||||
record.Meta = hostAttrs[record.HostName]
|
||||
}
|
||||
if osType, ok := record.Meta["os.type"]; ok {
|
||||
if osType, ok := record.Meta[GetDotMetrics("os_type")]; ok {
|
||||
record.OS = osType
|
||||
}
|
||||
record.Active = activeHosts[record.HostName]
|
||||
|
||||
@@ -18,20 +18,20 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForJobs = "k8s.job.desired_successful_pods"
|
||||
k8sJobNameAttrKey = "k8s.job.name"
|
||||
metricToUseForJobs = GetDotMetrics("k8s_job_desired_successful_pods")
|
||||
k8sJobNameAttrKey = GetDotMetrics("k8s_job_name")
|
||||
|
||||
metricNamesForJobs = map[string]string{
|
||||
"desired_successful_pods": "k8s.job.desired_successful_pods",
|
||||
"active_pods": "k8s.job.active_pods",
|
||||
"failed_pods": "k8s.job.failed_pods",
|
||||
"successful_pods": "k8s.job.successful_pods",
|
||||
"desired_successful_pods": GetDotMetrics("k8s_job_desired_successful_pods"),
|
||||
"active_pods": GetDotMetrics("k8s_job_active_pods"),
|
||||
"failed_pods": GetDotMetrics("k8s_job_failed_pods"),
|
||||
"successful_pods": GetDotMetrics("k8s_job_successful_pods"),
|
||||
}
|
||||
|
||||
jobAttrsToEnrich = []string{
|
||||
"k8s.job.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.cluster.name",
|
||||
GetDotMetrics("k8s_job_name"),
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
}
|
||||
|
||||
queryNamesForJobs = map[string][]string{
|
||||
@@ -54,7 +54,7 @@ var (
|
||||
QueryName: "H",
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: metricNamesForJobs["desired_successful_pods"],
|
||||
Key: GetDotMetrics(metricNamesForJobs["desired_successful_pods"]),
|
||||
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||
},
|
||||
Temporality: v3.Unspecified,
|
||||
@@ -74,7 +74,7 @@ var (
|
||||
QueryName: "I",
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: metricNamesForJobs["active_pods"],
|
||||
Key: GetDotMetrics(metricNamesForJobs["active_pods"]),
|
||||
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||
},
|
||||
Temporality: v3.Unspecified,
|
||||
@@ -94,7 +94,7 @@ var (
|
||||
QueryName: "J",
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: metricNamesForJobs["failed_pods"],
|
||||
Key: GetDotMetrics(metricNamesForJobs["failed_pods"]),
|
||||
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||
},
|
||||
Temporality: v3.Unspecified,
|
||||
@@ -114,7 +114,7 @@ var (
|
||||
QueryName: "K",
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: metricNamesForJobs["successful_pods"],
|
||||
Key: GetDotMetrics(metricNamesForJobs["successful_pods"]),
|
||||
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||
},
|
||||
Temporality: v3.Unspecified,
|
||||
@@ -327,7 +327,7 @@ func (d *JobsRepo) GetJobList(ctx context.Context, orgID valuer.UUID, req model.
|
||||
}
|
||||
|
||||
if req.OrderBy == nil {
|
||||
req.OrderBy = &v3.OrderBy{ColumnName: "desired_pods", Order: v3.DirectionDesc}
|
||||
req.OrderBy = &v3.OrderBy{ColumnName: GetDotMetrics("desired_pods"), Order: v3.DirectionDesc}
|
||||
}
|
||||
|
||||
if req.GroupBy == nil {
|
||||
|
||||
@@ -18,11 +18,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForNamespaces = "k8s.pod.cpu.usage"
|
||||
metricToUseForNamespaces = GetDotMetrics("k8s_pod_cpu_usage")
|
||||
|
||||
namespaceAttrsToEnrich = []string{
|
||||
"k8s.namespace.name",
|
||||
"k8s.cluster.name",
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
}
|
||||
|
||||
queryNamesForNamespaces = map[string][]string{
|
||||
@@ -33,11 +33,11 @@ var (
|
||||
namespaceQueryNames = []string{"A", "D", "H", "I", "J", "K"}
|
||||
|
||||
attributesKeysForNamespaces = []v3.AttributeKey{
|
||||
{Key: "k8s.namespace.name"},
|
||||
{Key: "k8s.cluster.name"},
|
||||
{Key: GetDotMetrics("k8s_namespace_name")},
|
||||
{Key: GetDotMetrics("k8s_cluster_name")},
|
||||
}
|
||||
|
||||
k8sNamespaceNameAttrKey = "k8s.namespace.name"
|
||||
k8sNamespaceNameAttrKey = GetDotMetrics("k8s_namespace_name")
|
||||
)
|
||||
|
||||
type NamespacesRepo struct {
|
||||
|
||||
@@ -21,11 +21,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForNodes = "k8s.node.cpu.usage"
|
||||
metricToUseForNodes = GetDotMetrics("k8s_node_cpu_usage")
|
||||
|
||||
nodeAttrsToEnrich = []string{"k8s.node.name", "k8s.node.uid", "k8s.cluster.name"}
|
||||
nodeAttrsToEnrich = []string{GetDotMetrics("k8s_node_name"), GetDotMetrics("k8s_node_uid"), GetDotMetrics("k8s_cluster_name")}
|
||||
|
||||
k8sNodeGroupAttrKey = "k8s.node.name"
|
||||
k8sNodeGroupAttrKey = GetDotMetrics("k8s_node_name")
|
||||
|
||||
queryNamesForNodes = map[string][]string{
|
||||
"cpu": {"A"},
|
||||
@@ -36,11 +36,11 @@ var (
|
||||
nodeQueryNames = []string{"A", "B", "C", "D", "E", "F"}
|
||||
|
||||
metricNamesForNodes = map[string]string{
|
||||
"cpu": "k8s.node.cpu.usage",
|
||||
"cpu_allocatable": "k8s.node.allocatable_cpu",
|
||||
"memory": "k8s.node.memory.working_set",
|
||||
"memory_allocatable": "k8s.node.allocatable_memory",
|
||||
"node_condition": "k8s.node.condition_ready",
|
||||
"cpu": GetDotMetrics("k8s_node_cpu_usage"),
|
||||
"cpu_allocatable": GetDotMetrics("k8s_node_allocatable_cpu"),
|
||||
"memory": GetDotMetrics("k8s_node_memory_working_set"),
|
||||
"memory_allocatable": GetDotMetrics("k8s_node_allocatable_memory"),
|
||||
"node_condition": GetDotMetrics("k8s_node_condition_ready"),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -21,22 +21,22 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForPods = "k8s.pod.cpu.usage"
|
||||
metricToUseForPods = GetDotMetrics("k8s_pod_cpu_usage")
|
||||
|
||||
podAttrsToEnrich = []string{
|
||||
"k8s.pod.uid",
|
||||
"k8s.pod.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.node.name",
|
||||
"k8s.deployment.name",
|
||||
"k8s.statefulset.name",
|
||||
"k8s.daemonset.name",
|
||||
"k8s.job.name",
|
||||
"k8s.cronjob.name",
|
||||
"k8s.cluster.name",
|
||||
GetDotMetrics("k8s_pod_uid"),
|
||||
GetDotMetrics("k8s_pod_name"),
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_node_name"),
|
||||
GetDotMetrics("k8s_deployment_name"),
|
||||
GetDotMetrics("k8s_statefulset_name"),
|
||||
GetDotMetrics("k8s_daemonset_name"),
|
||||
GetDotMetrics("k8s_job_name"),
|
||||
GetDotMetrics("k8s_cronjob_name"),
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
}
|
||||
|
||||
k8sPodUIDAttrKey = "k8s.pod.uid"
|
||||
k8sPodUIDAttrKey = GetDotMetrics("k8s_pod_uid")
|
||||
|
||||
queryNamesForPods = map[string][]string{
|
||||
"cpu": {"A"},
|
||||
@@ -51,14 +51,14 @@ var (
|
||||
podQueryNames = []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"}
|
||||
|
||||
metricNamesForPods = map[string]string{
|
||||
"cpu": "k8s.pod.cpu.usage",
|
||||
"cpu_request": "k8s.pod.cpu_request_utilization",
|
||||
"cpu_limit": "k8s.pod.cpu_limit_utilization",
|
||||
"memory": "k8s.pod.memory.working_set",
|
||||
"memory_request": "k8s.pod.memory_request_utilization",
|
||||
"memory_limit": "k8s.pod.memory_limit_utilization",
|
||||
"restarts": "k8s.container.restarts",
|
||||
"pod_phase": "k8s.pod.phase",
|
||||
"cpu": GetDotMetrics("k8s_pod_cpu_usage"),
|
||||
"cpu_request": GetDotMetrics("k8s_pod_cpu_request_utilization"),
|
||||
"cpu_limit": GetDotMetrics("k8s_pod_cpu_limit_utilization"),
|
||||
"memory": GetDotMetrics("k8s_pod_memory_working_set"),
|
||||
"memory_request": GetDotMetrics("k8s_pod_memory_request_utilization"),
|
||||
"memory_limit": GetDotMetrics("k8s_pod_memory_limit_utilization"),
|
||||
"restarts": GetDotMetrics("k8s_container_restarts"),
|
||||
"pod_phase": GetDotMetrics("k8s_pod_phase"),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -169,7 +169,7 @@ func (p *PodsRepo) SendingRequiredMetadata(ctx context.Context) ([]model.PodOnbo
|
||||
// for each pod, check if we have all the required metadata
|
||||
for _, row := range result {
|
||||
status := model.PodOnboardingStatus{}
|
||||
switch v := row.Data["k8s.cluster.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_cluster_name")].(type) {
|
||||
case string:
|
||||
status.HasClusterName = true
|
||||
status.ClusterName = v
|
||||
@@ -177,7 +177,7 @@ func (p *PodsRepo) SendingRequiredMetadata(ctx context.Context) ([]model.PodOnbo
|
||||
status.HasClusterName = *v != ""
|
||||
status.ClusterName = *v
|
||||
}
|
||||
switch v := row.Data["k8s.node.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_node_name")].(type) {
|
||||
case string:
|
||||
status.HasNodeName = true
|
||||
status.NodeName = v
|
||||
@@ -185,7 +185,7 @@ func (p *PodsRepo) SendingRequiredMetadata(ctx context.Context) ([]model.PodOnbo
|
||||
status.HasNodeName = *v != ""
|
||||
status.NodeName = *v
|
||||
}
|
||||
switch v := row.Data["k8s.namespace.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_namespace_name")].(type) {
|
||||
case string:
|
||||
status.HasNamespaceName = true
|
||||
status.NamespaceName = v
|
||||
@@ -193,38 +193,38 @@ func (p *PodsRepo) SendingRequiredMetadata(ctx context.Context) ([]model.PodOnbo
|
||||
status.HasNamespaceName = *v != ""
|
||||
status.NamespaceName = *v
|
||||
}
|
||||
switch v := row.Data["k8s.deployment.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_deployment_name")].(type) {
|
||||
case string:
|
||||
status.HasDeploymentName = true
|
||||
case *string:
|
||||
status.HasDeploymentName = *v != ""
|
||||
}
|
||||
switch v := row.Data["k8s.statefulset.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_statefulset_name")].(type) {
|
||||
case string:
|
||||
status.HasStatefulsetName = true
|
||||
case *string:
|
||||
status.HasStatefulsetName = *v != ""
|
||||
}
|
||||
switch v := row.Data["k8s.daemonset.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_daemonset_name")].(type) {
|
||||
case string:
|
||||
status.HasDaemonsetName = true
|
||||
case *string:
|
||||
status.HasDaemonsetName = *v != ""
|
||||
}
|
||||
switch v := row.Data["k8s.cronjob.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_cronjob_name")].(type) {
|
||||
case string:
|
||||
status.HasCronjobName = true
|
||||
case *string:
|
||||
status.HasCronjobName = *v != ""
|
||||
}
|
||||
switch v := row.Data["k8s.job.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_job_name")].(type) {
|
||||
case string:
|
||||
status.HasJobName = true
|
||||
case *string:
|
||||
status.HasJobName = *v != ""
|
||||
}
|
||||
|
||||
switch v := row.Data["k8s.pod.name"].(type) {
|
||||
switch v := row.Data[GetDotMetrics("k8s_pod_name")].(type) {
|
||||
case string:
|
||||
status.PodName = v
|
||||
case *string:
|
||||
|
||||
@@ -23,15 +23,15 @@ var (
|
||||
"memory": {"C"},
|
||||
}
|
||||
|
||||
processPIDAttrKey = "process.pid"
|
||||
processPIDAttrKey = GetDotMetrics("process_pid")
|
||||
metricNamesForProcesses = map[string]string{
|
||||
"cpu": "process.cpu.time",
|
||||
"memory": "process.memory.usage",
|
||||
"cpu": GetDotMetrics("process_cpu_time"),
|
||||
"memory": GetDotMetrics("process_memory_usage"),
|
||||
}
|
||||
metricToUseForProcessAttributes = "process.memory.usage"
|
||||
processNameAttrKey = "process.executable.name"
|
||||
processCMDAttrKey = "process.command"
|
||||
processCMDLineAttrKey = "process.command_line"
|
||||
metricToUseForProcessAttributes = GetDotMetrics("process_memory_usage")
|
||||
processNameAttrKey = GetDotMetrics("process_executable_name")
|
||||
processCMDAttrKey = GetDotMetrics("process_command")
|
||||
processCMDLineAttrKey = GetDotMetrics("process_command_line")
|
||||
)
|
||||
|
||||
type ProcessesRepo struct {
|
||||
@@ -46,7 +46,7 @@ func NewProcessesRepo(reader interfaces.Reader, querierV2 interfaces.Querier) *P
|
||||
func (p *ProcessesRepo) GetProcessAttributeKeys(ctx context.Context, orgID valuer.UUID, req v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error) {
|
||||
// TODO(srikanthccv): remove hardcoded metric name and support keys from any system metric
|
||||
req.DataSource = v3.DataSourceMetrics
|
||||
req.AggregateAttribute = "process.memory.usage"
|
||||
req.AggregateAttribute = GetDotMetrics("process_memory_usage")
|
||||
if req.Limit == 0 {
|
||||
req.Limit = 50
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func (p *ProcessesRepo) GetProcessAttributeKeys(ctx context.Context, orgID value
|
||||
|
||||
func (p *ProcessesRepo) GetProcessAttributeValues(ctx context.Context, orgID valuer.UUID, req v3.FilterAttributeValueRequest) (*v3.FilterAttributeValueResponse, error) {
|
||||
req.DataSource = v3.DataSourceMetrics
|
||||
req.AggregateAttribute = "process.memory.usage"
|
||||
req.AggregateAttribute = GetDotMetrics("process_memory_usage")
|
||||
if req.Limit == 0 {
|
||||
req.Limit = 50
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func (p *ProcessesRepo) getMetadataAttributes(ctx context.Context,
|
||||
req model.ProcessListRequest) (map[string]map[string]string, error) {
|
||||
processAttrs := map[string]map[string]string{}
|
||||
|
||||
keysToAdd := []string{"process.pid", "process.executable.name", "process.command", "process.command_line"}
|
||||
keysToAdd := []string{GetDotMetrics("process_pid"), GetDotMetrics("process_executable_name"), GetDotMetrics("process_command"), GetDotMetrics("process_command_line")}
|
||||
for _, key := range keysToAdd {
|
||||
hasKey := false
|
||||
for _, groupByKey := range req.GroupBy {
|
||||
|
||||
@@ -18,19 +18,19 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForVolumes = "k8s.volume.available"
|
||||
metricToUseForVolumes = GetDotMetrics("k8s_volume_available")
|
||||
|
||||
volumeAttrsToEnrich = []string{
|
||||
"k8s.pod.uid",
|
||||
"k8s.pod.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.node.name",
|
||||
"k8s.statefulset.name",
|
||||
"k8s.cluster.name",
|
||||
"k8s.persistentvolumeclaim.name",
|
||||
GetDotMetrics("k8s_pod_uid"),
|
||||
GetDotMetrics("k8s_pod_name"),
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_node_name"),
|
||||
GetDotMetrics("k8s_statefulset_name"),
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
GetDotMetrics("k8s_persistentvolumeclaim_name"),
|
||||
}
|
||||
|
||||
k8sPersistentVolumeClaimNameAttrKey = "k8s.persistentvolumeclaim.name"
|
||||
k8sPersistentVolumeClaimNameAttrKey = GetDotMetrics("k8s_persistentvolumeclaim_name")
|
||||
|
||||
queryNamesForVolumes = map[string][]string{
|
||||
"available": {"A"},
|
||||
@@ -44,11 +44,11 @@ var (
|
||||
volumeQueryNames = []string{"A", "B", "C", "D", "E", "F1"}
|
||||
|
||||
metricNamesForVolumes = map[string]string{
|
||||
"available": "k8s.volume.available",
|
||||
"capacity": "k8s.volume.capacity",
|
||||
"inodes": "k8s.volume.inodes",
|
||||
"inodes_free": "k8s.volume.inodes.free",
|
||||
"inodes_used": "k8s.volume.inodes.used",
|
||||
"available": GetDotMetrics("k8s_volume_available"),
|
||||
"capacity": GetDotMetrics("k8s_volume_capacity"),
|
||||
"inodes": GetDotMetrics("k8s_volume_inodes"),
|
||||
"inodes_free": GetDotMetrics("k8s_volume_inodes_free"),
|
||||
"inodes_used": GetDotMetrics("k8s_volume_inodes_used"),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -18,18 +18,18 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
metricToUseForStatefulSets = "k8s.pod.cpu.usage"
|
||||
k8sStatefulSetNameAttrKey = "k8s.statefulset.name"
|
||||
metricToUseForStatefulSets = GetDotMetrics("k8s_pod_cpu_usage")
|
||||
k8sStatefulSetNameAttrKey = GetDotMetrics("k8s_statefulset_name")
|
||||
|
||||
metricNamesForStatefulSets = map[string]string{
|
||||
"desired_pods": "k8s.statefulset.desired_pods",
|
||||
"available_pods": "k8s.statefulset.current_pods",
|
||||
"desired_pods": GetDotMetrics("k8s_statefulset_desired_pods"),
|
||||
"available_pods": GetDotMetrics("k8s_statefulset_current_pods"),
|
||||
}
|
||||
|
||||
statefulSetAttrsToEnrich = []string{
|
||||
"k8s.statefulset.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.cluster.name",
|
||||
GetDotMetrics("k8s_statefulset_name"),
|
||||
GetDotMetrics("k8s_namespace_name"),
|
||||
GetDotMetrics("k8s_cluster_name"),
|
||||
}
|
||||
|
||||
queryNamesForStatefulSets = map[string][]string{
|
||||
|
||||
@@ -4,13 +4,13 @@ import v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
|
||||
var (
|
||||
metricNamesForWorkloads = map[string]string{
|
||||
"cpu": "k8s.pod.cpu.usage",
|
||||
"cpu_request": "k8s.pod.cpu_request_utilization",
|
||||
"cpu_limit": "k8s.pod.cpu_limit_utilization",
|
||||
"memory": "k8s.pod.memory.working_set",
|
||||
"memory_request": "k8s.pod.memory_request_utilization",
|
||||
"memory_limit": "k8s.pod.memory_limit_utilization",
|
||||
"restarts": "k8s.container.restarts",
|
||||
"cpu": GetDotMetrics("k8s_pod_cpu_usage"),
|
||||
"cpu_request": GetDotMetrics("k8s_pod_cpu_request_utilization"),
|
||||
"cpu_limit": GetDotMetrics("k8s_pod_cpu_limit_utilization"),
|
||||
"memory": GetDotMetrics("k8s_pod_memory_working_set"),
|
||||
"memory_request": GetDotMetrics("k8s_pod_memory_request_utilization"),
|
||||
"memory_limit": GetDotMetrics("k8s_pod_memory_limit_utilization"),
|
||||
"restarts": GetDotMetrics("k8s_container_restarts"),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/query-service/common"
|
||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||
)
|
||||
|
||||
@@ -66,6 +67,11 @@ func buildBuilderQueriesProducerBytes(
|
||||
attributeCache *Clients,
|
||||
) (map[string]*v3.BuilderQuery, error) {
|
||||
|
||||
normalized := true
|
||||
if constants.IsDotMetricsEnabled {
|
||||
normalized = false
|
||||
}
|
||||
|
||||
bq := make(map[string]*v3.BuilderQuery)
|
||||
queryName := "byte_rate"
|
||||
|
||||
@@ -74,7 +80,7 @@ func buildBuilderQueriesProducerBytes(
|
||||
StepInterval: common.MinAllowedStepInterval(unixMilliStart, unixMilliEnd),
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: "kafka.producer.byte-rate",
|
||||
Key: getDotMetrics("kafka_producer_byte_rate", normalized),
|
||||
DataType: v3.AttributeKeyDataTypeFloat64,
|
||||
Type: v3.AttributeKeyType("Gauge"),
|
||||
IsColumn: true,
|
||||
@@ -88,7 +94,7 @@ func buildBuilderQueriesProducerBytes(
|
||||
Items: []v3.FilterItem{
|
||||
{
|
||||
Key: v3.AttributeKey{
|
||||
Key: "service.name",
|
||||
Key: getDotMetrics("service_name", normalized),
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
},
|
||||
@@ -110,7 +116,7 @@ func buildBuilderQueriesProducerBytes(
|
||||
ReduceTo: v3.ReduceToOperatorAvg,
|
||||
GroupBy: []v3.AttributeKey{
|
||||
{
|
||||
Key: "service.name",
|
||||
Key: getDotMetrics("service_name", normalized),
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
},
|
||||
@@ -133,12 +139,17 @@ func buildBuilderQueriesNetwork(
|
||||
bq := make(map[string]*v3.BuilderQuery)
|
||||
queryName := "latency"
|
||||
|
||||
normalized := true
|
||||
if constants.IsDotMetricsEnabled {
|
||||
normalized = false
|
||||
}
|
||||
|
||||
chq := &v3.BuilderQuery{
|
||||
QueryName: queryName,
|
||||
StepInterval: common.MinAllowedStepInterval(unixMilliStart, unixMilliEnd),
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: "kafka.consumer.fetch_latency_avg",
|
||||
Key: getDotMetrics("kafka_consumer_fetch_latency_avg", normalized),
|
||||
},
|
||||
AggregateOperator: v3.AggregateOperatorAvg,
|
||||
Temporality: v3.Unspecified,
|
||||
@@ -149,7 +160,7 @@ func buildBuilderQueriesNetwork(
|
||||
Items: []v3.FilterItem{
|
||||
{
|
||||
Key: v3.AttributeKey{
|
||||
Key: "service.name",
|
||||
Key: getDotMetrics("service_name", normalized),
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
},
|
||||
@@ -158,7 +169,7 @@ func buildBuilderQueriesNetwork(
|
||||
},
|
||||
{
|
||||
Key: v3.AttributeKey{
|
||||
Key: "client-id",
|
||||
Key: getDotMetrics("client_id", normalized),
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
},
|
||||
@@ -167,7 +178,7 @@ func buildBuilderQueriesNetwork(
|
||||
},
|
||||
{
|
||||
Key: v3.AttributeKey{
|
||||
Key: "service.instance.id",
|
||||
Key: getDotMetrics("service_instance_id", normalized),
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
},
|
||||
@@ -180,17 +191,17 @@ func buildBuilderQueriesNetwork(
|
||||
ReduceTo: v3.ReduceToOperatorAvg,
|
||||
GroupBy: []v3.AttributeKey{
|
||||
{
|
||||
Key: "service.name",
|
||||
Key: getDotMetrics("service_name", normalized),
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
},
|
||||
{
|
||||
Key: "client-id",
|
||||
Key: getDotMetrics("client_id", normalized),
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
},
|
||||
{
|
||||
Key: "service.instance.id",
|
||||
Key: getDotMetrics("service_instance_id", normalized),
|
||||
DataType: v3.AttributeKeyDataTypeString,
|
||||
Type: v3.AttributeKeyTypeTag,
|
||||
},
|
||||
@@ -207,12 +218,17 @@ func BuildBuilderQueriesKafkaOnboarding(messagingQueue *MessagingQueue) (*v3.Que
|
||||
unixMilliStart := messagingQueue.Start / 1000000
|
||||
unixMilliEnd := messagingQueue.End / 1000000
|
||||
|
||||
normalized := true
|
||||
if constants.IsDotMetricsEnabled {
|
||||
normalized = false
|
||||
}
|
||||
|
||||
buiderQuery := &v3.BuilderQuery{
|
||||
QueryName: "fetch_latency",
|
||||
StepInterval: common.MinAllowedStepInterval(unixMilliStart, unixMilliEnd),
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: "kafka.consumer.fetch_latency_avg",
|
||||
Key: getDotMetrics("kafka_consumer_fetch_latency_avg", normalized),
|
||||
},
|
||||
AggregateOperator: v3.AggregateOperatorCount,
|
||||
Temporality: v3.Unspecified,
|
||||
@@ -227,7 +243,7 @@ func BuildBuilderQueriesKafkaOnboarding(messagingQueue *MessagingQueue) (*v3.Que
|
||||
StepInterval: common.MinAllowedStepInterval(unixMilliStart, unixMilliEnd),
|
||||
DataSource: v3.DataSourceMetrics,
|
||||
AggregateAttribute: v3.AttributeKey{
|
||||
Key: "kafka.consumer_group.lag",
|
||||
Key: getDotMetrics("kafka_consumer_group_lag", normalized),
|
||||
},
|
||||
AggregateOperator: v3.AggregateOperatorCount,
|
||||
Temporality: v3.Unspecified,
|
||||
@@ -411,3 +427,19 @@ func buildCompositeQuery(chq *v3.ClickHouseQuery, queryContext string) (*v3.Comp
|
||||
PanelType: v3.PanelTypeTable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getDotMetrics(metricName string, normalized bool) string {
|
||||
dotMetricsMap := map[string]string{
|
||||
"kafka_producer_byte_rate": "kafka.producer.byte-rate",
|
||||
"service_name": "service.name",
|
||||
"kafka_consumer_fetch_latency_avg": "kafka.consumer.fetch_latency_avg",
|
||||
"service_instance_id": "service.instance.id",
|
||||
"client_id": "client-id",
|
||||
"kafka_consumer_group_lag": "kafka.consumer_group.lag",
|
||||
}
|
||||
if _, ok := dotMetricsMap[metricName]; ok && !normalized {
|
||||
return dotMetricsMap[metricName]
|
||||
} else {
|
||||
return metricName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,11 @@ func PrepareTimeseriesFilterQuery(start, end int64, mq *v3.BuilderQuery) (string
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name IN %s", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key)))
|
||||
conditions = append(conditions, fmt.Sprintf("temporality = '%s'", mq.Temporality))
|
||||
conditions = append(conditions, "__normalized = false")
|
||||
if constants.IsDotMetricsEnabled {
|
||||
conditions = append(conditions, "__normalized = false")
|
||||
} else {
|
||||
conditions = append(conditions, "__normalized = true")
|
||||
}
|
||||
|
||||
start, end, tableName := whichTSTableToUse(start, end, mq)
|
||||
|
||||
@@ -350,7 +354,11 @@ func PrepareTimeseriesFilterQueryV3(start, end int64, mq *v3.BuilderQuery) (stri
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name IN %s", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key)))
|
||||
conditions = append(conditions, fmt.Sprintf("temporality = '%s'", mq.Temporality))
|
||||
conditions = append(conditions, "__normalized = false")
|
||||
if constants.IsDotMetricsEnabled {
|
||||
conditions = append(conditions, "__normalized = false")
|
||||
} else {
|
||||
conditions = append(conditions, "__normalized = true")
|
||||
}
|
||||
|
||||
start, end, tableName := whichTSTableToUse(start, end, mq)
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
logsV4 "github.com/SigNoz/signoz/pkg/query-service/app/logs/v4"
|
||||
metricsV3 "github.com/SigNoz/signoz/pkg/query-service/app/metrics/v3"
|
||||
metricsV4 "github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4"
|
||||
@@ -275,3 +278,59 @@ func (q *querier) runBuilderQuery(
|
||||
Series: resultSeries,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateMetricNames function is used to print all those queries who are still using old normalized metrics and not new metrics.
|
||||
func (q *querier) ValidateMetricNames(ctx context.Context, query *v3.CompositeQuery, orgID valuer.UUID) {
|
||||
var metricNames []string
|
||||
switch query.QueryType {
|
||||
case v3.QueryTypePromQL:
|
||||
for _, query := range query.PromQueries {
|
||||
expr, err := q.parser.ParseExpr(query.Query)
|
||||
if err != nil {
|
||||
q.logger.DebugContext(ctx, "error parsing promql expression", "query", query.Query, errors.Attr(err))
|
||||
continue
|
||||
}
|
||||
parser.Inspect(expr, func(node parser.Node, path []parser.Node) error {
|
||||
if vs, ok := node.(*parser.VectorSelector); ok {
|
||||
for _, m := range vs.LabelMatchers {
|
||||
if m.Name == "__name__" {
|
||||
metricNames = append(metricNames, m.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
metrics, err := q.reader.GetNormalizedStatus(ctx, orgID, metricNames)
|
||||
if err != nil {
|
||||
q.logger.DebugContext(ctx, "error getting corresponding normalized metrics", errors.Attr(err))
|
||||
return
|
||||
}
|
||||
for metricName, metricPresent := range metrics {
|
||||
if metricPresent {
|
||||
continue
|
||||
} else {
|
||||
q.logger.WarnContext(ctx, "using normalized metric name", "metrics", metricName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
case v3.QueryTypeBuilder:
|
||||
for _, query := range query.BuilderQueries {
|
||||
metricName := query.AggregateAttribute.Key
|
||||
metricNames = append(metricNames, metricName)
|
||||
}
|
||||
metrics, err := q.reader.GetNormalizedStatus(ctx, orgID, metricNames)
|
||||
if err != nil {
|
||||
q.logger.DebugContext(ctx, "error getting corresponding normalized metrics", errors.Attr(err))
|
||||
return
|
||||
}
|
||||
for metricName, metricPresent := range metrics {
|
||||
if metricPresent {
|
||||
continue
|
||||
} else {
|
||||
q.logger.WarnContext(ctx, "using normalized metric name", "metrics", metricName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,6 +515,9 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, params *v3.
|
||||
var results []*v3.Result
|
||||
var err error
|
||||
var errQueriesByName map[string]error
|
||||
if !q.testingMode && q.reader != nil {
|
||||
q.ValidateMetricNames(ctx, params.CompositeQuery, orgID)
|
||||
}
|
||||
if params.CompositeQuery != nil {
|
||||
switch params.CompositeQuery.QueryType {
|
||||
case v3.QueryTypeBuilder:
|
||||
|
||||
@@ -212,7 +212,7 @@ func TestBuildQueryWithThreeOrMoreQueriesRefAndFormula(t *testing.T) {
|
||||
// So(queries["F5"], ShouldContainSubstring, "SELECT A.ts as ts, ((A.value - B.value) / B.value) * 100")
|
||||
// So(strings.Count(queries["F5"], " ON "), ShouldEqual, 1)
|
||||
})
|
||||
t.Run("TestBuildQueryWithMetricNameAndAttribute", func(t *testing.T) {
|
||||
t.Run("TestBuildQueryWithDotMetricNameAndAttribute", func(t *testing.T) {
|
||||
q := &v3.QueryRangeParamsV3{
|
||||
Start: 1735036101000,
|
||||
End: 1735637901000,
|
||||
|
||||
@@ -3,6 +3,7 @@ package constants
|
||||
import (
|
||||
"maps"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
@@ -25,6 +26,12 @@ const OrderBySpanCount = "span_count"
|
||||
var MetricsExplorerClickhouseThreads = GetOrDefaultEnvInt("METRICS_EXPLORER_CLICKHOUSE_THREADS", 8)
|
||||
var UpdatedMetricsMetadataCachePrefix = GetOrDefaultEnv("METRICS_UPDATED_METADATA_CACHE_KEY", "UPDATED_METRICS_METADATA")
|
||||
|
||||
const NormalizedMetricsMapCacheKey = "NORMALIZED_METRICS_MAP_CACHE_KEY"
|
||||
const NormalizedMetricsMapQueryThreads = 10
|
||||
|
||||
var NormalizedMetricsMapRegex = regexp.MustCompile(`[^a-zA-Z0-9]`)
|
||||
var NormalizedMetricsMapQuantileRegex = regexp.MustCompile(`(?i)([._-]?quantile.*)$`)
|
||||
|
||||
func GetEvalDelay() valuer.TextDuration {
|
||||
evalDelayStr := GetOrDefaultEnv("RULES_EVAL_DELAY", "2m")
|
||||
evalDelayDuration, err := valuer.ParseTextDuration(evalDelayStr)
|
||||
@@ -664,11 +671,16 @@ var OldToNewTraceFieldsMap = map[string]string{
|
||||
|
||||
var StaticFieldsTraces = map[string]v3.AttributeKey{}
|
||||
|
||||
var IsDotMetricsEnabled = false
|
||||
var MaxJSONFlatteningDepth = 1
|
||||
|
||||
func init() {
|
||||
StaticFieldsTraces = maps.Clone(NewStaticFieldsTraces)
|
||||
maps.Copy(StaticFieldsTraces, DeprecatedStaticFieldsTraces)
|
||||
if GetOrDefaultEnv(DotMetricsEnabled, "true") == "true" {
|
||||
IsDotMetricsEnabled = true
|
||||
}
|
||||
|
||||
// set max flattening depth
|
||||
depth, err := strconv.Atoi(GetOrDefaultEnv(maxJSONFlatteningDepth, "1"))
|
||||
if err == nil {
|
||||
@@ -696,4 +708,5 @@ var MaterializedDataTypeMap = map[string]string{
|
||||
|
||||
const InspectMetricsMaxTimeDiff = 1800000
|
||||
|
||||
const DotMetricsEnabled = "DOT_METRICS_ENABLED"
|
||||
const maxJSONFlatteningDepth = "MAX_JSON_FLATTENING_DEPTH"
|
||||
|
||||
@@ -108,6 +108,7 @@ type Reader interface {
|
||||
GetUpdatedMetricsMetadata(ctx context.Context, orgID valuer.UUID, metricNames ...string) (map[string]*model.UpdateMetricsMetadata, *model.ApiError)
|
||||
|
||||
CheckForLabelsInMetric(ctx context.Context, orgID valuer.UUID, metricName string, labels []string) (bool, *model.ApiError)
|
||||
GetNormalizedStatus(ctx context.Context, orgID valuer.UUID, metricNames []string) (map[string]bool, error)
|
||||
}
|
||||
|
||||
type Querier interface {
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
package metrics
|
||||
|
||||
var MetricsUnderTransition = map[string]string{
|
||||
"k8s_pod_cpu_utilization": "k8s_pod_cpu_usage",
|
||||
"k8s_node_cpu_utilization": "k8s_node_cpu_usage",
|
||||
"container_cpu_utilization": "container_cpu_usage",
|
||||
}
|
||||
|
||||
var DotMetricsUnderTransition = map[string]string{
|
||||
"k8s.pod.cpu.utilization": "k8s.pod.cpu.usage",
|
||||
"k8s.node.cpu.utilization": "k8s.node.cpu.usage",
|
||||
"container.cpu.utilization": "container.cpu.usage",
|
||||
}
|
||||
|
||||
func GetTransitionedMetric(metric string) string {
|
||||
if transitionedMetric, ok := MetricsUnderTransition[metric]; ok {
|
||||
return transitionedMetric
|
||||
func GetTransitionedMetric(metric string, normalized bool) string {
|
||||
if normalized {
|
||||
if _, ok := MetricsUnderTransition[metric]; ok {
|
||||
return MetricsUnderTransition[metric]
|
||||
}
|
||||
return metric
|
||||
} else {
|
||||
if _, ok := DotMetricsUnderTransition[metric]; ok {
|
||||
return DotMetricsUnderTransition[metric]
|
||||
}
|
||||
return metric
|
||||
}
|
||||
return metric
|
||||
}
|
||||
|
||||
15
pkg/query-service/model/normalizedMetrics.go
Normal file
15
pkg/query-service/model/normalizedMetrics.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package model
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type MetricsNormalizedMap struct {
|
||||
MetricName string `json:"metricName"`
|
||||
IsUnNormalized bool `json:"isUnNormalized"`
|
||||
}
|
||||
|
||||
func (c *MetricsNormalizedMap) MarshalBinary() (data []byte, err error) {
|
||||
return json.Marshal(c)
|
||||
}
|
||||
func (c *MetricsNormalizedMap) UnmarshalBinary(data []byte) error {
|
||||
return json.Unmarshal(data, c)
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func ClickHouseFormattedValue(v interface{}) string {
|
||||
|
||||
func ClickHouseFormattedMetricNames(v interface{}) string {
|
||||
if name, ok := v.(string); ok {
|
||||
transitionedMetrics := metrics.GetTransitionedMetric(name)
|
||||
transitionedMetrics := metrics.GetTransitionedMetric(name, !constants.IsDotMetricsEnabled)
|
||||
if transitionedMetrics != name {
|
||||
return ClickHouseFormattedValue([]interface{}{transitionedMetrics})
|
||||
} else {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// Code generated by scripts/semconv. DO NOT EDIT.
|
||||
|
||||
package semconv
|
||||
|
||||
var families = []Family{
|
||||
{
|
||||
Current: "db.system.name",
|
||||
Old: []string{"db.system"},
|
||||
Kind: KindAttribute,
|
||||
Contexts: nil,
|
||||
Signals: nil,
|
||||
ApplyToMetrics: nil,
|
||||
},
|
||||
{
|
||||
Current: "deployment.environment.name",
|
||||
Old: []string{"deployment.environment"},
|
||||
Kind: KindAttribute,
|
||||
Contexts: nil,
|
||||
Signals: nil,
|
||||
ApplyToMetrics: nil,
|
||||
},
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package semconv
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
//go:generate go run ../../scripts/semconv
|
||||
|
||||
// Kind identifies whether a family describes an attribute or a metric name.
|
||||
type Kind struct {
|
||||
valuer.String
|
||||
}
|
||||
|
||||
// Family is one logical telemetry field. Old is ordered from the most recent
|
||||
// predecessor to the oldest one and therefore also defines fallback order.
|
||||
type Family struct {
|
||||
Current string
|
||||
Old []string
|
||||
Kind Kind
|
||||
Contexts []telemetrytypes.FieldContext
|
||||
Signals []telemetrytypes.Signal
|
||||
ApplyToMetrics []string
|
||||
ValueMap map[string]string
|
||||
}
|
||||
|
||||
var (
|
||||
KindAttribute = Kind{String: valuer.NewString("attribute")}
|
||||
KindMetric = Kind{String: valuer.NewString("metric")}
|
||||
)
|
||||
|
||||
var memberToFamilies, familyMembers = buildIndexes()
|
||||
|
||||
// Enum returns the acceptable values for Kind.
|
||||
func (Kind) Enum() []any {
|
||||
return []any{KindAttribute, KindMetric}
|
||||
}
|
||||
|
||||
// Lookup returns the enabled family containing selector.Name for kind. The
|
||||
// returned family must not be modified.
|
||||
func Lookup(kind Kind, selector telemetrytypes.FieldKeySelector) (Family, bool) {
|
||||
idx, ok := lookupIndex(kind, selector)
|
||||
if !ok {
|
||||
return Family{}, false
|
||||
}
|
||||
return families[idx], true
|
||||
}
|
||||
|
||||
// Members returns the current name first, followed by historical names in
|
||||
// fallback order. A name outside an enabled family is returned unchanged. The
|
||||
// returned slice must not be modified.
|
||||
func Members(kind Kind, selector telemetrytypes.FieldKeySelector) []string {
|
||||
idx, ok := lookupIndex(kind, selector)
|
||||
if !ok {
|
||||
return []string{selector.Name}
|
||||
}
|
||||
return familyMembers[idx]
|
||||
}
|
||||
|
||||
// Current returns the current name for selector.Name, or the input name when
|
||||
// it does not belong to an enabled family.
|
||||
func Current(kind Kind, selector telemetrytypes.FieldKeySelector) string {
|
||||
idx, ok := lookupIndex(kind, selector)
|
||||
if !ok {
|
||||
return selector.Name
|
||||
}
|
||||
return families[idx].Current
|
||||
}
|
||||
|
||||
// All returns every enabled family. The returned slice and families must not be
|
||||
// modified.
|
||||
func All() []Family {
|
||||
return families
|
||||
}
|
||||
|
||||
func buildIndexes() (map[string][]int, [][]string) {
|
||||
index := make(map[string][]int)
|
||||
members := make([][]string, len(families))
|
||||
for i, family := range families {
|
||||
members[i] = make([]string, 0, len(family.Old)+1)
|
||||
members[i] = append(members[i], family.Current)
|
||||
members[i] = append(members[i], family.Old...)
|
||||
index[family.Current] = append(index[family.Current], i)
|
||||
for _, old := range family.Old {
|
||||
index[old] = append(index[old], i)
|
||||
}
|
||||
}
|
||||
return index, members
|
||||
}
|
||||
|
||||
func lookupIndex(kind Kind, selector telemetrytypes.FieldKeySelector) (int, bool) {
|
||||
for _, idx := range memberToFamilies[selector.Name] {
|
||||
if matchesSelector(families[idx], kind, selector) {
|
||||
return idx, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func matchesSelector(family Family, kind Kind, selector telemetrytypes.FieldKeySelector) bool {
|
||||
if family.Kind != kind {
|
||||
return false
|
||||
}
|
||||
|
||||
if selector.Signal != telemetrytypes.SignalUnspecified && len(family.Signals) > 0 {
|
||||
if !slices.Contains(family.Signals, selector.Signal) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if selector.FieldContext != telemetrytypes.FieldContextUnspecified && len(family.Contexts) > 0 {
|
||||
if !slices.Contains(family.Contexts, selector.FieldContext) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if selector.Signal == telemetrytypes.SignalMetrics && len(family.ApplyToMetrics) > 0 {
|
||||
if selector.MetricContext == nil {
|
||||
return false
|
||||
}
|
||||
return slices.Contains(family.ApplyToMetrics, selector.MetricContext.MetricName)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package semconv
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMembersReturnsCurrentBeforeHistoricalName(t *testing.T) {
|
||||
selector := telemetrytypes.FieldKeySelector{
|
||||
Name: "deployment.environment",
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
}
|
||||
|
||||
assert.Equal(t,
|
||||
[]string{"deployment.environment.name", "deployment.environment"},
|
||||
Members(KindAttribute, selector),
|
||||
"members should use current-first fallback order",
|
||||
)
|
||||
}
|
||||
|
||||
func TestCurrentReturnsCanonicalName(t *testing.T) {
|
||||
selector := telemetrytypes.FieldKeySelector{
|
||||
Name: "deployment.environment",
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
}
|
||||
|
||||
assert.Equal(t,
|
||||
"deployment.environment.name",
|
||||
Current(KindAttribute, selector),
|
||||
"historical name should resolve to the current family name",
|
||||
)
|
||||
}
|
||||
|
||||
func TestAllScopedFamilyMatchesSupportedScopes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
signal telemetrytypes.Signal
|
||||
fieldContext telemetrytypes.FieldContext
|
||||
}{
|
||||
{name: "trace resource", signal: telemetrytypes.SignalTraces, fieldContext: telemetrytypes.FieldContextResource},
|
||||
{name: "trace attribute", signal: telemetrytypes.SignalTraces, fieldContext: telemetrytypes.FieldContextAttribute},
|
||||
{name: "log resource", signal: telemetrytypes.SignalLogs, fieldContext: telemetrytypes.FieldContextResource},
|
||||
{name: "log attribute", signal: telemetrytypes.SignalLogs, fieldContext: telemetrytypes.FieldContextAttribute},
|
||||
{name: "metric resource", signal: telemetrytypes.SignalMetrics, fieldContext: telemetrytypes.FieldContextResource},
|
||||
{name: "metric attribute", signal: telemetrytypes.SignalMetrics, fieldContext: telemetrytypes.FieldContextAttribute},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
selector := telemetrytypes.FieldKeySelector{
|
||||
Name: "deployment.environment",
|
||||
Signal: test.signal,
|
||||
FieldContext: test.fieldContext,
|
||||
}
|
||||
|
||||
assert.Equal(t,
|
||||
"deployment.environment.name",
|
||||
Current(KindAttribute, selector),
|
||||
"an all-scoped family should match every supported signal and attribute context",
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMembersReturnsInputWhenKindDoesNotMatch(t *testing.T) {
|
||||
selector := telemetrytypes.FieldKeySelector{
|
||||
Name: "deployment.environment",
|
||||
Signal: telemetrytypes.SignalTraces,
|
||||
}
|
||||
|
||||
assert.Equal(t,
|
||||
[]string{"deployment.environment"},
|
||||
Members(KindMetric, selector),
|
||||
"an attribute family must not match a metric-name lookup",
|
||||
)
|
||||
}
|
||||
@@ -48,6 +48,8 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/modules/tracedetail/impltracedetail"
|
||||
"github.com/SigNoz/signoz/pkg/modules/tracefunnel"
|
||||
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||
"github.com/SigNoz/signoz/pkg/prometheus/promapi"
|
||||
"github.com/SigNoz/signoz/pkg/querier"
|
||||
"github.com/SigNoz/signoz/pkg/ruler"
|
||||
"github.com/SigNoz/signoz/pkg/ruler/signozruler"
|
||||
@@ -81,6 +83,7 @@ type Handlers struct {
|
||||
RuleStateHistory rulestatehistory.Handler
|
||||
SpanMapperHandler spanmapper.Handler
|
||||
AlertmanagerHandler alertmanager.Handler
|
||||
PrometheusHandler prometheus.Handler
|
||||
TraceDetail tracedetail.Handler
|
||||
RulerHandler ruler.Handler
|
||||
LLMPricingRuleHandler llmpricingrule.Handler
|
||||
@@ -101,6 +104,7 @@ func NewHandlers(
|
||||
zeusService zeus.Zeus,
|
||||
registryHandler factory.Handler,
|
||||
alertmanagerService alertmanager.Alertmanager,
|
||||
prometheusService prometheus.Prometheus,
|
||||
rulerService ruler.Ruler,
|
||||
statsAggregator statsreporter.Aggregator,
|
||||
) Handlers {
|
||||
@@ -129,6 +133,7 @@ func NewHandlers(
|
||||
CloudIntegrationHandler: implcloudintegration.NewHandler(modules.CloudIntegration),
|
||||
SpanMapperHandler: implspanmapper.NewHandler(modules.SpanMapper),
|
||||
AlertmanagerHandler: signozalertmanager.NewHandler(alertmanagerService),
|
||||
PrometheusHandler: promapi.NewHandler(providerSettings.Logger, prometheusService),
|
||||
TraceDetail: impltracedetail.NewHandler(modules.TraceDetail),
|
||||
RulerHandler: signozruler.NewHandler(rulerService),
|
||||
LLMPricingRuleHandler: impllmpricingrule.NewHandler(modules.LLMPricingRule),
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestNewHandlers(t *testing.T) {
|
||||
|
||||
querierHandler := querier.NewHandler(providerSettings, nil, nil)
|
||||
registryHandler := factory.NewHandler(nil)
|
||||
handlers := NewHandlers(modules, providerSettings, nil, querierHandler, nil, nil, nil, nil, nil, nil, nil, registryHandler, alertmanager, nil, nil)
|
||||
handlers := NewHandlers(modules, providerSettings, nil, querierHandler, nil, nil, nil, nil, nil, nil, nil, registryHandler, alertmanager, nil, nil, nil)
|
||||
reflectVal := reflect.ValueOf(handlers)
|
||||
for i := 0; i < reflectVal.NumField(); i++ {
|
||||
f := reflectVal.Field(i)
|
||||
|
||||
@@ -617,7 +617,7 @@ func New(
|
||||
|
||||
// Initialize all handlers for the modules
|
||||
registryHandler := factory.NewHandler(registry)
|
||||
handlers := NewHandlers(modules, providerSettings, analytics, querierHandler, licensing, global, flagger, gateway, telemetryMetadataStore, authz, zeus, registryHandler, alertmanager, rulerInstance, statsAggregator)
|
||||
handlers := NewHandlers(modules, providerSettings, analytics, querierHandler, licensing, global, flagger, gateway, telemetryMetadataStore, authz, zeus, registryHandler, alertmanager, prometheus, rulerInstance, statsAggregator)
|
||||
|
||||
// Initialize the API server (after registry so it can access service health)
|
||||
apiserverInstance, err := factory.NewProviderFromNamedMap(
|
||||
|
||||
@@ -12,7 +12,8 @@ var (
|
||||
Gateway = valuer.NewString("gateway")
|
||||
PremiumSupport = valuer.NewString("premium_support")
|
||||
|
||||
AnomalyDetection = valuer.NewString("anomaly_detection")
|
||||
AnomalyDetection = valuer.NewString("anomaly_detection")
|
||||
DotMetricsEnabled = valuer.NewString("dot_metrics_enabled")
|
||||
|
||||
// License State.
|
||||
LicenseStatusInvalid = valuer.NewString("invalid")
|
||||
@@ -59,6 +60,13 @@ var BasicPlan = []*Feature{
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
{
|
||||
Name: DotMetricsEnabled,
|
||||
Active: false,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
}
|
||||
|
||||
var EnterprisePlan = []*Feature{
|
||||
@@ -104,6 +112,21 @@ var EnterprisePlan = []*Feature{
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
{
|
||||
Name: DotMetricsEnabled,
|
||||
Active: false,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
}
|
||||
|
||||
var DefaultFeatureSet = []*Feature{}
|
||||
var DefaultFeatureSet = []*Feature{
|
||||
{
|
||||
Name: DotMetricsEnabled,
|
||||
Active: false,
|
||||
Usage: 0,
|
||||
UsageLimit: -1,
|
||||
Route: "",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,721 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
kindAttribute = "attribute"
|
||||
kindMetric = "metric"
|
||||
)
|
||||
|
||||
type stringListFlag []string
|
||||
|
||||
func (f *stringListFlag) String() string { return strings.Join(*f, ",") }
|
||||
func (f *stringListFlag) Set(value string) error {
|
||||
*f = append(*f, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
type schemaFile struct {
|
||||
FileFormat string `yaml:"file_format"`
|
||||
SchemaURL string `yaml:"schema_url"`
|
||||
Versions map[string]schemaVersion `yaml:"versions"`
|
||||
}
|
||||
|
||||
type schemaVersion struct {
|
||||
All changeSection `yaml:"all"`
|
||||
Resources changeSection `yaml:"resources"`
|
||||
Spans changeSection `yaml:"spans"`
|
||||
Logs changeSection `yaml:"logs"`
|
||||
Metrics changeSection `yaml:"metrics"`
|
||||
}
|
||||
|
||||
type changeSection struct {
|
||||
Changes []schemaChange `yaml:"changes"`
|
||||
}
|
||||
|
||||
type schemaChange struct {
|
||||
RenameAttributes *attributeRename `yaml:"rename_attributes"`
|
||||
RenameMetrics map[string]string `yaml:"rename_metrics"`
|
||||
}
|
||||
|
||||
type attributeRename struct {
|
||||
AttributeMap map[string]string `yaml:"attribute_map"`
|
||||
ApplyToMetrics []string `yaml:"apply_to_metrics"`
|
||||
}
|
||||
|
||||
type overlayFile struct {
|
||||
DefaultEnabled bool `yaml:"default_enabled"`
|
||||
// Families is keyed only by current name. One name cannot carry separate
|
||||
// policies for attribute and metric families; set kind explicitly whenever
|
||||
// a metric-name family is configured.
|
||||
Families map[string]overlayFamily `yaml:"families"`
|
||||
}
|
||||
|
||||
type overlayFamily struct {
|
||||
Enabled *bool `yaml:"enabled"`
|
||||
Kind string `yaml:"kind"`
|
||||
Old []string `yaml:"old"`
|
||||
AddOld []string `yaml:"add_old"`
|
||||
ExcludeOld []string `yaml:"exclude_old"`
|
||||
Contexts []string `yaml:"contexts"`
|
||||
Signals []string `yaml:"signals"`
|
||||
AddContexts []string `yaml:"add_contexts"`
|
||||
AddSignals []string `yaml:"add_signals"`
|
||||
ApplyToMetrics []string `yaml:"apply_to_metrics"`
|
||||
AddApplyToMetrics []string `yaml:"add_apply_to_metrics"`
|
||||
ValueMap map[string]string `yaml:"value_map"`
|
||||
}
|
||||
|
||||
type edge struct {
|
||||
old string
|
||||
current string
|
||||
kind string
|
||||
contexts []string
|
||||
signals []string
|
||||
allContexts bool
|
||||
allSignals bool
|
||||
applyToMetrics []string
|
||||
}
|
||||
|
||||
type graphKey struct{ kind, name string }
|
||||
|
||||
type generatedFamily struct {
|
||||
Current string
|
||||
Old []string
|
||||
Kind string
|
||||
Contexts []string
|
||||
Signals []string
|
||||
ApplyToMetrics []string
|
||||
ValueMap map[string]string
|
||||
}
|
||||
|
||||
func main() {
|
||||
root, err := findRepoRoot()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
var schemaPaths stringListFlag
|
||||
flag.Var(&schemaPaths, "schema", "schema source (repeatable)")
|
||||
overlayPath := flag.String("overlay", filepath.Join(root, "scripts/semconv/overlay.yaml"), "SigNoz overlay")
|
||||
goOutput := flag.String("go-out", filepath.Join(root, "pkg/semconv/families_gen.go"), "generated Go output")
|
||||
tsOutput := flag.String("ts-out", filepath.Join(root, "frontend/src/constants/generated/semconvFamilies.gen.ts"), "generated TypeScript output")
|
||||
check := flag.Bool("check", false, "fail if generated files are stale")
|
||||
flag.Parse()
|
||||
|
||||
if len(schemaPaths) == 0 {
|
||||
schemaPaths = append(schemaPaths, filepath.Join(root, "scripts/semconv/schema-1.42.0.yaml"))
|
||||
}
|
||||
|
||||
families, err := generate(schemaPaths, *overlayPath)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
goBytes, err := renderGo(families)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
tsBytes := renderTypeScript(families)
|
||||
|
||||
if *check {
|
||||
if err := checkFile(*goOutput, goBytes); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := checkFile(*tsOutput, tsBytes); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(*goOutput, goBytes, 0o644); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(*tsOutput, tsBytes, 0o644); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func fatal(err error) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func findRepoRoot() (string, error) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
||||
return dir, nil
|
||||
}
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
return "", errors.New("could not find repository root")
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
}
|
||||
|
||||
func generate(schemaPaths []string, overlayPath string) ([]generatedFamily, error) {
|
||||
var schemas []schemaFile
|
||||
for _, path := range schemaPaths {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read schema %s: %w", path, err)
|
||||
}
|
||||
var schema schemaFile
|
||||
if err := decodeKnownFields(data, &schema); err != nil {
|
||||
return nil, fmt.Errorf("parse schema %s: %w", path, err)
|
||||
}
|
||||
schemas = append(schemas, schema)
|
||||
}
|
||||
|
||||
overlayData, err := os.ReadFile(overlayPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read overlay: %w", err)
|
||||
}
|
||||
var overlay overlayFile
|
||||
if err := decodeKnownFields(overlayData, &overlay); err != nil {
|
||||
return nil, fmt.Errorf("parse overlay: %w", err)
|
||||
}
|
||||
|
||||
return buildFamilies(schemas, overlay)
|
||||
}
|
||||
|
||||
func decodeKnownFields(data []byte, target any) error {
|
||||
decoder := yaml.NewDecoder(bytes.NewReader(data))
|
||||
decoder.KnownFields(true)
|
||||
return decoder.Decode(target)
|
||||
}
|
||||
|
||||
func collectEdges(schemas []schemaFile) ([]edge, error) {
|
||||
var edges []edge
|
||||
for _, schema := range schemas {
|
||||
versions := make([]string, 0, len(schema.Versions))
|
||||
versionParts := make(map[string][3]int, len(schema.Versions))
|
||||
for version := range schema.Versions {
|
||||
parts, err := parseSchemaVersion(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
versions = append(versions, version)
|
||||
versionParts[version] = parts
|
||||
}
|
||||
sort.Slice(versions, func(i, j int) bool {
|
||||
return compareVersionParts(versionParts[versions[i]], versionParts[versions[j]]) < 0
|
||||
})
|
||||
for _, versionName := range versions {
|
||||
version := schema.Versions[versionName]
|
||||
var versionEdges []edge
|
||||
sections := []struct {
|
||||
name string
|
||||
section changeSection
|
||||
}{
|
||||
{name: "all", section: version.All},
|
||||
{name: "resources", section: version.Resources},
|
||||
{name: "spans", section: version.Spans},
|
||||
{name: "logs", section: version.Logs},
|
||||
{name: "metrics", section: version.Metrics},
|
||||
}
|
||||
for _, scoped := range sections {
|
||||
contexts, signals, allContexts, allSignals, err := scopeForSection(scoped.name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, change := range scoped.section.Changes {
|
||||
if change.RenameAttributes != nil {
|
||||
for _, old := range sortedMapKeys(change.RenameAttributes.AttributeMap) {
|
||||
versionEdges = append(versionEdges, edge{
|
||||
old: old, current: change.RenameAttributes.AttributeMap[old], kind: kindAttribute,
|
||||
contexts: contexts, signals: signals,
|
||||
allContexts: allContexts, allSignals: allSignals,
|
||||
applyToMetrics: change.RenameAttributes.ApplyToMetrics,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, old := range sortedMapKeys(change.RenameMetrics) {
|
||||
versionEdges = append(versionEdges, edge{
|
||||
old: old, current: change.RenameMetrics[old], kind: kindMetric,
|
||||
contexts: []string{"metric"}, signals: []string{"metrics"},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := rejectSameVersionChains(versionName, versionEdges); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
edges = append(edges, versionEdges...)
|
||||
}
|
||||
}
|
||||
return edges, nil
|
||||
}
|
||||
|
||||
func rejectSameVersionChains(version string, edges []edge) error {
|
||||
oldNames := make(map[graphKey]struct{}, len(edges))
|
||||
for _, item := range edges {
|
||||
oldNames[graphKey{kind: item.kind, name: item.old}] = struct{}{}
|
||||
}
|
||||
for _, item := range edges {
|
||||
if _, ok := oldNames[graphKey{kind: item.kind, name: item.current}]; ok {
|
||||
return fmt.Errorf(
|
||||
"schema version %q contains a same-version %s rename chain through %q",
|
||||
version,
|
||||
item.kind,
|
||||
item.current,
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSchemaVersion(version string) ([3]int, error) {
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) != 3 {
|
||||
return [3]int{}, fmt.Errorf("schema version %q must contain major, minor, and patch numbers", version)
|
||||
}
|
||||
|
||||
var parsed [3]int
|
||||
for i, part := range parts {
|
||||
value, err := strconv.Atoi(part)
|
||||
if err != nil || value < 0 {
|
||||
return [3]int{}, fmt.Errorf("schema version %q contains invalid numeric component %q", version, part)
|
||||
}
|
||||
parsed[i] = value
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func compareVersionParts(left, right [3]int) int {
|
||||
for i := range left {
|
||||
if left[i] < right[i] {
|
||||
return -1
|
||||
}
|
||||
if left[i] > right[i] {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func scopeForSection(section string) (contexts, signals []string, allContexts, allSignals bool, err error) {
|
||||
switch section {
|
||||
case "all":
|
||||
return nil, nil, true, true, nil
|
||||
case "resources":
|
||||
return []string{"resource"}, nil, false, true, nil
|
||||
case "spans":
|
||||
return []string{"attribute"}, []string{"traces"}, false, false, nil
|
||||
case "logs":
|
||||
return []string{"attribute"}, []string{"logs"}, false, false, nil
|
||||
case "metrics":
|
||||
return []string{"attribute"}, []string{"metrics"}, false, false, nil
|
||||
default:
|
||||
return nil, nil, false, false, fmt.Errorf("unsupported schema section %q", section)
|
||||
}
|
||||
}
|
||||
|
||||
func buildFamilies(schemas []schemaFile, overlay overlayFile) ([]generatedFamily, error) {
|
||||
edges, err := collectEdges(schemas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
next := make(map[graphKey]string)
|
||||
for _, item := range edges {
|
||||
key := graphKey{kind: item.kind, name: item.old}
|
||||
if existing, ok := next[key]; ok && existing == item.current {
|
||||
// Repeated entries are common in chained schema histories. Treat an
|
||||
// identical edge as a no-op so it cannot sever a later edge in the
|
||||
// same chain (A -> B, B -> C, then a repeated A -> B).
|
||||
continue
|
||||
}
|
||||
// Schema history occasionally repeats an old name with a newer direct
|
||||
// destination or rolls a rename back. Edges are collected
|
||||
// oldest-to-newest, so the latest published current name must be a root.
|
||||
delete(next, graphKey{kind: item.kind, name: item.current})
|
||||
next[key] = item.current
|
||||
}
|
||||
|
||||
type familyState struct {
|
||||
family generatedFamily
|
||||
distance map[string]int
|
||||
allContexts bool
|
||||
allSignals bool
|
||||
}
|
||||
states := map[graphKey]*familyState{}
|
||||
for _, item := range edges {
|
||||
root, distance, err := rootFor(next, item.kind, item.old)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := graphKey{kind: item.kind, name: root}
|
||||
state := states[key]
|
||||
if state == nil {
|
||||
state = &familyState{
|
||||
family: generatedFamily{Current: root, Kind: item.kind},
|
||||
distance: map[string]int{},
|
||||
}
|
||||
states[key] = state
|
||||
}
|
||||
if prior, ok := state.distance[item.old]; !ok || distance < prior {
|
||||
state.distance[item.old] = distance
|
||||
}
|
||||
state.allContexts = state.allContexts || item.allContexts
|
||||
state.allSignals = state.allSignals || item.allSignals
|
||||
state.family.Contexts = appendUnique(state.family.Contexts, item.contexts...)
|
||||
state.family.Signals = appendUnique(state.family.Signals, item.signals...)
|
||||
state.family.ApplyToMetrics = appendUnique(state.family.ApplyToMetrics, item.applyToMetrics...)
|
||||
}
|
||||
|
||||
for _, state := range states {
|
||||
for old := range state.distance {
|
||||
if old != state.family.Current {
|
||||
state.family.Old = append(state.family.Old, old)
|
||||
}
|
||||
}
|
||||
sort.Slice(state.family.Old, func(i, j int) bool {
|
||||
left, right := state.family.Old[i], state.family.Old[j]
|
||||
if state.distance[left] != state.distance[right] {
|
||||
return state.distance[left] < state.distance[right]
|
||||
}
|
||||
return left < right
|
||||
})
|
||||
if state.allContexts {
|
||||
state.family.Contexts = nil
|
||||
} else {
|
||||
sort.Strings(state.family.Contexts)
|
||||
}
|
||||
if state.allSignals {
|
||||
state.family.Signals = nil
|
||||
} else {
|
||||
sort.Strings(state.family.Signals)
|
||||
}
|
||||
sort.Strings(state.family.ApplyToMetrics)
|
||||
}
|
||||
|
||||
for _, current := range sortedMapKeys(overlay.Families) {
|
||||
policy := overlay.Families[current]
|
||||
kind, err := normalizedOverlayKind(current, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policy.Kind = kind
|
||||
overlay.Families[current] = policy
|
||||
key := graphKey{kind: kind, name: current}
|
||||
state := states[key]
|
||||
if state == nil {
|
||||
if len(policy.Old) == 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"overlay family %q with kind %q is absent from schemas and has no old members",
|
||||
current,
|
||||
kind,
|
||||
)
|
||||
}
|
||||
state = &familyState{
|
||||
family: generatedFamily{Current: current, Kind: kind, Old: append([]string(nil), policy.Old...)},
|
||||
distance: map[string]int{},
|
||||
}
|
||||
states[key] = state
|
||||
}
|
||||
applyOverlay(&state.family, policy)
|
||||
}
|
||||
|
||||
var result []generatedFamily
|
||||
for key, state := range states {
|
||||
policy, hasPolicy := overlay.Families[key.name]
|
||||
enabled := overlay.DefaultEnabled
|
||||
if hasPolicy && policy.Kind != key.kind {
|
||||
hasPolicy = false
|
||||
}
|
||||
if hasPolicy && policy.Enabled != nil {
|
||||
enabled = *policy.Enabled
|
||||
}
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
if len(state.family.Old) == 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"enabled family %q with kind %q has no old members",
|
||||
state.family.Current,
|
||||
state.family.Kind,
|
||||
)
|
||||
}
|
||||
sort.Strings(state.family.Contexts)
|
||||
sort.Strings(state.family.Signals)
|
||||
sort.Strings(state.family.ApplyToMetrics)
|
||||
result = append(result, state.family)
|
||||
}
|
||||
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
if result[i].Current != result[j].Current {
|
||||
return result[i].Current < result[j].Current
|
||||
}
|
||||
return result[i].Kind < result[j].Kind
|
||||
})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func rootFor(next map[graphKey]string, kind, name string) (string, int, error) {
|
||||
seen := map[string]bool{}
|
||||
distance := 0
|
||||
for {
|
||||
if seen[name] {
|
||||
return "", 0, fmt.Errorf("rename cycle for %s %q", kind, name)
|
||||
}
|
||||
seen[name] = true
|
||||
current, ok := next[graphKey{kind: kind, name: name}]
|
||||
if !ok {
|
||||
return name, distance, nil
|
||||
}
|
||||
name = current
|
||||
distance++
|
||||
}
|
||||
}
|
||||
|
||||
func normalizedOverlayKind(current string, policy overlayFamily) (string, error) {
|
||||
kind := policy.Kind
|
||||
if kind == "" {
|
||||
kind = kindAttribute
|
||||
}
|
||||
if kind != kindAttribute && kind != kindMetric {
|
||||
return "", fmt.Errorf("overlay family %q has unsupported kind %q", current, kind)
|
||||
}
|
||||
return kind, nil
|
||||
}
|
||||
|
||||
func applyOverlay(family *generatedFamily, policy overlayFamily) {
|
||||
if policy.Kind != "" {
|
||||
family.Kind = policy.Kind
|
||||
}
|
||||
if policy.Old != nil {
|
||||
family.Old = append([]string(nil), policy.Old...)
|
||||
}
|
||||
family.Old = appendUnique(family.Old, policy.AddOld...)
|
||||
if len(policy.ExcludeOld) > 0 {
|
||||
excluded := make(map[string]bool, len(policy.ExcludeOld))
|
||||
for _, old := range policy.ExcludeOld {
|
||||
excluded[old] = true
|
||||
}
|
||||
family.Old = deleteMatching(family.Old, excluded)
|
||||
}
|
||||
if policy.Contexts != nil {
|
||||
family.Contexts = append([]string(nil), policy.Contexts...)
|
||||
}
|
||||
if policy.Signals != nil {
|
||||
family.Signals = append([]string(nil), policy.Signals...)
|
||||
}
|
||||
family.Contexts = appendUnique(family.Contexts, policy.AddContexts...)
|
||||
family.Signals = appendUnique(family.Signals, policy.AddSignals...)
|
||||
if policy.ApplyToMetrics != nil {
|
||||
family.ApplyToMetrics = append([]string(nil), policy.ApplyToMetrics...)
|
||||
}
|
||||
family.ApplyToMetrics = appendUnique(family.ApplyToMetrics, policy.AddApplyToMetrics...)
|
||||
if policy.ValueMap != nil {
|
||||
family.ValueMap = make(map[string]string, len(policy.ValueMap))
|
||||
for old, current := range policy.ValueMap {
|
||||
family.ValueMap[old] = current
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendUnique(values []string, additions ...string) []string {
|
||||
seen := make(map[string]bool, len(values)+len(additions))
|
||||
for _, value := range values {
|
||||
seen[value] = true
|
||||
}
|
||||
for _, value := range additions {
|
||||
if value == "" || seen[value] {
|
||||
continue
|
||||
}
|
||||
seen[value] = true
|
||||
values = append(values, value)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func deleteMatching(values []string, excluded map[string]bool) []string {
|
||||
result := values[:0]
|
||||
for _, value := range values {
|
||||
if !excluded[value] {
|
||||
result = append(result, value)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func renderGo(families []generatedFamily) ([]byte, error) {
|
||||
var out bytes.Buffer
|
||||
out.WriteString("// Code generated by scripts/semconv. DO NOT EDIT.\n\n")
|
||||
out.WriteString("package semconv\n\n")
|
||||
needsTelemetryTypes := false
|
||||
for _, family := range families {
|
||||
if len(family.Contexts) > 0 || len(family.Signals) > 0 {
|
||||
needsTelemetryTypes = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if needsTelemetryTypes {
|
||||
out.WriteString("import \"github.com/SigNoz/signoz/pkg/types/telemetrytypes\"\n\n")
|
||||
}
|
||||
out.WriteString("var families = []Family{\n")
|
||||
for _, family := range families {
|
||||
contexts, err := goFieldContextSlice(family.Contexts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("render family %q: %w", family.Current, err)
|
||||
}
|
||||
signals, err := goSignalSlice(family.Signals)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("render family %q: %w", family.Current, err)
|
||||
}
|
||||
out.WriteString("\t{\n")
|
||||
fmt.Fprintf(&out, "\t\tCurrent: %s,\n", strconv.Quote(family.Current))
|
||||
fmt.Fprintf(&out, "\t\tOld: %s,\n", goStringSlice(family.Old))
|
||||
if family.Kind == kindMetric {
|
||||
out.WriteString("\t\tKind: KindMetric,\n")
|
||||
} else {
|
||||
out.WriteString("\t\tKind: KindAttribute,\n")
|
||||
}
|
||||
fmt.Fprintf(&out, "\t\tContexts: %s,\n", contexts)
|
||||
fmt.Fprintf(&out, "\t\tSignals: %s,\n", signals)
|
||||
fmt.Fprintf(&out, "\t\tApplyToMetrics: %s,\n", goStringSlice(family.ApplyToMetrics))
|
||||
if len(family.ValueMap) > 0 {
|
||||
out.WriteString("\t\tValueMap: map[string]string{\n")
|
||||
keys := sortedMapKeys(family.ValueMap)
|
||||
for _, key := range keys {
|
||||
fmt.Fprintf(&out, "\t\t\t%s: %s,\n", strconv.Quote(key), strconv.Quote(family.ValueMap[key]))
|
||||
}
|
||||
out.WriteString("\t\t},\n")
|
||||
}
|
||||
out.WriteString("\t},\n")
|
||||
}
|
||||
out.WriteString("}\n")
|
||||
return format.Source(out.Bytes())
|
||||
}
|
||||
|
||||
func goStringSlice(values []string) string {
|
||||
if len(values) == 0 {
|
||||
return "nil"
|
||||
}
|
||||
quoted := make([]string, len(values))
|
||||
for i, value := range values {
|
||||
quoted[i] = strconv.Quote(value)
|
||||
}
|
||||
return "[]string{" + strings.Join(quoted, ", ") + "}"
|
||||
}
|
||||
|
||||
func goFieldContextSlice(values []string) (string, error) {
|
||||
if len(values) == 0 {
|
||||
return "nil", nil
|
||||
}
|
||||
constants := make([]string, len(values))
|
||||
for i, value := range values {
|
||||
switch value {
|
||||
case "metric":
|
||||
constants[i] = "telemetrytypes.FieldContextMetric"
|
||||
case "resource":
|
||||
constants[i] = "telemetrytypes.FieldContextResource"
|
||||
case "attribute":
|
||||
constants[i] = "telemetrytypes.FieldContextAttribute"
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported field context %q", value)
|
||||
}
|
||||
}
|
||||
return "[]telemetrytypes.FieldContext{" + strings.Join(constants, ", ") + "}", nil
|
||||
}
|
||||
|
||||
func goSignalSlice(values []string) (string, error) {
|
||||
if len(values) == 0 {
|
||||
return "nil", nil
|
||||
}
|
||||
constants := make([]string, len(values))
|
||||
for i, value := range values {
|
||||
switch value {
|
||||
case "traces":
|
||||
constants[i] = "telemetrytypes.SignalTraces"
|
||||
case "logs":
|
||||
constants[i] = "telemetrytypes.SignalLogs"
|
||||
case "metrics":
|
||||
constants[i] = "telemetrytypes.SignalMetrics"
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported signal %q", value)
|
||||
}
|
||||
}
|
||||
return "[]telemetrytypes.Signal{" + strings.Join(constants, ", ") + "}", nil
|
||||
}
|
||||
|
||||
func renderTypeScript(families []generatedFamily) []byte {
|
||||
var out bytes.Buffer
|
||||
out.WriteString("// Code generated by scripts/semconv. DO NOT EDIT.\n\n")
|
||||
out.WriteString("export type SemconvFamily = {\n")
|
||||
out.WriteString("\treadonly current: string;\n\treadonly old: readonly string[];\n")
|
||||
out.WriteString("\treadonly kind: 'attribute' | 'metric';\n")
|
||||
out.WriteString("\treadonly contexts: readonly string[];\n\treadonly signals: readonly string[];\n")
|
||||
out.WriteString("\treadonly applyToMetrics: readonly string[];\n")
|
||||
out.WriteString("\treadonly valueMap: Readonly<Record<string, string>>;\n};\n\n")
|
||||
out.WriteString("export const SEMCONV_FAMILIES: readonly SemconvFamily[] = [\n")
|
||||
for _, family := range families {
|
||||
out.WriteString("\t{\n")
|
||||
fmt.Fprintf(&out, "\t\tcurrent: %s,\n", tsString(family.Current))
|
||||
fmt.Fprintf(&out, "\t\told: %s,\n", tsStringSlice(family.Old))
|
||||
fmt.Fprintf(&out, "\t\tkind: %s,\n", tsString(family.Kind))
|
||||
fmt.Fprintf(&out, "\t\tcontexts: %s,\n", tsStringSlice(family.Contexts))
|
||||
fmt.Fprintf(&out, "\t\tsignals: %s,\n", tsStringSlice(family.Signals))
|
||||
fmt.Fprintf(&out, "\t\tapplyToMetrics: %s,\n", tsStringSlice(family.ApplyToMetrics))
|
||||
out.WriteString("\t\tvalueMap: {")
|
||||
keys := sortedMapKeys(family.ValueMap)
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
out.WriteString(", ")
|
||||
}
|
||||
fmt.Fprintf(&out, "%s: %s", tsString(key), tsString(family.ValueMap[key]))
|
||||
}
|
||||
out.WriteString("},\n\t},\n")
|
||||
}
|
||||
out.WriteString("] as const;\n")
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
func tsString(value string) string {
|
||||
quoted := strconv.Quote(value)
|
||||
return "'" + strings.ReplaceAll(quoted[1:len(quoted)-1], "'", `\'`) + "'"
|
||||
}
|
||||
func tsStringSlice(values []string) string {
|
||||
quoted := make([]string, len(values))
|
||||
for i, value := range values {
|
||||
quoted[i] = tsString(value)
|
||||
}
|
||||
return "[" + strings.Join(quoted, ", ") + "]"
|
||||
}
|
||||
|
||||
func sortedMapKeys[T any](values map[string]T) []string {
|
||||
keys := make([]string, 0, len(values))
|
||||
for key := range values {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func checkFile(path string, expected []byte) error {
|
||||
actual, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generated file %s is missing: run go run ./scripts/semconv", path)
|
||||
}
|
||||
if !bytes.Equal(actual, expected) {
|
||||
return fmt.Errorf("generated file %s is stale: run go run ./scripts/semconv", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,376 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSchemaDecoderRejectsUnsupportedSection(t *testing.T) {
|
||||
var schema schemaFile
|
||||
err := decodeKnownFields([]byte(`
|
||||
versions:
|
||||
1.0.0:
|
||||
span_events:
|
||||
changes:
|
||||
- rename_events:
|
||||
event_map:
|
||||
old: current
|
||||
`), &schema)
|
||||
|
||||
assert.ErrorContains(t, err, "field span_events not found", "unsupported schema sections must fail generation")
|
||||
}
|
||||
|
||||
func TestBuildFamiliesRejectsMalformedSchemaVersion(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
latest:
|
||||
spans:
|
||||
changes: []
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
_, err := buildFamilies([]schemaFile{schema}, overlayFile{})
|
||||
assert.ErrorContains(t, err, `schema version "latest"`, "malformed versions must not be silently reordered")
|
||||
}
|
||||
|
||||
func TestBuildFamiliesResolvesRenameChain(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
4.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
a: b
|
||||
3.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
b: c
|
||||
x: c
|
||||
2.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
a: b
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
a: b
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
enabled := true
|
||||
families, err := buildFamilies([]schemaFile{schema}, overlayFile{Families: map[string]overlayFamily{
|
||||
"c": {Enabled: &enabled},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []generatedFamily{{
|
||||
Current: "c",
|
||||
Old: []string{"b", "x", "a"},
|
||||
Kind: kindAttribute,
|
||||
Contexts: []string{"attribute"},
|
||||
Signals: []string{"traces"},
|
||||
}}, families, "predecessors should be ordered by distance and then name")
|
||||
}
|
||||
|
||||
func TestBuildFamiliesMapsSchemaSectionsToScopes(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
1.0.0:
|
||||
all:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
all.old: all.current
|
||||
resources:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
resource.old: resource.current
|
||||
logs:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
log.old: log.current
|
||||
metrics:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
state: cpu.mode
|
||||
apply_to_metrics: [system.cpu.time]
|
||||
- rename_metrics:
|
||||
old.metric: current.metric
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
enabled := true
|
||||
families, err := buildFamilies([]schemaFile{schema}, overlayFile{Families: map[string]overlayFamily{
|
||||
"all.current": {Enabled: &enabled},
|
||||
"resource.current": {Enabled: &enabled},
|
||||
"log.current": {Enabled: &enabled},
|
||||
"cpu.mode": {Enabled: &enabled},
|
||||
"current.metric": {Enabled: &enabled, Kind: kindMetric},
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []generatedFamily{
|
||||
{
|
||||
Current: "all.current", Old: []string{"all.old"}, Kind: kindAttribute,
|
||||
Contexts: nil, Signals: nil,
|
||||
},
|
||||
{
|
||||
Current: "cpu.mode", Old: []string{"state"}, Kind: kindAttribute,
|
||||
Contexts: []string{"attribute"}, Signals: []string{"metrics"},
|
||||
ApplyToMetrics: []string{"system.cpu.time"},
|
||||
},
|
||||
{
|
||||
Current: "current.metric", Old: []string{"old.metric"}, Kind: kindMetric,
|
||||
Contexts: []string{"metric"}, Signals: []string{"metrics"},
|
||||
},
|
||||
{
|
||||
Current: "log.current", Old: []string{"log.old"}, Kind: kindAttribute,
|
||||
Contexts: []string{"attribute"}, Signals: []string{"logs"},
|
||||
},
|
||||
{
|
||||
Current: "resource.current", Old: []string{"resource.old"}, Kind: kindAttribute,
|
||||
Contexts: []string{"resource"},
|
||||
},
|
||||
}, families, "schema sections should produce their documented signal and context scopes")
|
||||
}
|
||||
|
||||
func TestOverlayAddsFamilyWithoutSchemaHistory(t *testing.T) {
|
||||
enabled := true
|
||||
families, err := buildFamilies(nil, overlayFile{Families: map[string]overlayFamily{
|
||||
"added.current": {
|
||||
Enabled: &enabled,
|
||||
Old: []string{"added.old"},
|
||||
Contexts: []string{"resource"},
|
||||
Signals: []string{"traces"},
|
||||
},
|
||||
}})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []generatedFamily{{
|
||||
Current: "added.current",
|
||||
Old: []string{"added.old"},
|
||||
Kind: kindAttribute,
|
||||
Contexts: []string{"resource"},
|
||||
Signals: []string{"traces"},
|
||||
}}, families, "an explicit overlay family should not require schema history")
|
||||
}
|
||||
|
||||
func TestOverlayOverridesGeneratedFamily(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
1.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
old: current
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
enabled := true
|
||||
families, err := buildFamilies([]schemaFile{schema}, overlayFile{Families: map[string]overlayFamily{
|
||||
"current": {
|
||||
Enabled: &enabled,
|
||||
AddOld: []string{"older"},
|
||||
ExcludeOld: []string{"old"},
|
||||
AddContexts: []string{"resource"},
|
||||
AddSignals: []string{"logs"},
|
||||
ValueMap: map[string]string{"legacy": "current"},
|
||||
},
|
||||
}})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []generatedFamily{{
|
||||
Current: "current",
|
||||
Old: []string{"older"},
|
||||
Kind: kindAttribute,
|
||||
Contexts: []string{"attribute", "resource"},
|
||||
Signals: []string{"logs", "traces"},
|
||||
ValueMap: map[string]string{"legacy": "current"},
|
||||
}}, families, "overlay additions and exclusions should be applied to the generated family")
|
||||
}
|
||||
|
||||
func TestOverlayDisablesFamilyWhenDefaultIsEnabled(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
1.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
old: current
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
disabled := false
|
||||
families, err := buildFamilies([]schemaFile{schema}, overlayFile{
|
||||
DefaultEnabled: true,
|
||||
Families: map[string]overlayFamily{
|
||||
"current": {Enabled: &disabled},
|
||||
},
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, families, "an explicitly disabled family must override default_enabled")
|
||||
}
|
||||
|
||||
func TestRenderGoIsDeterministic(t *testing.T) {
|
||||
families := []generatedFamily{{
|
||||
Current: "current", Old: []string{"old"}, Kind: kindAttribute,
|
||||
ValueMap: map[string]string{"b": "2", "a": "1"},
|
||||
}}
|
||||
|
||||
first, err := renderGo(families)
|
||||
require.NoError(t, err)
|
||||
second, err := renderGo(families)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, first, second, "Go generation must not depend on map iteration order")
|
||||
}
|
||||
|
||||
func TestRenderGoUsesCanonicalTelemetryTypes(t *testing.T) {
|
||||
families := []generatedFamily{{
|
||||
Current: "current", Old: []string{"old"}, Kind: kindAttribute,
|
||||
Contexts: []string{"resource"}, Signals: []string{"traces"},
|
||||
}}
|
||||
|
||||
output, err := renderGo(families)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(output), "telemetrytypes.FieldContextResource", "generated contexts should use telemetrytypes")
|
||||
assert.Contains(t, string(output), "telemetrytypes.SignalTraces", "generated signals should use telemetrytypes")
|
||||
}
|
||||
|
||||
func TestRenderTypeScriptIsDeterministic(t *testing.T) {
|
||||
families := []generatedFamily{{
|
||||
Current: "current", Old: []string{"old"}, Kind: kindAttribute,
|
||||
ValueMap: map[string]string{"b": "2", "a": "1"},
|
||||
}}
|
||||
|
||||
assert.Equal(t, renderTypeScript(families), renderTypeScript(families), "TypeScript generation must not depend on map iteration order")
|
||||
}
|
||||
|
||||
func TestBuildFamiliesHandlesRenameRollback(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
2.0.0:
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
temporary: original
|
||||
1.0.0:
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
original: temporary
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
enabled := true
|
||||
families, err := buildFamilies([]schemaFile{schema}, overlayFile{Families: map[string]overlayFamily{
|
||||
"original": {Enabled: &enabled, Kind: kindMetric},
|
||||
}})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []generatedFamily{{
|
||||
Current: "original",
|
||||
Old: []string{"temporary"},
|
||||
Kind: kindMetric,
|
||||
Contexts: []string{"metric"},
|
||||
Signals: []string{"metrics"},
|
||||
}}, families, "the latest rollback destination should remain the family root")
|
||||
}
|
||||
|
||||
func TestBuildFamiliesRejectsSameVersionRenameChain(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
1.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
x: y
|
||||
y: z
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
_, err := buildFamilies([]schemaFile{schema}, overlayFile{})
|
||||
assert.ErrorContains(t, err, `same-version attribute rename chain through "y"`, "order-sensitive same-version chains must be rejected")
|
||||
}
|
||||
|
||||
func TestBuildFamiliesRejectsOverlayFamilyWithoutHistory(t *testing.T) {
|
||||
enabled := true
|
||||
_, err := buildFamilies(nil, overlayFile{Families: map[string]overlayFamily{
|
||||
"missing": {Enabled: &enabled},
|
||||
}})
|
||||
|
||||
assert.ErrorContains(t, err, `overlay family "missing" with kind "attribute" is absent`, "an overlay cannot invent a family without old members")
|
||||
}
|
||||
|
||||
func TestBuildFamiliesRejectsEnabledFamilyWithoutOldMembers(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
1.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
old: current
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
enabled := true
|
||||
_, err := buildFamilies([]schemaFile{schema}, overlayFile{Families: map[string]overlayFamily{
|
||||
"current": {Enabled: &enabled, ExcludeOld: []string{"old"}},
|
||||
}})
|
||||
assert.ErrorContains(t, err, `enabled family "current" with kind "attribute" has no old members`, "exclude_old cannot empty an enabled family")
|
||||
}
|
||||
|
||||
func TestOverlayKindDefaultsToAttribute(t *testing.T) {
|
||||
var schema schemaFile
|
||||
require.NoError(t, decodeKnownFields([]byte(`
|
||||
versions:
|
||||
1.0.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
attribute.old: shared.current
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
metric.old: shared.current
|
||||
`), &schema), "test schema must decode")
|
||||
|
||||
enabled := true
|
||||
families, err := buildFamilies([]schemaFile{schema}, overlayFile{Families: map[string]overlayFamily{
|
||||
"shared.current": {Enabled: &enabled},
|
||||
}})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []generatedFamily{{
|
||||
Current: "shared.current",
|
||||
Old: []string{"attribute.old"},
|
||||
Kind: kindAttribute,
|
||||
Contexts: []string{"attribute"},
|
||||
Signals: []string{"traces"},
|
||||
}}, families, "a kind-less overlay policy should affect only the attribute family")
|
||||
}
|
||||
|
||||
func TestCheckFileReportsStaleOutput(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "generated.go")
|
||||
require.NoError(t, os.WriteFile(path, []byte("old"), 0o600), "test output must be writable")
|
||||
|
||||
assert.ErrorContains(t, checkFile(path, []byte("new")), "is stale", "check mode must reject stale generated output")
|
||||
}
|
||||
|
||||
func TestTypeScriptStringEscapesControlCharacters(t *testing.T) {
|
||||
assert.Equal(t, `'line\n\t\x01\'\\end'`, tsString("line\n\t\x01'\\end"), "generated TypeScript strings must remain valid literals")
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
# SigNoz semantic-convention rollout policy.
|
||||
#
|
||||
# Families are keyed by their current OpenTelemetry name. Schema-derived
|
||||
# families are disabled by default so rollout remains explicit and reversible.
|
||||
default_enabled: false
|
||||
|
||||
families:
|
||||
deployment.environment.name:
|
||||
enabled: true
|
||||
db.system.name:
|
||||
enabled: true
|
||||
@@ -1,760 +0,0 @@
|
||||
|
||||
|
||||
file_format: 1.1.0
|
||||
schema_url: https://opentelemetry.io/schemas/1.42.0
|
||||
versions:
|
||||
1.42.0:
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
v8js.memory.heap.limit: v8js.memory.heap.space.size
|
||||
1.41.1:
|
||||
1.41.0:
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
k8s.container.cpu.limit: k8s.container.cpu.limit.desired
|
||||
k8s.container.cpu.limit_utilization: k8s.container.cpu.limit.utilization
|
||||
k8s.container.cpu.request: k8s.container.cpu.request.desired
|
||||
k8s.container.cpu.request_utilization: k8s.container.cpu.request.utilization
|
||||
k8s.container.memory.limit: k8s.container.memory.limit.desired
|
||||
k8s.container.memory.request: k8s.container.memory.request.desired
|
||||
1.40.0:
|
||||
all:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
feature_flag.evaluation.error.message: feature_flag.error.message
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
system.memory.shared: system.memory.linux.shared
|
||||
1.39.0:
|
||||
all:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
linux.memory.slab.state: system.memory.linux.slab.state
|
||||
peer.service: service.peer.name
|
||||
rpc.connect_rpc.error_code: rpc.response.status_code
|
||||
rpc.connect_rpc.request.metadata: rpc.request.metadata
|
||||
rpc.connect_rpc.response.metadata: rpc.response.metadata
|
||||
rpc.grpc.request.metadata: rpc.request.metadata
|
||||
rpc.grpc.response.metadata: rpc.response.metadata
|
||||
rpc.jsonrpc.request_id: jsonrpc.request.id
|
||||
rpc.jsonrpc.version: jsonrpc.protocol.version
|
||||
rpc.system: rpc.system.name
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
process.open_file_descriptor.count: process.unix.file_descriptor.count
|
||||
system.linux.memory.available: system.memory.linux.available
|
||||
system.linux.memory.slab.usage: system.memory.linux.slab.usage
|
||||
1.38.0:
|
||||
all:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
process.context_switch_type: process.context_switch.type
|
||||
process.paging.fault_type: system.paging.fault.type
|
||||
system.cpu.logical_number: cpu.logical_number
|
||||
system.paging.type: system.paging.fault.type
|
||||
system.process.status: process.state
|
||||
system.processes.status: process.state
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
k8s.cronjob.active_jobs: k8s.cronjob.job.active
|
||||
k8s.daemonset.current_scheduled_nodes: k8s.daemonset.node.current_scheduled
|
||||
k8s.daemonset.desired_scheduled_nodes: k8s.daemonset.node.desired_scheduled
|
||||
k8s.daemonset.misscheduled_nodes: k8s.daemonset.node.misscheduled
|
||||
k8s.daemonset.ready_nodes: k8s.daemonset.node.ready
|
||||
k8s.deployment.available_pods: k8s.deployment.pod.available
|
||||
k8s.deployment.desired_pods: k8s.deployment.pod.desired
|
||||
k8s.hpa.current_pods: k8s.hpa.pod.current
|
||||
k8s.hpa.desired_pods: k8s.hpa.pod.desired
|
||||
k8s.hpa.max_pods: k8s.hpa.pod.max
|
||||
k8s.hpa.min_pods: k8s.hpa.pod.min
|
||||
k8s.job.active_pods: k8s.job.pod.active
|
||||
k8s.job.desired_successful_pods: k8s.job.pod.desired_successful
|
||||
k8s.job.failed_pods: k8s.job.pod.failed
|
||||
k8s.job.max_parallel_pods: k8s.job.pod.max_parallel
|
||||
k8s.job.successful_pods: k8s.job.pod.successful
|
||||
k8s.node.allocatable.cpu: k8s.node.cpu.allocatable
|
||||
k8s.node.allocatable.ephemeral_storage: k8s.node.ephemeral_storage.allocatable
|
||||
k8s.node.allocatable.memory: k8s.node.memory.allocatable
|
||||
k8s.node.allocatable.pods: k8s.node.pod.allocatable
|
||||
k8s.replicaset.available_pods: k8s.replicaset.pod.available
|
||||
k8s.replicaset.desired_pods: k8s.replicaset.pod.desired
|
||||
k8s.replication_controller.available_pods: k8s.replicationcontroller.pod.available
|
||||
k8s.replication_controller.desired_pods: k8s.replicationcontroller.pod.desired
|
||||
k8s.replicationcontroller.available_pods: k8s.replicationcontroller.pod.available
|
||||
k8s.replicationcontroller.desired_pods: k8s.replicationcontroller.pod.desired
|
||||
k8s.statefulset.current_pods: k8s.statefulset.pod.current
|
||||
k8s.statefulset.desired_pods: k8s.statefulset.pod.desired
|
||||
k8s.statefulset.ready_pods: k8s.statefulset.pod.ready
|
||||
k8s.statefulset.updated_pods: k8s.statefulset.pod.updated
|
||||
v8js.heap.space.available_size: v8js.memory.heap.space.available_size
|
||||
v8js.heap.space.physical_size: v8js.memory.heap.space.physical_size
|
||||
1.37.0:
|
||||
all:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
android.state: android.app.state
|
||||
container.runtime: container.runtime.name
|
||||
enduser.role: user.roles
|
||||
gen_ai.openai.request.service_tier: openai.request.service_tier
|
||||
gen_ai.openai.response.service_tier: openai.response.service_tier
|
||||
gen_ai.openai.response.system_fingerprint: openai.response.system_fingerprint
|
||||
gen_ai.system: gen_ai.provider.name
|
||||
ios.state: ios.app.state
|
||||
1.36.0:
|
||||
1.35.0:
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1698
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
az.namespace: azure.resource_provider.namespace
|
||||
az.service_request_id: azure.service.request.id
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/issues/1800
|
||||
- rename_metrics:
|
||||
system.network.connections: system.network.connection.count
|
||||
1.34.0:
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/2295
|
||||
- rename_metrics:
|
||||
cpu.time: system.cpu.time
|
||||
cpu.utilization: system.cpu.utilization
|
||||
cpu.frequency: system.cpu.frequency
|
||||
1.33.0:
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1982
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
feature_flag.provider_name: feature_flag.provider.name
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1994
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
feature_flag.evaluation.error.message: error.message
|
||||
1.32.0:
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1989
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
feature_flag.evaluation.reason: feature_flag.result.reason
|
||||
feature_flag.variant: feature_flag.result.variant
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/2042
|
||||
- rename_metrics:
|
||||
otel.sdk.span.live.count: otel.sdk.span.live
|
||||
otel.sdk.span.ended.count: otel.sdk.span.ended
|
||||
otel.sdk.processor.span.processed.count: otel.sdk.processor.span.processed
|
||||
otel.sdk.exporter.span.inflight.count: otel.sdk.exporter.span.inflight
|
||||
otel.sdk.exporter.span.exported.count: otel.sdk.exporter.span.exported
|
||||
1.31.0:
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1880
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
android.state: android.app.state
|
||||
io.state: ios.app.state
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
k8s.replication_controller.desired_pods: k8s.replicationcontroller.desired_pods
|
||||
k8s.replication_controller.available_pods: k8s.replicationcontroller.available_pods
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1896
|
||||
- rename_metrics:
|
||||
system.cpu.time: cpu.time
|
||||
system.cpu.utilization: cpu.utilization
|
||||
system.cpu.frequency: cpu.frequency
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1896
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
system.cpu.logical_number: cpu.logical_number
|
||||
1.30.0:
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1632
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
gen_ai.openai.request.seed: gen_ai.request.seed
|
||||
system.network.state: network.connection.state
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1624
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
code.function: code.function.name
|
||||
code.filepath: code.file.path
|
||||
code.lineno: code.line.number
|
||||
code.column: code.column.number
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1734
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.system: db.system.name
|
||||
db.cassandra.coordinator.dc: cassandra.coordinator.dc
|
||||
db.cassandra.coordinator.id: cassandra.coordinator.id
|
||||
db.cassandra.consistency_level: cassandra.consistency.level
|
||||
db.cassandra.idempotence: cassandra.query.idempotent
|
||||
db.cassandra.page_size: cassandra.page.size
|
||||
db.cassandra.speculative_execution_count: cassandra.speculative_execution.count
|
||||
db.cosmosdb.client_id: azure.client.id
|
||||
db.cosmosdb.connection_mode: azure.cosmosdb.connection.mode
|
||||
db.cosmosdb.consistency_level: azure.cosmosdb.consistency.level
|
||||
db.cosmosdb.request_charge: azure.cosmosdb.operation.request_charge
|
||||
db.cosmosdb.request_content_length: azure.cosmosdb.request.body.size
|
||||
db.cosmosdb.regions_contacted: azure.cosmosdb.operation.contacted_regions
|
||||
db.cosmosdb.sub_status_code: azure.cosmosdb.response.sub_status_code
|
||||
db.elasticsearch.node.name: elasticsearch.node.name
|
||||
# db.elasticsearch.path_parts is a template attribute, schema transformation
|
||||
# does not support it, adding as a comment for consistency
|
||||
# db.elasticsearch.path_parts.<key> -> db.operation.parameter.<key>
|
||||
metrics:
|
||||
changes:
|
||||
- rename_metrics:
|
||||
db.client.cosmosdb.operation.request_charge: azure.cosmosdb.client.operation.request_charge
|
||||
db.client.cosmosdb.active_instance.count: azure.cosmosdb.client.active_instance.count
|
||||
|
||||
1.29.0:
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1520
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
process.executable.build_id.profiling: process.executable.build_id.htlhash
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1383
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
vcs.repository.change.id: vcs.change.id
|
||||
vcs.repository.change.title: vcs.change.title
|
||||
vcs.repository.ref.name: vcs.ref.head.name
|
||||
vcs.repository.ref.revision: vcs.ref.head.revision
|
||||
vcs.repository.ref.type: vcs.ref.head.type
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1492
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
system.device: network.interface.name
|
||||
apply_to_metrics:
|
||||
- container.network.io
|
||||
- system.network.dropped
|
||||
- system.network.errors
|
||||
- system.network.io
|
||||
- system.network.connections
|
||||
1.28.0:
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1422
|
||||
- rename_metrics:
|
||||
messaging.client.published.messages: messaging.client.sent.messages
|
||||
1.27.0:
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1216
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
tls.client.server_name: server.address
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1075
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
deployment.environment: deployment.environment.name
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1245
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.kafka.message.offset: messaging.kafka.offset
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/815
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.kafka.consumer.group: messaging.consumer.group.name
|
||||
messaging.rocketmq.client_group: messaging.consumer.group.name
|
||||
messaging.eventhubs.consumer.group: messaging.consumer.group.name
|
||||
messaging.servicebus.destination.subscription_name: messaging.destination.subscription.name
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1200
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
gen_ai.usage.completion_tokens: gen_ai.usage.output_tokens
|
||||
gen_ai.usage.prompt_tokens: gen_ai.usage.input_tokens
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1002
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.elasticsearch.cluster.name: db.namespace
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1125
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.client.connections.state: db.client.connection.state
|
||||
apply_to_metrics:
|
||||
- db.client.connection.count
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.client.connections.pool.name: db.client.connection.pool.name
|
||||
apply_to_metrics:
|
||||
- db.client.connection.count
|
||||
- db.client.connection.idle.max
|
||||
- db.client.connection.idle.min
|
||||
- db.client.connection.max
|
||||
- db.client.connection.pending_requests
|
||||
- db.client.connection.timeouts
|
||||
- db.client.connection.create_time
|
||||
- db.client.connection.wait_time
|
||||
- db.client.connection.use_time
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1006
|
||||
- rename_metrics:
|
||||
messaging.publish.messages: messaging.client.published.messages
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1026
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
system.cpu.state: cpu.mode
|
||||
process.cpu.state: cpu.mode
|
||||
container.cpu.state: cpu.mode
|
||||
apply_to_metrics:
|
||||
- system.cpu.time
|
||||
- system.cpu.utilization
|
||||
- process.cpu.time
|
||||
- process.cpu.utilization
|
||||
- container.cpu.time
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/1265
|
||||
- rename_metrics:
|
||||
jvm.buffer.memory.usage: jvm.buffer.memory.used
|
||||
1.26.0:
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/966
|
||||
- rename_metrics:
|
||||
db.client.connections.usage: db.client.connection.count
|
||||
db.client.connections.idle.max: db.client.connection.idle.max
|
||||
db.client.connections.idle.min: db.client.connection.idle.min
|
||||
db.client.connections.max: db.client.connection.max
|
||||
db.client.connections.pending_requests: db.client.connection.pending_requests
|
||||
db.client.connections.timeouts: db.client.connection.timeouts
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/948
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.client_id: messaging.client.id
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/909
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
state: db.client.connections.state
|
||||
apply_to_metrics:
|
||||
- db.client.connections.usage
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
pool.name: db.client.connections.pool.name
|
||||
apply_to_metrics:
|
||||
- db.client.connections.usage
|
||||
- db.client.connections.idle.max
|
||||
- db.client.connections.idle.min
|
||||
- db.client.connections.max
|
||||
- db.client.connections.pending_requests
|
||||
- db.client.connections.timeouts
|
||||
- db.client.connections.create_time
|
||||
- db.client.connections.wait_time
|
||||
- db.client.connections.use_time
|
||||
all:
|
||||
changes:
|
||||
# https://github:com/open-telemetry/semantic-conventions/pull/731/
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
enduser.id: user.id
|
||||
|
||||
1.25.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/911
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.name: db.namespace
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/870
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.sql.table: db.collection.name
|
||||
db.mongodb.collection: db.collection.name
|
||||
db.cosmosdb.container: db.collection.name
|
||||
db.cassandra.table: db.collection.name
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/798
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.kafka.destination.partition: messaging.destination.partition.id
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/875
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.operation: db.operation.name
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/913
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.operation: messaging.operation.type
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/866
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.statement: db.query.text
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/484
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
system.processes.status: system.process.status
|
||||
apply_to_metrics:
|
||||
- system.processes.count
|
||||
- rename_metrics:
|
||||
system.processes.count: system.process.count
|
||||
system.processes.created: system.process.created
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/625
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
container.labels: container.label
|
||||
k8s.pod.labels: k8s.pod.label
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/330
|
||||
- rename_metrics:
|
||||
process.threads: process.thread.count
|
||||
process.open_file_descriptors: process.open_file_descriptor.count
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
state: process.cpu.state
|
||||
apply_to_metrics:
|
||||
- process.cpu.time
|
||||
- process.cpu.utilization
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
direction: disk.io.direction
|
||||
apply_to_metrics:
|
||||
- process.disk.io
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
type: process.context_switch_type
|
||||
apply_to_metrics:
|
||||
- process.context_switches
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
direction: network.io.direction
|
||||
apply_to_metrics:
|
||||
- process.network.io
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
type: process.paging.fault_type
|
||||
apply_to_metrics:
|
||||
- process.paging.faults
|
||||
all:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/854
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
message.type: rpc.message.type
|
||||
message.id: rpc.message.id
|
||||
message.compressed_size: rpc.message.compressed_size
|
||||
message.uncompressed_size: rpc.message.uncompressed_size
|
||||
|
||||
1.24.0:
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/536
|
||||
- rename_metrics:
|
||||
jvm.memory.usage: jvm.memory.used
|
||||
jvm.memory.usage_after_last_gc: jvm.memory.used_after_last_gc
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/530
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
system.network.io.direction: network.io.direction
|
||||
system.disk.io.direction: disk.io.direction
|
||||
1.23.1:
|
||||
1.23.0:
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/20
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
thread.daemon: jvm.thread.daemon
|
||||
apply_to_metrics:
|
||||
- jvm.thread.count
|
||||
1.22.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/229
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.message.payload_size_bytes: messaging.message.body.size
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/374
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
http.resend_count: http.request.resend_count
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/224
|
||||
- rename_metrics:
|
||||
http.client.duration: http.client.request.duration
|
||||
http.server.duration: http.server.request.duration
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/241
|
||||
- rename_metrics:
|
||||
process.runtime.jvm.memory.usage: jvm.memory.usage
|
||||
process.runtime.jvm.memory.committed: jvm.memory.committed
|
||||
process.runtime.jvm.memory.limit: jvm.memory.limit
|
||||
process.runtime.jvm.memory.usage_after_last_gc: jvm.memory.usage_after_last_gc
|
||||
process.runtime.jvm.gc.duration: jvm.gc.duration
|
||||
# also https://github.com/open-telemetry/semantic-conventions/pull/252
|
||||
process.runtime.jvm.threads.count: jvm.thread.count
|
||||
# also https://github.com/open-telemetry/semantic-conventions/pull/252
|
||||
process.runtime.jvm.classes.loaded: jvm.class.loaded
|
||||
# also https://github.com/open-telemetry/semantic-conventions/pull/252
|
||||
process.runtime.jvm.classes.unloaded: jvm.class.unloaded
|
||||
# also https://github.com/open-telemetry/semantic-conventions/pull/252
|
||||
# and https://github.com/open-telemetry/semantic-conventions/pull/60
|
||||
process.runtime.jvm.classes.current_loaded: jvm.class.count
|
||||
process.runtime.jvm.cpu.time: jvm.cpu.time
|
||||
process.runtime.jvm.cpu.recent_utilization: jvm.cpu.recent_utilization
|
||||
process.runtime.jvm.memory.init: jvm.memory.init
|
||||
process.runtime.jvm.system.cpu.utilization: jvm.system.cpu.utilization
|
||||
process.runtime.jvm.system.cpu.load_1m: jvm.system.cpu.load_1m
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/253
|
||||
process.runtime.jvm.buffer.usage: jvm.buffer.memory.usage
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/253
|
||||
process.runtime.jvm.buffer.limit: jvm.buffer.memory.limit
|
||||
process.runtime.jvm.buffer.count: jvm.buffer.count
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/20
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
type: jvm.memory.type
|
||||
pool: jvm.memory.pool.name
|
||||
apply_to_metrics:
|
||||
- jvm.memory.usage
|
||||
- jvm.memory.committed
|
||||
- jvm.memory.limit
|
||||
- jvm.memory.usage_after_last_gc
|
||||
- jvm.memory.init
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
name: jvm.gc.name
|
||||
action: jvm.gc.action
|
||||
apply_to_metrics:
|
||||
- jvm.gc.duration
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
daemon: thread.daemon
|
||||
apply_to_metrics:
|
||||
- jvm.threads.count
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
pool: jvm.buffer.pool.name
|
||||
apply_to_metrics:
|
||||
- jvm.buffer.memory.usage
|
||||
- jvm.buffer.memory.limit
|
||||
- jvm.buffer.count
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/89
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
state: system.cpu.state
|
||||
cpu: system.cpu.logical_number
|
||||
apply_to_metrics:
|
||||
- system.cpu.time
|
||||
- system.cpu.utilization
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
state: system.memory.state
|
||||
apply_to_metrics:
|
||||
- system.memory.usage
|
||||
- system.memory.utilization
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
state: system.paging.state
|
||||
apply_to_metrics:
|
||||
- system.paging.usage
|
||||
- system.paging.utilization
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
type: system.paging.type
|
||||
direction: system.paging.direction
|
||||
apply_to_metrics:
|
||||
- system.paging.faults
|
||||
- system.paging.operations
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
device: system.device
|
||||
direction: system.disk.direction
|
||||
apply_to_metrics:
|
||||
- system.disk.io
|
||||
- system.disk.operations
|
||||
- system.disk.io_time
|
||||
- system.disk.operation_time
|
||||
- system.disk.merged
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
device: system.device
|
||||
state: system.filesystem.state
|
||||
type: system.filesystem.type
|
||||
mode: system.filesystem.mode
|
||||
mountpoint: system.filesystem.mountpoint
|
||||
apply_to_metrics:
|
||||
- system.filesystem.usage
|
||||
- system.filesystem.utilization
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
device: system.device
|
||||
direction: system.network.direction
|
||||
protocol: network.protocol
|
||||
state: system.network.state
|
||||
apply_to_metrics:
|
||||
- system.network.dropped
|
||||
- system.network.packets
|
||||
- system.network.errors
|
||||
- system.network.io
|
||||
- system.network.connections
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
status: system.processes.status
|
||||
apply_to_metrics:
|
||||
- system.processes.count
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/247
|
||||
- rename_metrics:
|
||||
http.server.request.size: http.server.request.body.size
|
||||
http.server.response.size: http.server.response.body.size
|
||||
resources:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/178
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
telemetry.auto.version: telemetry.distro.version
|
||||
1.21.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3336
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.kafka.client_id: messaging.client_id
|
||||
messaging.rocketmq.client_id: messaging.client_id
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3402
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
# net.peer.(name|port) attributes were usually populated on client side
|
||||
# so they should be usually translated to server.(address|port)
|
||||
# net.host.* attributes were only populated on server side
|
||||
net.host.name: server.address
|
||||
net.host.port: server.port
|
||||
# was only populated on client side
|
||||
net.sock.peer.name: server.socket.domain
|
||||
# net.sock.peer.(addr|port) mapping is not possible
|
||||
# since they applied to both client and server side
|
||||
# were only populated on server side
|
||||
net.sock.host.addr: server.socket.address
|
||||
net.sock.host.port: server.socket.port
|
||||
http.client_ip: client.address
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3426
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
net.protocol.name: network.protocol.name
|
||||
net.protocol.version: network.protocol.version
|
||||
net.host.connection.type: network.connection.type
|
||||
net.host.connection.subtype: network.connection.subtype
|
||||
net.host.carrier.name: network.carrier.name
|
||||
net.host.carrier.mcc: network.carrier.mcc
|
||||
net.host.carrier.mnc: network.carrier.mnc
|
||||
net.host.carrier.icc: network.carrier.icc
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3355
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
http.method: http.request.method
|
||||
http.status_code: http.response.status_code
|
||||
http.scheme: url.scheme
|
||||
http.url: url.full
|
||||
http.request_content_length: http.request.body.size
|
||||
http.response_content_length: http.response.body.size
|
||||
metrics:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/semantic-conventions/pull/53
|
||||
- rename_metrics:
|
||||
process.runtime.jvm.cpu.utilization: process.runtime.jvm.cpu.recent_utilization
|
||||
1.20.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3272
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
net.app.protocol.name: net.protocol.name
|
||||
net.app.protocol.version: net.protocol.version
|
||||
1.19.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3209
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
faas.execution: faas.invocation_id
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3188
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
faas.id: cloud.resource_id
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3190
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
http.user_agent: user_agent.original
|
||||
resources:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/3190
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
browser.user_agent: user_agent.original
|
||||
1.18.0:
|
||||
1.17.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/2957
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
messaging.consumer_id: messaging.consumer.id
|
||||
messaging.protocol: net.app.protocol.name
|
||||
messaging.protocol_version: net.app.protocol.version
|
||||
messaging.destination: messaging.destination.name
|
||||
messaging.temp_destination: messaging.destination.temporary
|
||||
messaging.destination_kind: messaging.destination.kind
|
||||
messaging.message_id: messaging.message.id
|
||||
messaging.conversation_id: messaging.message.conversation_id
|
||||
messaging.message_payload_size_bytes: messaging.message.payload_size_bytes
|
||||
messaging.message_payload_compressed_size_bytes: messaging.message.payload_compressed_size_bytes
|
||||
messaging.rabbitmq.routing_key: messaging.rabbitmq.destination.routing_key
|
||||
messaging.kafka.message_key: messaging.kafka.message.key
|
||||
messaging.kafka.partition: messaging.kafka.destination.partition
|
||||
messaging.kafka.tombstone: messaging.kafka.message.tombstone
|
||||
messaging.rocketmq.message_type: messaging.rocketmq.message.type
|
||||
messaging.rocketmq.message_tag: messaging.rocketmq.message.tag
|
||||
messaging.rocketmq.message_keys: messaging.rocketmq.message.keys
|
||||
messaging.kafka.consumer_group: messaging.kafka.consumer.group
|
||||
1.16.0:
|
||||
1.15.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/2743
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
http.retry_count: http.resend_count
|
||||
1.14.0:
|
||||
1.13.0:
|
||||
spans:
|
||||
changes:
|
||||
# https://github.com/open-telemetry/opentelemetry-specification/pull/2614
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
net.peer.ip: net.sock.peer.addr
|
||||
net.host.ip: net.sock.host.addr
|
||||
1.12.0:
|
||||
1.11.0:
|
||||
1.10.0:
|
||||
1.9.0:
|
||||
1.8.0:
|
||||
spans:
|
||||
changes:
|
||||
- rename_attributes:
|
||||
attribute_map:
|
||||
db.cassandra.keyspace: db.name
|
||||
db.hbase.namespace: db.name
|
||||
1.7.0:
|
||||
1.6.1:
|
||||
1.5.0:
|
||||
1.4.0:
|
||||
@@ -18,7 +18,6 @@ pytest_plugins = [
|
||||
"fixtures.logs",
|
||||
"fixtures.traces",
|
||||
"fixtures.metrics",
|
||||
"fixtures.queriercommon",
|
||||
"fixtures.metadata",
|
||||
"fixtures.meter",
|
||||
"fixtures.browser",
|
||||
|
||||
44
tests/fixtures/promapi.py
vendored
Normal file
44
tests/fixtures/promapi.py
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
"""Client helpers for the /prometheus/api/v1 endpoints."""
|
||||
|
||||
import math
|
||||
|
||||
import requests
|
||||
|
||||
from fixtures import types
|
||||
|
||||
SPECIALS = {"NaN": math.nan, "Inf": math.inf, "+Inf": math.inf, "-Inf": -math.inf}
|
||||
QUERY_TIMEOUT = 30
|
||||
|
||||
|
||||
def prom_api_get(signoz: types.SigNoz, token: str, path: str, params: dict) -> requests.Response:
|
||||
return requests.get(
|
||||
signoz.self.host_configs["8080"].get(path),
|
||||
params=params,
|
||||
timeout=QUERY_TIMEOUT,
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
)
|
||||
|
||||
|
||||
def prom_api_value(v: str) -> float:
|
||||
"""Prometheus API sample values are strings, including "NaN" and "+Inf"."""
|
||||
if v in SPECIALS:
|
||||
return SPECIALS[v]
|
||||
return float(v)
|
||||
|
||||
|
||||
def series_from_prom_result(result_type: str, result) -> dict[tuple, dict[int, float]]:
|
||||
"""Flattens a matrix/vector/scalar result into
|
||||
{sorted-labels tuple: {unix_ms: value}}."""
|
||||
out: dict[tuple, dict[int, float]] = {}
|
||||
if result_type == "matrix":
|
||||
for series in result:
|
||||
points = {round(float(ts) * 1000): prom_api_value(v) for ts, v in series.get("values") or []}
|
||||
out[tuple(sorted((series.get("metric") or {}).items()))] = points
|
||||
elif result_type == "vector":
|
||||
for series in result:
|
||||
ts, v = series["value"]
|
||||
out[tuple(sorted((series.get("metric") or {}).items()))] = {round(float(ts) * 1000): prom_api_value(v)}
|
||||
elif result_type == "scalar":
|
||||
ts, v = result
|
||||
out[()] = {round(float(ts) * 1000): prom_api_value(v)}
|
||||
return out
|
||||
112
tests/fixtures/promqltestcorpus.py
vendored
Normal file
112
tests/fixtures/promqltestcorpus.py
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Shared helpers for suites that replay the frozen promqltest corpus
|
||||
(tests/integration/testdata/promqltestcorpus/corpus.json)."""
|
||||
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from collections.abc import Callable
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from fixtures.metrics import Metrics
|
||||
|
||||
TESTDATA_DIR = os.path.join(os.path.dirname(__file__), "..", "integration", "testdata", "promqltestcorpus")
|
||||
CORPUS_FILE = os.path.join(TESTDATA_DIR, "corpus.json")
|
||||
|
||||
ISOLATION_GAP_MS = 2 * 3600 * 1000
|
||||
SPECIALS = {"NaN": math.nan, "Inf": math.inf, "+Inf": math.inf, "-Inf": -math.inf}
|
||||
|
||||
|
||||
def decode_corpus_value(v: float | str) -> float:
|
||||
if isinstance(v, str):
|
||||
return SPECIALS[v]
|
||||
return float(v)
|
||||
|
||||
|
||||
def values_close(a: float, b: float) -> bool:
|
||||
"""Expected corpus values carry the v5 API's rounding (>=1: three decimal
|
||||
places; <1: three significant digits). One rounding quantum covers both a
|
||||
raw-vs-rounded comparison and a boundary that rounds either way."""
|
||||
if math.isnan(a) or math.isnan(b):
|
||||
return math.isnan(a) and math.isnan(b)
|
||||
if math.isinf(a) or math.isinf(b):
|
||||
return a == b
|
||||
if a == b:
|
||||
return True
|
||||
scale = max(abs(a), abs(b))
|
||||
if scale >= 1:
|
||||
quantum = max(1e-3, scale * 1e-9)
|
||||
else:
|
||||
quantum = 10 ** (math.floor(math.log10(scale)) - 2)
|
||||
return abs(a - b) <= quantum + 1e-12
|
||||
|
||||
|
||||
def labelset(labels: dict[str, str]) -> tuple:
|
||||
return tuple(sorted(labels.items()))
|
||||
|
||||
|
||||
def ledger(filename: str) -> dict[str, str]:
|
||||
path = os.path.join(TESTDATA_DIR, filename)
|
||||
if not os.path.exists(path):
|
||||
return {}
|
||||
with open(path, encoding="utf-8") as f:
|
||||
return json.load(f)["divergences"]
|
||||
|
||||
|
||||
@pytest.fixture(name="ingest_promqltest_corpus")
|
||||
def ingest_promqltest_corpus(insert_metrics: Callable[[list[Metrics]], None]) -> Callable[[], tuple[dict, dict[int, int]]]:
|
||||
"""Yields a callable that loads the corpus, lays its datasets end to end
|
||||
on the timeline, ingests every sample, and returns (corpus, dataset base
|
||||
timestamps).
|
||||
|
||||
Dataset bases are hour-aligned: registration rows are hour-bucketed, so
|
||||
behavior depends on where samples fall relative to hour boundaries, and
|
||||
exact known-divergences enforcement needs identical placement every run.
|
||||
Datasets sit on disjoint windows (2h gaps, far beyond the 5m lookback) so
|
||||
one bulk ingest serves every case without cross-talk."""
|
||||
|
||||
def ingest() -> tuple[dict, dict[int, int]]:
|
||||
with open(CORPUS_FILE, encoding="utf-8") as f:
|
||||
corpus = json.load(f)
|
||||
|
||||
cases_by_dataset: dict[int, list[dict]] = {}
|
||||
for case in corpus["cases"]:
|
||||
cases_by_dataset.setdefault(case["dataset"], []).append(case)
|
||||
|
||||
spans = {}
|
||||
for ds in corpus["datasets"]:
|
||||
sample_max = max((s["samples"][-1][0] for s in ds["series"] if s["samples"]), default=0)
|
||||
case_max = max((c["end_ms"] for c in cases_by_dataset.get(ds["id"], [])), default=0)
|
||||
spans[ds["id"]] = max(sample_max, case_max) + corpus["meta"]["lookback_ms"]
|
||||
|
||||
hour_ms = 3_600_000
|
||||
advances = {ds["id"]: -(-(spans[ds["id"]] + ISOLATION_GAP_MS) // hour_ms) * hour_ms for ds in corpus["datasets"]}
|
||||
total = sum(advances.values())
|
||||
now = datetime.now(tz=UTC).replace(second=0, microsecond=0)
|
||||
cursor = (int((now - timedelta(hours=1)).timestamp() * 1000) - total) // hour_ms * hour_ms
|
||||
|
||||
bases: dict[int, int] = {}
|
||||
metrics: list[Metrics] = []
|
||||
for ds in corpus["datasets"]:
|
||||
bases[ds["id"]] = cursor
|
||||
for series in ds["series"]:
|
||||
labels = dict(series["labels"])
|
||||
metric_name = labels.pop("__name__")
|
||||
for off_ms, raw in series["samples"]:
|
||||
stale = raw == "stale"
|
||||
metrics.append(
|
||||
Metrics(
|
||||
metric_name=metric_name,
|
||||
labels=labels,
|
||||
timestamp=datetime.fromtimestamp((cursor + off_ms) / 1000, tz=UTC),
|
||||
value=0.0 if stale else decode_corpus_value(raw),
|
||||
flags=1 if stale else 0,
|
||||
)
|
||||
)
|
||||
cursor += advances[ds["id"]]
|
||||
|
||||
insert_metrics(metrics)
|
||||
return corpus, bases
|
||||
|
||||
return ingest
|
||||
4
tests/fixtures/querier.py
vendored
4
tests/fixtures/querier.py
vendored
@@ -704,7 +704,6 @@ def build_raw_query(
|
||||
order: list[dict] | None = None,
|
||||
limit: int | None = None,
|
||||
filter_expression: str | None = None,
|
||||
select_fields: list[dict] | None = None,
|
||||
step_interval: int = DEFAULT_STEP_INTERVAL,
|
||||
disabled: bool = False,
|
||||
) -> dict:
|
||||
@@ -724,9 +723,6 @@ def build_raw_query(
|
||||
if filter_expression:
|
||||
spec["filter"] = {"expression": filter_expression}
|
||||
|
||||
if select_fields:
|
||||
spec["selectFields"] = select_fields
|
||||
|
||||
return {"type": "builder_query", "spec": spec}
|
||||
|
||||
|
||||
|
||||
124
tests/fixtures/queriercommon.py
vendored
124
tests/fixtures/queriercommon.py
vendored
@@ -1,124 +0,0 @@
|
||||
"""Seed data for the queriercommon keyless-semantics tests.
|
||||
|
||||
Three identities exist in every signal. GOLD and SILVER carry the test keys.
|
||||
NONE carries no key at all. The tests assert which identities a filter
|
||||
returns, so the membership of NONE is the point of every case.
|
||||
|
||||
The attribute names are outside every semantic-convention family, so the
|
||||
seeded data pins base behavior with any semconv overlay state.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable, Generator
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from fixtures.logs import Logs
|
||||
from fixtures.metrics import Metrics
|
||||
from fixtures.querier import aligned_epoch
|
||||
from fixtures.traces import TraceIdGenerator, Traces, TracesKind, TracesStatusCode
|
||||
|
||||
PREFIX = "keyless-sem"
|
||||
STRING_KEY = "tenant.tier"
|
||||
NUMBER_KEY = "retry.count"
|
||||
METRIC_NAME = "keyless_semantics_gauge"
|
||||
METRIC_LABEL = "tenant_tier"
|
||||
|
||||
# Row identities, keyed by the value of the string key that each row carries.
|
||||
GOLD = f"{PREFIX}-gold"
|
||||
SILVER = f"{PREFIX}-silver"
|
||||
NONE = f"{PREFIX}-none" # carries no string key and no number key
|
||||
|
||||
# (identity, string-key value, number-key value, insert offset)
|
||||
_ROWS = [
|
||||
(GOLD, "gold", 0, timedelta(seconds=3)),
|
||||
(SILVER, "silver", 5, timedelta(seconds=2)),
|
||||
(NONE, None, None, timedelta(seconds=1)),
|
||||
]
|
||||
|
||||
|
||||
def _resources(identity: str, tier: str | None) -> dict:
|
||||
base = {"service.name": identity}
|
||||
if tier is not None:
|
||||
base[STRING_KEY] = tier
|
||||
return base
|
||||
|
||||
|
||||
def _attributes(tier: str | None, retries: int | None) -> dict:
|
||||
attrs: dict = {}
|
||||
if tier is not None:
|
||||
attrs[STRING_KEY] = tier
|
||||
if retries is not None:
|
||||
attrs[NUMBER_KEY] = retries
|
||||
return attrs
|
||||
|
||||
|
||||
@pytest.fixture(name="keyless_rows", scope="function")
|
||||
def keyless_rows(
|
||||
insert_logs: Callable[[list[Logs]], None],
|
||||
insert_traces: Callable[[list[Traces]], None],
|
||||
) -> Generator[datetime]:
|
||||
"""Inserts one span and one log per identity: GOLD (string "gold",
|
||||
number 0), SILVER (string "silver", number 5), and NONE (no keys).
|
||||
Yields the base timestamp. Span name and log body are the identity."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0) - timedelta(minutes=1)
|
||||
|
||||
insert_traces(
|
||||
[
|
||||
Traces(
|
||||
timestamp=now - offset,
|
||||
duration=timedelta(milliseconds=10),
|
||||
trace_id=TraceIdGenerator.trace_id(),
|
||||
span_id=TraceIdGenerator.span_id(),
|
||||
name=identity,
|
||||
kind=TracesKind.SPAN_KIND_SERVER,
|
||||
status_code=TracesStatusCode.STATUS_CODE_OK,
|
||||
resources=_resources(identity, tier),
|
||||
attributes=_attributes(tier, retries),
|
||||
)
|
||||
for identity, tier, retries, offset in _ROWS
|
||||
]
|
||||
)
|
||||
insert_logs(
|
||||
[
|
||||
Logs(
|
||||
timestamp=now - offset,
|
||||
body=identity,
|
||||
resources=_resources(identity, tier),
|
||||
attributes=_attributes(tier, retries),
|
||||
)
|
||||
for identity, tier, retries, offset in _ROWS
|
||||
]
|
||||
)
|
||||
yield now
|
||||
|
||||
|
||||
@pytest.fixture(name="keyless_series", scope="function")
|
||||
def keyless_series(insert_metrics: Callable[[list[Metrics]], None]) -> Generator[tuple[int, int]]:
|
||||
"""Inserts three gauge series: GOLD and SILVER carry the metric label,
|
||||
NONE does not. The `service` label is the identity. Yields the
|
||||
(start, end) epoch-second window that covers the points."""
|
||||
start = aligned_epoch(timedelta(minutes=30))
|
||||
points = 5
|
||||
|
||||
def labels(identity: str, tier: str | None) -> dict:
|
||||
base = {"service": identity}
|
||||
if tier is not None:
|
||||
base[METRIC_LABEL] = tier
|
||||
return base
|
||||
|
||||
insert_metrics(
|
||||
[
|
||||
Metrics(
|
||||
metric_name=METRIC_NAME,
|
||||
labels=labels(identity, tier),
|
||||
timestamp=datetime.fromtimestamp(start + minute * 60, tz=UTC),
|
||||
value=10.0,
|
||||
type_="Gauge",
|
||||
is_monotonic=False,
|
||||
)
|
||||
for identity, tier in ((GOLD, "gold"), (SILVER, "silver"), (NONE, None))
|
||||
for minute in range(points)
|
||||
]
|
||||
)
|
||||
yield start, start + points * 60
|
||||
12
tests/integration/testdata/promqltestcorpus/known_divergences_promapi.json
vendored
Normal file
12
tests/integration/testdata/promqltestcorpus/known_divergences_promapi.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"note": "Divergences of the /prometheus/api/v1 endpoints, served by the clickhousev2 provider, from the upstream reference engine. 01_prometheus_api_corpus.py enforces this set exactly in both directions. All current entries are the Kahan class recorded in known_divergences_v2.json: the engine sums with Kahan compensation and an overflow-free incremental mean, ClickHouse's aggregates are naive. Only the [instant-coarse] variants appear here: they are range-encoded, so they serve transpiled; the [base] instant evals go through /prometheus/api/v1/query on the exact engine path.",
|
||||
"divergences": {
|
||||
"aggregators.test:651[instant-coarse]": "avg over near-max-float64 values: avgForEach overflows to +Inf where the engine's incremental mean does not",
|
||||
"aggregators.test:654[instant-coarse]": "avg over near-min-float64 values: avgForEach overflows to -Inf",
|
||||
"aggregators.test:687[instant-coarse]": "sum over {1e100, -1e100, small}: naive summation cancels to 0 where the engine's Kahan sum keeps 10",
|
||||
"aggregators.test:695[instant-coarse]": "avg over {1e100, -1e100, small}: same cancellation divided by count",
|
||||
"functions.test:1084[instant-coarse]": "sum_over_time over a ±1e100 window: the disjoint coarse-step form's arraySum cancels to 0",
|
||||
"functions.test:1087[instant-coarse]": "avg_over_time, same window and cancellation as functions.test:1084",
|
||||
"functions.test:1149[instant-coarse]": "avg_over_time over ±2.258e220 samples: naive slide summation leaves a ~1e202 residue where the engine cancels to 0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
Replay the frozen promqltest corpus against the /prometheus/api/v1
|
||||
endpoints, with clickhousev2 as the serving provider (see conftest.py).
|
||||
|
||||
The oracle is the same committed corpus the promqlconformance package
|
||||
replays through /api/v5/query_range. This package differs in two ways.
|
||||
Range cases go to /prometheus/api/v1/query_range, where a RangeExecutor
|
||||
provider serves transpiled statements when the shape allows. Instant cases
|
||||
go to /prometheus/api/v1/query with a real `time` parameter, so they need
|
||||
no grid encoding.
|
||||
"""
|
||||
|
||||
import json
|
||||
from collections.abc import Callable
|
||||
from http import HTTPStatus
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
|
||||
from fixtures.promapi import prom_api_get, series_from_prom_result
|
||||
from fixtures.promqltestcorpus import decode_corpus_value, labelset, ledger, values_close
|
||||
|
||||
|
||||
def test_prometheus_api_corpus(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
ingest_promqltest_corpus: Callable[[], tuple[dict, dict[int, int]]],
|
||||
) -> None:
|
||||
corpus, bases = ingest_promqltest_corpus()
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
failures: list[str] = []
|
||||
for case in corpus["cases"]:
|
||||
base = bases[case["dataset"]]
|
||||
start_ms = base + case["start_ms"]
|
||||
end_ms = base + case["end_ms"]
|
||||
step_s = max(1, case["step_ms"] // 1000)
|
||||
case_id = f"{case['source']}[{case['variant']}]"
|
||||
|
||||
if case["instant"]:
|
||||
response = prom_api_get(signoz, token, "/prometheus/api/v1/query", {"query": case["expr"], "time": end_ms / 1000})
|
||||
else:
|
||||
response = prom_api_get(
|
||||
signoz,
|
||||
token,
|
||||
"/prometheus/api/v1/query_range",
|
||||
{"query": case["expr"], "start": start_ms / 1000, "end": end_ms / 1000, "step": step_s},
|
||||
)
|
||||
if response.status_code != HTTPStatus.OK:
|
||||
failures.append(f"{case_id}: HTTP {response.status_code} for {case['expr']!r}: {response.text[:200]}")
|
||||
continue
|
||||
body = response.json()
|
||||
if body.get("status") != "success":
|
||||
failures.append(f"{case_id}: status {body.get('status')!r} for {case['expr']!r}: {json.dumps(body)[:200]}")
|
||||
continue
|
||||
|
||||
actual = series_from_prom_result(body["data"]["resultType"], body["data"]["result"])
|
||||
expected = {
|
||||
labelset(res["labels"]): {base + off_ms: decode_corpus_value(v) for off_ms, v in res["points"]}
|
||||
for res in case["expected"]
|
||||
}
|
||||
|
||||
if set(actual) != set(expected):
|
||||
missing = set(expected) - set(actual)
|
||||
extra = set(actual) - set(expected)
|
||||
failures.append(f"{case_id}: series mismatch for {case['expr']!r} (missing={sorted(missing)[:3]} extra={sorted(extra)[:3]})")
|
||||
continue
|
||||
for lset, exp_points in expected.items():
|
||||
act_points = actual[lset]
|
||||
if set(act_points) != set(exp_points):
|
||||
failures.append(f"{case_id}: timestamp mismatch for {case['expr']!r} series {dict(lset)} (expected {len(exp_points)} points, got {len(act_points)})")
|
||||
break
|
||||
for ts, exp_v in exp_points.items():
|
||||
if not values_close(act_points[ts], exp_v):
|
||||
failures.append(f"{case_id}: value mismatch for {case['expr']!r} series {dict(lset)} at {ts}: expected {exp_v}, got {act_points[ts]}")
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
|
||||
for f_line in failures:
|
||||
print("DIVERGED", f_line)
|
||||
|
||||
known = ledger("known_divergences_promapi.json")
|
||||
failed_ids = {f_line.split(": ", 1)[0] for f_line in failures}
|
||||
unexpected = [f_line for f_line in failures if f_line.split(": ", 1)[0] not in known]
|
||||
now_passing = sorted(set(known) - failed_ids)
|
||||
|
||||
assert not unexpected, f"{len(unexpected)} corpus cases diverged beyond the known set:\n" + "\n".join(unexpected[:25])
|
||||
assert not now_passing, f"{len(now_passing)} known divergences now pass — remove them from known_divergences_promapi.json: {now_passing[:25]}"
|
||||
38
tests/integration/tests/promapiconformance/conftest.py
Normal file
38
tests/integration/tests/promapiconformance/conftest.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import pytest
|
||||
from testcontainers.core.container import Network
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.promqltestcorpus import ingest_promqltest_corpus # noqa: F401 pylint: disable=unused-import
|
||||
from fixtures.signoz import create_signoz
|
||||
|
||||
|
||||
@pytest.fixture(name="signoz", scope="package")
|
||||
def signoz_promapi_v2(
|
||||
network: Network,
|
||||
migrator: types.Operation, # pylint: disable=unused-argument
|
||||
zeus: types.TestContainerDocker,
|
||||
gateway: types.TestContainerDocker,
|
||||
sqlstore: types.TestContainerSQL,
|
||||
clickhouse: types.TestContainerClickhouse,
|
||||
request: pytest.FixtureRequest,
|
||||
pytestconfig: pytest.Config,
|
||||
) -> types.SigNoz:
|
||||
"""
|
||||
SigNoz with clickhousev2 as the serving prometheus provider. The corpus
|
||||
replays against the /prometheus/api/v1 endpoints, so this package covers
|
||||
the two paths nothing else serves: v2 as the provider (range queries
|
||||
transpile when the shape allows), and the Prometheus HTTP API contract.
|
||||
"""
|
||||
return create_signoz(
|
||||
network=network,
|
||||
zeus=zeus,
|
||||
gateway=gateway,
|
||||
sqlstore=sqlstore,
|
||||
clickhouse=clickhouse,
|
||||
request=request,
|
||||
pytestconfig=pytestconfig,
|
||||
cache_key="signoz-promapi-v2",
|
||||
env_overrides={
|
||||
"SIGNOZ_PROMETHEUS_PROVIDER": "clickhousev2",
|
||||
},
|
||||
)
|
||||
@@ -1,202 +0,0 @@
|
||||
"""Pins the keyless-row contract for filter operators, per signal.
|
||||
|
||||
The contract (deliberate product semantics, enforced by
|
||||
`FilterOperator.AddDefaultExistsFilter` in
|
||||
pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go):
|
||||
|
||||
- Negative operators (!=, NOT IN, NOT LIKE, NOT CONTAINS, ...) are a set
|
||||
complement over ALL rows: a row that does not carry the key at all MUST
|
||||
match. Users opt into presence explicitly with `AND key EXISTS`.
|
||||
- Positive operators carry an implicit existence guard: a keyless row must
|
||||
NOT match `key = ''`-style comparisons against sentinel defaults.
|
||||
- EXISTS / NOT EXISTS partition rows exactly by key presence.
|
||||
- Numeric attributes inherit the map-default sentinel: a missing key reads
|
||||
as 0, so `num != 0` excludes keyless rows while `num != 5` includes them.
|
||||
This conflation is deliberate and pinned here as the reference for any
|
||||
value-expression change (for example coalesce tails in semconv families).
|
||||
|
||||
Any implementation change that makes these assertions fail is a behavior
|
||||
break, not a cleanup. Family-field behavior must mirror this matrix; see
|
||||
queriertraces/13_semconv_evolution.py.
|
||||
|
||||
Seed data lives in fixtures/queriercommon.py: GOLD and SILVER carry the
|
||||
keys, NONE carries none. Every case asserts which identities a filter
|
||||
returns, so the membership of NONE is the point of each case.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timedelta
|
||||
from http import HTTPStatus
|
||||
|
||||
import pytest
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
|
||||
from fixtures.querier import (
|
||||
RequestType,
|
||||
build_builder_query,
|
||||
build_order_by,
|
||||
build_raw_query,
|
||||
get_all_series,
|
||||
get_column_data_from_response,
|
||||
make_query_request,
|
||||
)
|
||||
from fixtures.queriercommon import (
|
||||
GOLD,
|
||||
METRIC_LABEL,
|
||||
METRIC_NAME,
|
||||
NONE,
|
||||
NUMBER_KEY,
|
||||
PREFIX,
|
||||
SILVER,
|
||||
STRING_KEY,
|
||||
)
|
||||
|
||||
STRING_MATRIX = [
|
||||
pytest.param("{key} = 'gold'", {GOLD}, id="eq_excludes_keyless"),
|
||||
pytest.param("{key} != 'gold'", {SILVER, NONE}, id="neq_includes_keyless"),
|
||||
pytest.param("{key} NOT IN ['gold', 'silver']", {NONE}, id="not_in_includes_keyless"),
|
||||
pytest.param("NOT {key} LIKE '%gold%'", {SILVER, NONE}, id="not_like_includes_keyless"),
|
||||
pytest.param("{key} NOT CONTAINS 'gol'", {SILVER, NONE}, id="not_contains_includes_keyless"),
|
||||
pytest.param("{key} EXISTS", {GOLD, SILVER}, id="exists_partitions"),
|
||||
pytest.param("{key} NOT EXISTS", {NONE}, id="not_exists_partitions"),
|
||||
# "Present and not X" is a composition. It is not a new operator
|
||||
# semantic.
|
||||
pytest.param("{key} != 'gold' AND {key} EXISTS", {SILVER}, id="neq_composed_with_exists"),
|
||||
]
|
||||
|
||||
NUMBER_MATRIX = [
|
||||
pytest.param("{key} = 0", {GOLD}, id="numeric_eq_zero_excludes_keyless"),
|
||||
pytest.param("{key} != 5", {GOLD, NONE}, id="numeric_neq_includes_keyless"),
|
||||
pytest.param("{key} != 0", {SILVER}, id="numeric_neq_zero_sentinel_conflation"),
|
||||
]
|
||||
|
||||
SIGNALS = [
|
||||
pytest.param("traces", "span.name", "name", id="traces"),
|
||||
pytest.param("logs", "body", "body", id="logs"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression_template,expected", STRING_MATRIX)
|
||||
@pytest.mark.parametrize("context", ["resource", "attribute"])
|
||||
@pytest.mark.parametrize("signal,identity_field,identity_column", SIGNALS)
|
||||
def test_negative_operators_include_keyless_rows(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
keyless_rows: datetime,
|
||||
signal: str,
|
||||
identity_field: str,
|
||||
identity_column: str,
|
||||
context: str,
|
||||
expression_template: str,
|
||||
expected: set[str],
|
||||
) -> None:
|
||||
"""A negative operator is a set complement over all rows. Presence is an
|
||||
explicit EXISTS opt-in. The contract holds the same way for resource and
|
||||
attribute contexts, on traces and on logs."""
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
expression = expression_template.format(key=f"{context}.{STRING_KEY}")
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((keyless_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((keyless_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
build_raw_query(
|
||||
"A",
|
||||
signal,
|
||||
limit=100,
|
||||
filter_expression=expression,
|
||||
order=[build_order_by("timestamp", "asc")],
|
||||
select_fields=[{"name": identity_field}],
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
|
||||
# Sets keep the assertion stable when the shared stack is reused and
|
||||
# older rows with the same identities remain.
|
||||
matched = {name for name in get_column_data_from_response(response.json(), identity_column) if name.startswith(PREFIX)}
|
||||
assert matched == expected, expression
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression_template,expected", NUMBER_MATRIX)
|
||||
@pytest.mark.parametrize("signal,identity_field,identity_column", SIGNALS)
|
||||
def test_numeric_sentinel_semantics(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
keyless_rows: datetime,
|
||||
signal: str,
|
||||
identity_field: str,
|
||||
identity_column: str,
|
||||
expression_template: str,
|
||||
expected: set[str],
|
||||
) -> None:
|
||||
"""A missing numeric key reads as the map default 0. `!= 0` therefore
|
||||
excludes rows without the key, and every other negative comparison
|
||||
includes them. This is inherited sentinel behavior, pinned on purpose."""
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
expression = expression_template.format(key=f"attribute.{NUMBER_KEY}")
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=int((keyless_rows - timedelta(minutes=2)).timestamp() * 1000),
|
||||
end_ms=int((keyless_rows + timedelta(minutes=1)).timestamp() * 1000),
|
||||
request_type=RequestType.RAW,
|
||||
queries=[
|
||||
build_raw_query(
|
||||
"A",
|
||||
signal,
|
||||
limit=100,
|
||||
filter_expression=expression,
|
||||
order=[build_order_by("timestamp", "asc")],
|
||||
select_fields=[{"name": identity_field}],
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
|
||||
matched = {name for name in get_column_data_from_response(response.json(), identity_column) if name.startswith(PREFIX)}
|
||||
assert matched == expected, expression
|
||||
|
||||
|
||||
@pytest.mark.parametrize("expression_template,expected", STRING_MATRIX)
|
||||
def test_metrics_negative_operators_include_keyless_series(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
keyless_series: tuple[int, int],
|
||||
expression_template: str,
|
||||
expected: set[str],
|
||||
) -> None:
|
||||
"""The same contract holds for metric labels: a series without the label
|
||||
matches every negative filter on it, and EXISTS opts into presence."""
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
expression = expression_template.format(key=METRIC_LABEL)
|
||||
start, end = keyless_series
|
||||
|
||||
response = make_query_request(
|
||||
signoz,
|
||||
token,
|
||||
start_ms=start * 1000,
|
||||
end_ms=end * 1000,
|
||||
queries=[
|
||||
build_builder_query(
|
||||
"A",
|
||||
METRIC_NAME,
|
||||
"avg",
|
||||
"sum",
|
||||
group_by=["service"],
|
||||
filter_expression=expression,
|
||||
)
|
||||
],
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK, response.text
|
||||
|
||||
matched = {label["value"] for series in get_all_series(response.json(), "A") for label in series["labels"] if label["key"]["name"] == "service" and label["value"].startswith(PREFIX)}
|
||||
assert matched == expected, expression
|
||||
@@ -47,7 +47,9 @@ def _data(
|
||||
}
|
||||
|
||||
|
||||
def _create_body(*, name: str = "my-view", generate_name: bool = False, display_name: str = "My View", source: str = "logs", **data_kwargs) -> dict:
|
||||
def _create_body(
|
||||
*, name: str = "my-view", generate_name: bool = False, display_name: str = "My View", source: str = "logs", **data_kwargs
|
||||
) -> dict:
|
||||
"""name is the immutable slug. Pass generate_name=True (and leave name
|
||||
empty) to have the server generate one from display_name instead."""
|
||||
return {"name": name, "generateName": generate_name, "source": source, "data": _data(display_name=display_name, **data_kwargs)}
|
||||
|
||||
Reference in New Issue
Block a user