Compare commits

..

1 Commits

Author SHA1 Message Date
SagarRajput-7
ebaa44c743 fix(settings): disable tabPane animation to prevent stale pane mounts 2026-06-16 15:16:06 +05:30
115 changed files with 1099 additions and 31392 deletions

View File

@@ -1357,14 +1357,6 @@ components:
- appservice
- containerapp
- aks
- sqldatabase
- sqldatabasemi
- mysqlflexibleserver
- postgresqlflexibleserver
- mongodb
- cosmosdb
- cassandradb
- redis
type: string
CloudintegrationtypesServiceMetadata:
properties:

View File

@@ -291,8 +291,6 @@
// Prevents the usage of specific antd components in favor of our lib
"signoz/no-signozhq-ui-barrel": "error",
// Forces subpath imports (@signozhq/ui/<component>) instead of the eagerly-loaded barrel
"signoz/no-css-module-bracket-access": "warn",
// Prevents bracket access on CSS modules (styles['kebab-case']) which fails with camelCaseOnly config
"no-restricted-globals": [
"error",
{

View File

@@ -2,33 +2,9 @@
const path = require('path');
module.exports = {
plugins: [
path.join(__dirname, 'stylelint-rules/no-unsupported-asset-url.js'),
path.join(__dirname, 'stylelint-rules/css-modules/no-deep-nesting.js'),
path.join(__dirname, 'stylelint-rules/css-modules/no-id-selectors.js'),
path.join(
__dirname,
'stylelint-rules/css-modules/no-bare-element-selectors.js',
),
path.join(__dirname, 'stylelint-rules/css-modules/prefer-css-variables.js'),
path.join(__dirname, 'stylelint-rules/css-modules/class-name-pattern.js'),
],
plugins: [path.join(__dirname, 'stylelint-rules/no-unsupported-asset-url.js')],
customSyntax: 'postcss-scss',
rules: {
// Applies to all SCSS files
'local/no-unsupported-asset-url': true,
},
overrides: [
{
// CSS module-specific rules
files: ['**/*.module.scss'],
rules: {
'local/no-deep-nesting': [true, { severity: 'warning' }],
'local/no-id-selectors': true,
'local/no-bare-element-selectors': true,
'local/prefer-css-variables': [true, { severity: 'warning' }],
'local/class-name-pattern': [true, { severity: 'warning' }],
},
},
],
};

View File

@@ -23,8 +23,6 @@ You are operating within a constrained context window and strict system prompts.
- Always add data-testid or testId (if supported) to critical/behavioral components like inputs, buttons, etc...
- When creating test, these IDs should be used instead of finding by role.
- Never create barrel files.
- When writing new css, prefer CSS Modules
- Use ./docs/css-modules-guide.md as reference on how to write good CSS Modules.
3. FORCED VERIFICATION: Your internal tools mark file writes as successful even if the code does not compile. You are FORBIDDEN from reporting a task as complete until you have:
- Run `pnpm tsgo --noEmit`

View File

@@ -1,513 +0,0 @@
# CSS Modules Guide
## Checklist Before Committing
- [ ] All class names use camelCase in CSS
- [ ] State classes use `is-`/`has-` prefix (e.g., `isActive`, `hasError`)
- [ ] No bracket access (`styles['...']`) in JS unless verified
- [ ] No dynamic class lookup - use explicit variant maps instead
- [ ] No deep class nesting (max 3 class levels; pseudo-classes/elements and parent-reference selectors like `&.active`, `&#bar` are not counted)
- [ ] No hardcoded colors - use `--l1/l2/l3-*` semantic tokens (not `--bg-*` primitives)
- [ ] No magic numbers - use `--spacing-*` tokens
- [ ] Typography uses `--periscope-font-size-*` or `--font-size-*` tokens
- [ ] @signozhq/ui overrides use CSS variables, not direct class overrides
- [ ] Global escapes only for third-party overrides
- [ ] No ID selectors
- [ ] No bare element selectors
- [ ] Keyframes use `:local(@keyframes name)` to avoid global collisions
## Config (vite.config.ts)
```ts
css: {
modules: {
localsConvention: 'camelCaseOnly',
},
}
```
**Critical:** `camelCaseOnly` exports ONLY camelCase keys. Original kebab-case NOT accessible.
## Quick Reference
| CSS Class | JS Access | Works? | Preferred? |
|-----------|-----------|--------|----------------------------|
| `.alertHistory` | `styles.alertHistory` | Yes | Yes |
| `.alert-history` | `styles.alertHistory` | Yes | No, use `.alertHistory` |
| `.alert-history` | `styles['alert-history']` | NO - undefined | Never, use `.alertHistory` |
## Bad Patterns
### Class Naming
```scss
// BAD: Bracket access won't work
.my-class { }
// Then in JS: styles['my-class'] -> undefined
// BAD: Collision - both become same key
.alertHistory { }
.alert-history { } // -> styles.alertHistory (conflicts)
// BAD: Underscore inconsistency
.my_class { } // -> styles.myClass (confusing)
// GOOD: Direct camelCase
.alertHistory { }
.statsCard { }
// GOOD: State classes with is-/has- prefix
.isDisabled { }
.isActive { }
.hasError { }
.isLoading { }
```
### Nesting
```scss
// BAD: Deep nesting - specificity wars, hard to override
.container {
.wrapper {
.inner {
.content { }
}
}
}
// BAD: Nesting creates separate classes you might not expect
.button {
.icon { } // -> styles.icon (separate class, not scoped under .button)
}
// GOOD: Flat structure
.container { }
.containerWrapper { }
.containerContent { }
// GOOD: Nesting only for pseudo/states
.button {
&:hover { }
&:disabled { }
&::before { }
}
```
### Global Escapes
```scss
// BAD: Overusing global
:global {
.everything { }
.in-here { }
.is-global { }
}
// BAD: Global without necessity
:global(.myComponent) { } // defeats purpose of modules
// GOOD: Targeted global for third-party overrides
.container {
:global(.ant-modal-content) {
padding: 0;
}
}
```
### Selectors
```scss
// BAD: ID selectors - not reusable
#myComponent { }
// BAD: Element selectors without scope
div { } // affects ALL divs in component
// BAD: Complex selectors
.container > div + span ~ p { }
// GOOD: Class-only selectors
.container { }
.title { }
```
### Variables & Values
```scss
// BAD: Hardcoded colors
.button {
background: #1890ff;
color: white;
}
// BAD: Magic numbers
.container {
padding: 17px;
margin-left: 43px;
}
// GOOD: Semantic tokens (theme-aware)
.button {
background: var(--primary-background);
color: var(--primary-foreground);
}
.card {
background: var(--l2-background);
color: var(--l2-foreground);
}
// GOOD: Spacing system
.container {
padding: var(--spacing-4);
margin-left: var(--spacing-5);
}
```
## Design Tokens (@signozhq/design-tokens)
Prefer semantic tokens over hardcoded values.
You can read the ./node_modules/@signozhq/design-tokens/dist/style.css to find complete list of available tokens.
### Spacing
```scss
// Spacing scale (index -> px):
// --spacing-0=0 --spacing-1=2 --spacing-2=4 --spacing-3=6 --spacing-4=8
// --spacing-5=10 --spacing-6=12 --spacing-7=14 --spacing-8=16 --spacing-10=20
// --spacing-12=24 --spacing-16=32 --spacing-20=40 --spacing-24=48 --spacing-32=64
// --spacing-40=80 --spacing-48=96 --spacing-56=112 --spacing-64=128
// (index != px; --spacing-2 is 4px, not 2px)
.container {
padding: var(--spacing-4); // 8px
gap: var(--spacing-6); // 12px
margin-bottom: var(--spacing-8); // 16px
}
// Also available: --padding-* and --margin-* (rem-based)
// --padding-1 = 0.25rem, --padding-4 = 1rem, etc.
```
### Typography
```scss
// Font sizes (preferred)
.title {
font-size: var(--periscope-font-size-large); // 18px
font-size: var(--periscope-font-size-medium); // 16px
font-size: var(--periscope-font-size-base); // 13px
font-size: var(--periscope-font-size-small); // 11px
}
// Alternative scale (rem-based)
.heading {
font-size: var(--font-size-xl); // 1.25rem
font-size: var(--font-size-lg); // 1.125rem
font-size: var(--font-size-base); // 1rem
font-size: var(--font-size-sm); // 0.875rem
}
// Font weights
.bold {
font-weight: var(--font-weight-semibold); // 600
font-weight: var(--font-weight-medium); // 500
font-weight: var(--font-weight-normal); // 400
}
// Line heights
.text {
line-height: var(--line-height-20); // 20px
line-height: var(--line-height-24); // 24px
}
```
### Colors (Prefer Semantic Tokens)
Use L1/L2/L3 semantic tokens - they handle light/dark theme automatically.
```scss
// BAD: Primitive tokens (fixed value across themes, won't swap on theme change)
.card {
background: var(--bg-ink-400);
color: var(--text-vanilla-100);
}
// GOOD: L1/L2/L3 tokens (theme-aware - swap automatically light/dark)
.card {
background: var(--l1-background); // base layer
color: var(--l1-foreground); // primary text
}
.panel {
background: var(--l2-background); // elevated surface
color: var(--l2-foreground); // secondary text
border-color: var(--l2-border);
}
.nested {
background: var(--l3-background); // nested/inset
color: var(--l3-foreground); // tertiary text
}
// Hover states
.card:hover {
background: var(--l1-background-hover);
color: var(--l1-foreground-hover);
}
// Semantic action colors (also theme-aware)
.primary {
background: var(--primary-background);
color: var(--primary-foreground);
}
.danger {
background: var(--danger-background);
color: var(--danger-foreground);
}
.success {
background: var(--success-background);
color: var(--success-foreground);
}
.warning {
background: var(--warning-background);
color: var(--warning-foreground);
}
// Accent colors (for highlights, badges, etc.)
.accent {
background: var(--accent-primary); // robin blue
background: var(--accent-forest); // green
background: var(--accent-cherry); // red
background: var(--accent-amber); // yellow
}
```
**Token hierarchy:**
- Primitive tokens (`--bg-*`, `--text-*`, etc.) have fixed values across themes.
- Semantic tokens (L1/L2/L3, `--primary-*`, `--danger-*`, etc.) automatically swap based on theme.
- L1 = base/root layer
- L2 = elevated surfaces (cards, panels)
- L3 = nested/inset elements
## Overriding @signozhq/ui Components
Components expose CSS variables for customization.
You can ensure they exist by looking at ./node_modules/@signozhq/ui/dist.
Never write a override without confirm it exists.
Override via:
### Method 1: CSS Variables (Preferred)
Each component exposes `--<component>-<property>` variables:
```scss
// Override Button
.customButton {
--button-background: var(--success-background);
--button-border-radius: var(--radius-2);
--button-padding: var(--spacing-4) var(--spacing-8);
--button-font-size: var(--periscope-font-size-base);
}
// Override Input
.customInput {
--input-height: 2.5rem;
--input-border-color: var(--l2-border);
--input-padding: var(--spacing-2) var(--spacing-6);
--input-placeholder-color: var(--l3-foreground);
}
// Override nested parts
.customInput {
--input-prefix-padding: 0 var(--spacing-4) 0 var(--spacing-6);
--input-suffix-color: var(--accent-primary);
}
```
### Method 2: Data Attributes
Components use data attributes for variants/states. Target them for state-specific overrides:
```scss
// Target variant
.wrapper :global([data-variant="outlined"]) {
--button-border-color: var(--accent-primary);
}
// Target size
.wrapper :global([data-size="sm"]) {
--button-font-size: var(--periscope-font-size-small);
}
// Target color
.wrapper :global([data-color="destructive"]) {
--button-background: var(--danger-background);
}
// Target state (Radix patterns)
.popover :global([data-state="open"]) {
opacity: 1;
}
.tooltip :global([data-side="top"]) {
margin-bottom: var(--spacing-2);
}
```
### Common Component CSS Variables
**Button:**
- `--button-background`, `--button-border-radius`, `--button-padding`
- `--button-font-size`, `--button-height`, `--button-gap`
- `--button-hover-background`, `--button-disabled-opacity`
**Input:**
- `--input-height`, `--input-border-color`, `--input-background`
- `--input-padding`, `--input-font-size`, `--input-placeholder-color`
- `--input-focus-outline-color`, `--input-hover-border-color`
- `--input-prefix-*`, `--input-suffix-*` for adornments
**General pattern:** `--<component>-<property>` or `--<component>-<state>-<property>`
## Good Patterns
### Structure
```scss
// Flat, descriptive, component-scoped
.alertHistory { }
.alertHistoryHeader { }
.alertHistoryContent { }
.alertHistoryFooter { }
// State modifiers as separate classes
.alertHistory { }
.alertHistoryLoading { }
.alertHistoryEmpty { }
.alertHistoryError { }
```
### Composition
```scss
// GOOD: Composing styles
.baseButton {
padding: var(--spacing-2) var(--spacing-4);
border-radius: var(--radius-2);
}
.primaryButton {
composes: baseButton;
background: var(--primary-background);
}
```
### Pseudo Elements
```scss
.button {
// States
&:hover { opacity: 0.9; }
&:focus { outline: 2px solid var(--ring); outline-offset: 2px; }
&:disabled { opacity: 0.5; cursor: not-allowed; }
// Pseudo elements
&::before { content: ''; }
&::after { content: ''; }
}
```
### Media Queries
```scss
.container {
display: flex;
flex-direction: column;
@media (min-width: 768px) {
flex-direction: row;
}
}
```
### Keyframes (Local Scoping)
Without `:local()`, keyframe names are global and can clash across modules:
```scss
// BAD: Global keyframe - can conflict with other modules
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
// GOOD: Locally scoped keyframe
:local(@keyframes fadeIn) {
from { opacity: 0; }
to { opacity: 1; }
}
.modal {
animation: fadeIn 200ms ease;
}
```
## JS Import Patterns
```tsx
// GOOD
import styles from './Component.module.scss';
<div className={styles.container}>
<span className={styles.title}>Title</span>
</div>
// GOOD: Conditional classes
<div className={`${styles.button} ${isActive ? styles.buttonActive : ''}`}>
// GOOD: With clsx/classnames
<div className={clsx(styles.button, { [styles.buttonActive]: isActive })}>
// BAD: Bracket access (may be undefined)
<div className={styles['button-active']}> // undefined if CSS has .button-active
// BAD: String interpolation for class names
<div className={`${styles.button}-active`}> // won't work
// BAD: Dynamic class lookup - can't be statically analyzed
const cls = styles[`variant${props.type}`]; // Vite can't tree-shake or type-check
// GOOD: Explicit map for dynamic variants
const variantMap = {
primary: styles.variantPrimary,
secondary: styles.variantSecondary,
ghost: styles.variantGhost,
};
const cls = variantMap[props.type];
```
## Lint Rules
### JS/TS (oxlint)
| Rule | Severity | Catches |
|------|----------|---------|
| `signoz/no-css-module-bracket-access` | warn | `styles['kebab-case']`, dynamic access |
### CSS/SCSS (stylelint)
| Rule | Severity | Catches |
|------|----------|---------|
| `local/no-deep-nesting` | warning | class nesting >3 levels (pseudo-classes/elements and parent-reference selectors `&.foo`, `&#bar` not counted; configurable via `maxDepth` secondary option) |
| `local/no-id-selectors` | error | `#id` selectors |
| `local/no-bare-element-selectors` | error | root-level `div`, `span` etc |
| `local/prefer-css-variables` | warning | hardcoded colors |
| `local/class-name-pattern` | warning | kebab-case, snake_case, PascalCase |
Run: `pnpm lint:styles` to check CSS modules.

View File

@@ -45,8 +45,8 @@
"@dnd-kit/utilities": "3.2.2",
"@grafana/data": "^11.6.14",
"@monaco-editor/react": "^4.7.0",
"@sentry/react": "10.57.0",
"@sentry/vite-plugin": "5.3.0",
"@sentry/react": "8.41.0",
"@sentry/vite-plugin": "2.22.6",
"@signozhq/design-tokens": "2.1.4",
"@signozhq/icons": "0.4.0",
"@signozhq/resizable": "0.0.2",
@@ -192,9 +192,9 @@
"lint-staged": "^17.0.4",
"msw": "1.3.2",
"orval": "8.9.1",
"oxfmt": "0.54.0",
"oxlint": "1.69.0",
"oxlint-tsgolint": "0.23.0",
"oxfmt": "0.47.0",
"oxlint": "1.62.0",
"oxlint-tsgolint": "0.22.1",
"postcss": "8.5.14",
"postcss-scss": "4.0.9",
"react-resizable": "3.0.4",

View File

@@ -1,144 +0,0 @@
/**
* Rule: no-css-module-bracket-access
*
* Prevents bracket access on CSS module imports that may fail with camelCaseOnly config.
*
* With Vite's `localsConvention: 'camelCaseOnly'`, kebab-case class names are
* converted to camelCase and the original key is NOT exported.
*
* This rule catches patterns like:
* styles['my-class'] // BAD - undefined if CSS has .my-class
* styles['myClass'] // OK but prefer dot notation
* styles.myClass // GOOD
*
* Catches:
* - Bracket access with kebab-case strings (always fails)
* - Bracket access with any string literal (warn - prefer dot notation)
* - Dynamic bracket access (warn - risky)
*/
const CSS_MODULE_IMPORT_NAMES = new Set([
'styles',
'classes',
'css',
'classNames',
]);
function looksLikeCssModuleImport(name) {
// Common patterns: styles, componentStyles, alertHistoryStyles
return (
CSS_MODULE_IMPORT_NAMES.has(name) ||
name.endsWith('Styles') ||
name.endsWith('Classes') ||
name.endsWith('Css')
);
}
function isKebabCase(str) {
return str.includes('-');
}
function isSnakeCase(str) {
return str.includes('_');
}
export default {
create(context) {
return {
MemberExpression(node) {
// Only check bracket notation: styles['...']
if (!node.computed) {
return;
}
const object = node.object;
if (object.type !== 'Identifier') {
return;
}
// Check if this looks like a CSS module import
if (!looksLikeCssModuleImport(object.name)) {
return;
}
const property = node.property;
// Dynamic access: styles[variable]
if (property.type === 'Identifier') {
context.report({
node,
message: `Dynamic CSS module access '${object.name}[${property.name}]' is risky. With 'camelCaseOnly' config, kebab-case keys don't exist. Use dot notation or verify the key exists.`,
});
return;
}
// Template literal: styles[\`...\`]
if (property.type === 'TemplateLiteral') {
context.report({
node,
message: `Template literal CSS module access is risky. With 'camelCaseOnly' config, kebab-case keys don't exist. Prefer dot notation.`,
});
return;
}
// Numeric / boolean / null literal: styles[0]. Not a class lookup; ignore.
if (property.type === 'Literal' && typeof property.value !== 'string') {
return;
}
// String literal: styles['...']
if (property.type === 'Literal' && typeof property.value === 'string') {
const className = property.value;
// Kebab-case will definitely fail
if (isKebabCase(className)) {
context.report({
node,
message: `CSS module class '${className}' uses kebab-case which won't work with 'camelCaseOnly' config. Use '${object.name}.${toCamelCase(className)}' instead.`,
});
return;
}
// Snake_case is suspicious
if (isSnakeCase(className)) {
context.report({
node,
message: `CSS module class '${className}' uses snake_case which may not work as expected. Prefer camelCase: '${object.name}.${toCamelCase(className)}'.`,
});
return;
}
// Valid camelCase but using bracket notation - prefer dot
if (/^[a-z][a-zA-Z0-9]*$/.test(className)) {
context.report({
node,
message: `Prefer dot notation: '${object.name}.${className}' instead of '${object.name}['${className}']'.`,
});
}
return;
}
// Catch-all for other dynamic expressions:
// styles['prefix' + suffix] (BinaryExpression)
// styles[isActive && 'foo'] (LogicalExpression)
// styles[isActive ? 'a' : 'b'] (ConditionalExpression)
// styles[fn()] (CallExpression)
context.report({
node,
message: `Dynamic CSS module access on '${object.name}' is risky. With 'camelCaseOnly' config, kebab-case keys don't exist. Use dot notation or verify each key resolves to an exported camelCase class.`,
});
},
};
},
};
function toCamelCase(str) {
return str
.split(/[-_]/)
.map((part, i) =>
i === 0
? part.toLowerCase()
: part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),
)
.join('');
}

View File

@@ -11,7 +11,6 @@ import noUnsupportedAssetPattern from './rules/no-unsupported-asset-pattern.mjs'
import noRawAbsolutePath from './rules/no-raw-absolute-path.mjs';
import noAntdComponents from './rules/no-antd-components.mjs';
import noSignozhqUiBarrel from './rules/no-signozhq-ui-barrel.mjs';
import noCssModuleBracketAccess from './rules/no-css-module-bracket-access.mjs';
export default {
meta: {
@@ -24,6 +23,5 @@ export default {
'no-raw-absolute-path': noRawAbsolutePath,
'no-antd-components': noAntdComponents,
'no-signozhq-ui-barrel': noSignozhqUiBarrel,
'no-css-module-bracket-access': noCssModuleBracketAccess,
},
};

696
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -64,17 +64,10 @@ export const TraceDetail = Loadable(
),
);
export const TraceDetailOldRedirect = Loadable(
() =>
import(
/* webpackChunkName: "TraceDetailOldRedirect" */ 'pages/TraceDetailOldRedirect/index'
),
);
export const TraceDetailV3 = Loadable(
() =>
import(
/* webpackChunkName: "TraceDetailV3 Page" */ 'pages/TraceDetailsV3/index'
/* webpackChunkName: "TraceDetailV3 Page" */ 'pages/TraceDetailV3Page/index'
),
);

View File

@@ -47,7 +47,7 @@ import {
SomethingWentWrong,
StatusPage,
SupportPage,
TraceDetailOldRedirect,
TraceDetail,
TraceDetailV3,
TraceFilter,
TracesExplorer,
@@ -139,11 +139,13 @@ const routes: AppRoutes[] = [
exact: true,
key: 'LOGS_SAVE_VIEWS',
},
// Legacy /trace-old/:id redirects to the current /trace/:id view.
// V3 trace details is gated until release: /trace serves V2 (public),
// /trace-old serves V3 (URL-only access). Flip the two `component`
// values back to release V3.
{
path: ROUTES.TRACE_DETAIL_OLD,
exact: true,
component: TraceDetailOldRedirect,
component: TraceDetail,
isPrivate: true,
key: 'TRACE_DETAIL_OLD',
},

View File

@@ -55,9 +55,6 @@ import type {
ThreadDetailResponseDTO,
ThreadListResponseDTO,
ThreadSummaryDTO,
ChipDTO,
ChipsResponseDTO,
PageTypeDTO,
ToolCallEventDTO,
ToolResultEventDTO,
} from './sigNozAIAssistantAPI.schemas';
@@ -544,19 +541,3 @@ export async function submitFeedback(
comment: comment ?? null,
});
}
// ---------------------------------------------------------------------------
// Contextual empty-state chips
// GET /api/v1/assistant/empty-state/chips?page_type=… → { chips }
// ---------------------------------------------------------------------------
export async function getEmptyStateChips(
pageType: PageTypeDTO,
signal?: AbortSignal,
): Promise<ChipDTO[]> {
const response = await AIAssistantInstance.get<ChipsResponseDTO>(
'/empty-state/chips',
{ params: { page_type: pageType }, signal },
);
return response.data.chips;
}

View File

