mirror of
https://github.com/SigNoz/signoz.git
synced 2026-06-09 10:30:27 +01:00
Compare commits
14 Commits
feat/trace
...
fix/update
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c11dfc071 | ||
|
|
303908542a | ||
|
|
9dbf936a7c | ||
|
|
f52ce461d0 | ||
|
|
2e8aa0f42d | ||
|
|
c688170a13 | ||
|
|
7844fc1fe1 | ||
|
|
e02da843f2 | ||
|
|
4500ca67f0 | ||
|
|
fed956c614 | ||
|
|
72426cfdc2 | ||
|
|
4bfc21faf5 | ||
|
|
919f792297 | ||
|
|
04d8f3484c |
4
.github/workflows/integrationci.yaml
vendored
4
.github/workflows/integrationci.yaml
vendored
@@ -39,10 +39,12 @@ jobs:
|
||||
matrix:
|
||||
suite:
|
||||
- alerts
|
||||
- basepath
|
||||
- callbackauthn
|
||||
- cloudintegrations
|
||||
- dashboard
|
||||
- ingestionkeys
|
||||
- inframonitoring
|
||||
- logspipelines
|
||||
- passwordauthn
|
||||
- preference
|
||||
@@ -83,7 +85,7 @@ jobs:
|
||||
run: |
|
||||
cd tests && uv sync
|
||||
- name: webdriver
|
||||
if: matrix.suite == 'callbackauthn'
|
||||
if: matrix.suite == 'callbackauthn' || matrix.suite == 'basepath'
|
||||
run: |
|
||||
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
|
||||
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee -a /etc/apt/sources.list.d/google-chrome.list
|
||||
|
||||
@@ -91,7 +91,7 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
sqlstoreProviderFactories(),
|
||||
signoz.NewTelemetryStoreProviderFactories(),
|
||||
func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error) {
|
||||
return signoz.NewAuthNs(ctx, providerSettings, store, licensing)
|
||||
return signoz.NewAuthNs(ctx, providerSettings, store, licensing, config.Global)
|
||||
},
|
||||
func(ctx context.Context, sqlstore sqlstore.SQLStore, config authz.Config, _ licensing.Licensing, _ []authz.OnBeforeRoleDelete) (factory.ProviderFactory[authz.AuthZ, authz.Config], error) {
|
||||
openfgaDataStore, err := openfgaserver.NewSQLStore(sqlstore, config)
|
||||
|
||||
@@ -107,17 +107,17 @@ func runServer(ctx context.Context, config signoz.Config, logger *slog.Logger) e
|
||||
sqlstoreProviderFactories(),
|
||||
signoz.NewTelemetryStoreProviderFactories(),
|
||||
func(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error) {
|
||||
samlCallbackAuthN, err := samlcallbackauthn.New(ctx, store, licensing)
|
||||
samlCallbackAuthN, err := samlcallbackauthn.New(ctx, store, licensing, config.Global)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oidcCallbackAuthN, err := oidccallbackauthn.New(store, licensing, providerSettings)
|
||||
oidcCallbackAuthN, err := oidccallbackauthn.New(store, licensing, providerSettings, config.Global)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authNs, err := signoz.NewAuthNs(ctx, providerSettings, store, licensing)
|
||||
authNs, err := signoz.NewAuthNs(ctx, providerSettings, store, licensing, config.Global)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -440,6 +440,17 @@ traces:
|
||||
max_depth_to_auto_expand: 5
|
||||
# Threshold below which all spans are returned without windowing.
|
||||
max_limit_to_select_all_spans: 10000
|
||||
flamegraph:
|
||||
# Maximum number of BFS depth levels included in a windowed response.
|
||||
max_selected_levels: 50
|
||||
# Maximum spans per level before sampling is applied.
|
||||
max_spans_per_level: 100
|
||||
# Number of highest-latency spans always included when sampling a level.
|
||||
sampling_top_latency_count: 5
|
||||
# Number of timestamp buckets used for uniform sampling within a level.
|
||||
sampling_bucket_count: 50
|
||||
# Threshold below which all spans are returned without windowing or sampling.
|
||||
select_all_spans_limit: 100000
|
||||
|
||||
##################### Authz #################################
|
||||
authz:
|
||||
|
||||
@@ -6638,6 +6638,70 @@ components:
|
||||
- attribute
|
||||
- resource
|
||||
type: string
|
||||
SpantypesFlamegraphSpan:
|
||||
properties:
|
||||
attributes:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
durationNano:
|
||||
minimum: 0
|
||||
type: integer
|
||||
event:
|
||||
items:
|
||||
$ref: '#/components/schemas/SpantypesEvent'
|
||||
type: array
|
||||
hasError:
|
||||
type: boolean
|
||||
level:
|
||||
format: int64
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
parentSpanId:
|
||||
type: string
|
||||
resource:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
spanId:
|
||||
type: string
|
||||
timestamp:
|
||||
minimum: 0
|
||||
type: integer
|
||||
required:
|
||||
- spanId
|
||||
- parentSpanId
|
||||
- timestamp
|
||||
- durationNano
|
||||
- hasError
|
||||
- name
|
||||
- level
|
||||
- event
|
||||
- attributes
|
||||
- resource
|
||||
type: object
|
||||
SpantypesGettableFlamegraphTrace:
|
||||
properties:
|
||||
endTimestampMillis:
|
||||
format: int64
|
||||
type: integer
|
||||
hasMore:
|
||||
type: boolean
|
||||
spans:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/components/schemas/SpantypesFlamegraphSpan'
|
||||
type: array
|
||||
type: array
|
||||
startTimestampMillis:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- spans
|
||||
- startTimestampMillis
|
||||
- endTimestampMillis
|
||||
- hasMore
|
||||
type: object
|
||||
SpantypesGettableSpanMapperGroups:
|
||||
properties:
|
||||
items:
|
||||
@@ -6703,6 +6767,15 @@ components:
|
||||
traceId:
|
||||
type: string
|
||||
type: object
|
||||
SpantypesPostableFlamegraph:
|
||||
properties:
|
||||
selectFields:
|
||||
items:
|
||||
$ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey'
|
||||
type: array
|
||||
selectedSpanId:
|
||||
type: string
|
||||
type: object
|
||||
SpantypesPostableSpanMapper:
|
||||
properties:
|
||||
config:
|
||||
@@ -20535,6 +20608,75 @@ paths:
|
||||
summary: Put profile in Zeus for a deployment.
|
||||
tags:
|
||||
- zeus
|
||||
/api/v3/traces/{traceID}/flamegraph:
|
||||
post:
|
||||
deprecated: false
|
||||
description: Returns the flamegraph view of spans for a given trace ID.
|
||||
operationId: GetFlamegraph
|
||||
parameters:
|
||||
- in: path
|
||||
name: traceID
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SpantypesPostableFlamegraph'
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/SpantypesGettableFlamegraphTrace'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Bad Request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Unauthorized
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Not Found
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: Get flamegraph view for a trace
|
||||
tags:
|
||||
- tracedetail
|
||||
/api/v3/traces/{traceID}/waterfall:
|
||||
post:
|
||||
deprecated: false
|
||||
|
||||
@@ -5,10 +5,12 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/http/client"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
@@ -26,13 +28,14 @@ var defaultScopes []string = []string{"email", "profile", oidc.ScopeOpenID}
|
||||
var _ authn.CallbackAuthN = (*AuthN)(nil)
|
||||
|
||||
type AuthN struct {
|
||||
settings factory.ScopedProviderSettings
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
httpClient *client.Client
|
||||
settings factory.ScopedProviderSettings
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
httpClient *client.Client
|
||||
globalConfig global.Config
|
||||
}
|
||||
|
||||
func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSettings factory.ProviderSettings) (*AuthN, error) {
|
||||
func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSettings factory.ProviderSettings, globalConfig global.Config) (*AuthN, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/authn/callbackauthn/oidccallbackauthn")
|
||||
|
||||
httpClient, err := client.New(providerSettings.Logger, providerSettings.TracerProvider, providerSettings.MeterProvider)
|
||||
@@ -41,10 +44,11 @@ func New(store authtypes.AuthNStore, licensing licensing.Licensing, providerSett
|
||||
}
|
||||
|
||||
return &AuthN{
|
||||
settings: settings,
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
httpClient: httpClient,
|
||||
settings: settings,
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
httpClient: httpClient,
|
||||
globalConfig: globalConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -197,7 +201,7 @@ func (a *AuthN) oidcProviderAndoauth2Config(ctx context.Context, siteURL *url.UR
|
||||
RedirectURL: (&url.URL{
|
||||
Scheme: siteURL.Scheme,
|
||||
Host: siteURL.Host,
|
||||
Path: redirectPath,
|
||||
Path: path.Join(a.globalConfig.ExternalPath(), redirectPath),
|
||||
}).String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
@@ -24,14 +26,16 @@ const (
|
||||
var _ authn.CallbackAuthN = (*AuthN)(nil)
|
||||
|
||||
type AuthN struct {
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
store authtypes.AuthNStore
|
||||
licensing licensing.Licensing
|
||||
globalConfig global.Config
|
||||
}
|
||||
|
||||
func New(ctx context.Context, store authtypes.AuthNStore, licensing licensing.Licensing) (*AuthN, error) {
|
||||
func New(ctx context.Context, store authtypes.AuthNStore, licensing licensing.Licensing, globalConfig global.Config) (*AuthN, error) {
|
||||
return &AuthN{
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
store: store,
|
||||
licensing: licensing,
|
||||
globalConfig: globalConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -132,7 +136,7 @@ func (a *AuthN) serviceProvider(siteURL *url.URL, authDomain *authtypes.AuthDoma
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acsURL := &url.URL{Scheme: siteURL.Scheme, Host: siteURL.Host, Path: redirectPath}
|
||||
acsURL := &url.URL{Scheme: siteURL.Scheme, Host: siteURL.Host, Path: path.Join(a.globalConfig.ExternalPath(), redirectPath)}
|
||||
|
||||
// Note:
|
||||
// The ServiceProviderIssuer is the client id in case of keycloak. Since we set it to the host here, we need to set the client id == host in keycloak.
|
||||
|
||||
@@ -41,6 +41,15 @@ if (typeof window.IntersectionObserver === 'undefined') {
|
||||
(window as any).IntersectionObserver = IntersectionObserverMock;
|
||||
}
|
||||
|
||||
if (typeof window.ResizeObserver === 'undefined') {
|
||||
class ResizeObserverMock {
|
||||
observe(): void {}
|
||||
unobserve(): void {}
|
||||
disconnect(): void {}
|
||||
}
|
||||
(window as any).ResizeObserver = ResizeObserverMock;
|
||||
}
|
||||
|
||||
// Patch getComputedStyle to handle CSS parsing errors from @signozhq/* packages.
|
||||
// These packages inject CSS at import time via style-inject / vite-plugin-css-injected-by-js.
|
||||
// jsdom's nwsapi cannot parse some of the injected selectors (e.g. Tailwind's :animate-in),
|
||||
|
||||
@@ -7769,6 +7769,77 @@ export enum SpantypesFieldContextDTO {
|
||||
attribute = 'attribute',
|
||||
resource = 'resource',
|
||||
}
|
||||
export type SpantypesFlamegraphSpanDTOAttributes = { [key: string]: unknown };
|
||||
|
||||
export type SpantypesFlamegraphSpanDTOResource = { [key: string]: string };
|
||||
|
||||
export interface SpantypesFlamegraphSpanDTO {
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
attributes: SpantypesFlamegraphSpanDTOAttributes;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
durationNano: number;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
event: SpantypesEventDTO[];
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
hasError: boolean;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
level: number;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
parentSpanId: string;
|
||||
/**
|
||||
* @type object
|
||||
*/
|
||||
resource: SpantypesFlamegraphSpanDTOResource;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
spanId: string;
|
||||
/**
|
||||
* @type integer
|
||||
* @minimum 0
|
||||
*/
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export interface SpantypesGettableFlamegraphTraceDTO {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
endTimestampMillis: number;
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
hasMore: boolean;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
spans: SpantypesFlamegraphSpanDTO[][];
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
startTimestampMillis: number;
|
||||
}
|
||||
|
||||
export type SpantypesSpanMapperGroupConditionDTOAnyOf = {
|
||||
/**
|
||||
* @type array,null
|
||||
@@ -8070,6 +8141,17 @@ export interface SpantypesGettableWaterfallTraceDTO {
|
||||
uncollapsedSpans?: string[] | null;
|
||||
}
|
||||
|
||||
export interface SpantypesPostableFlamegraphDTO {
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
selectFields?: TelemetrytypesTelemetryFieldKeyDTO[];
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
selectedSpanId?: string;
|
||||
}
|
||||
|
||||
export enum SpantypesSpanMapperOperationDTO {
|
||||
move = 'move',
|
||||
copy = 'copy',
|
||||
@@ -10424,6 +10506,17 @@ export type GetHosts200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetFlamegraphPathParameters = {
|
||||
traceID: string;
|
||||
};
|
||||
export type GetFlamegraph200 = {
|
||||
data: SpantypesGettableFlamegraphTraceDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type GetWaterfallPathParameters = {
|
||||
traceID: string;
|
||||
};
|
||||
|
||||
@@ -12,6 +12,8 @@ import type {
|
||||
} from 'react-query';
|
||||
|
||||
import type {
|
||||
GetFlamegraph200,
|
||||
GetFlamegraphPathParameters,
|
||||
GetTraceAggregations200,
|
||||
GetTraceAggregationsPathParameters,
|
||||
GetWaterfall200,
|
||||
@@ -19,6 +21,7 @@ import type {
|
||||
GetWaterfallV4200,
|
||||
GetWaterfallV4PathParameters,
|
||||
RenderErrorResponseDTO,
|
||||
SpantypesPostableFlamegraphDTO,
|
||||
SpantypesPostableTraceAggregationsDTO,
|
||||
SpantypesPostableWaterfallDTO,
|
||||
} from '../sigNoz.schemas';
|
||||
@@ -126,6 +129,105 @@ export const useGetTraceAggregations = <
|
||||
> => {
|
||||
return useMutation(getGetTraceAggregationsMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns the flamegraph view of spans for a given trace ID.
|
||||
* @summary Get flamegraph view for a trace
|
||||
*/
|
||||
export const getFlamegraph = (
|
||||
{ traceID }: GetFlamegraphPathParameters,
|
||||
spantypesPostableFlamegraphDTO?: BodyType<SpantypesPostableFlamegraphDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<GetFlamegraph200>({
|
||||
url: `/api/v3/traces/${traceID}/flamegraph`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: spantypesPostableFlamegraphDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetFlamegraphMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['getFlamegraph'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
}
|
||||
> = (props) => {
|
||||
const { pathParams, data } = props ?? {};
|
||||
|
||||
return getFlamegraph(pathParams, data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type GetFlamegraphMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>
|
||||
>;
|
||||
export type GetFlamegraphMutationBody =
|
||||
| BodyType<SpantypesPostableFlamegraphDTO>
|
||||
| undefined;
|
||||
export type GetFlamegraphMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary Get flamegraph view for a trace
|
||||
*/
|
||||
export const useGetFlamegraph = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
},
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof getFlamegraph>>,
|
||||
TError,
|
||||
{
|
||||
pathParams: GetFlamegraphPathParameters;
|
||||
data?: BodyType<SpantypesPostableFlamegraphDTO>;
|
||||
},
|
||||
TContext
|
||||
> => {
|
||||
return useMutation(getGetFlamegraphMutationOptions(options));
|
||||
};
|
||||
/**
|
||||
* Returns the waterfall view of spans for a given trace ID with tree structure, metadata, and windowed pagination
|
||||
* @summary Get waterfall view for a trace
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { QueryParams } from 'constants/query';
|
||||
|
||||
export const ExploreHeaderToolTip = {
|
||||
url: 'https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=new-query-builder',
|
||||
url: 'https://signoz.io/docs/querying/overview/?utm_source=product&utm_medium=new-query-builder',
|
||||
text: 'More details on how to use query builder',
|
||||
};
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ const MetricsAggregateSection = memo(function MetricsAggregateSection({
|
||||
Set the time interval for aggregation
|
||||
<br />
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#time-aggregation-windows"
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#temporal-aggregation-within-each-time-series"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#1890ff', textDecoration: 'underline' }}
|
||||
@@ -254,7 +254,7 @@ const MetricsAggregateSection = memo(function MetricsAggregateSection({
|
||||
Set the time interval for aggregation
|
||||
<br />
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#time-aggregation-windows"
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#temporal-aggregation-within-each-time-series"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#1890ff', textDecoration: 'underline' }}
|
||||
|
||||
@@ -52,7 +52,7 @@ const ADD_ONS = [
|
||||
key: ADD_ONS_KEYS.GROUP_BY,
|
||||
description:
|
||||
'Break down data by attributes like service name, endpoint, status code, or region. Essential for spotting patterns and comparing performance across different segments.',
|
||||
docLink: 'https://signoz.io/docs/userguide/query-builder-v5/#grouping',
|
||||
docLink: 'https://signoz.io/docs/querying/aggregation-grouping/#grouping',
|
||||
},
|
||||
{
|
||||
icon: <ScrollText size={14} />,
|
||||
@@ -61,7 +61,7 @@ const ADD_ONS = [
|
||||
description:
|
||||
'Filter grouped results based on aggregate conditions. Show only groups meeting specific criteria, like error rates > 5% or p99 latency > 500',
|
||||
docLink:
|
||||
'https://signoz.io/docs/userguide/query-builder-v5/#conditional-filtering-with-having',
|
||||
'https://signoz.io/docs/querying/result-manipulation/#conditional-filtering-with-having',
|
||||
},
|
||||
{
|
||||
icon: <ScrollText size={14} />,
|
||||
@@ -70,7 +70,7 @@ const ADD_ONS = [
|
||||
description:
|
||||
'Sort results to surface what matters most. Quickly identify slowest operations, most frequent errors, or highest resource consumers.',
|
||||
docLink:
|
||||
'https://signoz.io/docs/userguide/query-builder-v5/#sorting--limiting',
|
||||
'https://signoz.io/docs/querying/result-manipulation/#sorting--limiting',
|
||||
},
|
||||
{
|
||||
icon: <ScrollText size={14} />,
|
||||
@@ -79,7 +79,7 @@ const ADD_ONS = [
|
||||
description:
|
||||
'Show only the top/bottom N results. Perfect for focusing on outliers, reducing noise, and improving dashboard performance.',
|
||||
docLink:
|
||||
'https://signoz.io/docs/userguide/query-builder-v5/#sorting--limiting',
|
||||
'https://signoz.io/docs/querying/result-manipulation/#how-limit-works-for-time-series',
|
||||
},
|
||||
{
|
||||
icon: <ScrollText size={14} />,
|
||||
@@ -88,7 +88,7 @@ const ADD_ONS = [
|
||||
description:
|
||||
'Customize series labels using variables like {{service.name}}-{{endpoint}}. Makes charts readable at a glance during incident investigation.',
|
||||
docLink:
|
||||
'https://signoz.io/docs/userguide/query-builder-v5/#legend-formatting',
|
||||
'https://signoz.io/docs/querying/aggregation-grouping/#legend-formatting',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -99,7 +99,7 @@ const REDUCE_TO = {
|
||||
description:
|
||||
'Apply mathematical operations like sum, average, min, max, or percentiles to reduce multiple time series into a single value.',
|
||||
docLink:
|
||||
'https://signoz.io/docs/userguide/query-builder-v5/#reduce-operations',
|
||||
'https://signoz.io/docs/userguide/query-builder-v5/#result-manipulation',
|
||||
};
|
||||
|
||||
const hasValue = (value: unknown): boolean =>
|
||||
@@ -349,7 +349,7 @@ function QueryAddOns({
|
||||
<TooltipContent
|
||||
label="Group By"
|
||||
description="Break down data by attributes like service name, endpoint, status code, or region. Essential for spotting patterns and comparing performance across different segments."
|
||||
docLink="https://signoz.io/docs/userguide/query-builder-v5/#grouping"
|
||||
docLink="https://signoz.io/docs/querying/aggregation-grouping/#grouping"
|
||||
/>
|
||||
}
|
||||
placement="top"
|
||||
@@ -385,7 +385,7 @@ function QueryAddOns({
|
||||
<TooltipContent
|
||||
label="Having"
|
||||
description="Filter grouped results based on aggregate conditions. Show only groups meeting specific criteria, like error rates > 5% or p99 latency > 500"
|
||||
docLink="https://signoz.io/docs/userguide/query-builder-v5/#conditional-filtering-with-having"
|
||||
docLink="https://signoz.io/docs/querying/result-manipulation/#conditional-filtering-with-having"
|
||||
/>
|
||||
}
|
||||
placement="top"
|
||||
@@ -434,7 +434,7 @@ function QueryAddOns({
|
||||
<TooltipContent
|
||||
label="Order By"
|
||||
description="Sort results to surface what matters most. Quickly identify slowest operations, most frequent errors, or highest resource consumers."
|
||||
docLink="https://signoz.io/docs/userguide/query-builder-v5/#sorting--limiting"
|
||||
docLink="https://signoz.io/docs/querying/result-manipulation/#sorting--limiting"
|
||||
/>
|
||||
}
|
||||
placement="top"
|
||||
@@ -473,7 +473,7 @@ function QueryAddOns({
|
||||
<TooltipContent
|
||||
label="Reduce to"
|
||||
description="Apply mathematical operations like sum, average, min, max, or percentiles to reduce multiple time series into a single value."
|
||||
docLink="https://signoz.io/docs/userguide/query-builder-v5/#reduce-operations"
|
||||
docLink="https://signoz.io/docs/userguide/query-builder-v5/#result-manipulation"
|
||||
/>
|
||||
}
|
||||
placement="top"
|
||||
|
||||
@@ -65,7 +65,7 @@ function QueryAggregationOptions({
|
||||
Set the time interval for aggregation
|
||||
<br />
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#time-aggregation-windows"
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#temporal-aggregation-within-each-time-series"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#1890ff', textDecoration: 'underline' }}
|
||||
|
||||
@@ -676,7 +676,7 @@ function QueryAggregationSelect({
|
||||
</span>
|
||||
<br />
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#core-aggregation-functions"
|
||||
href="https://signoz.io/docs/querying/aggregation-grouping/#core-aggregation-functions-logs--traces"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ color: '#1890ff', textDecoration: 'underline' }}
|
||||
|
||||
@@ -44,7 +44,7 @@ function TraceOperatorSection({
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Add Trace Matching
|
||||
<Typography.Link
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#multi-query-analysis-trace-operators"
|
||||
href="https://signoz.io/docs/querying/multi-query-analysis/#trace-matching"
|
||||
target="_blank"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
>
|
||||
@@ -106,7 +106,7 @@ export default function QueryFooter({
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
Add New Formula
|
||||
<Typography.Link
|
||||
href="https://signoz.io/docs/userguide/query-builder-v5/#multi-query-analysis-advanced-comparisons"
|
||||
href="https://signoz.io/docs/querying/multi-query-analysis/#advanced-comparisons"
|
||||
target="_blank"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const apDexToolTipText =
|
||||
"Apdex is a way to measure your users' satisfaction with the response time of your web service. It's represented as a score from 0-1.";
|
||||
export const apDexToolTipUrl =
|
||||
'https://signoz.io/docs/userguide/metrics/#apdex?utm_source=product&utm_medium=frontend&utm_campaign=apdex';
|
||||
'https://signoz.io/docs/alerts-management/apdex-alerts/?utm_source=product&utm_medium=frontend&utm_campaign=apdex';
|
||||
export const apDexToolTipUrlText = 'Learn more about Apdex.';
|
||||
|
||||
@@ -68,7 +68,7 @@ function AlertChannels(): JSX.Element {
|
||||
<RightActionContainer>
|
||||
<TextToolTip
|
||||
text={t('tooltip_notification_channels')}
|
||||
url="https://signoz.io/docs/userguide/alerts-management/#setting-notification-channel"
|
||||
url="https://signoz.io/docs/setup-alerts-notification/"
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
|
||||
@@ -29,8 +29,7 @@ function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element {
|
||||
let url = '';
|
||||
switch (option) {
|
||||
case AlertTypes.ANOMALY_BASED_ALERT:
|
||||
url =
|
||||
'https://signoz.io/docs/alerts-management/anomaly-based-alerts/?utm_source=product&utm_medium=alert-source-selection-page#examples';
|
||||
url = 'https://signoz.io/docs/alerts-management/anomaly-based-alerts/';
|
||||
break;
|
||||
case AlertTypes.METRICS_BASED_ALERT:
|
||||
url =
|
||||
|
||||
@@ -31,8 +31,7 @@ export const ALERT_TYPE_URL_MAP: Record<
|
||||
'https://signoz.io/docs/alerts-management/exceptions-based-alerts/?utm_source=product&utm_medium=alert-creation-page',
|
||||
},
|
||||
[AlertTypes.ANOMALY_BASED_ALERT]: {
|
||||
selection:
|
||||
'https://signoz.io/docs/alerts-management/anomaly-based-alerts/?utm_source=product&utm_medium=alert-source-selection-page#examples',
|
||||
selection: 'https://signoz.io/docs/alerts-management/anomaly-based-alerts/',
|
||||
creation:
|
||||
'https://signoz.io/docs/alerts-management/anomaly-based-alerts/?utm_source=product&utm_medium=alert-creation-page',
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import ChartLayout from 'container/DashboardContainer/visualization/layout/ChartLayout/ChartLayout';
|
||||
import Legend from 'lib/uPlotV2/components/Legend/Legend';
|
||||
import UPlotLegend from 'lib/uPlotV2/components/Legend/UPlotLegend';
|
||||
import {
|
||||
LegendPosition,
|
||||
TooltipRenderArgs,
|
||||
@@ -47,7 +47,7 @@ export default function ChartWrapper({
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Legend
|
||||
<UPlotLegend
|
||||
config={config}
|
||||
position={legendConfig.position}
|
||||
averageLegendWidth={averageLegendWidth}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
.pieChartWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pieChartNoData {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
// Size is set inline from the computed chart dimensions (mirrors the uPlot
|
||||
// chart/legend split); this just centres the donut within that box.
|
||||
.pieChartContainer {
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pieChartTooltip {
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
background-color: var(--l2-background) !important;
|
||||
border: 1px solid var(--l2-border) !important;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.pieChartTooltipContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pieChartIndicator {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 2px;
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pieChartTooltipValue {
|
||||
font-weight: bold;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
// Wraps the shared chart Legend. Its width/height are set inline from the
|
||||
// computed chart dimensions, so the VirtuosoGrid inside gets the same bounded
|
||||
// box (right column / bottom rows) the uPlot charts use.
|
||||
.pieChartLegend {
|
||||
flex: 0 0 auto;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Group } from '@visx/group';
|
||||
import { Pie as VisxPie } from '@visx/shape';
|
||||
import { defaultStyles, useTooltip, useTooltipInPortal } from '@visx/tooltip';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
import { useResizeObserver } from 'hooks/useDimensions';
|
||||
import Legend from 'lib/uPlotV2/components/Legend/Legend';
|
||||
import { LegendPosition } from 'lib/uPlotV2/components/types';
|
||||
|
||||
import { PieChartProps, PieSlice } from '../types';
|
||||
import { calculateChartDimensions } from '../utils';
|
||||
|
||||
import { usePieInteractions } from '../../hooks/usePieInteractions';
|
||||
import PieArc from './PieArc';
|
||||
import PieCenterLabel from './PieCenterLabel';
|
||||
import styles from './Pie.module.scss';
|
||||
import { PieTooltipData } from './types';
|
||||
import { getFillColor } from './utils';
|
||||
|
||||
/**
|
||||
* Donut chart rendered with @visx. Splits its area into chart + legend with the
|
||||
* same `calculateChartDimensions` logic as the uPlot charts (right column /
|
||||
* up-to-two bottom rows), renders the shared chart Legend, and delegates the
|
||||
* arcs, centre total and interaction state to PieArc / PieCenterLabel /
|
||||
* usePieInteractions. Pure presentation — slices are pre-resolved by the caller.
|
||||
*/
|
||||
export default function Pie({
|
||||
data,
|
||||
yAxisUnit,
|
||||
decimalPrecision,
|
||||
isDarkMode,
|
||||
position = LegendPosition.BOTTOM,
|
||||
id,
|
||||
onSliceClick,
|
||||
'data-testid': testId,
|
||||
}: PieChartProps): JSX.Element {
|
||||
const {
|
||||
active,
|
||||
setActive,
|
||||
visibleData,
|
||||
legendItems,
|
||||
focusedSeriesIndex,
|
||||
onLegendClick,
|
||||
onLegendMouseMove,
|
||||
onLegendMouseLeave,
|
||||
} = usePieInteractions(data, id);
|
||||
|
||||
const {
|
||||
tooltipOpen,
|
||||
tooltipLeft,
|
||||
tooltipTop,
|
||||
tooltipData,
|
||||
hideTooltip,
|
||||
showTooltip,
|
||||
} = useTooltip<PieTooltipData>();
|
||||
|
||||
const { containerRef, TooltipInPortal } = useTooltipInPortal({
|
||||
scroll: true,
|
||||
detectBounds: true,
|
||||
});
|
||||
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const { width: containerWidth, height: containerHeight } =
|
||||
useResizeObserver(wrapperRef);
|
||||
|
||||
// Reuse the uPlot chart/legend split so the donut + legend get the same area
|
||||
// allocation (right column, or up-to-two bottom rows) as every other panel.
|
||||
const { width, height, legendWidth, legendHeight, averageLegendWidth } =
|
||||
useMemo(
|
||||
() =>
|
||||
calculateChartDimensions({
|
||||
containerWidth,
|
||||
containerHeight,
|
||||
legendConfig: { position },
|
||||
seriesLabels: data.map((slice) => slice.label),
|
||||
}),
|
||||
[containerWidth, containerHeight, position, data],
|
||||
);
|
||||
|
||||
// Donut geometry derived from the allocated chart box.
|
||||
const { size, radius, innerRadius } = useMemo(() => {
|
||||
const nextSize = Math.min(width, height);
|
||||
const nextRadius = nextSize * 0.35;
|
||||
return {
|
||||
size: nextSize,
|
||||
radius: nextRadius,
|
||||
innerRadius: nextRadius * 0.6,
|
||||
};
|
||||
}, [width, height]);
|
||||
|
||||
const totalValue = useMemo(
|
||||
() => visibleData.reduce((sum, slice) => sum + slice.value, 0),
|
||||
[visibleData],
|
||||
);
|
||||
|
||||
const labelColor = isDarkMode ? Color.BG_VANILLA_100 : Color.BG_INK_400;
|
||||
const activeColor = active?.color ?? null;
|
||||
|
||||
const handleSliceEnter = useCallback(
|
||||
(slice: PieSlice, centroidX: number, centroidY: number): void => {
|
||||
showTooltip({
|
||||
tooltipData: {
|
||||
label: slice.label,
|
||||
value: getYAxisFormattedValue(
|
||||
slice.value.toString(),
|
||||
yAxisUnit || 'none',
|
||||
decimalPrecision,
|
||||
),
|
||||
color: slice.color,
|
||||
},
|
||||
tooltipTop: centroidY + height / 2,
|
||||
tooltipLeft: centroidX + width / 2,
|
||||
});
|
||||
setActive(slice);
|
||||
},
|
||||
[showTooltip, setActive, yAxisUnit, decimalPrecision, height, width],
|
||||
);
|
||||
|
||||
const handleSliceLeave = useCallback((): void => {
|
||||
hideTooltip();
|
||||
setActive(null);
|
||||
}, [hideTooltip, setActive]);
|
||||
|
||||
if (!data.length) {
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
className={styles.pieChartWrapper}
|
||||
data-testid={testId}
|
||||
>
|
||||
<div className={styles.pieChartNoData}>No data</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const isRightLegend = position === LegendPosition.RIGHT;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
className={styles.pieChartWrapper}
|
||||
style={{ flexDirection: isRightLegend ? 'row' : 'column' }}
|
||||
data-testid={testId}
|
||||
>
|
||||
<div className={styles.pieChartContainer} style={{ width, height }}>
|
||||
{size > 0 && (
|
||||
<svg width={width} height={height} ref={containerRef}>
|
||||
<Group top={height / 2} left={width / 2}>
|
||||
<VisxPie
|
||||
data={visibleData}
|
||||
pieValue={(slice: PieSlice): number => slice.value}
|
||||
outerRadius={radius}
|
||||
innerRadius={innerRadius}
|
||||
padAngle={0.01}
|
||||
cornerRadius={3}
|
||||
width={size}
|
||||
height={size}
|
||||
>
|
||||
{(pie): JSX.Element[] =>
|
||||
pie.arcs.map((arc) => (
|
||||
<PieArc
|
||||
key={`arc-${arc.data.label}-${arc.data.value}-${arc.startAngle.toFixed(
|
||||
6,
|
||||
)}`}
|
||||
slice={arc.data}
|
||||
arcPath={pie.path(arc) || ''}
|
||||
centroid={pie.path.centroid(arc)}
|
||||
startAngle={arc.startAngle}
|
||||
endAngle={arc.endAngle}
|
||||
radius={radius}
|
||||
totalValue={totalValue}
|
||||
yAxisUnit={yAxisUnit}
|
||||
decimalPrecision={decimalPrecision}
|
||||
labelColor={labelColor}
|
||||
fill={getFillColor(arc.data.color, activeColor)}
|
||||
onEnter={handleSliceEnter}
|
||||
onLeave={handleSliceLeave}
|
||||
onClick={onSliceClick}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</VisxPie>
|
||||
<PieCenterLabel
|
||||
total={totalValue}
|
||||
yAxisUnit={yAxisUnit}
|
||||
decimalPrecision={decimalPrecision}
|
||||
radius={radius}
|
||||
innerRadius={innerRadius}
|
||||
color={labelColor}
|
||||
/>
|
||||
</Group>
|
||||
</svg>
|
||||
)}
|
||||
{tooltipOpen && tooltipData && (
|
||||
<TooltipInPortal
|
||||
top={tooltipTop}
|
||||
left={tooltipLeft}
|
||||
className={styles.pieChartTooltip}
|
||||
style={{
|
||||
...defaultStyles,
|
||||
color: labelColor,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={styles.pieChartIndicator}
|
||||
style={{ background: tooltipData.color }}
|
||||
/>
|
||||
<div className={styles.pieChartTooltipContent}>
|
||||
<span>{tooltipData.label}</span>
|
||||
<span className={styles.pieChartTooltipValue}>{tooltipData.value}</span>
|
||||
</div>
|
||||
</TooltipInPortal>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={styles.pieChartLegend}
|
||||
style={{
|
||||
width: legendWidth,
|
||||
height: legendHeight,
|
||||
}}
|
||||
>
|
||||
<Legend
|
||||
items={legendItems}
|
||||
position={position}
|
||||
averageLegendWidth={averageLegendWidth}
|
||||
focusedSeriesIndex={focusedSeriesIndex}
|
||||
onClick={onLegendClick}
|
||||
onMouseMove={onLegendMouseMove}
|
||||
onMouseLeave={onLegendMouseLeave}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import type { PrecisionOption } from 'components/Graph/types';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
|
||||
import { PieSlice } from '../types';
|
||||
|
||||
import { getArcGeometry } from './utils';
|
||||
|
||||
// Slices below this share of the total don't get a leader label (too cramped).
|
||||
const MIN_LABEL_SHARE = 0.03;
|
||||
const MAX_LABEL_LENGTH = 15;
|
||||
|
||||
interface PieArcProps {
|
||||
slice: PieSlice;
|
||||
/** SVG path `d` for the arc, from the visx pie generator. */
|
||||
arcPath: string;
|
||||
/** Arc centroid `[x, y]`, used to anchor the leader line and tooltip. */
|
||||
centroid: [number, number];
|
||||
startAngle: number;
|
||||
endAngle: number;
|
||||
radius: number;
|
||||
/** Sum of visible slice values — drives the show-label threshold. */
|
||||
totalValue: number;
|
||||
yAxisUnit?: string;
|
||||
decimalPrecision?: PrecisionOption;
|
||||
labelColor: string;
|
||||
/** Resolved fill (already dimmed if another slice is active). */
|
||||
fill: string;
|
||||
onEnter: (slice: PieSlice, centroidX: number, centroidY: number) => void;
|
||||
onLeave: () => void;
|
||||
onClick?: (slice: PieSlice) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single donut slice: the arc path plus, for non-tiny slices, a leader line
|
||||
* out to an external label + value. Pure presentation — interaction is
|
||||
* delegated to the `onEnter`/`onLeave`/`onClick` callbacks.
|
||||
*/
|
||||
export default function PieArc({
|
||||
slice,
|
||||
arcPath,
|
||||
centroid,
|
||||
startAngle,
|
||||
endAngle,
|
||||
radius,
|
||||
totalValue,
|
||||
yAxisUnit,
|
||||
decimalPrecision,
|
||||
labelColor,
|
||||
fill,
|
||||
onEnter,
|
||||
onLeave,
|
||||
onClick,
|
||||
}: PieArcProps): JSX.Element {
|
||||
const { label, value } = slice;
|
||||
const [centroidX, centroidY] = centroid;
|
||||
const { labelX, labelY, lineEndX, lineEndY, textAnchor } = getArcGeometry(
|
||||
startAngle,
|
||||
endAngle,
|
||||
radius,
|
||||
);
|
||||
|
||||
const displayValue = getYAxisFormattedValue(
|
||||
value.toString(),
|
||||
yAxisUnit || 'none',
|
||||
decimalPrecision,
|
||||
);
|
||||
const shortenedLabel =
|
||||
label.length > MAX_LABEL_LENGTH ? `${label.substring(0, 12)}...` : label;
|
||||
const shouldShowLabel = value / totalValue > MIN_LABEL_SHARE;
|
||||
|
||||
return (
|
||||
<g
|
||||
onMouseEnter={(): void => onEnter(slice, centroidX, centroidY)}
|
||||
onMouseLeave={onLeave}
|
||||
onClick={(): void => onClick?.(slice)}
|
||||
>
|
||||
<path d={arcPath} fill={fill} />
|
||||
{shouldShowLabel && (
|
||||
<>
|
||||
<line
|
||||
x1={centroidX}
|
||||
y1={centroidY}
|
||||
x2={lineEndX}
|
||||
y2={lineEndY}
|
||||
stroke={labelColor}
|
||||
strokeWidth={1}
|
||||
/>
|
||||
<line
|
||||
x1={lineEndX}
|
||||
y1={lineEndY}
|
||||
x2={labelX}
|
||||
y2={labelY}
|
||||
stroke={labelColor}
|
||||
strokeWidth={1}
|
||||
/>
|
||||
<text
|
||||
x={labelX}
|
||||
y={labelY - 8}
|
||||
dy=".33em"
|
||||
fill={labelColor}
|
||||
fontSize={10}
|
||||
textAnchor={textAnchor}
|
||||
pointerEvents="none"
|
||||
>
|
||||
{shortenedLabel}
|
||||
</text>
|
||||
<text
|
||||
x={labelX}
|
||||
y={labelY + 8}
|
||||
dy=".33em"
|
||||
fill={labelColor}
|
||||
fontSize={10}
|
||||
fontWeight="bold"
|
||||
textAnchor={textAnchor}
|
||||
pointerEvents="none"
|
||||
>
|
||||
{displayValue}
|
||||
</text>
|
||||
</>
|
||||
)}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { PrecisionOption } from 'components/Graph/types';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
|
||||
import { getScaledFontSize } from './utils';
|
||||
|
||||
interface PieCenterLabelProps {
|
||||
/** Sum of the visible slice values, shown in the donut hole. */
|
||||
total: number;
|
||||
yAxisUnit?: string;
|
||||
decimalPrecision?: PrecisionOption;
|
||||
radius: number;
|
||||
innerRadius: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total shown in the centre of the donut. Splits the formatted value into
|
||||
* its numeric part and unit so each can be sized independently, and scales the
|
||||
* numeric font down for long values so it never overflows the hole.
|
||||
*/
|
||||
export default function PieCenterLabel({
|
||||
total,
|
||||
yAxisUnit,
|
||||
decimalPrecision,
|
||||
radius,
|
||||
innerRadius,
|
||||
color,
|
||||
}: PieCenterLabelProps): JSX.Element {
|
||||
const formattedTotal = getYAxisFormattedValue(
|
||||
total.toString(),
|
||||
yAxisUnit || 'none',
|
||||
decimalPrecision,
|
||||
);
|
||||
const matches = formattedTotal.match(/([\d.]+[KMB]?)(.*)$/);
|
||||
const numericTotal = matches?.[1] || formattedTotal;
|
||||
const unitTotal = matches?.[2]?.trim() || '';
|
||||
|
||||
const numericFontSize = getScaledFontSize({
|
||||
text: numericTotal,
|
||||
baseSize: radius * 0.3,
|
||||
innerRadius,
|
||||
});
|
||||
const unitFontSize = numericFontSize * 0.5;
|
||||
|
||||
return (
|
||||
<text textAnchor="middle" dominantBaseline="central" fill={color}>
|
||||
<tspan fontSize={numericFontSize} fontWeight="bold">
|
||||
{numericTotal}
|
||||
</tspan>
|
||||
{unitTotal && (
|
||||
<tspan fontSize={unitFontSize} opacity={0.9} dx={2}>
|
||||
{unitTotal}
|
||||
</tspan>
|
||||
)}
|
||||
</text>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||
import { TooltipProvider } from '@signozhq/ui/tooltip';
|
||||
import { LegendPosition } from 'lib/uPlotV2/components/types';
|
||||
import { LegendItem } from 'lib/uPlotV2/config/types';
|
||||
|
||||
import { PieSlice } from '../../types';
|
||||
import Pie from '../Pie';
|
||||
|
||||
jest.mock('hooks/useDimensions', () => ({
|
||||
useResizeObserver: jest.fn().mockReturnValue({ width: 400, height: 300 }),
|
||||
}));
|
||||
|
||||
jest.mock('components/Graph/yAxisConfig', () => ({
|
||||
getYAxisFormattedValue: jest.fn((value: string) => value),
|
||||
}));
|
||||
|
||||
// VirtuosoGrid only renders a window in jsdom; render every item so we can
|
||||
// assert on legend entries.
|
||||
jest.mock('react-virtuoso', () => ({
|
||||
VirtuosoGrid: ({
|
||||
data,
|
||||
itemContent,
|
||||
}: {
|
||||
data: LegendItem[];
|
||||
itemContent: (index: number, item: LegendItem) => React.ReactNode;
|
||||
}): JSX.Element => (
|
||||
<div data-testid="virtuoso-grid">
|
||||
{data.map((item, index) => (
|
||||
<div key={item.seriesIndex ?? index}>{itemContent(index, item)}</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
const DATA: PieSlice[] = [
|
||||
{ label: 'frontend', value: 100, color: '#aa0000' },
|
||||
{ label: 'cart', value: 60, color: '#00aa00' },
|
||||
{ label: 'checkout', value: 40, color: '#0000aa' },
|
||||
];
|
||||
|
||||
function renderPie(
|
||||
props: Partial<React.ComponentProps<typeof Pie>> = {},
|
||||
): void {
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<Pie data={DATA} isDarkMode={false} data-testid="pie" {...props} />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe('Pie', () => {
|
||||
it('renders the "No data" state for empty data', () => {
|
||||
render(
|
||||
<TooltipProvider>
|
||||
<Pie data={[]} isDarkMode={false} data-testid="pie" />
|
||||
</TooltipProvider>,
|
||||
);
|
||||
expect(screen.getByText('No data')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders one arc per slice plus the legend entries and centre total', () => {
|
||||
renderPie();
|
||||
|
||||
const svg = screen.getByTestId('pie').querySelector('svg') as SVGElement;
|
||||
expect(svg.querySelectorAll('path')).toHaveLength(DATA.length);
|
||||
|
||||
const legend = screen.getByTestId('virtuoso-grid');
|
||||
expect(within(legend).getByText('frontend')).toBeInTheDocument();
|
||||
expect(within(legend).getByText('cart')).toBeInTheDocument();
|
||||
expect(within(legend).getByText('checkout')).toBeInTheDocument();
|
||||
|
||||
// Centre total = 100 + 60 + 40 (formatter mocked to echo the value).
|
||||
expect(screen.getByText('200')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('lays the legend out in a row for the right position and a column for bottom', () => {
|
||||
const { rerender } = render(
|
||||
<TooltipProvider>
|
||||
<Pie
|
||||
data={DATA}
|
||||
isDarkMode={false}
|
||||
position={LegendPosition.RIGHT}
|
||||
data-testid="pie"
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
expect(screen.getByTestId('pie')).toHaveStyle({ flexDirection: 'row' });
|
||||
|
||||
rerender(
|
||||
<TooltipProvider>
|
||||
<Pie
|
||||
data={DATA}
|
||||
isDarkMode={false}
|
||||
position={LegendPosition.BOTTOM}
|
||||
data-testid="pie"
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
expect(screen.getByTestId('pie')).toHaveStyle({ flexDirection: 'column' });
|
||||
});
|
||||
|
||||
it('hides a slice when its legend marker is clicked', () => {
|
||||
renderPie();
|
||||
const svg = screen.getByTestId('pie').querySelector('svg') as SVGElement;
|
||||
expect(svg.querySelectorAll('path')).toHaveLength(3);
|
||||
|
||||
const marker = document.querySelector(
|
||||
'[data-legend-item-id="1"] [data-is-legend-marker="true"]',
|
||||
) as HTMLElement;
|
||||
fireEvent.click(marker);
|
||||
|
||||
// One slice hidden → one fewer arc drawn.
|
||||
expect(svg.querySelectorAll('path')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
|
||||
import { PieSlice } from '../../types';
|
||||
import PieArc from '../PieArc';
|
||||
|
||||
jest.mock('components/Graph/yAxisConfig', () => ({
|
||||
// Echo the raw value so assertions are deterministic.
|
||||
getYAxisFormattedValue: jest.fn((value: string) => value),
|
||||
}));
|
||||
|
||||
const SLICE: PieSlice = { label: 'frontend', value: 50, color: '#f00' };
|
||||
|
||||
function renderArc(props: Partial<React.ComponentProps<typeof PieArc>> = {}): {
|
||||
onEnter: jest.Mock;
|
||||
onLeave: jest.Mock;
|
||||
onClick: jest.Mock;
|
||||
container: HTMLElement;
|
||||
} {
|
||||
const onEnter = jest.fn();
|
||||
const onLeave = jest.fn();
|
||||
const onClick = jest.fn();
|
||||
const { container } = render(
|
||||
<svg>
|
||||
<PieArc
|
||||
slice={SLICE}
|
||||
arcPath="M0,0L1,1"
|
||||
centroid={[10, 20]}
|
||||
startAngle={0}
|
||||
endAngle={Math.PI}
|
||||
radius={100}
|
||||
totalValue={100}
|
||||
labelColor="#fff"
|
||||
fill="#f00"
|
||||
onEnter={onEnter}
|
||||
onLeave={onLeave}
|
||||
onClick={onClick}
|
||||
{...props}
|
||||
/>
|
||||
</svg>,
|
||||
);
|
||||
return { onEnter, onLeave, onClick, container };
|
||||
}
|
||||
|
||||
describe('PieArc', () => {
|
||||
it('renders the arc path with the resolved fill', () => {
|
||||
const { container } = renderArc();
|
||||
const path = container.querySelector('path');
|
||||
expect(path).toHaveAttribute('d', 'M0,0L1,1');
|
||||
expect(path).toHaveAttribute('fill', '#f00');
|
||||
});
|
||||
|
||||
it('shows the leader label + value for a slice above the threshold', () => {
|
||||
renderArc(); // 50 / 100 = 0.5
|
||||
expect(screen.getByText('frontend')).toBeInTheDocument();
|
||||
expect(screen.getByText('50')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('hides the leader label for a slice below the 3% threshold', () => {
|
||||
renderArc({ totalValue: 10000 }); // 50 / 10000 = 0.005
|
||||
expect(screen.queryByText('frontend')).not.toBeInTheDocument();
|
||||
// the arc path itself still renders
|
||||
expect(screen.queryByText('50')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('truncates labels longer than 15 chars', () => {
|
||||
renderArc({
|
||||
slice: { label: 'a-really-long-service-name', value: 50, color: '#f00' },
|
||||
});
|
||||
expect(screen.getByText('a-really-lon...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('fires onEnter with the slice + centroid, and onLeave / onClick', () => {
|
||||
const { onEnter, onLeave, onClick, container } = renderArc();
|
||||
const g = container.querySelector('g') as SVGGElement;
|
||||
|
||||
fireEvent.mouseEnter(g);
|
||||
expect(onEnter).toHaveBeenCalledWith(SLICE, 10, 20);
|
||||
|
||||
fireEvent.mouseLeave(g);
|
||||
expect(onLeave).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.click(g);
|
||||
expect(onClick).toHaveBeenCalledWith(SLICE);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
|
||||
import PieCenterLabel from '../PieCenterLabel';
|
||||
|
||||
jest.mock('components/Graph/yAxisConfig', () => ({
|
||||
getYAxisFormattedValue: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockFormat = getYAxisFormattedValue as jest.MockedFunction<
|
||||
typeof getYAxisFormattedValue
|
||||
>;
|
||||
|
||||
function renderInSvg(node: JSX.Element): ReturnType<typeof render> {
|
||||
// PieCenterLabel returns an SVG <text>, so it needs an <svg> host.
|
||||
return render(<svg>{node}</svg>);
|
||||
}
|
||||
|
||||
describe('PieCenterLabel', () => {
|
||||
const baseProps = {
|
||||
total: 3700,
|
||||
radius: 100,
|
||||
innerRadius: 60,
|
||||
color: '#fff',
|
||||
};
|
||||
|
||||
it('renders the formatted total (numeric + unit suffix) as one numeric tspan when there is no separate unit', () => {
|
||||
mockFormat.mockReturnValue('3.7K');
|
||||
renderInSvg(<PieCenterLabel {...baseProps} />);
|
||||
expect(screen.getByText('3.7K')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('splits the numeric part and the trailing unit into separate tspans', () => {
|
||||
mockFormat.mockReturnValue('1.2 MB');
|
||||
renderInSvg(<PieCenterLabel {...baseProps} />);
|
||||
expect(screen.getByText('1.2')).toBeInTheDocument();
|
||||
expect(screen.getByText('MB')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('passes the unit + precision through to the formatter', () => {
|
||||
mockFormat.mockReturnValue('100');
|
||||
renderInSvg(<PieCenterLabel {...baseProps} total={100} yAxisUnit="bytes" />);
|
||||
expect(mockFormat).toHaveBeenCalledWith('100', 'bytes', undefined);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
import {
|
||||
getArcGeometry,
|
||||
getFillColor,
|
||||
getScaledFontSize,
|
||||
lightenColor,
|
||||
} from '../utils';
|
||||
|
||||
describe('Pie utils', () => {
|
||||
describe('getScaledFontSize', () => {
|
||||
it('returns the base size for empty text', () => {
|
||||
expect(getScaledFontSize({ text: '', baseSize: 30, innerRadius: 100 })).toBe(
|
||||
30,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not scale short text (length <= 3)', () => {
|
||||
// scaleFactor = max(0.3, 1) = 1 → baseSize, capped by innerRadius * 0.9.
|
||||
expect(
|
||||
getScaledFontSize({ text: '3.7', baseSize: 30, innerRadius: 100 }),
|
||||
).toBe(30);
|
||||
});
|
||||
|
||||
it('scales longer text down', () => {
|
||||
// length 8 → scaleFactor = max(0.3, 1 - 5 * 0.09) = 0.55 → 30 * 0.55.
|
||||
expect(
|
||||
getScaledFontSize({ text: '12345678', baseSize: 30, innerRadius: 100 }),
|
||||
).toBeCloseTo(16.5);
|
||||
});
|
||||
|
||||
it('floors the scale factor at 0.3 for very long text', () => {
|
||||
// length 20 → 1 - 17 * 0.09 < 0.3 → floored to 0.3 → 100 * 0.3.
|
||||
expect(
|
||||
getScaledFontSize({
|
||||
text: '12345678901234567890',
|
||||
baseSize: 100,
|
||||
innerRadius: 1000,
|
||||
}),
|
||||
).toBeCloseTo(30);
|
||||
});
|
||||
|
||||
it('caps the size at 90% of the inner radius', () => {
|
||||
expect(
|
||||
getScaledFontSize({ text: '3.7', baseSize: 200, innerRadius: 10 }),
|
||||
).toBeCloseTo(9);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getArcGeometry', () => {
|
||||
it('places the label below for a slice centred at the top (angle 0)', () => {
|
||||
const g = getArcGeometry(0, 0, 100);
|
||||
expect(g.labelX).toBeCloseTo(0);
|
||||
expect(g.labelY).toBeCloseTo(-130);
|
||||
expect(g.lineEndX).toBeCloseTo(0);
|
||||
expect(g.lineEndY).toBeCloseTo(-110);
|
||||
// sin(0) is not > 0 → anchor end.
|
||||
expect(g.textAnchor).toBe('end');
|
||||
});
|
||||
|
||||
it('anchors to the start on the right half (angle pi/2)', () => {
|
||||
const g = getArcGeometry(0, Math.PI, 100);
|
||||
expect(g.labelX).toBeCloseTo(130);
|
||||
expect(g.labelY).toBeCloseTo(0);
|
||||
expect(g.textAnchor).toBe('start');
|
||||
});
|
||||
|
||||
it('anchors to the end on the left half (angle 3pi/2)', () => {
|
||||
const g = getArcGeometry(Math.PI, 2 * Math.PI, 100);
|
||||
expect(g.labelX).toBeCloseTo(-130);
|
||||
expect(g.textAnchor).toBe('end');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lightenColor', () => {
|
||||
it('converts a #rrggbb hex to rgba at the given opacity', () => {
|
||||
expect(lightenColor('#ff0000', 0.4)).toBe('rgba(255, 0, 0, 0.4)');
|
||||
});
|
||||
|
||||
it('accepts hex without a leading #', () => {
|
||||
expect(lightenColor('00ff00', 0.4)).toBe('rgba(0, 255, 0, 0.4)');
|
||||
});
|
||||
|
||||
it('returns the original colour when it is not parseable hex', () => {
|
||||
expect(lightenColor('rgba(0,0,0,1)', 0.4)).toBe('rgba(0,0,0,1)');
|
||||
expect(lightenColor('red', 0.4)).toBe('red');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFillColor', () => {
|
||||
it('returns the colour unchanged when nothing is active', () => {
|
||||
expect(getFillColor('#ff0000', null)).toBe('#ff0000');
|
||||
});
|
||||
|
||||
it('returns the colour unchanged for the active slice', () => {
|
||||
expect(getFillColor('#ff0000', '#ff0000')).toBe('#ff0000');
|
||||
});
|
||||
|
||||
it('dims non-active slices to 40% opacity', () => {
|
||||
expect(getFillColor('#00ff00', '#ff0000')).toBe('rgba(0, 255, 0, 0.4)');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Pie-local types. Kept out of the component / util files so each stays focused
|
||||
* (per the one-component-per-file + dedicated-types rules). Shared chart types
|
||||
* (PieSlice, PieChartProps) live in the parent charts/types.ts.
|
||||
*/
|
||||
|
||||
export interface ScaledFontSizeArgs {
|
||||
text: string;
|
||||
baseSize: number;
|
||||
innerRadius: number;
|
||||
}
|
||||
|
||||
export interface ArcGeometry {
|
||||
/** Outer point where the leader label sits. */
|
||||
labelX: number;
|
||||
labelY: number;
|
||||
/** Elbow point where the leader line bends toward the label. */
|
||||
lineEndX: number;
|
||||
lineEndY: number;
|
||||
/** Anchor the label left/right depending on which half of the circle it's in. */
|
||||
textAnchor: 'start' | 'end';
|
||||
}
|
||||
|
||||
export interface ParsedRgb {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
/** Resolved tooltip payload shown when a slice is hovered. */
|
||||
export interface PieTooltipData {
|
||||
label: string;
|
||||
value: string;
|
||||
color: string;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Pure presentation helpers for the Pie chart. Kept out of the component file
|
||||
* so the renderer stays declarative (per the one-component-per-file rule).
|
||||
*/
|
||||
|
||||
import { ArcGeometry, ParsedRgb, ScaledFontSizeArgs } from './types';
|
||||
|
||||
/**
|
||||
* Shrinks the centre-total font as the text gets longer so it never overflows
|
||||
* the donut hole. Ported from the V1 PiePanelWrapper.
|
||||
*/
|
||||
export function getScaledFontSize({
|
||||
text,
|
||||
baseSize,
|
||||
innerRadius,
|
||||
}: ScaledFontSizeArgs): number {
|
||||
if (!text) {
|
||||
return baseSize;
|
||||
}
|
||||
|
||||
const { length } = text;
|
||||
// More aggressive scaling for very long numbers.
|
||||
const scaleFactor = Math.max(0.3, 1 - (length - 3) * 0.09);
|
||||
// Don't use more than 90% of the inner radius.
|
||||
const maxSize = innerRadius * 0.9;
|
||||
|
||||
return Math.min(baseSize * scaleFactor, maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the leader-line / label geometry for one arc from its angular span.
|
||||
* Pulled out of the render prop so the SVG markup stays declarative.
|
||||
*/
|
||||
export function getArcGeometry(
|
||||
startAngle: number,
|
||||
endAngle: number,
|
||||
radius: number,
|
||||
): ArcGeometry {
|
||||
const angle = (startAngle + endAngle) / 2;
|
||||
const labelRadius = radius * 1.3;
|
||||
const lineEndRadius = radius * 1.1;
|
||||
return {
|
||||
labelX: Math.sin(angle) * labelRadius,
|
||||
labelY: -Math.cos(angle) * labelRadius,
|
||||
lineEndX: Math.sin(angle) * lineEndRadius,
|
||||
lineEndY: -Math.cos(angle) * lineEndRadius,
|
||||
textAnchor: Math.sin(angle) > 0 ? 'start' : 'end',
|
||||
};
|
||||
}
|
||||
|
||||
// Parses `#rrggbb` into its components. Returns null for anything else (e.g. an
|
||||
// already-rgba string), letting callers fall back to the original colour.
|
||||
function hexToRgb(color: string): ParsedRgb | null {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16),
|
||||
g: parseInt(result[2], 16),
|
||||
b: parseInt(result[3], 16),
|
||||
}
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an rgba() string for `color` at the given opacity. Used to dim the
|
||||
* non-hovered slices. Falls back to the original colour if it can't be parsed.
|
||||
*/
|
||||
export function lightenColor(color: string, opacity: number): string {
|
||||
const rgb = hexToRgb(color);
|
||||
if (!rgb) {
|
||||
return color;
|
||||
}
|
||||
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${opacity})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the fill for a slice given the currently-hovered slice colour:
|
||||
* everything but the active slice dims to 40% opacity. With nothing hovered
|
||||
* (`activeColor === null`) every slice keeps its full colour.
|
||||
*/
|
||||
export function getFillColor(
|
||||
color: string,
|
||||
activeColor: string | null,
|
||||
): string {
|
||||
if (activeColor === null) {
|
||||
return color;
|
||||
}
|
||||
return activeColor === color ? color : lightenColor(color, 0.4);
|
||||
}
|
||||
@@ -3,13 +3,14 @@ import { PrecisionOption } from 'components/Graph/types';
|
||||
import {
|
||||
IRenderTooltipFooterArgs,
|
||||
LegendConfig,
|
||||
LegendPosition,
|
||||
TooltipRenderArgs,
|
||||
} from 'lib/uPlotV2/components/types';
|
||||
import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder';
|
||||
import {
|
||||
DashboardCursorSync,
|
||||
SyncTooltipFilterMode,
|
||||
TooltipClickData,
|
||||
ChartClickData,
|
||||
} from 'lib/uPlotV2/plugins/TooltipPlugin/types';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
@@ -22,10 +23,10 @@ interface BaseChartProps {
|
||||
/** Key that pins the tooltip while hovering. Defaults to DEFAULT_PIN_TOOLTIP_KEY ('l'). */
|
||||
pinKey?: string;
|
||||
/** Called when the user clicks the uPlot overlay. Receives resolved click data. */
|
||||
onClick?: (clickData: TooltipClickData) => void;
|
||||
onClick?: (clickData: ChartClickData) => void;
|
||||
yAxisUnit?: string;
|
||||
decimalPrecision?: PrecisionOption;
|
||||
pinnedTooltipElement?: (clickData: TooltipClickData) => React.ReactNode;
|
||||
pinnedTooltipElement?: (clickData: ChartClickData) => React.ReactNode;
|
||||
renderTooltipFooter?: (args: IRenderTooltipFooterArgs) => React.ReactNode;
|
||||
customTooltip?: (props: TooltipRenderArgs) => React.ReactNode;
|
||||
'data-testid'?: string;
|
||||
@@ -69,3 +70,36 @@ export type ChartProps =
|
||||
| TimeSeriesChartProps
|
||||
| BarChartProps
|
||||
| HistogramChartProps;
|
||||
|
||||
/**
|
||||
* One resolved pie/donut slice: a display label, its (already parsed) positive
|
||||
* numeric value, and the colour used for the arc + legend swatch.
|
||||
*/
|
||||
export interface PieSlice {
|
||||
label: string;
|
||||
value: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the Pie chart. Unlike the others above, Pie is NOT uPlot-based
|
||||
* (it renders with @visx), so it deliberately does not extend BaseChartProps /
|
||||
* UPlotBasedChartProps — it takes pre-resolved slices and self-measures its
|
||||
* draw area rather than receiving a uPlot config + aligned data.
|
||||
*/
|
||||
export interface PieChartProps {
|
||||
data: PieSlice[];
|
||||
yAxisUnit?: string;
|
||||
decimalPrecision?: PrecisionOption;
|
||||
isDarkMode: boolean;
|
||||
/** Legend placement. Drives the chart-vs-legend layout. Default BOTTOM. */
|
||||
position?: LegendPosition;
|
||||
/**
|
||||
* Widget id used to persist per-slice hide/unhide state to localStorage
|
||||
* (shared GRAPH_VISIBILITY_STATES, keyed by label). Omit to disable persistence.
|
||||
*/
|
||||
id?: string;
|
||||
/** Fired when a slice (or its legend entry) is clicked. */
|
||||
onSliceClick?: (slice: PieSlice) => void;
|
||||
'data-testid'?: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import {
|
||||
getStoredSeriesVisibility,
|
||||
updateSeriesVisibilityToLocalStorage,
|
||||
} from 'container/DashboardContainer/visualization/panels/utils/legendVisibilityUtils';
|
||||
import type { MouseEvent } from 'react';
|
||||
|
||||
import { PieSlice } from '../../charts/types';
|
||||
import { usePieInteractions } from '../usePieInteractions';
|
||||
|
||||
jest.mock(
|
||||
'container/DashboardContainer/visualization/panels/utils/legendVisibilityUtils',
|
||||
);
|
||||
|
||||
const mockGetStored = getStoredSeriesVisibility as jest.MockedFunction<
|
||||
typeof getStoredSeriesVisibility
|
||||
>;
|
||||
const mockUpdateStored =
|
||||
updateSeriesVisibilityToLocalStorage as jest.MockedFunction<
|
||||
typeof updateSeriesVisibilityToLocalStorage
|
||||
>;
|
||||
|
||||
const DATA: PieSlice[] = [
|
||||
{ label: 'frontend', value: 100, color: '#a' },
|
||||
{ label: 'cart', value: 60, color: '#b' },
|
||||
{ label: 'checkout', value: 40, color: '#c' },
|
||||
];
|
||||
|
||||
// Builds a fake legend click/move event: `e.target.closest('[data-legend-item-id]')`
|
||||
// resolves to the item at `index`, and `e.target.dataset.isLegendMarker` flags marker clicks.
|
||||
function legendEvent(
|
||||
index: number | null,
|
||||
isMarker = false,
|
||||
): MouseEvent<HTMLDivElement> {
|
||||
const itemEl =
|
||||
index == null ? null : { dataset: { legendItemId: String(index) } };
|
||||
return {
|
||||
target: {
|
||||
closest: (): unknown => itemEl,
|
||||
dataset: { isLegendMarker: isMarker ? 'true' : undefined },
|
||||
},
|
||||
} as unknown as MouseEvent<HTMLDivElement>;
|
||||
}
|
||||
|
||||
describe('usePieInteractions', () => {
|
||||
beforeEach(() => {
|
||||
mockGetStored.mockReturnValue(null);
|
||||
mockUpdateStored.mockReset();
|
||||
});
|
||||
|
||||
it('starts with everything visible and nothing focused', () => {
|
||||
const { result } = renderHook(() => usePieInteractions(DATA));
|
||||
|
||||
expect(result.current.visibleData).toStrictEqual(DATA);
|
||||
expect(result.current.legendItems.map((i) => i.show)).toStrictEqual([
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
]);
|
||||
expect(result.current.focusedSeriesIndex).toBeNull();
|
||||
expect(result.current.active).toBeNull();
|
||||
});
|
||||
|
||||
describe('marker click (toggle one)', () => {
|
||||
it('hides then unhides the clicked slice', () => {
|
||||
const { result } = renderHook(() => usePieInteractions(DATA, 'panel-1'));
|
||||
|
||||
act(() => result.current.onLegendClick(legendEvent(1, true)));
|
||||
|
||||
expect(result.current.visibleData).toStrictEqual([DATA[0], DATA[2]]);
|
||||
expect(result.current.legendItems[1].show).toBe(false);
|
||||
expect(mockUpdateStored).toHaveBeenLastCalledWith('panel-1', [
|
||||
{ label: 'frontend', show: true },
|
||||
{ label: 'cart', show: false },
|
||||
{ label: 'checkout', show: true },
|
||||
]);
|
||||
|
||||
act(() => result.current.onLegendClick(legendEvent(1, true)));
|
||||
|
||||
expect(result.current.visibleData).toStrictEqual(DATA);
|
||||
expect(result.current.legendItems[1].show).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('label click (isolate / reset)', () => {
|
||||
it('isolates the clicked slice, then resets on a second click', () => {
|
||||
const { result } = renderHook(() => usePieInteractions(DATA));
|
||||
|
||||
act(() => result.current.onLegendClick(legendEvent(0, false)));
|
||||
|
||||
expect(result.current.visibleData).toStrictEqual([DATA[0]]);
|
||||
expect(result.current.legendItems.map((i) => i.show)).toStrictEqual([
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
]);
|
||||
|
||||
act(() => result.current.onLegendClick(legendEvent(0, false)));
|
||||
|
||||
expect(result.current.visibleData).toStrictEqual(DATA);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hover', () => {
|
||||
it('focuses the hovered slice and clears on leave', () => {
|
||||
const { result } = renderHook(() => usePieInteractions(DATA));
|
||||
|
||||
act(() => result.current.onLegendMouseMove(legendEvent(2)));
|
||||
expect(result.current.active).toStrictEqual(DATA[2]);
|
||||
expect(result.current.focusedSeriesIndex).toBe(2);
|
||||
|
||||
act(() => result.current.onLegendMouseLeave());
|
||||
expect(result.current.active).toBeNull();
|
||||
expect(result.current.focusedSeriesIndex).toBeNull();
|
||||
});
|
||||
|
||||
it('does not focus a hidden slice', () => {
|
||||
const { result } = renderHook(() => usePieInteractions(DATA));
|
||||
|
||||
act(() => result.current.onLegendClick(legendEvent(1, true))); // hide cart
|
||||
act(() => result.current.onLegendMouseMove(legendEvent(1)));
|
||||
|
||||
expect(result.current.active).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('persistence', () => {
|
||||
it('does not write to storage when no id is provided', () => {
|
||||
const { result } = renderHook(() => usePieInteractions(DATA));
|
||||
act(() => result.current.onLegendClick(legendEvent(0, true)));
|
||||
expect(mockUpdateStored).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rehydrates hidden slices from storage on mount (matched by label)', () => {
|
||||
mockGetStored.mockReturnValue([
|
||||
{ label: 'frontend', show: true },
|
||||
{ label: 'cart', show: false },
|
||||
{ label: 'checkout', show: true },
|
||||
]);
|
||||
|
||||
const { result } = renderHook(() => usePieInteractions(DATA, 'panel-1'));
|
||||
|
||||
expect(result.current.visibleData).toStrictEqual([DATA[0], DATA[2]]);
|
||||
expect(result.current.legendItems[1].show).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,168 @@
|
||||
import { LegendItem } from 'lib/uPlotV2/config/types';
|
||||
import type { Dispatch, MouseEvent, SetStateAction } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
getStoredSeriesVisibility,
|
||||
updateSeriesVisibilityToLocalStorage,
|
||||
} from '../panels/utils/legendVisibilityUtils';
|
||||
import { PieSlice } from '../charts/types';
|
||||
|
||||
export interface UsePieInteractionsResult {
|
||||
/** The hovered/focused slice (drives donut dimming + tooltip). */
|
||||
active: PieSlice | null;
|
||||
setActive: Dispatch<SetStateAction<PieSlice | null>>;
|
||||
/** Slices currently shown (hidden ones removed). */
|
||||
visibleData: PieSlice[];
|
||||
/** Legend item per slice (`show` reflects hide state). */
|
||||
legendItems: LegendItem[];
|
||||
/** Index of the active slice for the legend's focus highlight, or null. */
|
||||
focusedSeriesIndex: number | null;
|
||||
onLegendClick: (e: MouseEvent<HTMLDivElement>) => void;
|
||||
onLegendMouseMove: (e: MouseEvent<HTMLDivElement>) => void;
|
||||
onLegendMouseLeave: () => void;
|
||||
}
|
||||
|
||||
// Reads the slice index off the nearest `[data-legend-item-id]` ancestor of the
|
||||
// event target (the shared Legend tags each item with its seriesIndex).
|
||||
function getLegendIndex(e: MouseEvent<HTMLDivElement>): number | null {
|
||||
const el = (e.target as HTMLElement | null)?.closest<HTMLElement>(
|
||||
'[data-legend-item-id]',
|
||||
);
|
||||
const id = el?.dataset.legendItemId;
|
||||
return id != null ? Number(id) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pie interaction + derived state: hover/focus, slice hide/unhide (mirroring the
|
||||
* uPlot legend — marker toggles one, label isolates), and persistence of the
|
||||
* hidden set to localStorage (keyed by `id`, matched by label) so it survives
|
||||
* reloads. Returns the visible slices, legend items, focus index, and the
|
||||
* legend container handlers.
|
||||
*/
|
||||
export function usePieInteractions(
|
||||
data: PieSlice[],
|
||||
id?: string,
|
||||
): UsePieInteractionsResult {
|
||||
const [active, setActive] = useState<PieSlice | null>(null);
|
||||
const [hiddenIndices, setHiddenIndices] = useState<Set<number>>(
|
||||
() => new Set(),
|
||||
);
|
||||
const isolatedIndexRef = useRef<number | null>(null);
|
||||
|
||||
const legendItems = useMemo<LegendItem[]>(
|
||||
() =>
|
||||
data.map((slice, index) => ({
|
||||
seriesIndex: index,
|
||||
label: slice.label,
|
||||
color: slice.color,
|
||||
show: !hiddenIndices.has(index),
|
||||
})),
|
||||
[data, hiddenIndices],
|
||||
);
|
||||
|
||||
// Hidden slices drop out so the remaining arcs + centre total recompute.
|
||||
const visibleData = useMemo(
|
||||
() => data.filter((_, index) => !hiddenIndices.has(index)),
|
||||
[data, hiddenIndices],
|
||||
);
|
||||
|
||||
// Rehydrate hide/unhide from localStorage (matched by label) whenever the
|
||||
// data set changes — including first load and every refetch, since the store
|
||||
// is the source of truth and toggles write back to it.
|
||||
useEffect(() => {
|
||||
if (!id || !data.length) {
|
||||
return;
|
||||
}
|
||||
const stored = getStoredSeriesVisibility(id);
|
||||
if (!stored) {
|
||||
return;
|
||||
}
|
||||
const hidden = new Set<number>();
|
||||
data.forEach((slice, index) => {
|
||||
if (stored.find((s) => s.label === slice.label)?.show === false) {
|
||||
hidden.add(index);
|
||||
}
|
||||
});
|
||||
setHiddenIndices(hidden);
|
||||
}, [id, data]);
|
||||
|
||||
// Apply a new hidden set and persist it (label + show) to localStorage.
|
||||
const applyHidden = useCallback(
|
||||
(hidden: Set<number>): void => {
|
||||
setHiddenIndices(hidden);
|
||||
if (id) {
|
||||
updateSeriesVisibilityToLocalStorage(
|
||||
id,
|
||||
data.map((slice, index) => ({
|
||||
label: slice.label,
|
||||
show: !hidden.has(index),
|
||||
})),
|
||||
);
|
||||
}
|
||||
},
|
||||
[id, data],
|
||||
);
|
||||
|
||||
const onLegendMouseMove = useCallback(
|
||||
(e: MouseEvent<HTMLDivElement>): void => {
|
||||
const index = getLegendIndex(e);
|
||||
// Don't focus/dim for hidden slices — they aren't on the donut.
|
||||
setActive(index != null && !hiddenIndices.has(index) ? data[index] : null);
|
||||
},
|
||||
[data, hiddenIndices],
|
||||
);
|
||||
|
||||
// Marker click toggles just that slice on/off; label click isolates it
|
||||
// (clicking the isolated one again resets to all) — mirrors the uPlot legend.
|
||||
const onLegendClick = useCallback(
|
||||
(e: MouseEvent<HTMLDivElement>): void => {
|
||||
const index = getLegendIndex(e);
|
||||
if (index == null) {
|
||||
return;
|
||||
}
|
||||
const isMarker = (e.target as HTMLElement).dataset.isLegendMarker;
|
||||
|
||||
if (isMarker) {
|
||||
const next = new Set(hiddenIndices);
|
||||
if (next.has(index)) {
|
||||
next.delete(index);
|
||||
} else {
|
||||
next.add(index);
|
||||
}
|
||||
applyHidden(next);
|
||||
return;
|
||||
}
|
||||
|
||||
const isReset = isolatedIndexRef.current === index;
|
||||
isolatedIndexRef.current = isReset ? null : index;
|
||||
if (isReset) {
|
||||
applyHidden(new Set());
|
||||
return;
|
||||
}
|
||||
const next = new Set<number>();
|
||||
data.forEach((_, i) => {
|
||||
if (i !== index) {
|
||||
next.add(i);
|
||||
}
|
||||
});
|
||||
applyHidden(next);
|
||||
},
|
||||
[data, hiddenIndices, applyHidden],
|
||||
);
|
||||
|
||||
const onLegendMouseLeave = useCallback((): void => setActive(null), []);
|
||||
|
||||
const focusedIndex = active ? data.indexOf(active) : -1;
|
||||
|
||||
return {
|
||||
active,
|
||||
setActive,
|
||||
visibleData,
|
||||
legendItems,
|
||||
focusedSeriesIndex: focusedIndex >= 0 ? focusedIndex : null,
|
||||
onLegendClick,
|
||||
onLegendMouseMove,
|
||||
onLegendMouseLeave,
|
||||
};
|
||||
}
|
||||
@@ -717,13 +717,13 @@ function ExplorerOptions({
|
||||
|
||||
const infoIconLink = useMemo(() => {
|
||||
if (isLogsExplorer) {
|
||||
return 'https://signoz.io/docs/product-features/logs-explorer/?utm_source=product&utm_medium=logs-explorer-toolbar';
|
||||
return 'https://signoz.io/docs/userguide/logs_query_builder/?utm_source=product&utm_medium=logs-explorer-toolbar';
|
||||
}
|
||||
// TODO: Add metrics explorer info icon link
|
||||
if (isMetricsExplorer) {
|
||||
return '';
|
||||
}
|
||||
return 'https://signoz.io/docs/product-features/trace-explorer/?utm_source=product&utm_medium=trace-explorer-toolbar';
|
||||
return 'https://signoz.io/docs/userguide/traces/?utm_source=product&utm_medium=trace-explorer-toolbar';
|
||||
}, [isLogsExplorer, isMetricsExplorer]);
|
||||
|
||||
const getQueryName = (query: Query): string => {
|
||||
|
||||
@@ -201,7 +201,7 @@ export default function SavedViews({
|
||||
});
|
||||
|
||||
window.open(
|
||||
'https://signoz.io/docs/product-features/saved-view/',
|
||||
'https://signoz.io/docs/metrics-management/metrics-explorer/#saved-views-in-metrics-explorer',
|
||||
'_blank',
|
||||
'noopener noreferrer',
|
||||
);
|
||||
|
||||
@@ -29,12 +29,12 @@ export const checkListStepToPreferenceKeyMap = {
|
||||
|
||||
export const DOCS_LINKS = {
|
||||
ADD_DATA_SOURCE: 'https://signoz.io/docs/instrumentation/overview/',
|
||||
SEND_LOGS: 'https://signoz.io/docs/userguide/logs/',
|
||||
SEND_LOGS: 'https://signoz.io/docs/userguide/logs_query_builder/',
|
||||
SEND_TRACES: 'https://signoz.io/docs/userguide/traces/',
|
||||
SEND_METRICS: 'https://signoz.io/docs/metrics-management/metrics-explorer/',
|
||||
SETUP_ALERTS: 'https://signoz.io/docs/userguide/alerts-management/',
|
||||
SETUP_ALERTS: 'https://signoz.io/docs/alerts/',
|
||||
SETUP_SAVED_VIEWS:
|
||||
'https://signoz.io/docs/product-features/saved-view/#step-2-save-your-view',
|
||||
'https://signoz.io/docs/metrics-management/metrics-explorer/#saved-views-in-metrics-explorer',
|
||||
SETUP_DASHBOARDS: 'https://signoz.io/docs/userguide/manage-dashboards/',
|
||||
};
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export function K8sEmptyState({
|
||||
<span className={styles.message}>
|
||||
Please refer to{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/hostmetrics/"
|
||||
href="https://signoz.io/docs/infrastructure-monitoring/hostmetrics/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -611,7 +611,7 @@ describe('K8sBaseList', () => {
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
'https://signoz.io/docs/userguide/hostmetrics/',
|
||||
'https://signoz.io/docs/infrastructure-monitoring/hostmetrics/',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@ export const deploymentWidgetInfo = [
|
||||
yAxisUnit: '',
|
||||
},
|
||||
{
|
||||
title: 'Memory usage, request, limits)',
|
||||
title: 'Memory usage, request, limits',
|
||||
yAxisUnit: 'bytes',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -69,7 +69,7 @@ export const jobWidgetInfo = [
|
||||
yAxisUnit: '',
|
||||
},
|
||||
{
|
||||
title: 'Memory usage, request, limits',
|
||||
title: 'Memory Usage',
|
||||
yAxisUnit: 'bytes',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -703,7 +703,7 @@ export const getNamespaceMetricsQueryPayload = (
|
||||
],
|
||||
having: [],
|
||||
legend: `{{${k8sPodNameKey}}}`,
|
||||
limit: 20,
|
||||
limit: 10,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
reduceTo: ReduceOperators.AVG,
|
||||
@@ -1014,8 +1014,8 @@ export const getNamespaceMetricsQueryPayload = (
|
||||
id: '5f2a55c5',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: k8sStatefulsetNameKey,
|
||||
key: k8sStatefulsetNameKey,
|
||||
id: k8sNamespaceNameKey,
|
||||
key: k8sNamespaceNameKey,
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
|
||||
@@ -317,9 +317,9 @@ export const getVolumeMetricsQueryPayload = (
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id: 'k8s_volume_inodes_used--float64----true',
|
||||
id: 'k8s_volume_inodes_used--float64--Gauge--true',
|
||||
key: k8sVolumeInodesUsedKey,
|
||||
type: '',
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
dataSource: DataSource.METRICS,
|
||||
@@ -409,9 +409,9 @@ export const getVolumeMetricsQueryPayload = (
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id: 'k8s_volume_inodes--float64----true',
|
||||
id: 'k8s_volume_inodes--float64--Gauge--true',
|
||||
key: k8sVolumeInodesKey,
|
||||
type: '',
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
dataSource: DataSource.METRICS,
|
||||
@@ -501,9 +501,9 @@ export const getVolumeMetricsQueryPayload = (
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id: 'k8s_volume_inodes_free--float64----true',
|
||||
id: 'k8s_volume_inodes_free--float64--Gauge--true',
|
||||
key: k8sVolumeInodesFreeKey,
|
||||
type: '',
|
||||
type: 'Gauge',
|
||||
},
|
||||
aggregateOperator: 'avg',
|
||||
dataSource: DataSource.METRICS,
|
||||
|
||||
@@ -1619,6 +1619,9 @@ export const getHostQueryPayload = (
|
||||
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';
|
||||
@@ -2375,9 +2378,24 @@ export const getHostQueryPayload = (
|
||||
op: 'AND',
|
||||
},
|
||||
functions: [],
|
||||
groupBy: [],
|
||||
groupBy: [
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'direction--string--tag--false',
|
||||
|
||||
key: 'direction',
|
||||
type: 'tag',
|
||||
},
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'device--string--tag--false',
|
||||
|
||||
key: 'device',
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [],
|
||||
legend: 'system disk io',
|
||||
legend: '{{device}}::{{direction}}',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
@@ -2409,9 +2427,9 @@ export const getHostQueryPayload = (
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id: 'system_disk_operation_time--float64--Sum--true',
|
||||
id: 'system_disk_operations--float64--Sum--true',
|
||||
|
||||
key: diskOpTimeKey,
|
||||
key: diskOpsKey,
|
||||
type: 'Sum',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
@@ -2421,7 +2439,7 @@ export const getHostQueryPayload = (
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: 'diskop_f1',
|
||||
id: 'diskops_f1',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'host_name--string--tag--false',
|
||||
@@ -2454,7 +2472,7 @@ export const getHostQueryPayload = (
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${diskOpTimeKey})`,
|
||||
columnName: `SUM(${diskOpsKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
@@ -2557,6 +2575,88 @@ export const getHostQueryPayload = (
|
||||
start,
|
||||
end,
|
||||
},
|
||||
{
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
graphType: PANEL_TYPES.TIME_SERIES,
|
||||
query: {
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id: 'system_disk_operation_time--float64--Sum--true',
|
||||
|
||||
key: diskOpTimeKey,
|
||||
type: 'Sum',
|
||||
},
|
||||
aggregateOperator: 'rate',
|
||||
dataSource: DataSource.METRICS,
|
||||
disabled: false,
|
||||
expression: 'A',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: 'diskoptime_f1',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'host_name--string--tag--false',
|
||||
|
||||
key: hostNameKey,
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: hostName,
|
||||
},
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
functions: [],
|
||||
groupBy: [
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'device--string--tag--false',
|
||||
|
||||
key: 'device',
|
||||
type: 'tag',
|
||||
},
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'direction--string--tag--false',
|
||||
|
||||
key: 'direction',
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
having: [
|
||||
{
|
||||
columnName: `SUM(${diskOpTimeKey})`,
|
||||
op: '>',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
legend: '{{device}}::{{direction}}',
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
queryName: 'A',
|
||||
reduceTo: ReduceOperators.AVG,
|
||||
spaceAggregation: 'sum',
|
||||
stepInterval: 60,
|
||||
timeAggregation: 'rate',
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
queryTraceOperator: [],
|
||||
},
|
||||
clickhouse_sql: [{ disabled: false, legend: '', name: 'A', query: '' }],
|
||||
id: 'a8b3d2e1-4f5c-4a6b-9c8d-7e2f1a0b3c4f',
|
||||
promql: [{ disabled: false, legend: '', name: 'A', query: '' }],
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
},
|
||||
variables: {},
|
||||
formatForWeb: false,
|
||||
start,
|
||||
end,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -2631,5 +2731,5 @@ export const hostWidgetInfo = [
|
||||
{ title: 'System disk io (bytes transferred)', yAxisUnit: 'bytes' },
|
||||
{ title: 'System disk operations/s', yAxisUnit: 'short' },
|
||||
{ title: 'Queue size', yAxisUnit: 'short' },
|
||||
{ title: 'Disk operations time', yAxisUnit: 's' },
|
||||
{ title: 'System disk operation time/s', yAxisUnit: 's' },
|
||||
];
|
||||
|
||||
@@ -164,7 +164,7 @@ function BreakDown(): JSX.Element {
|
||||
Meter metrics data is aggregated over 1 hour period. Please select time
|
||||
range accordingly.
|
||||
<a
|
||||
href="https://signoz.io/docs/cost-meter/overview/#accessing-cost-meter"
|
||||
href="https://signoz.io/docs/cost-meter/overview/#get-started"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
style={{ textDecoration: 'underline' }}
|
||||
|
||||
@@ -197,7 +197,7 @@ function TopOperationsTable({
|
||||
|
||||
const entryPointSpanInfo = {
|
||||
text: 'Shows the spans where requests enter new services for the first time',
|
||||
url: 'https://signoz.io/docs/traces-management/guides/entry-point-spans-service-overview/',
|
||||
url: 'https://signoz.io/docs/apm-and-distributed-tracing/application-details/',
|
||||
urlText: 'Learn more about Entrypoint Spans.',
|
||||
};
|
||||
|
||||
|
||||
@@ -96,14 +96,28 @@ function CreateOrEdit(props: CreateOrEditProps): JSX.Element {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { domainToAdminEmailList, ...rest } = config;
|
||||
const {
|
||||
domainToAdminEmailList,
|
||||
allowedGroups,
|
||||
serviceAccountJson,
|
||||
domainToAdminEmail: _domainToAdminEmail,
|
||||
fetchTransitiveGroupMembership,
|
||||
...rest
|
||||
} = config;
|
||||
const domainToAdminEmail = convertDomainMappingsToRecord(
|
||||
domainToAdminEmailList,
|
||||
);
|
||||
|
||||
return {
|
||||
...rest,
|
||||
domainToAdminEmail: domainToAdminEmail ?? {},
|
||||
...(rest.fetchGroups
|
||||
? {
|
||||
allowedGroups,
|
||||
serviceAccountJson,
|
||||
domainToAdminEmail: domainToAdminEmail ?? {},
|
||||
fetchTransitiveGroupMembership,
|
||||
}
|
||||
: { domainToAdminEmail: {} }),
|
||||
};
|
||||
}, [form]);
|
||||
|
||||
@@ -129,7 +143,7 @@ function CreateOrEdit(props: CreateOrEditProps): JSX.Element {
|
||||
|
||||
return {
|
||||
...rest,
|
||||
groupMappings: groupMappings ?? {},
|
||||
groupMappings: rest.useRoleAttribute ? undefined : (groupMappings ?? {}),
|
||||
};
|
||||
}, [form]);
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ function ConfigureGoogleAuthAuthnProvider({
|
||||
Enter OAuth 2.0 credentials obtained from the Google API Console below.
|
||||
Read the{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/sso-authentication"
|
||||
href="https://signoz.io/docs/manage/administrator-guide/sso/overview/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -38,7 +38,7 @@ function ConfigureOIDCAuthnProvider({
|
||||
Configure OpenID Connect Single Sign-On with your Identity Provider. Read
|
||||
the{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/sso-authentication"
|
||||
href="https://signoz.io/docs/manage/administrator-guide/sso/overview/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -37,7 +37,7 @@ function ConfigureSAMLAuthnProvider({
|
||||
<p className="authn-provider__description">
|
||||
Configure SAML 2.0 Single Sign-On with your Identity Provider. Read the{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/sso-authentication"
|
||||
href="https://signoz.io/docs/manage/administrator-guide/sso/overview/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
import { fireEvent, render, screen, waitFor } from 'tests/test-utils';
|
||||
import { rest, server } from 'mocks-server/server';
|
||||
import { AuthtypesGettableAuthDomainDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
|
||||
import CreateEdit from '../CreateEdit/CreateEdit';
|
||||
import {
|
||||
AUTH_DOMAINS_UPDATE_ENDPOINT,
|
||||
mockDomainWithRoleMapping,
|
||||
mockGoogleAuthDomain,
|
||||
mockGoogleAuthWithWorkspaceGroups,
|
||||
mockOidcWithClaimMapping,
|
||||
mockSamlWithAttributeMapping,
|
||||
mockUpdateSuccessResponse,
|
||||
} from './mocks';
|
||||
|
||||
// @signozhq/ui/button internal effects block form.validateFields() in tests
|
||||
jest.mock('@signozhq/ui/button', () => ({
|
||||
...jest.requireActual('@signozhq/ui/button'),
|
||||
Button: ({
|
||||
children,
|
||||
onClick,
|
||||
loading,
|
||||
disabled,
|
||||
'aria-label': ariaLabel,
|
||||
prefix,
|
||||
suffix,
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
'aria-label'?: string;
|
||||
prefix?: React.ReactNode;
|
||||
suffix?: React.ReactNode;
|
||||
}) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={disabled || loading}
|
||||
aria-label={ariaLabel}
|
||||
>
|
||||
{prefix}
|
||||
{children}
|
||||
{suffix}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
type SavedPayload = {
|
||||
config: {
|
||||
googleAuthConfig?: Record<string, unknown>;
|
||||
samlConfig?: Record<string, unknown>;
|
||||
oidcConfig?: Record<string, unknown>;
|
||||
roleMapping?: Record<string, unknown>;
|
||||
};
|
||||
};
|
||||
|
||||
async function submitForm(
|
||||
record: AuthtypesGettableAuthDomainDTO,
|
||||
): Promise<SavedPayload> {
|
||||
const requests: SavedPayload[] = [];
|
||||
|
||||
server.use(
|
||||
rest.put(AUTH_DOMAINS_UPDATE_ENDPOINT, async (req, res, ctx) => {
|
||||
requests.push((await req.json()) as SavedPayload);
|
||||
return res(ctx.status(200), ctx.json(mockUpdateSuccessResponse));
|
||||
}),
|
||||
);
|
||||
|
||||
render(<CreateEdit isCreate={false} record={record} onClose={jest.fn()} />);
|
||||
fireEvent.click(screen.getByRole('button', { name: /save changes/i }));
|
||||
await waitFor(() => expect(requests).toHaveLength(1));
|
||||
|
||||
return requests[0];
|
||||
}
|
||||
|
||||
describe('CreateEdit — payload sanitization', () => {
|
||||
afterEach(() => server.resetHandlers());
|
||||
|
||||
describe('Google Auth', () => {
|
||||
it('sends core fields and omits workspace fields when fetchGroups is not set', async () => {
|
||||
const payload = await submitForm(mockGoogleAuthDomain);
|
||||
|
||||
const g = payload.config.googleAuthConfig;
|
||||
expect(g?.clientId).toBe('test-client-id');
|
||||
expect(g?.clientSecret).toBe('test-client-secret');
|
||||
expect(g?.allowedGroups).toBeUndefined();
|
||||
expect(g?.serviceAccountJson).toBeUndefined();
|
||||
expect(g?.fetchTransitiveGroupMembership).toBeUndefined();
|
||||
expect(g?.domainToAdminEmail).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('strips workspace fields when fetchGroups is false', async () => {
|
||||
const payload = await submitForm({
|
||||
...mockGoogleAuthWithWorkspaceGroups,
|
||||
config: {
|
||||
...mockGoogleAuthWithWorkspaceGroups.config,
|
||||
googleAuthConfig: {
|
||||
...mockGoogleAuthWithWorkspaceGroups.config?.googleAuthConfig,
|
||||
fetchGroups: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const g = payload.config.googleAuthConfig;
|
||||
expect(g?.fetchGroups).toBe(false);
|
||||
expect(g?.allowedGroups).toBeUndefined();
|
||||
expect(g?.serviceAccountJson).toBeUndefined();
|
||||
expect(g?.fetchTransitiveGroupMembership).toBeUndefined();
|
||||
expect(g?.domainToAdminEmail).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('includes all workspace fields when fetchGroups is true', async () => {
|
||||
const payload = await submitForm(mockGoogleAuthWithWorkspaceGroups);
|
||||
|
||||
const g = payload.config.googleAuthConfig;
|
||||
expect(g?.fetchGroups).toBe(true);
|
||||
expect(g?.serviceAccountJson).toBe('{"type": "service_account"}');
|
||||
expect(g?.fetchTransitiveGroupMembership).toBe(true);
|
||||
expect(g?.allowedGroups).toStrictEqual([
|
||||
'allowed-group-1',
|
||||
'allowed-group-2',
|
||||
]);
|
||||
expect(g?.domainToAdminEmail).toStrictEqual({
|
||||
'google-groups.com': 'admin@google-groups.com',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SAML', () => {
|
||||
it('sends core and attributeMapping fields', async () => {
|
||||
const payload = await submitForm(mockSamlWithAttributeMapping);
|
||||
|
||||
const s = payload.config.samlConfig;
|
||||
expect(s?.samlIdp).toBe('https://idp.saml-attrs.com/sso');
|
||||
expect(s?.samlEntity).toBe('urn:saml-attrs:idp');
|
||||
expect(s?.samlCert).toBe('MOCK_CERTIFICATE_ATTRS');
|
||||
expect(s?.insecureSkipAuthNRequestsSigned).toBe(true);
|
||||
|
||||
const attr = s?.attributeMapping as Record<string, unknown>;
|
||||
expect(attr?.name).toBe('user_display_name');
|
||||
expect(attr?.groups).toBe('member_of');
|
||||
expect(attr?.role).toBe('signoz_role');
|
||||
});
|
||||
});
|
||||
|
||||
describe('OIDC', () => {
|
||||
it('sends all fields including claimMapping', async () => {
|
||||
const payload = await submitForm(mockOidcWithClaimMapping);
|
||||
|
||||
const o = payload.config.oidcConfig;
|
||||
expect(o?.issuer).toBe('https://oidc.claims.com');
|
||||
expect(o?.issuerAlias).toBe('https://alias.claims.com');
|
||||
expect(o?.clientId).toBe('claims-client-id');
|
||||
expect(o?.clientSecret).toBe('claims-client-secret');
|
||||
expect(o?.insecureSkipEmailVerified).toBe(true);
|
||||
expect(o?.getUserInfo).toBe(true);
|
||||
|
||||
const claim = o?.claimMapping as Record<string, unknown>;
|
||||
expect(claim?.email).toBe('user_email');
|
||||
expect(claim?.name).toBe('display_name');
|
||||
expect(claim?.groups).toBe('user_groups');
|
||||
expect(claim?.role).toBe('user_role');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Role Mapping', () => {
|
||||
it('strips groupMappings when useRoleAttribute is true', async () => {
|
||||
const payload = await submitForm({
|
||||
...mockDomainWithRoleMapping,
|
||||
config: {
|
||||
...mockDomainWithRoleMapping.config,
|
||||
roleMapping: {
|
||||
...mockDomainWithRoleMapping.config?.roleMapping,
|
||||
useRoleAttribute: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(payload.config.roleMapping?.useRoleAttribute).toBe(true);
|
||||
expect(payload.config.roleMapping?.groupMappings).toBeUndefined();
|
||||
});
|
||||
|
||||
it('sends groupMappings when useRoleAttribute is false', async () => {
|
||||
const payload = await submitForm(mockDomainWithRoleMapping);
|
||||
|
||||
expect(payload.config.roleMapping?.useRoleAttribute).toBe(false);
|
||||
expect(payload.config.roleMapping?.groupMappings).toStrictEqual({
|
||||
'admin-group': 'ADMIN',
|
||||
'dev-team': 'EDITOR',
|
||||
viewers: 'VIEWER',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -216,7 +216,7 @@ export default function QueryFunctions({
|
||||
Add new function
|
||||
<Typography.Link
|
||||
style={{ textDecoration: 'underline' }}
|
||||
href="https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=query-builder#functions-for-extended-data-analysis"
|
||||
href="https://signoz.io/docs/querying/functions-extended-analysis/?utm_source=product&utm_medium=query-builder"
|
||||
target="_blank"
|
||||
>
|
||||
{' '}
|
||||
|
||||
@@ -549,7 +549,7 @@ function Success(props: ISuccessProps): JSX.Element {
|
||||
type="text"
|
||||
onClick={(): WindowProxy | null =>
|
||||
window.open(
|
||||
'https://signoz.io/docs/userguide/traces/#missing-spans',
|
||||
'https://signoz.io/docs/traces-management/troubleshooting/faqs/#q-why-are-some-spans-missing-from-a-trace',
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ function Version(): JSX.Element {
|
||||
{!isError && !isLatestVersion && (
|
||||
<div className="version-page-upgrade-container">
|
||||
<Button
|
||||
href="https://signoz.io/docs/operate/docker-standalone/#upgrade"
|
||||
href="https://signoz.io/docs/opentelemetry-collection-agents/docker/overview/"
|
||||
target="_blank"
|
||||
type="primary"
|
||||
className="periscope-btn primary"
|
||||
|
||||
@@ -171,17 +171,18 @@
|
||||
}
|
||||
|
||||
.legend-copy-button {
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// Always laid out (space reserved) but transparent, so revealing it on
|
||||
// hover fades the icon in without reflowing the row / shifting the label.
|
||||
// Shrink the shared icon Button (defaults to a 2rem square) to the
|
||||
// compact legend row via its size tokens.
|
||||
--button-height: auto;
|
||||
--button-width: auto;
|
||||
--button-padding: 2px;
|
||||
|
||||
opacity: 0;
|
||||
flex-shrink: 0;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
border: none;
|
||||
color: var(--l2-foreground);
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
transition:
|
||||
opacity 0.15s ease,
|
||||
color 0.15s ease;
|
||||
@@ -192,9 +193,8 @@
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: color-mix(in srgb, var(--l1-foreground) 5%, transparent);
|
||||
background: var(--l3-background);
|
||||
.legend-copy-button {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,41 @@
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { VirtuosoGrid } from 'react-virtuoso';
|
||||
import { Input, Tooltip as AntdTooltip } from 'antd';
|
||||
import { Input } from 'antd';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import cx from 'classnames';
|
||||
import { useCopyToClipboard } from 'hooks/useCopyToClipboard';
|
||||
import { LegendItem } from 'lib/uPlotV2/config/types';
|
||||
import useLegendsSync from 'lib/uPlotV2/hooks/useLegendsSync';
|
||||
import { Check, Copy } from '@signozhq/icons';
|
||||
|
||||
import { useLegendActions } from '../../hooks/useLegendActions';
|
||||
import { LegendPosition, LegendProps } from '../types';
|
||||
|
||||
import './Legend.styles.scss';
|
||||
|
||||
export const MAX_LEGEND_WIDTH = 240;
|
||||
|
||||
/**
|
||||
* Presentational legend. Renders the supplied `items` (markers + labels, an
|
||||
* optional copy button, and a search box for the RIGHT position) and delegates
|
||||
* all interaction to the container handlers. Source-agnostic — the uPlot
|
||||
* charts feed it via UPlotLegend; Pie feeds it directly.
|
||||
*/
|
||||
export default function Legend({
|
||||
position = LegendPosition.BOTTOM,
|
||||
config,
|
||||
items,
|
||||
position,
|
||||
averageLegendWidth = MAX_LEGEND_WIDTH,
|
||||
focusedSeriesIndex,
|
||||
onClick,
|
||||
onMouseMove,
|
||||
onMouseLeave,
|
||||
showCopy = true,
|
||||
}: LegendProps): JSX.Element {
|
||||
const { legendItemsMap, focusedSeriesIndex, setFocusedSeriesIndex } =
|
||||
useLegendsSync({ config });
|
||||
const { onLegendClick, onLegendMouseMove, onLegendMouseLeave } =
|
||||
useLegendActions({
|
||||
setFocusedSeriesIndex,
|
||||
focusedSeriesIndex,
|
||||
});
|
||||
const legendContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [legendSearchQuery, setLegendSearchQuery] = useState('');
|
||||
const { copyToClipboard, id: copiedId } = useCopyToClipboard();
|
||||
|
||||
const legendItems = useMemo(
|
||||
() => Object.values(legendItemsMap),
|
||||
[legendItemsMap],
|
||||
);
|
||||
// Search is intrinsic to the right-positioned legend.
|
||||
const searchEnabled = position === LegendPosition.RIGHT;
|
||||
|
||||
const isSingleRow = useMemo(() => {
|
||||
if (!legendContainerRef.current || position !== LegendPosition.BOTTOM) {
|
||||
@@ -41,21 +43,19 @@ export default function Legend({
|
||||
}
|
||||
const containerWidth = legendContainerRef.current.clientWidth;
|
||||
|
||||
const totalLegendWidth = legendItems.length * (averageLegendWidth + 16);
|
||||
const totalLegendWidth = items.length * (averageLegendWidth + 16);
|
||||
const totalRows = Math.ceil(totalLegendWidth / containerWidth);
|
||||
return totalRows <= 1;
|
||||
}, [averageLegendWidth, legendContainerRef, legendItems.length, position]);
|
||||
}, [averageLegendWidth, items.length, position]);
|
||||
|
||||
const visibleLegendItems = useMemo(() => {
|
||||
if (position !== LegendPosition.RIGHT || !legendSearchQuery.trim()) {
|
||||
return legendItems;
|
||||
if (!searchEnabled || !legendSearchQuery.trim()) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const query = legendSearchQuery.trim().toLowerCase();
|
||||
return legendItems.filter((item) =>
|
||||
item.label?.toLowerCase().includes(query),
|
||||
);
|
||||
}, [position, legendSearchQuery, legendItems]);
|
||||
return items.filter((item) => item.label?.toLowerCase().includes(query));
|
||||
}, [searchEnabled, legendSearchQuery, items]);
|
||||
|
||||
const handleCopyLegendItem = useCallback(
|
||||
(e: React.MouseEvent, seriesIndex: number, label: string): void => {
|
||||
@@ -68,6 +68,9 @@ export default function Legend({
|
||||
const renderLegendItem = useCallback(
|
||||
(item: LegendItem): JSX.Element => {
|
||||
const isCopied = copiedId === item.seriesIndex;
|
||||
// `color` is uPlot's stroke union (string | fn | gradient); only a string
|
||||
// is a usable CSS colour for the marker.
|
||||
const markerColor = typeof item.color === 'string' ? item.color : undefined;
|
||||
return (
|
||||
<div
|
||||
key={item.seriesIndex}
|
||||
@@ -77,54 +80,68 @@ export default function Legend({
|
||||
'legend-item-focused': focusedSeriesIndex === item.seriesIndex,
|
||||
})}
|
||||
>
|
||||
<AntdTooltip title={item.label}>
|
||||
<TooltipSimple title={item.label} arrow side="top" disableHoverableContent>
|
||||
<div className="legend-item-label-trigger">
|
||||
<div
|
||||
className="legend-marker"
|
||||
style={{ borderColor: String(item.color) }}
|
||||
style={{ borderColor: markerColor }}
|
||||
data-is-legend-marker={true}
|
||||
/>
|
||||
<span className="legend-label">{item.label}</span>
|
||||
</div>
|
||||
</AntdTooltip>
|
||||
<AntdTooltip title={isCopied ? 'Copied' : 'Copy'}>
|
||||
<button
|
||||
type="button"
|
||||
className="legend-copy-button"
|
||||
onClick={(e): void =>
|
||||
handleCopyLegendItem(e, item.seriesIndex, item.label ?? '')
|
||||
}
|
||||
aria-label={`Copy ${item.label}`}
|
||||
data-testid="legend-copy"
|
||||
</TooltipSimple>
|
||||
{showCopy && (
|
||||
<TooltipSimple
|
||||
title={isCopied ? 'Copied' : 'Copy'}
|
||||
arrow
|
||||
side="top"
|
||||
disableHoverableContent
|
||||
>
|
||||
{isCopied ? <Check size={12} /> : <Copy size={12} />}
|
||||
</button>
|
||||
</AntdTooltip>
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
className="legend-copy-button"
|
||||
onClick={(e): void =>
|
||||
handleCopyLegendItem(e, item.seriesIndex, item.label ?? '')
|
||||
}
|
||||
aria-label={`Copy ${item.label}`}
|
||||
// data-testid (not testId): TooltipSimple's trigger injects
|
||||
// data-testid:undefined via Radix Slot, and Button spreads
|
||||
// incoming props after its own testId — so set it as a prop
|
||||
// that wins the Slot merge and survives the spread.
|
||||
data-testid="legend-copy"
|
||||
>
|
||||
{isCopied ? <Check size={12} /> : <Copy size={12} />}
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[copiedId, focusedSeriesIndex, handleCopyLegendItem, position],
|
||||
[copiedId, focusedSeriesIndex, handleCopyLegendItem, position, showCopy],
|
||||
);
|
||||
|
||||
const isEmptyState = useMemo(() => {
|
||||
if (position !== LegendPosition.RIGHT || !legendSearchQuery.trim()) {
|
||||
if (!searchEnabled || !legendSearchQuery.trim()) {
|
||||
return false;
|
||||
}
|
||||
return visibleLegendItems.length === 0;
|
||||
}, [position, legendSearchQuery, visibleLegendItems]);
|
||||
}, [searchEnabled, legendSearchQuery, visibleLegendItems]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={legendContainerRef}
|
||||
className="legend-container"
|
||||
onClick={onLegendClick}
|
||||
onMouseMove={onLegendMouseMove}
|
||||
onMouseLeave={onLegendMouseLeave}
|
||||
onClick={onClick}
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseLeave={onMouseLeave}
|
||||
style={{
|
||||
['--legend-average-width' as string]: `${averageLegendWidth + 16}px`, // 16px is the marker width
|
||||
}}
|
||||
>
|
||||
{position === LegendPosition.RIGHT && (
|
||||
{searchEnabled && (
|
||||
<div className="legend-search-container">
|
||||
<Input
|
||||
allowClear
|
||||
|
||||
41
frontend/src/lib/uPlotV2/components/Legend/UPlotLegend.tsx
Normal file
41
frontend/src/lib/uPlotV2/components/Legend/UPlotLegend.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useMemo } from 'react';
|
||||
import useLegendsSync from 'lib/uPlotV2/hooks/useLegendsSync';
|
||||
|
||||
import { useLegendActions } from '../../hooks/useLegendActions';
|
||||
import { LegendPosition, UPlotLegendProps } from '../types';
|
||||
|
||||
import Legend from './Legend';
|
||||
|
||||
/**
|
||||
* uPlot legend controller. Derives the legend items + focus/visibility state
|
||||
* from the chart config (useLegendsSync) and the toggle/focus interactions from
|
||||
* the plot context (useLegendActions), then renders the presentational Legend.
|
||||
* Must be rendered inside a PlotContextProvider.
|
||||
*/
|
||||
export default function UPlotLegend({
|
||||
position = LegendPosition.BOTTOM,
|
||||
config,
|
||||
averageLegendWidth,
|
||||
}: UPlotLegendProps): JSX.Element {
|
||||
const { legendItemsMap, focusedSeriesIndex, setFocusedSeriesIndex } =
|
||||
useLegendsSync({ config });
|
||||
const { onLegendClick, onLegendMouseMove, onLegendMouseLeave } =
|
||||
useLegendActions({
|
||||
setFocusedSeriesIndex,
|
||||
focusedSeriesIndex,
|
||||
});
|
||||
|
||||
const items = useMemo(() => Object.values(legendItemsMap), [legendItemsMap]);
|
||||
|
||||
return (
|
||||
<Legend
|
||||
items={items}
|
||||
position={position}
|
||||
averageLegendWidth={averageLegendWidth}
|
||||
focusedSeriesIndex={focusedSeriesIndex}
|
||||
onClick={onLegendClick}
|
||||
onMouseMove={onLegendMouseMove}
|
||||
onMouseLeave={onLegendMouseLeave}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -7,11 +7,12 @@ import {
|
||||
within,
|
||||
} from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { TooltipProvider } from '@signozhq/ui/tooltip';
|
||||
import { LegendItem } from 'lib/uPlotV2/config/types';
|
||||
import useLegendsSync from 'lib/uPlotV2/hooks/useLegendsSync';
|
||||
|
||||
import { useLegendActions } from '../../hooks/useLegendActions';
|
||||
import Legend from '../Legend/Legend';
|
||||
import UPlotLegend from '../Legend/UPlotLegend';
|
||||
import { LegendPosition } from '../types';
|
||||
|
||||
const mockWriteText = jest.fn().mockResolvedValue(undefined);
|
||||
@@ -47,7 +48,7 @@ const mockUseLegendActions = useLegendActions as jest.MockedFunction<
|
||||
typeof useLegendActions
|
||||
>;
|
||||
|
||||
describe('Legend', () => {
|
||||
describe('UPlotLegend', () => {
|
||||
beforeAll(() => {
|
||||
// JSDOM does not define navigator.clipboard; add it so we can spy on writeText
|
||||
Object.defineProperty(navigator, 'clipboard', {
|
||||
@@ -115,11 +116,13 @@ describe('Legend', () => {
|
||||
|
||||
const renderLegend = (position?: LegendPosition): RenderResult =>
|
||||
render(
|
||||
<Legend
|
||||
position={position}
|
||||
// config is not used directly in the component, it's consumed by the mocked hook
|
||||
config={{} as any}
|
||||
/>,
|
||||
<TooltipProvider>
|
||||
<UPlotLegend
|
||||
position={position}
|
||||
// config is consumed by the mocked useLegendsSync hook, not directly
|
||||
config={{} as any}
|
||||
/>
|
||||
</TooltipProvider>,
|
||||
);
|
||||
|
||||
describe('layout and position', () => {
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { MouseEventHandler, ReactNode } from 'react';
|
||||
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
|
||||
import { PrecisionOption } from 'components/Graph/types';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import { UPlotConfigBuilder } from '../config/UPlotConfigBuilder';
|
||||
import { LegendItem } from '../config/types';
|
||||
import { SyncTooltipFilterMode } from '../plugins/TooltipPlugin/types';
|
||||
|
||||
/**
|
||||
@@ -109,7 +110,33 @@ export enum LegendPosition {
|
||||
export interface LegendConfig {
|
||||
position: LegendPosition;
|
||||
}
|
||||
/**
|
||||
* Presentational legend props. Source-agnostic: it renders whatever `items`
|
||||
* it's given and delegates interaction to the container handlers, so it serves
|
||||
* both uPlot charts (via UPlotLegend) and non-uPlot charts (Pie). The search
|
||||
* box is intrinsic to the RIGHT position (derived from `position`, not a flag).
|
||||
*/
|
||||
export interface LegendProps {
|
||||
items: LegendItem[];
|
||||
/** Legend placement; always supplied by the container. */
|
||||
position: LegendPosition;
|
||||
averageLegendWidth?: number;
|
||||
/** Series index to highlight (hovered/focused). */
|
||||
focusedSeriesIndex: number | null;
|
||||
/**
|
||||
* Container-delegated handlers. Items carry `data-legend-item-id`, so the
|
||||
* handler reads the target's id rather than binding per item.
|
||||
*/
|
||||
onClick: MouseEventHandler<HTMLDivElement>;
|
||||
onMouseMove: MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave: () => void;
|
||||
/** Show the per-item copy button. Default true. */
|
||||
showCopy?: boolean;
|
||||
}
|
||||
|
||||
/** Props for the uPlot legend controller, which derives items + interaction
|
||||
* from the chart config and renders the presentational Legend. */
|
||||
export interface UPlotLegendProps {
|
||||
position?: LegendPosition;
|
||||
config: UPlotConfigBuilder;
|
||||
averageLegendWidth?: number;
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface TooltipViewState {
|
||||
isHovering: boolean;
|
||||
isPinned: boolean;
|
||||
dismiss: () => void;
|
||||
clickData: TooltipClickData | null;
|
||||
clickData: ChartClickData | null;
|
||||
contents?: ReactNode;
|
||||
}
|
||||
|
||||
@@ -59,17 +59,17 @@ export interface TooltipPluginProps {
|
||||
/** Key that pins the tooltip while hovering. Defaults to DEFAULT_PIN_TOOLTIP_KEY ('l'). */
|
||||
pinKey?: string;
|
||||
/** Called when the user clicks the uPlot overlay. Receives resolved click data. */
|
||||
onClick?: (clickData: TooltipClickData) => void;
|
||||
onClick?: (clickData: ChartClickData) => void;
|
||||
syncMode?: DashboardCursorSync;
|
||||
syncKey?: string;
|
||||
syncMetadata?: TooltipSyncMetadata;
|
||||
render: (args: TooltipRenderArgs) => ReactNode;
|
||||
pinnedTooltipElement?: (clickData: TooltipClickData) => ReactNode;
|
||||
pinnedTooltipElement?: (clickData: ChartClickData) => ReactNode;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
}
|
||||
|
||||
export interface TooltipClickData {
|
||||
export interface ChartClickData {
|
||||
xValue: number;
|
||||
yValue: number;
|
||||
focusedSeries: {
|
||||
@@ -101,7 +101,7 @@ export interface TooltipControllerState {
|
||||
hoverActive: boolean;
|
||||
isAnySeriesActive: boolean;
|
||||
pinned: boolean;
|
||||
clickData: TooltipClickData | null;
|
||||
clickData: ChartClickData | null;
|
||||
style: TooltipViewState['style'];
|
||||
horizontalOffset: number;
|
||||
verticalOffset: number;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getFocusedSeriesAtPosition } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
|
||||
import {
|
||||
TOOLTIP_OFFSET,
|
||||
TooltipClickData,
|
||||
ChartClickData,
|
||||
TooltipLayoutInfo,
|
||||
TooltipViewState,
|
||||
} from './types';
|
||||
@@ -167,14 +167,11 @@ export function createLayoutObserver(
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a TooltipClickData snapshot from a MouseEvent (real or synthetic)
|
||||
* Resolves a ChartClickData snapshot from a MouseEvent (real or synthetic)
|
||||
* and the current uPlot instance. Shared by the overlay click handler and the
|
||||
* keyboard-pin handler (which synthesises an event from the cursor position).
|
||||
*/
|
||||
export function buildClickData(
|
||||
event: MouseEvent,
|
||||
plot: uPlot,
|
||||
): TooltipClickData {
|
||||
export function buildClickData(event: MouseEvent, plot: uPlot): ChartClickData {
|
||||
const xValue = plot.posToVal(event.offsetX, 'x');
|
||||
const yValue = plot.posToVal(event.offsetY, 'y');
|
||||
const focusedSeries = getFocusedSeriesAtPosition(event, plot);
|
||||
|
||||
@@ -35,14 +35,14 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
icon: (
|
||||
<img src={`${javaPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/java/',
|
||||
url: 'https://signoz.io/docs/instrumentation/java/opentelemetry-java/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Python Application',
|
||||
icon: (
|
||||
<img src={`${pythonPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/python/',
|
||||
url: 'https://signoz.io/docs/instrumentation/opentelemetry-python/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your JS Application',
|
||||
@@ -52,12 +52,12 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
alt=""
|
||||
/>
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/javascript/',
|
||||
url: 'https://signoz.io/docs/instrumentation/javascript/overview/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Go Application',
|
||||
icon: <img src={`${goPngUrl}?currentVersion=${currentVersion}`} alt="" />,
|
||||
url: 'https://signoz.io/docs/instrumentation/golang/',
|
||||
url: 'https://signoz.io/docs/instrumentation/opentelemetry-golang/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your .NET Application',
|
||||
@@ -67,33 +67,33 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
alt=""
|
||||
/>
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/dotnet/',
|
||||
url: 'https://signoz.io/docs/instrumentation/opentelemetry-dotnet/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your PHP Application',
|
||||
icon: <img src={`${phpPngUrl}?currentVersion=${currentVersion}`} alt="" />,
|
||||
url: 'https://signoz.io/docs/instrumentation/php/',
|
||||
url: 'https://signoz.io/docs/instrumentation/opentelemetry-php/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Rails Application',
|
||||
icon: (
|
||||
<img src={`${railsPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/ruby-on-rails/',
|
||||
url: 'https://signoz.io/docs/instrumentation/opentelemetry-ruby/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Rust Application',
|
||||
icon: (
|
||||
<img src={`${rustPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/rust/',
|
||||
url: 'https://signoz.io/docs/instrumentation/opentelemetry-rust/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Elixir Application',
|
||||
icon: (
|
||||
<img src={`${elixirPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/elixir/',
|
||||
url: 'https://signoz.io/docs/instrumentation/opentelemetry-elixir/',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -103,12 +103,12 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
{
|
||||
title: 'Send metrics to SigNoz',
|
||||
icon: <ChartBar size="lg" />,
|
||||
url: 'https://signoz.io/docs/userguide/send-metrics/',
|
||||
url: 'https://signoz.io/docs/metrics-management/send-metrics/',
|
||||
},
|
||||
{
|
||||
title: 'Create and Manage Dashboards',
|
||||
icon: <LayoutDashboard size="lg" />,
|
||||
url: 'https://signoz.io/docs/userguide/manage-dashboards-and-panels/',
|
||||
url: 'https://signoz.io/docs/userguide/manage-dashboards/',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -118,7 +118,7 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
{
|
||||
title: 'Send your logs to SigNoz',
|
||||
icon: <AlignLeft size="lg" />,
|
||||
url: 'https://signoz.io/docs/userguide/logs/',
|
||||
url: 'https://signoz.io/docs/userguide/logs_query_builder/',
|
||||
},
|
||||
{
|
||||
title: 'Existing log collectors to SigNoz',
|
||||
@@ -133,12 +133,12 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
{
|
||||
title: 'Create alert rules on metrics',
|
||||
icon: <BellRing size="lg" />,
|
||||
url: 'https://signoz.io/docs/userguide/alerts-management/',
|
||||
url: 'https://signoz.io/docs/alerts/',
|
||||
},
|
||||
{
|
||||
title: 'Configure alert notification channels',
|
||||
icon: <Volume2 size="lg" />,
|
||||
url: 'https://signoz.io/docs/userguide/alerts-management/#setting-up-a-notification-channel',
|
||||
url: 'https://signoz.io/docs/setup-alerts-notification/',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -306,7 +306,7 @@ function SaveView(): JSX.Element {
|
||||
Manage your saved views for {ROUTES_VS_SOURCEPAGE[pathname]}.{' '}
|
||||
<Typography.Link
|
||||
className="learn-more"
|
||||
href="https://signoz.io/docs/product-features/saved-view/?utm_source=product&utm_medium=views-tab"
|
||||
href="https://signoz.io/docs/metrics-management/metrics-explorer/?utm_source=product&utm_medium=views-tab#saved-views-in-metrics-explorer"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function ServiceTopLevelOperations(): JSX.Element {
|
||||
SigNoz calculates the RED metrics for a service using the entry-point spans.
|
||||
For more details, you can check out our
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/metrics/#open-the-services-section"
|
||||
href="https://signoz.io/docs/userguide/metrics/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -852,7 +852,7 @@ function Success(props: ISuccessProps): JSX.Element {
|
||||
suffix={<ArrowUpRight size={14} />}
|
||||
onClick={(): WindowProxy | null =>
|
||||
window.open(
|
||||
'https://signoz.io/docs/userguide/traces/#missing-spans',
|
||||
'https://signoz.io/docs/traces-management/troubleshooting/faqs/#q-why-are-some-spans-missing-from-a-trace',
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
const DOCLINKS = {
|
||||
TRACES_EXPLORER_EMPTY_STATE:
|
||||
'https://signoz.io/docs/instrumentation/overview/?utm_source=product&utm_medium=traces-explorer-empty-state',
|
||||
USER_GUIDE: 'https://signoz.io/docs/userguide/',
|
||||
USER_GUIDE: 'https://signoz.io/docs/introduction/',
|
||||
TRACES_DETAILS_LINK:
|
||||
'https://signoz.io/docs/product-features/trace-explorer/?utm_source=product&utm_medium=traces-explorer-trace-tab#traces-view',
|
||||
'https://signoz.io/docs/userguide/traces/?utm_source=product&utm_medium=traces-explorer-trace-tab#traces-view',
|
||||
METRICS_EXPLORER_EMPTY_STATE:
|
||||
'https://signoz.io/docs/userguide/send-metrics-cloud/',
|
||||
'https://signoz.io/docs/metrics-management/send-metrics/',
|
||||
EXTERNAL_API_MONITORING:
|
||||
'https://signoz.io/docs/external-api-monitoring/overview/',
|
||||
QUERY_CLICKHOUSE_TRACES:
|
||||
'https://signoz.io/docs/userguide/writing-clickhouse-traces-query/#timestamp-bucketing-for-distributed_signoz_index_v3',
|
||||
'https://signoz.io/docs/userguide/writing-clickhouse-traces-query/#timestamp-bucketing',
|
||||
QUERY_CLICKHOUSE_LOGS:
|
||||
'https://signoz.io/docs/userguide/logs_clickhouse_queries/',
|
||||
QUERY_CLICKHOUSE_METRICS:
|
||||
|
||||
@@ -67,5 +67,24 @@ func (provider *provider) addTraceDetailRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v3/traces/{traceID}/flamegraph", handler.New(
|
||||
provider.authzMiddleware.ViewAccess(provider.traceDetailHandler.GetFlamegraph),
|
||||
handler.OpenAPIDef{
|
||||
ID: "GetFlamegraph",
|
||||
Tags: []string{"tracedetail"},
|
||||
Summary: "Get flamegraph view for a trace",
|
||||
Description: "Returns the flamegraph view of spans for a given trace ID.",
|
||||
Request: new(spantypes.PostableFlamegraph),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(spantypes.GettableFlamegraphTrace),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
},
|
||||
)).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/authn"
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/http/client"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
@@ -29,12 +31,13 @@ var scopes []string = []string{"email", "profile"}
|
||||
var _ authn.CallbackAuthN = (*AuthN)(nil)
|
||||
|
||||
type AuthN struct {
|
||||
store authtypes.AuthNStore
|
||||
settings factory.ScopedProviderSettings
|
||||
httpClient *client.Client
|
||||
store authtypes.AuthNStore
|
||||
settings factory.ScopedProviderSettings
|
||||
httpClient *client.Client
|
||||
globalConfig global.Config
|
||||
}
|
||||
|
||||
func New(ctx context.Context, store authtypes.AuthNStore, providerSettings factory.ProviderSettings) (*AuthN, error) {
|
||||
func New(ctx context.Context, store authtypes.AuthNStore, providerSettings factory.ProviderSettings, globalConfig global.Config) (*AuthN, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/authn/callbackauthn/googlecallbackauthn")
|
||||
|
||||
httpClient, err := client.New(settings.Logger(), providerSettings.TracerProvider, providerSettings.MeterProvider)
|
||||
@@ -43,9 +46,10 @@ func New(ctx context.Context, store authtypes.AuthNStore, providerSettings facto
|
||||
}
|
||||
|
||||
return &AuthN{
|
||||
store: store,
|
||||
settings: settings,
|
||||
httpClient: httpClient,
|
||||
store: store,
|
||||
settings: settings,
|
||||
httpClient: httpClient,
|
||||
globalConfig: globalConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -178,7 +182,7 @@ func (a *AuthN) oauth2Config(siteURL *url.URL, authDomain *authtypes.AuthDomain,
|
||||
RedirectURL: (&url.URL{
|
||||
Scheme: siteURL.Scheme,
|
||||
Host: siteURL.Host,
|
||||
Path: redirectPath,
|
||||
Path: path.Join(a.globalConfig.ExternalPath(), redirectPath),
|
||||
}).String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/http/binding"
|
||||
"github.com/SigNoz/signoz/pkg/http/render"
|
||||
"github.com/SigNoz/signoz/pkg/modules/session"
|
||||
@@ -15,11 +17,12 @@ import (
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
module session.Module
|
||||
module session.Module
|
||||
globalConfig global.Config
|
||||
}
|
||||
|
||||
func NewHandler(module session.Module) session.Handler {
|
||||
return &handler{module: module}
|
||||
func NewHandler(module session.Module, globalConfig global.Config) session.Handler {
|
||||
return &handler{module: module, globalConfig: globalConfig}
|
||||
}
|
||||
|
||||
func (handler *handler) GetSessionContext(rw http.ResponseWriter, req *http.Request) {
|
||||
@@ -158,13 +161,13 @@ func (handler *handler) DeleteSession(rw http.ResponseWriter, req *http.Request)
|
||||
render.Success(rw, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (*handler) getRedirectURLFromErr(err error) string {
|
||||
func (handler *handler) getRedirectURLFromErr(err error) string {
|
||||
values := errors.AsURLValues(err)
|
||||
values.Add("callbackauthnerr", "true")
|
||||
|
||||
return (&url.URL{
|
||||
// When UI is being served on a prefix, we need to redirect to the login page on the prefix.
|
||||
Path: "/login",
|
||||
Path: path.Join(handler.globalConfig.ExternalPath(), "/login"),
|
||||
RawQuery: values.Encode(),
|
||||
}).String()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,16 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Waterfall WaterfallConfig `mapstructure:"waterfall"`
|
||||
Waterfall WaterfallConfig `mapstructure:"waterfall"`
|
||||
Flamegraph FlamegraphConfig `mapstructure:"flamegraph"`
|
||||
}
|
||||
|
||||
type FlamegraphConfig struct {
|
||||
MaxSelectedLevels int `mapstructure:"max_selected_levels"`
|
||||
MaxSpansPerLevel int `mapstructure:"max_spans_per_level"`
|
||||
SamplingTopLatencySpansCount int `mapstructure:"sampling_top_latency_count"`
|
||||
SamplingBucketCount int `mapstructure:"sampling_bucket_count"`
|
||||
SelectAllSpansLimit uint `mapstructure:"select_all_spans_limit"`
|
||||
}
|
||||
|
||||
type WaterfallConfig struct {
|
||||
@@ -29,6 +38,13 @@ func newConfig() factory.Config {
|
||||
MaxDepthToAutoExpand: 5,
|
||||
MaxLimitToSelectAllSpans: 10_000,
|
||||
},
|
||||
Flamegraph: FlamegraphConfig{
|
||||
MaxSelectedLevels: 50,
|
||||
MaxSpansPerLevel: 100,
|
||||
SamplingTopLatencySpansCount: 5,
|
||||
SamplingBucketCount: 50,
|
||||
SelectAllSpansLimit: 100_000,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,5 +58,20 @@ func (c Config) Validate() error {
|
||||
if c.Waterfall.MaxLimitToSelectAllSpans == 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "traces.waterfall.max_limit_to_select_all_spans must be positive")
|
||||
}
|
||||
if c.Flamegraph.MaxSelectedLevels <= 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "tracedetail.flamegraph.level_limit must be positive, got %d", c.Flamegraph.MaxSelectedLevels)
|
||||
}
|
||||
if c.Flamegraph.MaxSpansPerLevel <= 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "tracedetail.flamegraph.spans_per_level must be positive, got %d", c.Flamegraph.MaxSpansPerLevel)
|
||||
}
|
||||
if c.Flamegraph.SamplingTopLatencySpansCount < 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "tracedetail.flamegraph.top_latency_count cannot be negative, got %d", c.Flamegraph.SamplingTopLatencySpansCount)
|
||||
}
|
||||
if c.Flamegraph.SamplingBucketCount <= 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "tracedetail.flamegraph.bucket_count must be positive, got %d", c.Flamegraph.SamplingBucketCount)
|
||||
}
|
||||
if c.Flamegraph.SelectAllSpansLimit == 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "tracedetail.flamegraph.max_limit_to_select_all_spans must be positive")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -80,3 +80,19 @@ func (h *handler) GetTraceAggregations(rw http.ResponseWriter, r *http.Request)
|
||||
|
||||
render.Success(rw, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (h *handler) GetFlamegraph(rw http.ResponseWriter, r *http.Request) {
|
||||
req := new(spantypes.PostableFlamegraph)
|
||||
if err := binding.JSON.BindBody(r.Body, req); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.module.GetFlamegraph(r.Context(), mux.Vars(r)["traceID"], req.SelectedSpanID, req.SelectFields)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, result)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/modules/tracedetail"
|
||||
"github.com/SigNoz/signoz/pkg/types/spantypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
)
|
||||
|
||||
@@ -164,6 +165,17 @@ func (m *module) GetTraceAggregations(ctx context.Context, traceID string, req *
|
||||
return &spantypes.GettableTraceAggregations{Aggregations: results}, nil
|
||||
}
|
||||
|
||||
func (m *module) GetFlamegraph(ctx context.Context, traceID string, selectedSpanID string, selectFields []telemetrytypes.TelemetryFieldKey) (*spantypes.GettableFlamegraphTrace, error) {
|
||||
summary, err := m.store.GetTraceSummary(ctx, traceID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if summary.NumSpans <= uint64(m.config.Flamegraph.SelectAllSpansLimit) {
|
||||
return m.getFullFlamegraph(ctx, traceID, summary, selectFields)
|
||||
}
|
||||
return m.getWindowedFlamegraph(ctx, traceID, selectedSpanID, summary, selectFields)
|
||||
}
|
||||
|
||||
// getWindowedWaterfall builds the waterfall tree with minimal data and then returns only a window of full spans.
|
||||
func (m *module) getWindowedWaterfall(ctx context.Context, traceID, selectedSpanID string, uncollapsedSpans []string, start, end time.Time) (*spantypes.GettableWaterfallTrace, error) {
|
||||
// Step 1: minimal fetch → build full tree → select visible window
|
||||
@@ -204,3 +216,47 @@ func (m *module) getWindowedWaterfall(ctx context.Context, traceID, selectedSpan
|
||||
waterfallTrace, selectedSpans, uncollapsedSpans, false, nil,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (m *module) getFullFlamegraph(ctx context.Context, traceID string, summary *spantypes.TraceSummary, selectFields []telemetrytypes.TelemetryFieldKey) (*spantypes.GettableFlamegraphTrace, error) {
|
||||
fullSpans, err := m.store.GetFlamegraphSpans(ctx, traceID, summary.Start, summary.End, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(fullSpans) == 0 {
|
||||
return nil, spantypes.ErrTraceNotFound
|
||||
}
|
||||
flamegraphTrace := spantypes.NewFlamegraphTraceFromStorable(fullSpans, selectFields)
|
||||
return spantypes.NewGettableFlamegraphTrace(flamegraphTrace.GetAllLevels(), summary.Start.UnixMilli(), summary.End.UnixMilli(), false), nil
|
||||
}
|
||||
|
||||
// getWindowedFlamegraph returns a window of a max levels and max sampled spans per level around the selected span.
|
||||
func (m *module) getWindowedFlamegraph(ctx context.Context, traceID, selectedSpanID string, summary *spantypes.TraceSummary, selectFields []telemetrytypes.TelemetryFieldKey) (*spantypes.GettableFlamegraphTrace, error) {
|
||||
minimalSpans, err := m.store.GetMinimalSpans(ctx, traceID, summary.Start, summary.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(minimalSpans) == 0 {
|
||||
return nil, spantypes.ErrTraceNotFound
|
||||
}
|
||||
|
||||
flamegraphTrace := spantypes.NewFlamegraphTraceFromMinimal(minimalSpans)
|
||||
minimalSpans = nil //nolint:ineffassign,wastedassign // release backing array before further db calls
|
||||
|
||||
cfg := m.config.Flamegraph
|
||||
selectedSpans := flamegraphTrace.GetSelectedLevels(selectedSpanID, cfg.MaxSelectedLevels, cfg.MaxSpansPerLevel, cfg.SamplingTopLatencySpansCount, cfg.SamplingBucketCount)
|
||||
if len(selectedSpans) == 0 {
|
||||
return nil, spantypes.ErrTraceNotFound
|
||||
}
|
||||
|
||||
fullSpans, err := m.store.GetFlamegraphSpans(ctx, traceID, summary.Start, summary.End, spantypes.FlamegraphWindowSpanIDs(selectedSpans))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return spantypes.NewGettableFlamegraphTrace(
|
||||
flamegraphTrace.EnrichSelectedSpans(selectedSpans, fullSpans, selectFields),
|
||||
summary.Start.UnixMilli(),
|
||||
summary.End.UnixMilli(),
|
||||
true,
|
||||
), nil
|
||||
}
|
||||
|
||||
@@ -154,6 +154,47 @@ func (s *traceStore) GetTraceSpansByIDs(ctx context.Context, traceID string, sta
|
||||
return spans, nil
|
||||
}
|
||||
|
||||
func (s *traceStore) GetFlamegraphSpans(ctx context.Context, traceID string, start, end time.Time, spanIDs []string) ([]spantypes.StorableSpan, error) {
|
||||
sb := sqlbuilder.NewSelectBuilder()
|
||||
sb.Select(
|
||||
"span_id",
|
||||
"any(parent_span_id) AS parent_span_id",
|
||||
"any(timestamp) AS timestamp",
|
||||
"any(duration_nano) AS duration_nano",
|
||||
"any(has_error) AS has_error",
|
||||
"any(name) AS name",
|
||||
"any(events) AS events",
|
||||
"any(attributes_string) AS attributes_string",
|
||||
"any(attributes_number) AS attributes_number",
|
||||
"any(attributes_bool) AS attributes_bool",
|
||||
"any(resources_string) AS resources_string",
|
||||
)
|
||||
sb.From(fmt.Sprintf("%s.%s", spantypes.TraceDB, spantypes.TraceTable))
|
||||
conditions := []string{
|
||||
sb.E("trace_id", traceID),
|
||||
sb.GE("ts_bucket_start", start.Unix()-1800),
|
||||
sb.LE("ts_bucket_start", end.Unix()),
|
||||
}
|
||||
if len(spanIDs) > 0 {
|
||||
ids := make([]any, len(spanIDs))
|
||||
for i, id := range spanIDs {
|
||||
ids[i] = id
|
||||
}
|
||||
conditions = append(conditions, sb.In("span_id", ids...))
|
||||
}
|
||||
sb.Where(conditions...)
|
||||
sb.GroupBy("span_id")
|
||||
sb.OrderByAsc("timestamp")
|
||||
sb.OrderByAsc("name")
|
||||
query, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
|
||||
|
||||
var spans []spantypes.StorableSpan
|
||||
if err := s.telemetryStore.ClickhouseDB().Select(ctx, &spans, query, args...); err != nil {
|
||||
return nil, errors.WrapInternalf(err, errors.CodeInternal, "error querying flamegraph spans")
|
||||
}
|
||||
return spans, nil
|
||||
}
|
||||
|
||||
func (s *traceStore) GetSpanCountByField(ctx context.Context, traceID string, summary *spantypes.TraceSummary, fieldKey telemetrytypes.TelemetryFieldKey) (map[string]uint64, error) {
|
||||
fieldExpr, err := buildFieldExpr(fieldKey)
|
||||
if err != nil {
|
||||
|
||||
@@ -91,6 +91,30 @@ func TestGetSpanCountByField(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFlamegraphSpans(t *testing.T) {
|
||||
baseSQL := "SELECT span_id, any(parent_span_id) AS parent_span_id, any(timestamp) AS timestamp, any(duration_nano) AS duration_nano, any(has_error) AS has_error, any(name) AS name, any(events) AS events, any(attributes_string) AS attributes_string, any(attributes_number) AS attributes_number, any(attributes_bool) AS attributes_bool, any(resources_string) AS resources_string FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? GROUP BY span_id ORDER BY timestamp ASC, name ASC"
|
||||
withSpanIDsSQL := "SELECT span_id, any(parent_span_id) AS parent_span_id, any(timestamp) AS timestamp, any(duration_nano) AS duration_nano, any(has_error) AS has_error, any(name) AS name, any(events) AS events, any(attributes_string) AS attributes_string, any(attributes_number) AS attributes_number, any(attributes_bool) AS attributes_bool, any(resources_string) AS resources_string FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND span_id IN (?, ?) GROUP BY span_id ORDER BY timestamp ASC, name ASC"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
spanIDs []string
|
||||
sql string
|
||||
}{
|
||||
{name: "NoSpanIDs_GeneratesBaseSQL", spanIDs: nil, sql: baseSQL},
|
||||
{name: "WithSpanIDs_GeneratesInClauseSQL", spanIDs: []string{"span-1", "span-2"}, sql: withSpanIDsSQL},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := newTestStore(sqlmock.QueryMatcherRegexp)
|
||||
s.Mock().ExpectSelect(regexp.QuoteMeta(tc.sql)).
|
||||
WillReturnRows(cmock.NewRows(nil, nil))
|
||||
_, _ = s.Store().GetFlamegraphSpans(context.Background(), testTraceID, testStart, testEnd, tc.spanIDs)
|
||||
assert.NoError(t, s.Mock().ExpectationsWereMet())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSpanDurationByField(t *testing.T) {
|
||||
|
||||
expectedSQL := "WITH all_spans AS (SELECT DISTINCT ON (span_id) resource.`service.name`::String AS field_value, toUnixTimestamp64Nano(timestamp) AS start_ns, start_ns + duration_nano AS end_ns FROM signoz_traces.distributed_signoz_index_v3 WHERE trace_id = ? AND ts_bucket_start >= ? AND ts_bucket_start <= ? AND notEmpty(field_value) ORDER BY timestamp ASC, name ASC), effective_start AS (SELECT field_value, end_ns, greatest(start_ns, ifNull(max(end_ns) OVER (PARTITION BY field_value ORDER BY start_ns ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), toUInt64(0))) AS effective_start_ns FROM all_spans) SELECT field_value, sum(toUInt64(greatest(end_ns - effective_start_ns, 0))) AS total_ns FROM effective_start GROUP BY field_value"
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/spantypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
// Handler exposes HTTP handlers for trace detail APIs.
|
||||
@@ -12,6 +13,7 @@ type Handler interface {
|
||||
GetWaterfall(http.ResponseWriter, *http.Request)
|
||||
GetWaterfallV4(http.ResponseWriter, *http.Request)
|
||||
GetTraceAggregations(http.ResponseWriter, *http.Request)
|
||||
GetFlamegraph(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
// Module defines the business logic for trace detail operations.
|
||||
@@ -19,4 +21,5 @@ type Module interface {
|
||||
GetWaterfall(ctx context.Context, traceID string, req *spantypes.PostableWaterfall) (*spantypes.GettableWaterfallTrace, error)
|
||||
GetWaterfallV4(ctx context.Context, traceID string, selectedSpanID string, uncollapsedSpans []string, selectAllLimit uint) (*spantypes.GettableWaterfallTrace, error)
|
||||
GetTraceAggregations(ctx context.Context, traceID string, req *spantypes.PostableTraceAggregations) (*spantypes.GettableTraceAggregations, error)
|
||||
GetFlamegraph(ctx context.Context, traceID string, selectedSpanID string, selectFields []telemetrytypes.TelemetryFieldKey) (*spantypes.GettableFlamegraphTrace, error)
|
||||
}
|
||||
|
||||
@@ -7,14 +7,15 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/authn/callbackauthn/googlecallbackauthn"
|
||||
"github.com/SigNoz/signoz/pkg/authn/passwordauthn/emailpasswordauthn"
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/global"
|
||||
"github.com/SigNoz/signoz/pkg/licensing"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
)
|
||||
|
||||
func NewAuthNs(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing) (map[authtypes.AuthNProvider]authn.AuthN, error) {
|
||||
func NewAuthNs(ctx context.Context, providerSettings factory.ProviderSettings, store authtypes.AuthNStore, licensing licensing.Licensing, globalConfig global.Config) (map[authtypes.AuthNProvider]authn.AuthN, error) {
|
||||
emailPasswordAuthN := emailpasswordauthn.New(store)
|
||||
|
||||
googleCallbackAuthN, err := googlecallbackauthn.New(ctx, store, providerSettings)
|
||||
googleCallbackAuthN, err := googlecallbackauthn.New(ctx, store, providerSettings, globalConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -275,14 +275,14 @@ func NewQuerierProviderFactories(telemetryStore telemetrystore.TelemetryStore, p
|
||||
)
|
||||
}
|
||||
|
||||
func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.AuthZ, modules Modules, handlers Handlers) factory.NamedMap[factory.ProviderFactory[apiserver.APIServer, apiserver.Config]] {
|
||||
func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.AuthZ, modules Modules, handlers Handlers, globalConfig global.Config) factory.NamedMap[factory.ProviderFactory[apiserver.APIServer, apiserver.Config]] {
|
||||
return factory.MustNewNamedMap(
|
||||
signozapiserver.NewFactory(
|
||||
orgGetter,
|
||||
authz,
|
||||
implorganization.NewHandler(modules.OrgGetter, modules.OrgSetter),
|
||||
impluser.NewHandler(modules.UserSetter, modules.UserGetter),
|
||||
implsession.NewHandler(modules.Session),
|
||||
implsession.NewHandler(modules.Session, globalConfig),
|
||||
implauthdomain.NewHandler(modules.AuthDomain),
|
||||
implpreference.NewHandler(modules.Preference),
|
||||
handlers.Global,
|
||||
|
||||
@@ -95,6 +95,7 @@ func TestNewProviderFactories(t *testing.T) {
|
||||
nil,
|
||||
Modules{},
|
||||
Handlers{},
|
||||
global.Config{},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -542,7 +542,7 @@ func New(
|
||||
ctx,
|
||||
providerSettings,
|
||||
config.APIServer,
|
||||
NewAPIServerProviderFactories(orgGetter, authz, modules, handlers),
|
||||
NewAPIServerProviderFactories(orgGetter, authz, modules, handlers, config.Global),
|
||||
"signoz",
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
102
pkg/types/spantypes/flamegraph_span.go
Normal file
102
pkg/types/spantypes/flamegraph_span.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package spantypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
type FlamegraphSpan struct {
|
||||
SpanID string `json:"spanId" required:"true"`
|
||||
ParentSpanID string `json:"parentSpanId" required:"true"`
|
||||
Timestamp uint64 `json:"timestamp" required:"true"`
|
||||
DurationNano uint64 `json:"durationNano" required:"true"`
|
||||
HasError bool `json:"hasError" required:"true"`
|
||||
Name string `json:"name" required:"true"`
|
||||
Level int64 `json:"level" required:"true"`
|
||||
Events []Event `json:"event" required:"true" nullable:"false"`
|
||||
Attributes map[string]any `json:"attributes" required:"true" nullable:"false"`
|
||||
Resource map[string]string `json:"resource" required:"true" nullable:"false"`
|
||||
Children []*FlamegraphSpan `json:"-"` // internal tree use only
|
||||
}
|
||||
|
||||
// FlamegraphLevel groups span IDs at a single level within the selected window.
|
||||
type FlamegraphLevel struct {
|
||||
Level int64
|
||||
SpanIDs []string
|
||||
}
|
||||
|
||||
type PostableFlamegraph struct {
|
||||
SelectedSpanID string `json:"selectedSpanId"`
|
||||
SelectFields []telemetrytypes.TelemetryFieldKey `json:"selectFields,omitempty"`
|
||||
}
|
||||
|
||||
// GettableFlamegraphTrace is the response for the v3 flamegraph API.
|
||||
type GettableFlamegraphTrace struct {
|
||||
Spans [][]*FlamegraphSpan `json:"spans" required:"true" nullable:"false"`
|
||||
StartTimestampMillis int64 `json:"startTimestampMillis" required:"true"`
|
||||
EndTimestampMillis int64 `json:"endTimestampMillis" required:"true"`
|
||||
HasMore bool `json:"hasMore" required:"true"`
|
||||
}
|
||||
|
||||
func NewGettableFlamegraphTrace(spans [][]*FlamegraphSpan, startMs, endMs int64, hasMore bool) *GettableFlamegraphTrace {
|
||||
return &GettableFlamegraphTrace{
|
||||
Spans: spans,
|
||||
StartTimestampMillis: startMs,
|
||||
EndTimestampMillis: endMs,
|
||||
HasMore: hasMore,
|
||||
}
|
||||
}
|
||||
|
||||
func NewFlamegraphSpanFromStorable(s *StorableSpan, level int64, selectFields []telemetrytypes.TelemetryFieldKey) *FlamegraphSpan {
|
||||
span := &FlamegraphSpan{
|
||||
SpanID: s.SpanID,
|
||||
ParentSpanID: s.ParentSpanID,
|
||||
Timestamp: uint64(s.StartTime.UnixNano()),
|
||||
DurationNano: s.DurationNano,
|
||||
HasError: s.HasError,
|
||||
Name: s.Name,
|
||||
Level: level,
|
||||
Events: s.UnmarshalledEvents(),
|
||||
Attributes: make(map[string]any),
|
||||
Resource: make(map[string]string),
|
||||
}
|
||||
if len(selectFields) == 0 {
|
||||
return span
|
||||
}
|
||||
for _, field := range selectFields {
|
||||
switch field.FieldContext {
|
||||
case telemetrytypes.FieldContextResource:
|
||||
if v, ok := s.ResourcesString[field.Name]; ok && v != "" {
|
||||
span.Resource[field.Name] = v
|
||||
}
|
||||
case telemetrytypes.FieldContextAttribute:
|
||||
if v := s.AttributeValue(field.Name); v != nil {
|
||||
span.Attributes[field.Name] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return span
|
||||
}
|
||||
|
||||
func NewMissingParentFlamegraphSpan(node *FlamegraphSpan) *FlamegraphSpan {
|
||||
return &FlamegraphSpan{
|
||||
SpanID: node.ParentSpanID,
|
||||
Name: "Missing Span",
|
||||
Timestamp: node.Timestamp,
|
||||
DurationNano: node.DurationNano,
|
||||
Events: []Event{},
|
||||
Children: []*FlamegraphSpan{node},
|
||||
}
|
||||
}
|
||||
|
||||
// FlamegraphWindowSpanIDs collects all span IDs from a level window into a flat slice.
|
||||
func FlamegraphWindowSpanIDs(window []FlamegraphLevel) []string {
|
||||
total := 0
|
||||
for _, lvl := range window {
|
||||
total += len(lvl.SpanIDs)
|
||||
}
|
||||
ids := make([]string, 0, total)
|
||||
for _, lvl := range window {
|
||||
ids = append(ids, lvl.SpanIDs...)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
111
pkg/types/spantypes/flamegraph_trace.go
Normal file
111
pkg/types/spantypes/flamegraph_trace.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package spantypes
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
// FlamegraphTrace holds the level wise tree built from minimal spans.
|
||||
type FlamegraphTrace struct {
|
||||
roots []*FlamegraphSpan
|
||||
nodeByID map[string]*FlamegraphSpan
|
||||
startTime uint64
|
||||
endTime uint64
|
||||
}
|
||||
|
||||
func NewFlamegraphTraceFromMinimal(spans []MinimalSpan) *FlamegraphTrace {
|
||||
t := &FlamegraphTrace{
|
||||
nodeByID: make(map[string]*FlamegraphSpan, len(spans)),
|
||||
}
|
||||
for i := range spans {
|
||||
node := spans[i].ToFlamegraphSpan()
|
||||
t.updateTimeRange(node.Timestamp, node.DurationNano)
|
||||
t.nodeByID[node.SpanID] = node
|
||||
}
|
||||
t.buildSpanTree()
|
||||
return t
|
||||
}
|
||||
|
||||
func NewFlamegraphTraceFromStorable(spans []StorableSpan, selectFields []telemetrytypes.TelemetryFieldKey) *FlamegraphTrace {
|
||||
t := &FlamegraphTrace{
|
||||
nodeByID: make(map[string]*FlamegraphSpan, len(spans)),
|
||||
}
|
||||
for i := range spans {
|
||||
node := NewFlamegraphSpanFromStorable(&spans[i], 0, selectFields) // level is set later by BFS
|
||||
t.updateTimeRange(node.Timestamp, node.DurationNano)
|
||||
t.nodeByID[node.SpanID] = node
|
||||
}
|
||||
t.buildSpanTree()
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *FlamegraphTrace) GetAllLevels() [][]*FlamegraphSpan {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSelectedLevels returns the window of levels around selectedSpanID with sampling applied to dense levels.
|
||||
func (t *FlamegraphTrace) GetSelectedLevels(selectedSpanID string, levelLimit, spansPerLevel, topLatencyCount, bucketCount int) []FlamegraphLevel {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *FlamegraphTrace) EnrichSelectedSpans(selectedSpans []FlamegraphLevel, fullSpans []StorableSpan, selectFields []telemetrytypes.TelemetryFieldKey) [][]*FlamegraphSpan {
|
||||
fullByID := make(map[string]*StorableSpan, len(fullSpans))
|
||||
for i := range fullSpans {
|
||||
fullByID[fullSpans[i].SpanID] = &fullSpans[i]
|
||||
}
|
||||
|
||||
result := make([][]*FlamegraphSpan, len(selectedSpans))
|
||||
for i, lvl := range selectedSpans {
|
||||
result[i] = make([]*FlamegraphSpan, 0, len(lvl.SpanIDs))
|
||||
for _, spanID := range lvl.SpanIDs {
|
||||
if full, ok := fullByID[spanID]; ok {
|
||||
result[i] = append(result[i], NewFlamegraphSpanFromStorable(full, lvl.Level, selectFields))
|
||||
} else if lean, ok := t.nodeByID[spanID]; ok {
|
||||
result[i] = append(result[i], lean)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (t *FlamegraphTrace) updateTimeRange(timestamp, durationNano uint64) {
|
||||
if t.startTime == 0 || timestamp < t.startTime {
|
||||
t.startTime = timestamp
|
||||
}
|
||||
if end := timestamp + durationNano; end > t.endTime {
|
||||
t.endTime = end
|
||||
}
|
||||
}
|
||||
|
||||
func (t *FlamegraphTrace) buildSpanTree() {
|
||||
for _, node := range t.nodeByID {
|
||||
if node.ParentSpanID != "" {
|
||||
if parent, ok := t.nodeByID[node.ParentSpanID]; ok {
|
||||
parent.Children = append(parent.Children, node)
|
||||
} else {
|
||||
missing := NewMissingParentFlamegraphSpan(node)
|
||||
t.nodeByID[missing.SpanID] = missing
|
||||
t.roots = append(t.roots, missing)
|
||||
}
|
||||
} else if flamegraphSpanIndex(t.roots, node.SpanID) == -1 {
|
||||
t.roots = append(t.roots, node)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(t.roots, func(i, j int) bool {
|
||||
if t.roots[i].Timestamp == t.roots[j].Timestamp {
|
||||
return t.roots[i].SpanID < t.roots[j].SpanID
|
||||
}
|
||||
return t.roots[i].Timestamp < t.roots[j].Timestamp
|
||||
})
|
||||
}
|
||||
|
||||
func flamegraphSpanIndex(spans []*FlamegraphSpan, spanID string) int {
|
||||
for i, s := range spans {
|
||||
if s.SpanID == spanID {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
@@ -30,6 +30,7 @@ type TraceStore interface {
|
||||
GetTraceSpans(ctx context.Context, traceID string, summary *TraceSummary) ([]StorableSpan, error)
|
||||
GetMinimalSpans(ctx context.Context, traceID string, start, end time.Time) ([]MinimalSpan, error)
|
||||
GetTraceSpansByIDs(ctx context.Context, traceID string, start, end time.Time, spanIDs []string) ([]StorableSpan, error)
|
||||
GetFlamegraphSpans(ctx context.Context, traceID string, start, end time.Time, spanIDs []string) ([]StorableSpan, error)
|
||||
|
||||
GetSpanCountByField(ctx context.Context, traceID string, summary *TraceSummary, fieldKey telemetrytypes.TelemetryFieldKey) (map[string]uint64, error)
|
||||
GetSpanDurationByField(ctx context.Context, traceID string, summary *TraceSummary, fieldKey telemetrytypes.TelemetryFieldKey) (map[string]uint64, error)
|
||||
|
||||
@@ -164,6 +164,17 @@ func (item *MinimalSpan) ToWaterfallSpan(traceID string) *WaterfallSpan {
|
||||
}
|
||||
}
|
||||
|
||||
func (item *MinimalSpan) ToFlamegraphSpan() *FlamegraphSpan {
|
||||
return &FlamegraphSpan{
|
||||
SpanID: item.SpanID,
|
||||
ParentSpanID: item.ParentSpanID,
|
||||
Timestamp: uint64(item.StartTime.UnixNano()),
|
||||
DurationNano: item.DurationNano,
|
||||
HasError: item.HasError,
|
||||
Children: make([]*FlamegraphSpan, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// NewMissingWaterfallSpan creates a synthetic placeholder span for a parent that has no recorded data.
|
||||
func NewMissingWaterfallSpan(spanID, traceID string, timeUnixNano, durationNano uint64) *WaterfallSpan {
|
||||
return &WaterfallSpan{
|
||||
@@ -267,6 +278,19 @@ func (ws *WaterfallSpan) getPathToSelectedSpanID(selectedSpanID string) ([]strin
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (item *StorableSpan) AttributeValue(name string) any {
|
||||
if v, ok := item.AttributesString[name]; ok {
|
||||
return v
|
||||
}
|
||||
if v, ok := item.AttributesNumber[name]; ok {
|
||||
return v
|
||||
}
|
||||
if v, ok := item.AttributesBool[name]; ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (item *StorableSpan) Attributes() map[string]any {
|
||||
attributes := make(map[string]any, len(item.AttributesString)+len(item.AttributesNumber)+len(item.AttributesBool))
|
||||
for k, v := range item.AttributesString {
|
||||
@@ -296,7 +320,7 @@ func (item *StorableSpan) UnmarshalledEvents() []Event {
|
||||
func (item *StorableSpan) UnmarshalledRefs() []OtelSpanRef {
|
||||
refs := []OtelSpanRef{}
|
||||
if err := json.Unmarshal([]byte(item.References), &refs); err != nil {
|
||||
return nil // skip malformed values
|
||||
return []OtelSpanRef{} // skip malformed values
|
||||
}
|
||||
return refs
|
||||
}
|
||||
|
||||
140
tests/fixtures/auth.py
vendored
140
tests/fixtures/auth.py
vendored
@@ -56,11 +56,18 @@ def _login(signoz: types.SigNoz, email: str, password: str) -> str:
|
||||
return login.json()["data"]["accessToken"]
|
||||
|
||||
|
||||
@pytest.fixture(name="create_user_admin", scope="package")
|
||||
def create_user_admin(signoz: types.SigNoz, request: pytest.FixtureRequest, pytestconfig: pytest.Config) -> types.Operation:
|
||||
def create() -> None:
|
||||
def register_admin(
|
||||
signoz: types.SigNoz,
|
||||
request: pytest.FixtureRequest,
|
||||
pytestconfig: pytest.Config,
|
||||
cache_key: str = "create_user_admin",
|
||||
base_path: str = "",
|
||||
) -> types.Operation:
|
||||
"""Register the first admin (creates the org), under base_path. Reuse-wrapped."""
|
||||
|
||||
def create() -> types.Operation:
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v1/register"),
|
||||
signoz.self.host_configs["8080"].get(f"{base_path}/api/v1/register"),
|
||||
json={
|
||||
"name": USER_ADMIN_NAME,
|
||||
"orgName": "",
|
||||
@@ -83,7 +90,7 @@ def create_user_admin(signoz: types.SigNoz, request: pytest.FixtureRequest, pyte
|
||||
return reuse.wrap(
|
||||
request,
|
||||
pytestconfig,
|
||||
"create_user_admin",
|
||||
cache_key,
|
||||
lambda: types.Operation(name=""),
|
||||
create,
|
||||
delete,
|
||||
@@ -91,86 +98,86 @@ def create_user_admin(signoz: types.SigNoz, request: pytest.FixtureRequest, pyte
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="get_session_context", scope="function")
|
||||
def get_session_context(signoz: types.SigNoz) -> Callable[[str, str], str]:
|
||||
def _get_session_context(email: str) -> str:
|
||||
@pytest.fixture(name="create_user_admin", scope="package")
|
||||
def create_user_admin(signoz: types.SigNoz, request: pytest.FixtureRequest, pytestconfig: pytest.Config) -> types.Operation:
|
||||
return register_admin(signoz, request, pytestconfig)
|
||||
|
||||
|
||||
def session_context_getter(signoz: types.SigNoz, base_path: str = "") -> Callable[[str], dict]:
|
||||
"""Build a callable that fetches the session context for an email (under base_path)."""
|
||||
|
||||
def fetch_session_context(email: str) -> dict:
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/context"),
|
||||
params={
|
||||
"email": email,
|
||||
"ref": f"{signoz.self.host_configs['8080'].base()}",
|
||||
},
|
||||
signoz.self.host_configs["8080"].get(f"{base_path}/api/v2/sessions/context"),
|
||||
params={"email": email, "ref": f"{signoz.self.host_configs['8080'].base()}"},
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
return response.json()["data"]
|
||||
|
||||
return _get_session_context
|
||||
return fetch_session_context
|
||||
|
||||
|
||||
@pytest.fixture(name="get_session_context", scope="function")
|
||||
def get_session_context(signoz: types.SigNoz) -> Callable[[str], dict]:
|
||||
return session_context_getter(signoz)
|
||||
|
||||
|
||||
def token_getter(signoz: types.SigNoz, base_path: str = "") -> Callable[[str, str], str]:
|
||||
"""Build a callable that logs in (email/password) and returns the access token (under base_path)."""
|
||||
|
||||
def fetch_token(email: str, password: str) -> str:
|
||||
context = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{base_path}/api/v2/sessions/context"),
|
||||
params={"email": email, "ref": f"{signoz.self.host_configs['8080'].base()}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert context.status_code == HTTPStatus.OK
|
||||
org_id = context.json()["data"]["orgs"][0]["id"]
|
||||
|
||||
login = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{base_path}/api/v2/sessions/email_password"),
|
||||
json={"email": email, "password": password, "orgId": org_id},
|
||||
timeout=5,
|
||||
)
|
||||
assert login.status_code == HTTPStatus.OK
|
||||
return login.json()["data"]["accessToken"]
|
||||
|
||||
return fetch_token
|
||||
|
||||
|
||||
@pytest.fixture(name="get_token", scope="function")
|
||||
def get_token(signoz: types.SigNoz) -> Callable[[str, str], str]:
|
||||
def _get_token(email: str, password: str) -> str:
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/context"),
|
||||
params={
|
||||
"email": email,
|
||||
"ref": f"{signoz.self.host_configs['8080'].base()}",
|
||||
},
|
||||
return token_getter(signoz)
|
||||
|
||||
|
||||
def tokens_getter(signoz: types.SigNoz, base_path: str = "") -> Callable[[str, str], tuple[str, str]]:
|
||||
"""Build a callable that logs in and returns the (access, refresh) token pair (under base_path)."""
|
||||
|
||||
def fetch_tokens(email: str, password: str) -> tuple[str, str]:
|
||||
context = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{base_path}/api/v2/sessions/context"),
|
||||
params={"email": email, "ref": f"{signoz.self.host_configs['8080'].base()}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert context.status_code == HTTPStatus.OK
|
||||
org_id = context.json()["data"]["orgs"][0]["id"]
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
org_id = response.json()["data"]["orgs"][0]["id"]
|
||||
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/email_password"),
|
||||
json={
|
||||
"email": email,
|
||||
"password": password,
|
||||
"orgId": org_id,
|
||||
},
|
||||
login = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{base_path}/api/v2/sessions/email_password"),
|
||||
json={"email": email, "password": password, "orgId": org_id},
|
||||
timeout=5,
|
||||
)
|
||||
assert login.status_code == HTTPStatus.OK
|
||||
data = login.json()["data"]
|
||||
return data["accessToken"], data["refreshToken"]
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
return response.json()["data"]["accessToken"]
|
||||
|
||||
return _get_token
|
||||
return fetch_tokens
|
||||
|
||||
|
||||
@pytest.fixture(name="get_tokens", scope="function")
|
||||
def get_tokens(signoz: types.SigNoz) -> Callable[[str, str], tuple[str, str]]:
|
||||
def _get_tokens(email: str, password: str) -> str:
|
||||
response = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/context"),
|
||||
params={
|
||||
"email": email,
|
||||
"ref": f"{signoz.self.host_configs['8080'].base()}",
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
org_id = response.json()["data"]["orgs"][0]["id"]
|
||||
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/api/v2/sessions/email_password"),
|
||||
json={
|
||||
"email": email,
|
||||
"password": password,
|
||||
"orgId": org_id,
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
access_token = response.json()["data"]["accessToken"]
|
||||
refresh_token = response.json()["data"]["refreshToken"]
|
||||
return access_token, refresh_token
|
||||
|
||||
return _get_tokens
|
||||
return tokens_getter(signoz)
|
||||
|
||||
|
||||
@pytest.fixture(name="apply_license", scope="package")
|
||||
@@ -270,6 +277,7 @@ def add_license(
|
||||
signoz: types.SigNoz,
|
||||
make_http_mocks: Callable[[types.TestContainerDocker, list[Mapping]], None],
|
||||
get_token: Callable[[str, str], str], # pylint: disable=redefined-outer-name
|
||||
base_path: str = "",
|
||||
) -> None:
|
||||
make_http_mocks(
|
||||
signoz.zeus,
|
||||
@@ -308,7 +316,7 @@ def add_license(
|
||||
access_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
response = requests.post(
|
||||
url=signoz.self.host_configs["8080"].get("/api/v3/licenses"),
|
||||
url=signoz.self.host_configs["8080"].get(f"{base_path}/api/v3/licenses"),
|
||||
json={"key": "secret-key"},
|
||||
headers={"Authorization": "Bearer " + access_token},
|
||||
timeout=5,
|
||||
|
||||
144
tests/integration/testdata/inframonitoring/hosts_filter_dataset.jsonl
vendored
Normal file
144
tests/integration/testdata/inframonitoring/hosts_filter_dataset.jsonl
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "user"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "user"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 200, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "user"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 300, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "system"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "system"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "system"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 150, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "idle"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 400, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "idle"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 600, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "idle"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 800, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "wait"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 10, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "wait"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 20, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "wait"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 30, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2100000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2200000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 6000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5900000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5800000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "buffered"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "buffered"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "buffered"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "cached"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "cached"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "cached"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "prod-linux-1", "os.type": "linux"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "prod-linux-1", "os.type": "linux"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1.55, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "prod-linux-1", "os.type": "linux"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1.6, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 51000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 52000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 49000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 48000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "reserved"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "reserved"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-linux-1", "os.type": "linux", "state": "reserved"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "user"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "user"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 200, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "user"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 300, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "system"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "system"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "system"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 150, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "idle"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 400, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "idle"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 600, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "idle"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 800, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "wait"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 10, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "wait"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 20, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "wait"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 30, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2100000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2200000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 6000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5900000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5800000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "buffered"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "buffered"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "buffered"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "cached"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "cached"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "cached"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "prod-windows-1", "os.type": "windows"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "prod-windows-1", "os.type": "windows"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1.55, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "prod-windows-1", "os.type": "windows"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1.6, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 51000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 52000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 49000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 48000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "reserved"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "reserved"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "prod-windows-1", "os.type": "windows", "state": "reserved"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "user"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "user"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 200, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "user"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 300, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "system"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "system"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "system"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 150, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "idle"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 400, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "idle"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 600, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "idle"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 800, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "wait"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 10, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "wait"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 20, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "wait"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 30, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2100000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2200000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 6000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5900000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5800000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "buffered"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "buffered"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "buffered"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "cached"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "cached"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "cached"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "dev-linux-1", "os.type": "linux"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "dev-linux-1", "os.type": "linux"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1.55, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "dev-linux-1", "os.type": "linux"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1.6, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 51000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 52000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 49000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 48000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "reserved"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "reserved"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-linux-1", "os.type": "linux", "state": "reserved"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "user"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "user"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 200, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "user"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 300, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "system"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "system"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 100, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "system"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 150, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "idle"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 400, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "idle"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 600, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "idle"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 800, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "wait"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 10, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "wait"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 20, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.cpu.time", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "wait"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 30, "temporality": "Cumulative", "type_": "Sum", "is_monotonic": true}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 2000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 2100000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 2200000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 6000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5900000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5800000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "buffered"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "buffered"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "buffered"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "cached"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "cached"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.memory.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "cached"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1500000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "dev-windows-1", "os.type": "windows"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 1.5, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "dev-windows-1", "os.type": "windows"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 1.55, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.cpu.load_average.15m", "labels": {"host.name": "dev-windows-1", "os.type": "windows"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 1.6, "temporality": "Unspecified", "type_": "Gauge", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 51000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "used"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 52000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 50000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 49000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "free"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 48000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "reserved"}, "timestamp": "2025-01-10T10:00:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "reserved"}, "timestamp": "2025-01-10T10:02:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
{"metric_name": "system.filesystem.usage", "labels": {"host.name": "dev-windows-1", "os.type": "windows", "state": "reserved"}, "timestamp": "2025-01-10T10:04:00+00:00", "value": 5000000000, "temporality": "Unspecified", "type_": "Sum", "is_monotonic": false}
|
||||
126
tests/integration/testdata/inframonitoring/hosts_groupby_os_type.jsonl
vendored
Normal file
126
tests/integration/testdata/inframonitoring/hosts_groupby_os_type.jsonl
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-linux-1","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-linux-1","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-linux-1","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-windows-1","os.type":"windows"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-windows-1","os.type":"windows"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-windows-1","os.type":"windows"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-windows-2","os.type":"windows"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-windows-2","os.type":"windows"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-windows-2","os.type":"windows"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-windows-2","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:20:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:22:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:24:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:20:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:22:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:24:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-linux-1","os.type":"linux"},"timestamp":"2025-01-10T10:20:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-linux-1","os.type":"linux"},"timestamp":"2025-01-10T10:22:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-linux-1","os.type":"linux"},"timestamp":"2025-01-10T10:24:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:20:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:22:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:24:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:20:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:22:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-linux-2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:24:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-linux-2","os.type":"linux"},"timestamp":"2025-01-10T10:20:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-linux-2","os.type":"linux"},"timestamp":"2025-01-10T10:22:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-linux-2","os.type":"linux"},"timestamp":"2025-01-10T10:24:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-linux-2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-windows-1","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:20:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-windows-1","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:22:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-windows-1","os.type":"windows","state":"user"},"timestamp":"2025-01-10T10:24:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-windows-1","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:20:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-windows-1","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:22:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-windows-1","os.type":"windows","state":"idle"},"timestamp":"2025-01-10T10:24:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-windows-1","os.type":"windows"},"timestamp":"2025-01-10T10:20:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-windows-1","os.type":"windows"},"timestamp":"2025-01-10T10:22:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-windows-1","os.type":"windows"},"timestamp":"2025-01-10T10:24:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-windows-1","os.type":"windows","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
12
tests/integration/testdata/inframonitoring/hosts_missing_metrics.jsonl
vendored
Normal file
12
tests/integration/testdata/inframonitoring/hosts_missing_metrics.jsonl
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:00:00+00:00","value":50,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:02:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:04:00+00:00","value":150,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:00+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:00+00:00","value":20,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"miss-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:00+00:00","value":30,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
120
tests/integration/testdata/inframonitoring/hosts_orderby.jsonl
vendored
Normal file
120
tests/integration/testdata/inframonitoring/hosts_orderby.jsonl
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:00+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:00+00:00","value":20,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:00+00:00","value":30,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h1","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h1","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h1","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:10+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:10+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:10+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:10+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:10+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:10+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:10+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:10+00:00","value":20,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h2","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:10+00:00","value":30,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:10+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:10+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:10+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:10+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:10+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:10+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h2","os.type":"linux"},"timestamp":"2025-01-10T10:00:10+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h2","os.type":"linux"},"timestamp":"2025-01-10T10:02:10+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h2","os.type":"linux"},"timestamp":"2025-01-10T10:04:10+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:10+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:10+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:10+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:10+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:10+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:10+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:20+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:20+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:20+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:20+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:20+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:20+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:20+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:20+00:00","value":20,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h3","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:20+00:00","value":30,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:20+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:20+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:20+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:20+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:20+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:20+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h3","os.type":"linux"},"timestamp":"2025-01-10T10:00:20+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h3","os.type":"linux"},"timestamp":"2025-01-10T10:02:20+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h3","os.type":"linux"},"timestamp":"2025-01-10T10:04:20+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:20+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:20+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:20+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:20+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:20+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:20+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:30+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:30+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:30+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:30+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:30+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:30+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:30+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:30+00:00","value":20,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h4","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:30+00:00","value":30,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:30+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:30+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:30+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:30+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:30+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:30+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h4","os.type":"linux"},"timestamp":"2025-01-10T10:00:30+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h4","os.type":"linux"},"timestamp":"2025-01-10T10:02:30+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h4","os.type":"linux"},"timestamp":"2025-01-10T10:04:30+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:30+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:30+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:30+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:30+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:30+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:30+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:40+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:40+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:40+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:40+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:40+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:40+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:40+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:40+00:00","value":20,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"order-h5","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:40+00:00","value":30,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:40+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:40+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:40+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:40+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:40+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:40+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h5","os.type":"linux"},"timestamp":"2025-01-10T10:00:40+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h5","os.type":"linux"},"timestamp":"2025-01-10T10:02:40+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"order-h5","os.type":"linux"},"timestamp":"2025-01-10T10:04:40+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:40+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:40+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:40+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:40+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:40+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"order-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:40+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
147
tests/integration/testdata/inframonitoring/hosts_pagination.jsonl
vendored
Normal file
147
tests/integration/testdata/inframonitoring/hosts_pagination.jsonl
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":50,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h1","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h1","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h1","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h2","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h2","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h2","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h3","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h3","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":150,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h3","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h3","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h3","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h3","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h3","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h3","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h3","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h3","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h4","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h4","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h4","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h4","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h4","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h4","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h4","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h4","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h4","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h4","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h5","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h5","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":250,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h5","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":500,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h5","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h5","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h5","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h5","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h5","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h5","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h5","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h6","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h6","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h6","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h6","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h6","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h6","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h6","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h6","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h6","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h6","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h7","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":0,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h7","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":350,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h7","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":700,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h7","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h7","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"page-h7","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h7","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h7","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"page-h7","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"page-h7","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
42
tests/integration/testdata/inframonitoring/hosts_status.jsonl
vendored
Normal file
42
tests/integration/testdata/inframonitoring/hosts_status.jsonl
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"inactive-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-h1","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-h1","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"inactive-h1","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"inactive-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:20:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:22:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:24:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:20:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:22:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"active-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:24:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-h1","os.type":"linux"},"timestamp":"2025-01-10T10:20:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-h1","os.type":"linux"},"timestamp":"2025-01-10T10:22:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"active-h1","os.type":"linux"},"timestamp":"2025-01-10T10:24:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:22:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:24:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:20:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:22:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"active-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:24:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
72
tests/integration/testdata/inframonitoring/hosts_value_accuracy.jsonl
vendored
Normal file
72
tests/integration/testdata/inframonitoring/hosts_value_accuracy.jsonl
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":200,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:00:00+00:00","value":50,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:02:00+00:00","value":100,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:04:00+00:00","value":150,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":400,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":600,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":800,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:00+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:00+00:00","value":20,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h1","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:00+00:00","value":30,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:00:00+00:00","value":80,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:02:00+00:00","value":160,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"user"},"timestamp":"2025-01-10T10:04:00+00:00","value":240,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:00:00+00:00","value":40,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:02:00+00:00","value":80,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"system"},"timestamp":"2025-01-10T10:04:00+00:00","value":120,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:00:00+00:00","value":300,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:02:00+00:00","value":500,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"idle"},"timestamp":"2025-01-10T10:04:00+00:00","value":700,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:00:00+00:00","value":5,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:02:00+00:00","value":10,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.cpu.time","labels":{"host.name":"acc-h2","os.type":"linux","state":"wait"},"timestamp":"2025-01-10T10:04:00+00:00","value":15,"temporality":"Cumulative","type_":"Sum","is_monotonic":true}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":2000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":2100000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":2200000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":6000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":5900000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":5800000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"buffered"},"timestamp":"2025-01-10T10:00:00+00:00","value":500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"buffered"},"timestamp":"2025-01-10T10:02:00+00:00","value":500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"buffered"},"timestamp":"2025-01-10T10:04:00+00:00","value":500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"cached"},"timestamp":"2025-01-10T10:00:00+00:00","value":1500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"cached"},"timestamp":"2025-01-10T10:02:00+00:00","value":1500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"cached"},"timestamp":"2025-01-10T10:04:00+00:00","value":1500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":3000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":3250000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":3500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":5000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":4750000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":4500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"buffered"},"timestamp":"2025-01-10T10:00:00+00:00","value":500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"buffered"},"timestamp":"2025-01-10T10:02:00+00:00","value":500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"buffered"},"timestamp":"2025-01-10T10:04:00+00:00","value":500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"cached"},"timestamp":"2025-01-10T10:00:00+00:00","value":1500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"cached"},"timestamp":"2025-01-10T10:02:00+00:00","value":1500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.memory.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"cached"},"timestamp":"2025-01-10T10:04:00+00:00","value":1500000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"acc-h1","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":1.5,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"acc-h1","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":1.55,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"acc-h1","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":1.6,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"acc-h2","os.type":"linux"},"timestamp":"2025-01-10T10:00:00+00:00","value":2.0,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"acc-h2","os.type":"linux"},"timestamp":"2025-01-10T10:02:00+00:00","value":2.05,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.cpu.load_average.15m","labels":{"host.name":"acc-h2","os.type":"linux"},"timestamp":"2025-01-10T10:04:00+00:00","value":2.1,"temporality":"Unspecified","type_":"Gauge","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":51000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":52000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":50000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":49000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":48000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"reserved"},"timestamp":"2025-01-10T10:00:00+00:00","value":5000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"reserved"},"timestamp":"2025-01-10T10:02:00+00:00","value":5000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h1","os.type":"linux","state":"reserved"},"timestamp":"2025-01-10T10:04:00+00:00","value":5000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:00:00+00:00","value":70000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:02:00+00:00","value":71000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"used"},"timestamp":"2025-01-10T10:04:00+00:00","value":72000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:00:00+00:00","value":30000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:02:00+00:00","value":29000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"free"},"timestamp":"2025-01-10T10:04:00+00:00","value":28000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"reserved"},"timestamp":"2025-01-10T10:00:00+00:00","value":5000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"reserved"},"timestamp":"2025-01-10T10:02:00+00:00","value":5000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
{"metric_name":"system.filesystem.usage","labels":{"host.name":"acc-h2","os.type":"linux","state":"reserved"},"timestamp":"2025-01-10T10:04:00+00:00","value":5000000000,"temporality":"Unspecified","type_":"Sum","is_monotonic":false}
|
||||
20
tests/integration/testdata/inframonitoring/hosts_value_accuracy_expected.json
vendored
Normal file
20
tests/integration/testdata/inframonitoring/hosts_value_accuracy_expected.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"hostName": "acc-h1",
|
||||
"cpu": 0.4444444444444445,
|
||||
"memory": 0.205,
|
||||
"wait": 0.027777777777777776,
|
||||
"load15": 1.525,
|
||||
"diskUsage": 0.48095238095238096
|
||||
},
|
||||
{
|
||||
"hostName": "acc-h2",
|
||||
"cpu": 0.3846153846153845,
|
||||
"memory": 0.3125,
|
||||
"wait": 0.015384615384615384,
|
||||
"load15": 2.025,
|
||||
"diskUsage": 0.6714285714285714
|
||||
}
|
||||
]
|
||||
}
|
||||
126
tests/integration/tests/basepath/01_oidc.py
Normal file
126
tests/integration/tests/basepath/01_oidc.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from collections.abc import Callable
|
||||
from http import HTTPStatus
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
from selenium import webdriver
|
||||
from wiremock.resources.mappings import Mapping
|
||||
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD, add_license, assert_user_has_role
|
||||
from fixtures.types import Operation, SigNoz, TestContainerDocker, TestContainerIDP
|
||||
|
||||
# SigNoz is served under /signoz, so the OIDC callback registered with the IdP
|
||||
# must include the prefix to match the backend-generated redirect URI.
|
||||
BASE_PATH = "/signoz"
|
||||
OIDC_CALLBACK_PATH = f"{BASE_PATH}/api/v1/complete/oidc"
|
||||
|
||||
|
||||
def test_apply_license(
|
||||
signoz: SigNoz,
|
||||
create_user_admin: Operation, # pylint: disable=unused-argument
|
||||
make_http_mocks: Callable[[TestContainerDocker, list[Mapping]], None],
|
||||
get_token: Callable[[str, str], str],
|
||||
) -> None:
|
||||
"""
|
||||
Applies a license to the signoz instance. add_license is a plain function
|
||||
called from the test (function scope), so the function-scoped make_http_mocks
|
||||
fixture is safe to use; base_path prefixes the licensing API call.
|
||||
"""
|
||||
add_license(signoz, make_http_mocks, get_token, base_path=BASE_PATH)
|
||||
|
||||
|
||||
def test_create_auth_domain(
|
||||
signoz: SigNoz,
|
||||
idp: TestContainerIDP, # pylint: disable=unused-argument
|
||||
create_oidc_client: Callable[[str, str], None],
|
||||
get_oidc_settings: Callable[[str], dict],
|
||||
get_token: Callable[[str, str], str],
|
||||
) -> None:
|
||||
"""
|
||||
Creates an OIDC auth domain in SigNoz served under a base path. The callback
|
||||
registered with the IdP carries the /signoz prefix.
|
||||
"""
|
||||
client_id = f"oidc.basepath.test.{signoz.self.host_configs['8080'].address}:{signoz.self.host_configs['8080'].port}"
|
||||
# Create an oidc client in the idp with the prefixed callback.
|
||||
create_oidc_client(client_id, OIDC_CALLBACK_PATH)
|
||||
|
||||
# Get the oidc settings from keycloak.
|
||||
settings = get_oidc_settings(client_id)
|
||||
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/signoz/api/v1/domains"),
|
||||
json={
|
||||
"name": "oidc.basepath.test",
|
||||
"config": {
|
||||
"ssoEnabled": True,
|
||||
"ssoType": "oidc",
|
||||
"oidcConfig": {
|
||||
"clientId": settings["client_id"],
|
||||
"clientSecret": settings["client_secret"],
|
||||
# Change the hostname of the issuer to the internal resolvable hostname of the idp
|
||||
"issuer": f"{idp.container.container_configs['6060'].get(urlparse(settings['issuer']).path)}",
|
||||
"issuerAlias": settings["issuer"],
|
||||
"getUserInfo": True,
|
||||
},
|
||||
},
|
||||
},
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.CREATED
|
||||
|
||||
|
||||
def test_oidc_authn(
|
||||
signoz: SigNoz,
|
||||
idp: TestContainerIDP,
|
||||
driver: webdriver.Chrome,
|
||||
create_user_idp: Callable[[str, str, bool, str, str], None],
|
||||
idp_login: Callable[[str, str], None],
|
||||
get_token: Callable[[str, str], str],
|
||||
get_session_context: Callable[[str], dict],
|
||||
) -> None:
|
||||
"""
|
||||
Tests the OIDC authn flow when SigNoz is served under a base path. The login
|
||||
URL the backend produces (and thus the IdP callback) carries the /signoz
|
||||
prefix; the e2e browser login must complete and create the user.
|
||||
"""
|
||||
# Create a user in the idp.
|
||||
create_user_idp("viewer@oidc.basepath.test", "password123", True)
|
||||
|
||||
# Get the session context from signoz which will give the OIDC login URL.
|
||||
session_context = get_session_context("viewer@oidc.basepath.test")
|
||||
|
||||
assert len(session_context["orgs"]) == 1
|
||||
assert len(session_context["orgs"][0]["authNSupport"]["callback"]) == 1
|
||||
|
||||
url = session_context["orgs"][0]["authNSupport"]["callback"][0]["url"]
|
||||
|
||||
# change the url to the external resolvable hostname of the idp
|
||||
parsed_url = urlparse(url)
|
||||
actual_url = f"{idp.container.host_configs['6060'].get(parsed_url.path)}?{parsed_url.query}"
|
||||
|
||||
driver.get(actual_url)
|
||||
idp_login("viewer@oidc.basepath.test", "password123")
|
||||
|
||||
# Assert that the user was created in signoz (lookup under the base path).
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
users = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/signoz/api/v2/users"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert users.status_code == HTTPStatus.OK, users.text
|
||||
user = next((u for u in users.json()["data"] if u["email"] == "viewer@oidc.basepath.test"), None)
|
||||
assert user is not None, "User with email 'viewer@oidc.basepath.test' not found"
|
||||
|
||||
user_with_roles = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"/signoz/api/v2/users/{user['id']}"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert user_with_roles.status_code == HTTPStatus.OK, user_with_roles.text
|
||||
assert_user_has_role(user_with_roles.json()["data"], "signoz-viewer")
|
||||
117
tests/integration/tests/basepath/02_saml.py
Normal file
117
tests/integration/tests/basepath/02_saml.py
Normal file
@@ -0,0 +1,117 @@
|
||||
from collections.abc import Callable
|
||||
from http import HTTPStatus
|
||||
|
||||
import requests
|
||||
from selenium import webdriver
|
||||
from wiremock.resources.mappings import Mapping
|
||||
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD, add_license, assert_user_has_role
|
||||
from fixtures.types import Operation, SigNoz, TestContainerDocker, TestContainerIDP
|
||||
|
||||
# SigNoz is served under /signoz, so the SAML ACS registered with the IdP must
|
||||
# include the prefix to match the backend-generated AssertionConsumerServiceURL.
|
||||
BASE_PATH = "/signoz"
|
||||
SAML_CALLBACK_PATH = f"{BASE_PATH}/api/v1/complete/saml"
|
||||
|
||||
|
||||
def test_apply_license(
|
||||
signoz: SigNoz,
|
||||
create_user_admin: Operation, # pylint: disable=unused-argument
|
||||
make_http_mocks: Callable[[TestContainerDocker, list[Mapping]], None],
|
||||
get_token: Callable[[str, str], str],
|
||||
) -> None:
|
||||
"""
|
||||
Applies a license to the signoz instance. add_license is a plain function
|
||||
called from the test (function scope), so the function-scoped make_http_mocks
|
||||
fixture is safe to use; base_path prefixes the licensing API call.
|
||||
"""
|
||||
add_license(signoz, make_http_mocks, get_token, base_path=BASE_PATH)
|
||||
|
||||
|
||||
def test_create_auth_domain(
|
||||
signoz: SigNoz,
|
||||
idp: TestContainerIDP, # pylint: disable=unused-argument
|
||||
create_saml_client: Callable[[str, str], None],
|
||||
get_saml_settings: Callable[[], dict],
|
||||
get_token: Callable[[str, str], str],
|
||||
) -> None:
|
||||
"""
|
||||
Creates a SAML auth domain in SigNoz served under a base path. The ACS
|
||||
registered with the IdP carries the /signoz prefix.
|
||||
"""
|
||||
# Create a saml client in the idp with the prefixed ACS.
|
||||
create_saml_client("saml.basepath.test", SAML_CALLBACK_PATH)
|
||||
|
||||
# Get the saml settings from keycloak.
|
||||
settings = get_saml_settings()
|
||||
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get("/signoz/api/v1/domains"),
|
||||
json={
|
||||
"name": "saml.basepath.test",
|
||||
"config": {
|
||||
"ssoEnabled": True,
|
||||
"ssoType": "saml",
|
||||
"samlConfig": {
|
||||
"samlEntity": settings["entityID"],
|
||||
"samlIdp": settings["singleSignOnServiceLocation"],
|
||||
"samlCert": settings["certificate"],
|
||||
},
|
||||
},
|
||||
},
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=2,
|
||||
)
|
||||
|
||||
assert response.status_code == HTTPStatus.CREATED
|
||||
|
||||
|
||||
def test_saml_authn(
|
||||
signoz: SigNoz,
|
||||
idp: TestContainerIDP, # pylint: disable=unused-argument
|
||||
driver: webdriver.Chrome,
|
||||
create_user_idp: Callable[[str, str, bool, str, str], None],
|
||||
idp_login: Callable[[str, str], None],
|
||||
get_token: Callable[[str, str], str],
|
||||
get_session_context: Callable[[str], dict],
|
||||
) -> None:
|
||||
"""
|
||||
Tests the SAML authn flow when SigNoz is served under a base path. The
|
||||
AssertionConsumerServiceURL in the AuthnRequest carries the /signoz prefix;
|
||||
the e2e browser login must complete and create the user.
|
||||
"""
|
||||
# Create a user in the idp.
|
||||
create_user_idp("viewer@saml.basepath.test", "password", True)
|
||||
|
||||
# Get the session context from signoz which will give the SAML login URL.
|
||||
session_context = get_session_context("viewer@saml.basepath.test")
|
||||
|
||||
assert len(session_context["orgs"]) == 1
|
||||
assert len(session_context["orgs"][0]["authNSupport"]["callback"]) == 1
|
||||
|
||||
url = session_context["orgs"][0]["authNSupport"]["callback"][0]["url"]
|
||||
|
||||
driver.get(url)
|
||||
idp_login("viewer@saml.basepath.test", "password")
|
||||
|
||||
# Assert that the user was created in signoz (lookup under the base path).
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
users = requests.get(
|
||||
signoz.self.host_configs["8080"].get("/signoz/api/v2/users"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert users.status_code == HTTPStatus.OK, users.text
|
||||
user = next((u for u in users.json()["data"] if u["email"] == "viewer@saml.basepath.test"), None)
|
||||
assert user is not None, "User with email 'viewer@saml.basepath.test' not found"
|
||||
|
||||
user_with_roles = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"/signoz/api/v2/users/{user['id']}"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert user_with_roles.status_code == HTTPStatus.OK, user_with_roles.text
|
||||
assert_user_has_role(user_with_roles.json()["data"], "signoz-viewer")
|
||||
0
tests/integration/tests/basepath/__init__.py
Normal file
0
tests/integration/tests/basepath/__init__.py
Normal file
57
tests/integration/tests/basepath/conftest.py
Normal file
57
tests/integration/tests/basepath/conftest.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from collections.abc import Callable
|
||||
|
||||
import pytest
|
||||
from testcontainers.core.container import Network
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import register_admin, session_context_getter, token_getter
|
||||
from fixtures.signoz import create_signoz
|
||||
|
||||
# SigNoz is served under this URL path prefix for the base-path suite. The auth
|
||||
# helpers from fixtures/auth.py are reused via their factories with this prefix,
|
||||
# so these fixtures shadow the same-named root ones without duplicating logic.
|
||||
# Only the path component is read by global.ExternalPath(), which derives the
|
||||
# http.StripPrefix route prefix.
|
||||
BASE_PATH = "/signoz"
|
||||
|
||||
|
||||
@pytest.fixture(name="signoz", scope="package")
|
||||
def signoz_base_path( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
network: Network,
|
||||
zeus: types.TestContainerDocker,
|
||||
gateway: types.TestContainerDocker,
|
||||
sqlstore: types.TestContainerSQL,
|
||||
clickhouse: types.TestContainerClickhouse,
|
||||
request: pytest.FixtureRequest,
|
||||
pytestconfig: pytest.Config,
|
||||
) -> types.SigNoz:
|
||||
"""
|
||||
Package-scoped SigNoz served under BASE_PATH. Sets SIGNOZ_GLOBAL_EXTERNAL__URL
|
||||
with the prefix so the backend derives the http.StripPrefix route prefix.
|
||||
"""
|
||||
return create_signoz(
|
||||
network=network,
|
||||
zeus=zeus,
|
||||
gateway=gateway,
|
||||
sqlstore=sqlstore,
|
||||
clickhouse=clickhouse,
|
||||
request=request,
|
||||
pytestconfig=pytestconfig,
|
||||
cache_key="signoz_base_path",
|
||||
env_overrides={"SIGNOZ_GLOBAL_EXTERNAL__URL": f"http://localhost:8080{BASE_PATH}"},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="create_user_admin", scope="package")
|
||||
def create_user_admin_base_path(signoz: types.SigNoz, request: pytest.FixtureRequest, pytestconfig: pytest.Config) -> types.Operation:
|
||||
return register_admin(signoz, request, pytestconfig, cache_key="create_user_admin_base_path", base_path=BASE_PATH)
|
||||
|
||||
|
||||
@pytest.fixture(name="get_token", scope="function")
|
||||
def get_token(signoz: types.SigNoz) -> Callable[[str, str], str]:
|
||||
return token_getter(signoz, BASE_PATH)
|
||||
|
||||
|
||||
@pytest.fixture(name="get_session_context", scope="function")
|
||||
def get_session_context(signoz: types.SigNoz) -> Callable[[str], dict]:
|
||||
return session_context_getter(signoz, BASE_PATH)
|
||||
@@ -483,7 +483,7 @@ def test_enable_metrics_provisions_dashboards(
|
||||
assert isinstance(dashboards_in_service, list) and len(dashboards_in_service) > 0, "assets.dashboards should be non-empty after enabling metrics"
|
||||
provisioned_ids = set()
|
||||
for dash in dashboards_in_service:
|
||||
assert "integrationDashboard" in dash, f"Integration dashboard entry missing"
|
||||
assert "integrationDashboard" in dash, "Integration dashboard entry missing"
|
||||
try:
|
||||
uuid.UUID(dash["integrationDashboard"]["id"])
|
||||
except ValueError as err:
|
||||
|
||||
634
tests/integration/tests/inframonitoring/01_hosts.py
Normal file
634
tests/integration/tests/inframonitoring/01_hosts.py
Normal file
@@ -0,0 +1,634 @@
|
||||
"""Integration tests for v2 infra-monitoring host endpoints."""
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from http import HTTPStatus
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD
|
||||
from fixtures.fs import get_testdata_file_path
|
||||
from fixtures.metrics import Metrics
|
||||
from fixtures.querier import compare_values
|
||||
|
||||
ENDPOINT = "/api/v2/infra_monitoring/hosts"
|
||||
|
||||
|
||||
def test_hosts_accuracy(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
) -> None:
|
||||
"""Seed 2 hosts x 4 metrics; assert response shape/contract + exact metric
|
||||
values per record against precomputed expected output."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path("inframonitoring/hosts_value_accuracy.jsonl"),
|
||||
base_time=now - timedelta(minutes=4),
|
||||
)
|
||||
)
|
||||
|
||||
with open(
|
||||
get_testdata_file_path("inframonitoring/hosts_value_accuracy_expected.json"),
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
expected = json.load(f)
|
||||
exp_by_host = {r["hostName"]: r for r in expected["records"]}
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json={
|
||||
"start": int((now - timedelta(minutes=5)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
data = response.json()["data"]
|
||||
|
||||
# Shape/contract.
|
||||
assert data["total"] == len(expected["records"])
|
||||
assert len(data["records"]) == len(expected["records"])
|
||||
assert data["requiredMetricsCheck"]["missingMetrics"] == []
|
||||
assert data["endTimeBeforeRetention"] is False
|
||||
assert {r["hostName"] for r in data["records"]} == set(exp_by_host.keys())
|
||||
|
||||
for record in data["records"]:
|
||||
for field in (
|
||||
"hostName",
|
||||
"status",
|
||||
"cpu",
|
||||
"memory",
|
||||
"wait",
|
||||
"load15",
|
||||
"diskUsage",
|
||||
"meta",
|
||||
):
|
||||
assert field in record, f"missing {field} in {record!r}"
|
||||
|
||||
# Exact metric values.
|
||||
exp = exp_by_host[record["hostName"]]
|
||||
for field in ("cpu", "memory", "wait", "load15", "diskUsage"):
|
||||
assert compare_values(record[field], exp[field], 1e-9), f"{record['hostName']}.{field}: got {record[field]}, expected {exp[field]}"
|
||||
|
||||
|
||||
def test_hosts_missing_metrics(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
) -> None:
|
||||
"""Seed only system.cpu.time; assert other 3 required metrics flagged missing."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path("inframonitoring/hosts_missing_metrics.jsonl"),
|
||||
base_time=now - timedelta(minutes=4),
|
||||
)
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json={
|
||||
"start": int((now - timedelta(minutes=5)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
data = response.json()["data"]
|
||||
|
||||
assert set(data["requiredMetricsCheck"]["missingMetrics"]) == {
|
||||
"system.memory.usage",
|
||||
"system.cpu.load_average.15m",
|
||||
"system.filesystem.usage",
|
||||
}
|
||||
# Endpoint short-circuits when any required metric is missing:
|
||||
# records is empty and total=0 regardless of which hosts have partial data.
|
||||
# See pkg/modules/inframonitoring/implinframonitoring/module.go:84-89.
|
||||
assert data["records"] == []
|
||||
assert data["total"] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"expression,expected_hosts",
|
||||
[
|
||||
pytest.param(
|
||||
"host.name = 'prod-linux-1' AND os.type = 'linux'",
|
||||
{"prod-linux-1"},
|
||||
id="and",
|
||||
),
|
||||
pytest.param(
|
||||
"host.name IN ('prod-linux-1', 'prod-windows-1')",
|
||||
{"prod-linux-1", "prod-windows-1"},
|
||||
id="in",
|
||||
),
|
||||
pytest.param(
|
||||
"host.name NOT IN ('prod-linux-1', 'prod-windows-1')",
|
||||
{"dev-linux-1", "dev-windows-1"},
|
||||
id="not_in",
|
||||
),
|
||||
pytest.param(
|
||||
"host.name CONTAINS 'prod-'",
|
||||
{"prod-linux-1", "prod-windows-1"},
|
||||
id="contains",
|
||||
),
|
||||
pytest.param(
|
||||
"os.type = 'linux' AND host.name IN ('prod-linux-1', 'prod-windows-1')",
|
||||
{"prod-linux-1"},
|
||||
id="and_in",
|
||||
),
|
||||
pytest.param(
|
||||
"os.type = 'linux' AND host.name NOT IN ('prod-linux-1', 'prod-windows-1')",
|
||||
{"dev-linux-1"},
|
||||
id="and_not_in",
|
||||
),
|
||||
pytest.param(
|
||||
"os.type = 'linux' AND host.name CONTAINS 'prod-'",
|
||||
{"prod-linux-1"},
|
||||
id="and_contains",
|
||||
),
|
||||
pytest.param(
|
||||
"host.name IN ('prod-linux-1', 'prod-windows-1', 'dev-linux-1') AND host.name CONTAINS 'linux'",
|
||||
{"prod-linux-1", "dev-linux-1"},
|
||||
id="in_contains",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_hosts_filter(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
expression: str,
|
||||
expected_hosts: set,
|
||||
) -> None:
|
||||
"""Filter operators (=, IN, NOT IN, CONTAINS) and their AND-combinations
|
||||
return exactly the matching hosts, with undistorted per-host metric values."""
|
||||
# Every host in hosts_filter_dataset.jsonl carries the same sample pattern
|
||||
# as acc-h1 in hosts_value_accuracy.jsonl, so all filtered records must
|
||||
# resolve to these exact values (mirrors hosts_value_accuracy_expected.json
|
||||
# acc-h1).
|
||||
expected_values = {
|
||||
"cpu": 0.4444444444444445,
|
||||
"memory": 0.205,
|
||||
"wait": 0.027777777777777776,
|
||||
"load15": 1.525,
|
||||
"diskUsage": 0.48095238095238096,
|
||||
}
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path("inframonitoring/hosts_filter_dataset.jsonl"),
|
||||
base_time=now - timedelta(minutes=4),
|
||||
)
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json={
|
||||
"start": int((now - timedelta(minutes=5)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
"filter": {"expression": expression},
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
data = response.json()["data"]
|
||||
assert {r["hostName"] for r in data["records"]} == expected_hosts
|
||||
assert data["total"] == len(expected_hosts)
|
||||
|
||||
# Filtering must not distort per-host aggregation values.
|
||||
for record in data["records"]:
|
||||
for field in ("cpu", "memory", "wait", "load15", "diskUsage"):
|
||||
assert compare_values(record[field], expected_values[field], 1e-9), f"{record['hostName']}.{field}: got {record[field]}, expected {expected_values[field]}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"expression,err_substr",
|
||||
[
|
||||
pytest.param("host.namee = 'prod-linux-1'", "host.namee", id="bad_attr_name"),
|
||||
pytest.param("host.name =", None, id="trailing_op"),
|
||||
pytest.param("(host.name = 'prod-linux-1'", None, id="unclosed_paren"),
|
||||
# Cases dropped — parser is permissive and accepts these silently:
|
||||
# `host.name == 'x'` → treated as `=` (matches as if single `=`)
|
||||
# `host.name 'x'` → returns 200 with empty records
|
||||
# Tracked as a QB v5 parser gap; not enforced by this test.
|
||||
],
|
||||
)
|
||||
def test_hosts_filter_invalid(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
expression: str,
|
||||
err_substr,
|
||||
) -> None:
|
||||
"""Invalid filter expressions (typo'd attribute key, malformed grammar) return
|
||||
400 invalid_input with structured errors; bad attribute keys are named in them."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path("inframonitoring/hosts_filter_dataset.jsonl"),
|
||||
base_time=now - timedelta(minutes=4),
|
||||
)
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json={
|
||||
"start": int((now - timedelta(minutes=5)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
"filter": {"expression": expression},
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.BAD_REQUEST, f"expected 400, got {response.status_code}: {response.text}"
|
||||
body = response.json()
|
||||
assert body["status"] == "error"
|
||||
assert body["error"]["code"] == "invalid_input"
|
||||
assert len(body["error"]["errors"]) > 0
|
||||
if err_substr is not None:
|
||||
assert any(err_substr in e["message"] for e in body["error"]["errors"]), f"{err_substr!r} not surfaced: {body['error']['errors']!r}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"status,expected_hosts",
|
||||
[
|
||||
pytest.param("active", {"active-h1"}, id="active"),
|
||||
pytest.param("inactive", {"inactive-h1"}, id="inactive"),
|
||||
pytest.param(None, {"active-h1", "inactive-h1"}, id="unset"),
|
||||
],
|
||||
)
|
||||
def test_hosts_filter_by_status(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
status,
|
||||
expected_hosts: set,
|
||||
) -> None:
|
||||
"""filterByStatus subsets hosts and per-record activeHostCount/inactiveHostCount
|
||||
track each host's status. Omitting filterByStatus returns all hosts.
|
||||
"""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path("inframonitoring/hosts_status.jsonl"),
|
||||
base_time=now - timedelta(minutes=24),
|
||||
)
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
body = {
|
||||
"start": int((now - timedelta(minutes=30)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
}
|
||||
if status is not None:
|
||||
body["filter"] = {"filterByStatus": status}
|
||||
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json=body,
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
data = response.json()["data"]
|
||||
|
||||
assert {r["hostName"] for r in data["records"]} == expected_hosts
|
||||
assert data["total"] == len(expected_hosts)
|
||||
|
||||
if status is not None:
|
||||
for r in data["records"]:
|
||||
assert r["status"] == status
|
||||
|
||||
for r in data["records"]:
|
||||
if r["status"] == "active":
|
||||
assert r["activeHostCount"] == 1
|
||||
assert r["inactiveHostCount"] == 0
|
||||
else:
|
||||
assert r["status"] == "inactive"
|
||||
assert r["activeHostCount"] == 0
|
||||
assert r["inactiveHostCount"] == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"dataset,seed_age_min,window_min,group_key,expected_counts,expected_values",
|
||||
[
|
||||
# groupBy=[host.name]: one record per distinct host. Per-host status is
|
||||
# not pinned by the dataset, so expected counts are None (assert
|
||||
# active+inactive == 1 instead).
|
||||
pytest.param(
|
||||
"hosts_filter_dataset.jsonl",
|
||||
4,
|
||||
5,
|
||||
"host.name",
|
||||
{
|
||||
"prod-linux-1": None,
|
||||
"prod-windows-1": None,
|
||||
"dev-linux-1": None,
|
||||
"dev-windows-1": None,
|
||||
},
|
||||
None,
|
||||
id="host_name",
|
||||
),
|
||||
# groupBy=[os.type]: aggregates active/inactive counts and metric values
|
||||
# per os.type. Seed had linux: 2 active + 1 inactive, windows: 1 active
|
||||
# + 2 inactive. Aggregated metric values differ between groups because
|
||||
# active hosts (last sample step-floored out) and inactive hosts (all 3
|
||||
# samples averaged) contribute slightly different per-host
|
||||
# contributions to the space-aggregated formula.
|
||||
pytest.param(
|
||||
"hosts_groupby_os_type.jsonl",
|
||||
24,
|
||||
30,
|
||||
"os.type",
|
||||
{"linux": (2, 1), "windows": (1, 2)},
|
||||
{
|
||||
"linux": {
|
||||
"cpu": 0.3333333333333333,
|
||||
"memory": 0.25892857142857145,
|
||||
"wait": 0,
|
||||
"load15": 2.15,
|
||||
"diskUsage": 0.5071428571428571,
|
||||
},
|
||||
"windows": {
|
||||
"cpu": 0.33333333333333337,
|
||||
"memory": 0.2609375,
|
||||
"wait": 0,
|
||||
"load15": 2.47,
|
||||
"diskUsage": 0.50875,
|
||||
},
|
||||
},
|
||||
id="os_type",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_hosts_groupby( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
dataset: str,
|
||||
seed_age_min: int,
|
||||
window_min: int,
|
||||
group_key: str,
|
||||
expected_counts: dict,
|
||||
expected_values: dict,
|
||||
) -> None:
|
||||
"""groupBy returns one record per distinct group with aggregated
|
||||
active/inactive counts and metric values. hostName is populated only when
|
||||
grouping by host.name (hosts.go:144-160 list-vs-grouped branches)."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path(f"inframonitoring/{dataset}"),
|
||||
base_time=now - timedelta(minutes=seed_age_min),
|
||||
)
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json={
|
||||
"start": int((now - timedelta(minutes=window_min)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
"groupBy": [
|
||||
{
|
||||
"name": group_key,
|
||||
"fieldDataType": "string",
|
||||
"fieldContext": "resource",
|
||||
},
|
||||
],
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
data = response.json()["data"]
|
||||
assert data["total"] == len(expected_counts)
|
||||
|
||||
group_of = lambda r: r["hostName"] if group_key == "host.name" else r["meta"][group_key] # noqa: E731 # pylint: disable=unnecessary-lambda-assignment
|
||||
by_group = {group_of(r): r for r in data["records"]}
|
||||
assert set(by_group.keys()) == set(expected_counts.keys())
|
||||
|
||||
for group, rec in by_group.items():
|
||||
counts = expected_counts[group]
|
||||
if counts is not None:
|
||||
assert (rec["activeHostCount"], rec["inactiveHostCount"]) == counts, f"{group}: got ({rec['activeHostCount']}, {rec['inactiveHostCount']}), expected {counts}"
|
||||
else:
|
||||
assert rec["activeHostCount"] + rec["inactiveHostCount"] == 1
|
||||
|
||||
# hostName is populated per host when grouping by host.name, and empty
|
||||
# when host.name is NOT in groupBy.
|
||||
assert rec["hostName"] == (group if group_key == "host.name" else "")
|
||||
|
||||
if expected_values is not None:
|
||||
for field in ("cpu", "memory", "wait", "load15", "diskUsage"):
|
||||
assert compare_values(rec[field], expected_values[group][field], 1e-9), f"{group}.{field}: got {rec[field]}, expected {expected_values[group][field]}"
|
||||
|
||||
|
||||
def test_hosts_pagination(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
) -> None:
|
||||
"""Pagination: per-page len matches min(limit, total-offset), total invariant,
|
||||
pages cover the full set with no overlap. The final offset is beyond total:
|
||||
it returns empty records while total still reflects dataset size.
|
||||
"""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path("inframonitoring/hosts_pagination.jsonl"),
|
||||
base_time=now - timedelta(minutes=4),
|
||||
)
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
K, limit = 7, 3
|
||||
seen_hosts: list[str] = []
|
||||
seen_totals: set[int] = set()
|
||||
|
||||
for offset in (0, 3, 6, K + 5):
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json={
|
||||
"start": int((now - timedelta(minutes=5)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
data = response.json()["data"]
|
||||
seen_totals.add(data["total"])
|
||||
expected_len = max(0, min(limit, K - offset))
|
||||
assert len(data["records"]) == expected_len, f"offset={offset}: expected {expected_len} records, got {len(data['records'])}"
|
||||
seen_hosts.extend(r["hostName"] for r in data["records"])
|
||||
|
||||
assert seen_totals == {K}
|
||||
assert len(seen_hosts) == K
|
||||
assert set(seen_hosts) == {f"page-h{i}" for i in range(1, K + 1)}
|
||||
|
||||
|
||||
# orderBy keys use snake_case (inframonitoringtypes/hosts_constants.go:26-30).
|
||||
# Note: response uses camelCase (diskUsage) but request uses disk_usage.
|
||||
# host.name sorts via the metadata-name branch (hosts.go:218-219,
|
||||
# PaginateMetadataByName) and is only allowed when groupBy is empty.
|
||||
@pytest.mark.parametrize(
|
||||
"column,record_field",
|
||||
[
|
||||
pytest.param("cpu", "cpu", id="cpu"),
|
||||
pytest.param("memory", "memory", id="memory"),
|
||||
pytest.param("wait", "wait", id="wait"),
|
||||
pytest.param("load15", "load15", id="load15"),
|
||||
pytest.param("disk_usage", "diskUsage", id="disk_usage"),
|
||||
pytest.param("host.name", "hostName", id="host_name"),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("direction", ["asc", "desc"])
|
||||
def test_hosts_orderby( # pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
insert_metrics,
|
||||
column: str,
|
||||
record_field: str,
|
||||
direction: str,
|
||||
) -> None:
|
||||
"""Every orderBy column x direction: total/len stay K (invariant under
|
||||
sort) and records come back sorted by the requested column. Hosts have
|
||||
staggered timestamps (simulating real-world emit drift)."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
insert_metrics(
|
||||
Metrics.load_from_file(
|
||||
get_testdata_file_path("inframonitoring/hosts_orderby.jsonl"),
|
||||
base_time=now - timedelta(minutes=4),
|
||||
)
|
||||
)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
K = 5
|
||||
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json={
|
||||
"start": int((now - timedelta(minutes=5)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
"orderBy": {"key": {"name": column}, "direction": direction},
|
||||
# Guards against hosts seeded by other tests in the shared backend.
|
||||
"filter": {"expression": "host.name CONTAINS 'order-'"},
|
||||
},
|
||||
timeout=5,
|
||||
)
|
||||
ctx = f"orderBy={column} {direction}"
|
||||
assert response.status_code == HTTPStatus.OK, f"{ctx}: {response.text}"
|
||||
data = response.json()["data"]
|
||||
assert data["total"] == K, f"{ctx}: total={data['total']}"
|
||||
assert len(data["records"]) == K, f"{ctx}: len(records)={len(data['records'])}"
|
||||
|
||||
values = [r[record_field] for r in data["records"]]
|
||||
expected = sorted(values, reverse=(direction == "desc"))
|
||||
assert values == expected, f"{ctx} not sorted; got {values}"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"payload_override,err_substr",
|
||||
[
|
||||
pytest.param({"start": 0}, "start must be greater than 0", id="start_zero"),
|
||||
pytest.param({"start": -1}, "start must be greater than 0", id="start_negative"),
|
||||
pytest.param({"end": 0}, "end must be greater than 0", id="end_zero"),
|
||||
pytest.param({"end": -1}, "end must be greater than 0", id="end_negative"),
|
||||
pytest.param({"_use_end_eq_start": True}, "must be less than end", id="start_equals_end"),
|
||||
pytest.param({"_use_start_gt_end": True}, "must be less than end", id="start_greater_than_end"),
|
||||
pytest.param({"limit": 0}, "limit must be between", id="limit_zero"),
|
||||
pytest.param({"limit": 5001}, "limit must be between", id="limit_too_large"),
|
||||
pytest.param({"offset": -1}, "offset cannot be negative", id="offset_negative"),
|
||||
pytest.param(
|
||||
{"filter": {"filterByStatus": "bogus"}},
|
||||
"invalid filter by status",
|
||||
id="filter_by_status_invalid",
|
||||
),
|
||||
pytest.param(
|
||||
{"orderBy": {"key": {"name": "bogus_col"}, "direction": "desc"}},
|
||||
"invalid order by key",
|
||||
id="orderby_invalid_key",
|
||||
),
|
||||
pytest.param(
|
||||
{"orderBy": {"key": {"name": "cpu"}, "direction": "up"}},
|
||||
"invalid order by direction",
|
||||
id="orderby_invalid_direction",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"orderBy": {"key": {"name": "host.name"}, "direction": "desc"},
|
||||
"groupBy": [
|
||||
{
|
||||
"name": "host.name",
|
||||
"fieldDataType": "string",
|
||||
"fieldContext": "resource",
|
||||
}
|
||||
],
|
||||
},
|
||||
"is only allowed when groupBy is empty",
|
||||
id="orderby_hostname_with_groupby",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_hosts_validation_errors(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: None, # pylint: disable=unused-argument
|
||||
get_token,
|
||||
payload_override: dict,
|
||||
err_substr: str,
|
||||
) -> None:
|
||||
"""All PostableHosts.Validate() rules reject with 400 + descriptive error.
|
||||
See pkg/types/inframonitoringtypes/hosts.go:53-108."""
|
||||
now = datetime.now(tz=UTC).replace(microsecond=0)
|
||||
body: dict = {
|
||||
"start": int((now - timedelta(minutes=5)).timestamp() * 1000),
|
||||
"end": int(now.timestamp() * 1000),
|
||||
"limit": 50,
|
||||
}
|
||||
if payload_override.pop("_use_end_eq_start", False):
|
||||
body["end"] = body["start"]
|
||||
if payload_override.pop("_use_start_gt_end", False):
|
||||
body["start"] = body["end"] + 1
|
||||
body.update(payload_override)
|
||||
|
||||
token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
response = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ENDPOINT),
|
||||
headers={"authorization": f"Bearer {token}"},
|
||||
json=body,
|
||||
timeout=5,
|
||||
)
|
||||
assert response.status_code == HTTPStatus.BAD_REQUEST, response.text
|
||||
error = response.json()["error"]
|
||||
assert error["code"] == "invalid_input"
|
||||
assert err_substr.lower() in error["message"].lower(), f"expected substring {err_substr!r} not found in: {error['message']!r}"
|
||||
Reference in New Issue
Block a user