mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-11 14:59:04 +08:00
commit
9de9fb5863
@ -146,7 +146,7 @@ services:
|
||||
condition: on-failure
|
||||
|
||||
query-service:
|
||||
image: signoz/query-service:0.37.0
|
||||
image: signoz/query-service:0.37.1
|
||||
command:
|
||||
[
|
||||
"-config=/root/config/prometheus.yml",
|
||||
@ -186,7 +186,7 @@ services:
|
||||
<<: *db-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:0.37.0
|
||||
image: signoz/frontend:0.37.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
|
@ -164,7 +164,7 @@ services:
|
||||
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
|
||||
|
||||
query-service:
|
||||
image: signoz/query-service:${DOCKER_TAG:-0.37.0}
|
||||
image: signoz/query-service:${DOCKER_TAG:-0.37.1}
|
||||
container_name: signoz-query-service
|
||||
command:
|
||||
[
|
||||
@ -203,7 +203,7 @@ services:
|
||||
<<: *db-depend
|
||||
|
||||
frontend:
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.37.0}
|
||||
image: signoz/frontend:${DOCKER_TAG:-0.37.1}
|
||||
container_name: signoz-frontend
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
|
@ -193,7 +193,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
||||
}
|
||||
|
||||
// start the usagemanager
|
||||
usageManager, err := usage.New("sqlite", localDB, lm.GetRepo(), reader.GetConn())
|
||||
usageManager, err := usage.New("sqlite", modelDao, lm.GetRepo(), reader.GetConn())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ type Usage struct {
|
||||
TimeStamp time.Time `json:"timestamp"`
|
||||
Count int64 `json:"count"`
|
||||
Size int64 `json:"size"`
|
||||
OrgName string `json:"orgName"`
|
||||
TenantId string `json:"tenantId"`
|
||||
}
|
||||
|
||||
type UsageDB struct {
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -11,10 +13,10 @@ import (
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/go-co-op/gocron"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"go.signoz.io/signoz/ee/query-service/dao"
|
||||
licenseserver "go.signoz.io/signoz/ee/query-service/integrations/signozio"
|
||||
"go.signoz.io/signoz/ee/query-service/license"
|
||||
"go.signoz.io/signoz/ee/query-service/model"
|
||||
@ -38,15 +40,29 @@ type Manager struct {
|
||||
licenseRepo *license.Repo
|
||||
|
||||
scheduler *gocron.Scheduler
|
||||
|
||||
modelDao dao.ModelDao
|
||||
|
||||
tenantID string
|
||||
}
|
||||
|
||||
func New(dbType string, db *sqlx.DB, licenseRepo *license.Repo, clickhouseConn clickhouse.Conn) (*Manager, error) {
|
||||
func New(dbType string, modelDao dao.ModelDao, licenseRepo *license.Repo, clickhouseConn clickhouse.Conn) (*Manager, error) {
|
||||
hostNameRegex := regexp.MustCompile(`tcp://(?P<hostname>.*):`)
|
||||
hostNameRegexMatches := hostNameRegex.FindStringSubmatch(os.Getenv("ClickHouseUrl"))
|
||||
|
||||
tenantID := ""
|
||||
if len(hostNameRegexMatches) == 2 {
|
||||
tenantID = hostNameRegexMatches[1]
|
||||
tenantID = strings.TrimRight(tenantID, "-clickhouse")
|
||||
}
|
||||
|
||||
m := &Manager{
|
||||
// repository: repo,
|
||||
clickhouseConn: clickhouseConn,
|
||||
licenseRepo: licenseRepo,
|
||||
scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC
|
||||
modelDao: modelDao,
|
||||
tenantID: tenantID,
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@ -123,6 +139,19 @@ func (lm *Manager) UploadUsage() {
|
||||
|
||||
zap.S().Info("uploading usage data")
|
||||
|
||||
// Try to get the org name
|
||||
orgName := ""
|
||||
orgNames, err := lm.modelDao.GetOrgs(ctx)
|
||||
if err != nil {
|
||||
zap.S().Errorf("failed to get org data: %v", zap.Error(err))
|
||||
} else {
|
||||
if len(orgNames) != 1 {
|
||||
zap.S().Errorf("expected one org but got %d orgs", len(orgNames))
|
||||
} else {
|
||||
orgName = orgNames[0].Name
|
||||
}
|
||||
}
|
||||
|
||||
usagesPayload := []model.Usage{}
|
||||
for _, usage := range usages {
|
||||
usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data))
|
||||
@ -142,6 +171,8 @@ func (lm *Manager) UploadUsage() {
|
||||
usageData.ExporterID = usage.ExporterID
|
||||
usageData.Type = usage.Type
|
||||
usageData.Tenant = usage.Tenant
|
||||
usageData.OrgName = orgName
|
||||
usageData.TenantId = lm.tenantID
|
||||
usagesPayload = append(usagesPayload, usageData)
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ export const Layout = styled(LayoutComponent)`
|
||||
min-height: calc(100vh - 8rem);
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -18,13 +18,14 @@ import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariab
|
||||
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
||||
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import uPlot from 'uplot';
|
||||
import { getTimeRange } from 'utils/getTimeRange';
|
||||
|
||||
import { getGraphVisibilityStateOnDataChange } from '../utils';
|
||||
import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants';
|
||||
import GraphManager from './GraphManager';
|
||||
// import GraphManager from './GraphManager';
|
||||
@ -36,13 +37,14 @@ function FullView({
|
||||
fullViewOptions = true,
|
||||
onClickHandler,
|
||||
name,
|
||||
originalName,
|
||||
yAxisUnit,
|
||||
options,
|
||||
onDragSelect,
|
||||
isDependedDataLoaded = false,
|
||||
graphsVisibilityStates,
|
||||
onToggleModelHandler,
|
||||
parentChartRef,
|
||||
setGraphsVisibilityStates,
|
||||
parentGraphVisibilityState,
|
||||
}: FullViewProps): JSX.Element {
|
||||
const { selectedTime: globalSelectedTime } = useSelector<
|
||||
AppState,
|
||||
@ -55,6 +57,20 @@ function FullView({
|
||||
|
||||
const { selectedDashboard, isDashboardLocked } = useDashboard();
|
||||
|
||||
const { graphVisibilityStates: localStoredVisibilityStates } = useMemo(
|
||||
() =>
|
||||
getGraphVisibilityStateOnDataChange({
|
||||
options,
|
||||
isExpandedName: false,
|
||||
name: originalName,
|
||||
}),
|
||||
[options, originalName],
|
||||
);
|
||||
|
||||
const [graphsVisibilityStates, setGraphsVisibilityStates] = useState(
|
||||
localStoredVisibilityStates,
|
||||
);
|
||||
|
||||
const getSelectedTime = useCallback(
|
||||
() =>
|
||||
timeItems.find((e) => e.enum === (widget?.timePreferance || 'GLOBAL_TIME')),
|
||||
@ -144,9 +160,9 @@ function FullView({
|
||||
useEffect(() => {
|
||||
graphsVisibilityStates?.forEach((e, i) => {
|
||||
fullViewChartRef?.current?.toggleGraph(i, e);
|
||||
parentChartRef?.current?.toggleGraph(i, e);
|
||||
});
|
||||
}, [graphsVisibilityStates, parentChartRef]);
|
||||
parentGraphVisibilityState(graphsVisibilityStates);
|
||||
}, [graphsVisibilityStates, parentGraphVisibilityState]);
|
||||
|
||||
if (response.isFetching) {
|
||||
return <Spinner height="100%" size="large" tip="Loading..." />;
|
||||
@ -206,7 +222,7 @@ function FullView({
|
||||
{canModifyChart && chartOptions && !isDashboardLocked && (
|
||||
<GraphManager
|
||||
data={chartData}
|
||||
name={name}
|
||||
name={originalName}
|
||||
options={chartOptions}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onToggleModelHandler={onToggleModelHandler}
|
||||
|
@ -50,13 +50,14 @@ export interface FullViewProps {
|
||||
fullViewOptions?: boolean;
|
||||
onClickHandler?: OnClickPluginOpts['onClick'];
|
||||
name: string;
|
||||
originalName: string;
|
||||
options: uPlot.Options;
|
||||
yAxisUnit?: string;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
isDependedDataLoaded?: boolean;
|
||||
graphsVisibilityStates?: boolean[];
|
||||
onToggleModelHandler?: GraphManagerProps['onToggleModelHandler'];
|
||||
setGraphsVisibilityStates: Dispatch<SetStateAction<boolean[]>>;
|
||||
parentChartRef: GraphManagerProps['lineChartRef'];
|
||||
parentGraphVisibilityState: Dispatch<SetStateAction<boolean[]>>;
|
||||
}
|
||||
|
||||
export interface GraphManagerProps extends UplotProps {
|
||||
@ -64,8 +65,8 @@ export interface GraphManagerProps extends UplotProps {
|
||||
yAxisUnit?: string;
|
||||
onToggleModelHandler?: () => void;
|
||||
options: uPlot.Options;
|
||||
setGraphsVisibilityStates: FullViewProps['setGraphsVisibilityStates'];
|
||||
graphsVisibilityStates: FullViewProps['graphsVisibilityStates'];
|
||||
graphsVisibilityStates?: boolean[];
|
||||
setGraphsVisibilityStates: Dispatch<SetStateAction<boolean[]>>;
|
||||
lineChartRef?: MutableRefObject<ToggleGraphProps | undefined>;
|
||||
parentChartRef?: MutableRefObject<ToggleGraphProps | undefined>;
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
@ -39,14 +38,15 @@ function WidgetGraphComponent({
|
||||
queryResponse,
|
||||
errorMessage,
|
||||
name,
|
||||
onClickHandler,
|
||||
threshold,
|
||||
headerMenuList,
|
||||
isWarning,
|
||||
data,
|
||||
options,
|
||||
graphVisibiltyState,
|
||||
onClickHandler,
|
||||
onDragSelect,
|
||||
graphVisibility,
|
||||
setGraphVisibility,
|
||||
}: WidgetGraphComponentProps): JSX.Element {
|
||||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
@ -60,33 +60,28 @@ function WidgetGraphComponent({
|
||||
const lineChartRef = useRef<ToggleGraphProps>();
|
||||
const graphRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { graphVisibilityStates: localStoredVisibilityStates } = useMemo(
|
||||
() =>
|
||||
getGraphVisibilityStateOnDataChange({
|
||||
useEffect(() => {
|
||||
if (queryResponse.isSuccess) {
|
||||
const {
|
||||
graphVisibilityStates: localStoredVisibilityState,
|
||||
} = getGraphVisibilityStateOnDataChange({
|
||||
options,
|
||||
isExpandedName: true,
|
||||
isExpandedName: false,
|
||||
name,
|
||||
}),
|
||||
[options, name],
|
||||
);
|
||||
|
||||
const [graphsVisibilityStates, setGraphsVisibilityStates] = useState<
|
||||
boolean[]
|
||||
>(localStoredVisibilityStates);
|
||||
});
|
||||
setGraphVisibility(localStoredVisibilityState);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [queryResponse.isSuccess]);
|
||||
|
||||
useEffect(() => {
|
||||
setGraphsVisibilityStates(localStoredVisibilityStates);
|
||||
if (!lineChartRef.current) return;
|
||||
|
||||
localStoredVisibilityStates.forEach((state, index) => {
|
||||
graphVisibiltyState.forEach((state, index) => {
|
||||
lineChartRef.current?.toggleGraph(index, state);
|
||||
});
|
||||
setGraphsVisibilityStates(localStoredVisibilityStates);
|
||||
}, [localStoredVisibilityStates]);
|
||||
|
||||
graphVisibility?.forEach((state, index) => {
|
||||
lineChartRef.current?.toggleGraph(index, state);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const { setLayouts, selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||
|
||||
@ -279,13 +274,14 @@ function WidgetGraphComponent({
|
||||
>
|
||||
<FullView
|
||||
name={`${name}expanded`}
|
||||
originalName={name}
|
||||
widget={widget}
|
||||
yAxisUnit={widget.yAxisUnit}
|
||||
onToggleModelHandler={onToggleModelHandler}
|
||||
parentChartRef={lineChartRef}
|
||||
parentGraphVisibilityState={setGraphVisibility}
|
||||
onDragSelect={onDragSelect}
|
||||
setGraphsVisibilityStates={setGraphsVisibilityStates}
|
||||
graphsVisibilityStates={graphsVisibilityStates}
|
||||
options={options}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
|
@ -167,13 +167,6 @@ function GridCardGraph({
|
||||
Array(queryResponse.data?.payload?.data.result.length || 0).fill(true),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setGraphVisibility([
|
||||
true,
|
||||
...Array(queryResponse.data?.payload?.data.result.length).fill(true),
|
||||
]);
|
||||
}, [queryResponse.data?.payload?.data.result.length]);
|
||||
|
||||
const options = useMemo(
|
||||
() =>
|
||||
getUPlotChartOptions({
|
||||
@ -227,7 +220,8 @@ function GridCardGraph({
|
||||
threshold={threshold}
|
||||
headerMenuList={menuList}
|
||||
onClickHandler={onClickHandler}
|
||||
graphVisibility={graphVisibility}
|
||||
graphVisibiltyState={graphVisibility}
|
||||
setGraphVisibility={setGraphVisibility}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import { UplotProps } from 'components/Uplot/Uplot';
|
||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { MutableRefObject, ReactNode } from 'react';
|
||||
import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||
@ -28,7 +28,8 @@ export interface WidgetGraphComponentProps extends UplotProps {
|
||||
threshold?: ReactNode;
|
||||
headerMenuList: MenuItemKeys[];
|
||||
isWarning: boolean;
|
||||
graphVisibility?: boolean[];
|
||||
graphVisibiltyState: boolean[];
|
||||
setGraphVisibility: Dispatch<SetStateAction<boolean[]>>;
|
||||
}
|
||||
|
||||
export interface GridCardGraphProps {
|
||||
|
@ -122,8 +122,17 @@ export function replaceStringWithMaxLength(
|
||||
if (lastSearchValue === '') {
|
||||
return `${mainString}${replacementString},`;
|
||||
}
|
||||
/**
|
||||
* We need to escape the special characters in the lastSearchValue else the
|
||||
* new RegExp fails with error range out of order in char class
|
||||
*/
|
||||
const escapedLastSearchValue = lastSearchValue.replace(
|
||||
/[-/\\^$*+?.()|[\]{}]/g,
|
||||
'\\$&',
|
||||
);
|
||||
|
||||
const updatedString = mainString.replace(
|
||||
new RegExp(`${lastSearchValue}(?=[^${lastSearchValue}]*$)`),
|
||||
new RegExp(`${escapedLastSearchValue}(?=[^${escapedLastSearchValue}]*$)`),
|
||||
replacementString,
|
||||
);
|
||||
return `${updatedString},`;
|
||||
|
@ -279,6 +279,18 @@ function DateTimeSelection({
|
||||
setRefreshButtonHidden(updatedTime === 'custom');
|
||||
|
||||
updateTimeInterval(updatedTime, [preStartTime, preEndTime]);
|
||||
|
||||
if (updatedTime !== 'custom') {
|
||||
const { minTime, maxTime } = GetMinMax(updatedTime);
|
||||
urlQuery.set(QueryParams.startTime, minTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
||||
} else {
|
||||
urlQuery.set(QueryParams.startTime, preStartTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, preEndTime.toString());
|
||||
}
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.replace(generatedUrl);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location.pathname, updateTimeInterval, globalTimeLoading]);
|
||||
|
||||
|
@ -25,6 +25,7 @@ import {
|
||||
parseQueryIntoPageSize,
|
||||
parseQueryIntoSelectedTags,
|
||||
parseSelectedFilter,
|
||||
stripTimestampsFromQuery,
|
||||
} from './util';
|
||||
|
||||
export const GetInitialTraceFilter = (
|
||||
@ -113,8 +114,11 @@ export const GetInitialTraceFilter = (
|
||||
);
|
||||
|
||||
if (response.payload && !isSelectionSkipped.currentValue) {
|
||||
/** this is required as now we have timestamps updated in date time selection component
|
||||
* so for initial filters we need to strip timestamps and check for initial load
|
||||
*/
|
||||
const diff =
|
||||
query.length === 0
|
||||
stripTimestampsFromQuery(query).length === 0
|
||||
? traces.filterToFetchData
|
||||
: xor(traces.filterToFetchData, getFilterToFetchData.currentValue);
|
||||
|
||||
|
@ -86,3 +86,6 @@ export const getFilter = (data: GetFilterPayload): TraceReducer['filter'] => {
|
||||
|
||||
return filter;
|
||||
};
|
||||
|
||||
export const stripTimestampsFromQuery = (query: string): string =>
|
||||
query.replace(/(\?|&)startTime=\d+/, '').replace(/&endTime=\d+/, '');
|
||||
|
Loading…
x
Reference in New Issue
Block a user