@@ -26,7 +26,6 @@ import type {
CancelApiV1AssistantCancelPostHeaders,
CancelRequestDTO,
CancelResponseDTO,
ChipsResponseDTO,
ClarifyApiV1AssistantClarifyPostHeaders,
ClarifyRequestDTO,
ClarifyResponseDTO,
@@ -40,11 +39,8 @@ import type {
ErrorResponseDTO,
FeedbackRequestDTO,
FeedbackResponseDTO,
GetChipsApiV1AssistantEmptyStateChipsGetHeaders,
GetChipsApiV1AssistantEmptyStateChipsGetParams,
GetThreadApiV1AssistantThreadsThreadIdGetHeaders,
GetThreadApiV1AssistantThreadsThreadIdGetPathParameters,
GetUsageApiV1AssistantUsageGetHeaders,
HTTPValidationErrorDTO,
HealthResponseDTO,
ListThreadsApiV1AssistantThreadsGetHeaders,
@@ -69,89 +65,93 @@ import type {
UpdateThreadApiV1AssistantThreadsThreadIdPatchHeaders,
UpdateThreadApiV1AssistantThreadsThreadIdPatchPathParameters,
UpdateThreadRequestDTO,
UsageResponseDTO,
} from './sigNozAIAssistantAPI.schemas';
import { GeneratedAPIInstance } from '../generatedAPIInstance';
import {
GeneratedAPIInstance,
getGeneratedAPIQueryKeyHeaders,
} from '../generatedAPIInstance';
import type { ErrorType, BodyType } from '../generatedAPIInstance';
/**
* @summary Healthz
* @summary Health
*/
export const healthzHealthzGet = (signal?: AbortSignal) => {
export const healthHealthGet = (signal?: AbortSignal) => {
return GeneratedAPIInstance<HealthResponseDTO>({
url: `/healthz`,
url: `/health`,
method: 'GET',
signal,
});
};
export const getHealthzHealthzGetQueryKey = () => {
return [`/healthz`] as const;
export const getHealthHealthGetQueryKey = () => {
return [`/health`] as const;
};
export const getHealthzHealthzGetQueryOptions = <
TData = Awaited<ReturnType<typeof healthzHealthzGet>>,
export const getHealthHealthGetQueryOptions = <
TData = Awaited<ReturnType<typeof healthHealthGet>>,
TError = ErrorType<unknown>,
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getHealthzHealthzGetQueryKey();
const queryKey = queryOptions?.queryKey ?? getHealthHealthGetQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof healthzHealthzGet>>
> = ({ signal }) => healthzHealthzGet(signal);
const queryFn: QueryFunction<Awaited<ReturnType<typeof healthHealthGet>>> = ({
signal,
}) => healthHealthGet(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type HealthzHealthzGetQueryResult = NonNullable<
Awaited<ReturnType<typeof healthzHealthzGet>>
export type HealthHealthGetQueryResult = NonNullable<
Awaited<ReturnType<typeof healthHealthGet>>
>;
export type HealthzHealthzGetQueryError = ErrorType<unknown>;
export type HealthHealthGetQueryError = ErrorType<unknown>;
/**
* @summary Healthz
* @summary Health
*/
export function useHealthzHealthzGet<
TData = Awaited<ReturnType<typeof healthzHealthzGet>>,
export function useHealthHealthGet<
TData = Awaited<ReturnType<typeof healthHealthGet>>,
TError = ErrorType<unknown>,
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof healthzHealthzGet>>,
Awaited<ReturnType<typeof healthHealthGet>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getHealthzHealthzGetQueryOptions(options);
const queryOptions = getHealthHealthGetQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return { ...query, queryKey: queryOptions.queryKey };
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Healthz
* @summary Health
*/
export const invalidateHealthzHealthzGet = async (
export const invalidateHealthHealthGet = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getHealthzHealthzGetQueryKey() },
{ queryKey: getHealthHealthGetQueryKey() },
options,
);
@@ -159,82 +159,84 @@ export const invalidateHealthzHealthzGet = async (
};
/**
* @summary Readyz
* @summary Ready
*/
export const readyzReadyzGet = (signal?: AbortSignal) => {
export const readyReadyGet = (signal?: AbortSignal) => {
return GeneratedAPIInstance<ReadinessResponseDTO>({
url: `/readyz`,
url: `/ready`,
method: 'GET',
signal,
});
};
export const getReadyzReadyzGetQueryKey = () => {
return [`/readyz`] as const;
export const getReadyReadyGetQueryKey = () => {
return [`/ready`] as const;
};
export const getReadyzReadyzGetQueryOptions = <
TData = Awaited<ReturnType<typeof readyzReadyzGet>>,
export const getReadyReadyGetQueryOptions = <
TData = Awaited<ReturnType<typeof readyReadyGet>>,
TError = ErrorType<ReadinessResponseDTO>,
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
Awaited<ReturnType<typeof readyReadyGet>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getReadyzReadyzGetQueryKey();
const queryKey = queryOptions?.queryKey ?? getReadyReadyGetQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof readyzReadyzGet>>> = ({
const queryFn: QueryFunction<Awaited<ReturnType<typeof readyReadyGet>>> = ({
signal,
}) => readyzReadyzGet(signal);
}) => readyReadyGet(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
Awaited<ReturnType<typeof readyReadyGet>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ReadyzReadyzGetQueryResult = NonNullable<
Awaited<ReturnType<typeof readyzReadyzGet>>
export type ReadyReadyGetQueryResult = NonNullable<
Awaited<ReturnType<typeof readyReadyGet>>
>;
export type ReadyzReadyzGetQueryError = ErrorType<ReadinessResponseDTO>;
export type ReadyReadyGetQueryError = ErrorType<ReadinessResponseDTO>;
/**
* @summary Readyz
* @summary Ready
*/
export function useReadyzReadyzGet<
TData = Awaited<ReturnType<typeof readyzReadyzGet>>,
export function useReadyReadyGet<
TData = Awaited<ReturnType<typeof readyReadyGet>>,
TError = ErrorType<ReadinessResponseDTO>,
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof readyzReadyzGet>>,
Awaited<ReturnType<typeof readyReadyGet>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getReadyzReadyzGetQueryOptions(options);
const queryOptions = getReadyReadyGetQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return { ...query, queryKey: queryOptions.queryKey };
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Readyz
* @summary Ready
*/
export const invalidateReadyzReadyzGet = async (
export const invalidateReadyReadyGet = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getReadyzReadyzGetQueryKey() },
{ queryKey: getReadyReadyGetQueryKey() },
options,
);
@@ -245,7 +247,7 @@ export const invalidateReadyzReadyzGet = async (
* @summary Create a new thread
*/
export const createThreadApiV1AssistantThreadsPost = (
createThreadApiV1AssistantThreadsPostBody?: BodyType<CreateThreadApiV1AssistantThreadsPostBody>,
createThreadApiV1AssistantThreadsPostBody: BodyType<CreateThreadApiV1AssistantThreadsPostBody>,
headers?: CreateThreadApiV1AssistantThreadsPostHeaders,
signal?: AbortSignal,
) => {
@@ -266,7 +268,7 @@ export const getCreateThreadApiV1AssistantThreadsPostMutationOptions = <
Awaited<ReturnType<typeof createThreadApiV1AssistantThreadsPost>>,
TError,
{
data?: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
data: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
headers?: CreateThreadApiV1AssistantThreadsPostHeaders;
},
TContext
@@ -275,7 +277,7 @@ export const getCreateThreadApiV1AssistantThreadsPostMutationOptions = <
Awaited<ReturnType<typeof createThreadApiV1AssistantThreadsPost>>,
TError,
{
data?: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
data: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
headers?: CreateThreadApiV1AssistantThreadsPostHeaders;
},
TContext
@@ -292,7 +294,7 @@ export const getCreateThreadApiV1AssistantThreadsPostMutationOptions = <
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createThreadApiV1AssistantThreadsPost>>,
{
data?: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
data: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
headers?: CreateThreadApiV1AssistantThreadsPostHeaders;
}
> = (props) => {
@@ -308,8 +310,7 @@ export type CreateThreadApiV1AssistantThreadsPostMutationResult = NonNullable<
Awaited<ReturnType<typeof createThreadApiV1AssistantThreadsPost>>
>;
export type CreateThreadApiV1AssistantThreadsPostMutationBody =
| BodyType<CreateThreadApiV1AssistantThreadsPostBody>
| undefined;
BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
export type CreateThreadApiV1AssistantThreadsPostMutationError = ErrorType<
ErrorResponseDTO | HTTPValidationErrorDTO
>;
@@ -325,7 +326,7 @@ export const useCreateThreadApiV1AssistantThreadsPost = <
Awaited<ReturnType<typeof createThreadApiV1AssistantThreadsPost>>,
TError,
{
data?: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
data: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
headers?: CreateThreadApiV1AssistantThreadsPostHeaders;
},
TContext
@@ -334,14 +335,15 @@ export const useCreateThreadApiV1AssistantThreadsPost = <
Awaited<ReturnType<typeof createThreadApiV1AssistantThreadsPost>>,
TError,
{
data?: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
data: BodyType<CreateThreadApiV1AssistantThreadsPostBody>;
headers?: CreateThreadApiV1AssistantThreadsPostHeaders;
},
TContext
> => {
return useMutation(
getCreateThreadApiV1AssistantThreadsPostMutationOptions(options),
);
const mutationOptions =
getCreateThreadApiV1AssistantThreadsPostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Cursor-based pagination, sorted by updatedAt desc. Use `archived=true|false|all` to filter.
@@ -363,8 +365,13 @@ export const listThreadsApiV1AssistantThreadsGet = (
export const getListThreadsApiV1AssistantThreadsGetQueryKey = (
params?: ListThreadsApiV1AssistantThreadsGetParams,
headers?: ListThreadsApiV1AssistantThreadsGetHeaders,
) => {
return [`/api/v1/assistant/threads`, ...(params ? [params] : [])] as const;
return [
`/api/v1/assistant/threads`,
...(params ? [params] : []),
...getGeneratedAPIQueryKeyHeaders(headers),
] as const;
};
export const getListThreadsApiV1AssistantThreadsGetQueryOptions = <
@@ -385,7 +392,7 @@ export const getListThreadsApiV1AssistantThreadsGetQueryOptions = <
const queryKey =
queryOptions?.queryKey ??
getListThreadsApiV1AssistantThreadsGetQueryKey(params);
getListThreadsApiV1AssistantThreadsGetQueryKey(params, headers);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof listThreadsApiV1AssistantThreadsGet>>
@@ -434,7 +441,9 @@ export function useListThreadsApiV1AssistantThreadsGet<
queryKey: QueryKey;
};
return { ...query, queryKey: queryOptions.queryKey };
query.queryKey = queryOptions.queryKey;
return query;
}
/**
@@ -447,7 +456,7 @@ export const invalidateListThreadsApiV1AssistantThreadsGet = async (
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListThreadsApiV1AssistantThreadsGetQueryKey(params) },
{ queryKey: getListThreadsApiV1AssistantThreadsGetQueryKey(params, headers) },
options,
);
@@ -471,10 +480,14 @@ export const getThreadApiV1AssistantThreadsThreadIdGet = (
});
};
export const getGetThreadApiV1AssistantThreadsThreadIdGetQueryKey = ({
threadId,
}: GetThreadApiV1AssistantThreadsThreadIdGetPathParameters) => {
return [`/api/v1/assistant/threads/${threadId}`] as const;
export const getGetThreadApiV1AssistantThreadsThreadIdGetQueryKey = (
{ threadId }: GetThreadApiV1AssistantThreadsThreadIdGetPathParameters,
headers?: GetThreadApiV1AssistantThreadsThreadIdGetHeaders,
) => {
return [
`/api/v1/assistant/threads/${threadId}`,
...getGeneratedAPIQueryKeyHeaders(headers),
] as const;
};
export const getGetThreadApiV1AssistantThreadsThreadIdGetQueryOptions = <
@@ -495,7 +508,7 @@ export const getGetThreadApiV1AssistantThreadsThreadIdGetQueryOptions = <
const queryKey =
queryOptions?.queryKey ??
getGetThreadApiV1AssistantThreadsThreadIdGetQueryKey({ threadId });
getGetThreadApiV1AssistantThreadsThreadIdGetQueryKey({ threadId }, headers);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getThreadApiV1AssistantThreadsThreadIdGet>>
@@ -549,7 +562,9 @@ export function useGetThreadApiV1AssistantThreadsThreadIdGet<
queryKey: QueryKey;
};
return { ...query, queryKey: queryOptions.queryKey };
query.queryKey = queryOptions.queryKey;
return query;
}
/**
@@ -563,7 +578,10 @@ export const invalidateGetThreadApiV1AssistantThreadsThreadIdGet = async (
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{
queryKey: getGetThreadApiV1AssistantThreadsThreadIdGetQueryKey({ threadId }),
queryKey: getGetThreadApiV1AssistantThreadsThreadIdGetQueryKey(
{ threadId },
headers,
),
},
options,
);
@@ -578,14 +596,12 @@ export const updateThreadApiV1AssistantThreadsThreadIdPatch = (
{ threadId }: UpdateThreadApiV1AssistantThreadsThreadIdPatchPathParameters,
updateThreadRequestDTO: BodyType<UpdateThreadRequestDTO>,
headers?: UpdateThreadApiV1AssistantThreadsThreadIdPatchHeaders,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<ThreadSummaryDTO>({
url: `/api/v1/assistant/threads/${threadId}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/json', ...headers },
data: updateThreadRequestDTO,
signal,
});
};
@@ -679,9 +695,10 @@ export const useUpdateThreadApiV1AssistantThreadsThreadIdPatch = <
},
TContext
> => {
return useMutation(
getUpdateThreadApiV1AssistantThreadsThreadIdPatchMutationOptions(options),
);
const mutationOptions =
getUpdateThreadApiV1AssistantThreadsThreadIdPatchMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Persists the user message, creates an execution (state: queued), kicks off the agent loop asynchronously, and returns immediately. Open `GET /executions/{executionId}/events` for the SSE stream.
@@ -808,11 +825,12 @@ export const useCreateMessageApiV1AssistantThreadsThreadIdMessagesPost = <
},
TContext
> => {
return useMutation(
const mutationOptions =
getCreateMessageApiV1AssistantThreadsThreadIdMessagesPostMutationOptions(
options,
),
);
);
return useMutation(mutationOptions);
};
/**
* Clean-slate regeneration. Starts a fresh execution with conversation history up to (excluding) the original assistant response.
@@ -943,11 +961,12 @@ export const useRegenerateMessageApiV1AssistantMessagesMessageIdRegeneratePost =
},
TContext
> => {
return useMutation(
const mutationOptions =
getRegenerateMessageApiV1AssistantMessagesMessageIdRegeneratePostMutationOptions(
options,
),
);
);
return useMutation(mutationOptions);
};
/**
* Triggers a replay execution that runs the stored tool call with exact params. Returns a new executionId — open SSE for that execution.
@@ -1047,9 +1066,10 @@ export const useApproveApiV1AssistantApprovePost = <
},
TContext
> => {
return useMutation(
getApproveApiV1AssistantApprovePostMutationOptions(options),
);
const mutationOptions =
getApproveApiV1AssistantApprovePostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Marks the approval as rejected. The execution completes with no tool execution.
@@ -1149,7 +1169,10 @@ export const useRejectApiV1AssistantRejectPost = <
},
TContext
> => {
return useMutation(getRejectApiV1AssistantRejectPostMutationOptions(options));
const mutationOptions =
getRejectApiV1AssistantRejectPostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Provides structured answers to a clarification request. Persists the answers as a user transcript message, emits `user_message` as the first replayable event on the new execution stream, and resumes the agent with the answers as tool results.
@@ -1249,9 +1272,10 @@ export const useClarifyApiV1AssistantClarifyPost = <
},
TContext
> => {
return useMutation(
getClarifyApiV1AssistantClarifyPostMutationOptions(options),
);
const mutationOptions =
getClarifyApiV1AssistantClarifyPostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Cooperative cancel. The agent loop finishes its current step, emits a truncated message if streaming, and transitions to canceled.
@@ -1351,7 +1375,10 @@ export const useCancelApiV1AssistantCancelPost = <
},
TContext
> => {
return useMutation(getCancelApiV1AssistantCancelPostMutationOptions(options));
const mutationOptions =
getCancelApiV1AssistantCancelPostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Deletes the resource that was created by the assistant.
@@ -1450,7 +1477,9 @@ export const useUndoApiV1AssistantUndoPost = <
},
TContext
> => {
return useMutation(getUndoApiV1AssistantUndoPostMutationOptions(options));
const mutationOptions = getUndoApiV1AssistantUndoPostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Rolls back the resource to its pre-change snapshot.
@@ -1550,7 +1579,10 @@ export const useRevertApiV1AssistantRevertPost = <
},
TContext
> => {
return useMutation(getRevertApiV1AssistantRevertPostMutationOptions(options));
const mutationOptions =
getRevertApiV1AssistantRevertPostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Recreates the resource from its pre-delete snapshot.
@@ -1650,9 +1682,10 @@ export const useRestoreApiV1AssistantRestorePost = <
},
TContext
> => {
return useMutation(
getRestoreApiV1AssistantRestorePostMutationOptions(options),
);
const mutationOptions =
getRestoreApiV1AssistantRestorePostMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* @summary Submit feedback on an assistant message
@@ -1778,221 +1811,10 @@ export const useSubmitFeedbackApiV1AssistantMessagesMessageIdFeedbackPost = <
},
TContext
> => {
return useMutation(
const mutationOptions =
getSubmitFeedbackApiV1AssistantMessagesMessageIdFeedbackPostMutationOptions(
options,
),
);
};
/**
* @summary Current rate-limit usage for the authenticated user + org
*/
export const getUsageApiV1AssistantUsageGet = (
headers?: GetUsageApiV1AssistantUsageGetHeaders,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<UsageResponseDTO>({
url: `/api/v1/assistant/usage`,
method: 'GET',
headers,
signal,
});
};
export const getGetUsageApiV1AssistantUsageGetQueryKey = () => {
return [`/api/v1/assistant/usage`] as const;
};
export const getGetUsageApiV1AssistantUsageGetQueryOptions = <
TData = Awaited<ReturnType<typeof getUsageApiV1AssistantUsageGet>>,
TError = ErrorType<ErrorResponseDTO | HTTPValidationErrorDTO>,
>(
headers?: GetUsageApiV1AssistantUsageGetHeaders,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getUsageApiV1AssistantUsageGet>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetUsageApiV1AssistantUsageGetQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getUsageApiV1AssistantUsageGet>>
> = ({ signal }) => getUsageApiV1AssistantUsageGet(headers, signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getUsageApiV1AssistantUsageGet>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetUsageApiV1AssistantUsageGetQueryResult = NonNullable<
Awaited<ReturnType<typeof getUsageApiV1AssistantUsageGet>>
>;
export type GetUsageApiV1AssistantUsageGetQueryError = ErrorType<
ErrorResponseDTO | HTTPValidationErrorDTO
>;
/**
* @summary Current rate-limit usage for the authenticated user + org
*/
export function useGetUsageApiV1AssistantUsageGet<
TData = Awaited<ReturnType<typeof getUsageApiV1AssistantUsageGet>>,
TError = ErrorType<ErrorResponseDTO | HTTPValidationErrorDTO>,
>(
headers?: GetUsageApiV1AssistantUsageGetHeaders,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getUsageApiV1AssistantUsageGet>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetUsageApiV1AssistantUsageGetQueryOptions(
headers,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Current rate-limit usage for the authenticated user + org
*/
export const invalidateGetUsageApiV1AssistantUsageGet = async (
queryClient: QueryClient,
headers?: GetUsageApiV1AssistantUsageGetHeaders,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetUsageApiV1AssistantUsageGetQueryKey() },
options,
);
return queryClient;
};
/**
* @summary Contextual empty-state chips
*/
export const getChipsApiV1AssistantEmptyStateChipsGet = (
params: GetChipsApiV1AssistantEmptyStateChipsGetParams,
headers?: GetChipsApiV1AssistantEmptyStateChipsGetHeaders,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<ChipsResponseDTO>({
url: `/api/v1/assistant/empty-state/chips`,
method: 'GET',
headers,
params,
signal,
});
};
export const getGetChipsApiV1AssistantEmptyStateChipsGetQueryKey = (
params?: GetChipsApiV1AssistantEmptyStateChipsGetParams,
) => {
return [
`/api/v1/assistant/empty-state/chips`,
...(params ? [params] : []),
] as const;
};
export const getGetChipsApiV1AssistantEmptyStateChipsGetQueryOptions = <
TData = Awaited<ReturnType<typeof getChipsApiV1AssistantEmptyStateChipsGet>>,
TError = ErrorType<ErrorResponseDTO | HTTPValidationErrorDTO>,
>(
params: GetChipsApiV1AssistantEmptyStateChipsGetParams,
headers?: GetChipsApiV1AssistantEmptyStateChipsGetHeaders,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getChipsApiV1AssistantEmptyStateChipsGet>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ??
getGetChipsApiV1AssistantEmptyStateChipsGetQueryKey(params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getChipsApiV1AssistantEmptyStateChipsGet>>
> = ({ signal }) =>
getChipsApiV1AssistantEmptyStateChipsGet(params, headers, signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getChipsApiV1AssistantEmptyStateChipsGet>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetChipsApiV1AssistantEmptyStateChipsGetQueryResult = NonNullable<
Awaited<ReturnType<typeof getChipsApiV1AssistantEmptyStateChipsGet>>
>;
export type GetChipsApiV1AssistantEmptyStateChipsGetQueryError = ErrorType<
ErrorResponseDTO | HTTPValidationErrorDTO
>;
/**
* @summary Contextual empty-state chips
*/
export function useGetChipsApiV1AssistantEmptyStateChipsGet<
TData = Awaited<ReturnType<typeof getChipsApiV1AssistantEmptyStateChipsGet>>,
TError = ErrorType<ErrorResponseDTO | HTTPValidationErrorDTO>,
>(
params: GetChipsApiV1AssistantEmptyStateChipsGetParams,
headers?: GetChipsApiV1AssistantEmptyStateChipsGetHeaders,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getChipsApiV1AssistantEmptyStateChipsGet>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetChipsApiV1AssistantEmptyStateChipsGetQueryOptions(
params,
headers,
options,
);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return { ...query, queryKey: queryOptions.queryKey };
}
/**
* @summary Contextual empty-state chips
*/
export const invalidateGetChipsApiV1AssistantEmptyStateChipsGet = async (
queryClient: QueryClient,
params: GetChipsApiV1AssistantEmptyStateChipsGetParams,
headers?: GetChipsApiV1AssistantEmptyStateChipsGetHeaders,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetChipsApiV1AssistantEmptyStateChipsGetQueryKey(params) },
options,
);
return queryClient;
);
return useMutation(mutationOptions);
};

View File

@@ -159,25 +159,6 @@ export interface CancelResponseDTO {
state: ExecutionStateDTO;
}
export interface ChipDTO {
/**
* @type string
* @description Stable chip id. Rule-engine chips use intent ids.
*/
id: string;
/**
* @type string
*/
text: string;
}
export interface ChipsResponseDTO {
/**
* @type array
*/
chips: ChipDTO[];
}
export type ClarificationFieldDTOOptions = string[] | null;
export type ClarificationFieldDTODefault = string | string[] | null;
@@ -405,74 +386,15 @@ export type ErrorBodyDTOErrors = ErrorResponseAdditionalDTO[] | null;
export type ErrorBodyDTOUrl = string | null;
/**
* Machine-readable error codes carried on ``ErrorBody.code``.
**Extensible set.** This enum is the single source of truth for every code
the backend can emit, on both the REST envelope and the SSE ``ErrorEvent``.
It is published in the OpenAPI schema (and therefore the generated TS
client) so clients get autocomplete and a typed discriminant. The set is
expected to *grow*: adding a member is a backward-compatible change (the
wire is still a plain JSON string), so clients MUST treat unknown codes
gracefully — branch on the codes they handle and keep a default fallback,
never hard-reject an unrecognized value. Re-exported from ``app.errors``
for convenience; ``AssistantError(code=...)`` requires a member of this
enum so a typo can never reach a client.
*/
export enum ErrorCodeDTO {
missing_signoz_url = 'missing_signoz_url',
invalid_signoz_url = 'invalid_signoz_url',
invalid_content_length = 'invalid_content_length',
invalid_fork_target = 'invalid_fork_target',
rate_limit_override_exceeds_ceiling = 'rate_limit_override_exceeds_ceiling',
thread_message_limit = 'thread_message_limit',
validation_error = 'validation_error',
missing_token = 'missing_token',
invalid_token = 'invalid_token',
permission_denied = 'permission_denied',
user_disabled = 'user_disabled',
org_disabled = 'org_disabled',
thread_not_found = 'thread_not_found',
message_not_found = 'message_not_found',
execution_not_found = 'execution_not_found',
approval_not_found = 'approval_not_found',
clarification_not_found = 'clarification_not_found',
action_metadata_not_found = 'action_metadata_not_found',
user_not_found = 'user_not_found',
region_not_configured = 'region_not_configured',
thread_busy = 'thread_busy',
thread_has_active_execution = 'thread_has_active_execution',
no_active_execution = 'no_active_execution',
approval_superseded = 'approval_superseded',
clarification_superseded = 'clarification_superseded',
undo_conflict = 'undo_conflict',
revert_conflict = 'revert_conflict',
revert_expired = 'revert_expired',
restore_expired = 'restore_expired',
connection_limit_exceeded = 'connection_limit_exceeded',
hourly_message_limit = 'hourly_message_limit',
daily_message_limit = 'daily_message_limit',
daily_token_limit = 'daily_token_limit',
daily_cost_limit = 'daily_cost_limit',
upstream_auth_error = 'upstream_auth_error',
max_turns_exceeded = 'max_turns_exceeded',
budget_exceeded = 'budget_exceeded',
agent_execution_error = 'agent_execution_error',
cli_not_found = 'cli_not_found',
cli_connection_error = 'cli_connection_error',
cli_process_error = 'cli_process_error',
sandbox_unavailable = 'sandbox_unavailable',
mcp_unavailable = 'mcp_unavailable',
internal_error = 'internal_error',
region_unreachable = 'region_unreachable',
heartbeat_expired = 'heartbeat_expired',
replay_unavailable = 'replay_unavailable',
}
/**
* Inner error object — matches Go ErrorsJSON.
*/
export interface ErrorBodyDTO {
code: ErrorCodeDTO;
/**
* @type string
* @pattern ^[a-z_]+$
*/
code: string;
/**
* @type string
*/
@@ -568,23 +490,6 @@ export type MessageActionDTOQuery = MessageActionDTOQueryAnyOf | null;
export type MessageActionDTOUrl = string | null;
/**
* Explorer namespace a saved view belongs to — its ``sourcePage``.
Mirrors the SigNoz product's saved-view ``sourcePage`` values so the
frontend can route an ``open_resource`` action for a view to the right
Explorer via its existing ``SOURCEPAGE_VS_ROUTES`` map. ``meter`` is the
Cost Meter Explorer and is intentionally distinct from ``metrics`` (the
product persists and lists meter views under ``sourcePage="meter"``).
*/
export enum SavedViewEntityDTO {
logs = 'logs',
traces = 'traces',
metrics = 'metrics',
meter = 'meter',
}
export type MessageActionDTOEntity = SavedViewEntityDTO | null;
export enum MessageActionKindDTO {
undo = 'undo',
revert = 'revert',
@@ -595,7 +500,7 @@ export enum MessageActionKindDTO {
apply_filter = 'apply_filter',
}
/**
* Assistant action. Kind-specific requirements: rollback actions require actionMetadataId/resourceType/resourceId; follow_up requires input.intent; open_resource requires resourceType/resourceId; apply_filter requires signal and query; open_docs requires a SigNoz docs url. open_resource for a saved view also carries entity (logs/traces/metrics/meter) so the frontend routes to the correct Explorer.
* Assistant action. Kind-specific requirements: rollback actions require actionMetadataId/resourceType/resourceId; follow_up requires input.intent; open_resource requires resourceType/resourceId; apply_filter requires signal and query; open_docs requires a SigNoz docs url.
*/
export interface MessageActionDTO {
kind: MessageActionKindDTO;
@@ -612,7 +517,6 @@ export interface MessageActionDTO {
signal?: MessageActionDTOSignal;
query?: MessageActionDTOQuery;
url?: MessageActionDTOUrl;
entity?: MessageActionDTOEntity;
}
export enum MessageContentTypeDTO {
@@ -686,26 +590,6 @@ export interface MessageSummaryDTO {
updatedAt: string;
}
export enum PageTypeDTO {
homepage = 'homepage',
dashboard_detail = 'dashboard_detail',
dashboard_list = 'dashboard_list',
panel_edit = 'panel_edit',
panel_fullscreen = 'panel_fullscreen',
logs_explorer = 'logs_explorer',
log_detail = 'log_detail',
traces_explorer = 'traces_explorer',
trace_detail = 'trace_detail',
metrics_explorer = 'metrics_explorer',
service_detail = 'service_detail',
services_list = 'services_list',
alert_edit = 'alert_edit',
alert_list = 'alert_list',
alert_new = 'alert_new',
alerts_triggered = 'alerts_triggered',
infra_entity_detail = 'infra_entity_detail',
other = 'other',
}
export enum ReadinessChecksDTODatabase {
ok = 'ok',
failed = 'failed',
@@ -1106,10 +990,8 @@ export type MessageActionEventDTOQuery = MessageActionEventDTOQueryAnyOf | null;
export type MessageActionEventDTOUrl = string | null;
export type MessageActionEventDTOEntity = SavedViewEntityDTO | null;
/**
* Assistant action. Kind-specific requirements: rollback actions require actionMetadataId/resourceType/resourceId; follow_up requires input.intent; open_resource requires resourceType/resourceId; apply_filter requires signal and query; open_docs requires a SigNoz docs url. open_resource for a saved view also carries entity (logs/traces/metrics/meter) so the frontend routes to the correct Explorer.
* Assistant action. Kind-specific requirements: rollback actions require actionMetadataId/resourceType/resourceId; follow_up requires input.intent; open_resource requires resourceType/resourceId; apply_filter requires signal and query; open_docs requires a SigNoz docs url.
*/
export interface MessageActionEventDTO {
kind: MessageActionKindDTO;
@@ -1126,7 +1008,6 @@ export interface MessageActionEventDTO {
signal?: MessageActionEventDTOSignal;
query?: MessageActionEventDTOQuery;
url?: MessageActionEventDTOUrl;
entity?: MessageActionEventDTOEntity;
}
export type MessageEventDTOActions = MessageActionEventDTO[] | null;
@@ -1504,21 +1385,3 @@ export type GetUsageApiV1AssistantUsageGetHeaders = {
*/
'X-SigNoz-URL'?: string | null;
};
export type GetChipsApiV1AssistantEmptyStateChipsGetParams = {
/**
* @description Frontend-declared page type. Typed as an enum, but unrecognized values are coerced to 'other' (not rejected) so a new frontend page type works before the backend knows it. The page type alone identifies the focused entity (e.g. trace_detail) for the 'Explain this …' chip; the agent reads the concrete entity from page context once a chip is clicked, so no separate entity id is needed.
*/
page_type: PageTypeDTO;
};
export type GetChipsApiV1AssistantEmptyStateChipsGetHeaders = {
/**
* @description SigNoz auth token (Bearer or raw JWT)
*/
authorization?: string | null;
/**
* @description SigNoz instance base URL for multi-tenant deployments. Falls back to SIGNOZ_API_URL env var when omitted.
*/
'X-SigNoz-URL'?: string | null;
};

View File

@@ -2645,14 +2645,6 @@ export enum CloudintegrationtypesServiceIDDTO {
appservice = 'appservice',
containerapp = 'containerapp',
aks = 'aks',
sqldatabase = 'sqldatabase',
sqldatabasemi = 'sqldatabasemi',
mysqlflexibleserver = 'mysqlflexibleserver',
postgresqlflexibleserver = 'postgresqlflexibleserver',
mongodb = 'mongodb',
cosmosdb = 'cosmosdb',
cassandradb = 'cassandradb',
redis = 'redis',
}
export type CloudintegrationtypesCloudIntegrationServiceDTOAnyOf = {
/**

View File

@@ -1,24 +0,0 @@
import axios from 'api';
import { AxiosResponse } from 'axios';
import { ViewProps } from 'types/api/saveViews/types';
/**
* Fetches a single saved view by ID (`GET /api/v1/explorer/views/{viewId}`).
*
* Hand-maintained alongside the other `api/saveView/*` clients — explorer views
* are not in `docs/api/openapi.yml`, so Orval does not generate a hook here
* (unlike e.g. `useGetChannelByID` under `api/generated/services/channels`).
*
* Used by the AI assistant "Open view" action to load `compositeQuery` and
* navigate to the correct explorer without listing every view per source page.
* See `container/AIAssistant/components/ActionsSection/utils/openSavedView.ts`.
*/
export interface GetViewByIdProps {
status: string;
data: ViewProps;
}
export const getViewById = (
viewKey: string,
): Promise<AxiosResponse<GetViewByIdProps>> =>
axios.get(`/explorer/views/${viewKey}`);

View File

@@ -90,4 +90,20 @@ describe('RouteTab component', () => {
fireEvent.click(screen.getByRole('tab', { name: 'Tab2' }));
expect(onChangeHandler).toHaveBeenCalled();
});
it('unmounts inactive tab pane content after switching', () => {
const history = createMemoryHistory({ initialEntries: ['/tab1'] });
render(
<Router history={history}>
<RouteTab history={history} routes={testRoutes} activeKey="Tab1" />
</Router>,
);
expect(screen.getByText('Dummy Component 1')).toBeInTheDocument();
fireEvent.click(screen.getByRole('tab', { name: 'Tab2' }));
expect(screen.queryByText('Dummy Component 1')).not.toBeInTheDocument();
expect(screen.getByText('Dummy Component 2')).toBeInTheDocument();
});
});

View File

@@ -59,7 +59,7 @@ function RouteTab({
destroyInactiveTabPane
activeKey={currentRoute?.key || activeKey}
defaultActiveKey={currentRoute?.key || activeKey}
animated
animated={{ inkBar: true, tabPane: false }}
items={items}
tabBarExtraContent={
showRightSection && (

View File

@@ -38,8 +38,8 @@ export enum LOCALSTORAGE {
DISSMISSED_COST_METER_INFO = 'DISMISSED_COST_METER_INFO',
DISMISSED_API_KEYS_DEPRECATION_BANNER = 'DISMISSED_API_KEYS_DEPRECATION_BANNER',
TRACE_DETAILS_SPAN_DETAILS_POSITION = 'TRACE_DETAILS_SPAN_DETAILS_POSITION',
LICENSE_KEY_CALLOUT_DISMISSED = 'LICENSE_KEY_CALLOUT_DISMISSED',
TRACE_DETAILS_PREFER_OLD_VIEW = 'TRACE_DETAILS_PREFER_OLD_VIEW',
LICENSE_KEY_CALLOUT_DISMISSED = 'LICENSE_KEY_CALLOUT_DISMISSED',
DASHBOARD_PREFERENCES = 'DASHBOARD_PREFERENCES',
ACTIVE_SIGNOZ_INSTANCE_URL = 'ACTIVE_SIGNOZ_INSTANCE_URL',
DASHBOARDS_LIST_VISIBLE_COLUMNS = 'DASHBOARDS_LIST_VISIBLE_COLUMNS',

View File

@@ -113,7 +113,4 @@ export const REACT_QUERY_KEY = {
// Fields Selector Query Keys
GET_FIELDS_SELECTOR_SUGGESTIONS: 'GET_FIELDS_SELECTOR_SUGGESTIONS',
// AI Assistant Query Keys
AI_ASSISTANT_EMPTY_STATE_CHIPS: 'AI_ASSISTANT_EMPTY_STATE_CHIPS',
} as const;

View File

@@ -1,150 +0,0 @@
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { getAutoContexts } from '../getAutoContexts';
describe('getAutoContexts', () => {
it('returns alert detail context on alert overview with ruleId', () => {
const ruleId = 'rule-abc';
const search = `?${QueryParams.ruleId}=${ruleId}&${QueryParams.relativeTime}=1h`;
const contexts = getAutoContexts(ROUTES.ALERT_OVERVIEW, search);
expect(contexts).toStrictEqual([
{
source: 'auto',
type: 'alert',
resourceId: ruleId,
metadata: {
page: 'alert_detail',
ruleId,
},
},
]);
});
it('returns alert detail context on alert history with ruleId', () => {
const ruleId = 'rule-xyz';
const startTime = '1700000000000';
const endTime = '1700003600000';
const search = `?${QueryParams.ruleId}=${ruleId}&${QueryParams.startTime}=${startTime}&${QueryParams.endTime}=${endTime}`;
const contexts = getAutoContexts(ROUTES.ALERT_HISTORY, search);
expect(contexts).toStrictEqual([
{
source: 'auto',
type: 'alert',
resourceId: ruleId,
metadata: {
page: 'alert_detail',
ruleId,
timeRange: {
start: Number(startTime),
end: Number(endTime),
},
},
},
]);
});
it('returns triggered alerts context on alert history without ruleId', () => {
const contexts = getAutoContexts(ROUTES.ALERT_HISTORY, '');
expect(contexts).toStrictEqual([
{
source: 'auto',
type: 'alert',
resourceId: null,
metadata: {
page: 'alerts_triggered',
},
},
]);
});
it('resolves alert list tabs on /alerts', () => {
expect(getAutoContexts(ROUTES.LIST_ALL_ALERT, '')).toStrictEqual([
{
source: 'auto',
type: 'alert',
resourceId: null,
metadata: { page: 'alert_list' },
},
]);
expect(
getAutoContexts(ROUTES.LIST_ALL_ALERT, '?tab=AlertRules'),
).toStrictEqual([
{
source: 'auto',
type: 'alert',
resourceId: null,
metadata: { page: 'alert_list' },
},
]);
expect(
getAutoContexts(ROUTES.LIST_ALL_ALERT, '?tab=TriggeredAlerts'),
).toStrictEqual([
{
source: 'auto',
type: 'alert',
resourceId: null,
metadata: { page: 'alerts_triggered' },
},
]);
expect(
getAutoContexts(ROUTES.LIST_ALL_ALERT, '?tab=Configuration'),
).toStrictEqual([
{
source: 'auto',
type: 'alert',
resourceId: null,
metadata: { page: 'alert_list' },
},
]);
});
it('returns dashboard detail context on dashboard page', () => {
const dashboardId = 'dash-123';
const pathname = ROUTES.DASHBOARD.replace(':dashboardId', dashboardId);
const contexts = getAutoContexts(pathname, '');
expect(contexts).toStrictEqual([
{
source: 'auto',
type: 'dashboard',
resourceId: dashboardId,
metadata: {
page: 'dashboard_detail',
},
},
]);
});
it('returns empty array on alert overview without ruleId', () => {
const contexts = getAutoContexts(ROUTES.ALERT_OVERVIEW, '');
expect(contexts).toStrictEqual([]);
});
it('emits no auto-context on /home (no attachable resource)', () => {
expect(getAutoContexts(ROUTES.HOME, '')).toStrictEqual([]);
});
it('emits no auto-context on infrastructure monitoring routes', () => {
expect(
getAutoContexts(ROUTES.INFRASTRUCTURE_MONITORING_BASE, ''),
).toStrictEqual([]);
expect(
getAutoContexts(
ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
'?selectedItem=host-1',
),
).toStrictEqual([]);
});
});

View File

@@ -1,87 +0,0 @@
import { PageTypeDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { resolvePageType } from '../resolvePageType';
describe('resolvePageType', () => {
it('returns other for the standalone assistant surface', () => {
expect(
resolvePageType('/services', '', { isStandaloneAssistant: true }),
).toBe(PageTypeDTO.other);
});
it('returns dashboard_detail on a dashboard page', () => {
const pathname = ROUTES.DASHBOARD.replace(':dashboardId', 'dash-123');
expect(resolvePageType(pathname, '')).toBe(PageTypeDTO.dashboard_detail);
});
it('returns alerts_triggered on alert history without ruleId', () => {
expect(resolvePageType(ROUTES.ALERT_HISTORY, '')).toBe(
PageTypeDTO.alerts_triggered,
);
});
it('resolves alert list tabs on /alerts', () => {
expect(resolvePageType(ROUTES.LIST_ALL_ALERT, '')).toBe(
PageTypeDTO.alert_list,
);
expect(resolvePageType(ROUTES.LIST_ALL_ALERT, '?tab=AlertRules')).toBe(
PageTypeDTO.alert_list,
);
expect(resolvePageType(ROUTES.LIST_ALL_ALERT, '?tab=TriggeredAlerts')).toBe(
PageTypeDTO.alerts_triggered,
);
expect(resolvePageType(ROUTES.LIST_ALL_ALERT, '?tab=Configuration')).toBe(
PageTypeDTO.alert_list,
);
});
it('returns log_detail when logs explorer has activeLogId', () => {
const search = `?${QueryParams.activeLogId}=log-1`;
expect(resolvePageType(ROUTES.LOGS_EXPLORER, search)).toBe(
PageTypeDTO.log_detail,
);
});
it('returns other for unmapped routes', () => {
expect(resolvePageType(ROUTES.ALERT_OVERVIEW, '')).toBe(PageTypeDTO.other);
});
it('returns other for the app root route (no contextual mapping)', () => {
expect(resolvePageType(ROUTES.HOME_PAGE, '')).toBe(PageTypeDTO.other);
});
it('returns homepage on /home', () => {
expect(resolvePageType(ROUTES.HOME, '')).toBe(PageTypeDTO.homepage);
});
it('returns infra_entity_detail on infrastructure monitoring routes', () => {
expect(resolvePageType(ROUTES.INFRASTRUCTURE_MONITORING_BASE, '')).toBe(
PageTypeDTO.infra_entity_detail,
);
expect(resolvePageType(ROUTES.INFRASTRUCTURE_MONITORING_HOSTS, '')).toBe(
PageTypeDTO.infra_entity_detail,
);
expect(resolvePageType(ROUTES.INFRASTRUCTURE_MONITORING_KUBERNETES, '')).toBe(
PageTypeDTO.infra_entity_detail,
);
});
it('returns metrics_explorer on all metrics explorer routes', () => {
expect(resolvePageType(ROUTES.METRICS_EXPLORER_BASE, '')).toBe(
PageTypeDTO.metrics_explorer,
);
expect(resolvePageType(ROUTES.METRICS_EXPLORER, '')).toBe(
PageTypeDTO.metrics_explorer,
);
expect(resolvePageType(ROUTES.METRICS_EXPLORER_EXPLORER, '')).toBe(
PageTypeDTO.metrics_explorer,
);
expect(resolvePageType(ROUTES.METRICS_EXPLORER_VIEWS, '')).toBe(
PageTypeDTO.metrics_explorer,
);
});
});

View File

@@ -47,15 +47,6 @@ import { AIAssistantEvents, SuggestedPromptCategory } from '../../events';
import { useAIAssistantAnalyticsContext } from '../../hooks/useAIAssistantAnalyticsContext';
import { useAIAssistantStore } from '../../store/useAIAssistantStore';
import { openSavedViewByKey } from './utils/openSavedView';
import {
isSavedViewOpenAction,
resolveOpenResourceType,
resolveResourceId,
resolveSavedViewSourceHint,
} from './utils/resolveOpenResource';
import { ResourceType, resourceRoute } from './utils/resourceRoute';
import styles from './ActionsSection.module.scss';
interface ActionsSectionProps {
@@ -64,6 +55,20 @@ interface ActionsSectionProps {
messageId: string;
}
/**
* Resource-type strings the backend uses for `open_resource` and rollback
* actions. Centralized here so the route/module lookups below stay in sync.
*/
const ResourceType = {
dashboard: 'dashboard',
alert: 'alert',
service: 'service',
saved_view: 'saved_view',
logs_explorer: 'logs_explorer',
traces_explorer: 'traces_explorer',
metrics_explorer: 'metrics_explorer',
} as const;
/** Maps an open_resource action's resourceType to its product module name. */
function targetModuleForResource(resourceType: string): string | null {
switch (resourceType) {
@@ -73,8 +78,6 @@ function targetModuleForResource(resourceType: string): string | null {
return 'alerts';
case ResourceType.service:
return 'apm';
case ResourceType.channel:
return 'channels';
case ResourceType.saved_view:
return 'savedViews';
case ResourceType.logs_explorer:
@@ -137,6 +140,39 @@ function ActionIcon({
}
}
/**
* Resolves an `open_resource` action to an in-app route.
* Resource taxonomy mirrors `MessageContextDTOType`: dashboard, alert,
* saved_view, service, and the *_explorer signals.
*/
function resourceRoute(
resourceType: string,
resourceId: string,
): string | null {
switch (resourceType) {
case ResourceType.dashboard:
return ROUTES.DASHBOARD.replace(':dashboardId', resourceId);
case ResourceType.alert: {
const params = new URLSearchParams({ [QueryParams.ruleId]: resourceId });
return `${ROUTES.EDIT_ALERTS}?${params.toString()}`;
}
case ResourceType.service:
return ROUTES.SERVICE_METRICS.replace(':servicename', resourceId);
case ResourceType.saved_view:
// No detail route — saved views land on the list page.
// Caller may provide signal-aware metadata in future; default to logs.
return ROUTES.LOGS_SAVE_VIEWS;
case ResourceType.logs_explorer:
return ROUTES.LOGS_EXPLORER;
case ResourceType.traces_explorer:
return ROUTES.TRACES_EXPLORER;
case ResourceType.metrics_explorer:
return ROUTES.METRICS_EXPLORER_EXPLORER;
default:
return null;
}
}
/**
* The agent emits `action.query` as the SigNoz REST query-range request body:
*
@@ -448,35 +484,6 @@ export default function ActionsSection({
setResults((prev) => ({ ...prev, [key]: result }));
};
const runOpenSavedView = async (
key: string,
action: MessageActionDTO,
): Promise<void> => {
const resourceId = resolveResourceId(action);
if (!resourceId) {
return;
}
setResult(key, { state: 'loading' });
try {
await openSavedViewByKey(
resourceId,
resolveSavedViewSourceHint(action),
history,
);
void logEvent(AIAssistantEvents.ResourceOpened, {
threadId,
messageId,
targetModule: targetModuleForResource(ResourceType.saved_view),
resourceId,
});
setResult(key, { state: 'success' });
} catch (err) {
const message =
err instanceof Error ? err.message : 'Failed to open saved view';
setResult(key, { state: 'error', error: message });
}
};
const runRollback = async (
key: string,
action: MessageActionDTO,
@@ -495,31 +502,6 @@ export default function ActionsSection({
}
};
const handleOpenResource = (key: string, action: MessageActionDTO): void => {
if (isSavedViewOpenAction(action)) {
void runOpenSavedView(key, action);
return;
}
const resourceType = resolveOpenResourceType(action);
const resourceId = resolveResourceId(action);
if (!resourceType || !resourceId) {
return;
}
const path = resourceRoute(resourceType, resourceId);
if (!path) {
return;
}
void logEvent(AIAssistantEvents.ResourceOpened, {
threadId,
messageId,
targetModule: targetModuleForResource(resourceType),
resourceId,
});
history.push(path);
};
const handleClick = (key: string, action: MessageActionDTO): void => {
switch (action.kind) {
case MessageActionKindDTO.open_docs: {
@@ -560,9 +542,21 @@ export default function ActionsSection({
}
break;
}
case MessageActionKindDTO.open_resource:
handleOpenResource(key, action);
case MessageActionKindDTO.open_resource: {
if (action.resourceType && action.resourceId) {
const path = resourceRoute(action.resourceType, action.resourceId);
if (path) {
void logEvent(AIAssistantEvents.ResourceOpened, {
threadId,
messageId,
targetModule: targetModuleForResource(action.resourceType),
resourceId: action.resourceId,
});
history.push(path);
}
}
break;
}
case MessageActionKindDTO.undo:
case MessageActionKindDTO.revert:
case MessageActionKindDTO.restore: {

View File

@@ -1,283 +0,0 @@
import {
ApplyFilterSignalDTO,
MessageActionKindDTO,
SavedViewEntityDTO,
} from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import { getAllViews } from 'api/saveView/getAllViews';
import { getViewById } from 'api/saveView/getViewById';
import ROUTES from 'constants/routes';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery';
import { AllViewsProps, ViewProps } from 'types/api/saveViews/types';
import { DataSource } from 'types/common/queryBuilder';
import { AxiosResponse } from 'axios';
import type { History } from 'history';
import {
buildExplorerNavigationUrl,
findSavedViewInLists,
openSavedView,
openSavedViewByKey,
} from '../openSavedView';
import {
entityToDataSource,
isSavedViewOpenAction,
resolveActionEntity,
resolveOpenResourceType,
resolveResourceId,
resolveResourceType,
resolveSavedViewSourceHint,
} from '../resolveOpenResource';
import { resourceRoute, ResourceType } from '../resourceRoute';
jest.mock('api/saveView/getAllViews');
jest.mock('api/saveView/getViewById');
jest.mock(
'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi',
() => ({
mapQueryDataFromApi: jest.fn(() => ({
queryType: 'builder',
builder: {
queryData: [{ id: 'A' }],
queryFormulas: [],
queryTraceOperator: [],
},
})),
}),
);
const mockedGetAllViews = getAllViews as jest.MockedFunction<
typeof getAllViews
>;
const mockedGetViewById = getViewById as jest.MockedFunction<
typeof getViewById
>;
function makeView(id: string, sourcePage: DataSource): ViewProps {
return {
id,
name: `View ${id}`,
category: 'test',
createdAt: '2021-07-07T06:31:00.000Z',
createdBy: 'user',
updatedAt: '2021-07-07T06:33:00.000Z',
updatedBy: 'user',
sourcePage,
tags: [],
extraData: '',
compositeQuery: {
panelType: PANEL_TYPES.LIST,
} as ICompositeMetricQuery,
};
}
function mockViewsResponse(views: ViewProps[]): AxiosResponse<AllViewsProps> {
return {
data: { status: 'success', data: views },
} as AxiosResponse<AllViewsProps>;
}
function mockViewByIdResponse(
view: ViewProps,
): AxiosResponse<{ status: string; data: ViewProps }> {
return {
data: { status: 'success', data: view },
} as AxiosResponse<{ status: string; data: ViewProps }>;
}
describe('resourceRoute', () => {
it('returns null for saved_view so async navigation is used', () => {
expect(resourceRoute(ResourceType.saved_view, 'view-123')).toBeNull();
});
it('routes channels to the edit page', () => {
expect(resourceRoute(ResourceType.channel, 'channel-uuid-1')).toBe(
'/settings/channels/edit/channel-uuid-1',
);
});
});
describe('resolveOpenResource', () => {
it('reads entity from the action envelope', () => {
expect(
resolveActionEntity({
kind: MessageActionKindDTO.open_resource,
label: 'Open view',
entity: SavedViewEntityDTO.traces,
}),
).toBe(SavedViewEntityDTO.traces);
});
it('reads resource id from input.viewKey', () => {
expect(
resolveResourceId({
kind: MessageActionKindDTO.open_resource,
label: 'Open view',
input: { viewKey: 'abc-123' },
}),
).toBe('abc-123');
});
it('maps entity values to explorer data sources', () => {
expect(entityToDataSource('logs')).toBe(DataSource.LOGS);
expect(entityToDataSource('logs_explorer')).toBe(DataSource.LOGS);
expect(entityToDataSource('traces')).toBe(DataSource.TRACES);
});
it('prefers entity over signal for saved-view source hints', () => {
expect(
resolveSavedViewSourceHint({
kind: MessageActionKindDTO.open_resource,
label: 'Open view',
entity: SavedViewEntityDTO.traces,
signal: ApplyFilterSignalDTO.logs,
}),
).toBe(DataSource.TRACES);
});
it('falls back to signal when entity is absent', () => {
expect(
resolveSavedViewSourceHint({
kind: MessageActionKindDTO.open_resource,
label: 'Open view',
signal: ApplyFilterSignalDTO.metrics,
}),
).toBe(DataSource.METRICS);
});
it('normalises saved-view resource types', () => {
expect(
resolveResourceType({
kind: MessageActionKindDTO.open_resource,
label: 'Open view',
resourceType: 'saved-view',
}),
).toBe(ResourceType.saved_view);
});
it('detects open-view actions from label when id is present in input', () => {
expect(
isSavedViewOpenAction({
kind: MessageActionKindDTO.open_resource,
label: 'Open view',
input: { viewId: 'view-1' },
}),
).toBe(true);
});
it('resolves channel type from notification_channel alias', () => {
expect(
resolveResourceType({
kind: MessageActionKindDTO.open_resource,
label: 'Open channel',
resourceType: 'notification_channel',
}),
).toBe(ResourceType.channel);
});
it('infers channel type from Open channel label when resourceId is present', () => {
expect(
resolveOpenResourceType({
kind: MessageActionKindDTO.open_resource,
label: 'Open channel',
resourceId: 'channel-1',
}),
).toBe(ResourceType.channel);
});
});
describe('findSavedViewInLists', () => {
beforeEach(() => {
mockedGetAllViews.mockReset();
});
it('loads only the hinted source when entity is provided', async () => {
const tracesView = makeView('view-traces', DataSource.TRACES);
mockedGetAllViews.mockResolvedValueOnce(mockViewsResponse([tracesView]));
const result = await findSavedViewInLists('view-traces', DataSource.TRACES);
expect(result).toStrictEqual(tracesView);
expect(mockedGetAllViews).toHaveBeenCalledTimes(1);
expect(mockedGetAllViews).toHaveBeenCalledWith(DataSource.TRACES);
});
});
describe('buildExplorerNavigationUrl', () => {
it('encodes composite query and view selectors', () => {
const url = buildExplorerNavigationUrl(
ROUTES.LOGS_EXPLORER,
{ queryType: 'builder' } as never,
{
[QueryParams.panelTypes]: PANEL_TYPES.LIST,
[QueryParams.viewName]: 'My view',
[QueryParams.viewKey]: 'view-1',
},
);
expect(url).toContain(ROUTES.LOGS_EXPLORER);
expect(url).toContain(`${QueryParams.compositeQuery}=`);
expect(url).toContain(`${QueryParams.viewKey}=`);
});
});
describe('openSavedView', () => {
it('navigates with history.push and view query params', () => {
const push = jest.fn();
const history = { push } as unknown as History;
const view = makeView('view-logs', DataSource.LOGS);
openSavedView(view, history);
expect(push).toHaveBeenCalledTimes(1);
const pushedUrl = push.mock.calls[0][0] as string;
expect(pushedUrl).toContain(ROUTES.LOGS_EXPLORER);
expect(pushedUrl).toContain(QueryParams.viewKey);
});
});
describe('openSavedViewByKey', () => {
beforeEach(() => {
mockedGetAllViews.mockReset();
mockedGetViewById.mockReset();
});
it('prefers the direct view lookup endpoint', async () => {
const view = makeView('view-logs', DataSource.LOGS);
mockedGetViewById.mockResolvedValueOnce(mockViewByIdResponse(view));
const push = jest.fn();
const history = { push } as unknown as History;
await openSavedViewByKey('view-logs', DataSource.LOGS, history);
expect(mockedGetViewById).toHaveBeenCalledWith('view-logs');
expect(mockedGetAllViews).not.toHaveBeenCalled();
expect(push).toHaveBeenCalled();
});
it('falls back to list probing when direct lookup fails', async () => {
const view = makeView('view-traces', DataSource.TRACES);
mockedGetViewById.mockRejectedValueOnce(new Error('not found'));
mockedGetAllViews.mockResolvedValueOnce(mockViewsResponse([view]));
const push = jest.fn();
const history = { push } as unknown as History;
await openSavedViewByKey('view-traces', DataSource.TRACES, history);
expect(mockedGetAllViews).toHaveBeenCalledWith(DataSource.TRACES);
expect(push).toHaveBeenCalled();
});
it('throws when the saved view does not exist', async () => {
mockedGetViewById.mockRejectedValueOnce(new Error('not found'));
mockedGetAllViews.mockResolvedValue(mockViewsResponse([]));
await expect(
openSavedViewByKey('missing', DataSource.LOGS, {
push: jest.fn(),
} as unknown as History),
).rejects.toThrow('Saved view not found');
});
});

View File

@@ -1,117 +0,0 @@
import { getAllViews } from 'api/saveView/getAllViews';
import { getViewById } from 'api/saveView/getViewById';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi';
import { SOURCEPAGE_VS_ROUTES } from 'pages/SaveView/constants';
import { ViewProps } from 'types/api/saveViews/types';
import { DataSource } from 'types/common/queryBuilder';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { History } from 'history';
type SavedViewSourceHint = DataSource | 'meter';
const DEFAULT_PROBE_SOURCES: SavedViewSourceHint[] = [
DataSource.LOGS,
DataSource.TRACES,
DataSource.METRICS,
];
export async function findSavedViewInLists(
viewKey: string,
sourceHint?: SavedViewSourceHint | null,
): Promise<ViewProps | null> {
const sources = sourceHint ? [sourceHint] : DEFAULT_PROBE_SOURCES;
for (const source of sources) {
try {
const response = await getAllViews(source);
const match = response.data.data.find((view) => view.id === viewKey);
if (match) {
return match;
}
} catch {
// Probe the next source page when no entity hint is provided.
}
}
return null;
}
async function loadSavedView(
viewKey: string,
sourceHint?: SavedViewSourceHint | null,
): Promise<ViewProps> {
try {
const response = await getViewById(viewKey);
if (response.data?.data) {
return response.data.data;
}
} catch {
// Fall back to list probing when the direct lookup fails.
}
const fromList = await findSavedViewInLists(viewKey, sourceHint);
if (fromList) {
return fromList;
}
throw new Error('Saved view not found');
}
export function explorerRouteForSourcePage(
sourcePage: DataSource | string,
): (typeof SOURCEPAGE_VS_ROUTES)[keyof typeof SOURCEPAGE_VS_ROUTES] | null {
return SOURCEPAGE_VS_ROUTES[sourcePage] ?? null;
}
/**
* Builds an explorer URL the same way `redirectWithQueryBuilderData` does —
* without inheriting stale query params from the current page's `urlQuery`.
*/
export function buildExplorerNavigationUrl(
route: string,
query: Query,
searchParams: Record<string, unknown>,
): string {
const params = new URLSearchParams();
params.set(
QueryParams.compositeQuery,
encodeURIComponent(JSON.stringify(query)),
);
Object.entries(searchParams).forEach(([key, value]) => {
params.set(key, JSON.stringify(value));
});
return `${route}?${params.toString()}`;
}
export function openSavedView(view: ViewProps, history: History): void {
const route = explorerRouteForSourcePage(view.sourcePage);
if (!route) {
throw new Error('Unsupported saved view source');
}
if (!view.compositeQuery) {
throw new Error('Saved view is missing query data');
}
const query = mapQueryDataFromApi(view.compositeQuery);
const url = buildExplorerNavigationUrl(route, query, {
[QueryParams.panelTypes]: view.compositeQuery.panelType as PANEL_TYPES,
[QueryParams.viewName]: view.name,
[QueryParams.viewKey]: view.id,
});
history.push(url);
}
export async function openSavedViewByKey(
viewKey: string,
sourceHint: SavedViewSourceHint | null | undefined,
history: History,
): Promise<void> {
const view = await loadSavedView(viewKey, sourceHint);
openSavedView(view, history);
}
/** @deprecated Use findSavedViewInLists — kept for tests. */
export const findSavedView = findSavedViewInLists;

View File

@@ -1,203 +0,0 @@
import type { MessageActionDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import {
ApplyFilterSignalDTO,
SavedViewEntityDTO,
} from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import { DataSource } from 'types/common/queryBuilder';
import { ResourceType } from './resourceRoute';
function readString(value: unknown): string | null {
if (typeof value !== 'string') {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
/** Normalises backend resource-type strings to the taxonomy used in the UI. */
export function normalizeResourceType(
resourceType: string | null | undefined,
): string | null {
if (!resourceType) {
return null;
}
const normalized = resourceType.trim().toLowerCase().replace(/-/g, '_');
if (normalized === 'savedview') {
return ResourceType.saved_view;
}
if (
normalized === 'notification_channel' ||
normalized === 'notificationchannel'
) {
return ResourceType.channel;
}
return normalized;
}
/** Reads a resource type from the action envelope or its `input` payload. */
export function resolveResourceType(action: MessageActionDTO): string | null {
const direct = normalizeResourceType(action.resourceType);
if (direct) {
return direct;
}
const input = action.input;
if (!input) {
return null;
}
return (
normalizeResourceType(readString(input.resourceType)) ??
normalizeResourceType(readString(input.type))
);
}
/**
* Resolves the resource type for an `open_resource` action, including label-based
* fallbacks when the backend only sends a display label + id.
*/
export function resolveOpenResourceType(
action: MessageActionDTO,
): string | null {
const fromFields = resolveResourceType(action);
if (fromFields) {
return fromFields;
}
if (/open\s+channel/i.test(action.label) && resolveResourceId(action)) {
return ResourceType.channel;
}
return null;
}
/** Reads a resource id from `resourceId` or common `input` keys. */
export function resolveResourceId(action: MessageActionDTO): string | null {
const direct = readString(action.resourceId);
if (direct) {
return direct;
}
const input = action.input;
if (!input) {
return null;
}
for (const key of [
'resourceId',
'viewId',
'viewKey',
'channelId',
'id',
] as const) {
const value = readString(input[key]);
if (value) {
return value;
}
}
return null;
}
/** Reads `entity` from the action envelope or its `input` payload. */
export function resolveActionEntity(
action: MessageActionDTO,
): SavedViewEntityDTO | null {
if (action.entity) {
return action.entity;
}
const fromInput = readString(action.input?.entity);
if (!fromInput) {
return null;
}
return normalizeToSavedViewEntity(fromInput);
}
function normalizeToSavedViewEntity(value: string): SavedViewEntityDTO | null {
const source = entityToDataSource(value);
switch (source) {
case DataSource.LOGS:
return SavedViewEntityDTO.logs;
case DataSource.TRACES:
return SavedViewEntityDTO.traces;
case DataSource.METRICS:
return SavedViewEntityDTO.metrics;
case 'meter':
return SavedViewEntityDTO.meter;
default:
return null;
}
}
/**
* Maps an action `entity` to an explorer `DataSource` for saved-view lookups.
* Accepts both short (`logs`) and taxonomy (`logs_explorer`) values.
*/
export function entityToDataSource(
entity: SavedViewEntityDTO | string,
): DataSource | 'meter' | null {
const normalized = entity.trim().toLowerCase().replace(/-/g, '_');
switch (normalized) {
case SavedViewEntityDTO.logs:
case ResourceType.logs_explorer:
return DataSource.LOGS;
case SavedViewEntityDTO.traces:
case ResourceType.traces_explorer:
return DataSource.TRACES;
case SavedViewEntityDTO.metrics:
case ResourceType.metrics_explorer:
return DataSource.METRICS;
case SavedViewEntityDTO.meter:
return 'meter';
default:
return null;
}
}
/**
* Picks which explorer source page to search when resolving a saved view.
* Prefers `entity` (open_resource); falls back to `signal` only for legacy payloads.
*/
export function resolveSavedViewSourceHint(
action: MessageActionDTO,
): DataSource | 'meter' | null {
const entity = resolveActionEntity(action);
if (entity) {
const fromEntity = entityToDataSource(entity);
if (fromEntity) {
return fromEntity;
}
}
if (action.signal) {
switch (action.signal) {
case ApplyFilterSignalDTO.logs:
return DataSource.LOGS;
case ApplyFilterSignalDTO.traces:
return DataSource.TRACES;
case ApplyFilterSignalDTO.metrics:
return DataSource.METRICS;
default: {
const _exhaustive: never = action.signal;
return _exhaustive;
}
}
}
return null;
}
export function isSavedViewOpenAction(action: MessageActionDTO): boolean {
if (resolveResourceType(action) === ResourceType.saved_view) {
return true;
}
// Defensive: some agent payloads only set a human label + id in `input`.
return /open\s+view/i.test(action.label) && resolveResourceId(action) !== null;
}

View File

@@ -1,50 +0,0 @@
import ROUTES from 'constants/routes';
import { QueryParams } from 'constants/query';
/**
* Resource-type strings the backend uses for `open_resource` and rollback
* actions. Centralized here so route/module lookups stay in sync.
*/
export const ResourceType = {
dashboard: 'dashboard',
alert: 'alert',
service: 'service',
channel: 'channel',
saved_view: 'saved_view',
logs_explorer: 'logs_explorer',
traces_explorer: 'traces_explorer',
metrics_explorer: 'metrics_explorer',
} as const;
/**
* Resolves an `open_resource` action to an in-app route for synchronous
* navigation. Returns `null` for `saved_view` — callers must load the view
* by id and navigate with query-builder state instead.
*/
export function resourceRoute(
resourceType: string,
resourceId: string,
): string | null {
switch (resourceType) {
case ResourceType.dashboard:
return ROUTES.DASHBOARD.replace(':dashboardId', resourceId);
case ResourceType.alert: {
const params = new URLSearchParams({ [QueryParams.ruleId]: resourceId });
return `${ROUTES.EDIT_ALERTS}?${params.toString()}`;
}
case ResourceType.service:
return ROUTES.SERVICE_METRICS.replace(':servicename', resourceId);
case ResourceType.channel:
return ROUTES.CHANNELS_EDIT.replace(':channelId', resourceId);
case ResourceType.saved_view:
return null;
case ResourceType.logs_explorer:
return ROUTES.LOGS_EXPLORER;
case ResourceType.traces_explorer:
return ROUTES.TRACES_EXPLORER;
case ResourceType.metrics_explorer:
return ROUTES.METRICS_EXPLORER_EXPLORER;
default:
return null;
}
}

View File

@@ -101,8 +101,6 @@ function autoContextLabel(ctx: MessageContext): string {
return 'Panel (fullscreen)';
case 'dashboard_list':
return 'Dashboards';
case 'alert_detail':
return 'Current alert';
case 'alert_edit':
return 'Editing alert';
case 'alert_new':

View File

@@ -1,26 +0,0 @@
import type { ComponentProps } from 'react';
type MarkdownExternalLinkProps = ComponentProps<'a'> & {
// react-markdown passes `node` — accept and ignore it
// eslint-disable-next-line @typescript-eslint/no-explicit-any
node?: any;
};
export default function MarkdownExternalLink({
href,
children,
node: _node,
...props
}: MarkdownExternalLinkProps): JSX.Element {
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
data-testid="ai-markdown-link"
{...props}
>
{children}
</a>
);
}

View File

@@ -11,7 +11,6 @@ import { Message, MessageBlock } from '../../types';
import ActionsSection from '../ActionsSection';
import ActivityGroup, { ActivityItem } from '../ActivityGroup';
import { RichCodeBlock } from '../blocks';
import MarkdownExternalLink from '../MarkdownExternalLink/MarkdownExternalLink';
import { MessageContext } from '../MessageContext';
import MessageFeedback from '../MessageFeedback';
import UserMessageActions from '../UserMessageActions';
@@ -38,11 +37,7 @@ function SmartPre({ children }: { children?: React.ReactNode }): JSX.Element {
}
const MD_PLUGINS = [remarkGfm];
const MD_COMPONENTS = {
code: RichCodeBlock,
pre: SmartPre,
a: MarkdownExternalLink,
};
const MD_COMPONENTS = { code: RichCodeBlock, pre: SmartPre };
type RenderGroup =
| { kind: 'text'; id: string; content: string }

View File

@@ -50,7 +50,7 @@
font-size: 10px;
color: var(--l3-foreground);
white-space: nowrap;
padding-left: 8px;
padding-left: 2px;
border-left: 1px solid var(--l2-border);
}

View File

@@ -13,7 +13,6 @@ import { StreamingEventItem } from '../../types';
import ActivityGroup, { ActivityItem } from '../ActivityGroup';
import ApprovalCard from '../ApprovalCard';
import { RichCodeBlock } from '../blocks';
import MarkdownExternalLink from '../MarkdownExternalLink/MarkdownExternalLink';
import ClarificationForm from '../ClarificationForm';
import messageStyles from '../MessageBubble/MessageBubble.module.scss';
@@ -31,11 +30,7 @@ function SmartPre({ children }: { children?: React.ReactNode }): JSX.Element {
}
const MD_PLUGINS = [remarkGfm];
const MD_COMPONENTS = {
code: RichCodeBlock,
pre: SmartPre,
a: MarkdownExternalLink,
};
const MD_COMPONENTS = { code: RichCodeBlock, pre: SmartPre };
type RenderGroup =
| { kind: 'text'; id: string; content: string }

View File

@@ -45,7 +45,7 @@
line-height: 1.45;
}
.suggestions {
.emptySuggestions {
display: flex;
flex-direction: column;
gap: 6px;
@@ -53,8 +53,11 @@
max-width: 360px;
}
.suggestion {
width: 100%;
.emptyChip {
display: flex;
align-items: center;
justify-content: flex-start !important;
gap: 8px;
padding: 10px 14px;
border: 1px solid var(--l2-border);
border-radius: var(--radius-2);
@@ -63,16 +66,21 @@
font-size: 12.5px;
text-align: left;
cursor: pointer;
transition:
background 0.15s ease,
border-color 0.15s ease,
color 0.15s ease;
transition: all 0.15s ease;
line-height: 1.35;
overflow-wrap: anywhere;
&:hover {
background: var(--l2-background);
border-color: var(--l3-border);
color: var(--l1-foreground);
}
svg {
flex-shrink: 0;
color: var(--l3-foreground);
}
&:hover svg {
color: var(--accent-primary);
}
}

View File

@@ -1,5 +1,13 @@
import { useCallback, useEffect, useRef } from 'react';
import { Button } from '@signozhq/ui/button';
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
import {
Activity,
TriangleAlert,
ChartBar,
Search,
Zap,
} from '@signozhq/icons';
import Noz from 'components/Noz/Noz';
import logEvent from 'api/common/logEvent';
@@ -12,7 +20,29 @@ import MessageBubble from '../MessageBubble';
import StreamingMessage from '../StreamingMessage';
import styles from './VirtualizedMessages.module.scss';
import { useEmptyStateChips } from './useEmptyStateChips';
const SUGGESTIONS = [
{
icon: TriangleAlert,
text: 'Show me the top errors in the last hour',
},
{
icon: Activity,
text: 'What services have the highest latency?',
},
{
icon: ChartBar,
text: 'Give me an overview of system health',
},
{
icon: Search,
text: 'Find slow database queries',
},
{
icon: Zap,
text: 'Which endpoints have the most 5xx errors?',
},
];
const EMPTY_EVENTS: StreamingEventItem[] = [];
@@ -143,10 +173,8 @@ export default function VirtualizedMessages({
const showStreamingSlot =
isStreaming || Boolean(pendingApproval) || Boolean(pendingClarification);
const isEmptyState = messages.length === 0 && !showStreamingSlot;
const { chips: emptyStateChips } = useEmptyStateChips(isEmptyState);
if (isEmptyState) {
if (messages.length === 0 && !showStreamingSlot) {
return (
<div className={styles.empty}>
<div className={`${styles.emptyIcon} noz-wave`}>
@@ -156,22 +184,24 @@ export default function VirtualizedMessages({
<p className={styles.emptySubtitle}>
Ask questions about your traces, logs, metrics, and infrastructure.
</p>
<div className={styles.suggestions}>
{emptyStateChips.map((chip) => (
<div
key={chip.id}
className={styles.suggestion}
<div className={styles.emptySuggestions}>
{SUGGESTIONS.map((s) => (
<Button
key={s.text}
variant="outlined"
color="secondary"
className={styles.emptyChip}
onClick={(): void => {
void logEvent(AIAssistantEvents.SuggestedPromptClicked, {
promptId: chip.id,
promptId: s.text,
category: SuggestedPromptCategory.EmptyState,
});
onSendSuggestedPrompt(chip.text);
onSendSuggestedPrompt(s.text);
}}
data-testid={`empty-state-chip-${chip.id}`}
prefix={<s.icon size={14} />}
>
{chip.text}
</div>
{s.text}
</Button>
))}
</div>
</div>

View File

@@ -1,25 +0,0 @@
import type { ChipDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
/** Static empty-state chips used when the contextual chips API is unavailable. */
export const EMPTY_STATE_CHIPS_FALLBACK: ChipDTO[] = [
{
id: 'top_errors_last_hour',
text: 'Show me the top errors in the last hour',
},
{
id: 'highest_latency_services',
text: 'What services have the highest latency?',
},
{
id: 'system_health_overview',
text: 'Give me an overview of system health',
},
{
id: 'slow_database_queries',
text: 'Find slow database queries',
},
{
id: 'endpoints_5xx_errors',
text: 'Which endpoints have the most 5xx errors?',
},
];

View File

@@ -1,33 +0,0 @@
import { useMemo } from 'react';
import { useQuery } from 'react-query';
import { getEmptyStateChips } from 'api/ai-assistant/chat';
import type { ChipDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useResolvePageType } from 'hooks/aiAssistant/useResolvePageType';
import { EMPTY_STATE_CHIPS_FALLBACK } from './emptyStateChipsFallback';
interface UseEmptyStateChipsResult {
chips: ChipDTO[];
}
export function useEmptyStateChips(enabled: boolean): UseEmptyStateChipsResult {
const pageType = useResolvePageType();
const { data, isError } = useQuery(
[REACT_QUERY_KEY.AI_ASSISTANT_EMPTY_STATE_CHIPS, pageType],
({ signal }) => getEmptyStateChips(pageType, signal),
{ enabled },
);
const chips = useMemo(() => {
if (isError) {
return EMPTY_STATE_CHIPS_FALLBACK;
}
return data ?? [];
}, [data, isError]);
return { chips };
}

View File

@@ -1,7 +1,6 @@
import type { MessageContext } from 'api/ai-assistant/chat';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { AlertListTabs } from 'pages/AlertList/types';
import { matchPath } from 'react-router-dom';
/**
@@ -100,30 +99,6 @@ export function getAutoContexts(
// ── Alerts ────────────────────────────────────────────────────────────────
// Alert detail (overview / per-rule history) — `/alerts/overview?ruleId=…`
// or `/alerts/history?ruleId=…`. Mirrors dashboard_detail: resourceId is the
// rule id and shared metadata carries the URL time range when present.
if (
matchPath(pathname, { path: ROUTES.ALERT_OVERVIEW, exact: true }) ||
matchPath(pathname, { path: ROUTES.ALERT_HISTORY, exact: true })
) {
const ruleId = params.get(QueryParams.ruleId);
if (ruleId) {
return [
{
source: 'auto',
type: 'alert',
resourceId: ruleId,
metadata: {
page: 'alert_detail',
ruleId,
...sharedMetadata,
},
},
];
}
}
// Alert edit — `/alerts/edit?ruleId=…`.
if (matchPath(pathname, { path: ROUTES.EDIT_ALERTS, exact: true })) {
const ruleId = params.get(QueryParams.ruleId);
@@ -133,7 +108,7 @@ export function getAutoContexts(
source: 'auto',
type: 'alert',
resourceId: ruleId,
metadata: { page: 'alert_edit', ruleId },
metadata: { page: 'alert_edit' },
},
];
}
@@ -150,7 +125,6 @@ export function getAutoContexts(
];
}
// Triggered-alerts index — `/alerts/history` without a rule id.
if (matchPath(pathname, { path: ROUTES.ALERT_HISTORY, exact: true })) {
return [
{
@@ -165,18 +139,13 @@ export function getAutoContexts(
];
}
// Alerts index — `/alerts` with tab query param (defaults to Alert Rules).
if (matchPath(pathname, { path: ROUTES.LIST_ALL_ALERT, exact: true })) {
const page = resolveAlertsIndexPage(params.get(QueryParams.tab));
return [
{
source: 'auto',
type: 'alert',
resourceId: null,
metadata: {
page,
...(page === 'alerts_triggered' ? sharedMetadata : {}),
},
metadata: { page: 'alert_list' },
},
];
}
@@ -282,9 +251,8 @@ export function getAutoContexts(
// ── Metrics ───────────────────────────────────────────────────────────────
// Metrics explorer — `/metrics-explorer` and sub-routes (summary, explorer, views).
if (
matchPath(pathname, { path: ROUTES.METRICS_EXPLORER_BASE, exact: false })
matchPath(pathname, { path: ROUTES.METRICS_EXPLORER_EXPLORER, exact: false })
) {
return [
{
@@ -299,25 +267,9 @@ export function getAutoContexts(
];
}
// NOTE: Homepage (`/home`) and infrastructure monitoring
// (`/infrastructure-monitoring/*`) intentionally emit no auto-context here.
// They have no resource that maps to `MessageContextDTOType`, so attaching
// a chip would misrepresent the page (e.g. a bogus "metrics_explorer"
// context). Their `page_type` for empty-state chips is resolved directly
// from the route in `resolvePageType`.
return [];
}
type AlertsIndexPage = 'alert_list' | 'alerts_triggered';
function resolveAlertsIndexPage(tab: string | null): AlertsIndexPage {
if (tab === AlertListTabs.TRIGGERED_ALERTS) {
return 'alerts_triggered';
}
return 'alert_list';
}
/**
* Pulls metadata fields that any page may carry in its query string —
* `timeRange`, `query`, saved-view selectors, dashboard variables. Each

View File

@@ -1,74 +0,0 @@
import { PageTypeDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { matchPath } from 'react-router-dom';
import { getAutoContexts } from './getAutoContexts';
const PAGE_METADATA_TO_DTO: Record<string, PageTypeDTO> = {
dashboard_detail: PageTypeDTO.dashboard_detail,
dashboard_list: PageTypeDTO.dashboard_list,
panel_edit: PageTypeDTO.panel_edit,
panel_fullscreen: PageTypeDTO.panel_fullscreen,
logs_explorer: PageTypeDTO.logs_explorer,
trace_detail: PageTypeDTO.trace_detail,
traces_explorer: PageTypeDTO.traces_explorer,
metrics_explorer: PageTypeDTO.metrics_explorer,
service_detail: PageTypeDTO.service_detail,
services_list: PageTypeDTO.services_list,
alert_edit: PageTypeDTO.alert_edit,
alert_list: PageTypeDTO.alert_list,
alert_new: PageTypeDTO.alert_new,
alerts_triggered: PageTypeDTO.alerts_triggered,
};
interface ResolvePageTypeOptions {
/** Standalone `/ai-assistant` surface — no underlying observability page. */
isStandaloneAssistant?: boolean;
}
/**
* Maps the current URL (and assistant surface) to the backend `page_type`
* enum used by contextual empty-state chips.
*/
export function resolvePageType(
pathname: string,
search: string,
options?: ResolvePageTypeOptions,
): PageTypeDTO {
if (options?.isStandaloneAssistant) {
return PageTypeDTO.other;
}
// Pseudo-pages with no attachable resource: resolved straight from the
// route. They deliberately emit no auto-context chip (see `getAutoContexts`),
// so they can't be derived from `metadata.page` like the pages below.
if (matchPath(pathname, { path: ROUTES.HOME, exact: true })) {
return PageTypeDTO.homepage;
}
if (
matchPath(pathname, {
path: ROUTES.INFRASTRUCTURE_MONITORING_BASE,
exact: false,
})
) {
return PageTypeDTO.infra_entity_detail;
}
const contexts = getAutoContexts(pathname, search);
const page = contexts[0]?.metadata?.page;
if (typeof page === 'string') {
if (page === 'logs_explorer') {
const activeLogId = new URLSearchParams(search).get(QueryParams.activeLogId);
if (activeLogId) {
return PageTypeDTO.log_detail;
}
}
const mapped = PAGE_METADATA_TO_DTO[page];
if (mapped) {
return mapped;
}
}
return PageTypeDTO.other;
}

View File

@@ -1,10 +1,12 @@
/* eslint-disable sonarjs/cognitive-complexity */
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import type {
ErrorResponseDTO,
MessageActionDTO,
MessageSummaryDTOBlocksAnyOfItem,
} from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
@@ -35,7 +37,6 @@ import {
MessageBlock,
MessageRole,
} from '../types';
import { resolveAssistantErrorMessage } from '../utils/resolveAssistantErrorMessage';
// ---------------------------------------------------------------------------
// Types used by module-level helpers
@@ -398,7 +399,6 @@ async function runStreamingLoop(
}
throw Object.assign(new Error(event.error.message), {
retryAction: event.retryAction,
code: event.error.code,
});
} else if (event.type === 'conversation' && event.title) {
set((s) => {
@@ -484,6 +484,36 @@ function hasPendingInput(conversationId: string, get: StoreGetter): boolean {
return Boolean(stream?.pendingApproval || stream?.pendingClarification);
}
function parseErrorBody(value: unknown): string | null {
if (typeof value === 'string') {
try {
return parseErrorBody(JSON.parse(value));
} catch {
return null;
}
}
const message = (value as ErrorResponseDTO | undefined)?.error?.message;
return typeof message === 'string' && message.length > 0 ? message : null;
}
/**
* Returns the backend's `error.message` when `err` is a 429 axios response
* (typically from the threads API surface — createThread, sendMessage, approve,
* clarify, regenerate). Returns null for any other error so callers fall
* through to their generic copy.
*/
function rateLimitMessage(err: unknown): string | null {
if (axios.isAxiosError(err) && err.response?.status === 429) {
return parseErrorBody(err.response.data);
}
return null;
}
/**
* Commits an error message and removes the stream entry. When `isRateLimit`
* is true, the committed message is flagged so the feedback/regenerate bar
* is hidden — clicking regenerate would just 429 again.
*/
function finalizeStreamingError(
conversationId: string,
errorContent: string,
@@ -1144,11 +1174,14 @@ export const useAIAssistantStore = create<AIAssistantStore>()(
return;
}
console.error('[AIAssistant] sendMessage failed:', err);
const { message, isRateLimit } = resolveAssistantErrorMessage(
err,
'Something went wrong while fetching the response. Please try again.',
const rateLimit = rateLimitMessage(err);
finalizeStreamingError(
convId,
rateLimit ??
'Something went wrong while fetching the response. Please try again.',
set,
rateLimit !== null,
);
finalizeStreamingError(convId, message, set, isRateLimit);
}
},
@@ -1181,11 +1214,14 @@ export const useAIAssistantStore = create<AIAssistantStore>()(
return;
}
console.error('[AIAssistant] approveAction failed:', err);
const { message, isRateLimit } = resolveAssistantErrorMessage(
err,
'Something went wrong while processing the approval. Please try again.',
const rateLimit = rateLimitMessage(err);
finalizeStreamingError(
conversationId,
rateLimit ??
'Something went wrong while processing the approval. Please try again.',
set,
rateLimit !== null,
);
finalizeStreamingError(conversationId, message, set, isRateLimit);
}
},
@@ -1260,11 +1296,14 @@ export const useAIAssistantStore = create<AIAssistantStore>()(
return;
}
console.error('[AIAssistant] regenerateAssistantMessage failed:', err);
const { message, isRateLimit } = resolveAssistantErrorMessage(
err,
'Something went wrong while regenerating the response. Please try again.',
const rateLimit = rateLimitMessage(err);
finalizeStreamingError(
conversationId,
rateLimit ??
'Something went wrong while regenerating the response. Please try again.',
set,
rateLimit !== null,
);
finalizeStreamingError(conversationId, message, set, isRateLimit);
}
},
@@ -1326,11 +1365,14 @@ export const useAIAssistantStore = create<AIAssistantStore>()(
return;
}
console.error('[AIAssistant] submitClarification failed:', err);
const { message, isRateLimit } = resolveAssistantErrorMessage(
err,
'Something went wrong while processing your answers. Please try again.',
const rateLimit = rateLimitMessage(err);
finalizeStreamingError(
conversationId,
rateLimit ??
'Something went wrong while processing your answers. Please try again.',
set,
rateLimit !== null,
);
finalizeStreamingError(conversationId, message, set, isRateLimit);
}
},
})),

View File

@@ -1,91 +0,0 @@
import { AxiosError } from 'axios';
import { ErrorCodeDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import { resolveAssistantErrorMessage } from '../resolveAssistantErrorMessage';
const FALLBACK = 'Something went wrong. Please try again.';
describe('resolveAssistantErrorMessage', () => {
it('returns backend message for a known error code', () => {
const err = new AxiosError('Request failed');
err.response = {
status: 400,
data: {
error: {
code: ErrorCodeDTO.thread_busy,
message: 'This thread is busy. Try again shortly.',
},
},
} as AxiosError['response'];
expect(resolveAssistantErrorMessage(err, FALLBACK)).toStrictEqual({
message: 'This thread is busy. Try again shortly.',
isRateLimit: false,
});
});
it('falls back when error code is not in ErrorCodeDTO', () => {
const err = new AxiosError('Request failed');
err.response = {
status: 400,
data: {
error: {
code: 'future_unknown_code',
message: 'Backend-only message',
},
},
} as AxiosError['response'];
expect(resolveAssistantErrorMessage(err, FALLBACK)).toStrictEqual({
message: FALLBACK,
isRateLimit: false,
});
});
it('marks HTTP 429 responses as rate limited', () => {
const err = new AxiosError('Too many requests');
err.response = {
status: 429,
data: {
error: {
code: ErrorCodeDTO.hourly_message_limit,
message: 'Hourly limit reached.',
},
},
} as AxiosError['response'];
expect(resolveAssistantErrorMessage(err, FALLBACK)).toStrictEqual({
message: 'Hourly limit reached.',
isRateLimit: true,
});
});
it('uses backend message for known SSE rate-limit error codes', () => {
const err = Object.assign(new Error('Daily token limit exceeded.'), {
code: ErrorCodeDTO.daily_token_limit,
});
expect(resolveAssistantErrorMessage(err, FALLBACK)).toStrictEqual({
message: 'Daily token limit exceeded.',
isRateLimit: true,
});
});
it('marks 429 as rate limited even when error code is unknown', () => {
const err = new AxiosError('Too many requests');
err.response = {
status: 429,
data: {
error: {
code: 'future_unknown_code',
message: 'Too many requests',
},
},
} as AxiosError['response'];
expect(resolveAssistantErrorMessage(err, FALLBACK)).toStrictEqual({
message: FALLBACK,
isRateLimit: true,
});
});
});

View File

@@ -1,71 +0,0 @@
import { isAxiosError } from 'axios';
import {
ErrorCodeDTO,
type ErrorBodyDTO,
type ErrorResponseDTO,
} from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
export interface AssistantErrorResolution {
message: string;
isRateLimit: boolean;
}
function isErrorCodeDTO(code: string | undefined): code is ErrorCodeDTO {
return (
code !== undefined && (Object.values(ErrorCodeDTO) as string[]).includes(code)
);
}
const RATE_LIMIT_ERROR_CODES = new Set<ErrorCodeDTO>([
ErrorCodeDTO.rate_limit_override_exceeds_ceiling,
ErrorCodeDTO.thread_message_limit,
ErrorCodeDTO.connection_limit_exceeded,
ErrorCodeDTO.hourly_message_limit,
ErrorCodeDTO.daily_message_limit,
ErrorCodeDTO.daily_token_limit,
ErrorCodeDTO.daily_cost_limit,
ErrorCodeDTO.budget_exceeded,
]);
function isRateLimitError(code: string | undefined, err: unknown): boolean {
if (isAxiosError(err) && err.response?.status === 429) {
return true;
}
return isErrorCodeDTO(code) && RATE_LIMIT_ERROR_CODES.has(code);
}
function getErrorBody(err: unknown): ErrorBodyDTO | null {
if (isAxiosError(err)) {
return (err.response?.data as ErrorResponseDTO | undefined)?.error ?? null;
}
const code = (err as { code?: string } | undefined)?.code;
const message = err instanceof Error ? err.message : undefined;
if (!code || !message) {
return null;
}
return { code: code as ErrorCodeDTO, message };
}
/**
* Uses `error.message` when `error.code` is a known `ErrorCodeDTO`;
* otherwise returns `fallback`.
*/
export function resolveAssistantErrorMessage(
err: unknown,
fallback: string,
): AssistantErrorResolution {
const body = getErrorBody(err);
const isRateLimit = isRateLimitError(body?.code, err);
if (body && isErrorCodeDTO(body.code) && body.message.trim()) {
return {
message: body.message.trim(),
isRateLimit,
};
}
return { message: fallback, isRateLimit: Boolean(isRateLimit) };
}

View File

@@ -11,6 +11,7 @@ import CreateAlertV2 from 'container/CreateAlertV2';
import FormAlertRules, { AlertDetectionTypes } from 'container/FormAlertRules';
import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2';
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
import useReducedMotion from 'hooks/useReducedMotion';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
import { AlertListTabs } from 'pages/AlertList/types';
@@ -26,6 +27,7 @@ import './CreateAlertRule.styles.scss';
function CreateRules(): JSX.Element {
const [formInstance] = Form.useForm();
const prefersReducedMotion = useReducedMotion();
const compositeQuery = useGetCompositeQueryParam();
const queryParams = useUrlQuery();
const { safeNavigate } = useSafeNavigate();
@@ -41,7 +43,7 @@ function CreateRules(): JSX.Element {
useEffect(() => {
if (isTypeSelectionMode) {
logEvent('Alert: New alert data source selection page visited', {});
void logEvent('Alert: New alert data source selection page visited', {});
}
}, [isTypeSelectionMode]);
@@ -187,6 +189,7 @@ function CreateRules(): JSX.Element {
return (
<Tabs
destroyInactiveTabPane
animated={!prefersReducedMotion}
items={items}
activeKey={AlertListTabs.ALERT_RULES}
onChange={handleTabChange}

View File

@@ -29,7 +29,3 @@
color: var(--l2-foreground);
}
}
body.ai-assistant-panel-open .create-alert-v2-footer {
right: var(--ai-assistant-panel-width, 380px);
}

View File

@@ -39,7 +39,6 @@
.right-header {
display: flex;
gap: 16px;
align-items: center;
}
}

View File

@@ -15,7 +15,6 @@ import { Flex } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
import HeaderRightSection from 'components/HeaderRightSection/HeaderRightSection';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { adjustQueryForV5 } from 'components/QueryBuilderV2/utils';
import { QueryParams } from 'constants/query';
@@ -821,11 +820,6 @@ function NewWidget({
</Flex>
</div>
<div className="right-header">
<HeaderRightSection
enableAnnouncements={false}
enableShare={false}
enableFeedback={false}
/>
{showSwitchToViewModeButton && (
<Button
color="primary"

View File

@@ -68,6 +68,10 @@
border-left: unset;
border-radius: 0px 4px 4px 0px;
}
.new-view-btn {
margin-left: 8px;
}
}
.second-row {

View File

@@ -0,0 +1,80 @@
import { act, renderHook } from '@testing-library/react';
import useReducedMotion from 'hooks/useReducedMotion';
type ChangeListener = (e: Partial<MediaQueryListEvent>) => void;
function mockMatchMedia(matches: boolean): {
setMatches: (next: boolean) => void;
} {
const listeners: ChangeListener[] = [];
let currentMatches = matches;
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn(() => ({
get matches() {
return currentMatches;
},
addEventListener: jest.fn((_: string, fn: ChangeListener) => {
listeners.push(fn);
}),
removeEventListener: jest.fn(),
})),
});
return {
setMatches: (next: boolean): void => {
currentMatches = next;
listeners.forEach((fn) => fn({ matches: next } as MediaQueryListEvent));
},
};
}
describe('useReducedMotion', () => {
it('returns false when prefers-reduced-motion is not set', () => {
mockMatchMedia(false);
const { result } = renderHook(() => useReducedMotion());
expect(result.current).toBe(false);
});
it('returns true when prefers-reduced-motion: reduce is active', () => {
mockMatchMedia(true);
const { result } = renderHook(() => useReducedMotion());
expect(result.current).toBe(true);
});
it('updates when system preference changes at runtime', () => {
const { setMatches } = mockMatchMedia(false);
const { result } = renderHook(() => useReducedMotion());
expect(result.current).toBe(false);
act(() => {
setMatches(true);
});
expect(result.current).toBe(true);
act(() => {
setMatches(false);
});
expect(result.current).toBe(false);
});
it('removes event listener on unmount', () => {
const removeEventListener = jest.fn();
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn(() => ({
matches: false,
addEventListener: jest.fn(),
removeEventListener,
})),
});
const { unmount } = renderHook(() => useReducedMotion());
unmount();
expect(removeEventListener).toHaveBeenCalledWith(
'change',
expect.any(Function),
);
});
});

View File

@@ -1,48 +0,0 @@
import { renderHook } from '@testing-library/react';
import { PageTypeDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import type { AIAssistantVariant } from 'container/AIAssistant/VariantContext';
import ROUTES from 'constants/routes';
import { useResolvePageType } from '../useResolvePageType';
const mockUseLocation = jest.fn();
const mockUseVariant = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: (): unknown => mockUseLocation(),
}));
jest.mock('container/AIAssistant/VariantContext', () => ({
useVariant: (): unknown => mockUseVariant(),
}));
function setup(
pathname: string,
search: string,
variant: AIAssistantVariant,
): PageTypeDTO {
mockUseLocation.mockReturnValue({ pathname, search });
mockUseVariant.mockReturnValue(variant);
return renderHook(() => useResolvePageType()).result.current;
}
describe('useResolvePageType', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('returns other for the standalone "page" assistant surface', () => {
const pathname = ROUTES.DASHBOARD.replace(':dashboardId', 'dash-123');
expect(setup(pathname, '', 'page')).toBe(PageTypeDTO.other);
});
it('resolves the underlying page type for embedded variants', () => {
const pathname = ROUTES.DASHBOARD.replace(':dashboardId', 'dash-123');
expect(setup(pathname, '', 'panel')).toBe(PageTypeDTO.dashboard_detail);
expect(setup(pathname, '', 'modal')).toBe(PageTypeDTO.dashboard_detail);
});
});

View File

@@ -1,23 +0,0 @@
import { useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { PageTypeDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import { resolvePageType } from 'container/AIAssistant/resolvePageType';
import { useVariant } from 'container/AIAssistant/VariantContext';
/**
* React hook wrapper around `resolvePageType` that derives the current
* `page_type` from the active location and assistant variant.
*/
export function useResolvePageType(): PageTypeDTO {
const location = useLocation();
const variant = useVariant();
return useMemo(
() =>
resolvePageType(location.pathname, location.search, {
isStandaloneAssistant: variant === 'page',
}),
[location.pathname, location.search, variant],
);
}

View File

@@ -0,0 +1,20 @@
import { useEffect, useState } from 'react';
function useReducedMotion(): boolean {
const [prefersReducedMotion, setPrefersReducedMotion] = useState<boolean>(
() => window.matchMedia('(prefers-reduced-motion: reduce)').matches,
);
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
const onChange = (e: MediaQueryListEvent): void => {
setPrefersReducedMotion(e.matches);
};
mediaQuery.addEventListener('change', onChange);
return (): void => mediaQuery.removeEventListener('change', onChange);
}, []);
return prefersReducedMotion;
}
export default useReducedMotion;

View File

@@ -8,6 +8,7 @@ import AllAlertRules from 'container/ListAlertRules';
import { PlannedDowntime } from 'container/PlannedDowntime/PlannedDowntime';
import RoutingPolicies from 'container/RoutingPolicies';
import TriggeredAlerts from 'container/TriggeredAlerts';
import useReducedMotion from 'hooks/useReducedMotion';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
import { GalleryVerticalEnd, Pyramid } from '@signozhq/icons';
@@ -21,6 +22,7 @@ function AllAlertList(): JSX.Element {
const urlQuery = useUrlQuery();
const location = useLocation();
const { safeNavigate } = useSafeNavigate();
const prefersReducedMotion = useReducedMotion();
const tab = urlQuery.get('tab');
const subTab = urlQuery.get('subTab');
@@ -101,6 +103,7 @@ function AllAlertList(): JSX.Element {
return (
<Tabs
destroyInactiveTabPane
animated={!prefersReducedMotion}
items={items}
activeKey={tab || AlertListTabs.ALERT_RULES}
onChange={(tab): void => {

View File

@@ -5,6 +5,7 @@ import DBCall from 'container/MetricsApplication/Tabs/DBCall';
import External from 'container/MetricsApplication/Tabs/External';
import Overview from 'container/MetricsApplication/Tabs/Overview';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import useReducedMotion from 'hooks/useReducedMotion';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
@@ -20,6 +21,7 @@ function MetricsApplication(): JSX.Element {
}>();
const activeKey = useMetricsApplicationTabKey();
const prefersReducedMotion = useReducedMotion();
const urlQuery = useUrlQuery();
const { safeNavigate } = useSafeNavigate();
@@ -56,6 +58,7 @@ function MetricsApplication(): JSX.Element {
activeKey={activeKey}
className="service-route-tab"
destroyInactiveTabPane
animated={!prefersReducedMotion}
onChange={onTabChange}
/>
</div>

View File

@@ -1,18 +0,0 @@
import { Redirect, useParams } from 'react-router-dom';
import { TraceDetailV2URLProps } from 'types/api/trace/getTraceV2';
// Legacy /trace-old/:id now redirects to the current /trace/:id view,
// preserving the query string and hash.
export default function TraceDetailOldRedirect(): JSX.Element {
const { id } = useParams<TraceDetailV2URLProps>();
return (
<Redirect
to={{
pathname: `/trace/${id}`,
search: window.location.search,
hash: window.location.hash,
}}
/>
);
}

View File

@@ -0,0 +1,25 @@
import { Redirect, useParams } from 'react-router-dom';
import getLocalStorageKey from 'api/browser/localstorage/get';
import { LOCALSTORAGE } from 'constants/localStorage';
import { TraceDetailV2URLProps } from 'types/api/trace/getTraceV2';
import TraceDetailsV3 from '../TraceDetailsV3';
export default function TraceDetailV3Page(): JSX.Element {
const { id } = useParams<TraceDetailV2URLProps>();
const preferOld =
getLocalStorageKey(LOCALSTORAGE.TRACE_DETAILS_PREFER_OLD_VIEW) === 'true';
if (preferOld) {
return (
<Redirect
to={{
pathname: `/trace-old/${id}`,
search: window.location.search,
}}
/>
);
}
return <TraceDetailsV3 />;
}

View File

@@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { useCallback, useRef, useState } from 'react';
import { useParams } from 'react-router-dom';
import { Button } from '@signozhq/ui/button';
import {
@@ -8,9 +8,11 @@ import {
TooltipTrigger,
} from '@signozhq/ui/tooltip';
import { Skeleton } from 'antd';
import setLocalStorageKey from 'api/browser/localstorage/set';
import cx from 'classnames';
import FieldsSelector from 'components/FieldsSelector';
import HttpStatusBadge from 'components/HttpStatusBadge/HttpStatusBadge';
import { LOCALSTORAGE } from 'constants/localStorage';
import ROUTES from 'constants/routes';
import { convertTimeToRelevantUnit } from 'container/TraceDetail/utils';
import dayjs from 'dayjs';
@@ -19,6 +21,7 @@ import {
ArrowLeft,
CalendarClock,
ChartPie,
CornerUpLeft,
Server,
Timer,
} from '@signozhq/icons';
@@ -90,6 +93,18 @@ function TraceDetailsHeader({
const setPreviewFields = useTraceStore((s) => s.setPreviewFields);
const logTraceEvent = useTraceDetailLogEvent('v3', traceID || '');
const pageLoadedAtRef = useRef(Date.now());
const handleSwitchToOldView = useCallback((): void => {
logTraceEvent(TraceDetailEvents.ViewSwitched, {
[TraceDetailEventKeys.From]: 'v3',
[TraceDetailEventKeys.To]: 'v2',
[TraceDetailEventKeys.DwellMs]: Date.now() - pageLoadedAtRef.current,
});
setLocalStorageKey(LOCALSTORAGE.TRACE_DETAILS_PREFER_OLD_VIEW, 'true');
const oldUrl = `/trace-old/${traceID}${window.location.search}`;
history.replace(oldUrl);
}, [traceID, logTraceEvent]);
const handleToggleAnalytics = useCallback((): void => {
logTraceEvent(TraceDetailEvents.AnalyticsPanelToggled, {
@@ -157,6 +172,20 @@ function TraceDetailsHeader({
{!isFilterExpanded && (
<TooltipProvider>
<div className={styles.headerActions}>
<TooltipRoot>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
color="secondary"
aria-label="Switch to legacy trace view"
onClick={handleSwitchToOldView}
>
<CornerUpLeft size={14} />
</Button>
</TooltipTrigger>
<TooltipContent>Switch to legacy trace view</TooltipContent>
</TooltipRoot>
<TooltipRoot>
<TooltipTrigger asChild>
<Button

View File

@@ -26,6 +26,13 @@ jest.mock('react-router-dom', () => ({
useParams: (): { id: string } => ({ id: 'trace-123' }),
}));
const mockSetLocalStorageKey = jest.fn();
jest.mock('api/browser/localstorage/set', () => ({
__esModule: true,
default: (key: string, value: string): void =>
mockSetLocalStorageKey(key, value),
}));
jest.mock(
'../../TraceWaterfall/TraceWaterfallStates/Success/Filters/Filters',
() => ({
@@ -90,11 +97,15 @@ describe('TraceDetailsHeader back button', () => {
describe('TraceDetailsHeader action cluster', () => {
beforeEach(() => {
mockReplace.mockClear();
mockSetLocalStorageKey.mockClear();
});
it('does not render the action buttons while data is still loading', () => {
render(<TraceDetailsHeader {...baseProps} isDataLoaded={false} />);
expect(
screen.queryByRole('button', { name: /switch to legacy trace view/i }),
).not.toBeInTheDocument();
expect(
screen.queryByRole('button', { name: /^analytics$/i }),
).not.toBeInTheDocument();
@@ -103,9 +114,12 @@ describe('TraceDetailsHeader action cluster', () => {
).not.toBeInTheDocument();
});
it('renders Analytics and Settings action buttons once data is loaded', () => {
it('renders Legacy View, Analytics, and Settings action buttons once data is loaded', () => {
render(<TraceDetailsHeader {...baseProps} isDataLoaded />);
expect(
screen.getByRole('button', { name: /switch to legacy trace view/i }),
).toBeInTheDocument();
expect(
screen.getByRole('button', { name: /^analytics$/i }),
).toBeInTheDocument();
@@ -114,6 +128,23 @@ describe('TraceDetailsHeader action cluster', () => {
).toBeInTheDocument();
});
it('routes to the legacy trace view and persists the preference on click', () => {
render(<TraceDetailsHeader {...baseProps} isDataLoaded />);
fireEvent.click(
screen.getByRole('button', { name: /switch to legacy trace view/i }),
);
expect(mockSetLocalStorageKey).toHaveBeenCalledWith(
'TRACE_DETAILS_PREFER_OLD_VIEW',
'true',
);
expect(mockReplace).toHaveBeenCalledTimes(1);
expect(mockReplace).toHaveBeenCalledWith(
expect.stringContaining('/trace-old/trace-123'),
);
});
it('toggles the AnalyticsPanel open state when the Analytics button is clicked', () => {
render(<TraceDetailsHeader {...baseProps} isDataLoaded />);

View File

@@ -22,6 +22,7 @@ export enum TraceDetailEventKeys {
SpanPanelVariant = 'spanPanelVariant',
ColorByField = 'colorByField',
PreviewFieldsCount = 'previewFieldsCount',
EntryPreferOldView = 'entryPreferOldView',
// View switched
From = 'from',
To = 'to',

View File

@@ -179,6 +179,8 @@ function TraceDetailsV3(): JSX.Element {
SpanDetailVariant.DOCKED_RIGHT,
[TraceDetailEventKeys.ColorByField]: colorByField.name,
[TraceDetailEventKeys.PreviewFieldsCount]: previewFieldsCount,
[TraceDetailEventKeys.EntryPreferOldView]:
getLocalStorageKey(LOCALSTORAGE.TRACE_DETAILS_PREFER_OLD_VIEW) === 'true',
});
}, [
traceId,

View File

@@ -1,72 +0,0 @@
import getLocalStorageApi from 'api/browser/localstorage/get';
import { ENVIRONMENT } from 'constants/env';
import { LOCALSTORAGE } from 'constants/localStorage';
import { getSigNozInstanceUrl } from './signozInstanceUrl';
jest.mock('api/browser/localstorage/get');
jest.mock('constants/env', () => ({
ENVIRONMENT: { baseURL: '' },
}));
const mockedGet = getLocalStorageApi as jest.MockedFunction<
typeof getLocalStorageApi
>;
function setOrigin(origin: string): void {
Object.defineProperty(window, 'location', {
value: { origin },
writable: true,
configurable: true,
});
}
describe('getSigNozInstanceUrl', () => {
beforeEach(() => {
mockedGet.mockReset();
ENVIRONMENT.baseURL = '';
setOrigin('http://localhost');
});
it('returns the localStorage override when present', () => {
mockedGet.mockReturnValue('https://override.example.com');
ENVIRONMENT.baseURL = 'https://build.example.com';
setOrigin('https://browser.example.com');
expect(getSigNozInstanceUrl()).toBe('https://override.example.com');
expect(mockedGet).toHaveBeenCalledWith(
LOCALSTORAGE.ACTIVE_SIGNOZ_INSTANCE_URL,
);
});
it('ignores a blank/whitespace-only override and falls through', () => {
mockedGet.mockReturnValue(' ');
ENVIRONMENT.baseURL = 'https://build.example.com';
expect(getSigNozInstanceUrl()).toBe('https://build.example.com');
});
it('returns the build-time baseURL when no override exists (cloud)', () => {
mockedGet.mockReturnValue(null);
ENVIRONMENT.baseURL = 'https://build.example.com';
setOrigin('https://browser.example.com');
expect(getSigNozInstanceUrl()).toBe('https://build.example.com');
});
it('falls back to window.location.origin when baseURL is empty (self-hosted)', () => {
mockedGet.mockReturnValue(null);
ENVIRONMENT.baseURL = '';
setOrigin('https://self-hosted.example.com');
expect(getSigNozInstanceUrl()).toBe('https://self-hosted.example.com');
});
it('returns an empty string when nothing is resolvable', () => {
mockedGet.mockReturnValue(null);
ENVIRONMENT.baseURL = '';
setOrigin('');
expect(getSigNozInstanceUrl()).toBe('');
});
});

View File

@@ -2,37 +2,7 @@ import getLocalStorageApi from 'api/browser/localstorage/get';
import setLocalStorageApi from 'api/browser/localstorage/set';
import { LOCALSTORAGE } from 'constants/localStorage';
import { ENVIRONMENT } from 'constants/env';
import { getBaseUrl } from 'utils/basePath';
/**
* Resolves the SigNoz instance URL sent to the AI Assistant backend via the
* `X-SigNoz-URL` header. Resolution order:
*
* 1. `ACTIVE_SIGNOZ_INSTANCE_URL` localStorage override (cloud instance switcher).
* 2. `ENVIRONMENT.baseURL` — the build-time absolute endpoint
* (`VITE_FRONTEND_API_ENDPOINT`). Non-empty on SigNoz Cloud builds.
* 3. `getBaseUrl()` (origin + base path) — fallback for self-hosted builds.
*
* Decision (self-hosted fallback to the browser's current URL):
* Self-hosted builds ship with `VITE_FRONTEND_API_ENDPOINT` unset because the
* frontend talks to its backend over relative paths, so `ENVIRONMENT.baseURL`
* is an empty string. That previously caused `X-SigNoz-URL` to be sent empty,
* which the AI backend rejects (`missing_signoz_url` / `invalid_signoz_url`)
* rather than treating as "omitted". We deliberately fall back to the URL the
* user is actually accessing the instance from so the header is always
* populated. We use `getBaseUrl()` rather than raw `window.location.origin`
* because a self-hosted instance may be served under a base path (e.g.
* `https://host/signoz`); the backend needs that full base to reach the API.
*
* Known tradeoff: this reflects the *browser's* view of the instance. For
* deployments where the instance is not reachable from the cloud AI backend at
* that URL (air-gapped, internal-only DNS, private IPs, behind a reverse proxy
* on a different external host), it will be unreachable and the AI backend may
* fail to query the instance. Those deployments must set an explicit,
* externally reachable URL via the localStorage override. A first-class admin
* config for the public instance URL is the proper long-term fix; this fallback
* is the pragmatic default until that exists.
*/
export function getSigNozInstanceUrl(): string {
const fromStorage = getLocalStorageApi(
LOCALSTORAGE.ACTIVE_SIGNOZ_INSTANCE_URL,
@@ -42,11 +12,7 @@ export function getSigNozInstanceUrl(): string {
return fromStorage;
}
if (ENVIRONMENT.baseURL) {
return ENVIRONMENT.baseURL;
}
return getBaseUrl();
return ENVIRONMENT.baseURL;
}
export function setSigNozInstanceUrl(url: string | null | undefined): void {

View File

@@ -1,186 +0,0 @@
/**
* Stylelint rule: local/class-name-pattern
*
* Enforces camelCase class names in CSS modules.
* With Vite's `localsConvention: 'camelCaseOnly'`, kebab-case is converted but
* using camelCase directly avoids confusion.
*
* BAD:
* .my-class { } // converted to myClass, but confusing
* .my_class { } // converted to myClass, but confusing
* .MyClass { } // PascalCase not conventional
*
* GOOD:
* .myClass { }
* .alertHistory { }
* .statsCard { }
*/
import stylelint from 'stylelint';
const ruleName = 'local/class-name-pattern';
const messages = stylelint.utils.ruleMessages(ruleName, {
kebabCase: (className) =>
`Class "${className}" is not on camelCase. Use instead: "${toCamelCase(className)}".`,
snakeCase: (className) =>
`Class "${className}" is not on camelCase. Use instead: "${toCamelCase(className)}".`,
pascalCase: (className) =>
`Class "${className}" is not on camelCase. Use instead: "${className.charAt(0).toLowerCase() + className.slice(1)}".`,
});
function toCamelCase(str) {
return str
.split(/[-_]/)
.map((part, i) =>
i === 0
? part.toLowerCase()
: part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(),
)
.join('');
}
function isKebabCase(str) {
return str.includes('-');
}
function isSnakeCase(str) {
return str.includes('_');
}
function isPascalCase(str) {
return /^[A-Z][a-zA-Z0-9]*$/.test(str);
}
const CLASS_PATTERN = /\.([a-zA-Z_][a-zA-Z0-9_-]*)/g;
const DEFAULT_THIRD_PARTY_PREFIXES = [
'ant-', // Ant Design
'rc-', // rc-components (Ant Design internals)
'recharts-', // Recharts
'uplot-', // uPlot
'u-', // uPlot legacy
'leaflet-', // Leaflet
'monaco-', // Monaco editor
'react-resizable', // react-resizable
'cm-', // CodeMirror
];
// Bare `:global { ... }` block (no parens) makes all descendants global.
// `:global(.foo)` is a per-selector escape — handled separately by globalRanges().
function hasBareGlobalAncestor(node) {
let current = node.parent;
while (current) {
const selector = current.selector;
if (selector && /:global(?!\s*\()/.test(selector)) {
return true;
}
current = current.parent;
}
return false;
}
// Return list of [start, end) index ranges inside `selector` that fall within a
// balanced `:global(...)` argument list. Class matches inside these ranges are
// third-party and should be skipped.
function globalRanges(selector) {
const ranges = [];
const re = /:global\s*\(/g;
let match;
while ((match = re.exec(selector)) !== null) {
const argStart = match.index + match[0].length;
let depth = 1;
let i = argStart;
while (i < selector.length && depth > 0) {
const ch = selector[i];
if (ch === '(') {
depth++;
} else if (ch === ')') {
depth--;
}
if (depth > 0) {
i++;
}
}
ranges.push([argStart, i]);
re.lastIndex = i;
}
return ranges;
}
function indexInRanges(index, ranges) {
for (const [start, end] of ranges) {
if (index >= start && index < end) {
return true;
}
}
return false;
}
const rule = (primaryOption, secondaryOptions) => {
return (root, result) => {
if (!primaryOption) {
return;
}
const userPrefixes =
(secondaryOptions && secondaryOptions.ignoreThirdPartyPrefixes) || [];
const allPrefixes = [...DEFAULT_THIRD_PARTY_PREFIXES, ...userPrefixes];
root.walkRules((ruleNode) => {
const selector = ruleNode.selector;
// Bare `:global { }` block makes all descendants global — skip entirely.
if (hasBareGlobalAncestor(ruleNode)) {
return;
}
const ranges = globalRanges(selector);
let match;
CLASS_PATTERN.lastIndex = 0;
while ((match = CLASS_PATTERN.exec(selector)) !== null) {
const className = match[1];
// Skip classes inside `:global(...)` ranges of this selector.
if (indexInRanges(match.index, ranges)) {
continue;
}
// Skip third-party library classes
if (allPrefixes.some((prefix) => className.startsWith(prefix))) {
continue;
}
if (isKebabCase(className)) {
stylelint.utils.report({
message: messages.kebabCase(className),
node: ruleNode,
result,
ruleName,
});
} else if (isSnakeCase(className)) {
stylelint.utils.report({
message: messages.snakeCase(className),
node: ruleNode,
result,
ruleName,
});
} else if (isPascalCase(className)) {
stylelint.utils.report({
message: messages.pascalCase(className),
node: ruleNode,
result,
ruleName,
});
}
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,164 +0,0 @@
/**
* Stylelint rule: local/no-bare-element-selectors
*
* Prevents bare element selectors at root level in CSS modules.
* Bare elements affect ALL instances of that element within component scope,
* often unintentionally.
*
* BAD:
* div { }
* span { padding: 4px; }
* p { margin: 0; }
*
* GOOD:
* .container { }
* .title { }
*
* ALLOWED (nested under class):
* .container {
* p { margin: 0; } // Scoped to .container
* }
*/
import stylelint from 'stylelint';
const ruleName = 'local/no-bare-element-selectors';
const messages = stylelint.utils.ruleMessages(ruleName, {
unexpected: (element) =>
`Bare element selector "${element}" at root level affects all instances. Use class selector or nest under a class.`,
unexpectedInCompound: (element, full) =>
`Bare element "${element}" in unscoped selector "${full}" at root level matches every "<${element}>" in the module. Anchor the selector with a class (e.g., ".container ${element}") or use :global() if intentional.`,
});
const ELEMENT_PATTERN = /^[a-z][a-z0-9]*$/i;
const EXCLUDED_ELEMENTS = new Set(['html', 'body', 'root']);
function isPartBareElement(part) {
const trimmed = part.trim();
if (!trimmed) {
return false;
}
// Strip pseudo suffixes (`:hover`, `::before`) — element part is what's before
const elementOnly = trimmed.split(':')[0];
if (!elementOnly) {
return false;
}
// Skip class/id/attribute parts
if (/[.#[]/.test(elementOnly)) {
return false;
}
return (
ELEMENT_PATTERN.test(elementOnly) &&
!EXCLUDED_ELEMENTS.has(elementOnly.toLowerCase())
);
}
function isBareElement(selector) {
const trimmed = selector.trim();
// Skip combined selectors
if (/[,\s>+~]/.test(trimmed)) {
return false;
}
// Skip pseudo-selectors
if (trimmed.includes(':')) {
return false;
}
// Skip class/id/attribute selectors
if (/[.#[]/.test(trimmed)) {
return false;
}
return (
ELEMENT_PATTERN.test(trimmed) && !EXCLUDED_ELEMENTS.has(trimmed.toLowerCase())
);
}
// Split a compound selector on combinators (`>`, `+`, `~`, descendant space)
// while keeping each part intact. Returns an array of selector parts.
function splitOnCombinators(selector) {
return selector
.split(/\s*[>+~]\s*|\s+/)
.map((p) => p.trim())
.filter(Boolean);
}
function isInsideGlobal(selector) {
return /:global\s*\(/.test(selector);
}
const KEYFRAME_ATRULES = new Set([
'keyframes',
'-webkit-keyframes',
'-moz-keyframes',
'-o-keyframes',
]);
// Rule's effective parent is root if all ancestors above it are atrules
// (e.g. `@media`, `@supports`). A bare `div` inside `@media { }` at top level
// still matches every `<div>` in the module. Exclude `@keyframes` — its
// `from`/`to`/`0%` children are not element selectors.
function isEffectivelyTopLevel(node) {
let parent = node.parent;
while (parent && parent.type === 'atrule') {
if (KEYFRAME_ATRULES.has(parent.name.toLowerCase())) {
return false;
}
parent = parent.parent;
}
return Boolean(parent) && parent.type === 'root';
}
const rule = (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkRules((ruleNode) => {
if (!isEffectivelyTopLevel(ruleNode)) {
return;
}
const selectors = ruleNode.selector.split(',').map((s) => s.trim());
for (const selector of selectors) {
if (isInsideGlobal(selector)) {
continue;
}
if (isBareElement(selector)) {
stylelint.utils.report({
message: messages.unexpected(selector),
node: ruleNode,
result,
ruleName,
});
continue;
}
// Check combined selectors part-by-part. Only flag when the compound
// has NO class/id anchor — `.container > div` is already scoped, but
// `div + span` at root affects every matching descendant in the module.
if (/[\s>+~]/.test(selector) && !/[.#]/.test(selector)) {
const parts = splitOnCombinators(selector);
for (const part of parts) {
if (isPartBareElement(part)) {
stylelint.utils.report({
message: messages.unexpectedInCompound(part.split(':')[0], selector),
node: ruleNode,
result,
ruleName,
});
}
}
}
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,97 +0,0 @@
/**
* Stylelint rule: local/no-deep-nesting
*
* Prevents deep nesting in CSS modules (max 3 levels).
* Deep nesting creates specificity wars and hard-to-override styles.
*
* BAD:
* .container { .wrapper { .inner { .content { } } } } // 4 levels
*
* GOOD:
* .container { }
* .containerWrapper { }
* .containerContent { }
*
* Allowed nesting (pseudo-classes/elements):
* .button { &:hover { } &::before { } }
*/
import stylelint from 'stylelint';
const ruleName = 'local/no-deep-nesting';
const DEFAULT_MAX_DEPTH = 3;
const messages = stylelint.utils.ruleMessages(ruleName, {
tooDeep: (depth, max) =>
`Nesting depth ${depth} exceeds maximum of ${max}. Flatten selectors for CSS modules.`,
});
function isPseudoSelector(selector) {
return /^&?:/.test(selector.trim());
}
function isParentReference(selector) {
return /^&[.#[]/.test(selector.trim());
}
function countNestingDepth(rule, depth = 0) {
const selector = rule.selector || '';
// Don't count pseudo-selectors or parent references toward depth
if (isPseudoSelector(selector) || isParentReference(selector)) {
// Still check children
let maxChildDepth = depth;
rule.walkRules?.((child) => {
const childDepth = countNestingDepth(child, depth);
maxChildDepth = Math.max(maxChildDepth, childDepth);
});
return maxChildDepth;
}
const currentDepth = depth + 1;
let maxDepth = currentDepth;
rule.walkRules?.((child) => {
const childDepth = countNestingDepth(child, currentDepth);
maxDepth = Math.max(maxDepth, childDepth);
});
return maxDepth;
}
const rule = (primaryOption, secondaryOptions) => {
return (root, result) => {
if (!primaryOption) {
return;
}
const maxDepth =
secondaryOptions && Number.isInteger(secondaryOptions.maxDepth)
? secondaryOptions.maxDepth
: DEFAULT_MAX_DEPTH;
root.walkRules((ruleNode) => {
// Only check top-level rules
if (ruleNode.parent.type !== 'root' && ruleNode.parent.type !== 'atrule') {
return;
}
const depth = countNestingDepth(ruleNode);
if (depth > maxDepth) {
stylelint.utils.report({
message: messages.tooDeep(depth, maxDepth),
node: ruleNode,
result,
ruleName,
});
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,55 +0,0 @@
/**
* Stylelint rule: local/no-id-selectors
*
* Prevents ID selectors in CSS modules.
* IDs create high specificity, can't be reused, and defeat CSS modules scoping purpose.
*
* BAD:
* #myComponent { }
* .container #header { }
*
* GOOD:
* .myComponent { }
* .containerHeader { }
*/
import stylelint from 'stylelint';
const ruleName = 'local/no-id-selectors';
const messages = stylelint.utils.ruleMessages(ruleName, {
unexpected: (selector) =>
`ID selector "${selector}" not allowed in CSS modules. Use class selector instead.`,
});
const ID_PATTERN = /#[a-zA-Z_][a-zA-Z0-9_-]*/g;
const rule = (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkRules((ruleNode) => {
const selector = ruleNode.selector;
const matches = selector.match(ID_PATTERN);
if (matches) {
for (const match of matches) {
stylelint.utils.report({
message: messages.unexpected(match),
node: ruleNode,
result,
ruleName,
});
}
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -1,168 +0,0 @@
/**
* Stylelint rule: local/prefer-css-variables
*
* Warns on hardcoded colors in CSS modules.
* Use CSS variables for consistent theming.
*
* BAD:
* color: #ff0000;
* background: rgb(255, 0, 0);
* border: 1px solid blue;
*
* GOOD:
* color: var(--l1-foreground);
* background: var(--primary-background);
* border: 1px solid var(--l2-border);
*
* ALLOWED:
* transparent, inherit, currentColor, none
* Colors inside var() fallbacks
*/
import stylelint from 'stylelint';
const ruleName = 'local/prefer-css-variables';
const messages = stylelint.utils.ruleMessages(ruleName, {
hardcodedColor: (value, property) =>
`Hardcoded color "${value}" in "${property}". Use a semantic CSS variable instead (e.g., var(--l1-foreground), var(--primary-background)). See docs/css-modules-guide.md.`,
});
const COLOR_PROPERTIES = new Set([
'color',
'background',
'background-color',
'background-image',
'border',
'border-color',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-image',
'border-image-source',
'outline',
'outline-color',
'box-shadow',
'text-shadow',
'fill',
'stroke',
'caret-color',
'text-decoration-color',
'column-rule-color',
'mask',
'mask-image',
]);
const ALLOWED_VALUES = new Set([
'transparent',
'inherit',
'initial',
'unset',
'currentcolor',
'none',
'auto',
]);
const HEX_PATTERN = /#[0-9a-fA-F]{3,8}\b/;
const RGB_PATTERN = /rgba?\s*\([^)]+\)/i;
const HSL_PATTERN = /hsla?\s*\([^)]+\)/i;
const NAMED_COLOR_PATTERN =
/\b(red|blue|green|yellow|orange|purple|pink|black|white|gray|grey|cyan|magenta|brown|navy|teal|olive|maroon|lime|aqua|fuchsia|silver)\b/i;
// Strip balanced `fn(...)` sections (e.g. `var(...)`, `url(...)`) from value.
// Handles nested parens by counting depth.
function stripBalancedFn(value, fnName) {
const needle = `${fnName}(`;
let out = '';
let i = 0;
while (i < value.length) {
if (value.startsWith(needle, i)) {
let depth = 1;
i += needle.length;
while (i < value.length && depth > 0) {
const ch = value[i];
if (ch === '(') {
depth++;
} else if (ch === ')') {
depth--;
}
i++;
}
} else {
out += value[i];
i++;
}
}
return out;
}
function containsHardcodedColor(value) {
// Strip url(...) first — paths/fragments may contain hex-like or color-named substrings
let scanned = value.includes('url(') ? stripBalancedFn(value, 'url') : value;
// Strip var(...) — color tokens inside var fallbacks are allowed
if (scanned.includes('var(')) {
scanned = stripBalancedFn(scanned, 'var');
if (!scanned.trim()) {
return null;
}
}
const lower = scanned.toLowerCase();
if (ALLOWED_VALUES.has(lower)) {
return null;
}
if (HEX_PATTERN.test(scanned)) {
return scanned.match(HEX_PATTERN)[0];
}
if (RGB_PATTERN.test(scanned)) {
return scanned.match(RGB_PATTERN)[0];
}
if (HSL_PATTERN.test(scanned)) {
return scanned.match(HSL_PATTERN)[0];
}
if (NAMED_COLOR_PATTERN.test(scanned)) {
return scanned.match(NAMED_COLOR_PATTERN)[0];
}
return null;
}
const rule = (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkDecls((decl) => {
const prop = decl.prop.toLowerCase();
if (!COLOR_PROPERTIES.has(prop)) {
return;
}
const hardcodedColor = containsHardcodedColor(decl.value);
if (hardcodedColor) {
stylelint.utils.report({
message: messages.hardcodedColor(hardcodedColor, decl.prop),
node: decl,
result,
ruleName,
});
}
});
};
};
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = {};
export { ruleName, rule };
export default { ruleName, rule };

View File

@@ -82,11 +82,6 @@ export default defineConfig(({ mode }): UserConfig => {
];
if (env.VITE_SENTRY_AUTH_TOKEN) {
if (!env.VITE_SENTRY_ORG || !env.VITE_SENTRY_PROJECT_ID) {
throw new Error(
'VITE_SENTRY_ORG and VITE_SENTRY_PROJECT_ID must be defined when VITE_SENTRY_AUTH_TOKEN is present.',
);
}
// Refuse to upload sourcemaps without an explicit version.
if (!env.VITE_VERSION) {
throw new Error(

View File

@@ -22,7 +22,7 @@ func newConfig() factory.Config {
Agent: AgentConfig{
// we will maintain the latest version of cloud integration agent from here,
// till we automate it externally or figure out a way to validate it.
Version: "v0.0.12",
Version: "v0.0.11",
},
}
}

View File

@@ -1 +0,0 @@
<svg id="ba7e3c83-4ea6-40d5-820f-746ccedb8156" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><linearGradient id="e74ddf23-fe74-4f89-9a49-c7f2e22be284" x1="3.707" y1="5.123" x2="3.707" y2="2.061" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0078d4"/><stop offset="1" stop-color="#5ea0ef"/></linearGradient><linearGradient id="bcce3b94-6b83-44c5-9bf7-a89d666f3d1e" x1="12.741" y1="10.558" x2="12.741" y2="5.161" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0078d4"/><stop offset="1" stop-color="#5ea0ef"/></linearGradient><linearGradient id="f9b0803f-41e5-4165-9230-e220427ec475" x1="3.707" y1="13.723" x2="3.707" y2="10.378" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0078d4"/><stop offset="1" stop-color="#5ea0ef"/></linearGradient></defs><g><g><rect x="7.923" y="1.922" width="0.98" height="6.183" transform="translate(0.013 10.048) rotate(-61.725)" fill="#003067"/><path d="M7.081,4.2V1.5H.33v2.7h0v0c0,.716,1.511,1.3,3.376,1.3s3.376-.58,3.376-1.3Z" fill="url(#e74ddf23-fe74-4f89-9a49-c7f2e22be284)"/><ellipse cx="3.707" cy="1.546" rx="3.376" ry="1.296" fill="#83b9f9"/><path d="M16.115,8.616v-2.7H9.365v2.7h0v0c0,.716,1.511,1.3,3.376,1.3s3.376-.58,3.376-1.3Z" fill="url(#bcce3b94-6b83-44c5-9bf7-a89d666f3d1e)"/><ellipse cx="12.741" cy="5.96" rx="3.376" ry="1.296" fill="#83b9f9"/><rect x="3.22" y="4.168" width="0.98" height="7.962" fill="#003067"/><circle cx="3.75" cy="3.337" r="1.157" fill="#c3f1ff"/><path d="M7.081,12.824v-2.7H.33v2.7h0v0c0,.716,1.511,1.3,3.376,1.3s3.376-.58,3.376-1.3Z" fill="url(#f9b0803f-41e5-4165-9230-e220427ec475)"/><ellipse cx="3.707" cy="10.167" rx="3.376" ry="1.296" fill="#83b9f9"/><rect x="3.708" y="9.164" width="9.741" height="0.98" transform="translate(-3.376 4.767) rotate(-26.14)" fill="#003067"/><circle cx="3.701" cy="11.939" r="1.157" fill="#c3f1ff"/><circle cx="12.743" cy="7.548" r="1.157" fill="#c3f1ff"/></g><g><path d="M17.166,15.5h0a.255.255,0,0,0-.1-.185A17,17,0,0,0,15.563,14a3.563,3.563,0,0,0-2.638-.706,5.515,5.515,0,0,0-3.339,2.066.224.224,0,0,0-.058.14h0v0h0a.26.26,0,0,0,.1.186,17.319,17.319,0,0,0,1.507,1.315,3.568,3.568,0,0,0,2.637.706,5.517,5.517,0,0,0,3.339-2.066.229.229,0,0,0,.059-.141h0Z" fill="#003067"/><circle cx="13.381" cy="15.501" r="1.846" fill="#c3f1ff"/><path d="M13.389,14.478a.875.875,0,0,0-.144.029.537.537,0,0,1,.1.292.559.559,0,0,1-.559.558.548.548,0,0,1-.368-.145.985.985,0,0,0-.062.3,1.038,1.038,0,1,0,1.038-1.037Z" fill="#003067"/><path d="M17.529,13.472A6.582,6.582,0,0,0,15.6,12.241l.437-.776a.458.458,0,0,0,.044-.34.441.441,0,0,0-.806-.106l-.535.952a5.762,5.762,0,0,0-.914-.143v-.975a.442.442,0,0,0-.883,0v.974a5.879,5.879,0,0,0-.934.144l-.536-.952a.442.442,0,0,0-.6-.158.45.45,0,0,0-.159.6q.219.387.437.776a6.582,6.582,0,0,0-1.931,1.231c-.422.382.2,1,.624.624a5.183,5.183,0,0,1,7.067,0C17.326,14.476,17.951,13.854,17.529,13.472Z" fill="#003067"/></g></g><rect x="-4.934" y="-3.691" width="29.331" height="34.261" fill="none" stroke="#b31b1b" stroke-miterlimit="10"/></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1,7 +0,0 @@
### Monitor Azure Managed Instance for Apache Cassandra with SigNoz
Collect key Azure Managed Instance for Apache Cassandra metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.DocumentDB/cassandraClusters` resource type.
Note: This integration is for Azure Managed Instance for Apache Cassandra. Azure Cosmos DB for Apache Cassandra (the API offering) exposes a different set of metrics and is not covered here.

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><radialGradient id="a" cx="-105.006" cy="-10.409" r="5.954" gradientTransform="matrix(1.036 0 0 1.027 117.739 19.644)" gradientUnits="userSpaceOnUse"><stop offset=".183" stop-color="#5ea0ef"/><stop offset="1" stop-color="#0078d4"/></radialGradient><clipPath id="b"><path d="M14.969 7.53a6.137 6.137 0 11-7.395-4.543 6.137 6.137 0 017.395 4.543z" fill="none"/></clipPath></defs><path d="M2.954 5.266a.175.175 0 01-.176-.176A2.012 2.012 0 00.769 3.081a.176.176 0 01-.176-.175.176.176 0 01.176-.176A2.012 2.012 0 002.778.72a.175.175 0 01.176-.176.175.175 0 01.176.176 2.012 2.012 0 002.009 2.009.175.175 0 01.176.176.175.175 0 01-.176.176A2.011 2.011 0 003.13 5.09a.177.177 0 01-.176.176zM15.611 17.456a.141.141 0 01-.141-.141 1.609 1.609 0 00-1.607-1.607.141.141 0 01-.141-.14.141.141 0 01.141-.141 1.608 1.608 0 001.607-1.607.141.141 0 01.141-.141.141.141 0 01.141.141 1.608 1.608 0 001.607 1.607.141.141 0 110 .282 1.609 1.609 0 00-1.607 1.607.141.141 0 01-.141.14z" fill="#50e6ff"/><path d="M14.969 7.53a6.137 6.137 0 11-7.395-4.543 6.137 6.137 0 017.395 4.543z" fill="url(#a)"/><g clip-path="url(#b)" fill="#f2f2f2"><path d="M5.709 13.115a1.638 1.638 0 10.005-3.275 1.307 1.307 0 00.007-.14A1.651 1.651 0 004.06 8.064H2.832a6.251 6.251 0 001.595 5.051zM15.045 7.815c0-.015 0-.03-.007-.044a5.978 5.978 0 00-1.406-2.88 1.825 1.825 0 00-.289-.09 1.806 1.806 0 00-2.3 1.663 2 2 0 00-.2-.013 1.737 1.737 0 00-.581 3.374 1.451 1.451 0 00.541.1h2.03a13.453 13.453 0 002.212-2.11z"/></g><path d="M17.191 3.832c-.629-1.047-2.1-1.455-4.155-1.149a14.606 14.606 0 00-2.082.452 6.456 6.456 0 011.528.767c.241-.053.483-.116.715-.151a7.49 7.49 0 011.103-.089 2.188 2.188 0 011.959.725c.383.638.06 1.729-.886 3a16.723 16.723 0 01-4.749 4.051A16.758 16.758 0 014.8 13.7c-1.564.234-2.682 0-3.065-.636s-.06-1.73.886-2.995c.117-.157.146-.234.279-.392a6.252 6.252 0 01.026-1.63 11.552 11.552 0 00-1.17 1.372C.517 11.076.181 12.566.809 13.613a3.165 3.165 0 002.9 1.249 8.434 8.434 0 001.251-.1 17.855 17.855 0 006.219-2.4A17.808 17.808 0 0016.24 8.03c1.243-1.661 1.579-3.15.951-4.198z" fill="#50e6ff"/></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,529 +0,0 @@
{
"id": "cosmosdb",
"title": "Cosmos DB",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "azure_totalrequests_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of requests made against the account."
},
{
"name": "azure_totalrequestunits_total",
"unit": "Count",
"type": "Sum",
"description": "Request Units (RUs) consumed."
},
{
"name": "azure_totalrequestunits_average",
"unit": "Count",
"type": "Gauge",
"description": "Request Units (RUs) consumed."
},
{
"name": "azure_totalrequestunits_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Request Units (RUs) consumed."
},
{
"name": "azure_normalizedruconsumption_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Maximum RU consumption percentage per minute."
},
{
"name": "azure_normalizedruconsumption_average",
"unit": "Percent",
"type": "Gauge",
"description": "Maximum RU consumption percentage per minute."
},
{
"name": "azure_metadatarequests_count",
"unit": "Count",
"type": "Gauge",
"description": "Count of metadata requests (enumerating databases/collections and their configurations)."
},
{
"name": "azure_datausage_total",
"unit": "Bytes",
"type": "Sum",
"description": "Total data usage, reported at 5-minute granularity."
},
{
"name": "azure_datausage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Total data usage, reported at 5-minute granularity."
},
{
"name": "azure_datausage_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Total data usage, reported at 5-minute granularity."
},
{
"name": "azure_datausage_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Total data usage, reported at 5-minute granularity."
},
{
"name": "azure_indexusage_total",
"unit": "Bytes",
"type": "Sum",
"description": "Total index usage, reported at 5-minute granularity."
},
{
"name": "azure_indexusage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Total index usage, reported at 5-minute granularity."
},
{
"name": "azure_indexusage_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Total index usage, reported at 5-minute granularity."
},
{
"name": "azure_indexusage_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Total index usage, reported at 5-minute granularity."
},
{
"name": "azure_documentcountv2_total",
"unit": "Count",
"type": "Sum",
"description": "Total document count."
},
{
"name": "azure_documentcountv2_average",
"unit": "Count",
"type": "Gauge",
"description": "Total document count."
},
{
"name": "azure_documentquota_total",
"unit": "Bytes",
"type": "Sum",
"description": "Total storage quota, reported at 5-minute granularity."
},
{
"name": "azure_documentquota_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Total storage quota, reported at 5-minute granularity."
},
{
"name": "azure_provisionedthroughput_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Provisioned throughput (RU/s)."
},
{
"name": "azure_autoscalemaxthroughput_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Autoscale maximum throughput (RU/s)."
},
{
"name": "azure_autoscaledru_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Autoscaled RU consumption per region and per partition."
},
{
"name": "azure_serversidelatencydirect_average",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "Server-side latency in Direct connection mode."
},
{
"name": "azure_serversidelatencydirect_minimum",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "Server-side latency in Direct connection mode."
},
{
"name": "azure_serversidelatencydirect_maximum",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "Server-side latency in Direct connection mode."
},
{
"name": "azure_serversidelatencydirect_total",
"unit": "MilliSeconds",
"type": "Sum",
"description": "Server-side latency in Direct connection mode."
},
{
"name": "azure_serversidelatencygateway_average",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "Server-side latency in Gateway connection mode."
},
{
"name": "azure_serversidelatencygateway_minimum",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "Server-side latency in Gateway connection mode."
},
{
"name": "azure_serversidelatencygateway_maximum",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "Server-side latency in Gateway connection mode."
},
{
"name": "azure_serversidelatencygateway_total",
"unit": "MilliSeconds",
"type": "Sum",
"description": "Server-side latency in Gateway connection mode."
},
{
"name": "azure_replicationlatency_minimum",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "P99 replication latency across source and target regions for a geo-enabled account."
},
{
"name": "azure_replicationlatency_maximum",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "P99 replication latency across source and target regions for a geo-enabled account."
},
{
"name": "azure_replicationlatency_average",
"unit": "MilliSeconds",
"type": "Gauge",
"description": "P99 replication latency across source and target regions for a geo-enabled account."
},
{
"name": "azure_serviceavailability_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Account requests availability at one-hour, day, or month granularity."
},
{
"name": "azure_serviceavailability_average",
"unit": "Percent",
"type": "Gauge",
"description": "Account requests availability at one-hour, day, or month granularity."
},
{
"name": "azure_serviceavailability_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Account requests availability at one-hour, day, or month granularity."
},
{
"name": "azure_physicalpartitioncount_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Physical partition count."
},
{
"name": "azure_physicalpartitionsizeinfo_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Physical partition size in bytes."
},
{
"name": "azure_physicalpartitionsizeinfo_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Physical partition size in bytes."
},
{
"name": "azure_physicalpartitionthroughputinfo_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Physical partition throughput (RU/s)."
},
{
"name": "azure_cassandrarequests_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of Cassandra API requests made."
},
{
"name": "azure_cassandrarequestcharges_total",
"unit": "Count",
"type": "Sum",
"description": "Request Units consumed for Cassandra API requests."
},
{
"name": "azure_cassandrarequestcharges_average",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Cassandra API requests."
},
{
"name": "azure_cassandrarequestcharges_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Cassandra API requests."
},
{
"name": "azure_cassandrarequestcharges_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Cassandra API requests."
},
{
"name": "azure_cassandraconnectionclosures_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of Cassandra connections that were closed, reported at 1-minute granularity."
},
{
"name": "azure_cassandraconnectionclosures_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of Cassandra connections that were closed, reported at 1-minute granularity."
},
{
"name": "azure_cassandraconnectionclosures_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of Cassandra connections that were closed, reported at 1-minute granularity."
},
{
"name": "azure_cassandraconnectionclosures_total",
"unit": "Count",
"type": "Sum",
"description": "Number of Cassandra connections that were closed, reported at 1-minute granularity."
},
{
"name": "azure_gremlinrequests_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of Gremlin API requests made."
},
{
"name": "azure_gremlinrequestcharges_total",
"unit": "Count",
"type": "Sum",
"description": "Request Units consumed for Gremlin API requests."
},
{
"name": "azure_gremlinrequestcharges_average",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Gremlin API requests."
},
{
"name": "azure_gremlinrequestcharges_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Gremlin API requests."
},
{
"name": "azure_gremlinrequestcharges_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Gremlin API requests."
},
{
"name": "azure_mongorequests_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of Mongo API requests made."
},
{
"name": "azure_mongorequestcharge_total",
"unit": "Count",
"type": "Sum",
"description": "Request Units consumed for Mongo API requests."
},
{
"name": "azure_mongorequestcharge_average",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Mongo API requests."
},
{
"name": "azure_mongorequestcharge_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Request Units consumed for Mongo API requests."
},
{
"name": "azure_dedicatedgatewayaveragecpuusage_average",
"unit": "Percent",
"type": "Gauge",
"description": "Average CPU usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaymaximumcpuusage_average",
"unit": "Percent",
"type": "Gauge",
"description": "Average maximum CPU usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaymaximumcpuusage_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Average maximum CPU usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaycpuusage_average",
"unit": "Percent",
"type": "Gauge",
"description": "CPU usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaycpuusage_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "CPU usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaycpuusage_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "CPU usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewayaveragememoryusage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Average memory usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaymemoryusage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Memory usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaymemoryusage_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Memory usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewaymemoryusage_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Memory usage across dedicated gateway instances."
},
{
"name": "azure_dedicatedgatewayrequests_count",
"unit": "Count",
"type": "Gauge",
"description": "Requests at the dedicated gateway."
},
{
"name": "azure_integratedcacheevictedentriessize_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Size of the entries evicted from the integrated cache."
},
{
"name": "azure_integratedcacheitemexpirationcount_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of items evicted from the integrated cache due to TTL expiration."
},
{
"name": "azure_integratedcacheitemhitrate_average",
"unit": "Percent",
"type": "Gauge",
"description": "Point reads that used the integrated cache divided by point reads routed through the dedicated gateway with eventual consistency."
},
{
"name": "azure_integratedcachequeryexpirationcount_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of queries evicted from the integrated cache due to TTL expiration."
},
{
"name": "azure_integratedcachequeryhitrate_average",
"unit": "Percent",
"type": "Gauge",
"description": "Queries that used the integrated cache divided by queries routed through the dedicated gateway with eventual consistency."
},
{
"name": "azure_materializedviewcatchupgapinminutes_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Maximum time difference (minutes) between data in the source container and data propagated to the materialized view."
},
{
"name": "azure_materializedviewsbuilderaveragecpuusage_average",
"unit": "Percent",
"type": "Gauge",
"description": "Average CPU usage across materialized view builder instances."
},
{
"name": "azure_materializedviewsbuildermaximumcpuusage_average",
"unit": "Percent",
"type": "Gauge",
"description": "Average maximum CPU usage across materialized view builder instances."
},
{
"name": "azure_materializedviewsbuildermaximumcpuusage_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Average maximum CPU usage across materialized view builder instances."
},
{
"name": "azure_materializedviewsbuilderaveragememoryusage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Average memory usage across materialized view builder instances."
},
{
"name": "azure_globalsecondaryindexcatchupgapinminutes_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Maximum time difference (minutes) between data in the source container and data propagated to the global secondary index."
},
{
"name": "azure_globalsecondaryindexpropagationlatencyinseconds_average",
"unit": "Count",
"type": "Gauge",
"description": "Average time difference (seconds) between data in the source container and data propagated to the global secondary index."
}
],
"logs": [
{
"name": "Resource ID",
"path": "resources.azure.resource.id",
"type": "string"
}
]
},
"telemetryCollectionStrategy": {
"azure": {
"resourceProvider": "Microsoft.DocumentDB",
"resourceType": "databaseAccounts",
"metrics": {},
"logs": {
"categoryGroups": [
"allLogs"
]
}
}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "Azure Cosmos DB Overview",
"description": "Overview of Azure Cosmos DB metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -1,7 +0,0 @@
### Monitor Azure Cosmos DB with SigNoz
Collect key Azure Cosmos DB metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.DocumentDB/databaseAccounts` resource type — the Request Unit (RU) based Azure Cosmos DB account. A single account resource type is used across all Cosmos DB APIs (NoSQL, Cassandra, Gremlin, Table, and MongoDB for RU), so per-API request metrics are only reported for the API your account uses.
Note: This integration is for the RU-based Cosmos DB account (`Microsoft.DocumentDB/databaseAccounts`).

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 61 81" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x=".5" y=".5"/><symbol id="A" overflow="visible"><g stroke="none"><path d="M0 10.88v58.08c0 6.08 13.333 10.88 30 10.88V10.88H0z" fill="#3999c6"/><path d="M29.524 80H30c16.508 0 30-4.96 30-10.88V10.88H29.524V80z" fill="#59b4d9"/><path d="M60 10.88c0 6.08-13.333 10.88-30 10.88S0 16.96 0 10.88C0 4.96 13.492 0 30 0s30 4.96 30 10.88"/><path d="M53.81 10.24c0 4-10.635 7.2-23.81 7.2s-23.809-3.2-23.809-7.2 10.635-7.2 23.81-7.2 23.81 3.2 23.81 7.2" fill="#7fba00"/><path d="M48.889 14.72c3.175-1.28 4.921-2.72 4.921-4.48 0-4-10.635-7.2-23.81-7.2s-23.809 3.2-23.809 7.2c0 1.6 1.905 3.2 4.921 4.48 4.286-1.76 11.111-2.88 18.889-2.88 7.619 0 14.444 1.12 18.889 2.88" fill="#b8d432"/><path d="M21.429 66.88c-2.381 0-4.286-.32-5.555-.8-1.429-.48-2.381-1.28-3.016-2.24-.635-1.12-.952-2.72-.952-4.8v-3.52c0-2.24-.952-3.52-2.857-3.52v-4.32c1.905 0 2.857-1.12 2.857-3.52v-3.2c0-2.24.317-4 .952-4.96.635-1.12 1.587-1.76 2.857-2.4 1.429-.48 3.175-.8 5.556-.8v4.32c-1.111 0-1.746.16-2.381.64-.476.48-.794 1.28-.794 2.4v2.72c0 1.76-.159 3.2-.635 4.32s-1.27 1.92-2.381 2.4h0c1.111.48 1.905 1.28 2.381 2.56.476 1.12.794 2.72.794 4.48v2.4c0 1.12.159 1.92.635 2.4s1.27.8 2.381.8v4.64zM48.413 52c-1.905 0-2.857 1.12-2.857 3.52v3.2c0 2.24-.318 4-.952 4.96-.635 1.12-1.587 1.76-2.857 2.4-1.428.48-3.175.8-5.556.8v-4.32c1.111 0 1.905-.16 2.381-.64s.794-1.28.794-2.4V56.8c0-1.76.159-3.2.635-4.32a4.39 4.39 0 0 1 2.381-2.4h0c-1.111-.48-1.905-1.44-2.381-2.56s-.794-2.72-.794-4.48v-2.4c0-1.12-.159-1.92-.635-2.4s-1.27-.8-2.381-.8v-4.32c2.381 0 4.286.32 5.556.8 1.428.48 2.381 1.28 3.016 2.24.635 1.12.953 2.72.953 4.8v3.52c0 2.24.952 3.52 2.857 3.52v4z"/></g></symbol></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,247 +0,0 @@
{
"id": "mongodb",
"title": "MongoDB (DocumentDB)",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "azure_cpupercent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Percent CPU utilization on the node."
},
{
"name": "azure_cpupercent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent CPU utilization on the node."
},
{
"name": "azure_cpupercent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent CPU utilization on the node."
},
{
"name": "azure_memorypercent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Percent memory utilization on the node."
},
{
"name": "azure_memorypercent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent memory utilization on the node."
},
{
"name": "azure_memorypercent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent memory utilization on the node."
},
{
"name": "azure_committedmemorypercent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Percentage of the commit memory limit allocated by applications on the node."
},
{
"name": "azure_committedmemorypercent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Percentage of the commit memory limit allocated by applications on the node."
},
{
"name": "azure_committedmemorypercent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Percentage of the commit memory limit allocated by applications on the node."
},
{
"name": "azure_storagepercent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Percent of available storage used on the node."
},
{
"name": "azure_storagepercent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent of available storage used on the node."
},
{
"name": "azure_storagepercent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent of available storage used on the node."
},
{
"name": "azure_storageused_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Quantity of available storage used on the node."
},
{
"name": "azure_storageused_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Quantity of available storage used on the node."
},
{
"name": "azure_storageused_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Quantity of available storage used on the node."
},
{
"name": "azure_autoscaleutilizationpercent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Percent autoscale utilization."
},
{
"name": "azure_autoscaleutilizationpercent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent autoscale utilization."
},
{
"name": "azure_autoscaleutilizationpercent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Percent autoscale utilization."
},
{
"name": "azure_iops_average",
"unit": "Count",
"type": "Gauge",
"description": "Disk IO operations per second on the node."
},
{
"name": "azure_iops_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Disk IO operations per second on the node."
},
{
"name": "azure_iops_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Disk IO operations per second on the node."
},
{
"name": "azure_networkbytesegress_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Network bytes sent on the node."
},
{
"name": "azure_networkbytesegress_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Network bytes sent on the node."
},
{
"name": "azure_networkbytesegress_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Network bytes sent on the node."
},
{
"name": "azure_networkbytesegress_total",
"unit": "Bytes",
"type": "Sum",
"description": "Network bytes sent on the node."
},
{
"name": "azure_networkbytesingress_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Network bytes received on the node."
},
{
"name": "azure_networkbytesingress_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Network bytes received on the node."
},
{
"name": "azure_networkbytesingress_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Network bytes received on the node."
},
{
"name": "azure_networkbytesingress_total",
"unit": "Bytes",
"type": "Sum",
"description": "Network bytes received on the node."
},
{
"name": "azure_mongorequestdurationms_average",
"unit": "Milliseconds",
"type": "Gauge",
"description": "End-to-end duration in milliseconds of client MongoDB requests handled by the cluster."
},
{
"name": "azure_mongorequestdurationms_maximum",
"unit": "Milliseconds",
"type": "Gauge",
"description": "End-to-end duration in milliseconds of client MongoDB requests handled by the cluster."
},
{
"name": "azure_mongorequestdurationms_minimum",
"unit": "Milliseconds",
"type": "Gauge",
"description": "End-to-end duration in milliseconds of client MongoDB requests handled by the cluster."
},
{
"name": "azure_mongorequestdurationms_total",
"unit": "Milliseconds",
"type": "Sum",
"description": "End-to-end duration in milliseconds of client MongoDB requests handled by the cluster."
},
{
"name": "azure_mongorequestdurationms_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of client MongoDB requests handled by the cluster."
}
],
"logs": [
{
"name": "Resource ID",
"path": "resources.azure.resource.id",
"type": "string"
}
]
},
"telemetryCollectionStrategy": {
"azure": {
"resourceProvider": "Microsoft.DocumentDB",
"resourceType": "mongoClusters",
"metrics": {},
"logs": {
"categoryGroups": [
"allLogs"
]
}
}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "Azure MongoDB (DocumentDB) Overview",
"description": "Overview of Azure MongoDB (DocumentDB) metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -1,5 +0,0 @@
### Monitor Azure MongoDB with SigNoz
Collect key Azure MongoDB (DocumentDB) cluster metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.DocumentDB/mongoClusters` resource type — the vCore-based Azure DocumentDB offering with MongoDB compatibility.

View File

@@ -1 +0,0 @@
<svg id="e05c9575-4c38-4bcd-90eb-276caf26e3d0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><linearGradient id="e291aba6-8038-4db1-a04d-c7be74f5a3e6" x1="2.59" y1="10.16" x2="15.41" y2="10.16" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#005ba1"/><stop offset="0.07" stop-color="#0060a9"/><stop offset="0.36" stop-color="#0071c8"/><stop offset="0.52" stop-color="#0078d4"/><stop offset="0.64" stop-color="#0074cd"/><stop offset="0.82" stop-color="#006abb"/><stop offset="1" stop-color="#005ba1"/></linearGradient></defs><title>Icon-databases-122</title><path d="M9,5.14c-3.54,0-6.41-1-6.41-2.32V15.18c0,1.27,2.82,2.3,6.32,2.32H9c3.54,0,6.41-1,6.41-2.32V2.82C15.41,4.11,12.54,5.14,9,5.14Z" fill="url(#e291aba6-8038-4db1-a04d-c7be74f5a3e6)"/><path d="M15.41,2.82c0,1.29-2.87,2.32-6.41,2.32s-6.41-1-6.41-2.32S5.46.5,9,.5s6.41,1,6.41,2.32" fill="#e8e8e8"/><path d="M13.92,2.63c0,.82-2.21,1.48-4.92,1.48S4.08,3.45,4.08,2.63,6.29,1.16,9,1.16s4.92.66,4.92,1.47" fill="#50e6ff"/><path d="M9,3a11.55,11.55,0,0,0-3.89.57A11.42,11.42,0,0,0,9,4.11a11.15,11.15,0,0,0,3.89-.58A11.84,11.84,0,0,0,9,3Z" fill="#198ab3"/><path d="M12.64,9v1.63h-1a.39.39,0,0,1-.29-.14V9H10v1.78a.92.92,0,0,0,1,.89h1.49l.26-.13s-.11.41-.26.43H10.11v1h2.66A1.21,1.21,0,0,0,14,11.7V9Z" fill="#f2f2f2"/><path d="M9.53,9s0,0,0,0V8.51a.7.7,0,0,0-.48-.77,1.74,1.74,0,0,0-.5-.08.94.94,0,0,0-.91.58l-.78,1.9-1-1.9A.93.93,0,0,0,5,7.66a1.44,1.44,0,0,0-.51.09c-.35.11-.43.34-.43.73v3.31H5.23V9.56l.63,1.57a1.08,1.08,0,0,0,1,.66c.44,0,.62-.26.8-.66l.67-1.51v2.15H9.51V9h0Z" fill="#f2f2f2"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,787 +0,0 @@
{
"id": "mysqlflexibleserver",
"title": "MySQL - Flexible Server",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "azure_ha_io_status_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Status for replication IO thread running (HA)."
},
{
"name": "azure_ha_sql_status_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Status for replication SQL thread running (HA)."
},
{
"name": "azure_replica_io_running_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Status for replication IO thread running (replica)."
},
{
"name": "azure_replica_sql_running_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Status for replication SQL thread running (replica)."
},
{
"name": "azure_aborted_connections_total",
"unit": "Count",
"type": "Sum",
"description": "Number of failed attempts to connect to the MySQL server."
},
{
"name": "azure_ha_replication_lag_average",
"unit": "Seconds",
"type": "Gauge",
"description": "HA replication lag in seconds."
},
{
"name": "azure_ha_replication_lag_maximum",
"unit": "Seconds",
"type": "Gauge",
"description": "HA replication lag in seconds."
},
{
"name": "azure_ha_replication_lag_minimum",
"unit": "Seconds",
"type": "Gauge",
"description": "HA replication lag in seconds."
},
{
"name": "azure_innodb_row_lock_time_average",
"unit": "Milliseconds",
"type": "Gauge",
"description": "Total time spent acquiring row locks for InnoDB tables, in milliseconds."
},
{
"name": "azure_innodb_row_lock_time_maximum",
"unit": "Milliseconds",
"type": "Gauge",
"description": "Total time spent acquiring row locks for InnoDB tables, in milliseconds."
},
{
"name": "azure_innodb_row_lock_time_minimum",
"unit": "Milliseconds",
"type": "Gauge",
"description": "Total time spent acquiring row locks for InnoDB tables, in milliseconds."
},
{
"name": "azure_innodb_row_lock_waits_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times operations on InnoDB tables had to wait for a row lock."
},
{
"name": "azure_innodb_row_lock_waits_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of times operations on InnoDB tables had to wait for a row lock."
},
{
"name": "azure_innodb_row_lock_waits_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of times operations on InnoDB tables had to wait for a row lock."
},
{
"name": "azure_replication_lag_average",
"unit": "Seconds",
"type": "Gauge",
"description": "Replication lag in seconds."
},
{
"name": "azure_replication_lag_maximum",
"unit": "Seconds",
"type": "Gauge",
"description": "Replication lag in seconds."
},
{
"name": "azure_replication_lag_minimum",
"unit": "Seconds",
"type": "Gauge",
"description": "Replication lag in seconds."
},
{
"name": "azure_uptime_total",
"unit": "Seconds",
"type": "Sum",
"description": "Number of seconds that the server has been up."
},
{
"name": "azure_uptime_maximum",
"unit": "Seconds",
"type": "Gauge",
"description": "Number of seconds that the server has been up."
},
{
"name": "azure_backup_storage_used_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Backup storage used."
},
{
"name": "azure_backup_storage_used_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Backup storage used."
},
{
"name": "azure_backup_storage_used_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Backup storage used."
},
{
"name": "azure_binlog_storage_used_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by binlog files."
},
{
"name": "azure_binlog_storage_used_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by binlog files."
},
{
"name": "azure_binlog_storage_used_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by binlog files."
},
{
"name": "azure_cpu_credits_consumed_average",
"unit": "Count",
"type": "Gauge",
"description": "CPU credits consumed (Burstable SKU)."
},
{
"name": "azure_cpu_credits_consumed_maximum",
"unit": "Count",
"type": "Gauge",
"description": "CPU credits consumed (Burstable SKU)."
},
{
"name": "azure_cpu_credits_consumed_minimum",
"unit": "Count",
"type": "Gauge",
"description": "CPU credits consumed (Burstable SKU)."
},
{
"name": "azure_cpu_credits_remaining_average",
"unit": "Count",
"type": "Gauge",
"description": "CPU credits remaining (Burstable SKU)."
},
{
"name": "azure_cpu_credits_remaining_maximum",
"unit": "Count",
"type": "Gauge",
"description": "CPU credits remaining (Burstable SKU)."
},
{
"name": "azure_cpu_credits_remaining_minimum",
"unit": "Count",
"type": "Gauge",
"description": "CPU credits remaining (Burstable SKU)."
},
{
"name": "azure_cpu_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Host CPU percent."
},
{
"name": "azure_cpu_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Host CPU percent."
},
{
"name": "azure_cpu_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Host CPU percent."
},
{
"name": "azure_data_storage_used_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by data files."
},
{
"name": "azure_data_storage_used_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by data files."
},
{
"name": "azure_data_storage_used_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by data files."
},
{
"name": "azure_ibdata1_storage_used_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by ibdata1 files."
},
{
"name": "azure_ibdata1_storage_used_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by ibdata1 files."
},
{
"name": "azure_ibdata1_storage_used_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by ibdata1 files."
},
{
"name": "azure_innodb_buffer_pool_pages_data_total",
"unit": "Count",
"type": "Sum",
"description": "Number of pages in the InnoDB buffer pool containing data."
},
{
"name": "azure_innodb_buffer_pool_pages_data_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of pages in the InnoDB buffer pool containing data."
},
{
"name": "azure_innodb_buffer_pool_pages_data_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of pages in the InnoDB buffer pool containing data."
},
{
"name": "azure_innodb_buffer_pool_pages_dirty_total",
"unit": "Count",
"type": "Sum",
"description": "Current number of dirty pages in the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_pages_dirty_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Current number of dirty pages in the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_pages_dirty_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Current number of dirty pages in the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_pages_free_total",
"unit": "Count",
"type": "Sum",
"description": "Number of free pages in the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_pages_free_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of free pages in the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_pages_free_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of free pages in the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_read_requests_total",
"unit": "Count",
"type": "Sum",
"description": "Number of logical read requests."
},
{
"name": "azure_innodb_buffer_pool_read_requests_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of logical read requests."
},
{
"name": "azure_innodb_buffer_pool_read_requests_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of logical read requests."
},
{
"name": "azure_innodb_buffer_pool_reads_total",
"unit": "Count",
"type": "Sum",
"description": "Number of logical reads that InnoDB could not satisfy from the buffer pool and had to read from disk."
},
{
"name": "azure_innodb_buffer_pool_reads_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of logical reads that InnoDB could not satisfy from the buffer pool and had to read from disk."
},
{
"name": "azure_innodb_buffer_pool_reads_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of logical reads that InnoDB could not satisfy from the buffer pool and had to read from disk."
},
{
"name": "azure_io_consumption_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Storage I/O consumption percent."
},
{
"name": "azure_io_consumption_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Storage I/O consumption percent."
},
{
"name": "azure_io_consumption_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Storage I/O consumption percent."
},
{
"name": "azure_memory_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Memory percent."
},
{
"name": "azure_memory_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Memory percent."
},
{
"name": "azure_memory_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Memory percent."
},
{
"name": "azure_others_storage_used_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by other files."
},
{
"name": "azure_others_storage_used_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by other files."
},
{
"name": "azure_others_storage_used_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used by other files."
},
{
"name": "azure_serverlog_storage_limit_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Server log storage limit."
},
{
"name": "azure_serverlog_storage_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Server log storage percent."
},
{
"name": "azure_serverlog_storage_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Server log storage percent."
},
{
"name": "azure_serverlog_storage_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Server log storage percent."
},
{
"name": "azure_serverlog_storage_usage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Server log storage used."
},
{
"name": "azure_serverlog_storage_usage_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Server log storage used."
},
{
"name": "azure_serverlog_storage_usage_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Server log storage used."
},
{
"name": "azure_sort_merge_passes_total",
"unit": "Count",
"type": "Sum",
"description": "Number of merge passes the sort algorithm has had to do."
},
{
"name": "azure_sort_merge_passes_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of merge passes the sort algorithm has had to do."
},
{
"name": "azure_sort_merge_passes_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of merge passes the sort algorithm has had to do."
},
{
"name": "azure_storage_limit_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage limit."
},
{
"name": "azure_storage_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Storage percent."
},
{
"name": "azure_storage_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Storage percent."
},
{
"name": "azure_storage_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Storage percent."
},
{
"name": "azure_storage_used_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used."
},
{
"name": "azure_storage_used_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used."
},
{
"name": "azure_storage_used_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Storage used."
},
{
"name": "azure_threads_running_total",
"unit": "Count",
"type": "Sum",
"description": "Number of threads that are not sleeping."
},
{
"name": "azure_threads_running_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of threads that are not sleeping."
},
{
"name": "azure_threads_running_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of threads that are not sleeping."
},
{
"name": "azure_active_connections_average",
"unit": "Count",
"type": "Gauge",
"description": "Active connections."
},
{
"name": "azure_active_connections_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Active connections."
},
{
"name": "azure_active_connections_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Active connections."
},
{
"name": "azure_active_transactions_total",
"unit": "Count",
"type": "Sum",
"description": "Number of active transactions."
},
{
"name": "azure_active_transactions_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of active transactions."
},
{
"name": "azure_active_transactions_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of active transactions."
},
{
"name": "azure_active_transactions_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of active transactions."
},
{
"name": "azure_com_alter_table_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times ALTER TABLE statement has been executed."
},
{
"name": "azure_com_create_db_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times CREATE DB statement has been executed."
},
{
"name": "azure_com_create_table_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times CREATE TABLE statement has been executed."
},
{
"name": "azure_com_delete_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times DELETE statement has been executed."
},
{
"name": "azure_com_drop_db_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times DROP DB statement has been executed."
},
{
"name": "azure_com_drop_table_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times DROP TABLE statement has been executed."
},
{
"name": "azure_com_insert_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times INSERT statement has been executed."
},
{
"name": "azure_com_select_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times SELECT statement has been executed."
},
{
"name": "azure_com_update_total",
"unit": "Count",
"type": "Sum",
"description": "Number of times UPDATE statement has been executed."
},
{
"name": "azure_innodb_buffer_pool_pages_flushed_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of requests to flush pages from the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_pages_flushed_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of requests to flush pages from the InnoDB buffer pool."
},
{
"name": "azure_innodb_buffer_pool_pages_flushed_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of requests to flush pages from the InnoDB buffer pool."
},
{
"name": "azure_innodb_data_writes_total",
"unit": "Count",
"type": "Sum",
"description": "Total number of data writes."
},
{
"name": "azure_innodb_data_writes_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Total number of data writes."
},
{
"name": "azure_innodb_data_writes_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Total number of data writes."
},
{
"name": "azure_lock_deadlocks_total",
"unit": "Count",
"type": "Sum",
"description": "Number of deadlocks."
},
{
"name": "azure_lock_deadlocks_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of deadlocks."
},
{
"name": "azure_lock_deadlocks_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of deadlocks."
},
{
"name": "azure_lock_deadlocks_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of deadlocks."
},
{
"name": "azure_lock_timeouts_total",
"unit": "Count",
"type": "Sum",
"description": "Number of lock timeouts."
},
{
"name": "azure_lock_timeouts_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of lock timeouts."
},
{
"name": "azure_lock_timeouts_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of lock timeouts."
},
{
"name": "azure_lock_timeouts_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of lock timeouts."
},
{
"name": "azure_network_bytes_egress_total",
"unit": "Bytes",
"type": "Sum",
"description": "Host network egress in bytes."
},
{
"name": "azure_network_bytes_ingress_total",
"unit": "Bytes",
"type": "Sum",
"description": "Host network ingress in bytes."
},
{
"name": "azure_queries_total",
"unit": "Count",
"type": "Sum",
"description": "Number of queries executed."
},
{
"name": "azure_slow_queries_total",
"unit": "Count",
"type": "Sum",
"description": "Number of queries that have taken more than long_query_time seconds."
},
{
"name": "azure_storage_io_count_total",
"unit": "Count",
"type": "Sum",
"description": "Number of storage I/O operations consumed."
},
{
"name": "azure_total_connections_total",
"unit": "Count",
"type": "Sum",
"description": "Total connections."
},
{
"name": "azure_trx_rseg_history_len_total",
"unit": "Count",
"type": "Sum",
"description": "Length of the TRX_RSEG_HISTORY list."
},
{
"name": "azure_trx_rseg_history_len_average",
"unit": "Count",
"type": "Gauge",
"description": "Length of the TRX_RSEG_HISTORY list."
},
{
"name": "azure_trx_rseg_history_len_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Length of the TRX_RSEG_HISTORY list."
},
{
"name": "azure_trx_rseg_history_len_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Length of the TRX_RSEG_HISTORY list."
}
],
"logs": [
{
"name": "Resource ID",
"path": "resources.azure.resource.id",
"type": "string"
}
]
},
"telemetryCollectionStrategy": {
"azure": {
"resourceProvider": "Microsoft.DBforMySQL",
"resourceType": "flexibleServers",
"metrics": {},
"logs": {
"categoryGroups": [
"allLogs"
]
}
}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "Azure Database for MySQL - Flexible Server Overview",
"description": "Overview of Azure Database for MySQL - Flexible Server metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -1,5 +0,0 @@
### Monitor Azure Database for MySQL - Flexible Server with SigNoz
Collect key Azure Database for MySQL - Flexible Server metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.DBforMySQL/flexibleServers` resource type.

View File

@@ -1 +0,0 @@
<svg id="fc890127-728b-4ac0-b5da-86cdfc191e86" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><linearGradient id="a28dee20-4c71-46b5-b957-804c67da725a" x1="2.44" y1="10.67" x2="15.27" y2="10.67" gradientTransform="translate(0.14 -0.5) rotate(-0.01)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#005ba1"/><stop offset="0.07" stop-color="#0060a9"/><stop offset="0.36" stop-color="#0071c8"/><stop offset="0.52" stop-color="#0078d4"/><stop offset="0.64" stop-color="#0074cd"/><stop offset="0.82" stop-color="#006abb"/><stop offset="1" stop-color="#005ba1"/></linearGradient></defs><title>Icon-databases-131</title><path d="M9,5.14c-3.54,0-6.41-1-6.41-2.32V15.18c0,1.27,2.82,2.3,6.32,2.32H9c3.54,0,6.41-1,6.41-2.32V2.82C15.41,4.1,12.54,5.14,9,5.14Z" fill="url(#a28dee20-4c71-46b5-b957-804c67da725a)"/><path d="M15.41,2.82c0,1.28-2.87,2.32-6.41,2.32s-6.41-1-6.41-2.32S5.46.5,9,.5s6.41,1,6.41,2.32" fill="#e8e8e8"/><path d="M13.91,2.63c0,.82-2.2,1.48-4.91,1.48S4.08,3.45,4.08,2.64,6.28,1.16,9,1.16s4.91.66,4.91,1.47" fill="#50e6ff"/><path d="M9,3a11.65,11.65,0,0,0-3.9.57A11.53,11.53,0,0,0,9,4.11a11.47,11.47,0,0,0,3.89-.58A11.93,11.93,0,0,0,9,3Z" fill="#198ab3"/><path d="M12,9c0,.08,0,.24,0,.42a5.12,5.12,0,0,0-.08.63c0,.12,0,.3.05.46s0,.27,0,.36a1.68,1.68,0,0,1-.25.86.43.43,0,0,0,0,.07l.1.12a10.55,10.55,0,0,0,1.06-2.38h0c.28-.95.31-1.63.09-1.92a2.58,2.58,0,0,0-2.68-.86,3.29,3.29,0,0,1,.91.67A2.28,2.28,0,0,1,12,9Zm-.31.08a1.15,1.15,0,0,0-.79.09c-.29.18-.2.55,0,1a7.77,7.77,0,0,0,.35.85l.09.16h0c.05.08.08.15.11.2l.08.13a1.28,1.28,0,0,0,.18-.65,2.86,2.86,0,0,0,0-.33c0-.17,0-.36-.05-.49a6.1,6.1,0,0,1,.08-.69C11.71,9.24,11.72,9.13,11.73,9.05Zm-.3.41a.4.4,0,0,1-.18.1h0a.33.33,0,0,1-.14,0A.23.23,0,0,1,11,9.4h0c0-.07.11-.13.24-.15h.19s.09,0,.09.08A.2.2,0,0,1,11.43,9.46Zm-4.69.89c0-.07,0-.13,0-.17a1,1,0,0,0,0-.17,5.55,5.55,0,0,1,0-1A5.22,5.22,0,0,1,7,7.94,2.41,2.41,0,0,1,7.58,7,4.78,4.78,0,0,0,6.23,6.8a1.87,1.87,0,0,0-1.1.3A2,2,0,0,0,4.5,8.92,12.27,12.27,0,0,0,5,11.16c.34,1.14.73,1.84,1.07,1.95h0c.15.05.31,0,.47-.21.28-.34.54-.63.69-.8A1.88,1.88,0,0,1,6.74,10.35Zm.26.4a2.18,2.18,0,0,0,.06.56,1.5,1.5,0,0,0,.26.44,1.07,1.07,0,0,0,.35.25,1.09,1.09,0,0,0,.39.08c0-.17.14-.38.23-.6a4.35,4.35,0,0,0,.21-.59,6.61,6.61,0,0,0,0-1.11c0-.09,0-.19-.06-.3a.45.45,0,0,0-.12-.27l0,0A.66.66,0,0,0,8,9.08H7.8a1.56,1.56,0,0,0-.48.14A2,2,0,0,0,7,9.41v.11c0,.2,0,.41,0,.61v0a.86.86,0,0,1,0,.15l0,.39H7Zm.66-1.34,0,0a.46.46,0,0,1,.28,0,.6.6,0,0,1,.19.06c.09,0,.1.11.09.14h0a.37.37,0,0,1-.12.13A.3.3,0,0,1,8,9.73h0a.34.34,0,0,1-.28-.28Zm.74,3a.23.23,0,0,0-.13.06l-.14.17c-.17.21-.24.28-.73.38a.52.52,0,0,0-.26.1.58.58,0,0,0,.24.11,1.18,1.18,0,0,0,.72,0A1.7,1.7,0,0,0,8.45,13a.61.61,0,0,0,.2-.31.36.36,0,0,0-.1-.28A.15.15,0,0,0,8.4,12.39Zm4.61,0a1.63,1.63,0,0,1-1.13,0,.22.22,0,0,0-.14,0,.3.3,0,0,0-.17.14,1.09,1.09,0,0,0,0,.31,1.8,1.8,0,0,0,1.1-.1,1.36,1.36,0,0,0,.48-.34Zm-2.4-2.14c-.14-.41-.34-1,.18-1.36a1.35,1.35,0,0,1,.89-.15A3.18,3.18,0,0,0,11,7.64l-.1-.1-.06-.06h0a.34.34,0,0,0-.06-.06l0,0,0,0A2.34,2.34,0,0,0,10.11,7,2.63,2.63,0,0,0,9,6.78a1.52,1.52,0,0,0-.7.11A1.77,1.77,0,0,0,8,7a2.1,2.1,0,0,0-.74,1,5.84,5.84,0,0,0-.21,1,1.66,1.66,0,0,1,1-.23.84.84,0,0,1,.77.91,7.26,7.26,0,0,1,0,1.27,3.87,3.87,0,0,1-.23.63c-.06.17-.14.35-.18.49a.5.5,0,0,1,.38.14.66.66,0,0,1,.19.53H9c0,.08,0,.16,0,.24a6.55,6.55,0,0,0,.24,2.44.54.54,0,0,0,.24.21.6.6,0,0,0,.31.1,1.3,1.3,0,0,0,1-.34,1.05,1.05,0,0,0,.29-.66c.07-.45.22-1.7.24-2h0a.72.72,0,0,1,.09-.44.63.63,0,0,1,.27-.24A5.36,5.36,0,0,1,10.61,10.24Z" fill="#f2f2f2"/></svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,7 +0,0 @@
### Monitor Azure Database for PostgreSQL - Flexible Server with SigNoz
Collect key Azure Database for PostgreSQL - Flexible Server metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.DBforPostgreSQL/flexibleServers` resource type.
Note: PgBouncer metrics are only emitted when the built-in PgBouncer connection pooler is enabled.

View File

@@ -1 +0,0 @@
<svg id="uuid-8e49878e-6f1e-45dc-b13f-de3f3b0a3028" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><linearGradient id="uuid-e275200f-8047-483d-9165-335881dc0492" x1=".562" y1="2.684" x2="17.561" y2="19.437" gradientTransform="translate(0 20) scale(1 -1)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#757575"/><stop offset=".915" stop-color="#b0b0b0"/></linearGradient><linearGradient id="uuid-a8467988-fb81-47a9-9300-4fac9dcb97a4" x1="13.321" y1="15.339" x2="4.581" y2="6.563" gradientTransform="translate(0 20) scale(1 -1)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0078d4"/><stop offset="1" stop-color="#005ba1"/></linearGradient></defs><path d="M9,.5c.276,0,.5.224.5.5v1h2v-1c0-.276.224-.5.5-.5s.5.224.5.5v1h1.5c1.105,0,2,.895,2,2v1.5h1c.276,0,.5.224.5.5s-.224.5-.5.5h-1v2h1c.276,0,.5.224.5.5s-.224.5-.5.5h-1v2h1c.276,0,.5.224.5.5s-.224.5-.5.5h-1v1.5c0,1.105-.895,2-2,2h-1.5v1c0,.276-.224.5-.5.5s-.5-.224-.5-.5v-1h-2v1c0,.276-.224.5-.5.5s-.5-.224-.5-.5v-1h-2v1c0,.276-.224.5-.5.5s-.5-.224-.5-.5v-1h-1.5c-1.105,0-2-.895-2-2v-1.5h-1c-.276,0-.5-.224-.5-.5s.224-.5.5-.5h1v-2h-1c-.276,0-.5-.224-.5-.5s.224-.5.5-.5h1v-2h-1c-.276,0-.5-.224-.5-.5s.224-.5.5-.5h1v-1.5c0-1.105.895-2,2-2h1.5v-1c0-.276.224-.5.5-.5s.5.224.5.5v1h2v-1c0-.276.224-.5.5-.5ZM8.984,3h-2.968c-.005,0-.01,0-.016,0s-.011,0-.016,0h-1.984c-.552,0-1,.448-1,1v10c0,.552.448,1,1,1h1.984c.005,0,.01,0,.016,0s.011,0,.016,0h2.968c.005,0,.01,0,.016,0s.011,0,.016,0h2.968c.005,0,.01,0,.016,0,.005,0,.01,0,.016,0h1.984c.552,0,1-.448,1-1v-1.978c0-.007,0-.015,0-.022s0-.015,0-.022v-2.955c0-.007,0-.015,0-.022s0-.015,0-.022v-2.955c0-.007,0-.015,0-.022s0-.015,0-.022v-1.978c0-.552-.448-1-1-1h-1.984c-.005,0-.01,0-.016,0-.005,0-.011,0-.016,0h-2.968c-.005,0-.01,0-.016,0s-.011,0-.016,0Z" fill="url(#uuid-e275200f-8047-483d-9165-335881dc0492)" fill-rule="evenodd"/><rect x="4" y="4" width="10" height="10" rx=".5" ry=".5" fill="url(#uuid-a8467988-fb81-47a9-9300-4fac9dcb97a4)"/><path d="M12.124,9.31c-.484.567-1.007,1.214-2.053,1.214-.934,0-1.282-.765-1.307-1.387.205.402.605.728,1.229.713,1.201-.036,2.024-1.043,2.024-1.961,0-1.097-.881-1.889-2.411-1.889-1.094,0-2.45.387-3.341.998-.01.63.368,1.448.504,1.358.772-.516,1.385-.848,1.979-1.014-.879.911-2.989,3.026-3.248,3.398.029.342.484,1.259.707,1.259.068,0,.126-.036.194-.099.636-.664,1.155-1.259,1.616-1.833.065.841.51,1.869,1.754,1.869,1.114,0,2.218-.747,2.721-2.429.058-.207-.213-.369-.368-.198ZM10.855,7.952c0,.531-.562.792-1.075.792-.274,0-.485-.067-.652-.154.307-.431.61-.874.936-1.347.575.09.79.387.79.709Z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,151 +0,0 @@
{
"id": "redis",
"title": "Redis",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "azure_cachehits_total",
"unit": "Count",
"type": "Sum",
"description": "The number of successful key lookups."
},
{
"name": "azure_cachemisses_total",
"unit": "Count",
"type": "Sum",
"description": "The number of failed key lookups."
},
{
"name": "azure_evictedkeys_total",
"unit": "Count",
"type": "Sum",
"description": "The number of items evicted from the cache."
},
{
"name": "azure_expiredkeys_total",
"unit": "Count",
"type": "Sum",
"description": "The number of items expired from the cache."
},
{
"name": "azure_getcommands_total",
"unit": "Count",
"type": "Sum",
"description": "The number of get operations from the cache."
},
{
"name": "azure_setcommands_total",
"unit": "Count",
"type": "Sum",
"description": "The number of set operations to the cache."
},
{
"name": "azure_totalcommandsprocessed_total",
"unit": "Count",
"type": "Sum",
"description": "The total number of commands processed by the cache server."
},
{
"name": "azure_cachelatency_average",
"unit": "Count",
"type": "Gauge",
"description": "The latency to the cache in microseconds (preview)."
},
{
"name": "azure_cacheread_maximum",
"unit": "BytesPerSecond",
"type": "Gauge",
"description": "The amount of data read from the cache in Megabytes per second (MB/s)."
},
{
"name": "azure_cachewrite_maximum",
"unit": "BytesPerSecond",
"type": "Gauge",
"description": "The amount of data written to the cache in Megabytes per second (MB/s)."
},
{
"name": "azure_connectedclients_maximum",
"unit": "Count",
"type": "Gauge",
"description": "The number of client connections to the cache."
},
{
"name": "azure_operationspersecond_maximum",
"unit": "Count",
"type": "Gauge",
"description": "The number of instantaneous operations per second executed on the cache."
},
{
"name": "azure_totalkeys_maximum",
"unit": "Count",
"type": "Gauge",
"description": "The total number of items in the cache."
},
{
"name": "azure_percentprocessortime_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "The CPU utilization of the Azure Redis Cache server as a percentage."
},
{
"name": "azure_serverload_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "The percentage of cycles in which the Redis server is busy processing and not waiting idle for messages."
},
{
"name": "azure_usedmemory_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "The amount of cache memory used for key/value pairs in the cache in MB."
},
{
"name": "azure_usedmemorypercentage_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "The percentage of cache memory used for key/value pairs."
},
{
"name": "azure_georeplicationhealthy_maximum",
"unit": "Count",
"type": "Gauge",
"description": "The health of geo replication in an Active Geo-Replication group. 0 represents Unhealthy and 1 represents Healthy."
}
],
"logs": [
{
"name": "Resource ID",
"path": "resources.azure.resource.id",
"type": "string"
}
]
},
"telemetryCollectionStrategy": {
"azure": {
"resourceProvider": "Microsoft.Cache",
"resourceType": "redisEnterprise",
"metrics": {},
"logs": {
"categoryGroups": [
"allLogs"
]
}
}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "Azure Redis Overview",
"description": "Overview of Azure Redis metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -1,5 +0,0 @@
### Monitor Azure Redis with SigNoz
Collect key Azure Redis metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.Cache/redisEnterprise` resource type.

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><linearGradient id="f67d1585-6164-4ad0-b2dd-f9cc59b2969f" x1="9.908" y1="15.943" x2="7.516" y2="2.383" gradientUnits="userSpaceOnUse"><stop offset="0.15" stop-color="#0078d4"/><stop offset="0.8" stop-color="#5ea0ef"/><stop offset="1" stop-color="#83b9f9"/></linearGradient></defs><g id="a4fd1868-54fe-4ca6-8ff6-3b01866dc27b"><path d="M14.49,7.15A5.147,5.147,0,0,0,9.24,2.164,5.272,5.272,0,0,0,4.216,5.653,4.869,4.869,0,0,0,0,10.4a4.946,4.946,0,0,0,5.068,4.814H13.82A4.292,4.292,0,0,0,18,11.127,4.105,4.105,0,0,0,14.49,7.15Z" fill="url(#f67d1585-6164-4ad0-b2dd-f9cc59b2969f)"/><path d="M12.9,11.4V8H12v4.13h2.46V11.4ZM5.76,9.73a1.825,1.825,0,0,1-.51-.31.441.441,0,0,1-.12-.32.342.342,0,0,1,.15-.3.683.683,0,0,1,.42-.12,1.62,1.62,0,0,1,1,.29V8.11a2.58,2.58,0,0,0-1-.16,1.641,1.641,0,0,0-1.09.34,1.08,1.08,0,0,0-.42.89c0,.51.32.91,1,1.21a2.907,2.907,0,0,1,.62.36.419.419,0,0,1,.15.32.381.381,0,0,1-.16.31.806.806,0,0,1-.45.11,1.66,1.66,0,0,1-1.09-.42V12a2.173,2.173,0,0,0,1.07.24,1.877,1.877,0,0,0,1.18-.33A1.08,1.08,0,0,0,6.84,11a1.048,1.048,0,0,0-.25-.7A2.425,2.425,0,0,0,5.76,9.73ZM11,11.32A2.191,2.191,0,0,0,11,9a1.808,1.808,0,0,0-.7-.75,2,2,0,0,0-1-.26,2.112,2.112,0,0,0-1.08.27A1.856,1.856,0,0,0,7.49,9a2.465,2.465,0,0,0-.26,1.14,2.256,2.256,0,0,0,.24,1,1.766,1.766,0,0,0,.69.74,2.056,2.056,0,0,0,1,.3l.86,1h1.21L10,12.08A1.79,1.79,0,0,0,11,11.32Zm-1-.25a.941.941,0,0,1-.76.35.916.916,0,0,1-.76-.36,1.523,1.523,0,0,1-.29-1,1.529,1.529,0,0,1,.29-1,1,1,0,0,1,.78-.37.869.869,0,0,1,.75.37,1.619,1.619,0,0,1,.27,1A1.459,1.459,0,0,1,10,11.07Z" fill="#f2f2f2"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,565 +0,0 @@
{
"id": "sqldatabase",
"title": "SQL Database",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "azure_cpu_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Percentage of CPU used by the database workload, relative to its limit."
},
{
"name": "azure_cpu_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Percentage of CPU used by the database workload, relative to its limit."
},
{
"name": "azure_cpu_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Percentage of CPU used by the database workload, relative to its limit."
},
{
"name": "azure_sql_instance_cpu_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Total CPU usage (user plus system) of the SQL instance, as a percentage."
},
{
"name": "azure_sql_instance_cpu_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Total CPU usage (user plus system) of the SQL instance, as a percentage."
},
{
"name": "azure_sql_instance_cpu_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Total CPU usage (user plus system) of the SQL instance, as a percentage."
},
{
"name": "azure_sql_instance_memory_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Memory usage of the SQL instance, as a percentage of its limit."
},
{
"name": "azure_sql_instance_memory_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Memory usage of the SQL instance, as a percentage of its limit."
},
{
"name": "azure_sql_instance_memory_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Memory usage of the SQL instance, as a percentage of its limit."
},
{
"name": "azure_physical_data_read_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Data file IO usage as a percentage of the limit."
},
{
"name": "azure_physical_data_read_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Data file IO usage as a percentage of the limit."
},
{
"name": "azure_physical_data_read_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Data file IO usage as a percentage of the limit."
},
{
"name": "azure_log_write_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Transaction log write throughput as a percentage of the limit."
},
{
"name": "azure_log_write_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Transaction log write throughput as a percentage of the limit."
},
{
"name": "azure_log_write_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Transaction log write throughput as a percentage of the limit."
},
{
"name": "azure_workers_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Worker threads in use as a percentage of the limit."
},
{
"name": "azure_workers_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Worker threads in use as a percentage of the limit."
},
{
"name": "azure_workers_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Worker threads in use as a percentage of the limit."
},
{
"name": "azure_sessions_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Active sessions as a percentage of the limit."
},
{
"name": "azure_sessions_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Active sessions as a percentage of the limit."
},
{
"name": "azure_sessions_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Active sessions as a percentage of the limit."
},
{
"name": "azure_sessions_count_average",
"unit": "Count",
"type": "Gauge",
"description": "Number of active sessions."
},
{
"name": "azure_sessions_count_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Number of active sessions."
},
{
"name": "azure_sessions_count_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Number of active sessions."
},
{
"name": "azure_dtu_consumption_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "DTU consumption as a percentage of the limit (DTU-based purchasing model)."
},
{
"name": "azure_dtu_consumption_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "DTU consumption as a percentage of the limit (DTU-based purchasing model)."
},
{
"name": "azure_dtu_consumption_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "DTU consumption as a percentage of the limit (DTU-based purchasing model)."
},
{
"name": "azure_dtu_used_average",
"unit": "Count",
"type": "Gauge",
"description": "DTUs used (DTU-based purchasing model)."
},
{
"name": "azure_dtu_used_maximum",
"unit": "Count",
"type": "Gauge",
"description": "DTUs used (DTU-based purchasing model)."
},
{
"name": "azure_dtu_used_minimum",
"unit": "Count",
"type": "Gauge",
"description": "DTUs used (DTU-based purchasing model)."
},
{
"name": "azure_dtu_limit_average",
"unit": "Count",
"type": "Gauge",
"description": "DTU limit (DTU-based purchasing model)."
},
{
"name": "azure_dtu_limit_maximum",
"unit": "Count",
"type": "Gauge",
"description": "DTU limit (DTU-based purchasing model)."
},
{
"name": "azure_dtu_limit_minimum",
"unit": "Count",
"type": "Gauge",
"description": "DTU limit (DTU-based purchasing model)."
},
{
"name": "azure_cpu_used_average",
"unit": "Count",
"type": "Gauge",
"description": "vCores used (vCore-based purchasing model)."
},
{
"name": "azure_cpu_used_maximum",
"unit": "Count",
"type": "Gauge",
"description": "vCores used (vCore-based purchasing model)."
},
{
"name": "azure_cpu_used_minimum",
"unit": "Count",
"type": "Gauge",
"description": "vCores used (vCore-based purchasing model)."
},
{
"name": "azure_cpu_limit_average",
"unit": "Count",
"type": "Gauge",
"description": "vCore limit (vCore-based purchasing model)."
},
{
"name": "azure_cpu_limit_maximum",
"unit": "Count",
"type": "Gauge",
"description": "vCore limit (vCore-based purchasing model)."
},
{
"name": "azure_cpu_limit_minimum",
"unit": "Count",
"type": "Gauge",
"description": "vCore limit (vCore-based purchasing model)."
},
{
"name": "azure_storage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Data space used by the database."
},
{
"name": "azure_storage_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Data space used by the database."
},
{
"name": "azure_storage_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Data space used by the database."
},
{
"name": "azure_storage_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Data space used as a percentage of the maximum data size."
},
{
"name": "azure_storage_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Data space used as a percentage of the maximum data size."
},
{
"name": "azure_storage_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Data space used as a percentage of the maximum data size."
},
{
"name": "azure_allocated_data_storage_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Data space allocated to the database (includes unused space)."
},
{
"name": "azure_allocated_data_storage_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Data space allocated to the database (includes unused space)."
},
{
"name": "azure_allocated_data_storage_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Data space allocated to the database (includes unused space)."
},
{
"name": "azure_xtp_storage_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "In-Memory OLTP storage used as a percentage of the limit."
},
{
"name": "azure_xtp_storage_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "In-Memory OLTP storage used as a percentage of the limit."
},
{
"name": "azure_xtp_storage_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "In-Memory OLTP storage used as a percentage of the limit."
},
{
"name": "azure_full_backup_size_bytes_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative full backup storage size."
},
{
"name": "azure_full_backup_size_bytes_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative full backup storage size."
},
{
"name": "azure_full_backup_size_bytes_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative full backup storage size."
},
{
"name": "azure_diff_backup_size_bytes_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative differential backup storage size."
},
{
"name": "azure_diff_backup_size_bytes_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative differential backup storage size."
},
{
"name": "azure_diff_backup_size_bytes_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative differential backup storage size."
},
{
"name": "azure_log_backup_size_bytes_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative transaction log backup storage size."
},
{
"name": "azure_log_backup_size_bytes_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative transaction log backup storage size."
},
{
"name": "azure_log_backup_size_bytes_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Cumulative transaction log backup storage size."
},
{
"name": "azure_replication_lag_seconds_average",
"unit": "Seconds",
"type": "Gauge",
"description": "Geo-replication lag (RPO) in seconds; reported on the primary database only."
},
{
"name": "azure_replication_lag_seconds_maximum",
"unit": "Seconds",
"type": "Gauge",
"description": "Geo-replication lag (RPO) in seconds; reported on the primary database only."
},
{
"name": "azure_replication_lag_seconds_minimum",
"unit": "Seconds",
"type": "Gauge",
"description": "Geo-replication lag (RPO) in seconds; reported on the primary database only."
},
{
"name": "azure_connection_successful_total",
"unit": "Count",
"type": "Sum",
"description": "Number of successful connections."
},
{
"name": "azure_connection_successful_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of successful connections."
},
{
"name": "azure_connection_failed_total",
"unit": "Count",
"type": "Sum",
"description": "Number of failed connections caused by system errors."
},
{
"name": "azure_connection_failed_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of failed connections caused by system errors."
},
{
"name": "azure_connection_failed_user_error_total",
"unit": "Count",
"type": "Sum",
"description": "Number of failed connections caused by user errors."
},
{
"name": "azure_connection_failed_user_error_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of failed connections caused by user errors."
},
{
"name": "azure_blocked_by_firewall_total",
"unit": "Count",
"type": "Sum",
"description": "Number of connection attempts blocked by the firewall."
},
{
"name": "azure_blocked_by_firewall_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of connection attempts blocked by the firewall."
},
{
"name": "azure_deadlock_total",
"unit": "Count",
"type": "Sum",
"description": "Number of deadlocks."
},
{
"name": "azure_deadlock_count",
"unit": "Count",
"type": "Gauge",
"description": "Number of deadlocks."
},
{
"name": "azure_availability_average",
"unit": "Percent",
"type": "Gauge",
"description": "Database availability percentage (100 if connections succeed, 0 if all fail) per minute."
},
{
"name": "azure_availability_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Database availability percentage (100 if connections succeed, 0 if all fail) per minute."
},
{
"name": "azure_availability_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Database availability percentage (100 if connections succeed, 0 if all fail) per minute."
},
{
"name": "azure_availability_count",
"unit": "Percent",
"type": "Gauge",
"description": "Database availability percentage (100 if connections succeed, 0 if all fail) per minute."
},
{
"name": "azure_availability_total",
"unit": "Percent",
"type": "Sum",
"description": "Database availability percentage (100 if connections succeed, 0 if all fail) per minute."
},
{
"name": "azure_tempdb_data_size_average",
"unit": "Count",
"type": "Gauge",
"description": "Space used in tempdb data files, in kilobytes."
},
{
"name": "azure_tempdb_data_size_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Space used in tempdb data files, in kilobytes."
},
{
"name": "azure_tempdb_data_size_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Space used in tempdb data files, in kilobytes."
},
{
"name": "azure_tempdb_log_size_average",
"unit": "Count",
"type": "Gauge",
"description": "Space used in the tempdb transaction log file, in kilobytes."
},
{
"name": "azure_tempdb_log_size_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Space used in the tempdb transaction log file, in kilobytes."
},
{
"name": "azure_tempdb_log_size_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Space used in the tempdb transaction log file, in kilobytes."
},
{
"name": "azure_tempdb_log_used_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "tempdb transaction log space used as a percentage."
},
{
"name": "azure_tempdb_log_used_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "tempdb transaction log space used as a percentage."
},
{
"name": "azure_tempdb_log_used_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "tempdb transaction log space used as a percentage."
}
],
"logs": [
{
"name": "Resource ID",
"path": "resources.azure.resource.id",
"type": "string"
}
]
},
"telemetryCollectionStrategy": {
"azure": {
"resourceProvider": "Microsoft.Sql",
"resourceType": "servers/databases",
"metrics": {},
"logs": {
"categoryGroups": [
"allLogs"
]
}
}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "Azure SQL Database Overview",
"description": "Overview of Azure SQL Database metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -1,7 +0,0 @@
### Monitor Azure SQL Database with SigNoz
Collect key Azure SQL Database (single database) metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.Sql/servers/databases` resource type.
Note: This integration is for Azure SQL Database (the PaaS offering). Azure SQL Managed Instance and SQL Server on Azure VMs expose a different set of metrics and are not covered here.

View File

@@ -1 +0,0 @@
<svg id="a5c93a83-9fd9-4ccb-ba77-53d6c609a7d3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><defs><linearGradient id="acf2da4b-8aca-4b58-b995-ca6956cf663e" x1="5.41" y1="17.33" x2="5.41" y2="0.61" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#949494"/><stop offset="0.53" stop-color="#a2a2a2"/><stop offset="1" stop-color="#b3b3b3"/></linearGradient><linearGradient id="b152b416-25c2-4a4a-9d7f-a0bb2a13f84a" x1="10.04" y1="17.39" x2="10.04" y2="6.82" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0078d4"/><stop offset="0.16" stop-color="#1380da"/><stop offset="0.53" stop-color="#3c91e5"/><stop offset="0.82" stop-color="#559cec"/><stop offset="1" stop-color="#5ea0ef"/></linearGradient></defs><title>Icon-databases-136</title><path d="M10.32,16.76a.58.58,0,0,1-.57.57H1.07a.57.57,0,0,1-.57-.57V1.18A.56.56,0,0,1,1.07.61H9.75a.57.57,0,0,1,.57.57Z" fill="url(#acf2da4b-8aca-4b58-b995-ca6956cf663e)"/><path d="M1.94,6.47A1.07,1.07,0,0,1,3,5.41H7.9A1.07,1.07,0,0,1,9,6.47H9A1.07,1.07,0,0,1,7.9,7.54H3A1.07,1.07,0,0,1,1.94,6.47Z" fill="#003067"/><path d="M1.94,3.31A1.07,1.07,0,0,1,3,2.24H7.9A1.07,1.07,0,0,1,9,3.31H9A1.07,1.07,0,0,1,7.9,4.37H3A1.07,1.07,0,0,1,1.94,3.31Z" fill="#003067"/><circle cx="3.06" cy="3.31" r="0.72" fill="#50e6ff"/><circle cx="3.06" cy="6.47" r="0.72" fill="#50e6ff"/><path d="M17.5,14.08a3.36,3.36,0,0,0-2.91-3.22,4.22,4.22,0,0,0-4.35-4A4.32,4.32,0,0,0,6.1,9.64a4,4,0,0,0-3.52,3.85,4.06,4.06,0,0,0,4.2,3.9l.37,0H14l.17,0A3.39,3.39,0,0,0,17.5,14.08Z" fill="url(#b152b416-25c2-4a4a-9d7f-a0bb2a13f84a)"/><path d="M13.61,14.45V11.09h-.93v4.12h2.45v-.76ZM6.51,12.8A2.23,2.23,0,0,1,6,12.49a.44.44,0,0,1-.12-.32.34.34,0,0,1,.15-.3.66.66,0,0,1,.42-.12,1.66,1.66,0,0,1,1,.29v-.86a2.89,2.89,0,0,0-1-.15,1.69,1.69,0,0,0-1.09.33,1.1,1.1,0,0,0-.41.89,1.34,1.34,0,0,0,.94,1.2,2.51,2.51,0,0,1,.61.36.42.42,0,0,1,.15.32.34.34,0,0,1-.15.3.75.75,0,0,1-.45.12,1.63,1.63,0,0,1-1.08-.42v.92A2.25,2.25,0,0,0,6,15.28,1.91,1.91,0,0,0,7.15,15a1.07,1.07,0,0,0,.43-.91,1,1,0,0,0-.25-.7A2.36,2.36,0,0,0,6.51,12.8Zm5.16,1.58A2.37,2.37,0,0,0,12,13.12,2.28,2.28,0,0,0,11.75,12a1.77,1.77,0,0,0-.69-.74A1.94,1.94,0,0,0,10,11,2.21,2.21,0,0,0,9,11.29a1.87,1.87,0,0,0-.73.77A2.52,2.52,0,0,0,8,13.2a2.26,2.26,0,0,0,.24,1.05,1.87,1.87,0,0,0,.68.74,2,2,0,0,0,1,.29l.85,1h1.2l-1.19-1.1A1.82,1.82,0,0,0,11.67,14.38Zm-.93-.25a.92.92,0,0,1-.76.35.91.91,0,0,1-.75-.36,1.5,1.5,0,0,1-.28-1,1.46,1.46,0,0,1,.29-1,.92.92,0,0,1,.77-.37.86.86,0,0,1,.74.37,1.54,1.54,0,0,1,.28,1A1.47,1.47,0,0,1,10.74,14.13Z" fill="#f2f2f2"/></svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,167 +0,0 @@
{
"id": "sqldatabasemi",
"title": "SQL Database Managed Instance",
"icon": "file://icon.svg",
"overview": "file://overview.md",
"supportedSignals": {
"metrics": true,
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "azure_avg_cpu_percent_average",
"unit": "Percent",
"type": "Gauge",
"description": "Average CPU utilization of the managed instance, as a percentage."
},
{
"name": "azure_avg_cpu_percent_maximum",
"unit": "Percent",
"type": "Gauge",
"description": "Maximum CPU utilization of the managed instance, as a percentage."
},
{
"name": "azure_avg_cpu_percent_minimum",
"unit": "Percent",
"type": "Gauge",
"description": "Minimum CPU utilization of the managed instance, as a percentage."
},
{
"name": "azure_io_bytes_read_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Average bytes read from storage by the managed instance."
},
{
"name": "azure_io_bytes_read_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Maximum bytes read from storage by the managed instance."
},
{
"name": "azure_io_bytes_read_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Minimum bytes read from storage by the managed instance."
},
{
"name": "azure_io_bytes_written_average",
"unit": "Bytes",
"type": "Gauge",
"description": "Average bytes written to storage by the managed instance."
},
{
"name": "azure_io_bytes_written_maximum",
"unit": "Bytes",
"type": "Gauge",
"description": "Maximum bytes written to storage by the managed instance."
},
{
"name": "azure_io_bytes_written_minimum",
"unit": "Bytes",
"type": "Gauge",
"description": "Minimum bytes written to storage by the managed instance."
},
{
"name": "azure_io_requests_average",
"unit": "Count",
"type": "Gauge",
"description": "Average number of storage IO requests made by the managed instance."
},
{
"name": "azure_io_requests_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Maximum number of storage IO requests made by the managed instance."
},
{
"name": "azure_io_requests_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Minimum number of storage IO requests made by the managed instance."
},
{
"name": "azure_reserved_storage_mb_average",
"unit": "Count",
"type": "Gauge",
"description": "Average storage space reserved for the managed instance, in megabytes."
},
{
"name": "azure_reserved_storage_mb_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Maximum storage space reserved for the managed instance, in megabytes."
},
{
"name": "azure_reserved_storage_mb_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Minimum storage space reserved for the managed instance, in megabytes."
},
{
"name": "azure_storage_space_used_mb_average",
"unit": "Count",
"type": "Gauge",
"description": "Average storage space used by the managed instance, in megabytes."
},
{
"name": "azure_storage_space_used_mb_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Maximum storage space used by the managed instance, in megabytes."
},
{
"name": "azure_storage_space_used_mb_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Minimum storage space used by the managed instance, in megabytes."
},
{
"name": "azure_virtual_core_count_average",
"unit": "Count",
"type": "Gauge",
"description": "Average number of virtual cores provisioned for the managed instance."
},
{
"name": "azure_virtual_core_count_maximum",
"unit": "Count",
"type": "Gauge",
"description": "Maximum number of virtual cores provisioned for the managed instance."
},
{
"name": "azure_virtual_core_count_minimum",
"unit": "Count",
"type": "Gauge",
"description": "Minimum number of virtual cores provisioned for the managed instance."
}
],
"logs": [
{
"name": "Resource ID",
"path": "resources.azure.resource.id",
"type": "string"
}
]
},
"telemetryCollectionStrategy": {
"azure": {
"resourceProvider": "Microsoft.Sql",
"resourceType": "managedInstances",
"metrics": {},
"logs": {
"categoryGroups": ["allLogs"]
}
}
},
"assets": {
"dashboards": [
{
"id": "overview",
"title": "Azure SQL Database Managed Instance Overview",
"description": "Overview of Azure SQL Database Managed Instance metrics",
"definition": "file://assets/dashboards/overview.json"
}
]
}
}

View File

@@ -1,5 +0,0 @@
### Monitor Azure SQL Database Managed Instance with SigNoz
Collect key Azure SQL Database Managed Instance metrics and view them with an out of the box dashboard.
This integration collects platform metrics for the `Microsoft.Sql/managedInstances` resource type.

View File

@@ -1,32 +0,0 @@
package implinframonitoring
import (
"context"
"fmt"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/telemetrymetrics"
"github.com/SigNoz/signoz/pkg/valuer"
)
func (m *module) Collect(ctx context.Context, _ valuer.UUID) (map[string]any, error) {
stats := make(map[string]any)
metadataTable := fmt.Sprintf("%s.%s", telemetrymetrics.DBName, telemetrymetrics.AttributesMetadataTableName)
var (
systemMetricCount uint64
k8sMetricCount uint64
)
query := fmt.Sprintf(
"SELECT (SELECT count() FROM (SELECT 1 FROM %s WHERE metric_name LIKE 'system.%%' LIMIT 1)), (SELECT count() FROM (SELECT 1 FROM %s WHERE metric_name LIKE 'k8s.%%' LIMIT 1))",
metadataTable, metadataTable,
)
if err := m.telemetryStore.ClickhouseDB().QueryRow(ctx, query).Scan(&systemMetricCount, &k8sMetricCount); err == nil {
stats["telemetry.metrics.system.exists"] = systemMetricCount > 0
stats["telemetry.metrics.k8s.exists"] = k8sMetricCount > 0
} else {
m.logger.DebugContext(ctx, "failed to collect metrics namespace existence stats", errors.Attr(err))
}
return stats, nil
}

Some files were not shown because too many files have changed in this diff Show More