@oneuptime/common 14.0.10 → 14.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AIConversation.ts +127 -1
- package/Models/DatabaseModels/CephCluster.ts +12 -0
- package/Models/DatabaseModels/CloudResource.ts +12 -0
- package/Models/DatabaseModels/DatabaseServer.ts +12 -0
- package/Models/DatabaseModels/DockerHost.ts +12 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +12 -0
- package/Models/DatabaseModels/GlobalOidc.ts +0 -4
- package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
- package/Models/DatabaseModels/GlobalSso.ts +0 -4
- package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
- package/Models/DatabaseModels/Host.ts +12 -0
- package/Models/DatabaseModels/IncidentForm.ts +594 -0
- package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
- package/Models/DatabaseModels/Index.ts +14 -0
- package/Models/DatabaseModels/IoTFleet.ts +12 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
- package/Models/DatabaseModels/MessageQueue.ts +1102 -0
- package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
- package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
- package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
- package/Models/DatabaseModels/PodmanHost.ts +12 -0
- package/Models/DatabaseModels/Project.ts +59 -0
- package/Models/DatabaseModels/ProjectOidc.ts +0 -4
- package/Models/DatabaseModels/ProjectSso.ts +0 -4
- package/Models/DatabaseModels/ProxmoxCluster.ts +12 -0
- package/Models/DatabaseModels/RumApplication.ts +12 -0
- package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
- package/Models/DatabaseModels/Service.ts +12 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
- package/Models/DatabaseModels/StatusPageSso.ts +0 -4
- package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
- package/Models/DatabaseModels/VMwareVCenter.ts +12 -0
- package/Server/API/AIChatAPI.ts +13 -114
- package/Server/API/AIInvestigationAPI.ts +3 -3
- package/Server/API/AIInvestigationConversationAPI.ts +266 -0
- package/Server/API/IncidentFormAPI.ts +376 -0
- package/Server/API/ProjectOIDC.ts +16 -25
- package/Server/API/ProjectSSO.ts +16 -24
- package/Server/API/StatusPageAPI.ts +42 -77
- package/Server/Enterprise/EnterpriseEdition.ts +93 -28
- package/Server/Enterprise/EnterpriseFeature.ts +25 -3
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
- package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
- package/Server/EnvironmentConfig.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
- package/Server/Middleware/UserAuthorization.ts +16 -38
- package/Server/Services/AIService.ts +10 -13
- package/Server/Services/IncidentCustomFieldService.ts +112 -2
- package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
- package/Server/Services/IncidentEpisodeService.ts +10 -1
- package/Server/Services/IncidentFormService.ts +1566 -0
- package/Server/Services/IncidentFormSubmissionService.ts +97 -0
- package/Server/Services/IncidentGroupingEngineService.ts +45 -8
- package/Server/Services/IncidentService.ts +123 -5
- package/Server/Services/IncidentTemplateService.ts +22 -0
- package/Server/Services/Index.ts +14 -0
- package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
- package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
- package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
- package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
- package/Server/Services/MessageQueueService.ts +1463 -0
- package/Server/Services/OpenTelemetryIngestService.ts +37 -4
- package/Server/Services/ProjectService.ts +0 -59
- package/Server/Services/StatusPagePrivateUserService.ts +1 -4
- package/Server/Services/StatusPageService.ts +1 -51
- package/Server/Services/TeamComplianceSettingService.ts +163 -42
- package/Server/Services/UserNotificationRuleService.ts +8 -1
- package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
- package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
- package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
- package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
- package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
- package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +7 -1
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +34 -14
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +69 -24
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +9 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +53 -10
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
- package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
- package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
- package/Server/Utils/EditionEnforcement.ts +19 -202
- package/Server/Utils/LLM/LLMService.ts +259 -63
- package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/SameOriginRequest.ts +203 -0
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
- package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
- package/Server/Utils/VendorAssets.ts +3 -3
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +10 -1
- package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
- package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
- package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
- package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
- package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
- package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
- package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
- package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
- package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
- package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +174 -12
- package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
- package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
- package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +5 -0
- package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
- package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
- package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
- package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
- package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
- package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
- package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
- package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
- package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
- package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
- package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
- package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
- package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
- package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
- package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
- package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
- package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
- package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
- package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
- package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
- package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
- package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
- package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
- package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
- package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
- package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
- package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
- package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
- package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
- package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
- package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
- package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
- package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
- package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
- package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
- package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
- package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
- package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
- package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
- package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
- package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
- package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
- package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
- package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
- package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
- package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
- package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
- package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +298 -9
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +453 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
- package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
- package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
- package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
- package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
- package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
- package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
- package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
- package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
- package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
- package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
- package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
- package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
- package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
- package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +28 -9
- package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
- package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
- package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
- package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
- package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
- package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
- package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
- package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
- package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
- package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
- package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
- package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
- package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
- package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
- package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
- package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
- package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
- package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
- package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
- package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
- package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
- package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
- package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
- package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
- package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
- package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
- package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
- package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
- package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
- package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
- package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
- package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
- package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
- package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
- package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
- package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
- package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
- package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
- package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
- package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +266 -25
- package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
- package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
- package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
- package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
- package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +297 -15
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +10 -6
- package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
- package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
- package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
- package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
- package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
- package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
- package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
- package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
- package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
- package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
- package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
- package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
- package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
- package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
- package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
- package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
- package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
- package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +5 -3
- package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
- package/Tests/Types/XML.test.ts +51 -0
- package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
- package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
- package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
- package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
- package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
- package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
- package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
- package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
- package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
- package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
- package/Tests/UI/Components/Icon.test.tsx +32 -0
- package/Tests/UI/Components/Input.test.tsx +31 -0
- package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
- package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
- package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
- package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
- package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
- package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
- package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
- package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
- package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
- package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
- package/Tests/UI/Components/TextArea.test.tsx +19 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
- package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
- package/Tests/UI/EsbuildConfig.test.ts +115 -39
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
- package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
- package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
- package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
- package/Types/AI/AIAgentRunLimits.ts +146 -0
- package/Types/Code/CodeType.ts +2 -0
- package/Types/Code/JsonSyntax.ts +923 -0
- package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
- package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
- package/Types/Icon/IconProp.ts +2 -0
- package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
- package/Types/Incident/IncidentFormPublic.ts +1145 -0
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
- package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
- package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
- package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
- package/Types/MessageQueue/MessagingSystem.ts +518 -0
- package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
- package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
- package/Types/Permission.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +10 -4
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/Team/ComplianceNotificationChannel.ts +4 -3
- package/Types/Team/ComplianceRule.ts +71 -18
- package/Types/Team/ComplianceRuleType.ts +2 -2
- package/Types/Team/TeamComplianceStatus.ts +6 -2
- package/Types/Telemetry/EntityType.ts +16 -0
- package/Types/Telemetry/LockedFilterDetail.ts +22 -0
- package/Types/XML.ts +26 -0
- package/UI/Components/CodeEditor/CodeEditor.css +379 -0
- package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
- package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
- package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
- package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
- package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
- package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
- package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
- package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
- package/UI/Components/Forms/Fields/FormField.tsx +8 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Icon/Icon.tsx +18 -0
- package/UI/Components/Input/Input.tsx +11 -4
- package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
- package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
- package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
- package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
- package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
- package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
- package/UI/Components/Table/EmptyTableMessage.ts +81 -0
- package/UI/Components/Table/Table.tsx +50 -21
- package/UI/Components/Table/TableRow.tsx +9 -1
- package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
- package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
- package/UI/Components/TextArea/TextArea.tsx +9 -2
- package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
- package/UI/Styles/Theme.css +8 -0
- package/UI/esbuild-config.js +2 -32
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
- package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
- package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
- package/Utils/Telemetry/EntityKey.ts +48 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +12 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
- package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +14 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
- package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js +12 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +61 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +12 -0
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +12 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +9 -103
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
- package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
- package/build/dist/Server/API/IncidentFormAPI.js +245 -0
- package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectOIDC.js +16 -24
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +16 -24
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +42 -75
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +10 -13
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFormService.js +1202 -0
- package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +106 -4
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +14 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueService.js +1140 -0
- package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +0 -54
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +0 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +7 -2
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +29 -14
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +38 -14
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +2 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +40 -10
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
- package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
- package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +3 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +10 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
- package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
- package/build/dist/Types/Code/CodeType.js +2 -0
- package/build/dist/Types/Code/CodeType.js.map +1 -1
- package/build/dist/Types/Code/JsonSyntax.js +604 -0
- package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
- package/build/dist/Types/Icon/IconProp.js +2 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +274 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +10 -4
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRule.js +48 -14
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
- package/build/dist/Types/Telemetry/EntityType.js +16 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/XML.js +23 -0
- package/build/dist/Types/XML.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +8 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +11 -3
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
- package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
- package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +30 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +9 -2
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -3
- package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
- package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
- package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
- package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
- package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
- package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
- package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
- package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
- package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
- package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
|
@@ -0,0 +1,2128 @@
|
|
|
1
|
+
import ObjectID from "../ObjectID";
|
|
2
|
+
import Dictionary from "../Dictionary";
|
|
3
|
+
import Includes from "../BaseDatabase/Includes";
|
|
4
|
+
import InBetween from "../BaseDatabase/InBetween";
|
|
5
|
+
import MonitorStep from "./MonitorStep";
|
|
6
|
+
import MonitorCriteria from "./MonitorCriteria";
|
|
7
|
+
import MonitorCriteriaInstance from "./MonitorCriteriaInstance";
|
|
8
|
+
import { EvaluateOverTimeType, FilterType } from "./CriteriaFilter";
|
|
9
|
+
import MonitorType from "./MonitorType";
|
|
10
|
+
import RollingTime from "../RollingTime/RollingTime";
|
|
11
|
+
import RollingTimeUtil from "../RollingTime/RollingTimeUtil";
|
|
12
|
+
import MetricsAggregationType from "../Metrics/MetricsAggregationType";
|
|
13
|
+
import MetricQueryConfigData from "../Metrics/MetricQueryConfigData";
|
|
14
|
+
import MetricFormulaConfigData from "../Metrics/MetricFormulaConfigData";
|
|
15
|
+
import MetricsViewConfig from "../Metrics/MetricsViewConfig";
|
|
16
|
+
import MetricUnitUtil from "../../Utils/MetricUnitUtil";
|
|
17
|
+
import {
|
|
18
|
+
MESSAGE_QUEUE_METRICS,
|
|
19
|
+
MessageQueueMetricDescriptor,
|
|
20
|
+
MessageQueueSignal,
|
|
21
|
+
getMessageQueueMetricId,
|
|
22
|
+
} from "../MessageQueue/MessageQueueMetricCatalog";
|
|
23
|
+
import {
|
|
24
|
+
ResolvedMessagingDestination,
|
|
25
|
+
resolveMessagingMetricDatapoint,
|
|
26
|
+
} from "../MessageQueue/MessagingTelemetryResolver";
|
|
27
|
+
import {
|
|
28
|
+
MessageQueueIdentity,
|
|
29
|
+
buildMessageQueueIdentifier,
|
|
30
|
+
toMessageQueueIdentity,
|
|
31
|
+
} from "../MessageQueue/MessageQueueIdentity";
|
|
32
|
+
import {
|
|
33
|
+
buildHealthyCriteriaInstance,
|
|
34
|
+
buildUnhealthyCriteriaInstance,
|
|
35
|
+
getRecoveryFilterType,
|
|
36
|
+
} from "./Recommendation/RecommendationCriteriaBuilder";
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* The alert policy of the broker health metrics the Queues product curates
|
|
40
|
+
* (MESSAGE_QUEUE_METRICS: the collector-contrib receivers, Prometheus
|
|
41
|
+
* scrapes and cloud monitoring sources each messaging system's catalog entry
|
|
42
|
+
* names), as one template per catalog entry, and the builders that turn a
|
|
43
|
+
* catalog entry into a Metrics monitor on ONE queue (a MessageQueue row).
|
|
44
|
+
* Pure and isomorphic, like the catalog it reads.
|
|
45
|
+
*
|
|
46
|
+
* WHO READS THEM
|
|
47
|
+
*
|
|
48
|
+
* A queue's broker health is ordinary OTel metrics, so a queue is watched by
|
|
49
|
+
* a plain `MonitorType.Metrics` monitor, which gets the generic metric
|
|
50
|
+
* evaluator, the monitor-overview preview and the incident root-cause chart
|
|
51
|
+
* for free. In the product, this module builds one in exactly one place:
|
|
52
|
+
* "Create monitor" beside a broker health gauge on a queue's Overview
|
|
53
|
+
* (Components/MessageQueue/MessageQueueMetricMonitorLink.ts). The link builds
|
|
54
|
+
* its query and view with buildMessageQueueMetricMonitorQuery and
|
|
55
|
+
* buildMessageQueueMetricMonitorViewConfig, and reads four fields of the
|
|
56
|
+
* entry's template (getMessageQueueAlertTemplateForMetric): the threshold,
|
|
57
|
+
* the comparison, the severity and the window. They are only a starting
|
|
58
|
+
* point: Monitor Create builds its own criteria from them, which fire on ANY
|
|
59
|
+
* point above the threshold, not the held breach and the recovery that
|
|
60
|
+
* THRESHOLDS below describes. The Overview's broker health reads use the
|
|
61
|
+
* rest (Components/MessageQueue/MessageQueueTelemetryQueries.ts): which
|
|
62
|
+
* metrics a monitor can evaluate, the attributes an observed series is read
|
|
63
|
+
* with, and how late a source's points arrive.
|
|
64
|
+
*
|
|
65
|
+
* Nothing lists or offers the templates themselves. The Database library's
|
|
66
|
+
* templates reach people through the monitor Recommendations, and
|
|
67
|
+
* MonitorRecommendationResourceType has no queue member, so a template's
|
|
68
|
+
* getMonitorStep (the complete monitor it stands for: queries, breach and
|
|
69
|
+
* recovery criteria, incident text) has no caller in the product. The tests
|
|
70
|
+
* build it and evaluate it against broker-shaped rows through the real
|
|
71
|
+
* metric criteria evaluator. Offering the templates would be a feature of
|
|
72
|
+
* its own.
|
|
73
|
+
*
|
|
74
|
+
* WHAT MAKES THEM THIS QUEUE'S MONITORS
|
|
75
|
+
*
|
|
76
|
+
* Nothing is stamped on a broker metric that names its queue the way
|
|
77
|
+
* `oneuptime.database.server.id` names a database, and a metric monitor
|
|
78
|
+
* cannot be scoped by entity key. So the query is scoped by the attributes
|
|
79
|
+
* the resolver itself keys the queue on (resolveMessagingMetricDatapoint):
|
|
80
|
+
* the catalog entry's destination attribute, its broker-scope attribute (an
|
|
81
|
+
* Azure namespace) and its required attributes (Azure's resource `type`,
|
|
82
|
+
* a CloudWatch JSON stream's `resource.service.name`) — with the values
|
|
83
|
+
* EXACTLY as a series of the metric carries them. Exactly, because a metric
|
|
84
|
+
* filter compiles to `attributes['<key>'] = '<value>'`: the queue's identity
|
|
85
|
+
* is canonical (lowercased), but a stored "Orders" only matches "Orders".
|
|
86
|
+
*
|
|
87
|
+
* buildMessageQueueMetricMonitorQuery is that one code path, shared by the
|
|
88
|
+
* queue page's "Create monitor" link and getMonitorStep: hand it the catalog
|
|
89
|
+
* entry and the stored attributes of the series observed for it, and it
|
|
90
|
+
* builds the query only from series that resolve, through the same resolver
|
|
91
|
+
* ingest runs, to the same queue — so a filter can never watch another queue.
|
|
92
|
+
* Several series of one queue (the partitions of a Pulsar topic, the same
|
|
93
|
+
* entity under two spellings) become an `Includes` on the key that differs.
|
|
94
|
+
* The attributes that tell a queue's series APART (RabbitMQ's `state`, a
|
|
95
|
+
* Kafka consumer `group`) are never filtered on by the queue's own scope: a
|
|
96
|
+
* monitor watches all of them. buildMessageQueueMetricMonitorViewConfig
|
|
97
|
+
* then turns that query into the monitor's queries — one, or one per part
|
|
98
|
+
* of a series total and the formula adding them up — for both callers too.
|
|
99
|
+
*
|
|
100
|
+
* WHICH METRICS GET A TEMPLATE
|
|
101
|
+
*
|
|
102
|
+
* The monitor path has no rate: aggregations stop at Sum / Avg / Min / Max
|
|
103
|
+
* and percentiles, `EvaluateOverTimeType` has no delta, and the per-second
|
|
104
|
+
* transform is chart-only. A cumulative counter (RabbitMQ's published total,
|
|
105
|
+
* Kafka offsets, Pulsar's `*_total`) thresholded raw compares against a
|
|
106
|
+
* since-restart total that only grows: it fires once and never clears. So
|
|
107
|
+
* only catalog GAUGES get a template, and only for a signal a static
|
|
108
|
+
* "at or above" threshold means something for:
|
|
109
|
+
*
|
|
110
|
+
* - backlog, consumerLag, oldestMessageAge, deadLetter: a level that is
|
|
111
|
+
* healthy near zero and bad when high;
|
|
112
|
+
* - errors, throttled: a count per minute or per CloudWatch period (Azure
|
|
113
|
+
* Monitor's `*_total`, CloudWatch sums), bad when high.
|
|
114
|
+
*
|
|
115
|
+
* Not published, consumed or consumers. A throughput is not good or bad at
|
|
116
|
+
* a fixed level (the interesting event, a drop to zero, needs a baseline or
|
|
117
|
+
* an absence rule), and a queue with no consumers is often intended — a
|
|
118
|
+
* batch job that connects on a schedule, a topic nobody reads yet — while
|
|
119
|
+
* the harm it does, messages piling up, is exactly what Backlog catches.
|
|
120
|
+
* UNALERTABLE_MESSAGE_QUEUE_COUNTERS lists every counter the rule leaves
|
|
121
|
+
* out, and UNTEMPLATED_MESSAGE_QUEUE_GAUGES every gauge with a thresholdable
|
|
122
|
+
* signal that is deliberately left out, with the reason; the tests prove the
|
|
123
|
+
* three sets cover the catalog exactly.
|
|
124
|
+
*
|
|
125
|
+
* AGGREGATION CONTRACT
|
|
126
|
+
*
|
|
127
|
+
* A monitor window of up to three hours is read in one-minute buckets, and an
|
|
128
|
+
* ungrouped query folds every series AND every sample of a bucket into one
|
|
129
|
+
* number. The queue page combines a metric's series the way its catalog
|
|
130
|
+
* entry says (`aggregation` within a series, then `seriesCombine` across
|
|
131
|
+
* them), and a monitor must evaluate the number the page shows — so
|
|
132
|
+
* getMessageQueueMetricMonitorSeed, which the "Create monitor" link uses
|
|
133
|
+
* too, reads each entry this way:
|
|
134
|
+
*
|
|
135
|
+
* - a per-period count (aggregation Sum: Azure Monitor's `*_total`,
|
|
136
|
+
* CloudWatch's NumberOf*, Pub/Sub's DELTA counts): each point already is
|
|
137
|
+
* one series' count for its period, so the Sum of a bucket is the count.
|
|
138
|
+
* For "sum" that adds the series up, as the page does; for "max" an
|
|
139
|
+
* entry without series keys has one series per queue (CloudWatch's), and
|
|
140
|
+
* one with series keys alerts per series — the worst series crossing IS
|
|
141
|
+
* the highest one crossing. Never Max: a CloudWatch point is a Summary,
|
|
142
|
+
* which has no max field, and on such a point the metric engine's Max
|
|
143
|
+
* reads the point's own mean (Sum / SampleCount) — CloudWatch's Average,
|
|
144
|
+
* which for SNS's NumberOfNotificationsFailed, reported as one sample of
|
|
145
|
+
* 1 per failure, is 1 however many deliveries failed.
|
|
146
|
+
* - "max" over a level (the consumer group furthest behind, the worst
|
|
147
|
+
* partition, the same queue reported twice): Max of everything IS the
|
|
148
|
+
* worst series. On a CloudWatch Summary that Max reads each period's
|
|
149
|
+
* Average, which is what the page charts for the CloudWatch levels too.
|
|
150
|
+
* - "sum" over a level whose series are a closed set the source reports
|
|
151
|
+
* together on every scrape (MESSAGE_QUEUE_SERIES_TOTALS: RabbitMQ's
|
|
152
|
+
* ready and unacknowledged `state`): no single fold is right — the
|
|
153
|
+
* rabbitmq receiver scrapes every 10 seconds by default, so a Sum counts
|
|
154
|
+
* each series six times a minute and an Average halves ready +
|
|
155
|
+
* unacknowledged — so the monitor reads one query per value, with the
|
|
156
|
+
* entry's own aggregation, and adds them up with a formula: the page's
|
|
157
|
+
* total exactly.
|
|
158
|
+
* - "sum" over a level whose series are an open set (ActiveMQ's brokers, a
|
|
159
|
+
* Pulsar topic's partitions): a formula needs one query per series, and
|
|
160
|
+
* one written for the series seen today leaves out a broker or partition
|
|
161
|
+
* added tomorrow. Such a template is grouped by the entry's `seriesKeys`
|
|
162
|
+
* and alerts on each series with the entry's own aggregation — and says
|
|
163
|
+
* so: the threshold is for one series, not for the total.
|
|
164
|
+
*
|
|
165
|
+
* THRESHOLDS
|
|
166
|
+
*
|
|
167
|
+
* One default per signal (MESSAGE_QUEUE_ALERT_POLICIES), in the metric's own
|
|
168
|
+
* unit, always "at or above", and a few catalog entries whose meaning differs
|
|
169
|
+
* from their signal's override it (MESSAGE_QUEUE_ALERT_POLICY_OVERRIDES).
|
|
170
|
+
* Every threshold is a starting point a team retunes to its own queue; each
|
|
171
|
+
* default, and why, is documented where it is declared.
|
|
172
|
+
*
|
|
173
|
+
* The rest of this section describes the criteria getMonitorStep builds; a
|
|
174
|
+
* monitor opened from the "Create monitor" link has Monitor Create's own
|
|
175
|
+
* instead (see WHO READS THEM). A level must hold for the whole window (the
|
|
176
|
+
* sustained evaluation the recommendation builders default to), while a
|
|
177
|
+
* count fires on any one point: each point already is the count for its
|
|
178
|
+
* minute or CloudWatch period, and whether a period without events arrives
|
|
179
|
+
* as a 0 or as nothing at all depends on the source, so "every point" would
|
|
180
|
+
* only ever mean "every period that had some". Recovery needs the value back
|
|
181
|
+
* below a dead band for the whole window; for a count, and for a metric its
|
|
182
|
+
* source stops reporting when it is zero, a silent window counts as zero
|
|
183
|
+
* there, or an alert opened by a burst would hold the monitor offline
|
|
184
|
+
* forever.
|
|
185
|
+
*
|
|
186
|
+
* WINDOWS AND LATE SOURCES
|
|
187
|
+
*
|
|
188
|
+
* A monitor reads the points whose own timestamps fall in [now - window,
|
|
189
|
+
* now], and ingest stores a point at the time it measures, not the time it
|
|
190
|
+
* arrives. Brokers that are scraped or push live arrive within seconds. The
|
|
191
|
+
* two cloud receivers that poll arrive minutes late, by design:
|
|
192
|
+
*
|
|
193
|
+
* - `aws_cloudwatch` asks CloudWatch, once per `collection_interval`, for
|
|
194
|
+
* the periods in [align(end - collection_interval), end) with end =
|
|
195
|
+
* align(now - delay), and stamps each point with its period's start
|
|
196
|
+
* (receiver/awscloudwatchreceiver metrics.go@v0.161.0). Its newest point
|
|
197
|
+
* is 11 to 17 minutes old under the documented configuration (`period`
|
|
198
|
+
* 1m, `collection_interval` 5m, `delay` 10m) and 15 to 25 minutes old
|
|
199
|
+
* under the receiver's defaults (`period` 5m): a five- or ten-minute
|
|
200
|
+
* window never holds one.
|
|
201
|
+
* - `googlecloudmonitoring` asks Cloud Monitoring, once per
|
|
202
|
+
* `collection_interval`, for [now - ingestDelay - collection_interval,
|
|
203
|
+
* now - ingestDelay], ingestDelay being the metric's own (120 seconds for
|
|
204
|
+
* Pub/Sub's levels, 240 for dead_letter_message_count), and stamps each
|
|
205
|
+
* point with its interval's end (receiver.go and metrics_conversion.go
|
|
206
|
+
* @v0.161.0). A point arrives up to ingestDelay + collection_interval
|
|
207
|
+
* late — nine minutes with the receiver's default five-minute interval —
|
|
208
|
+
* so a five-minute window never reads most dead-lettered samples.
|
|
209
|
+
*
|
|
210
|
+
* An empty window is not harmless: a level never fires in one, and a count,
|
|
211
|
+
* whose recovery reads silence as zero, reports healthy through the
|
|
212
|
+
* incident. So a template reads at least its source's floor
|
|
213
|
+
* (MESSAGE_QUEUE_SOURCE_WINDOW_FLOORS: thirty minutes for CloudWatch,
|
|
214
|
+
* fifteen for Cloud Monitoring) instead of its signal's shorter window, and
|
|
215
|
+
* "sustained" keeps its meaning: AllValues still needs EVERY point in the
|
|
216
|
+
* window at or above the threshold, and a late source's window holds the
|
|
217
|
+
* points of its last (window - lag) minutes — 14 to 20 one-minute
|
|
218
|
+
* CloudWatch periods under the documented configuration, two to four
|
|
219
|
+
* five-minute periods under the receiver's defaults, and 8 to 14 of
|
|
220
|
+
* Pub/Sub's one-minute samples. The OpenTelemetry 1.0 Metric Stream names
|
|
221
|
+
* its metrics like the pull receiver, so its queues read the same window:
|
|
222
|
+
* longer than a stream needs, never blind. The JSON stream (`awsfirehose`)
|
|
223
|
+
* pushes as CloudWatch publishes and keeps its signal's window.
|
|
224
|
+
*
|
|
225
|
+
* UNITS
|
|
226
|
+
*
|
|
227
|
+
* Every query carries its catalog entry's display unit ("messages",
|
|
228
|
+
* "requests", "s") as its legend unit, so the alert text reads "1200
|
|
229
|
+
* messages" rather than a bare number (Kafka declares lag as the
|
|
230
|
+
* dimensionless "1", Azure Monitor its counts as "Count", a Prometheus scrape
|
|
231
|
+
* nothing at all), and a time threshold holds whether the source spells
|
|
232
|
+
* seconds "s" or "Seconds".
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
export type MessageQueueAlertTemplateCategory =
|
|
236
|
+
| "Backlog"
|
|
237
|
+
| "Consumer Lag"
|
|
238
|
+
| "Message Age"
|
|
239
|
+
| "Dead Letters"
|
|
240
|
+
| "Errors"
|
|
241
|
+
| "Throttling";
|
|
242
|
+
|
|
243
|
+
export type MessageQueueAlertTemplateSeverity = "Critical" | "Warning";
|
|
244
|
+
|
|
245
|
+
/*
|
|
246
|
+
* The stored attributes of one series of a metric, as the Metric table holds
|
|
247
|
+
* them: datapoint attributes bare, resource attributes `resource.`-prefixed.
|
|
248
|
+
*/
|
|
249
|
+
export type MessageQueueObservedAttributes = Readonly<Record<string, unknown>>;
|
|
250
|
+
|
|
251
|
+
/*
|
|
252
|
+
* A total a monitor adds up from a closed set of series: one query per
|
|
253
|
+
* value of `key`, each filtered `key = value`, summed by a formula (see
|
|
254
|
+
* MESSAGE_QUEUE_SERIES_TOTALS).
|
|
255
|
+
*/
|
|
256
|
+
export interface MessageQueueSeriesTotal {
|
|
257
|
+
// The series key whose values are the parts ("state").
|
|
258
|
+
key: string;
|
|
259
|
+
// Every value it takes, in the order the formula adds them.
|
|
260
|
+
values: Array<string>;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface MessageQueueAlertTemplateArgs {
|
|
264
|
+
/*
|
|
265
|
+
* The stored attributes of the series this queue's metric arrives with —
|
|
266
|
+
* one entry per series (a partitioned Pulsar topic reports one per
|
|
267
|
+
* partition), read from the template's own metric. Only series that
|
|
268
|
+
* resolve to the queue are used (buildMessageQueueMetricMonitorQuery).
|
|
269
|
+
*/
|
|
270
|
+
observedSeries: ReadonlyArray<MessageQueueObservedAttributes>;
|
|
271
|
+
/*
|
|
272
|
+
* The queue the monitor is for (parseMessageQueueIdentifier of the row's
|
|
273
|
+
* queueIdentifier). When given, a series that resolves to any other queue
|
|
274
|
+
* is ignored; when not, the first series that resolves decides.
|
|
275
|
+
*/
|
|
276
|
+
identity?: MessageQueueIdentity | null | undefined;
|
|
277
|
+
onlineMonitorStatusId: ObjectID;
|
|
278
|
+
offlineMonitorStatusId: ObjectID;
|
|
279
|
+
defaultIncidentSeverityId: ObjectID;
|
|
280
|
+
defaultAlertSeverityId: ObjectID;
|
|
281
|
+
monitorName: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
* One catalog entry's alert. The "Create monitor" link reads `threshold`,
|
|
286
|
+
* `filterType`, `severity` and `rollingTime`; everything else describes the
|
|
287
|
+
* monitor getMonitorStep builds (see WHO READS THEM).
|
|
288
|
+
*/
|
|
289
|
+
export interface MessageQueueAlertTemplate {
|
|
290
|
+
// "message-queue-<system>-<metric name>", kebab-cased: stable per entry.
|
|
291
|
+
id: string;
|
|
292
|
+
name: string;
|
|
293
|
+
description: string;
|
|
294
|
+
category: MessageQueueAlertTemplateCategory;
|
|
295
|
+
severity: MessageQueueAlertTemplateSeverity;
|
|
296
|
+
// Always Metrics; carried per template the way the other libraries do.
|
|
297
|
+
monitorType: MonitorType;
|
|
298
|
+
// The catalog entry's canonical system ("servicebus", "aws_sqs").
|
|
299
|
+
system: string;
|
|
300
|
+
// The component that emits the metric, as the catalog names it.
|
|
301
|
+
receiver: string;
|
|
302
|
+
signal: MessageQueueSignal;
|
|
303
|
+
// getMessageQueueMetricId of the entry: `${system}:${metricName}`.
|
|
304
|
+
metricId: string;
|
|
305
|
+
metricName: string;
|
|
306
|
+
// Every metric name the template's query reads — always one.
|
|
307
|
+
metricNames: Array<string>;
|
|
308
|
+
// The catalog's display unit, which the query carries as its legend unit.
|
|
309
|
+
unit: string;
|
|
310
|
+
// How each query folds a bucket (getMessageQueueMetricMonitorSeed).
|
|
311
|
+
aggregationType: MetricsAggregationType;
|
|
312
|
+
// The keys the monitor alerts per; empty when it reads as one queue.
|
|
313
|
+
groupByAttributeKeys: Array<string>;
|
|
314
|
+
/*
|
|
315
|
+
* The series the monitor adds up with a formula, one query each, or null
|
|
316
|
+
* when it reads one query (MESSAGE_QUEUE_SERIES_TOTALS).
|
|
317
|
+
*/
|
|
318
|
+
seriesTotal: MessageQueueSeriesTotal | null;
|
|
319
|
+
filterType: FilterType;
|
|
320
|
+
// In the metric's own unit.
|
|
321
|
+
threshold: number;
|
|
322
|
+
// "at or above 1,000 messages" — the comparison in words.
|
|
323
|
+
thresholdLabel: string;
|
|
324
|
+
// AllValues (a level held for the window) or AnyValue (a count, per minute).
|
|
325
|
+
evaluation: EvaluateOverTimeType;
|
|
326
|
+
/*
|
|
327
|
+
* The signal's window, widened to the source's floor when its points
|
|
328
|
+
* arrive late (MESSAGE_QUEUE_SOURCE_WINDOW_FLOORS).
|
|
329
|
+
*/
|
|
330
|
+
rollingTime: RollingTime;
|
|
331
|
+
// Whether recovery reads a silent window as zero.
|
|
332
|
+
recoverOnNoData: boolean;
|
|
333
|
+
/*
|
|
334
|
+
* The monitor step for one queue, or null when none of the observed series
|
|
335
|
+
* resolves to the queue under this template's metric — a template never
|
|
336
|
+
* builds a monitor that would watch nothing or another queue. Nothing in
|
|
337
|
+
* the product calls it yet (see WHO READS THEM).
|
|
338
|
+
*/
|
|
339
|
+
getMonitorStep: (args: MessageQueueAlertTemplateArgs) => MonitorStep | null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/*
|
|
343
|
+
* The signals a static "at or above" threshold means something for (see
|
|
344
|
+
* WHICH METRICS GET A TEMPLATE in the header). published, consumed and
|
|
345
|
+
* consumers are the rest of MessageQueueSignal.
|
|
346
|
+
*/
|
|
347
|
+
export const MESSAGE_QUEUE_THRESHOLDABLE_SIGNALS: ReadonlyArray<MessageQueueSignal> =
|
|
348
|
+
[
|
|
349
|
+
"backlog",
|
|
350
|
+
"consumerLag",
|
|
351
|
+
"oldestMessageAge",
|
|
352
|
+
"deadLetter",
|
|
353
|
+
"errors",
|
|
354
|
+
"throttled",
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
/*
|
|
358
|
+
* "level": a value at a point in time (a queue's depth, a lag, an age).
|
|
359
|
+
* "count": how many events a minute or a CloudWatch period saw — the catalog
|
|
360
|
+
* marks those with aggregation Sum, because each point already is the count
|
|
361
|
+
* for its period and is summed within a bucket.
|
|
362
|
+
*/
|
|
363
|
+
export type MessageQueueAlertMeasure = "level" | "count";
|
|
364
|
+
|
|
365
|
+
export function getMessageQueueAlertMeasure(
|
|
366
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
367
|
+
): MessageQueueAlertMeasure {
|
|
368
|
+
return descriptor.aggregation === MetricsAggregationType.Sum
|
|
369
|
+
? "count"
|
|
370
|
+
: "level";
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/*
|
|
374
|
+
* The default alert for one (signal, measure) pair. `threshold` is written in
|
|
375
|
+
* `thresholdUnit` when it is a time (converted to the metric's own unit when
|
|
376
|
+
* the template is built), and otherwise in the metric's own count unit.
|
|
377
|
+
*/
|
|
378
|
+
export interface MessageQueueAlertPolicyDefault {
|
|
379
|
+
signal: MessageQueueSignal;
|
|
380
|
+
measure: MessageQueueAlertMeasure;
|
|
381
|
+
threshold: number;
|
|
382
|
+
thresholdUnit: string | null;
|
|
383
|
+
evaluation: EvaluateOverTimeType;
|
|
384
|
+
rollingTime: RollingTime;
|
|
385
|
+
severity: MessageQueueAlertTemplateSeverity;
|
|
386
|
+
category: MessageQueueAlertTemplateCategory;
|
|
387
|
+
// Appended to the catalog title to name the template ("Queue depth High").
|
|
388
|
+
nameSuffix: string;
|
|
389
|
+
// The query's alias; one query per template, so it never collides.
|
|
390
|
+
metricAlias: string;
|
|
391
|
+
// What the breach means and how to retune it, as full sentences.
|
|
392
|
+
meaning: string;
|
|
393
|
+
// What on-call checks first, as full sentences.
|
|
394
|
+
incidentDescription: string;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export const MESSAGE_QUEUE_ALERT_POLICIES: ReadonlyArray<MessageQueueAlertPolicyDefault> =
|
|
398
|
+
[
|
|
399
|
+
/*
|
|
400
|
+
* A thousand messages held for ten minutes. A work queue that keeps up
|
|
401
|
+
* drains to near empty between bursts, so a thousand waiting messages
|
|
402
|
+
* that do not go away within ten minutes means the queue fills faster
|
|
403
|
+
* than it drains, or nothing drains it; a burst that drains inside the
|
|
404
|
+
* window never fires. For the brokers that track each message
|
|
405
|
+
* (RabbitMQ, ActiveMQ, SQS, Service Bus, Pub/Sub subscriptions) and the
|
|
406
|
+
* ready count of RocketMQ and Pulsar's topic backlog.
|
|
407
|
+
*/
|
|
408
|
+
{
|
|
409
|
+
signal: "backlog",
|
|
410
|
+
measure: "level",
|
|
411
|
+
threshold: 1000,
|
|
412
|
+
thresholdUnit: null,
|
|
413
|
+
evaluation: EvaluateOverTimeType.AllValues,
|
|
414
|
+
rollingTime: RollingTime.Past10Minutes,
|
|
415
|
+
severity: "Warning",
|
|
416
|
+
category: "Backlog",
|
|
417
|
+
nameSuffix: "High",
|
|
418
|
+
metricAlias: "queue_backlog",
|
|
419
|
+
meaning:
|
|
420
|
+
"Messages are arriving faster than consumers take them, or nothing is consuming them. A healthy queue drains to near empty between bursts, so retune to a few times this queue's normal peak.",
|
|
421
|
+
incidentDescription:
|
|
422
|
+
"Messages are piling up in the queue. Check that its consumers are running and connected, whether they are failing and returning messages to the queue, and whether they need more instances to keep up with the publish rate.",
|
|
423
|
+
},
|
|
424
|
+
/*
|
|
425
|
+
* Ten thousand messages held for ten minutes — ten times the backlog
|
|
426
|
+
* default, because a consumer group's lag is measured against the offset
|
|
427
|
+
* it last COMMITTED: a healthy Kafka consumer commits every few seconds
|
|
428
|
+
* (five by default), so on a topic taking a few thousand messages a
|
|
429
|
+
* second it trails by thousands between commits, all the time. The same
|
|
430
|
+
* default serves Pulsar's subscription backlog and RocketMQ's lag, the
|
|
431
|
+
* other log-based brokers.
|
|
432
|
+
*/
|
|
433
|
+
{
|
|
434
|
+
signal: "consumerLag",
|
|
435
|
+
measure: "level",
|
|
436
|
+
threshold: 10000,
|
|
437
|
+
thresholdUnit: null,
|
|
438
|
+
evaluation: EvaluateOverTimeType.AllValues,
|
|
439
|
+
rollingTime: RollingTime.Past10Minutes,
|
|
440
|
+
severity: "Warning",
|
|
441
|
+
category: "Consumer Lag",
|
|
442
|
+
nameSuffix: "High",
|
|
443
|
+
metricAlias: "consumer_lag",
|
|
444
|
+
meaning:
|
|
445
|
+
"Consumers are falling behind what is produced. A healthy consumer group that commits its offsets every few seconds already trails a busy topic by thousands of messages, so retune to a few times this topic's normal lag.",
|
|
446
|
+
incidentDescription:
|
|
447
|
+
"The consumer group is falling behind its producers. Check that every consumer is running and not stuck rebalancing, whether one partition is stuck on a message its consumer cannot process, and whether the group needs more consumers (up to one per partition).",
|
|
448
|
+
},
|
|
449
|
+
/*
|
|
450
|
+
* Ten minutes of waiting, held for five. A consumer that keeps up takes a
|
|
451
|
+
* message within seconds, so a message ten minutes old means consumers
|
|
452
|
+
* are stopped or far behind, or keep failing on one message that is
|
|
453
|
+
* received and returned until its dead-letter policy moves it aside. An
|
|
454
|
+
* age already is a duration, so a shorter sustain (five minutes) is
|
|
455
|
+
* enough to ignore one late sample.
|
|
456
|
+
*/
|
|
457
|
+
{
|
|
458
|
+
signal: "oldestMessageAge",
|
|
459
|
+
measure: "level",
|
|
460
|
+
threshold: 600,
|
|
461
|
+
thresholdUnit: "s",
|
|
462
|
+
evaluation: EvaluateOverTimeType.AllValues,
|
|
463
|
+
rollingTime: RollingTime.Past5Minutes,
|
|
464
|
+
severity: "Warning",
|
|
465
|
+
category: "Message Age",
|
|
466
|
+
nameSuffix: "High",
|
|
467
|
+
metricAlias: "oldest_message_age",
|
|
468
|
+
meaning:
|
|
469
|
+
"Messages are waiting far longer than a working consumer takes: consumers are stopped or far behind, or keep failing on a message that is received and returned again and again. Retune to how long a message may wait before it is late.",
|
|
470
|
+
incidentDescription:
|
|
471
|
+
"The oldest message has waited longer than a working consumer takes. Check that consumers are running; a message that fails every time it is processed is received and returned until the dead-letter policy moves it aside, so look at its receive count.",
|
|
472
|
+
},
|
|
473
|
+
/*
|
|
474
|
+
* Any dead-lettered message, held for five minutes: each one failed
|
|
475
|
+
* every delivery attempt and waits for a person. It stays open until
|
|
476
|
+
* the dead-letter queue is drained, which is the point.
|
|
477
|
+
*/
|
|
478
|
+
{
|
|
479
|
+
signal: "deadLetter",
|
|
480
|
+
measure: "level",
|
|
481
|
+
threshold: 1,
|
|
482
|
+
thresholdUnit: null,
|
|
483
|
+
evaluation: EvaluateOverTimeType.AllValues,
|
|
484
|
+
rollingTime: RollingTime.Past5Minutes,
|
|
485
|
+
severity: "Warning",
|
|
486
|
+
category: "Dead Letters",
|
|
487
|
+
nameSuffix: "",
|
|
488
|
+
metricAlias: "dead_letters",
|
|
489
|
+
meaning:
|
|
490
|
+
"Messages that failed every delivery attempt are waiting in the dead-letter queue for someone to inspect them. Raise the threshold if the dead-letter queue is drained on a schedule.",
|
|
491
|
+
incidentDescription:
|
|
492
|
+
"Messages failed every delivery attempt and were moved to the dead-letter queue. Inspect them to find the failing handler or the malformed payload, fix the cause, then resubmit or discard them.",
|
|
493
|
+
},
|
|
494
|
+
// Any message dead-lettered in any one minute.
|
|
495
|
+
{
|
|
496
|
+
signal: "deadLetter",
|
|
497
|
+
measure: "count",
|
|
498
|
+
threshold: 1,
|
|
499
|
+
thresholdUnit: null,
|
|
500
|
+
evaluation: EvaluateOverTimeType.AnyValue,
|
|
501
|
+
rollingTime: RollingTime.Past5Minutes,
|
|
502
|
+
severity: "Warning",
|
|
503
|
+
category: "Dead Letters",
|
|
504
|
+
nameSuffix: "",
|
|
505
|
+
metricAlias: "dead_letters",
|
|
506
|
+
meaning:
|
|
507
|
+
"Messages are being moved to a dead-letter queue after failing every delivery attempt.",
|
|
508
|
+
incidentDescription:
|
|
509
|
+
"Messages failed every delivery attempt and were moved to a dead-letter queue. Inspect them to find the failing handler or the malformed payload, fix the cause, then resubmit or discard them.",
|
|
510
|
+
},
|
|
511
|
+
/*
|
|
512
|
+
* Ten in any one minute. Clients retry a failed request and brokers
|
|
513
|
+
* redeliver a failed message, so one failure goes unnoticed; ten in a
|
|
514
|
+
* minute is a pattern, not a blip.
|
|
515
|
+
*/
|
|
516
|
+
{
|
|
517
|
+
signal: "errors",
|
|
518
|
+
measure: "count",
|
|
519
|
+
threshold: 10,
|
|
520
|
+
thresholdUnit: null,
|
|
521
|
+
evaluation: EvaluateOverTimeType.AnyValue,
|
|
522
|
+
rollingTime: RollingTime.Past5Minutes,
|
|
523
|
+
severity: "Warning",
|
|
524
|
+
category: "Errors",
|
|
525
|
+
nameSuffix: "",
|
|
526
|
+
metricAlias: "failures",
|
|
527
|
+
meaning:
|
|
528
|
+
"Failures are recurring rather than occasional: clients retry a failed request and brokers redeliver a failed message, so a single failure goes unnoticed, while a burst like this is a pattern, not a blip. Retune to this queue's volume.",
|
|
529
|
+
incidentDescription:
|
|
530
|
+
"Operations on the queue are failing. The broker's own diagnostics name the operation and the error: failures the broker causes point at the service itself (check its health status), while failures its clients cause usually follow a deployment, a credential or permission change, a consumer that crashes on a message, or an entity that was deleted or disabled.",
|
|
531
|
+
},
|
|
532
|
+
/*
|
|
533
|
+
* Ten in any one minute: a throttled client backs off before it retries,
|
|
534
|
+
* so a few throttled requests at a peak are absorbed, while ten a minute
|
|
535
|
+
* means the namespace is out of capacity.
|
|
536
|
+
*/
|
|
537
|
+
{
|
|
538
|
+
signal: "throttled",
|
|
539
|
+
measure: "count",
|
|
540
|
+
threshold: 10,
|
|
541
|
+
thresholdUnit: null,
|
|
542
|
+
evaluation: EvaluateOverTimeType.AnyValue,
|
|
543
|
+
rollingTime: RollingTime.Past5Minutes,
|
|
544
|
+
severity: "Warning",
|
|
545
|
+
category: "Throttling",
|
|
546
|
+
nameSuffix: "",
|
|
547
|
+
metricAlias: "throttled_requests",
|
|
548
|
+
meaning:
|
|
549
|
+
"The broker is refusing requests because the namespace is out of capacity, and every throttled client backs off before it retries, which slows everything on the namespace. If it persists, add capacity (Service Bus Premium messaging units, Event Hubs throughput or processing units).",
|
|
550
|
+
incidentDescription:
|
|
551
|
+
"The broker is throttling requests because the namespace is out of capacity. Look for a traffic spike or a new heavy client; if throttling persists, scale the namespace up.",
|
|
552
|
+
},
|
|
553
|
+
];
|
|
554
|
+
|
|
555
|
+
/*
|
|
556
|
+
* Catalog entries whose meaning differs from their signal's, and so from its
|
|
557
|
+
* default. Keyed by system and by every stored name the metric has (both
|
|
558
|
+
* CloudWatch shapes of one metric are one override).
|
|
559
|
+
*/
|
|
560
|
+
export interface MessageQueueAlertPolicyOverride {
|
|
561
|
+
system: string;
|
|
562
|
+
metricNames: ReadonlyArray<string>;
|
|
563
|
+
threshold?: number | undefined;
|
|
564
|
+
rollingTime?: RollingTime | undefined;
|
|
565
|
+
severity?: MessageQueueAlertTemplateSeverity | undefined;
|
|
566
|
+
// Replaces the generated name, before any "(JSON stream)" qualifier.
|
|
567
|
+
name?: string | undefined;
|
|
568
|
+
recoverOnNoData?: boolean | undefined;
|
|
569
|
+
meaning?: string | undefined;
|
|
570
|
+
incidentDescription?: string | undefined;
|
|
571
|
+
// Why the override exists, for the reader and the tests.
|
|
572
|
+
reason: string;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export const MESSAGE_QUEUE_ALERT_POLICY_OVERRIDES: ReadonlyArray<MessageQueueAlertPolicyOverride> =
|
|
576
|
+
[
|
|
577
|
+
{
|
|
578
|
+
system: "aws_sqs",
|
|
579
|
+
metricNames: [
|
|
580
|
+
"amazonaws.com/aws/sqs/approximatenumberofmessagesnotvisible",
|
|
581
|
+
"approximatenumberofmessagesnotvisible",
|
|
582
|
+
],
|
|
583
|
+
/*
|
|
584
|
+
* 90% of the in-flight quota of a standard queue (about 120,000
|
|
585
|
+
* messages received but not yet deleted): early enough to act before
|
|
586
|
+
* receives start failing, and far above what a busy queue that is
|
|
587
|
+
* draining keeps in flight.
|
|
588
|
+
*/
|
|
589
|
+
threshold: 108000,
|
|
590
|
+
/*
|
|
591
|
+
* Critical, and held for five minutes rather than a backlog's ten: the
|
|
592
|
+
* last 10% of the quota is what stands between the queue and receives
|
|
593
|
+
* that fail.
|
|
594
|
+
*/
|
|
595
|
+
rollingTime: RollingTime.Past5Minutes,
|
|
596
|
+
severity: "Critical",
|
|
597
|
+
name: "Messages In Flight Near Quota",
|
|
598
|
+
meaning:
|
|
599
|
+
"A standard queue keeps about 120,000 messages in flight (received but not yet deleted) at most; past that, short-polling receives fail with OverLimit and long polls return nothing, so every consumer stalls. This fires at 90% of that quota; for a FIFO queue, retune to 90% of its own quota.",
|
|
600
|
+
incidentDescription:
|
|
601
|
+
"Close to 120,000 messages are received but not yet deleted, so receives are about to fail. Check that consumers delete messages once they have processed them, and whether a long visibility timeout keeps failed messages in flight.",
|
|
602
|
+
reason:
|
|
603
|
+
"Messages in flight are not a backlog: a busy queue keeps thousands in flight while it drains, and the one level that matters is the queue's in-flight quota.",
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
system: "aws_sqs",
|
|
607
|
+
metricNames: [
|
|
608
|
+
"amazonaws.com/aws/sqs/approximateageofoldestmessage",
|
|
609
|
+
"approximateageofoldestmessage",
|
|
610
|
+
],
|
|
611
|
+
recoverOnNoData: true,
|
|
612
|
+
reason:
|
|
613
|
+
"CloudWatch reports this metric only while the queue holds a message, so a silent window is an empty queue: recovery reads it as an age of zero.",
|
|
614
|
+
},
|
|
615
|
+
];
|
|
616
|
+
|
|
617
|
+
/*
|
|
618
|
+
* Catalog gauges with a thresholdable signal that deliberately get no
|
|
619
|
+
* template. The queue page still charts them, and its "Create monitor" link
|
|
620
|
+
* still builds a monitor on them for a team that wants one.
|
|
621
|
+
*/
|
|
622
|
+
export const UNTEMPLATED_MESSAGE_QUEUE_GAUGES: ReadonlyArray<{
|
|
623
|
+
system: string;
|
|
624
|
+
metricName: string;
|
|
625
|
+
reason: string;
|
|
626
|
+
}> = [
|
|
627
|
+
{
|
|
628
|
+
system: "kafka",
|
|
629
|
+
metricName: "kafka.consumer_group.lag",
|
|
630
|
+
reason:
|
|
631
|
+
"Every partition's lag is part of its consumer group's lag, so at Consumer lag's threshold it could only fire after that template has, and no fixed lower threshold tells one stuck partition from a busy one: a healthy group that commits every few seconds trails each busy partition by thousands of messages.",
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
system: "activemq",
|
|
635
|
+
metricName: "activemq.message.enqueue.average_duration",
|
|
636
|
+
reason:
|
|
637
|
+
"It is a running average the broker keeps from its start (AverageEnqueueTime), so after one stall it stays high long after the destination has drained, and on a destination with a long history a new stall barely moves it: a threshold on it fires late and does not clear. Queue size catches a stall directly.",
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
system: "activemq",
|
|
641
|
+
metricName: "activemq.message.wait_time.avg",
|
|
642
|
+
reason:
|
|
643
|
+
"It is the same running average the broker keeps from its start (AverageEnqueueTime), under the JMX Scraper's legacy name, so a threshold on it fires late and does not clear. Queue size (legacy names) catches a stall directly.",
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
system: "pulsar",
|
|
647
|
+
metricName: "pulsar_storage_backlog_age_seconds",
|
|
648
|
+
reason:
|
|
649
|
+
"It read -1 (unknown) on topics that held a backlog in a live capture from Pulsar 4.2.4 on its default configuration, so a threshold on it would watch nothing there. Subscription backlog catches a subscription that stopped acknowledging.",
|
|
650
|
+
},
|
|
651
|
+
];
|
|
652
|
+
|
|
653
|
+
/*
|
|
654
|
+
* What each counter would be tempting to alert on, by signal: the catalog
|
|
655
|
+
* marks every cumulative total `kind: "counter"`, and the monitor path has
|
|
656
|
+
* no rate to threshold it with.
|
|
657
|
+
*/
|
|
658
|
+
const COUNTER_TEMPTATION_BY_SIGNAL: Readonly<
|
|
659
|
+
Record<MessageQueueSignal, string>
|
|
660
|
+
> = {
|
|
661
|
+
published: "publishing stopping or spiking",
|
|
662
|
+
consumed: "consumers stopping",
|
|
663
|
+
errors: "a rise in failed or expired messages",
|
|
664
|
+
deadLetter: "messages being dead-lettered",
|
|
665
|
+
backlog: "a growing backlog",
|
|
666
|
+
consumerLag: "a growing lag",
|
|
667
|
+
oldestMessageAge: "messages getting old",
|
|
668
|
+
throttled: "throttling",
|
|
669
|
+
consumers: "consumers leaving",
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
/*
|
|
673
|
+
* Every catalog counter, which no template reads, and what a team would
|
|
674
|
+
* reach for it for. The honest workaround for a team that needs one alerted
|
|
675
|
+
* today is a `cumulative_to_delta` processor in its collector pipeline
|
|
676
|
+
* (`cumulativetodelta`, its old name, is a deprecated alias in
|
|
677
|
+
* collector-contrib 0.161.0) converting a COPY of the counter, made under a
|
|
678
|
+
* name of its own, into per-scrape deltas that a Sum over the window
|
|
679
|
+
* thresholds correctly; `initial_value: drop` keeps back the copy's first
|
|
680
|
+
* point after a collector start, the counter's whole running total. The
|
|
681
|
+
* config is at /docs/telemetry/queues#alerting. Never convert the counter
|
|
682
|
+
* itself: the queue page reads each counter as a running total and charts
|
|
683
|
+
* how fast it grows, so a counter converted in place charts about 0/s for
|
|
684
|
+
* steady traffic. The processor only converts sums, so it leaves Kafka's
|
|
685
|
+
* offsets and Pulsar's totals, which reach the collector as gauges, as they
|
|
686
|
+
* are.
|
|
687
|
+
*/
|
|
688
|
+
export const UNALERTABLE_MESSAGE_QUEUE_COUNTERS: ReadonlyArray<{
|
|
689
|
+
system: string;
|
|
690
|
+
metricName: string;
|
|
691
|
+
wouldAlertOn: string;
|
|
692
|
+
}> = MESSAGE_QUEUE_METRICS.filter(
|
|
693
|
+
(descriptor: MessageQueueMetricDescriptor): boolean => {
|
|
694
|
+
return descriptor.kind === "counter";
|
|
695
|
+
},
|
|
696
|
+
).map(
|
|
697
|
+
(
|
|
698
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
699
|
+
): { system: string; metricName: string; wouldAlertOn: string } => {
|
|
700
|
+
return {
|
|
701
|
+
system: descriptor.system,
|
|
702
|
+
metricName: descriptor.metricName,
|
|
703
|
+
wouldAlertOn: COUNTER_TEMPTATION_BY_SIGNAL[descriptor.signal],
|
|
704
|
+
};
|
|
705
|
+
},
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
/*
|
|
709
|
+
* The shortest window a monitor on a late source may read (see WINDOWS AND
|
|
710
|
+
* LATE SOURCES in the header), keyed by the catalog entry's `receiver`: long
|
|
711
|
+
* enough that, under the documented collector configuration AND the
|
|
712
|
+
* receiver's own defaults, no evaluation finds the window empty and every
|
|
713
|
+
* point is read by at least one evaluation. A source not listed delivers
|
|
714
|
+
* within seconds and keeps its signal's window.
|
|
715
|
+
*/
|
|
716
|
+
export interface MessageQueueSourceWindowFloor {
|
|
717
|
+
// The catalog `receiver` whose entries it applies to.
|
|
718
|
+
receiver: string;
|
|
719
|
+
minimumRollingTime: RollingTime;
|
|
720
|
+
// How late the source's points arrive, as the template description says it.
|
|
721
|
+
reason: string;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
export const MESSAGE_QUEUE_SOURCE_WINDOW_FLOORS: ReadonlyArray<MessageQueueSourceWindowFloor> =
|
|
725
|
+
[
|
|
726
|
+
{
|
|
727
|
+
receiver: "aws_cloudwatch",
|
|
728
|
+
/*
|
|
729
|
+
* The newest point is up to delay + 2 × period + collection_interval
|
|
730
|
+
* old: 17 minutes under the documented configuration, 25 under the
|
|
731
|
+
* receiver's defaults. Thirty is the shortest RollingTime above both.
|
|
732
|
+
*/
|
|
733
|
+
minimumRollingTime: RollingTime.Past30Minutes,
|
|
734
|
+
reason:
|
|
735
|
+
"The collector's `aws_cloudwatch` receiver fetches CloudWatch's periods `delay` (ten minutes) after they end, `collection_interval` (five minutes) at a time, so its points arrive 11 to 17 minutes late, and up to 25 minutes late with the receiver's default five-minute `period`: the window is thirty minutes so that it always holds some.",
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
receiver: "googlecloudmonitoring",
|
|
739
|
+
/*
|
|
740
|
+
* A sample arrives up to ingestDelay + collection_interval late: nine
|
|
741
|
+
* minutes for dead_letter_message_count (240 seconds) with the
|
|
742
|
+
* receiver's default five-minute interval, and the next one-minute
|
|
743
|
+
* evaluation reads it up to ten minutes after it was taken — the very
|
|
744
|
+
* edge of a Past10Minutes window, which any export delay tips over.
|
|
745
|
+
* Fifteen leaves a margin.
|
|
746
|
+
*/
|
|
747
|
+
minimumRollingTime: RollingTime.Past15Minutes,
|
|
748
|
+
reason:
|
|
749
|
+
"Cloud Monitoring makes a Pub/Sub sample visible two to four minutes after it is taken, and the collector's `googlecloudmonitoring` receiver fetches what has become visible once per `collection_interval`, so a point arrives up to nine minutes late with the receiver's default five-minute interval: the window is fifteen minutes so that it always holds some and reads every point.",
|
|
750
|
+
},
|
|
751
|
+
];
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* The window floor of a catalog entry's source, or null when its points
|
|
755
|
+
* arrive live.
|
|
756
|
+
*/
|
|
757
|
+
export function getMessageQueueSourceWindowFloor(
|
|
758
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
759
|
+
): MessageQueueSourceWindowFloor | null {
|
|
760
|
+
if (!descriptor) {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
for (const floor of MESSAGE_QUEUE_SOURCE_WINDOW_FLOORS) {
|
|
765
|
+
if (floor.receiver === descriptor.receiver) {
|
|
766
|
+
return floor;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function getRollingTimeSeconds(rollingTime: RollingTime): number {
|
|
774
|
+
const window: InBetween<Date> =
|
|
775
|
+
RollingTimeUtil.convertToStartAndEndDate(rollingTime);
|
|
776
|
+
|
|
777
|
+
return (window.endValue.getTime() - window.startValue.getTime()) / 1000;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* The window a monitor on this catalog entry reads: `preferred`, widened to
|
|
782
|
+
* the entry's source floor when that is longer. The templates read their
|
|
783
|
+
* signal's window through it, and the "Create monitor" link its default.
|
|
784
|
+
*/
|
|
785
|
+
export function getMessageQueueMetricMonitorRollingTime(
|
|
786
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
787
|
+
preferred: RollingTime,
|
|
788
|
+
): RollingTime {
|
|
789
|
+
const floor: MessageQueueSourceWindowFloor | null =
|
|
790
|
+
getMessageQueueSourceWindowFloor(descriptor);
|
|
791
|
+
|
|
792
|
+
if (
|
|
793
|
+
floor &&
|
|
794
|
+
getRollingTimeSeconds(floor.minimumRollingTime) >
|
|
795
|
+
getRollingTimeSeconds(preferred)
|
|
796
|
+
) {
|
|
797
|
+
return floor.minimumRollingTime;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return preferred;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/*
|
|
804
|
+
* Catalog entries whose series are the parts of one total (seriesCombine
|
|
805
|
+
* "sum" over a level) AND a closed set the source reports together, on
|
|
806
|
+
* every scrape, under one timestamp — so each bucket holds every part, and
|
|
807
|
+
* a formula adding one query per value reads exactly the total the queue
|
|
808
|
+
* page shows. Entries whose series are an open set (ActiveMQ's brokers, a
|
|
809
|
+
* Pulsar topic's partitions) are left out on purpose: a formula over the
|
|
810
|
+
* series seen today leaves out a broker or partition added tomorrow, so
|
|
811
|
+
* their monitors alert per series instead (see AGGREGATION CONTRACT).
|
|
812
|
+
*/
|
|
813
|
+
export interface MessageQueueSeriesTotalDefinition {
|
|
814
|
+
system: string;
|
|
815
|
+
metricName: string;
|
|
816
|
+
// The entry's one series key.
|
|
817
|
+
seriesKey: string;
|
|
818
|
+
// Every value the source gives it, as stored, in the order they are added.
|
|
819
|
+
values: ReadonlyArray<string>;
|
|
820
|
+
// Why the set is closed and complete in every scrape.
|
|
821
|
+
reason: string;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
export const MESSAGE_QUEUE_SERIES_TOTALS: ReadonlyArray<MessageQueueSeriesTotalDefinition> =
|
|
825
|
+
[
|
|
826
|
+
{
|
|
827
|
+
system: "rabbitmq",
|
|
828
|
+
metricName: "rabbitmq.message.current",
|
|
829
|
+
seriesKey: "state",
|
|
830
|
+
values: ["ready", "unacknowledged"],
|
|
831
|
+
reason:
|
|
832
|
+
"The rabbitmq receiver records both states of every queue on every scrape, under the scrape's one timestamp (collectQueue in receiver/rabbitmqreceiver scraper.go@v0.161.0), so the average of each state over a bucket, added together, is the queue's average depth.",
|
|
833
|
+
},
|
|
834
|
+
];
|
|
835
|
+
|
|
836
|
+
function getSeriesTotalDefinition(
|
|
837
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
838
|
+
): MessageQueueSeriesTotalDefinition | null {
|
|
839
|
+
const seriesKeys: ReadonlyArray<string> = descriptor.seriesKeys || [];
|
|
840
|
+
|
|
841
|
+
for (const definition of MESSAGE_QUEUE_SERIES_TOTALS) {
|
|
842
|
+
if (
|
|
843
|
+
definition.system === descriptor.system &&
|
|
844
|
+
definition.metricName === descriptor.metricName &&
|
|
845
|
+
seriesKeys.length === 1 &&
|
|
846
|
+
seriesKeys[0] === definition.seriesKey
|
|
847
|
+
) {
|
|
848
|
+
return definition;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// --- the shared query builder (templates and the "Create monitor" link) ---
|
|
856
|
+
|
|
857
|
+
/*
|
|
858
|
+
* Whether a monitor can evaluate this catalog entry at all: a gauge, whose
|
|
859
|
+
* series are all part of the queue. A counter only grows (see the header),
|
|
860
|
+
* and an entry with `excludeSeriesWithAttributes` needs "this attribute is
|
|
861
|
+
* absent" to leave out its finer-grained repeats, which an equality filter
|
|
862
|
+
* cannot say — both are refused rather than built into a monitor that
|
|
863
|
+
* compares the wrong number.
|
|
864
|
+
*/
|
|
865
|
+
export function isMessageQueueMetricMonitorable(
|
|
866
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
867
|
+
): boolean {
|
|
868
|
+
if (!descriptor || descriptor.kind !== "gauge") {
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
return !(
|
|
873
|
+
descriptor.excludeSeriesWithAttributes &&
|
|
874
|
+
descriptor.excludeSeriesWithAttributes.length > 0
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/*
|
|
879
|
+
* How a monitor reads a catalog entry: each query's aggregation, the keys it
|
|
880
|
+
* alerts per (empty: one number for the queue), the series it adds up with a
|
|
881
|
+
* formula, the shortest window its source allows, and — when that is not
|
|
882
|
+
* exactly the number the queue page shows — one sentence saying what it
|
|
883
|
+
* measures instead.
|
|
884
|
+
*/
|
|
885
|
+
export interface MessageQueueMetricMonitorSeed {
|
|
886
|
+
aggregationType: MetricsAggregationType;
|
|
887
|
+
groupByAttributeKeys: Array<string>;
|
|
888
|
+
// Set when the monitor adds a closed set of series up with a formula.
|
|
889
|
+
seriesTotal: MessageQueueSeriesTotal | null;
|
|
890
|
+
// The source's window floor (MESSAGE_QUEUE_SOURCE_WINDOW_FLOORS), or null.
|
|
891
|
+
minimumRollingTime: RollingTime | null;
|
|
892
|
+
note: string | null;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function aggregationLabel(aggregationType: MetricsAggregationType): string {
|
|
896
|
+
return aggregationType === MetricsAggregationType.Avg
|
|
897
|
+
? "Average"
|
|
898
|
+
: String(aggregationType);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* The query shape that evaluates what the queue page shows for this entry
|
|
903
|
+
* (see AGGREGATION CONTRACT in the header), or null for an entry no monitor
|
|
904
|
+
* can evaluate (isMessageQueueMetricMonitorable).
|
|
905
|
+
*/
|
|
906
|
+
export function getMessageQueueMetricMonitorSeed(
|
|
907
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
908
|
+
): MessageQueueMetricMonitorSeed | null {
|
|
909
|
+
if (!isMessageQueueMetricMonitorable(descriptor)) {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const aggregation: MetricsAggregationType = descriptor.aggregation;
|
|
914
|
+
const seriesKeys: Array<string> = [...(descriptor.seriesKeys || [])];
|
|
915
|
+
const floor: MessageQueueSourceWindowFloor | null =
|
|
916
|
+
getMessageQueueSourceWindowFloor(descriptor);
|
|
917
|
+
const shape: {
|
|
918
|
+
aggregationType: MetricsAggregationType;
|
|
919
|
+
groupByAttributeKeys: Array<string>;
|
|
920
|
+
seriesTotal: MessageQueueSeriesTotal | null;
|
|
921
|
+
note: string | null;
|
|
922
|
+
} = getMonitorShape({
|
|
923
|
+
descriptor: descriptor,
|
|
924
|
+
aggregation: aggregation,
|
|
925
|
+
seriesKeys: seriesKeys,
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
return {
|
|
929
|
+
...shape,
|
|
930
|
+
minimumRollingTime: floor ? floor.minimumRollingTime : null,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function getMonitorShape(data: {
|
|
935
|
+
descriptor: MessageQueueMetricDescriptor;
|
|
936
|
+
aggregation: MetricsAggregationType;
|
|
937
|
+
seriesKeys: Array<string>;
|
|
938
|
+
}): {
|
|
939
|
+
aggregationType: MetricsAggregationType;
|
|
940
|
+
groupByAttributeKeys: Array<string>;
|
|
941
|
+
seriesTotal: MessageQueueSeriesTotal | null;
|
|
942
|
+
note: string | null;
|
|
943
|
+
} {
|
|
944
|
+
const aggregation: MetricsAggregationType = data.aggregation;
|
|
945
|
+
const seriesKeys: Array<string> = data.seriesKeys;
|
|
946
|
+
|
|
947
|
+
switch (data.descriptor.seriesCombine) {
|
|
948
|
+
case "max":
|
|
949
|
+
if (aggregation === MetricsAggregationType.Sum) {
|
|
950
|
+
/*
|
|
951
|
+
* A per-period count: the Sum of a bucket is one series' count for
|
|
952
|
+
* its period, never Max, which on a CloudWatch Summary reads the
|
|
953
|
+
* period's Average (see AGGREGATION CONTRACT). With series keys, the
|
|
954
|
+
* worst series crossing is the highest one crossing.
|
|
955
|
+
*/
|
|
956
|
+
return {
|
|
957
|
+
aggregationType: MetricsAggregationType.Sum,
|
|
958
|
+
groupByAttributeKeys: seriesKeys,
|
|
959
|
+
seriesTotal: null,
|
|
960
|
+
note:
|
|
961
|
+
seriesKeys.length > 0
|
|
962
|
+
? `The chart shows the highest of this metric's series (one per ${seriesKeys.join(
|
|
963
|
+
" / ",
|
|
964
|
+
)}). A monitor would add them up, so this one alerts on each series separately, which fires exactly when the highest one crosses the threshold.`
|
|
965
|
+
: null,
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
return {
|
|
970
|
+
aggregationType: MetricsAggregationType.Max,
|
|
971
|
+
groupByAttributeKeys: [],
|
|
972
|
+
seriesTotal: null,
|
|
973
|
+
note:
|
|
974
|
+
aggregation === MetricsAggregationType.Max
|
|
975
|
+
? null
|
|
976
|
+
: `The chart shows the highest of this metric's series. A monitor folds every series and sample into one number, so this one takes their Max instead of each series' ${aggregationLabel(
|
|
977
|
+
aggregation,
|
|
978
|
+
)}.`,
|
|
979
|
+
};
|
|
980
|
+
case "avg":
|
|
981
|
+
return {
|
|
982
|
+
aggregationType: aggregation,
|
|
983
|
+
groupByAttributeKeys: [],
|
|
984
|
+
seriesTotal: null,
|
|
985
|
+
note: null,
|
|
986
|
+
};
|
|
987
|
+
default: {
|
|
988
|
+
// "sum".
|
|
989
|
+
if (aggregation === MetricsAggregationType.Sum) {
|
|
990
|
+
return {
|
|
991
|
+
aggregationType: MetricsAggregationType.Sum,
|
|
992
|
+
groupByAttributeKeys: [],
|
|
993
|
+
seriesTotal: null,
|
|
994
|
+
note: null,
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
const total: MessageQueueSeriesTotalDefinition | null =
|
|
999
|
+
getSeriesTotalDefinition(data.descriptor);
|
|
1000
|
+
|
|
1001
|
+
if (total) {
|
|
1002
|
+
// The page's own total: one query per part, added up by a formula.
|
|
1003
|
+
return {
|
|
1004
|
+
aggregationType: aggregation,
|
|
1005
|
+
groupByAttributeKeys: [],
|
|
1006
|
+
seriesTotal: { key: total.seriesKey, values: [...total.values] },
|
|
1007
|
+
note: null,
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
return {
|
|
1012
|
+
aggregationType: aggregation,
|
|
1013
|
+
groupByAttributeKeys: seriesKeys,
|
|
1014
|
+
seriesTotal: null,
|
|
1015
|
+
note:
|
|
1016
|
+
seriesKeys.length > 0
|
|
1017
|
+
? `The chart adds this metric's series up (one per ${seriesKeys.join(
|
|
1018
|
+
" / ",
|
|
1019
|
+
)}). They are not a fixed set, so a monitor cannot add them together the way the chart does: this one alerts on each series separately. Set a threshold for one series, not for the total.`
|
|
1020
|
+
: null,
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Every stored key a monitor filter on this entry can pin: its destination,
|
|
1028
|
+
* broker-scope and required attributes. The attributes a series of the
|
|
1029
|
+
* metric has to be read with to build its filter.
|
|
1030
|
+
*/
|
|
1031
|
+
export function getMessageQueueMetricFilterAttributeKeys(
|
|
1032
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
1033
|
+
): Array<string> {
|
|
1034
|
+
const keys: Array<string> = [];
|
|
1035
|
+
|
|
1036
|
+
for (const key of [
|
|
1037
|
+
...descriptor.destinationAttributes,
|
|
1038
|
+
...(descriptor.scopeAttributes || []),
|
|
1039
|
+
...Object.keys(descriptor.requiredAttributes || {}),
|
|
1040
|
+
]) {
|
|
1041
|
+
if (!keys.includes(key)) {
|
|
1042
|
+
keys.push(key);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
return keys;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/*
|
|
1050
|
+
* A metric monitor query on one queue: the metric, the exact stored values
|
|
1051
|
+
* that scope it to the queue, and how it folds and groups.
|
|
1052
|
+
*/
|
|
1053
|
+
export interface MessageQueueMetricMonitorQuery {
|
|
1054
|
+
// getMessageQueueMetricId of the entry the query reads.
|
|
1055
|
+
metricId: string;
|
|
1056
|
+
metricName: string;
|
|
1057
|
+
/*
|
|
1058
|
+
* Stored key → the value (an equality filter) or values (an Includes) the
|
|
1059
|
+
* observed series carry, in the order: destination, broker scope, required
|
|
1060
|
+
* attributes.
|
|
1061
|
+
*/
|
|
1062
|
+
attributes: Dictionary<string | Includes>;
|
|
1063
|
+
aggregationType: MetricsAggregationType;
|
|
1064
|
+
groupByAttributeKeys: Array<string>;
|
|
1065
|
+
/*
|
|
1066
|
+
* The series the monitor adds up (the seed's), which
|
|
1067
|
+
* buildMessageQueueMetricMonitorViewConfig reads with one query each.
|
|
1068
|
+
*/
|
|
1069
|
+
seriesTotal: MessageQueueSeriesTotal | null;
|
|
1070
|
+
// The catalog's display unit, carried as the query's legend unit.
|
|
1071
|
+
unit: string;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function readObserved(
|
|
1075
|
+
series: MessageQueueObservedAttributes,
|
|
1076
|
+
key: string,
|
|
1077
|
+
): unknown {
|
|
1078
|
+
return Object.prototype.hasOwnProperty.call(series, key)
|
|
1079
|
+
? series[key]
|
|
1080
|
+
: undefined;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/*
|
|
1084
|
+
* The text a value is stored as, which is what an equality filter has to
|
|
1085
|
+
* match: a string exactly as it is — surrounding whitespace included — and a
|
|
1086
|
+
* number or boolean as its String() form. Null for a value that is absent,
|
|
1087
|
+
* blank or not a scalar, which the resolver does not read a destination,
|
|
1088
|
+
* scope or required value from either.
|
|
1089
|
+
*/
|
|
1090
|
+
function observedText(value: unknown): string | null {
|
|
1091
|
+
let text: string | null = null;
|
|
1092
|
+
|
|
1093
|
+
if (typeof value === "string") {
|
|
1094
|
+
text = value;
|
|
1095
|
+
} else if (typeof value === "number") {
|
|
1096
|
+
text = Number.isFinite(value) ? String(value) : null;
|
|
1097
|
+
} else if (typeof value === "boolean" || typeof value === "bigint") {
|
|
1098
|
+
text = String(value);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return text !== null && text.trim().length > 0 ? text : null;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/*
|
|
1105
|
+
* The (key, stored value) pairs that pin one series to its queue: the first
|
|
1106
|
+
* destination key with a value (the resolver's own "first non-empty wins"),
|
|
1107
|
+
* the first broker-scope key with one, and every required key. Null when the
|
|
1108
|
+
* series lacks a destination or a required attribute.
|
|
1109
|
+
*/
|
|
1110
|
+
function getSeriesFilter(
|
|
1111
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
1112
|
+
series: MessageQueueObservedAttributes,
|
|
1113
|
+
): Array<[string, string]> | null {
|
|
1114
|
+
const firstWithValue: (
|
|
1115
|
+
keys: ReadonlyArray<string>,
|
|
1116
|
+
) => [string, string] | null = (
|
|
1117
|
+
keys: ReadonlyArray<string>,
|
|
1118
|
+
): [string, string] | null => {
|
|
1119
|
+
for (const key of keys) {
|
|
1120
|
+
const text: string | null = observedText(readObserved(series, key));
|
|
1121
|
+
|
|
1122
|
+
if (text !== null) {
|
|
1123
|
+
return [key, text];
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
return null;
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
const destination: [string, string] | null = firstWithValue(
|
|
1131
|
+
descriptor.destinationAttributes,
|
|
1132
|
+
);
|
|
1133
|
+
|
|
1134
|
+
if (!destination) {
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const entries: Array<[string, string]> = [destination];
|
|
1139
|
+
|
|
1140
|
+
const scope: [string, string] | null = descriptor.scopeAttributes
|
|
1141
|
+
? firstWithValue(descriptor.scopeAttributes)
|
|
1142
|
+
: null;
|
|
1143
|
+
|
|
1144
|
+
if (scope) {
|
|
1145
|
+
entries.push(scope);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
for (const key of Object.keys(descriptor.requiredAttributes || {})) {
|
|
1149
|
+
const text: string | null = observedText(readObserved(series, key));
|
|
1150
|
+
|
|
1151
|
+
if (text === null) {
|
|
1152
|
+
return null;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
entries.push([key, text]);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return entries;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/*
|
|
1162
|
+
* The identifier of the queue a series of this entry belongs to, through the
|
|
1163
|
+
* same resolver ingest stamps entity keys with — or null when the series
|
|
1164
|
+
* resolves to nothing, or to another system's entry (a Service Bus name
|
|
1165
|
+
* observed on an Event Hubs series).
|
|
1166
|
+
*/
|
|
1167
|
+
function getSeriesQueueIdentifier(
|
|
1168
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
1169
|
+
series: MessageQueueObservedAttributes,
|
|
1170
|
+
): string | null {
|
|
1171
|
+
const resolved: ResolvedMessagingDestination | null =
|
|
1172
|
+
resolveMessagingMetricDatapoint({
|
|
1173
|
+
metricName: descriptor.metricName,
|
|
1174
|
+
getAttribute: (key: string): unknown => {
|
|
1175
|
+
return readObserved(series, key);
|
|
1176
|
+
},
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
if (!resolved || resolved.system !== descriptor.system) {
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
const identity: MessageQueueIdentity | null = toMessageQueueIdentity({
|
|
1184
|
+
system: resolved.system,
|
|
1185
|
+
brokerScope: resolved.brokerScope,
|
|
1186
|
+
destination: resolved.destination,
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
return identity ? buildMessageQueueIdentifier(identity) : null;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* The metric monitor query that watches one queue through one catalog
|
|
1194
|
+
* entry, built from the stored attributes of the series observed for it —
|
|
1195
|
+
* the code path the queue page's "Create monitor" link and getMonitorStep
|
|
1196
|
+
* share. Null when the entry cannot be monitored
|
|
1197
|
+
* (isMessageQueueMetricMonitorable) or no observed series resolves to the
|
|
1198
|
+
* queue.
|
|
1199
|
+
*
|
|
1200
|
+
* A series is used only when the resolver maps it to the queue under this
|
|
1201
|
+
* entry — `identity`'s queue when given, else the first series that
|
|
1202
|
+
* resolves — and when it pins the same keys as the first series used (one
|
|
1203
|
+
* spelling of Azure's entity key, not two: an AND of equalities cannot say
|
|
1204
|
+
* "this key or that one"). Values are kept exactly as stored, and a key the
|
|
1205
|
+
* used series disagree on (a Pulsar topic's partitions) becomes an Includes
|
|
1206
|
+
* of every value, sorted. A queue whose destination is templated from many
|
|
1207
|
+
* raw names (a UUID) is watched under the names observed.
|
|
1208
|
+
*/
|
|
1209
|
+
export function buildMessageQueueMetricMonitorQuery(data: {
|
|
1210
|
+
descriptor: MessageQueueMetricDescriptor;
|
|
1211
|
+
observedSeries: ReadonlyArray<MessageQueueObservedAttributes>;
|
|
1212
|
+
identity?: MessageQueueIdentity | null | undefined;
|
|
1213
|
+
}): MessageQueueMetricMonitorQuery | null {
|
|
1214
|
+
if (!data || !data.descriptor) {
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const descriptor: MessageQueueMetricDescriptor = data.descriptor;
|
|
1219
|
+
const seed: MessageQueueMetricMonitorSeed | null =
|
|
1220
|
+
getMessageQueueMetricMonitorSeed(descriptor);
|
|
1221
|
+
|
|
1222
|
+
if (!seed) {
|
|
1223
|
+
return null;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
let queueIdentifier: string | null = null;
|
|
1227
|
+
|
|
1228
|
+
if (data.identity) {
|
|
1229
|
+
queueIdentifier = buildMessageQueueIdentifier(data.identity);
|
|
1230
|
+
|
|
1231
|
+
if (!queueIdentifier) {
|
|
1232
|
+
return null;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
let keys: Array<string> | null = null;
|
|
1237
|
+
const valuesByKey: Map<string, Array<string>> = new Map<
|
|
1238
|
+
string,
|
|
1239
|
+
Array<string>
|
|
1240
|
+
>();
|
|
1241
|
+
|
|
1242
|
+
const observedSeries: ReadonlyArray<unknown> = Array.isArray(
|
|
1243
|
+
data.observedSeries,
|
|
1244
|
+
)
|
|
1245
|
+
? data.observedSeries
|
|
1246
|
+
: [];
|
|
1247
|
+
|
|
1248
|
+
for (const candidate of observedSeries) {
|
|
1249
|
+
if (
|
|
1250
|
+
!candidate ||
|
|
1251
|
+
typeof candidate !== "object" ||
|
|
1252
|
+
Array.isArray(candidate)
|
|
1253
|
+
) {
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
const series: MessageQueueObservedAttributes =
|
|
1258
|
+
candidate as MessageQueueObservedAttributes;
|
|
1259
|
+
const entries: Array<[string, string]> | null = getSeriesFilter(
|
|
1260
|
+
descriptor,
|
|
1261
|
+
series,
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1264
|
+
if (!entries) {
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
const identifier: string | null = getSeriesQueueIdentifier(
|
|
1269
|
+
descriptor,
|
|
1270
|
+
series,
|
|
1271
|
+
);
|
|
1272
|
+
|
|
1273
|
+
if (!identifier) {
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
if (queueIdentifier === null) {
|
|
1278
|
+
queueIdentifier = identifier;
|
|
1279
|
+
} else if (identifier !== queueIdentifier) {
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
const seriesKeys: Array<string> = entries.map(
|
|
1284
|
+
(entry: [string, string]): string => {
|
|
1285
|
+
return entry[0];
|
|
1286
|
+
},
|
|
1287
|
+
);
|
|
1288
|
+
|
|
1289
|
+
if (keys === null) {
|
|
1290
|
+
keys = seriesKeys;
|
|
1291
|
+
} else if (keys.join("\n") !== seriesKeys.join("\n")) {
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
for (const [key, value] of entries) {
|
|
1296
|
+
const values: Array<string> = valuesByKey.get(key) || [];
|
|
1297
|
+
|
|
1298
|
+
if (!values.includes(value)) {
|
|
1299
|
+
values.push(value);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
valuesByKey.set(key, values);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
if (keys === null) {
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
const attributes: Dictionary<string | Includes> = {};
|
|
1311
|
+
|
|
1312
|
+
for (const key of keys) {
|
|
1313
|
+
const values: Array<string> = [...(valuesByKey.get(key) || [])].sort();
|
|
1314
|
+
|
|
1315
|
+
attributes[key] = values.length === 1 ? values[0]! : new Includes(values);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
return {
|
|
1319
|
+
metricId: getMessageQueueMetricId(descriptor),
|
|
1320
|
+
metricName: descriptor.metricName,
|
|
1321
|
+
attributes: attributes,
|
|
1322
|
+
aggregationType: seed.aggregationType,
|
|
1323
|
+
groupByAttributeKeys: [...seed.groupByAttributeKeys],
|
|
1324
|
+
seriesTotal: seed.seriesTotal
|
|
1325
|
+
? { key: seed.seriesTotal.key, values: [...seed.seriesTotal.values] }
|
|
1326
|
+
: null,
|
|
1327
|
+
unit: descriptor.unit,
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/*
|
|
1332
|
+
* A monitor's view of a queue query: its queries, the formula adding a
|
|
1333
|
+
* series total up (none otherwise), and the alias a criteria compares.
|
|
1334
|
+
*/
|
|
1335
|
+
export interface MessageQueueMetricMonitorViewConfig {
|
|
1336
|
+
metricViewConfig: MetricsViewConfig;
|
|
1337
|
+
criteriaAlias: string;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
// "unacknowledged" → "unacknowledged"; any other character → "_".
|
|
1341
|
+
function toAliasPart(value: string): string {
|
|
1342
|
+
return (
|
|
1343
|
+
value
|
|
1344
|
+
.toLowerCase()
|
|
1345
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
1346
|
+
.replace(/^_+|_+$/g, "") || "part"
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// A copy, so a config edited in the explorer never edits the query.
|
|
1351
|
+
function copyAttributes(
|
|
1352
|
+
attributes: Dictionary<string | Includes>,
|
|
1353
|
+
): Dictionary<string | Includes> {
|
|
1354
|
+
const copy: Dictionary<string | Includes> = {};
|
|
1355
|
+
|
|
1356
|
+
for (const key of Object.keys(attributes)) {
|
|
1357
|
+
const value: string | Includes | undefined = attributes[key];
|
|
1358
|
+
|
|
1359
|
+
if (value instanceof Includes) {
|
|
1360
|
+
copy[key] = new Includes([...(value.values as Array<string>)]);
|
|
1361
|
+
} else if (value !== undefined) {
|
|
1362
|
+
copy[key] = value;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
return copy;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
function buildQueryConfig(data: {
|
|
1370
|
+
query: MessageQueueMetricMonitorQuery;
|
|
1371
|
+
attributes: Dictionary<string | Includes>;
|
|
1372
|
+
metricVariable: string;
|
|
1373
|
+
title: string;
|
|
1374
|
+
description: string;
|
|
1375
|
+
}): MetricQueryConfigData {
|
|
1376
|
+
const query: MessageQueueMetricMonitorQuery = data.query;
|
|
1377
|
+
|
|
1378
|
+
return {
|
|
1379
|
+
metricAliasData: {
|
|
1380
|
+
metricVariable: data.metricVariable,
|
|
1381
|
+
title: data.title,
|
|
1382
|
+
description: data.description,
|
|
1383
|
+
legend: data.title,
|
|
1384
|
+
legendUnit: query.unit || undefined,
|
|
1385
|
+
},
|
|
1386
|
+
metricQueryData: {
|
|
1387
|
+
filterData: {
|
|
1388
|
+
metricName: query.metricName,
|
|
1389
|
+
attributes: data.attributes,
|
|
1390
|
+
aggegationType: query.aggregationType,
|
|
1391
|
+
aggregateBy: {},
|
|
1392
|
+
},
|
|
1393
|
+
...(query.groupByAttributeKeys.length > 0
|
|
1394
|
+
? { groupByAttributeKeys: [...query.groupByAttributeKeys] }
|
|
1395
|
+
: {}),
|
|
1396
|
+
},
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* The metric monitor view for a queue query — what the "Create monitor"
|
|
1402
|
+
* link pre-seeds Monitor Create with, and what getMonitorStep builds: the
|
|
1403
|
+
* exact filters, the fold, the grouping, and the catalog's unit as the legend
|
|
1404
|
+
* unit. A plain query is one query config, aliased `metricVariable`. A
|
|
1405
|
+
* series total is one query per part, each also filtered on its value and
|
|
1406
|
+
* aliased `<metricVariable>_<value>`, plus the formula adding them up,
|
|
1407
|
+
* aliased `metricVariable` — the alias a criteria compares either way.
|
|
1408
|
+
* `metricVariable` must be a formula identifier (letters, digits, "_").
|
|
1409
|
+
*/
|
|
1410
|
+
export function buildMessageQueueMetricMonitorViewConfig(data: {
|
|
1411
|
+
query: MessageQueueMetricMonitorQuery;
|
|
1412
|
+
metricVariable: string;
|
|
1413
|
+
title: string;
|
|
1414
|
+
description?: string | undefined;
|
|
1415
|
+
}): MessageQueueMetricMonitorViewConfig {
|
|
1416
|
+
const query: MessageQueueMetricMonitorQuery = data.query;
|
|
1417
|
+
const description: string = data.description || data.title;
|
|
1418
|
+
const total: MessageQueueSeriesTotal | null = query.seriesTotal;
|
|
1419
|
+
|
|
1420
|
+
if (!total || total.values.length === 0) {
|
|
1421
|
+
return {
|
|
1422
|
+
metricViewConfig: {
|
|
1423
|
+
queryConfigs: [
|
|
1424
|
+
buildQueryConfig({
|
|
1425
|
+
query: query,
|
|
1426
|
+
attributes: copyAttributes(query.attributes),
|
|
1427
|
+
metricVariable: data.metricVariable,
|
|
1428
|
+
title: data.title,
|
|
1429
|
+
description: description,
|
|
1430
|
+
}),
|
|
1431
|
+
],
|
|
1432
|
+
formulaConfigs: [],
|
|
1433
|
+
},
|
|
1434
|
+
criteriaAlias: data.metricVariable,
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
const partAliases: Array<string> = total.values.map(
|
|
1439
|
+
(value: string): string => {
|
|
1440
|
+
return `${data.metricVariable}_${toAliasPart(value)}`;
|
|
1441
|
+
},
|
|
1442
|
+
);
|
|
1443
|
+
|
|
1444
|
+
const queryConfigs: Array<MetricQueryConfigData> = total.values.map(
|
|
1445
|
+
(value: string, index: number): MetricQueryConfigData => {
|
|
1446
|
+
return buildQueryConfig({
|
|
1447
|
+
query: query,
|
|
1448
|
+
attributes: {
|
|
1449
|
+
...copyAttributes(query.attributes),
|
|
1450
|
+
[total.key]: value,
|
|
1451
|
+
},
|
|
1452
|
+
metricVariable: partAliases[index]!,
|
|
1453
|
+
title: `${data.title} (${value})`,
|
|
1454
|
+
description: description,
|
|
1455
|
+
});
|
|
1456
|
+
},
|
|
1457
|
+
);
|
|
1458
|
+
|
|
1459
|
+
const formulaConfig: MetricFormulaConfigData = {
|
|
1460
|
+
metricAliasData: {
|
|
1461
|
+
metricVariable: data.metricVariable,
|
|
1462
|
+
title: data.title,
|
|
1463
|
+
description: description,
|
|
1464
|
+
legend: data.title,
|
|
1465
|
+
legendUnit: query.unit || undefined,
|
|
1466
|
+
},
|
|
1467
|
+
metricFormulaData: {
|
|
1468
|
+
metricFormula: partAliases.join(" + "),
|
|
1469
|
+
},
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
return {
|
|
1473
|
+
metricViewConfig: {
|
|
1474
|
+
queryConfigs: queryConfigs,
|
|
1475
|
+
formulaConfigs: [formulaConfig],
|
|
1476
|
+
},
|
|
1477
|
+
criteriaAlias: data.metricVariable,
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// --- policies ---
|
|
1482
|
+
|
|
1483
|
+
/*
|
|
1484
|
+
* The alert one catalog entry gets: its signal's default, adjusted by the
|
|
1485
|
+
* entry's override, with the threshold in the metric's own unit.
|
|
1486
|
+
*/
|
|
1487
|
+
export interface MessageQueueAlertPolicy {
|
|
1488
|
+
threshold: number;
|
|
1489
|
+
thresholdLabel: string;
|
|
1490
|
+
filterType: FilterType;
|
|
1491
|
+
evaluation: EvaluateOverTimeType;
|
|
1492
|
+
// The signal's (or override's) window, widened to the source's floor.
|
|
1493
|
+
rollingTime: RollingTime;
|
|
1494
|
+
// The floor that widened it, or null when the source's points are live.
|
|
1495
|
+
windowFloor: MessageQueueSourceWindowFloor | null;
|
|
1496
|
+
severity: MessageQueueAlertTemplateSeverity;
|
|
1497
|
+
category: MessageQueueAlertTemplateCategory;
|
|
1498
|
+
recoverOnNoData: boolean;
|
|
1499
|
+
metricAlias: string;
|
|
1500
|
+
meaning: string;
|
|
1501
|
+
incidentDescription: string;
|
|
1502
|
+
// The generated name, before any "(JSON stream)" qualifier.
|
|
1503
|
+
baseName: string;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
function getUntemplatedReason(
|
|
1507
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
1508
|
+
): string | null {
|
|
1509
|
+
for (const entry of UNTEMPLATED_MESSAGE_QUEUE_GAUGES) {
|
|
1510
|
+
if (
|
|
1511
|
+
entry.system === descriptor.system &&
|
|
1512
|
+
entry.metricName === descriptor.metricName
|
|
1513
|
+
) {
|
|
1514
|
+
return entry.reason;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
return null;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
function getPolicyDefault(
|
|
1522
|
+
signal: MessageQueueSignal,
|
|
1523
|
+
measure: MessageQueueAlertMeasure,
|
|
1524
|
+
): MessageQueueAlertPolicyDefault | null {
|
|
1525
|
+
for (const policy of MESSAGE_QUEUE_ALERT_POLICIES) {
|
|
1526
|
+
if (policy.signal === signal && policy.measure === measure) {
|
|
1527
|
+
return policy;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
return null;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function getPolicyOverride(
|
|
1535
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
1536
|
+
): MessageQueueAlertPolicyOverride | null {
|
|
1537
|
+
for (const override of MESSAGE_QUEUE_ALERT_POLICY_OVERRIDES) {
|
|
1538
|
+
if (
|
|
1539
|
+
override.system === descriptor.system &&
|
|
1540
|
+
override.metricNames.includes(descriptor.metricName)
|
|
1541
|
+
) {
|
|
1542
|
+
return override;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
return null;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// "1000" → "1,000"; a fraction to at most two decimals.
|
|
1550
|
+
function formatNumber(value: number): string {
|
|
1551
|
+
if (!Number.isInteger(value)) {
|
|
1552
|
+
return String(Number(value.toFixed(2)));
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
function formatDuration(seconds: number): string {
|
|
1559
|
+
if (seconds > 0 && seconds % 3600 === 0) {
|
|
1560
|
+
const hours: number = seconds / 3600;
|
|
1561
|
+
return `${formatNumber(hours)} ${hours === 1 ? "hour" : "hours"}`;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
if (seconds > 0 && seconds % 60 === 0) {
|
|
1565
|
+
const minutes: number = seconds / 60;
|
|
1566
|
+
return `${formatNumber(minutes)} ${minutes === 1 ? "minute" : "minutes"}`;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
return `${formatNumber(seconds)} ${seconds === 1 ? "second" : "seconds"}`;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
/*
|
|
1573
|
+
* A threshold in words, in the metric's own unit: "1,000 messages",
|
|
1574
|
+
* "1 message", "10 minutes".
|
|
1575
|
+
*/
|
|
1576
|
+
export function formatMessageQueueThreshold(
|
|
1577
|
+
value: number,
|
|
1578
|
+
unit: string,
|
|
1579
|
+
): string {
|
|
1580
|
+
const trimmedUnit: string = (unit || "").trim();
|
|
1581
|
+
|
|
1582
|
+
if (trimmedUnit === "s") {
|
|
1583
|
+
return formatDuration(value);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
if (trimmedUnit === "ms") {
|
|
1587
|
+
return value % 1000 === 0
|
|
1588
|
+
? formatDuration(value / 1000)
|
|
1589
|
+
: `${formatNumber(value)} milliseconds`;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
if (!trimmedUnit) {
|
|
1593
|
+
return formatNumber(value);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
const singular: string =
|
|
1597
|
+
value === 1 && trimmedUnit.length > 1 && trimmedUnit.endsWith("s")
|
|
1598
|
+
? trimmedUnit.substring(0, trimmedUnit.length - 1)
|
|
1599
|
+
: trimmedUnit;
|
|
1600
|
+
|
|
1601
|
+
return `${formatNumber(value)} ${singular}`;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// "Dead-lettered messages" → "Dead-Lettered Messages"; small words stay small.
|
|
1605
|
+
const TITLE_CASE_SMALL_WORDS: ReadonlyArray<string> = [
|
|
1606
|
+
"a",
|
|
1607
|
+
"an",
|
|
1608
|
+
"and",
|
|
1609
|
+
"at",
|
|
1610
|
+
"by",
|
|
1611
|
+
"for",
|
|
1612
|
+
"in",
|
|
1613
|
+
"of",
|
|
1614
|
+
"on",
|
|
1615
|
+
"or",
|
|
1616
|
+
"the",
|
|
1617
|
+
"to",
|
|
1618
|
+
];
|
|
1619
|
+
|
|
1620
|
+
function toTitleCase(value: string): string {
|
|
1621
|
+
return value
|
|
1622
|
+
.split(" ")
|
|
1623
|
+
.map((word: string, index: number): string => {
|
|
1624
|
+
if (index > 0 && TITLE_CASE_SMALL_WORDS.includes(word.toLowerCase())) {
|
|
1625
|
+
return word.toLowerCase();
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
return word
|
|
1629
|
+
.split("-")
|
|
1630
|
+
.map((part: string): string => {
|
|
1631
|
+
return part.length > 0
|
|
1632
|
+
? `${part.charAt(0).toUpperCase()}${part.substring(1)}`
|
|
1633
|
+
: part;
|
|
1634
|
+
})
|
|
1635
|
+
.join("-");
|
|
1636
|
+
})
|
|
1637
|
+
.join(" ");
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/*
|
|
1641
|
+
* A catalog title split into its name and a trailing qualifier:
|
|
1642
|
+
* "Messages visible (JSON stream)" → "Messages visible", "(JSON stream)".
|
|
1643
|
+
*/
|
|
1644
|
+
function splitTitle(title: string): { base: string; qualifier: string } {
|
|
1645
|
+
const match: RegExpMatchArray | null = title.match(/^(.*?)\s*(\([^()]*\))$/);
|
|
1646
|
+
|
|
1647
|
+
if (!match) {
|
|
1648
|
+
return { base: title, qualifier: "" };
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
return { base: match[1] || title, qualifier: match[2] || "" };
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
/**
|
|
1655
|
+
* The alert a catalog entry gets, or null when it gets none: not a
|
|
1656
|
+
* monitorable gauge, a signal no static threshold means anything for, no
|
|
1657
|
+
* default for its (signal, measure), or a deliberately untemplated gauge.
|
|
1658
|
+
* The threshold is in the metric's own unit (an age of 600 s reads 600000 on
|
|
1659
|
+
* a metric reported in milliseconds).
|
|
1660
|
+
*/
|
|
1661
|
+
export function getMessageQueueAlertPolicy(
|
|
1662
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
1663
|
+
): MessageQueueAlertPolicy | null {
|
|
1664
|
+
if (
|
|
1665
|
+
!isMessageQueueMetricMonitorable(descriptor) ||
|
|
1666
|
+
!MESSAGE_QUEUE_THRESHOLDABLE_SIGNALS.includes(descriptor.signal) ||
|
|
1667
|
+
getUntemplatedReason(descriptor) !== null
|
|
1668
|
+
) {
|
|
1669
|
+
return null;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
const measure: MessageQueueAlertMeasure =
|
|
1673
|
+
getMessageQueueAlertMeasure(descriptor);
|
|
1674
|
+
const defaults: MessageQueueAlertPolicyDefault | null = getPolicyDefault(
|
|
1675
|
+
descriptor.signal,
|
|
1676
|
+
measure,
|
|
1677
|
+
);
|
|
1678
|
+
|
|
1679
|
+
if (!defaults) {
|
|
1680
|
+
return null;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
const override: MessageQueueAlertPolicyOverride | null =
|
|
1684
|
+
getPolicyOverride(descriptor);
|
|
1685
|
+
|
|
1686
|
+
const defaultThreshold: number = defaults.thresholdUnit
|
|
1687
|
+
? MetricUnitUtil.convertToMetricUnit({
|
|
1688
|
+
value: defaults.threshold,
|
|
1689
|
+
fromUnit: defaults.thresholdUnit,
|
|
1690
|
+
metricUnit: descriptor.unit,
|
|
1691
|
+
})
|
|
1692
|
+
: defaults.threshold;
|
|
1693
|
+
|
|
1694
|
+
// Clears the binary noise a unit conversion leaves (600 / 0.001).
|
|
1695
|
+
const threshold: number = Number(
|
|
1696
|
+
(override?.threshold ?? defaultThreshold).toPrecision(12),
|
|
1697
|
+
);
|
|
1698
|
+
|
|
1699
|
+
const title: { base: string; qualifier: string } = splitTitle(
|
|
1700
|
+
descriptor.title,
|
|
1701
|
+
);
|
|
1702
|
+
|
|
1703
|
+
const generatedName: string = defaults.nameSuffix
|
|
1704
|
+
? `${toTitleCase(title.base)} ${defaults.nameSuffix}`
|
|
1705
|
+
: toTitleCase(title.base);
|
|
1706
|
+
|
|
1707
|
+
// A late source's points never land in a window shorter than their lag.
|
|
1708
|
+
const signalWindow: RollingTime =
|
|
1709
|
+
override?.rollingTime || defaults.rollingTime;
|
|
1710
|
+
const rollingTime: RollingTime = getMessageQueueMetricMonitorRollingTime(
|
|
1711
|
+
descriptor,
|
|
1712
|
+
signalWindow,
|
|
1713
|
+
);
|
|
1714
|
+
|
|
1715
|
+
return {
|
|
1716
|
+
threshold: threshold,
|
|
1717
|
+
thresholdLabel: `at or above ${formatMessageQueueThreshold(
|
|
1718
|
+
threshold,
|
|
1719
|
+
descriptor.unit,
|
|
1720
|
+
)}`,
|
|
1721
|
+
filterType: FilterType.GreaterThanOrEqualTo,
|
|
1722
|
+
evaluation: defaults.evaluation,
|
|
1723
|
+
rollingTime: rollingTime,
|
|
1724
|
+
windowFloor:
|
|
1725
|
+
rollingTime === signalWindow
|
|
1726
|
+
? null
|
|
1727
|
+
: getMessageQueueSourceWindowFloor(descriptor),
|
|
1728
|
+
severity: override?.severity || defaults.severity,
|
|
1729
|
+
category: defaults.category,
|
|
1730
|
+
// A count's silent window is a window without events (see THRESHOLDS).
|
|
1731
|
+
recoverOnNoData: override?.recoverOnNoData ?? measure === "count",
|
|
1732
|
+
metricAlias: defaults.metricAlias,
|
|
1733
|
+
meaning: override?.meaning || defaults.meaning,
|
|
1734
|
+
incidentDescription:
|
|
1735
|
+
override?.incidentDescription || defaults.incidentDescription,
|
|
1736
|
+
baseName: override?.name || generatedName,
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// --- template builders ---
|
|
1741
|
+
|
|
1742
|
+
const ROLLING_TIME_WORDS: Readonly<
|
|
1743
|
+
Record<string, { noun: string; adjective: string }>
|
|
1744
|
+
> = {
|
|
1745
|
+
[RollingTime.Past5Minutes]: {
|
|
1746
|
+
noun: "five minutes",
|
|
1747
|
+
adjective: "five-minute",
|
|
1748
|
+
},
|
|
1749
|
+
[RollingTime.Past10Minutes]: {
|
|
1750
|
+
noun: "ten minutes",
|
|
1751
|
+
adjective: "ten-minute",
|
|
1752
|
+
},
|
|
1753
|
+
[RollingTime.Past15Minutes]: {
|
|
1754
|
+
noun: "fifteen minutes",
|
|
1755
|
+
adjective: "fifteen-minute",
|
|
1756
|
+
},
|
|
1757
|
+
[RollingTime.Past30Minutes]: {
|
|
1758
|
+
noun: "thirty minutes",
|
|
1759
|
+
adjective: "thirty-minute",
|
|
1760
|
+
},
|
|
1761
|
+
};
|
|
1762
|
+
|
|
1763
|
+
// ["ready", "unacknowledged"] → "ready and unacknowledged".
|
|
1764
|
+
function joinWords(values: ReadonlyArray<string>): string {
|
|
1765
|
+
if (values.length <= 1) {
|
|
1766
|
+
return values.join("");
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
return `${values.slice(0, -1).join(", ")} and ${values[values.length - 1]}`;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
function getWindowWords(rollingTime: RollingTime): {
|
|
1773
|
+
noun: string;
|
|
1774
|
+
adjective: string;
|
|
1775
|
+
} {
|
|
1776
|
+
return (
|
|
1777
|
+
ROLLING_TIME_WORDS[rollingTime] || {
|
|
1778
|
+
noun: rollingTime.toLowerCase(),
|
|
1779
|
+
adjective: rollingTime.toLowerCase(),
|
|
1780
|
+
}
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// A collector component type: "googlecloudmonitoring", "kafka_metrics".
|
|
1785
|
+
const COLLECTOR_COMPONENT_PATTERN: RegExp = /^[a-z0-9_]+$/;
|
|
1786
|
+
|
|
1787
|
+
/*
|
|
1788
|
+
* "the collector's `googlecloudmonitoring` receiver" — how a sentence names
|
|
1789
|
+
* the component, which the catalog spells as a receiver type,
|
|
1790
|
+
* "prometheus" or a product name.
|
|
1791
|
+
*/
|
|
1792
|
+
function describeReceiver(receiver: string): string {
|
|
1793
|
+
return COLLECTOR_COMPONENT_PATTERN.test(receiver)
|
|
1794
|
+
? `the collector's \`${receiver}\` receiver`
|
|
1795
|
+
: receiver;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
function buildTemplateDescription(data: {
|
|
1799
|
+
descriptor: MessageQueueMetricDescriptor;
|
|
1800
|
+
policy: MessageQueueAlertPolicy;
|
|
1801
|
+
seed: MessageQueueMetricMonitorSeed;
|
|
1802
|
+
}): string {
|
|
1803
|
+
const descriptor: MessageQueueMetricDescriptor = data.descriptor;
|
|
1804
|
+
const policy: MessageQueueAlertPolicy = data.policy;
|
|
1805
|
+
const window: { noun: string; adjective: string } = getWindowWords(
|
|
1806
|
+
policy.rollingTime,
|
|
1807
|
+
);
|
|
1808
|
+
const subject: string = `"${splitTitle(descriptor.title).base}"`;
|
|
1809
|
+
const perSeries: string =
|
|
1810
|
+
data.seed.groupByAttributeKeys.length > 0
|
|
1811
|
+
? `any one ${data.seed.groupByAttributeKeys.join(" / ")} series of `
|
|
1812
|
+
: "";
|
|
1813
|
+
|
|
1814
|
+
/*
|
|
1815
|
+
* A late source's window holds less than its length of data (see WINDOWS
|
|
1816
|
+
* AND LATE SOURCES), so its breach is worded by points, not by minutes.
|
|
1817
|
+
*/
|
|
1818
|
+
let breach: string;
|
|
1819
|
+
|
|
1820
|
+
if (policy.evaluation === EvaluateOverTimeType.AnyValue) {
|
|
1821
|
+
breach = policy.windowFloor
|
|
1822
|
+
? `Alert when ${perSeries}${subject} reads ${policy.thresholdLabel} at any point of a ${window.adjective} window.`
|
|
1823
|
+
: `Alert when ${perSeries}${subject} reads ${policy.thresholdLabel} in any single minute of a ${window.adjective} window.`;
|
|
1824
|
+
} else {
|
|
1825
|
+
breach = policy.windowFloor
|
|
1826
|
+
? `Alert when ${perSeries}${subject} reads ${policy.thresholdLabel} at every point of a ${window.adjective} window.`
|
|
1827
|
+
: `Alert when ${perSeries}${subject} stays ${policy.thresholdLabel} for ${window.noun}.`;
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
const sentences: Array<string> = [breach, policy.meaning];
|
|
1831
|
+
|
|
1832
|
+
if (policy.windowFloor) {
|
|
1833
|
+
sentences.push(policy.windowFloor.reason);
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
sentences.push(`Reads ${descriptor.metricName}. ${descriptor.description}`);
|
|
1837
|
+
|
|
1838
|
+
if (data.seed.seriesTotal) {
|
|
1839
|
+
sentences.push(
|
|
1840
|
+
`The monitor reads the ${joinWords(
|
|
1841
|
+
data.seed.seriesTotal.values,
|
|
1842
|
+
)} series with one query each and adds them up with a formula, the total the queue page shows.`,
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
if (data.seed.groupByAttributeKeys.length > 0) {
|
|
1847
|
+
sentences.push(
|
|
1848
|
+
"Its series are not a fixed set, so the monitor cannot add them up the way the queue page does: it watches each series on its own, and the threshold is for one series, not for the total.",
|
|
1849
|
+
);
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
if (!descriptor.enabledByDefault) {
|
|
1853
|
+
sentences.push(
|
|
1854
|
+
`${capitalizeFirst(describeReceiver(descriptor.receiver))} only reports ${
|
|
1855
|
+
descriptor.metricName
|
|
1856
|
+
} once it is enabled in the collector's configuration${
|
|
1857
|
+
descriptor.receiver === "googlecloudmonitoring"
|
|
1858
|
+
? " (listed under the receiver's `metrics_list`)"
|
|
1859
|
+
: ""
|
|
1860
|
+
}.`,
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
return sentences.join(" ");
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
function capitalizeFirst(value: string): string {
|
|
1868
|
+
return value.length > 0
|
|
1869
|
+
? `${value.charAt(0).toUpperCase()}${value.substring(1)}`
|
|
1870
|
+
: value;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
/*
|
|
1874
|
+
* "rabbitmq.message.current (resource.rabbitmq.queue.name = orders), state
|
|
1875
|
+
* ready + unacknowledged" or "…, per broker" — the query as the criteria
|
|
1876
|
+
* description names it.
|
|
1877
|
+
*/
|
|
1878
|
+
function describeQuery(query: MessageQueueMetricMonitorQuery): string {
|
|
1879
|
+
const filters: Array<string> = Object.keys(query.attributes).map(
|
|
1880
|
+
(key: string): string => {
|
|
1881
|
+
const value: string | Includes | undefined = query.attributes[key];
|
|
1882
|
+
|
|
1883
|
+
if (value instanceof Includes) {
|
|
1884
|
+
const values: Array<string | ObjectID | number> = value.values as Array<
|
|
1885
|
+
string | ObjectID | number
|
|
1886
|
+
>;
|
|
1887
|
+
|
|
1888
|
+
return `${key} in [${values
|
|
1889
|
+
.map((item: string | ObjectID | number): string => {
|
|
1890
|
+
return String(item);
|
|
1891
|
+
})
|
|
1892
|
+
.join(", ")}]`;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
return `${key} = ${String(value)}`;
|
|
1896
|
+
},
|
|
1897
|
+
);
|
|
1898
|
+
|
|
1899
|
+
let series: string = "";
|
|
1900
|
+
|
|
1901
|
+
if (query.seriesTotal) {
|
|
1902
|
+
series = `, ${query.seriesTotal.key} ${query.seriesTotal.values.join(
|
|
1903
|
+
" + ",
|
|
1904
|
+
)}`;
|
|
1905
|
+
} else if (query.groupByAttributeKeys.length > 0) {
|
|
1906
|
+
series = `, per ${query.groupByAttributeKeys.join(" / ")}`;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
return `${query.metricName} (${filters.join(", ")})${series}`;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
function describeCriteria(data: {
|
|
1913
|
+
subject: string;
|
|
1914
|
+
policy: MessageQueueAlertPolicy;
|
|
1915
|
+
}): string {
|
|
1916
|
+
if (data.policy.evaluation === EvaluateOverTimeType.AnyValue) {
|
|
1917
|
+
return `Triggers when ${data.subject} reads ${data.policy.thresholdLabel} at any point in the evaluation window.`;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
return `Triggers when ${data.subject} stays ${data.policy.thresholdLabel} for the whole evaluation window.`;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
function buildCriteria(data: {
|
|
1924
|
+
args: MessageQueueAlertTemplateArgs;
|
|
1925
|
+
templateName: string;
|
|
1926
|
+
query: MessageQueueMetricMonitorQuery;
|
|
1927
|
+
policy: MessageQueueAlertPolicy;
|
|
1928
|
+
// The query's alias, or the formula's for a series total.
|
|
1929
|
+
criteriaAlias: string;
|
|
1930
|
+
}): MonitorCriteria {
|
|
1931
|
+
const policy: MessageQueueAlertPolicy = data.policy;
|
|
1932
|
+
|
|
1933
|
+
const unhealthy: MonitorCriteriaInstance = buildUnhealthyCriteriaInstance({
|
|
1934
|
+
offlineMonitorStatusId: data.args.offlineMonitorStatusId,
|
|
1935
|
+
incidentSeverityId: data.args.defaultIncidentSeverityId,
|
|
1936
|
+
alertSeverityId: data.args.defaultAlertSeverityId,
|
|
1937
|
+
monitorName: data.args.monitorName,
|
|
1938
|
+
metricAlias: data.criteriaAlias,
|
|
1939
|
+
filterType: policy.filterType,
|
|
1940
|
+
value: policy.threshold,
|
|
1941
|
+
incidentTitle: `[Queue] ${data.templateName} - ${data.args.monitorName}`,
|
|
1942
|
+
incidentDescription: policy.incidentDescription,
|
|
1943
|
+
criteriaName: `${data.templateName} - ${policy.thresholdLabel}`,
|
|
1944
|
+
criteriaDescription: describeCriteria({
|
|
1945
|
+
subject: describeQuery(data.query),
|
|
1946
|
+
policy: policy,
|
|
1947
|
+
}),
|
|
1948
|
+
resourceNoun: "queue",
|
|
1949
|
+
metricAggregationType: policy.evaluation,
|
|
1950
|
+
});
|
|
1951
|
+
|
|
1952
|
+
/*
|
|
1953
|
+
* Recovery mirrors the breach: the complementary comparison below a dead
|
|
1954
|
+
* band, held for the whole window — also for a count, which breaches on
|
|
1955
|
+
* any one minute — and, where a silent window means zero, met by one.
|
|
1956
|
+
*/
|
|
1957
|
+
const healthy: MonitorCriteriaInstance = buildHealthyCriteriaInstance({
|
|
1958
|
+
onlineMonitorStatusId: data.args.onlineMonitorStatusId,
|
|
1959
|
+
metricAlias: data.criteriaAlias,
|
|
1960
|
+
filterType: getRecoveryFilterType(policy.filterType),
|
|
1961
|
+
value: policy.threshold,
|
|
1962
|
+
metricAggregationType:
|
|
1963
|
+
policy.evaluation === EvaluateOverTimeType.AnyValue
|
|
1964
|
+
? EvaluateOverTimeType.AllValues
|
|
1965
|
+
: policy.evaluation,
|
|
1966
|
+
treatNoDataAsZero: policy.recoverOnNoData,
|
|
1967
|
+
});
|
|
1968
|
+
|
|
1969
|
+
const monitorCriteria: MonitorCriteria = new MonitorCriteria();
|
|
1970
|
+
monitorCriteria.data = {
|
|
1971
|
+
monitorCriteriaInstanceArray: [unhealthy, healthy],
|
|
1972
|
+
};
|
|
1973
|
+
|
|
1974
|
+
return monitorCriteria;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// "aws_sqs" + "amazonaws.com/aws/sqs/x" → "aws-sqs-amazonaws-com-aws-sqs-x".
|
|
1978
|
+
function toIdSlug(value: string): string {
|
|
1979
|
+
return value
|
|
1980
|
+
.toLowerCase()
|
|
1981
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
1982
|
+
.replace(/^-+|-+$/g, "");
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
function buildTemplate(
|
|
1986
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
1987
|
+
): MessageQueueAlertTemplate | null {
|
|
1988
|
+
const policy: MessageQueueAlertPolicy | null =
|
|
1989
|
+
getMessageQueueAlertPolicy(descriptor);
|
|
1990
|
+
const seed: MessageQueueMetricMonitorSeed | null =
|
|
1991
|
+
getMessageQueueMetricMonitorSeed(descriptor);
|
|
1992
|
+
|
|
1993
|
+
if (!policy || !seed) {
|
|
1994
|
+
return null;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
const qualifier: string = splitTitle(descriptor.title).qualifier;
|
|
1998
|
+
const name: string = qualifier
|
|
1999
|
+
? `${policy.baseName} ${qualifier}`
|
|
2000
|
+
: policy.baseName;
|
|
2001
|
+
|
|
2002
|
+
return {
|
|
2003
|
+
id: `message-queue-${toIdSlug(descriptor.system)}-${toIdSlug(
|
|
2004
|
+
descriptor.metricName,
|
|
2005
|
+
)}`,
|
|
2006
|
+
name: name,
|
|
2007
|
+
description: buildTemplateDescription({
|
|
2008
|
+
descriptor: descriptor,
|
|
2009
|
+
policy: policy,
|
|
2010
|
+
seed: seed,
|
|
2011
|
+
}),
|
|
2012
|
+
category: policy.category,
|
|
2013
|
+
severity: policy.severity,
|
|
2014
|
+
monitorType: MonitorType.Metrics,
|
|
2015
|
+
system: descriptor.system,
|
|
2016
|
+
receiver: descriptor.receiver,
|
|
2017
|
+
signal: descriptor.signal,
|
|
2018
|
+
metricId: getMessageQueueMetricId(descriptor),
|
|
2019
|
+
metricName: descriptor.metricName,
|
|
2020
|
+
metricNames: [descriptor.metricName],
|
|
2021
|
+
unit: descriptor.unit,
|
|
2022
|
+
aggregationType: seed.aggregationType,
|
|
2023
|
+
groupByAttributeKeys: [...seed.groupByAttributeKeys],
|
|
2024
|
+
seriesTotal: seed.seriesTotal
|
|
2025
|
+
? { key: seed.seriesTotal.key, values: [...seed.seriesTotal.values] }
|
|
2026
|
+
: null,
|
|
2027
|
+
filterType: policy.filterType,
|
|
2028
|
+
threshold: policy.threshold,
|
|
2029
|
+
thresholdLabel: policy.thresholdLabel,
|
|
2030
|
+
evaluation: policy.evaluation,
|
|
2031
|
+
rollingTime: policy.rollingTime,
|
|
2032
|
+
recoverOnNoData: policy.recoverOnNoData,
|
|
2033
|
+
getMonitorStep: (
|
|
2034
|
+
args: MessageQueueAlertTemplateArgs,
|
|
2035
|
+
): MonitorStep | null => {
|
|
2036
|
+
if (!args) {
|
|
2037
|
+
return null;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
const query: MessageQueueMetricMonitorQuery | null =
|
|
2041
|
+
buildMessageQueueMetricMonitorQuery({
|
|
2042
|
+
descriptor: descriptor,
|
|
2043
|
+
observedSeries: args.observedSeries,
|
|
2044
|
+
identity: args.identity,
|
|
2045
|
+
});
|
|
2046
|
+
|
|
2047
|
+
if (!query) {
|
|
2048
|
+
return null;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
const step: MonitorStep = MonitorStep.getDefaultMonitorStep({
|
|
2052
|
+
monitorName: args.monitorName,
|
|
2053
|
+
monitorType: MonitorType.Metrics,
|
|
2054
|
+
onlineMonitorStatusId: args.onlineMonitorStatusId,
|
|
2055
|
+
offlineMonitorStatusId: args.offlineMonitorStatusId,
|
|
2056
|
+
defaultIncidentSeverityId: args.defaultIncidentSeverityId,
|
|
2057
|
+
defaultAlertSeverityId: args.defaultAlertSeverityId,
|
|
2058
|
+
});
|
|
2059
|
+
|
|
2060
|
+
const view: MessageQueueMetricMonitorViewConfig =
|
|
2061
|
+
buildMessageQueueMetricMonitorViewConfig({
|
|
2062
|
+
query: query,
|
|
2063
|
+
metricVariable: policy.metricAlias,
|
|
2064
|
+
title: name,
|
|
2065
|
+
description: descriptor.description,
|
|
2066
|
+
});
|
|
2067
|
+
|
|
2068
|
+
step.setMetricMonitor({
|
|
2069
|
+
rollingTime: policy.rollingTime,
|
|
2070
|
+
metricViewConfig: view.metricViewConfig,
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
step.setMonitorCriteria(
|
|
2074
|
+
buildCriteria({
|
|
2075
|
+
args: args,
|
|
2076
|
+
templateName: name,
|
|
2077
|
+
query: query,
|
|
2078
|
+
policy: policy,
|
|
2079
|
+
criteriaAlias: view.criteriaAlias,
|
|
2080
|
+
}),
|
|
2081
|
+
);
|
|
2082
|
+
|
|
2083
|
+
return step;
|
|
2084
|
+
},
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
/*
|
|
2089
|
+
* Every catalog entry's template, in catalog order, built once. The product
|
|
2090
|
+
* reads them one at a time, by catalog entry, and never as a list (see WHO
|
|
2091
|
+
* READS THEM).
|
|
2092
|
+
*/
|
|
2093
|
+
const ALL_MESSAGE_QUEUE_ALERT_TEMPLATES: Array<MessageQueueAlertTemplate> =
|
|
2094
|
+
MESSAGE_QUEUE_METRICS.map(
|
|
2095
|
+
(
|
|
2096
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
2097
|
+
): MessageQueueAlertTemplate | null => {
|
|
2098
|
+
return buildTemplate(descriptor);
|
|
2099
|
+
},
|
|
2100
|
+
).filter(
|
|
2101
|
+
(
|
|
2102
|
+
template: MessageQueueAlertTemplate | null,
|
|
2103
|
+
): template is MessageQueueAlertTemplate => {
|
|
2104
|
+
return template !== null;
|
|
2105
|
+
},
|
|
2106
|
+
);
|
|
2107
|
+
|
|
2108
|
+
/**
|
|
2109
|
+
* The template of one catalog entry, or undefined when the entry has none:
|
|
2110
|
+
* where the "Create monitor" link reads its starting threshold, comparison,
|
|
2111
|
+
* severity and window. Keyed by the entry (getMessageQueueMetricId), not the
|
|
2112
|
+
* metric name, which Service Bus and Event Hubs share.
|
|
2113
|
+
*/
|
|
2114
|
+
export function getMessageQueueAlertTemplateForMetric(
|
|
2115
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
2116
|
+
): MessageQueueAlertTemplate | undefined {
|
|
2117
|
+
if (!descriptor) {
|
|
2118
|
+
return undefined;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
const metricId: string = getMessageQueueMetricId(descriptor);
|
|
2122
|
+
|
|
2123
|
+
return ALL_MESSAGE_QUEUE_ALERT_TEMPLATES.find(
|
|
2124
|
+
(template: MessageQueueAlertTemplate): boolean => {
|
|
2125
|
+
return template.metricId === metricId;
|
|
2126
|
+
},
|
|
2127
|
+
);
|
|
2128
|
+
}
|