@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,2311 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getMessagingBrokerScope,
|
|
3
|
+
isExcludedMessagingSystem,
|
|
4
|
+
normalizeMessagingSystem,
|
|
5
|
+
} from "./MessagingSystem";
|
|
6
|
+
import {
|
|
7
|
+
canonicalizeMessageQueueBrokerScope,
|
|
8
|
+
getAzureNamespaceFromHost,
|
|
9
|
+
hasControlCharacter,
|
|
10
|
+
} from "./MessageQueueIdentity";
|
|
11
|
+
import {
|
|
12
|
+
getMessageQueueMetricDescriptorsByName,
|
|
13
|
+
MESSAGE_QUEUE_METRICS,
|
|
14
|
+
MessageQueueMetricDescriptor,
|
|
15
|
+
MessageQueueSignal,
|
|
16
|
+
MESSAGING_SDK_METRIC_SYSTEMS,
|
|
17
|
+
} from "./MessageQueueMetricCatalog";
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* Turning telemetry into a message queue — the one pure gate ingest (which
|
|
21
|
+
* stamps the queue's entity key on the row) and the discovery cron (which
|
|
22
|
+
* creates the queue's row) both run, so the two can never disagree:
|
|
23
|
+
*
|
|
24
|
+
* - resolveMessagingSpan: "which destination does this messaging span
|
|
25
|
+
* publish to, consume from or settle on?";
|
|
26
|
+
* - resolveMessagingMetricDatapoint: "which destination does this broker
|
|
27
|
+
* or messaging-client datapoint describe?".
|
|
28
|
+
*
|
|
29
|
+
* Every messaging attribute is still at stability "development", so real
|
|
30
|
+
* telemetry mixes five naming generations (spec 1.16 and earlier, 1.17-1.20,
|
|
31
|
+
* 1.21-1.25, 1.26-1.27, 1.28 and later), and each concept is read from
|
|
32
|
+
* several keys, newest first. Span NAMES are never parsed: their format
|
|
33
|
+
* flipped in 1.27, and vendors use their own.
|
|
34
|
+
*
|
|
35
|
+
* Conservative on purpose: a wrong destination creates a queue nobody has
|
|
36
|
+
* (a reply queue per request, a consumer tag per restart), so anything
|
|
37
|
+
* temporary, generated or ambiguous resolves to null. The cron reads back
|
|
38
|
+
* exactly MESSAGING_RESOLVER_INPUT_ATTRIBUTES from each stored span and
|
|
39
|
+
* feeds them through the same function, so a key read here and not listed
|
|
40
|
+
* there would split the two — the tests pin that. Values are read the way
|
|
41
|
+
* ClickHouse stores them (see storedText), so resolving a row at ingest and
|
|
42
|
+
* resolving it again from the table give the same answer. Pure and
|
|
43
|
+
* isomorphic; nothing here throws on telemetry input.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
export type MessagingDirection = "publish" | "consume" | "settle" | "unknown";
|
|
47
|
+
|
|
48
|
+
export interface ResolvedMessagingDestination {
|
|
49
|
+
// Canonical system (normalizeMessagingSystem).
|
|
50
|
+
system: string;
|
|
51
|
+
/*
|
|
52
|
+
* The destination for display: original casing kept, the system's
|
|
53
|
+
* normalizers applied (a queue URL reduced to its name, a Pulsar
|
|
54
|
+
* partition folded into its topic, …) and UUIDs templated to `{uuid}`.
|
|
55
|
+
*/
|
|
56
|
+
destination: string;
|
|
57
|
+
/*
|
|
58
|
+
* "" unless the system's broker scope is "azure-namespace": then the
|
|
59
|
+
* lowercased namespace, when an address names one.
|
|
60
|
+
*/
|
|
61
|
+
brokerScope: string;
|
|
62
|
+
// Sanitized host[:port] as reported — display only, never identity.
|
|
63
|
+
brokerAddress: string | null;
|
|
64
|
+
direction: MessagingDirection;
|
|
65
|
+
// Consumer group or subscription — display only, never identity.
|
|
66
|
+
consumerGroup: string | null;
|
|
67
|
+
// The destination is a dead-letter (sub-)queue — display only.
|
|
68
|
+
isDeadLetter: boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type AttributeGetter = (key: string) => unknown;
|
|
72
|
+
|
|
73
|
+
// A destination longer than this, after normalization, is not a queue name.
|
|
74
|
+
export const MESSAGE_QUEUE_DESTINATION_MAX_LENGTH: number = 255;
|
|
75
|
+
|
|
76
|
+
// Display-only values are clamped to this.
|
|
77
|
+
const DISPLAY_VALUE_MAX_LENGTH: number = 255;
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* The presence of ANY of these (non-blank) makes a span or datapoint a
|
|
81
|
+
* messaging candidate. Each is set by at least one instrumentation that
|
|
82
|
+
* sets none of the others: `messaging.destination` by pre-1.17 JS and
|
|
83
|
+
* Python instrumentations, `message_bus.destination` and `az.namespace` by
|
|
84
|
+
* the Azure SDKs' legacy mode, `aws.queue_url` by the .NET AWS
|
|
85
|
+
* instrumentation's legacy mode (no `messaging.system`), `aws.sns.topic.arn`
|
|
86
|
+
* by the Java agent's SNS spans (no `messaging.system` either).
|
|
87
|
+
*/
|
|
88
|
+
export const MESSAGING_TRIGGER_ATTRIBUTES: ReadonlyArray<string> = [
|
|
89
|
+
"messaging.system",
|
|
90
|
+
"messaging.destination.name",
|
|
91
|
+
"messaging.destination",
|
|
92
|
+
"message_bus.destination",
|
|
93
|
+
"az.namespace",
|
|
94
|
+
"azure.resource_provider.namespace",
|
|
95
|
+
"aws.sqs.queue.url",
|
|
96
|
+
"aws.queue_url",
|
|
97
|
+
"aws.sns.topic.arn",
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
export const MESSAGING_SYSTEM_ATTRIBUTE: string = "messaging.system";
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
* The Azure resource provider namespace the Azure SDKs stamp on every span
|
|
104
|
+
* (`az.namespace`; the Java SDK dual-emits the newer name).
|
|
105
|
+
*/
|
|
106
|
+
export const AZURE_RESOURCE_PROVIDER_ATTRIBUTES: ReadonlyArray<string> = [
|
|
107
|
+
"az.namespace",
|
|
108
|
+
"azure.resource_provider.namespace",
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
* The resource provider namespaces, among those keys' values, that name a
|
|
113
|
+
* messaging service — Service Bus's and Event Hubs' — and the system each
|
|
114
|
+
* one is. Compared trimmed and lowercased.
|
|
115
|
+
*/
|
|
116
|
+
const AZURE_PROVIDER_SYSTEMS: ReadonlyMap<string, string> = new Map<
|
|
117
|
+
string,
|
|
118
|
+
string
|
|
119
|
+
>([
|
|
120
|
+
["microsoft.servicebus", "servicebus"],
|
|
121
|
+
["microsoft.eventhub", "eventhubs"],
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Those namespaces, lowercase and sorted: the only values that make an Azure
|
|
126
|
+
* resource provider key a messaging trigger (see isTriggerValue). The
|
|
127
|
+
* discovery cron's span query admits a span on those keys for exactly these
|
|
128
|
+
* values, so it reads the Azure SDK spans ingest keys, and not the Storage,
|
|
129
|
+
* Key Vault or Cosmos DB spans that carry the same keys.
|
|
130
|
+
*/
|
|
131
|
+
export const AZURE_MESSAGING_PROVIDER_NAMESPACES: ReadonlyArray<string> =
|
|
132
|
+
Array.from(AZURE_PROVIDER_SYSTEMS.keys()).sort();
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* The Azure SDKs' legacy DiagnosticSource mode names the service in
|
|
136
|
+
* `component`, next to `message_bus.destination` and `peer.address`.
|
|
137
|
+
*/
|
|
138
|
+
const AZURE_LEGACY_COMPONENT_ATTRIBUTE: string = "component";
|
|
139
|
+
const AZURE_LEGACY_COMPONENT_EVIDENCE: ReadonlyArray<string> = [
|
|
140
|
+
"message_bus.destination",
|
|
141
|
+
"peer.address",
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
export const RPC_SYSTEM_ATTRIBUTES: ReadonlyArray<string> = [
|
|
145
|
+
"rpc.system",
|
|
146
|
+
"rpc.system.name",
|
|
147
|
+
];
|
|
148
|
+
const RPC_SERVICE_ATTRIBUTE: string = "rpc.service";
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* Destination precedence: the low-cardinality template first, then the
|
|
152
|
+
* name, then the consumer-side names of spec 1.17-1.20 (the Azure JS SDK
|
|
153
|
+
* still uses `messaging.source.name` on receive), the pre-1.17 name, and
|
|
154
|
+
* the Azure SDKs' legacy key.
|
|
155
|
+
*/
|
|
156
|
+
export const MESSAGING_DESTINATION_ATTRIBUTES: ReadonlyArray<string> = [
|
|
157
|
+
"messaging.destination.template",
|
|
158
|
+
"messaging.destination.name",
|
|
159
|
+
"messaging.source.template",
|
|
160
|
+
"messaging.source.name",
|
|
161
|
+
"messaging.destination",
|
|
162
|
+
"message_bus.destination",
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* Booleans that mark a destination as temporary or anonymous (the source
|
|
167
|
+
* pair for spec 1.17-1.20 consumers). The pre-1.17
|
|
168
|
+
* `messaging.temp_destination` is deliberately NOT read: Python pika and
|
|
169
|
+
* aio-pika set it on every publish.
|
|
170
|
+
*/
|
|
171
|
+
export const MESSAGING_TEMPORARY_FLAG_ATTRIBUTES: ReadonlyArray<string> = [
|
|
172
|
+
"messaging.destination.temporary",
|
|
173
|
+
"messaging.destination.anonymous",
|
|
174
|
+
"messaging.source.temporary",
|
|
175
|
+
"messaging.source.anonymous",
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
/*
|
|
179
|
+
* Amazon SQS names its queue by URL (last path segment is the name) when no
|
|
180
|
+
* destination key does: the semconv key, then the .NET / Python botocore
|
|
181
|
+
* key.
|
|
182
|
+
*/
|
|
183
|
+
export const SQS_QUEUE_URL_ATTRIBUTES: ReadonlyArray<string> = [
|
|
184
|
+
"aws.sqs.queue.url",
|
|
185
|
+
"aws.queue_url",
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
* Keys that hold an SQS queue URL only in some instrumentations, so they
|
|
190
|
+
* count only when they ARE a URL: `messaging.url` (Python boto3sqs,
|
|
191
|
+
* botocore), `server.address` (Go otelaws from v0.62.0) and `net.peer.name`
|
|
192
|
+
* (Go otelaws up to v0.61.0).
|
|
193
|
+
*/
|
|
194
|
+
const SQS_QUEUE_URL_FALLBACK_ATTRIBUTES: ReadonlyArray<string> = [
|
|
195
|
+
"messaging.url",
|
|
196
|
+
"server.address",
|
|
197
|
+
"net.peer.name",
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
export const SNS_TOPIC_ARN_ATTRIBUTES: ReadonlyArray<string> = [
|
|
201
|
+
"aws.sns.topic.arn",
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
// The routing key a RabbitMQ message was published with (1.17+, then ≤1.16).
|
|
205
|
+
export const RABBITMQ_ROUTING_KEY_ATTRIBUTES: ReadonlyArray<string> = [
|
|
206
|
+
"messaging.rabbitmq.destination.routing_key",
|
|
207
|
+
"messaging.rabbitmq.routing_key",
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
/*
|
|
211
|
+
* RabbitMQ.Client for .NET (v7) stamps `network.protocol.name` "amqp" on
|
|
212
|
+
* every span (a messaging attribute only up to semconv 1.25). It writes the
|
|
213
|
+
* 1.26+ operation keys, yet names the exchange alone, never semconv 1.30's
|
|
214
|
+
* joined `{exchange}:{routing key}:{queue}`; the Java agent's opt-in mode,
|
|
215
|
+
* which does join them, never sets it. See rabbitMqQueue.
|
|
216
|
+
*/
|
|
217
|
+
export const MESSAGING_PROTOCOL_NAME_ATTRIBUTE: string =
|
|
218
|
+
"network.protocol.name";
|
|
219
|
+
const RABBITMQ_DOTNET_CLIENT_PROTOCOL: string = "amqp";
|
|
220
|
+
|
|
221
|
+
export interface MessagingAddressAttribute {
|
|
222
|
+
address: string;
|
|
223
|
+
port: string | null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* Broker address precedence, display only: current, pre-1.21, 1.22+ peer,
|
|
228
|
+
* the socket keys of 1.13-1.21, the Azure legacy key, then `messaging.url`
|
|
229
|
+
* (removed in 1.17 but still emitted: a JSON list of bootstrap servers from
|
|
230
|
+
* kafka-python, a censored AMQP URL from amqplib).
|
|
231
|
+
*/
|
|
232
|
+
export const MESSAGING_ADDRESS_ATTRIBUTES: ReadonlyArray<MessagingAddressAttribute> =
|
|
233
|
+
[
|
|
234
|
+
{ address: "server.address", port: "server.port" },
|
|
235
|
+
{ address: "net.peer.name", port: "net.peer.port" },
|
|
236
|
+
{ address: "network.peer.address", port: "network.peer.port" },
|
|
237
|
+
{ address: "net.sock.peer.addr", port: null },
|
|
238
|
+
{ address: "server.socket.address", port: null },
|
|
239
|
+
{ address: "peer.address", port: null },
|
|
240
|
+
{ address: "messaging.url", port: null },
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
/*
|
|
244
|
+
* Where an Azure namespace-scoped span names its namespace host
|
|
245
|
+
* (`<namespace>.servicebus.windows.net`): .NET ActivitySource mode sets
|
|
246
|
+
* `server.address`, the JS and Python SDKs `net.peer.name`, the legacy mode
|
|
247
|
+
* `peer.address` (as `sb://…/`).
|
|
248
|
+
*/
|
|
249
|
+
export const MESSAGING_BROKER_SCOPE_ADDRESS_ATTRIBUTES: ReadonlyArray<string> =
|
|
250
|
+
["server.address", "net.peer.name", "peer.address", "network.peer.address"];
|
|
251
|
+
|
|
252
|
+
const OPERATION_TYPE_ATTRIBUTE: string = "messaging.operation.type";
|
|
253
|
+
const LEGACY_OPERATION_ATTRIBUTE: string = "messaging.operation";
|
|
254
|
+
const OPERATION_NAME_ATTRIBUTE: string = "messaging.operation.name";
|
|
255
|
+
const RPC_METHOD_ATTRIBUTE: string = "rpc.method";
|
|
256
|
+
|
|
257
|
+
/*
|
|
258
|
+
* Direction evidence, strongest first (span kind sits between the first and
|
|
259
|
+
* the second — see resolveDirection).
|
|
260
|
+
*/
|
|
261
|
+
export const MESSAGING_DIRECTION_ATTRIBUTES: ReadonlyArray<string> = [
|
|
262
|
+
OPERATION_TYPE_ATTRIBUTE,
|
|
263
|
+
LEGACY_OPERATION_ATTRIBUTE,
|
|
264
|
+
OPERATION_NAME_ATTRIBUTE,
|
|
265
|
+
RPC_METHOD_ATTRIBUTE,
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
/*
|
|
269
|
+
* Consumer group / subscription precedence: 1.27+, then the Kafka keys of
|
|
270
|
+
* 1.17-1.26 (still the Java agent's default) and 1.16, RocketMQ's legacy
|
|
271
|
+
* key, Event Hubs', Service Bus's pre-1.27 subscription key, and the key
|
|
272
|
+
* the Azure SDK for Java's Service Bus metrics name the subscription by
|
|
273
|
+
* (azure-core-metrics-opentelemetry's rename of `subscriptionName`). The
|
|
274
|
+
* pre-1.17 `messaging.consumer_id` ("{group} - {client id}") is deliberately
|
|
275
|
+
* not read: it is per client instance, and the discovery cron groups by
|
|
276
|
+
* every key read here.
|
|
277
|
+
*/
|
|
278
|
+
export const MESSAGING_CONSUMER_GROUP_ATTRIBUTES: ReadonlyArray<string> = [
|
|
279
|
+
"messaging.consumer.group.name",
|
|
280
|
+
"messaging.destination.subscription.name",
|
|
281
|
+
"messaging.kafka.consumer.group",
|
|
282
|
+
"messaging.kafka.consumer_group",
|
|
283
|
+
"messaging.rocketmq.client_group",
|
|
284
|
+
"messaging.eventhubs.consumer.group",
|
|
285
|
+
"messaging.servicebus.destination.subscription_name",
|
|
286
|
+
"messaging.servicebus.subscription_name",
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
/*
|
|
290
|
+
* The reporting broker of a curated broker metric, when its source says so:
|
|
291
|
+
* a Prometheus scrape stamps the target it scraped (the Pulsar or RocketMQ
|
|
292
|
+
* broker itself) on the resource. Display only.
|
|
293
|
+
*/
|
|
294
|
+
export const MESSAGE_QUEUE_METRIC_ADDRESS_ATTRIBUTES: ReadonlyArray<MessagingAddressAttribute> =
|
|
295
|
+
[{ address: "resource.server.address", port: "resource.server.port" }];
|
|
296
|
+
|
|
297
|
+
function uniqueKeys(
|
|
298
|
+
lists: ReadonlyArray<ReadonlyArray<string>>,
|
|
299
|
+
): ReadonlyArray<string> {
|
|
300
|
+
const seen: Set<string> = new Set<string>();
|
|
301
|
+
const keys: Array<string> = [];
|
|
302
|
+
for (const list of lists) {
|
|
303
|
+
for (const key of list) {
|
|
304
|
+
if (!seen.has(key)) {
|
|
305
|
+
seen.add(key);
|
|
306
|
+
keys.push(key);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return keys;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function addressKeys(
|
|
314
|
+
attributes: ReadonlyArray<MessagingAddressAttribute>,
|
|
315
|
+
): Array<string> {
|
|
316
|
+
const keys: Array<string> = [];
|
|
317
|
+
for (const attribute of attributes) {
|
|
318
|
+
keys.push(attribute.address);
|
|
319
|
+
if (attribute.port) {
|
|
320
|
+
keys.push(attribute.port);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return keys;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* EVERY attribute key resolveMessagingSpan reads — and so what the discovery
|
|
328
|
+
* cron selects from each stored span and groups by, before handing the row
|
|
329
|
+
* back to the same resolver. Only keys that describe a destination, its
|
|
330
|
+
* broker or the direction: never a per-message or per-instance value
|
|
331
|
+
* (message ids, offsets, partitions, consumer or client ids), which would
|
|
332
|
+
* split one queue's spans into a group per message.
|
|
333
|
+
*/
|
|
334
|
+
export const MESSAGING_RESOLVER_INPUT_ATTRIBUTES: ReadonlyArray<string> =
|
|
335
|
+
uniqueKeys([
|
|
336
|
+
MESSAGING_TRIGGER_ATTRIBUTES,
|
|
337
|
+
[MESSAGING_SYSTEM_ATTRIBUTE],
|
|
338
|
+
AZURE_RESOURCE_PROVIDER_ATTRIBUTES,
|
|
339
|
+
[AZURE_LEGACY_COMPONENT_ATTRIBUTE],
|
|
340
|
+
AZURE_LEGACY_COMPONENT_EVIDENCE,
|
|
341
|
+
RPC_SYSTEM_ATTRIBUTES,
|
|
342
|
+
[RPC_SERVICE_ATTRIBUTE],
|
|
343
|
+
MESSAGING_DESTINATION_ATTRIBUTES,
|
|
344
|
+
MESSAGING_TEMPORARY_FLAG_ATTRIBUTES,
|
|
345
|
+
SQS_QUEUE_URL_ATTRIBUTES,
|
|
346
|
+
SQS_QUEUE_URL_FALLBACK_ATTRIBUTES,
|
|
347
|
+
SNS_TOPIC_ARN_ATTRIBUTES,
|
|
348
|
+
RABBITMQ_ROUTING_KEY_ATTRIBUTES,
|
|
349
|
+
[MESSAGING_PROTOCOL_NAME_ATTRIBUTE],
|
|
350
|
+
addressKeys(MESSAGING_ADDRESS_ATTRIBUTES),
|
|
351
|
+
MESSAGING_BROKER_SCOPE_ADDRESS_ATTRIBUTES,
|
|
352
|
+
MESSAGING_DIRECTION_ATTRIBUTES,
|
|
353
|
+
MESSAGING_CONSUMER_GROUP_ATTRIBUTES,
|
|
354
|
+
]);
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* EVERY attribute key resolveMessagingMetricDatapoint reads to find a
|
|
358
|
+
* datapoint's queue: the span keys (a messaging-client datapoint resolves
|
|
359
|
+
* like a span), plus each curated broker metric's destination, scope and
|
|
360
|
+
* required keys and the reporting broker's address. The discovery cron
|
|
361
|
+
* groups by exactly these.
|
|
362
|
+
*/
|
|
363
|
+
export const MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES: ReadonlyArray<string> =
|
|
364
|
+
uniqueKeys([
|
|
365
|
+
MESSAGING_RESOLVER_INPUT_ATTRIBUTES,
|
|
366
|
+
...MESSAGE_QUEUE_METRICS.map(
|
|
367
|
+
(descriptor: MessageQueueMetricDescriptor): ReadonlyArray<string> => {
|
|
368
|
+
return [
|
|
369
|
+
...descriptor.destinationAttributes,
|
|
370
|
+
...(descriptor.scopeAttributes || []),
|
|
371
|
+
...Object.keys(descriptor.requiredAttributes || {}),
|
|
372
|
+
];
|
|
373
|
+
},
|
|
374
|
+
),
|
|
375
|
+
addressKeys(MESSAGE_QUEUE_METRIC_ADDRESS_ATTRIBUTES),
|
|
376
|
+
]);
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* The keys that mark a curated metric's finer-grained repeat
|
|
380
|
+
* (excludeSeriesWithAttributes): the metric resolver also reads these, but
|
|
381
|
+
* only to DROP a datapoint, and they are deliberately not in
|
|
382
|
+
* MESSAGING_METRIC_RESOLVER_INPUT_ATTRIBUTES. They are per instance
|
|
383
|
+
* (Pulsar's consumer name and id), so grouping the discovery query by them
|
|
384
|
+
* would split a topic's rows per consumer. Leaving them out cannot change
|
|
385
|
+
* which queues discovery finds: Pulsar writes a consumer's copy only inside
|
|
386
|
+
* a subscription whose own series names the same topic.
|
|
387
|
+
*/
|
|
388
|
+
export const MESSAGE_QUEUE_METRIC_EXCLUDED_SERIES_ATTRIBUTES: ReadonlyArray<string> =
|
|
389
|
+
uniqueKeys(
|
|
390
|
+
MESSAGE_QUEUE_METRICS.map(
|
|
391
|
+
(descriptor: MessageQueueMetricDescriptor): ReadonlyArray<string> => {
|
|
392
|
+
return descriptor.excludeSeriesWithAttributes || [];
|
|
393
|
+
},
|
|
394
|
+
),
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
// ---- reading attributes --------------------------------------------------
|
|
398
|
+
|
|
399
|
+
/*
|
|
400
|
+
* Exactly the characters String.prototype.trim removes (ECMAScript
|
|
401
|
+
* WhiteSpace and LineTerminator), so the allocation-free blank check below
|
|
402
|
+
* agrees with readText, which trims.
|
|
403
|
+
*/
|
|
404
|
+
function isWhitespaceCode(code: number): boolean {
|
|
405
|
+
return (
|
|
406
|
+
code === 0x20 ||
|
|
407
|
+
(code >= 0x09 && code <= 0x0d) ||
|
|
408
|
+
code === 0xa0 ||
|
|
409
|
+
code === 0x1680 ||
|
|
410
|
+
(code >= 0x2000 && code <= 0x200a) ||
|
|
411
|
+
code === 0x2028 ||
|
|
412
|
+
code === 0x2029 ||
|
|
413
|
+
code === 0x202f ||
|
|
414
|
+
code === 0x205f ||
|
|
415
|
+
code === 0x3000 ||
|
|
416
|
+
code === 0xfeff
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/*
|
|
421
|
+
* Whether a value is present with something in it, without allocating:
|
|
422
|
+
* a string with a non-whitespace character, a finite number, a boolean, an
|
|
423
|
+
* array (stored as its JSON text, never blank).
|
|
424
|
+
*/
|
|
425
|
+
function isNonBlankValue(value: unknown): boolean {
|
|
426
|
+
if (typeof value === "string") {
|
|
427
|
+
for (let index: number = 0; index < value.length; index++) {
|
|
428
|
+
if (!isWhitespaceCode(value.charCodeAt(index))) {
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
if (typeof value === "number") {
|
|
435
|
+
return Number.isFinite(value);
|
|
436
|
+
}
|
|
437
|
+
if (typeof value === "boolean" || typeof value === "bigint") {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
return Array.isArray(value);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/*
|
|
444
|
+
* The text a value is stored as in a ClickHouse String column — an
|
|
445
|
+
* attribute map's values (Map(String, String)) and the span kind alike: a
|
|
446
|
+
* string as it is, a number or boolean as its String() form, an array as
|
|
447
|
+
* its JSON text — so an ingest-time row (typed values) and the same row read
|
|
448
|
+
* back from the table (strings) resolve alike. Anything else is absent.
|
|
449
|
+
*/
|
|
450
|
+
function storedText(value: unknown): string | null {
|
|
451
|
+
if (typeof value === "string") {
|
|
452
|
+
return value;
|
|
453
|
+
}
|
|
454
|
+
if (typeof value === "number") {
|
|
455
|
+
return Number.isFinite(value) ? String(value) : null;
|
|
456
|
+
}
|
|
457
|
+
if (typeof value === "boolean" || typeof value === "bigint") {
|
|
458
|
+
return String(value);
|
|
459
|
+
}
|
|
460
|
+
if (Array.isArray(value)) {
|
|
461
|
+
try {
|
|
462
|
+
return JSON.stringify(value);
|
|
463
|
+
} catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// The attribute's stored text, trimmed; null when absent or blank.
|
|
471
|
+
function readText(getAttribute: AttributeGetter, key: string): string | null {
|
|
472
|
+
const text: string | null = storedText(getAttribute(key));
|
|
473
|
+
if (text === null) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
const trimmed: string = text.trim();
|
|
477
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// The first of `keys` readText finds, in list order.
|
|
481
|
+
function readFirstText(
|
|
482
|
+
getAttribute: AttributeGetter,
|
|
483
|
+
keys: ReadonlyArray<string>,
|
|
484
|
+
): string | null {
|
|
485
|
+
for (const key of keys) {
|
|
486
|
+
const text: string | null = readText(getAttribute, key);
|
|
487
|
+
if (text !== null) {
|
|
488
|
+
return text;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/*
|
|
495
|
+
* The Azure SDKs stamp their resource provider namespace on EVERY client
|
|
496
|
+
* span — Storage, Cosmos DB and Key Vault as much as Service Bus — so those
|
|
497
|
+
* two trigger keys admit a span only when they name a messaging provider
|
|
498
|
+
* (AZURE_PROVIDER_SYSTEMS). Otherwise every Azure SDK span paid a full
|
|
499
|
+
* resolve for a guaranteed "no queue": about 1 µs a row against 0.07 µs for
|
|
500
|
+
* plain HTTP in the ingest benchmark. The comparison is resolveSystem's own
|
|
501
|
+
* (stored text, trimmed, lowercased), so the gate never refuses a span the
|
|
502
|
+
* resolver would place, and the discovery SQL admits the same values
|
|
503
|
+
* (AZURE_MESSAGING_PROVIDER_NAMESPACES).
|
|
504
|
+
*/
|
|
505
|
+
const AZURE_RESOURCE_PROVIDER_TRIGGERS: ReadonlySet<string> = new Set<string>(
|
|
506
|
+
AZURE_RESOURCE_PROVIDER_ATTRIBUTES,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
// Longer than any provider namespace in AZURE_PROVIDER_SYSTEMS.
|
|
510
|
+
const AZURE_PROVIDER_TRIGGER_MAX_LENGTH: number = 32;
|
|
511
|
+
|
|
512
|
+
function isTriggerValue(key: string, value: unknown): boolean {
|
|
513
|
+
if (!AZURE_RESOURCE_PROVIDER_TRIGGERS.has(key)) {
|
|
514
|
+
return isNonBlankValue(value);
|
|
515
|
+
}
|
|
516
|
+
const text: string | null = storedText(value);
|
|
517
|
+
if (text === null) {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
const trimmed: string = text.trim();
|
|
521
|
+
return (
|
|
522
|
+
trimmed.length <= AZURE_PROVIDER_TRIGGER_MAX_LENGTH &&
|
|
523
|
+
AZURE_PROVIDER_SYSTEMS.has(trimmed.toLowerCase())
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/*
|
|
528
|
+
* Cached once: under Jest's vm context every lookup of the global `Object`
|
|
529
|
+
* goes through the sandbox's interceptors, which made this gate ~20× slower
|
|
530
|
+
* there than in plain Node.
|
|
531
|
+
*/
|
|
532
|
+
const HAS_OWN_PROPERTY: (
|
|
533
|
+
this: Record<string, unknown>,
|
|
534
|
+
key: PropertyKey,
|
|
535
|
+
) => boolean = Object.prototype.hasOwnProperty;
|
|
536
|
+
|
|
537
|
+
function hasTrigger(getAttribute: AttributeGetter): boolean {
|
|
538
|
+
for (
|
|
539
|
+
let index: number = 0;
|
|
540
|
+
index < MESSAGING_TRIGGER_ATTRIBUTES.length;
|
|
541
|
+
index++
|
|
542
|
+
) {
|
|
543
|
+
const key: string | undefined = MESSAGING_TRIGGER_ATTRIBUTES[index];
|
|
544
|
+
if (key !== undefined && isTriggerValue(key, getAttribute(key))) {
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Whether a row's flattened attributes carry any messaging trigger
|
|
553
|
+
* (MESSAGING_TRIGGER_ATTRIBUTES) with a non-blank value — the Azure resource
|
|
554
|
+
* provider keys only when they name Service Bus or Event Hubs — the cheap
|
|
555
|
+
* gate ingest runs on every span before anything else. Own keys only; it
|
|
556
|
+
* never enumerates the map, and allocates only to compare an Azure provider
|
|
557
|
+
* value, so it is safe on every row.
|
|
558
|
+
*/
|
|
559
|
+
export function hasMessagingTrigger(
|
|
560
|
+
attributes: Record<string, unknown> | null | undefined,
|
|
561
|
+
): boolean {
|
|
562
|
+
if (!attributes || typeof attributes !== "object") {
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
for (
|
|
566
|
+
let index: number = 0;
|
|
567
|
+
index < MESSAGING_TRIGGER_ATTRIBUTES.length;
|
|
568
|
+
index++
|
|
569
|
+
) {
|
|
570
|
+
const key: string | undefined = MESSAGING_TRIGGER_ATTRIBUTES[index];
|
|
571
|
+
if (
|
|
572
|
+
key !== undefined &&
|
|
573
|
+
HAS_OWN_PROPERTY.call(attributes, key) &&
|
|
574
|
+
isTriggerValue(key, attributes[key])
|
|
575
|
+
) {
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// ---- span kind -------------------------------------------------------------
|
|
583
|
+
|
|
584
|
+
type SpanKindName = "INTERNAL" | "SERVER" | "CLIENT" | "PRODUCER" | "CONSUMER";
|
|
585
|
+
|
|
586
|
+
/*
|
|
587
|
+
* The stored SpanKind strings ("SPAN_KIND_PRODUCER", …), the bare names,
|
|
588
|
+
* and OTLP's numbers (1 internal … 5 consumer). Compared as literals: the
|
|
589
|
+
* SpanKind enum lives in the Span analytics model, which a Types module does
|
|
590
|
+
* not pull in (the tests pin the two against each other).
|
|
591
|
+
*/
|
|
592
|
+
const SPAN_KIND_BY_NAME: ReadonlyMap<string, SpanKindName> = new Map<
|
|
593
|
+
string,
|
|
594
|
+
SpanKindName
|
|
595
|
+
>([
|
|
596
|
+
["INTERNAL", "INTERNAL"],
|
|
597
|
+
["SERVER", "SERVER"],
|
|
598
|
+
["CLIENT", "CLIENT"],
|
|
599
|
+
["PRODUCER", "PRODUCER"],
|
|
600
|
+
["CONSUMER", "CONSUMER"],
|
|
601
|
+
]);
|
|
602
|
+
|
|
603
|
+
const SPAN_KIND_BY_OTLP_NUMBER: ReadonlyMap<number, SpanKindName> = new Map<
|
|
604
|
+
number,
|
|
605
|
+
SpanKindName
|
|
606
|
+
>([
|
|
607
|
+
[1, "INTERNAL"],
|
|
608
|
+
[2, "SERVER"],
|
|
609
|
+
[3, "CLIENT"],
|
|
610
|
+
[4, "PRODUCER"],
|
|
611
|
+
[5, "CONSUMER"],
|
|
612
|
+
]);
|
|
613
|
+
|
|
614
|
+
const SPAN_KIND_PREFIX: string = "SPAN_KIND_";
|
|
615
|
+
|
|
616
|
+
// One or more ASCII digits, and nothing else.
|
|
617
|
+
function isAsciiDigits(value: string): boolean {
|
|
618
|
+
if (value.length === 0) {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
for (let index: number = 0; index < value.length; index++) {
|
|
622
|
+
const code: number = value.charCodeAt(index);
|
|
623
|
+
if (code < 0x30 || code > 0x39) {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/*
|
|
631
|
+
* A span's kind, read from the text ClickHouse keeps for it (storedText),
|
|
632
|
+
* so that the stamper, which sees the value a row carries, and the
|
|
633
|
+
* discovery cron, which reads the kind column back, read every kind alike.
|
|
634
|
+
* The two can differ: ingest stores the SpanKind string, but a trace
|
|
635
|
+
* pipeline's Span Kind Remapper writes its mapping's kind as it is, and a
|
|
636
|
+
* configuration saved through the API may hold a NUMBER. The stamper then
|
|
637
|
+
* sees 2 while the column keeps the JSON text "2"; read as a name, "2"
|
|
638
|
+
* would be no kind, and the cron would create a queue for SERVER spans
|
|
639
|
+
* ingest never keyed. So a trimmed run of ASCII digits is the OTLP number
|
|
640
|
+
* it names, read as Number() reads it: leading zeros count for nothing
|
|
641
|
+
* ("02" is 2, SERVER, as OtelTracesIngestService.mapSpanKind's parseInt
|
|
642
|
+
* reads an OTLP kind). Any other text is a name ("SPAN_KIND_" optional, any
|
|
643
|
+
* case). A number OTLP does not define, and numeric text that is not plain
|
|
644
|
+
* digits ("-2", "2.5", "+2", "1e+21"), is no kind.
|
|
645
|
+
*/
|
|
646
|
+
function normalizeSpanKind(kind: unknown): SpanKindName | null {
|
|
647
|
+
const text: string | null = storedText(kind);
|
|
648
|
+
if (text === null) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
const trimmed: string = text.trim();
|
|
652
|
+
if (isAsciiDigits(trimmed)) {
|
|
653
|
+
return SPAN_KIND_BY_OTLP_NUMBER.get(Number(trimmed)) || null;
|
|
654
|
+
}
|
|
655
|
+
let name: string = trimmed.toUpperCase();
|
|
656
|
+
if (name.startsWith(SPAN_KIND_PREFIX)) {
|
|
657
|
+
name = name.substring(SPAN_KIND_PREFIX.length);
|
|
658
|
+
}
|
|
659
|
+
return SPAN_KIND_BY_NAME.get(name) || null;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// ---- system ------------------------------------------------------------------
|
|
663
|
+
|
|
664
|
+
const AZURE_LEGACY_COMPONENT_SYSTEMS: ReadonlyMap<string, string> = new Map<
|
|
665
|
+
string,
|
|
666
|
+
string
|
|
667
|
+
>([
|
|
668
|
+
["servicebus", "servicebus"],
|
|
669
|
+
["eventhubs", "eventhubs"],
|
|
670
|
+
]);
|
|
671
|
+
|
|
672
|
+
const AWS_RPC_SYSTEM: string = "aws-api";
|
|
673
|
+
|
|
674
|
+
const AWS_RPC_SERVICE_SYSTEMS: ReadonlyMap<string, string> = new Map<
|
|
675
|
+
string,
|
|
676
|
+
string
|
|
677
|
+
>([
|
|
678
|
+
["sqs", "aws_sqs"],
|
|
679
|
+
["amazonsqs", "aws_sqs"],
|
|
680
|
+
["sns", "aws.sns"],
|
|
681
|
+
["amazonsns", "aws.sns"],
|
|
682
|
+
]);
|
|
683
|
+
|
|
684
|
+
/*
|
|
685
|
+
* Which system, in this order: `messaging.system`; the Azure resource
|
|
686
|
+
* provider namespace; the Azure legacy `component` (only beside its other
|
|
687
|
+
* legacy keys); an AWS SDK RPC span naming SQS or SNS (the Java agent's SNS
|
|
688
|
+
* spans carry no `messaging.system`); the SQS queue URL / SNS topic ARN
|
|
689
|
+
* keys. An excluded `messaging.system` (in-process channels) stops here; a
|
|
690
|
+
* malformed one falls through to the other evidence. Celery sets no
|
|
691
|
+
* messaging system and is not detected (its broker is unknown).
|
|
692
|
+
*/
|
|
693
|
+
function resolveSystem(getAttribute: AttributeGetter): string | null {
|
|
694
|
+
const rawSystem: string | null = readText(
|
|
695
|
+
getAttribute,
|
|
696
|
+
MESSAGING_SYSTEM_ATTRIBUTE,
|
|
697
|
+
);
|
|
698
|
+
if (rawSystem !== null) {
|
|
699
|
+
if (isExcludedMessagingSystem(rawSystem)) {
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
const normalized: string | null = normalizeMessagingSystem(rawSystem);
|
|
703
|
+
if (normalized) {
|
|
704
|
+
return normalized;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const provider: string | null = readFirstText(
|
|
709
|
+
getAttribute,
|
|
710
|
+
AZURE_RESOURCE_PROVIDER_ATTRIBUTES,
|
|
711
|
+
);
|
|
712
|
+
const providerSystem: string | undefined = provider
|
|
713
|
+
? AZURE_PROVIDER_SYSTEMS.get(provider.toLowerCase())
|
|
714
|
+
: undefined;
|
|
715
|
+
if (providerSystem) {
|
|
716
|
+
return providerSystem;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const component: string | null = readText(
|
|
720
|
+
getAttribute,
|
|
721
|
+
AZURE_LEGACY_COMPONENT_ATTRIBUTE,
|
|
722
|
+
);
|
|
723
|
+
const componentSystem: string | undefined = component
|
|
724
|
+
? AZURE_LEGACY_COMPONENT_SYSTEMS.get(component.toLowerCase())
|
|
725
|
+
: undefined;
|
|
726
|
+
if (
|
|
727
|
+
componentSystem &&
|
|
728
|
+
readFirstText(getAttribute, AZURE_LEGACY_COMPONENT_EVIDENCE) !== null
|
|
729
|
+
) {
|
|
730
|
+
return componentSystem;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
const rpcSystem: string | null = readFirstText(
|
|
734
|
+
getAttribute,
|
|
735
|
+
RPC_SYSTEM_ATTRIBUTES,
|
|
736
|
+
);
|
|
737
|
+
if (rpcSystem !== null && rpcSystem.toLowerCase() === AWS_RPC_SYSTEM) {
|
|
738
|
+
const rpcService: string | null = readText(
|
|
739
|
+
getAttribute,
|
|
740
|
+
RPC_SERVICE_ATTRIBUTE,
|
|
741
|
+
);
|
|
742
|
+
const awsSystem: string | undefined = rpcService
|
|
743
|
+
? AWS_RPC_SERVICE_SYSTEMS.get(rpcService.toLowerCase())
|
|
744
|
+
: undefined;
|
|
745
|
+
if (awsSystem) {
|
|
746
|
+
return awsSystem;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (readFirstText(getAttribute, SQS_QUEUE_URL_ATTRIBUTES) !== null) {
|
|
751
|
+
return "aws_sqs";
|
|
752
|
+
}
|
|
753
|
+
if (readFirstText(getAttribute, SNS_TOPIC_ARN_ATTRIBUTES) !== null) {
|
|
754
|
+
return "aws.sns";
|
|
755
|
+
}
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// ---- temporary destinations ------------------------------------------------
|
|
760
|
+
|
|
761
|
+
function isTrueFlag(value: unknown): boolean {
|
|
762
|
+
if (value === true) {
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
return typeof value === "string" && value.trim().toLowerCase() === "true";
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function hasTemporaryFlag(getAttribute: AttributeGetter): boolean {
|
|
769
|
+
for (const key of MESSAGING_TEMPORARY_FLAG_ATTRIBUTES) {
|
|
770
|
+
if (isTrueFlag(getAttribute(key))) {
|
|
771
|
+
return true;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/*
|
|
778
|
+
* Names instrumentations emit INSTEAD of a real destination:
|
|
779
|
+
* - "(temporary)": Java JMS temporary queues (and pika span names);
|
|
780
|
+
* - "(anonymous)": the spec's own placeholder up to 1.26;
|
|
781
|
+
* - "<generated>", "<default>": the Java agent's legacy RabbitMQ names
|
|
782
|
+
* ("<default>" is the default exchange; RabbitMQ resolves it through
|
|
783
|
+
* the routing key before this check);
|
|
784
|
+
* - "unknown": the Java agent's null destination (a Kafka batch spanning
|
|
785
|
+
* several topics) and JS / Python SNS spans without an ARN;
|
|
786
|
+
* - "aws:sqs": the Java agent's legacy Lambda SQS event source;
|
|
787
|
+
* - "-namespaceonlymetric-": Azure Monitor's EntityName for a
|
|
788
|
+
* namespace-level value.
|
|
789
|
+
*/
|
|
790
|
+
const PLACEHOLDER_DESTINATIONS: ReadonlySet<string> = new Set<string>([
|
|
791
|
+
"(temporary)",
|
|
792
|
+
"(anonymous)",
|
|
793
|
+
"<generated>",
|
|
794
|
+
"<default>",
|
|
795
|
+
"unknown",
|
|
796
|
+
"aws:sqs",
|
|
797
|
+
"-namespaceonlymetric-",
|
|
798
|
+
]);
|
|
799
|
+
|
|
800
|
+
/*
|
|
801
|
+
* SNS SMS publishes: the Python botocore instrumentation names the
|
|
802
|
+
* destination "phone_number:**" (the number censored) and its span
|
|
803
|
+
* "phone_number send". Exact values, for SNS only: a Kafka topic or an SQS
|
|
804
|
+
* queue called "phone_numbers" is a real destination.
|
|
805
|
+
*/
|
|
806
|
+
const SNS_PHONE_NUMBER_PLACEHOLDER: string = "phone_number";
|
|
807
|
+
const SNS_PHONE_NUMBER_PLACEHOLDER_PREFIX: string = "phone_number:";
|
|
808
|
+
|
|
809
|
+
/*
|
|
810
|
+
* What OneUptime's own scrubbers leave when they replace a WHOLE value: the
|
|
811
|
+
* trace scrubber's "[REDACTED]" and "[HASHED:<first 8 hex of the SHA-256>]",
|
|
812
|
+
* its mask of a short value ("***"), and a metric pipeline's default
|
|
813
|
+
* "[REDACTED]". Nothing of the name is left, so there is no queue to find
|
|
814
|
+
* (and every scrubbed destination must not merge into one queue). A value
|
|
815
|
+
* scrubbed only in part ("orders-[REDACTED]") keeps its queue: the rest
|
|
816
|
+
* still names it, and every scrubbed variant folds into it, as a templated
|
|
817
|
+
* UUID does.
|
|
818
|
+
*/
|
|
819
|
+
const SCRUBBED_VALUE: string = "[redacted]";
|
|
820
|
+
const SCRUBBED_HASH_PATTERN: RegExp = /^\[hashed:[0-9a-f]+\]$/;
|
|
821
|
+
const MASK_CHARACTER: string = "*";
|
|
822
|
+
|
|
823
|
+
function isScrubbedValue(lower: string): boolean {
|
|
824
|
+
if (lower === SCRUBBED_VALUE || SCRUBBED_HASH_PATTERN.test(lower)) {
|
|
825
|
+
return true;
|
|
826
|
+
}
|
|
827
|
+
for (let index: number = 0; index < lower.length; index++) {
|
|
828
|
+
if (lower.charAt(index) !== MASK_CHARACTER) {
|
|
829
|
+
return false;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return lower.length > 0;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/*
|
|
836
|
+
* JMS temporary destinations as ActiveMQ prints them
|
|
837
|
+
* ("temp-queue://ID:host-1234-…").
|
|
838
|
+
*/
|
|
839
|
+
const JMS_TEMPORARY_PREFIXES: ReadonlyArray<string> = [
|
|
840
|
+
"temp-queue://",
|
|
841
|
+
"temp-topic://",
|
|
842
|
+
];
|
|
843
|
+
|
|
844
|
+
const CANONICAL_UUID_PATTERN: RegExp =
|
|
845
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
846
|
+
|
|
847
|
+
const UUID_SUBSTRING_PATTERN: RegExp =
|
|
848
|
+
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
|
|
849
|
+
|
|
850
|
+
/*
|
|
851
|
+
* The Java agent's generated-RabbitMQ-queue heuristic: server-named
|
|
852
|
+
* `amq.gen-…`, Spring AMQP's `spring.gen-…`, and Spring Cloud Stream's
|
|
853
|
+
* anonymous queues `<destination>.anonymous.<22 base64url characters>`
|
|
854
|
+
* (plus the bare UUID, checked for every system). Direct reply-to uses the
|
|
855
|
+
* `amq.rabbitmq.reply-to` pseudo-queue and `amq.rabbitmq.reply-to.<suffix>`.
|
|
856
|
+
*/
|
|
857
|
+
const RABBITMQ_GENERATED_PREFIXES: ReadonlyArray<string> = [
|
|
858
|
+
"amq.gen-",
|
|
859
|
+
"spring.gen-",
|
|
860
|
+
"amq.rabbitmq.reply-to",
|
|
861
|
+
];
|
|
862
|
+
const RABBITMQ_ANONYMOUS_QUEUE_PATTERN: RegExp =
|
|
863
|
+
/\.anonymous\.[A-Za-z0-9_-]{22}$/;
|
|
864
|
+
|
|
865
|
+
/*
|
|
866
|
+
* MassTransit's per-process endpoints, on each broker it runs on (RabbitMQ,
|
|
867
|
+
* ActiveMQ, Azure Service Bus, Amazon SQS). Every process names its own,
|
|
868
|
+
* deleted once the process is gone, so each start of a process would be a
|
|
869
|
+
* new queue; MassTransit's own diagnostics fold them into "bus",
|
|
870
|
+
* "endpoint", "signalr" and "instance" (its send spans name those, which
|
|
871
|
+
* are left alone: a real queue may be called that). Two shapes, where {id}
|
|
872
|
+
* is a NewId in lowercase z-base-32 (26 characters):
|
|
873
|
+
* - a temporary endpoint (DefaultEndpointNameFormatter
|
|
874
|
+
* .GetTemporaryQueueName), `{machine}_{process}_{tag}_{id}`: the machine
|
|
875
|
+
* and process names cut down to ASCII letters and digits, and the tag
|
|
876
|
+
* "bus" (the bus endpoint, where a request client's responses arrive),
|
|
877
|
+
* "endpoint", "response", "signalr_{hub}" or the application's own;
|
|
878
|
+
* - a service's instance endpoint (InstanceEndpointDefinition),
|
|
879
|
+
* `Instance_{id}`, or `instance_{id}` / `instance-{id}` under the snake-
|
|
880
|
+
* and kebab-case endpoint name formatters.
|
|
881
|
+
* Matched on the lowercased name. MassTransit caps these names at 72
|
|
882
|
+
* characters; a name longer than a queue name may be is not tested, which
|
|
883
|
+
* bounds the pattern's cost on any attribute value.
|
|
884
|
+
*/
|
|
885
|
+
const MASSTRANSIT_TEMPORARY_ENDPOINT_PATTERN: RegExp =
|
|
886
|
+
/^[a-z0-9]+_[a-z0-9]+_.+_[ybndrfg8ejkmcpqxot1uwisza345h769]{26}$/;
|
|
887
|
+
const MASSTRANSIT_INSTANCE_ENDPOINT_PATTERN: RegExp =
|
|
888
|
+
/^instance[_-][ybndrfg8ejkmcpqxot1uwisza345h769]{26}$/;
|
|
889
|
+
|
|
890
|
+
function isMassTransitProcessEndpoint(lower: string): boolean {
|
|
891
|
+
return (
|
|
892
|
+
lower.length <= MESSAGE_QUEUE_DESTINATION_MAX_LENGTH &&
|
|
893
|
+
(MASSTRANSIT_TEMPORARY_ENDPOINT_PATTERN.test(lower) ||
|
|
894
|
+
MASSTRANSIT_INSTANCE_ENDPOINT_PATTERN.test(lower))
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// TIBCO's temporary destinations; ActiveMQ's advisory topics.
|
|
899
|
+
const JMS_GENERATED_PREFIXES: ReadonlyArray<string> = [
|
|
900
|
+
"$tmp$",
|
|
901
|
+
"activemq.advisory.",
|
|
902
|
+
];
|
|
903
|
+
|
|
904
|
+
/*
|
|
905
|
+
* NATS request/reply inboxes (`_INBOX.<id>`; the Java agent's template is
|
|
906
|
+
* the prefix itself) and JetStream acknowledgement subjects (`$JS.ACK.…`,
|
|
907
|
+
* templated as `$JS.ACK`).
|
|
908
|
+
*/
|
|
909
|
+
const NATS_GENERATED_NAMES: ReadonlyArray<string> = ["_inbox", "$js.ack"];
|
|
910
|
+
|
|
911
|
+
// Pulsar's system topics: `__change_events`, `__transaction_*`, `…/__…`.
|
|
912
|
+
const PULSAR_SYSTEM_TOPIC_MARKER: string = "/__";
|
|
913
|
+
const PULSAR_SYSTEM_TOPICS: ReadonlyArray<string> = ["__change_events"];
|
|
914
|
+
const PULSAR_SYSTEM_TOPIC_PREFIXES: ReadonlyArray<string> = ["__transaction_"];
|
|
915
|
+
|
|
916
|
+
// An SNS SMS publish names the phone number (E.164) as its destination.
|
|
917
|
+
const SNS_PHONE_NUMBER_PATTERN: RegExp = /^\+[0-9][0-9 ().-]{4,}$/;
|
|
918
|
+
|
|
919
|
+
function startsWithAny(
|
|
920
|
+
value: string,
|
|
921
|
+
prefixes: ReadonlyArray<string>,
|
|
922
|
+
): boolean {
|
|
923
|
+
for (const prefix of prefixes) {
|
|
924
|
+
if (value.startsWith(prefix)) {
|
|
925
|
+
return true;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return false;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
function isNameOrChild(value: string, names: ReadonlyArray<string>): boolean {
|
|
932
|
+
for (const name of names) {
|
|
933
|
+
if (value === name || value.startsWith(`${name}.`)) {
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Whether a destination name (already normalized for its system) is
|
|
942
|
+
* temporary, generated or a placeholder rather than a queue: the
|
|
943
|
+
* placeholders above, a value a scrubber replaced whole, a bare UUID, a JMS
|
|
944
|
+
* temporary destination, and per system — RabbitMQ's generated and
|
|
945
|
+
* reply-to queues, JMS / ActiveMQ's `$TMP$` and advisory topics,
|
|
946
|
+
* MassTransit's per-process endpoints on the brokers it runs on, NATS
|
|
947
|
+
* inboxes and JetStream ack subjects, Pulsar's system topics, SNS phone
|
|
948
|
+
* numbers and their placeholders. Case-insensitive; true for a blank or
|
|
949
|
+
* non-string name too (never a queue).
|
|
950
|
+
*/
|
|
951
|
+
export function isTemporaryMessagingDestination(
|
|
952
|
+
system: string,
|
|
953
|
+
destination: string,
|
|
954
|
+
): boolean {
|
|
955
|
+
if (typeof destination !== "string") {
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
const value: string = destination.trim();
|
|
959
|
+
if (!value) {
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
const lower: string = value.toLowerCase();
|
|
963
|
+
|
|
964
|
+
if (
|
|
965
|
+
PLACEHOLDER_DESTINATIONS.has(lower) ||
|
|
966
|
+
isScrubbedValue(lower) ||
|
|
967
|
+
CANONICAL_UUID_PATTERN.test(value) ||
|
|
968
|
+
startsWithAny(lower, JMS_TEMPORARY_PREFIXES)
|
|
969
|
+
) {
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
switch (normalizeMessagingSystem(system)) {
|
|
974
|
+
case "rabbitmq":
|
|
975
|
+
return (
|
|
976
|
+
startsWithAny(lower, RABBITMQ_GENERATED_PREFIXES) ||
|
|
977
|
+
RABBITMQ_ANONYMOUS_QUEUE_PATTERN.test(value) ||
|
|
978
|
+
isMassTransitProcessEndpoint(lower)
|
|
979
|
+
);
|
|
980
|
+
case "jms":
|
|
981
|
+
case "activemq":
|
|
982
|
+
return (
|
|
983
|
+
startsWithAny(lower, JMS_GENERATED_PREFIXES) ||
|
|
984
|
+
isMassTransitProcessEndpoint(lower)
|
|
985
|
+
);
|
|
986
|
+
case "servicebus":
|
|
987
|
+
case "aws_sqs":
|
|
988
|
+
return isMassTransitProcessEndpoint(lower);
|
|
989
|
+
case "nats":
|
|
990
|
+
return isNameOrChild(lower, NATS_GENERATED_NAMES);
|
|
991
|
+
case "pulsar":
|
|
992
|
+
return (
|
|
993
|
+
lower.includes(PULSAR_SYSTEM_TOPIC_MARKER) ||
|
|
994
|
+
PULSAR_SYSTEM_TOPICS.includes(lower) ||
|
|
995
|
+
startsWithAny(lower, PULSAR_SYSTEM_TOPIC_PREFIXES)
|
|
996
|
+
);
|
|
997
|
+
case "aws.sns":
|
|
998
|
+
return (
|
|
999
|
+
lower === SNS_PHONE_NUMBER_PLACEHOLDER ||
|
|
1000
|
+
lower.startsWith(SNS_PHONE_NUMBER_PLACEHOLDER_PREFIX) ||
|
|
1001
|
+
SNS_PHONE_NUMBER_PATTERN.test(value)
|
|
1002
|
+
);
|
|
1003
|
+
default:
|
|
1004
|
+
return false;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// ---- per-system destination normalization ----------------------------------
|
|
1009
|
+
|
|
1010
|
+
interface NormalizedDestination {
|
|
1011
|
+
destination: string;
|
|
1012
|
+
consumerGroup: string | null;
|
|
1013
|
+
isDeadLetter: boolean;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// A value naming several destinations at once (a batch): never one queue.
|
|
1017
|
+
function isListText(value: string): boolean {
|
|
1018
|
+
return value.startsWith("[") && value.endsWith("]");
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function hasScheme(value: string): boolean {
|
|
1022
|
+
return value.indexOf("://") > 0;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/*
|
|
1026
|
+
* The last non-empty path segment of a URL (`https://sqs.us-east-1.
|
|
1027
|
+
* amazonaws.com/123456789012/orders` → "orders"), or null for a value that
|
|
1028
|
+
* is not a URL or has no path.
|
|
1029
|
+
*/
|
|
1030
|
+
function lastUrlPathSegment(value: string): string | null {
|
|
1031
|
+
const schemeEnd: number = value.indexOf("://");
|
|
1032
|
+
if (schemeEnd <= 0) {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
let rest: string = value.substring(schemeEnd + 3);
|
|
1036
|
+
for (const terminator of ["?", "#"]) {
|
|
1037
|
+
const index: number = rest.indexOf(terminator);
|
|
1038
|
+
if (index >= 0) {
|
|
1039
|
+
rest = rest.substring(0, index);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
const pathStart: number = rest.indexOf("/");
|
|
1043
|
+
if (pathStart < 0) {
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
const segments: Array<string> = rest
|
|
1047
|
+
.substring(pathStart + 1)
|
|
1048
|
+
.split("/")
|
|
1049
|
+
.filter((segment: string): boolean => {
|
|
1050
|
+
return segment.trim().length > 0;
|
|
1051
|
+
});
|
|
1052
|
+
const last: string | undefined = segments[segments.length - 1];
|
|
1053
|
+
return last ? last.trim() : null;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
const ARN_PREFIX: string = "arn:";
|
|
1057
|
+
|
|
1058
|
+
function isArn(value: string): boolean {
|
|
1059
|
+
return value.toLowerCase().startsWith(ARN_PREFIX);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/*
|
|
1063
|
+
* `arn:partition:service:region:account:resource` → the resource (for an
|
|
1064
|
+
* SQS queue or SNS topic, its name), or null for anything else — a
|
|
1065
|
+
* truncated ARN included. An SNS subscription ARN
|
|
1066
|
+
* (`…:topic:subscription-id`) names its topic.
|
|
1067
|
+
*/
|
|
1068
|
+
function arnResource(value: string): string | null {
|
|
1069
|
+
if (!isArn(value)) {
|
|
1070
|
+
return null;
|
|
1071
|
+
}
|
|
1072
|
+
const parts: Array<string> = value.split(":");
|
|
1073
|
+
const resource: string | undefined = parts[5];
|
|
1074
|
+
return resource && resource.trim() ? resource.trim() : null;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/*
|
|
1078
|
+
* SQS: a queue URL or ARN → the queue name; a plain name stays. An ARN cut
|
|
1079
|
+
* short of its resource names nothing (an SQS name cannot contain ":").
|
|
1080
|
+
*/
|
|
1081
|
+
function sqsQueueName(value: string): string {
|
|
1082
|
+
if (hasScheme(value)) {
|
|
1083
|
+
return lastUrlPathSegment(value) || "";
|
|
1084
|
+
}
|
|
1085
|
+
if (isArn(value)) {
|
|
1086
|
+
return arnResource(value) || "";
|
|
1087
|
+
}
|
|
1088
|
+
return value;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
/*
|
|
1092
|
+
* SNS: a topic ARN → the topic name; a plain name stays. A topic name is
|
|
1093
|
+
* only ASCII letters, digits, "_" and "-" (plus FIFO's ".fifo", per SNS's
|
|
1094
|
+
* CreateTopic), so a truncated ARN names nothing, and a "/" means a
|
|
1095
|
+
* mobile-push platform endpoint or application (one per device), not a
|
|
1096
|
+
* topic — whether the ARN is whole (the Java agent, JS
|
|
1097
|
+
* messaging.destination.name) or cut after its last ":" (Go otelaws, JS
|
|
1098
|
+
* messaging.destination: `endpoint/GCM/<app>/<id>`).
|
|
1099
|
+
*/
|
|
1100
|
+
function snsTopicName(value: string): string {
|
|
1101
|
+
const name: string = isArn(value) ? arnResource(value) || "" : value;
|
|
1102
|
+
return name.includes("/") ? "" : name;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/*
|
|
1106
|
+
* Pub/Sub: a full resource name (`projects/<p>/topics/<t>`,
|
|
1107
|
+
* `projects/<p>/subscriptions/<s>`, optionally after
|
|
1108
|
+
* `//pubsub.googleapis.com/`) → the short id the client libraries report.
|
|
1109
|
+
*/
|
|
1110
|
+
const PUBSUB_RESOURCE_NAME_PATTERN: RegExp =
|
|
1111
|
+
/(?:^|\/)projects\/[^/]+\/(?:topics|subscriptions)\/([^/]+)$/;
|
|
1112
|
+
|
|
1113
|
+
function pubSubResourceId(value: string): string {
|
|
1114
|
+
const match: RegExpExecArray | null =
|
|
1115
|
+
PUBSUB_RESOURCE_NAME_PATTERN.exec(value);
|
|
1116
|
+
return match && match[1] ? match[1] : value;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/*
|
|
1120
|
+
* Service Bus / Event Hubs entity paths, split case-insensitively: the .NET
|
|
1121
|
+
* and JS SDKs say `{topic}/Subscriptions/{sub}` and `/$DeadLetterQueue`,
|
|
1122
|
+
* Java `/subscriptions/` and `/$deadletterqueue`, transfer dead-letter
|
|
1123
|
+
* queues add `/$Transfer/$DeadLetterQueue`, and an Event Hubs receiver's
|
|
1124
|
+
* path is `{hub}/ConsumerGroups/{group}/Partitions/{n}`. The entity (queue,
|
|
1125
|
+
* topic or event hub) is the destination; the subscription or consumer
|
|
1126
|
+
* group is display only, and so is the partition, which is dropped.
|
|
1127
|
+
*/
|
|
1128
|
+
const AZURE_DEAD_LETTER_MARKERS: ReadonlyArray<string> = [
|
|
1129
|
+
"/$transfer/$deadletterqueue",
|
|
1130
|
+
"/$deadletterqueue",
|
|
1131
|
+
];
|
|
1132
|
+
const AZURE_CONSUMER_MARKERS: ReadonlyArray<string> = [
|
|
1133
|
+
"/subscriptions/",
|
|
1134
|
+
"/consumergroups/",
|
|
1135
|
+
];
|
|
1136
|
+
|
|
1137
|
+
function splitAzureEntityPath(value: string): NormalizedDestination {
|
|
1138
|
+
let entity: string = value;
|
|
1139
|
+
let isDeadLetter: boolean = false;
|
|
1140
|
+
let consumerGroup: string | null = null;
|
|
1141
|
+
|
|
1142
|
+
for (const marker of AZURE_DEAD_LETTER_MARKERS) {
|
|
1143
|
+
const index: number = entity.toLowerCase().indexOf(marker);
|
|
1144
|
+
if (index >= 0) {
|
|
1145
|
+
entity = entity.substring(0, index);
|
|
1146
|
+
isDeadLetter = true;
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
for (const marker of AZURE_CONSUMER_MARKERS) {
|
|
1152
|
+
const index: number = entity.toLowerCase().indexOf(marker);
|
|
1153
|
+
if (index >= 0) {
|
|
1154
|
+
const rest: string = entity.substring(index + marker.length);
|
|
1155
|
+
const end: number = rest.indexOf("/");
|
|
1156
|
+
const group: string = (end >= 0 ? rest.substring(0, end) : rest).trim();
|
|
1157
|
+
consumerGroup = group || null;
|
|
1158
|
+
entity = entity.substring(0, index);
|
|
1159
|
+
break;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
return { destination: entity, consumerGroup, isDeadLetter };
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/*
|
|
1167
|
+
* Pulsar: a partition folds into its topic (`orders-partition-3` →
|
|
1168
|
+
* `orders`), and a name without a domain is expanded the way Pulsar itself
|
|
1169
|
+
* does — a short name into the default tenant and namespace
|
|
1170
|
+
* (`persistent://public/default/orders`), `tenant/namespace/topic` into the
|
|
1171
|
+
* persistent domain — so the Java agent's names as given join the brokers'
|
|
1172
|
+
* fully-qualified metric labels.
|
|
1173
|
+
*/
|
|
1174
|
+
const PULSAR_PARTITION_SUFFIX_PATTERN: RegExp = /-partition-\d+$/;
|
|
1175
|
+
|
|
1176
|
+
function pulsarTopic(value: string): string {
|
|
1177
|
+
const topic: string = value.replace(PULSAR_PARTITION_SUFFIX_PATTERN, "");
|
|
1178
|
+
if (topic.includes("://")) {
|
|
1179
|
+
return topic;
|
|
1180
|
+
}
|
|
1181
|
+
if (!topic.includes("/")) {
|
|
1182
|
+
return `persistent://public/default/${topic}`;
|
|
1183
|
+
}
|
|
1184
|
+
const parts: Array<string> = topic.split("/");
|
|
1185
|
+
if (
|
|
1186
|
+
parts.length === 3 &&
|
|
1187
|
+
parts.every((part: string): boolean => {
|
|
1188
|
+
return part.length > 0;
|
|
1189
|
+
})
|
|
1190
|
+
) {
|
|
1191
|
+
return `persistent://${topic}`;
|
|
1192
|
+
}
|
|
1193
|
+
return topic;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// JMS / ActiveMQ: "queue://orders" and "topic://orders" name "orders".
|
|
1197
|
+
const JMS_DESTINATION_PREFIXES: ReadonlyArray<string> = [
|
|
1198
|
+
"queue://",
|
|
1199
|
+
"topic://",
|
|
1200
|
+
];
|
|
1201
|
+
|
|
1202
|
+
function stripJmsPrefix(value: string): string {
|
|
1203
|
+
const lower: string = value.toLowerCase();
|
|
1204
|
+
for (const prefix of JMS_DESTINATION_PREFIXES) {
|
|
1205
|
+
if (lower.startsWith(prefix)) {
|
|
1206
|
+
return value.substring(prefix.length);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return value;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// ActiveMQ's shared DLQ, and the individual ones' default `DLQ.` prefix.
|
|
1213
|
+
function isActiveMqDeadLetterQueue(value: string): boolean {
|
|
1214
|
+
const lower: string = value.toLowerCase();
|
|
1215
|
+
return lower === "activemq.dlq" || lower.startsWith("dlq.");
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// RocketMQ names a consumer group's dead-letter topic `%DLQ%<group>`.
|
|
1219
|
+
const ROCKETMQ_DEAD_LETTER_PREFIX: string = "%dlq%";
|
|
1220
|
+
|
|
1221
|
+
/*
|
|
1222
|
+
* RabbitMQ's default exchange, as instrumentations spell it: "" (JS
|
|
1223
|
+
* amqplib; absent once stored), "amq.default" (semconv, the .NET client)
|
|
1224
|
+
* and "<default>" (the Java agent's legacy mode).
|
|
1225
|
+
*/
|
|
1226
|
+
const RABBITMQ_DEFAULT_EXCHANGES: ReadonlySet<string> = new Set<string>([
|
|
1227
|
+
"",
|
|
1228
|
+
"amq.default",
|
|
1229
|
+
"<default>",
|
|
1230
|
+
]);
|
|
1231
|
+
|
|
1232
|
+
function isRabbitMqDefaultExchange(value: string): boolean {
|
|
1233
|
+
return RABBITMQ_DEFAULT_EXCHANGES.has(value.trim().toLowerCase());
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/*
|
|
1237
|
+
* What parsing a RabbitMQ name needs to know about the span or datapoint it
|
|
1238
|
+
* came from (see rabbitMqQueue).
|
|
1239
|
+
*/
|
|
1240
|
+
interface RabbitMqNaming {
|
|
1241
|
+
// The routing-key attribute, or null when absent or blank.
|
|
1242
|
+
routingKey: string | null;
|
|
1243
|
+
// It consumes or settles, so it names a queue; else it publishes.
|
|
1244
|
+
consumerSide: boolean;
|
|
1245
|
+
/*
|
|
1246
|
+
* A span, whose instrumentation sets the routing-key attribute whenever
|
|
1247
|
+
* the key is non-empty. A datapoint never carries the key: it is no
|
|
1248
|
+
* metric attribute.
|
|
1249
|
+
*/
|
|
1250
|
+
routingKeyReported: boolean;
|
|
1251
|
+
/*
|
|
1252
|
+
* Whether it is written to the conventions that join names (1.26+ keys,
|
|
1253
|
+
* and not RabbitMQ.Client for .NET) — read only when the name needs it.
|
|
1254
|
+
*/
|
|
1255
|
+
hasJoinedNaming: () => boolean;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// The exchange / routing key / queue parts of a joined name, trimmed.
|
|
1259
|
+
function splitJoinedRabbitMqName(value: string): Array<string> {
|
|
1260
|
+
return value.split(":").map((part: string): string => {
|
|
1261
|
+
return part.trim();
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
/*
|
|
1266
|
+
* aio-pika's publish name `{exchange},{routing key}` (it sets no routing-key
|
|
1267
|
+
* attribute): the exchange, or the routing key when the exchange is the
|
|
1268
|
+
* default one. Anything else with a comma is a name as given.
|
|
1269
|
+
*/
|
|
1270
|
+
function rabbitMqCommaName(value: string, routingKey: string | null): string {
|
|
1271
|
+
const parts: Array<string> = value.split(",");
|
|
1272
|
+
if (parts.length !== 2) {
|
|
1273
|
+
return value;
|
|
1274
|
+
}
|
|
1275
|
+
const exchange: string = (parts[0] || "").trim();
|
|
1276
|
+
const key: string = (parts[1] || "").trim();
|
|
1277
|
+
if (routingKey !== null && key.length > 0 && key !== routingKey) {
|
|
1278
|
+
return value;
|
|
1279
|
+
}
|
|
1280
|
+
return isRabbitMqDefaultExchange(exchange)
|
|
1281
|
+
? key || routingKey || ""
|
|
1282
|
+
: exchange;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/*
|
|
1286
|
+
* Whether a three-part RabbitMQ name, `middle` its second part, is semconv
|
|
1287
|
+
* 1.30's consumer name `{exchange}:{routing key}:{queue}`, so its third part
|
|
1288
|
+
* is the queue. Only a consumer's or a settlement's can be. With the
|
|
1289
|
+
* routing-key attribute, the routing key must be the middle part. Without
|
|
1290
|
+
* it, the name is also an exchange named with a colon, its empty routing
|
|
1291
|
+
* key left out (`Namespace:Type:{queue}`), when it is written to the joining
|
|
1292
|
+
* conventions (hasJoinedNaming) — a datapoint's as much as a span's. One
|
|
1293
|
+
* that is not is one name, as the same consumer's span is read: the older
|
|
1294
|
+
* conventions name the exchange, MassTransit's Fault<T> exchange
|
|
1295
|
+
* (`MassTransit:Fault--Namespace:Type--`) among them.
|
|
1296
|
+
*/
|
|
1297
|
+
function isJoinedRabbitMqConsumerName(
|
|
1298
|
+
middle: string,
|
|
1299
|
+
naming: RabbitMqNaming,
|
|
1300
|
+
): boolean {
|
|
1301
|
+
if (!naming.consumerSide) {
|
|
1302
|
+
return false;
|
|
1303
|
+
}
|
|
1304
|
+
if (naming.routingKey !== null) {
|
|
1305
|
+
return naming.routingKey === middle;
|
|
1306
|
+
}
|
|
1307
|
+
return naming.hasJoinedNaming();
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/*
|
|
1311
|
+
* RabbitMQ destinations, where two naming conventions meet:
|
|
1312
|
+
* - most instrumentations put ONE name in the key — the exchange (the
|
|
1313
|
+
* Java agent's default mode, amqplib, pika, RabbitMQ.Client for .NET,
|
|
1314
|
+
* MassTransit's own spans), "" / "<default>" / "amq.default" for the
|
|
1315
|
+
* default exchange (whose routing key IS the queue name), or the
|
|
1316
|
+
* routing key (pika, Spring Rabbit) — and that name may itself contain
|
|
1317
|
+
* ":" (MassTransit names each message type's exchange
|
|
1318
|
+
* `Namespace:Type` and publishes to it with an empty routing key);
|
|
1319
|
+
* - semconv 1.30 joins the parts with ":", leaving empty ones out:
|
|
1320
|
+
* `{exchange}:{routing key}` on a producer, and
|
|
1321
|
+
* `{exchange}:{routing key}:{queue}` on a consumer, which also leaves
|
|
1322
|
+
* the queue out when it equals the routing key. So on a consumer
|
|
1323
|
+
* `direct_logs:warning` is the queue `warning`, and `logs:logs-audit`
|
|
1324
|
+
* (a fanout exchange's empty routing key left out) the queue
|
|
1325
|
+
* `logs-audit`. The Java agent's opt-in mode emits these.
|
|
1326
|
+
* A consumer resolves to its queue, which is what the broker's own metrics
|
|
1327
|
+
* are keyed by; a producer to its exchange (a publisher names no queue).
|
|
1328
|
+
*
|
|
1329
|
+
* Three parts are a joined name only on the consuming side: semconv joins
|
|
1330
|
+
* three nowhere else (a producer's is `{exchange}:{routing key}`). On a
|
|
1331
|
+
* producer, or with no direction, three parts are one exchange name —
|
|
1332
|
+
* MassTransit names a generic message type's exchange, a Fault<T>'s among
|
|
1333
|
+
* them, with two colons (`MassTransit:Fault--Sample.Contracts:SubmitOrder--`)
|
|
1334
|
+
* and publishes to it with an empty routing key. See
|
|
1335
|
+
* isJoinedRabbitMqConsumerName for when a consumer's three parts name its
|
|
1336
|
+
* queue. An empty third part leaves two parts.
|
|
1337
|
+
*
|
|
1338
|
+
* For two parts, the routing-key attribute tells the conventions apart:
|
|
1339
|
+
* semconv requires it whenever the key is non-empty, so a joined name with a
|
|
1340
|
+
* routing key in it comes with the attribute, in the place the joined form
|
|
1341
|
+
* puts it — on a consumer the second part is the queue (after the exchange,
|
|
1342
|
+
* or after the routing key of a default-exchange delivery); on a producer
|
|
1343
|
+
* the first is the exchange when the routing key is the second; otherwise
|
|
1344
|
+
* the name is one name. Without the attribute:
|
|
1345
|
+
* - a producer SPAN's two parts are one exchange name, colon and all;
|
|
1346
|
+
* - a consumer's two parts are `{exchange}:{queue}` only when it is
|
|
1347
|
+
* written to the joining conventions (see RabbitMqNaming), else one
|
|
1348
|
+
* name;
|
|
1349
|
+
* - a DATAPOINT never carries the key, so a producer's two joined parts
|
|
1350
|
+
* split to their exchange on the same condition. Without the key,
|
|
1351
|
+
* `{exchange}:{routing key}` — how the Java agent names every publish
|
|
1352
|
+
* that has one — and an exchange named with a colon, published to with
|
|
1353
|
+
* an empty routing key, look alike; a datapoint takes the first.
|
|
1354
|
+
* A default-exchange marker as the first part (`:orders`,
|
|
1355
|
+
* `amq.default:orders`) can only be a split name: its second part is the
|
|
1356
|
+
* queue. The default exchange alone names the routing key, else nothing.
|
|
1357
|
+
*/
|
|
1358
|
+
function rabbitMqQueue(value: string, naming: RabbitMqNaming): string {
|
|
1359
|
+
const routingKey: string | null = naming.routingKey;
|
|
1360
|
+
if (isRabbitMqDefaultExchange(value)) {
|
|
1361
|
+
return routingKey || "";
|
|
1362
|
+
}
|
|
1363
|
+
if (!value.includes(":")) {
|
|
1364
|
+
return rabbitMqCommaName(value, routingKey);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
let parts: Array<string> = splitJoinedRabbitMqName(value);
|
|
1368
|
+
if (parts.length === 3) {
|
|
1369
|
+
const exchange: string = parts[0] || "";
|
|
1370
|
+
const key: string = parts[1] || "";
|
|
1371
|
+
const queue: string = parts[2] || "";
|
|
1372
|
+
if (queue) {
|
|
1373
|
+
return isJoinedRabbitMqConsumerName(key, naming) ? queue : value;
|
|
1374
|
+
}
|
|
1375
|
+
// `exchange:key:` — no queue part: read what is left as two parts.
|
|
1376
|
+
parts = [exchange, key];
|
|
1377
|
+
}
|
|
1378
|
+
if (parts.length !== 2) {
|
|
1379
|
+
return value;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
const first: string = parts[0] || "";
|
|
1383
|
+
const second: string = parts[1] || "";
|
|
1384
|
+
if (!second) {
|
|
1385
|
+
// `exchange:` names the exchange alone (the default one: its key).
|
|
1386
|
+
return isRabbitMqDefaultExchange(first) ? routingKey || "" : first;
|
|
1387
|
+
}
|
|
1388
|
+
if (isRabbitMqDefaultExchange(first)) {
|
|
1389
|
+
return routingKey === null || routingKey === second ? second : value;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
if (routingKey !== null) {
|
|
1393
|
+
if (naming.consumerSide) {
|
|
1394
|
+
return routingKey === second || routingKey === first ? second : value;
|
|
1395
|
+
}
|
|
1396
|
+
return routingKey === second ? first : value;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
if (naming.consumerSide) {
|
|
1400
|
+
return naming.hasJoinedNaming() ? second : value;
|
|
1401
|
+
}
|
|
1402
|
+
if (naming.routingKeyReported) {
|
|
1403
|
+
return value;
|
|
1404
|
+
}
|
|
1405
|
+
return naming.hasJoinedNaming() ? first : value;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
function normalizeDestinationForSystem(
|
|
1409
|
+
system: string,
|
|
1410
|
+
raw: string,
|
|
1411
|
+
// RabbitMQ only; null when the value is a name as given (see rabbitMqQueue).
|
|
1412
|
+
rabbitMqNaming: RabbitMqNaming | null,
|
|
1413
|
+
): NormalizedDestination | null {
|
|
1414
|
+
let value: string = raw.trim();
|
|
1415
|
+
if (!value || isListText(value) || isScrubbedValue(value.toLowerCase())) {
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
let consumerGroup: string | null = null;
|
|
1420
|
+
let isDeadLetter: boolean = false;
|
|
1421
|
+
|
|
1422
|
+
switch (system) {
|
|
1423
|
+
case "rabbitmq":
|
|
1424
|
+
if (rabbitMqNaming) {
|
|
1425
|
+
value = rabbitMqQueue(value, rabbitMqNaming);
|
|
1426
|
+
}
|
|
1427
|
+
break;
|
|
1428
|
+
case "aws_sqs":
|
|
1429
|
+
value = sqsQueueName(value);
|
|
1430
|
+
break;
|
|
1431
|
+
case "aws.sns":
|
|
1432
|
+
value = snsTopicName(value);
|
|
1433
|
+
break;
|
|
1434
|
+
case "gcp_pubsub":
|
|
1435
|
+
value = pubSubResourceId(value);
|
|
1436
|
+
break;
|
|
1437
|
+
case "servicebus":
|
|
1438
|
+
case "eventhubs": {
|
|
1439
|
+
const path: NormalizedDestination = splitAzureEntityPath(value);
|
|
1440
|
+
value = path.destination;
|
|
1441
|
+
consumerGroup = path.consumerGroup;
|
|
1442
|
+
isDeadLetter = path.isDeadLetter;
|
|
1443
|
+
break;
|
|
1444
|
+
}
|
|
1445
|
+
case "pulsar":
|
|
1446
|
+
value = pulsarTopic(value);
|
|
1447
|
+
break;
|
|
1448
|
+
case "activemq":
|
|
1449
|
+
case "jms":
|
|
1450
|
+
value = stripJmsPrefix(value);
|
|
1451
|
+
isDeadLetter = isActiveMqDeadLetterQueue(value.trim());
|
|
1452
|
+
break;
|
|
1453
|
+
case "rocketmq":
|
|
1454
|
+
isDeadLetter = value
|
|
1455
|
+
.toLowerCase()
|
|
1456
|
+
.startsWith(ROCKETMQ_DEAD_LETTER_PREFIX);
|
|
1457
|
+
break;
|
|
1458
|
+
default:
|
|
1459
|
+
break;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
value = value.trim();
|
|
1463
|
+
if (!value) {
|
|
1464
|
+
return null;
|
|
1465
|
+
}
|
|
1466
|
+
return { destination: value, consumerGroup, isDeadLetter };
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// Each UUID → "{uuid}", so a per-request queue name keys one queue.
|
|
1470
|
+
function templateDestination(destination: string): string {
|
|
1471
|
+
return destination.replace(UUID_SUBSTRING_PATTERN, "{uuid}");
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
/*
|
|
1475
|
+
* A display-only value (broker address, consumer group), trimmed and
|
|
1476
|
+
* clamped; null when blank, and when it holds a control character it
|
|
1477
|
+
* could not be stored or shown with.
|
|
1478
|
+
*/
|
|
1479
|
+
function clampDisplay(value: string | null): string | null {
|
|
1480
|
+
if (value === null) {
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
const trimmed: string = value.trim();
|
|
1484
|
+
if (!trimmed || hasControlCharacter(trimmed)) {
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
return trimmed.length > DISPLAY_VALUE_MAX_LENGTH
|
|
1488
|
+
? trimmed.substring(0, DISPLAY_VALUE_MAX_LENGTH)
|
|
1489
|
+
: trimmed;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// ---- destination selection ---------------------------------------------------
|
|
1493
|
+
|
|
1494
|
+
interface SelectedDestination {
|
|
1495
|
+
value: string;
|
|
1496
|
+
// False for a bare routing key: it is already a queue name.
|
|
1497
|
+
parseRabbitMqAddressing: boolean;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
/*
|
|
1501
|
+
* The FIRST present destination key wins — a placeholder there means no
|
|
1502
|
+
* queue, never "try the next key" — then, only when none is present, the
|
|
1503
|
+
* system's own keys: SQS queue URLs, the SNS topic ARN, and RabbitMQ's
|
|
1504
|
+
* routing key (no destination at all is the default exchange, whose routing
|
|
1505
|
+
* key is the queue name).
|
|
1506
|
+
*/
|
|
1507
|
+
function selectDestination(
|
|
1508
|
+
system: string,
|
|
1509
|
+
getAttribute: AttributeGetter,
|
|
1510
|
+
): SelectedDestination | null {
|
|
1511
|
+
const generic: string | null = readFirstText(
|
|
1512
|
+
getAttribute,
|
|
1513
|
+
MESSAGING_DESTINATION_ATTRIBUTES,
|
|
1514
|
+
);
|
|
1515
|
+
if (generic !== null) {
|
|
1516
|
+
return { value: generic, parseRabbitMqAddressing: true };
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
if (system === "aws_sqs") {
|
|
1520
|
+
const queueUrl: string | null = readFirstText(
|
|
1521
|
+
getAttribute,
|
|
1522
|
+
SQS_QUEUE_URL_ATTRIBUTES,
|
|
1523
|
+
);
|
|
1524
|
+
if (queueUrl !== null) {
|
|
1525
|
+
return { value: queueUrl, parseRabbitMqAddressing: false };
|
|
1526
|
+
}
|
|
1527
|
+
for (const key of SQS_QUEUE_URL_FALLBACK_ATTRIBUTES) {
|
|
1528
|
+
const candidate: string | null = readText(getAttribute, key);
|
|
1529
|
+
if (
|
|
1530
|
+
candidate !== null &&
|
|
1531
|
+
hasScheme(candidate) &&
|
|
1532
|
+
lastUrlPathSegment(candidate) !== null
|
|
1533
|
+
) {
|
|
1534
|
+
return { value: candidate, parseRabbitMqAddressing: false };
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
return null;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
if (system === "aws.sns") {
|
|
1541
|
+
const topicArn: string | null = readFirstText(
|
|
1542
|
+
getAttribute,
|
|
1543
|
+
SNS_TOPIC_ARN_ATTRIBUTES,
|
|
1544
|
+
);
|
|
1545
|
+
return topicArn !== null
|
|
1546
|
+
? { value: topicArn, parseRabbitMqAddressing: false }
|
|
1547
|
+
: null;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
if (system === "rabbitmq") {
|
|
1551
|
+
const routingKey: string | null = readFirstText(
|
|
1552
|
+
getAttribute,
|
|
1553
|
+
RABBITMQ_ROUTING_KEY_ATTRIBUTES,
|
|
1554
|
+
);
|
|
1555
|
+
return routingKey !== null
|
|
1556
|
+
? { value: routingKey, parseRabbitMqAddressing: false }
|
|
1557
|
+
: null;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
return null;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
// ---- addresses -------------------------------------------------------------------
|
|
1564
|
+
|
|
1565
|
+
interface ParsedAddress {
|
|
1566
|
+
host: string;
|
|
1567
|
+
port: string | null;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// A port 1-65535, without leading zeros; null for anything else.
|
|
1571
|
+
function validPort(value: string | null): string | null {
|
|
1572
|
+
if (value === null) {
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
const trimmed: string = value.trim();
|
|
1576
|
+
if (!trimmed || trimmed.length > 5) {
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
for (let index: number = 0; index < trimmed.length; index++) {
|
|
1580
|
+
const code: number = trimmed.charCodeAt(index);
|
|
1581
|
+
if (code < 0x30 || code > 0x39) {
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
const port: number = Number(trimmed);
|
|
1586
|
+
return port >= 1 && port <= 65535 ? String(port) : null;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
function stripQuotes(value: string): string {
|
|
1590
|
+
let text: string = value.trim();
|
|
1591
|
+
const first: string = text.charAt(0);
|
|
1592
|
+
if ((first === '"' || first === "'") && text.endsWith(first)) {
|
|
1593
|
+
text = text.substring(1, text.length - 1);
|
|
1594
|
+
}
|
|
1595
|
+
return text.trim();
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// `["b1:9092", "b2:9092"]` (JSON) or `['b1:9092']` (Python) — not `[::1]`.
|
|
1599
|
+
function isAddressList(value: string): boolean {
|
|
1600
|
+
if (!value.startsWith("[")) {
|
|
1601
|
+
return false;
|
|
1602
|
+
}
|
|
1603
|
+
const next: string = value.substring(1).trim().charAt(0);
|
|
1604
|
+
return next === '"' || next === "'" || next === "]";
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
/*
|
|
1608
|
+
* host[:port] out of whatever an instrumentation put in an address key: a
|
|
1609
|
+
* JSON or Python list of bootstrap servers (the first), a comma-separated
|
|
1610
|
+
* list (the first), a URL (its authority, credentials stripped:
|
|
1611
|
+
* `amqp://user:***@rabbit:5672/vhost` → `rabbit:5672`), `host:port/path`,
|
|
1612
|
+
* a bracketed IPv6 literal.
|
|
1613
|
+
*/
|
|
1614
|
+
function parseAddress(raw: string): ParsedAddress | null {
|
|
1615
|
+
let value: string = raw.trim();
|
|
1616
|
+
|
|
1617
|
+
if (isAddressList(value)) {
|
|
1618
|
+
const inner: string = value.substring(
|
|
1619
|
+
1,
|
|
1620
|
+
value.endsWith("]") ? value.length - 1 : value.length,
|
|
1621
|
+
);
|
|
1622
|
+
value = stripQuotes(inner.split(",")[0] || "");
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
const schemeEnd: number = value.indexOf("://");
|
|
1626
|
+
if (schemeEnd > 0) {
|
|
1627
|
+
value = value.substring(schemeEnd + 3);
|
|
1628
|
+
for (const terminator of ["/", "?", "#"]) {
|
|
1629
|
+
const index: number = value.indexOf(terminator);
|
|
1630
|
+
if (index >= 0) {
|
|
1631
|
+
value = value.substring(0, index);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
} else {
|
|
1635
|
+
if (value.includes(",")) {
|
|
1636
|
+
value = value.split(",")[0] || "";
|
|
1637
|
+
}
|
|
1638
|
+
const slash: number = value.indexOf("/");
|
|
1639
|
+
if (slash >= 0) {
|
|
1640
|
+
value = value.substring(0, slash);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
const at: number = value.lastIndexOf("@");
|
|
1645
|
+
if (at >= 0) {
|
|
1646
|
+
value = value.substring(at + 1);
|
|
1647
|
+
}
|
|
1648
|
+
value = value.trim();
|
|
1649
|
+
if (!value) {
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
if (value.startsWith("[")) {
|
|
1654
|
+
const close: number = value.indexOf("]");
|
|
1655
|
+
if (close <= 1) {
|
|
1656
|
+
return null;
|
|
1657
|
+
}
|
|
1658
|
+
const rest: string = value.substring(close + 1);
|
|
1659
|
+
return {
|
|
1660
|
+
host: value.substring(0, close + 1),
|
|
1661
|
+
port: rest.startsWith(":") ? validPort(rest.substring(1)) : null,
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
const firstColon: number = value.indexOf(":");
|
|
1666
|
+
if (firstColon >= 0 && firstColon === value.lastIndexOf(":")) {
|
|
1667
|
+
const host: string = value.substring(0, firstColon).trim();
|
|
1668
|
+
return host
|
|
1669
|
+
? { host, port: validPort(value.substring(firstColon + 1)) }
|
|
1670
|
+
: null;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
// No port, or a bare IPv6 literal (several colons).
|
|
1674
|
+
return { host: value, port: null };
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
function formatAddress(host: string, port: string | null): string {
|
|
1678
|
+
if (!port) {
|
|
1679
|
+
return host;
|
|
1680
|
+
}
|
|
1681
|
+
return host.includes(":") && !host.startsWith("[")
|
|
1682
|
+
? `[${host}]:${port}`
|
|
1683
|
+
: `${host}:${port}`;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
function resolveBrokerAddress(
|
|
1687
|
+
getAttribute: AttributeGetter,
|
|
1688
|
+
attributes: ReadonlyArray<MessagingAddressAttribute>,
|
|
1689
|
+
): string | null {
|
|
1690
|
+
for (const attribute of attributes) {
|
|
1691
|
+
const raw: string | null = readText(getAttribute, attribute.address);
|
|
1692
|
+
if (raw === null) {
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
const parsed: ParsedAddress | null = parseAddress(raw);
|
|
1696
|
+
if (!parsed) {
|
|
1697
|
+
continue;
|
|
1698
|
+
}
|
|
1699
|
+
const port: string | null =
|
|
1700
|
+
parsed.port ||
|
|
1701
|
+
(attribute.port
|
|
1702
|
+
? validPort(readText(getAttribute, attribute.port))
|
|
1703
|
+
: null);
|
|
1704
|
+
return clampDisplay(formatAddress(parsed.host, port));
|
|
1705
|
+
}
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
/*
|
|
1710
|
+
* The Azure namespace a namespace-scoped system's span was sent to: the
|
|
1711
|
+
* first address whose host is `<namespace>.servicebus.windows.net` (or a
|
|
1712
|
+
* sovereign cloud's). "" for every other system, and when no address names
|
|
1713
|
+
* a namespace (the local emulator, a custom domain).
|
|
1714
|
+
*/
|
|
1715
|
+
function resolveBrokerScope(
|
|
1716
|
+
system: string,
|
|
1717
|
+
getAttribute: AttributeGetter,
|
|
1718
|
+
): string {
|
|
1719
|
+
if (getMessagingBrokerScope(system) !== "azure-namespace") {
|
|
1720
|
+
return "";
|
|
1721
|
+
}
|
|
1722
|
+
for (const key of MESSAGING_BROKER_SCOPE_ADDRESS_ATTRIBUTES) {
|
|
1723
|
+
const raw: string | null = readText(getAttribute, key);
|
|
1724
|
+
if (raw === null) {
|
|
1725
|
+
continue;
|
|
1726
|
+
}
|
|
1727
|
+
const parsed: ParsedAddress | null = parseAddress(raw);
|
|
1728
|
+
const namespace: string | null = parsed
|
|
1729
|
+
? getAzureNamespaceFromHost(parsed.host)
|
|
1730
|
+
: null;
|
|
1731
|
+
if (namespace) {
|
|
1732
|
+
return namespace;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
return "";
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// ---- direction -------------------------------------------------------------------
|
|
1739
|
+
|
|
1740
|
+
/*
|
|
1741
|
+
* `messaging.operation.type` (1.26+) and the legacy `messaging.operation`:
|
|
1742
|
+
* send / publish / create produce (publish became send in 1.28), receive /
|
|
1743
|
+
* process / deliver consume (deliver was 1.23-1.24's process), settle
|
|
1744
|
+
* acknowledges.
|
|
1745
|
+
*/
|
|
1746
|
+
const OPERATION_TYPE_DIRECTIONS: ReadonlyMap<string, MessagingDirection> =
|
|
1747
|
+
new Map<string, MessagingDirection>([
|
|
1748
|
+
["send", "publish"],
|
|
1749
|
+
["publish", "publish"],
|
|
1750
|
+
["create", "publish"],
|
|
1751
|
+
["receive", "consume"],
|
|
1752
|
+
["process", "consume"],
|
|
1753
|
+
["deliver", "consume"],
|
|
1754
|
+
["settle", "settle"],
|
|
1755
|
+
]);
|
|
1756
|
+
|
|
1757
|
+
// `messaging.operation.name`: system-specific, from the semconv refinements.
|
|
1758
|
+
const OPERATION_NAME_DIRECTIONS: ReadonlyMap<string, MessagingDirection> =
|
|
1759
|
+
new Map<string, MessagingDirection>([
|
|
1760
|
+
["send", "publish"],
|
|
1761
|
+
["publish", "publish"],
|
|
1762
|
+
["schedule", "publish"],
|
|
1763
|
+
["publish_input", "publish"],
|
|
1764
|
+
["publish_batch_input", "publish"],
|
|
1765
|
+
["create", "publish"],
|
|
1766
|
+
["event", "publish"],
|
|
1767
|
+
["poll", "consume"],
|
|
1768
|
+
["receive", "consume"],
|
|
1769
|
+
["fetch", "consume"],
|
|
1770
|
+
["fetch (empty)", "consume"],
|
|
1771
|
+
["peek", "consume"],
|
|
1772
|
+
["receive_deferred", "consume"],
|
|
1773
|
+
["subscribe", "consume"],
|
|
1774
|
+
["deliver", "consume"],
|
|
1775
|
+
["consume", "consume"],
|
|
1776
|
+
["process", "consume"],
|
|
1777
|
+
["ack", "settle"],
|
|
1778
|
+
["nack", "settle"],
|
|
1779
|
+
["modack", "settle"],
|
|
1780
|
+
["reject", "settle"],
|
|
1781
|
+
["complete", "settle"],
|
|
1782
|
+
["abandon", "settle"],
|
|
1783
|
+
["defer", "settle"],
|
|
1784
|
+
["dead_letter", "settle"],
|
|
1785
|
+
["delete", "settle"],
|
|
1786
|
+
["commit", "settle"],
|
|
1787
|
+
["checkpoint", "settle"],
|
|
1788
|
+
["term", "settle"],
|
|
1789
|
+
["nak", "settle"],
|
|
1790
|
+
["in-progress", "settle"],
|
|
1791
|
+
]);
|
|
1792
|
+
|
|
1793
|
+
/*
|
|
1794
|
+
* AWS SDK operations (`rpc.method`; Go writes `SQS/SendMessage`): only for
|
|
1795
|
+
* SQS and SNS spans.
|
|
1796
|
+
*/
|
|
1797
|
+
const AWS_RPC_METHOD_DIRECTIONS: ReadonlyMap<string, MessagingDirection> =
|
|
1798
|
+
new Map<string, MessagingDirection>([
|
|
1799
|
+
["sendmessage", "publish"],
|
|
1800
|
+
["sendmessagebatch", "publish"],
|
|
1801
|
+
["publish", "publish"],
|
|
1802
|
+
["publishbatch", "publish"],
|
|
1803
|
+
["receivemessage", "consume"],
|
|
1804
|
+
["deletemessage", "settle"],
|
|
1805
|
+
["deletemessagebatch", "settle"],
|
|
1806
|
+
["changemessagevisibility", "settle"],
|
|
1807
|
+
["changemessagevisibilitybatch", "settle"],
|
|
1808
|
+
]);
|
|
1809
|
+
|
|
1810
|
+
function lookupDirection(
|
|
1811
|
+
map: ReadonlyMap<string, MessagingDirection>,
|
|
1812
|
+
value: string | null,
|
|
1813
|
+
): MessagingDirection | null {
|
|
1814
|
+
if (value === null) {
|
|
1815
|
+
return null;
|
|
1816
|
+
}
|
|
1817
|
+
return map.get(value.toLowerCase()) || null;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/*
|
|
1821
|
+
* Direction, strongest evidence first: `messaging.operation.type`; the span
|
|
1822
|
+
* kind (PRODUCER publishes, CONSUMER consumes) — ahead of the legacy
|
|
1823
|
+
* `messaging.operation`, which Python confluent-kafka up to v0.62b1 wrote
|
|
1824
|
+
* as "receive" on its PRODUCER spans, and which producers must not set at
|
|
1825
|
+
* all up to spec 1.16; then the legacy key, which some clients fill with an
|
|
1826
|
+
* operation NAME rather than a type (the Google Cloud Pub/Sub clients write
|
|
1827
|
+
* "ack", "modack" and "nack" there: OpenTelemetryPubsubTracer); then
|
|
1828
|
+
* `messaging.operation.name`; and, for SQS / SNS, the AWS SDK operation.
|
|
1829
|
+
*/
|
|
1830
|
+
function resolveDirection(
|
|
1831
|
+
getAttribute: AttributeGetter,
|
|
1832
|
+
system: string,
|
|
1833
|
+
kind: SpanKindName | null,
|
|
1834
|
+
): MessagingDirection {
|
|
1835
|
+
const operationType: MessagingDirection | null = lookupDirection(
|
|
1836
|
+
OPERATION_TYPE_DIRECTIONS,
|
|
1837
|
+
readText(getAttribute, OPERATION_TYPE_ATTRIBUTE),
|
|
1838
|
+
);
|
|
1839
|
+
if (operationType) {
|
|
1840
|
+
return operationType;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
if (kind === "PRODUCER") {
|
|
1844
|
+
return "publish";
|
|
1845
|
+
}
|
|
1846
|
+
if (kind === "CONSUMER") {
|
|
1847
|
+
return "consume";
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
const legacyOperationText: string | null = readText(
|
|
1851
|
+
getAttribute,
|
|
1852
|
+
LEGACY_OPERATION_ATTRIBUTE,
|
|
1853
|
+
);
|
|
1854
|
+
const legacyOperation: MessagingDirection | null =
|
|
1855
|
+
lookupDirection(OPERATION_TYPE_DIRECTIONS, legacyOperationText) ||
|
|
1856
|
+
lookupDirection(OPERATION_NAME_DIRECTIONS, legacyOperationText);
|
|
1857
|
+
if (legacyOperation) {
|
|
1858
|
+
return legacyOperation;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
const operationName: MessagingDirection | null = lookupDirection(
|
|
1862
|
+
OPERATION_NAME_DIRECTIONS,
|
|
1863
|
+
readText(getAttribute, OPERATION_NAME_ATTRIBUTE),
|
|
1864
|
+
);
|
|
1865
|
+
if (operationName) {
|
|
1866
|
+
return operationName;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
if (system === "aws_sqs" || system === "aws.sns") {
|
|
1870
|
+
const method: string | null = readText(getAttribute, RPC_METHOD_ATTRIBUTE);
|
|
1871
|
+
const operation: string | null = method
|
|
1872
|
+
? method.substring(method.lastIndexOf("/") + 1)
|
|
1873
|
+
: null;
|
|
1874
|
+
const awsDirection: MessagingDirection | null = lookupDirection(
|
|
1875
|
+
AWS_RPC_METHOD_DIRECTIONS,
|
|
1876
|
+
operation,
|
|
1877
|
+
);
|
|
1878
|
+
if (awsDirection) {
|
|
1879
|
+
return awsDirection;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
return "unknown";
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// The words of a metric name that say which way its messages go.
|
|
1887
|
+
const METRIC_NAME_PUBLISH_WORDS: ReadonlySet<string> = new Set<string>([
|
|
1888
|
+
"sent",
|
|
1889
|
+
"send",
|
|
1890
|
+
"publish",
|
|
1891
|
+
"published",
|
|
1892
|
+
]);
|
|
1893
|
+
const METRIC_NAME_CONSUME_WORDS: ReadonlySet<string> = new Set<string>([
|
|
1894
|
+
"consumed",
|
|
1895
|
+
"consume",
|
|
1896
|
+
"receive",
|
|
1897
|
+
"received",
|
|
1898
|
+
"receiver",
|
|
1899
|
+
"process",
|
|
1900
|
+
"processed",
|
|
1901
|
+
]);
|
|
1902
|
+
const METRIC_NAME_SETTLE_WORDS: ReadonlySet<string> = new Set<string>([
|
|
1903
|
+
"settle",
|
|
1904
|
+
"settlement",
|
|
1905
|
+
]);
|
|
1906
|
+
const METRIC_NAME_WORD_SEPARATOR: RegExp = /[._/]/;
|
|
1907
|
+
|
|
1908
|
+
/*
|
|
1909
|
+
* messaging.client.sent.messages → publish; messaging.process.duration and
|
|
1910
|
+
* messaging.servicebus.receiver.lag → consume;
|
|
1911
|
+
* messaging.servicebus.settlement.request.duration → settle.
|
|
1912
|
+
*/
|
|
1913
|
+
function directionFromMetricName(metricName: string): MessagingDirection {
|
|
1914
|
+
for (const word of metricName.split(METRIC_NAME_WORD_SEPARATOR)) {
|
|
1915
|
+
if (METRIC_NAME_PUBLISH_WORDS.has(word)) {
|
|
1916
|
+
return "publish";
|
|
1917
|
+
}
|
|
1918
|
+
if (METRIC_NAME_CONSUME_WORDS.has(word)) {
|
|
1919
|
+
return "consume";
|
|
1920
|
+
}
|
|
1921
|
+
if (METRIC_NAME_SETTLE_WORDS.has(word)) {
|
|
1922
|
+
return "settle";
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
return "unknown";
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
function directionFromSignal(signal: MessageQueueSignal): MessagingDirection {
|
|
1929
|
+
if (signal === "published") {
|
|
1930
|
+
return "publish";
|
|
1931
|
+
}
|
|
1932
|
+
if (signal === "consumed") {
|
|
1933
|
+
return "consume";
|
|
1934
|
+
}
|
|
1935
|
+
return "unknown";
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// ---- consumer group ----------------------------------------------------------------
|
|
1939
|
+
|
|
1940
|
+
/*
|
|
1941
|
+
* The consumer group or subscription: the attribute keys, then what the
|
|
1942
|
+
* Azure entity path named, then — for Pub/Sub — the destination itself, as
|
|
1943
|
+
* a consumer's destination IS its subscription.
|
|
1944
|
+
*/
|
|
1945
|
+
function resolveConsumerGroup(data: {
|
|
1946
|
+
getAttribute: AttributeGetter;
|
|
1947
|
+
system: string;
|
|
1948
|
+
direction: MessagingDirection;
|
|
1949
|
+
destination: string;
|
|
1950
|
+
fromPath: string | null;
|
|
1951
|
+
}): string | null {
|
|
1952
|
+
const fromAttribute: string | null = readFirstText(
|
|
1953
|
+
data.getAttribute,
|
|
1954
|
+
MESSAGING_CONSUMER_GROUP_ATTRIBUTES,
|
|
1955
|
+
);
|
|
1956
|
+
if (fromAttribute !== null) {
|
|
1957
|
+
return clampDisplay(fromAttribute);
|
|
1958
|
+
}
|
|
1959
|
+
if (data.fromPath !== null) {
|
|
1960
|
+
return clampDisplay(data.fromPath);
|
|
1961
|
+
}
|
|
1962
|
+
if (
|
|
1963
|
+
data.system === "gcp_pubsub" &&
|
|
1964
|
+
(data.direction === "consume" || data.direction === "settle")
|
|
1965
|
+
) {
|
|
1966
|
+
return clampDisplay(data.destination);
|
|
1967
|
+
}
|
|
1968
|
+
return null;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
// ---- resolution ----------------------------------------------------------------------
|
|
1972
|
+
|
|
1973
|
+
/*
|
|
1974
|
+
* A normalized destination's last checks: not temporary, no control
|
|
1975
|
+
* character (see hasControlCharacter), UUIDs templated, not overlong. Null
|
|
1976
|
+
* when it is no queue.
|
|
1977
|
+
*/
|
|
1978
|
+
function finishDestination(
|
|
1979
|
+
system: string,
|
|
1980
|
+
normalized: NormalizedDestination | null,
|
|
1981
|
+
): string | null {
|
|
1982
|
+
if (
|
|
1983
|
+
!normalized ||
|
|
1984
|
+
hasControlCharacter(normalized.destination) ||
|
|
1985
|
+
isTemporaryMessagingDestination(system, normalized.destination)
|
|
1986
|
+
) {
|
|
1987
|
+
return null;
|
|
1988
|
+
}
|
|
1989
|
+
const destination: string = templateDestination(normalized.destination);
|
|
1990
|
+
return destination.length <= MESSAGE_QUEUE_DESTINATION_MAX_LENGTH
|
|
1991
|
+
? destination
|
|
1992
|
+
: null;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
// What is being resolved: a span, or a metric datapoint.
|
|
1996
|
+
type MessagingSignal = "span" | "datapoint";
|
|
1997
|
+
|
|
1998
|
+
/*
|
|
1999
|
+
* The 1.26+ operation keys that show a RabbitMQ span or datapoint is written
|
|
2000
|
+
* to the conventions that join its names, as each signal carries them. A
|
|
2001
|
+
* span, `messaging.operation.type`, which those conventions set on the span
|
|
2002
|
+
* of every messaging operation. A datapoint, the operation name too —
|
|
2003
|
+
* semconv puts the type on
|
|
2004
|
+
* messaging.client.operation.duration alone and the name on all four
|
|
2005
|
+
* messaging client metrics, and so does the Java agent
|
|
2006
|
+
* (MessagingMetricsAdvice), whose sent-messages, consumed-messages and
|
|
2007
|
+
* process-duration datapoints show their conventions by the name alone.
|
|
2008
|
+
* The agent is the one instrumentation found recording RabbitMQ client
|
|
2009
|
+
* metrics (rabbitmq-2.7 and spring-rabbit-1.0, from 2.31.0), and only in
|
|
2010
|
+
* its opt-in mode, which joins names — so its span and every metric of the
|
|
2011
|
+
* same operation key one queue.
|
|
2012
|
+
*/
|
|
2013
|
+
const JOINED_RABBITMQ_NAMING_EVIDENCE: Readonly<
|
|
2014
|
+
Record<MessagingSignal, ReadonlyArray<string>>
|
|
2015
|
+
> = {
|
|
2016
|
+
span: [OPERATION_TYPE_ATTRIBUTE],
|
|
2017
|
+
datapoint: [OPERATION_TYPE_ATTRIBUTE, OPERATION_NAME_ATTRIBUTE],
|
|
2018
|
+
};
|
|
2019
|
+
|
|
2020
|
+
/*
|
|
2021
|
+
* Whether a RabbitMQ span or datapoint is written to the conventions that
|
|
2022
|
+
* join exchange, routing key and queue with ":" (semconv 1.30): it carries
|
|
2023
|
+
* a 1.26+ operation key (JOINED_RABBITMQ_NAMING_EVIDENCE), and it is not
|
|
2024
|
+
* RabbitMQ.Client for .NET's, which writes those keys but names the
|
|
2025
|
+
* exchange alone (see MESSAGING_PROTOCOL_NAME_ATTRIBUTE). The older
|
|
2026
|
+
* conventions — the Java agent's default mode, amqplib, pika, MassTransit's
|
|
2027
|
+
* own spans — name one thing and carry neither key.
|
|
2028
|
+
*/
|
|
2029
|
+
function hasJoinedRabbitMqNaming(
|
|
2030
|
+
getAttribute: AttributeGetter,
|
|
2031
|
+
signal: MessagingSignal,
|
|
2032
|
+
): boolean {
|
|
2033
|
+
if (
|
|
2034
|
+
readFirstText(getAttribute, JOINED_RABBITMQ_NAMING_EVIDENCE[signal]) ===
|
|
2035
|
+
null
|
|
2036
|
+
) {
|
|
2037
|
+
return false;
|
|
2038
|
+
}
|
|
2039
|
+
const protocol: string | null = readText(
|
|
2040
|
+
getAttribute,
|
|
2041
|
+
MESSAGING_PROTOCOL_NAME_ATTRIBUTE,
|
|
2042
|
+
);
|
|
2043
|
+
return (
|
|
2044
|
+
protocol === null ||
|
|
2045
|
+
protocol.toLowerCase() !== RABBITMQ_DOTNET_CLIENT_PROTOCOL
|
|
2046
|
+
);
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// How to read one span's or datapoint's attributes.
|
|
2050
|
+
interface AttributeResolution {
|
|
2051
|
+
kind: SpanKindName | null;
|
|
2052
|
+
// A datapoint's direction as its metric name states it, else "unknown".
|
|
2053
|
+
directionHint: MessagingDirection;
|
|
2054
|
+
/*
|
|
2055
|
+
* A span, which carries RabbitMQ's routing key whenever there is one, or
|
|
2056
|
+
* a datapoint, which never does.
|
|
2057
|
+
*/
|
|
2058
|
+
source: MessagingSignal;
|
|
2059
|
+
/*
|
|
2060
|
+
* The system a metric's name implies (MESSAGING_SDK_METRIC_SYSTEMS), used
|
|
2061
|
+
* only when the datapoint carries no `messaging.system` at all.
|
|
2062
|
+
*/
|
|
2063
|
+
systemHint: string | null;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
function resolveFromAttributes(
|
|
2067
|
+
getAttribute: AttributeGetter,
|
|
2068
|
+
resolution: AttributeResolution,
|
|
2069
|
+
): ResolvedMessagingDestination | null {
|
|
2070
|
+
if (!hasTrigger(getAttribute)) {
|
|
2071
|
+
return null;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
const system: string | null =
|
|
2075
|
+
resolveSystem(getAttribute) ||
|
|
2076
|
+
(resolution.systemHint !== null &&
|
|
2077
|
+
readText(getAttribute, MESSAGING_SYSTEM_ATTRIBUTE) === null
|
|
2078
|
+
? resolution.systemHint
|
|
2079
|
+
: null);
|
|
2080
|
+
if (!system || hasTemporaryFlag(getAttribute)) {
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
const selected: SelectedDestination | null = selectDestination(
|
|
2085
|
+
system,
|
|
2086
|
+
getAttribute,
|
|
2087
|
+
);
|
|
2088
|
+
if (!selected) {
|
|
2089
|
+
return null;
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
const direction: MessagingDirection =
|
|
2093
|
+
resolution.directionHint !== "unknown"
|
|
2094
|
+
? resolution.directionHint
|
|
2095
|
+
: resolveDirection(getAttribute, system, resolution.kind);
|
|
2096
|
+
|
|
2097
|
+
const normalized: NormalizedDestination | null =
|
|
2098
|
+
normalizeDestinationForSystem(
|
|
2099
|
+
system,
|
|
2100
|
+
selected.value,
|
|
2101
|
+
system === "rabbitmq" && selected.parseRabbitMqAddressing
|
|
2102
|
+
? {
|
|
2103
|
+
routingKey: readFirstText(
|
|
2104
|
+
getAttribute,
|
|
2105
|
+
RABBITMQ_ROUTING_KEY_ATTRIBUTES,
|
|
2106
|
+
),
|
|
2107
|
+
consumerSide: direction === "consume" || direction === "settle",
|
|
2108
|
+
routingKeyReported: resolution.source === "span",
|
|
2109
|
+
hasJoinedNaming: (): boolean => {
|
|
2110
|
+
return hasJoinedRabbitMqNaming(getAttribute, resolution.source);
|
|
2111
|
+
},
|
|
2112
|
+
}
|
|
2113
|
+
: null,
|
|
2114
|
+
);
|
|
2115
|
+
const destination: string | null = finishDestination(system, normalized);
|
|
2116
|
+
if (destination === null || !normalized) {
|
|
2117
|
+
return null;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
return {
|
|
2121
|
+
system,
|
|
2122
|
+
destination,
|
|
2123
|
+
brokerScope: resolveBrokerScope(system, getAttribute),
|
|
2124
|
+
brokerAddress: resolveBrokerAddress(
|
|
2125
|
+
getAttribute,
|
|
2126
|
+
MESSAGING_ADDRESS_ATTRIBUTES,
|
|
2127
|
+
),
|
|
2128
|
+
direction,
|
|
2129
|
+
consumerGroup: resolveConsumerGroup({
|
|
2130
|
+
getAttribute,
|
|
2131
|
+
system,
|
|
2132
|
+
direction,
|
|
2133
|
+
destination,
|
|
2134
|
+
fromPath: normalized.consumerGroup,
|
|
2135
|
+
}),
|
|
2136
|
+
isDeadLetter: normalized.isDeadLetter,
|
|
2137
|
+
};
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
/**
|
|
2141
|
+
* The destination a messaging span publishes to, consumes from or settles
|
|
2142
|
+
* on, or null. SERVER spans never resolve (a messaging operation is never
|
|
2143
|
+
* served), and neither does a span with no messaging trigger — checked
|
|
2144
|
+
* first, without allocating, so this is cheap on every span. `kind` is the
|
|
2145
|
+
* span's kind as stored: the SpanKind string ("SPAN_KIND_PRODUCER"), or
|
|
2146
|
+
* whatever a Span Kind Remapper wrote — a bare name, or OTLP's number as a
|
|
2147
|
+
* number or as its digits, read alike (see normalizeSpanKind). SERVER is
|
|
2148
|
+
* refused in every spelling; any other kind only decides the direction.
|
|
2149
|
+
*/
|
|
2150
|
+
export function resolveMessagingSpan(input: {
|
|
2151
|
+
getAttribute: AttributeGetter;
|
|
2152
|
+
kind?: string | number | null | undefined;
|
|
2153
|
+
}): ResolvedMessagingDestination | null {
|
|
2154
|
+
if (!input || typeof input.getAttribute !== "function") {
|
|
2155
|
+
return null;
|
|
2156
|
+
}
|
|
2157
|
+
const kind: SpanKindName | null = normalizeSpanKind(input.kind);
|
|
2158
|
+
if (kind === "SERVER") {
|
|
2159
|
+
return null;
|
|
2160
|
+
}
|
|
2161
|
+
return resolveFromAttributes(input.getAttribute, {
|
|
2162
|
+
kind,
|
|
2163
|
+
directionHint: "unknown",
|
|
2164
|
+
source: "span",
|
|
2165
|
+
systemHint: null,
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
// Whether every required attribute is present with an accepted value.
|
|
2170
|
+
function matchesRequiredAttributes(
|
|
2171
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
2172
|
+
getAttribute: AttributeGetter,
|
|
2173
|
+
): boolean {
|
|
2174
|
+
const required: Readonly<Record<string, ReadonlyArray<string>>> | undefined =
|
|
2175
|
+
descriptor.requiredAttributes;
|
|
2176
|
+
if (!required) {
|
|
2177
|
+
return true;
|
|
2178
|
+
}
|
|
2179
|
+
for (const key of Object.keys(required)) {
|
|
2180
|
+
const accepted: ReadonlyArray<string> | undefined = required[key];
|
|
2181
|
+
const value: string | null = readText(getAttribute, key);
|
|
2182
|
+
if (!accepted || value === null) {
|
|
2183
|
+
return false;
|
|
2184
|
+
}
|
|
2185
|
+
const lower: string = value.toLowerCase();
|
|
2186
|
+
if (
|
|
2187
|
+
!accepted.some((candidate: string): boolean => {
|
|
2188
|
+
return candidate.toLowerCase() === lower;
|
|
2189
|
+
})
|
|
2190
|
+
) {
|
|
2191
|
+
return false;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
return true;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
function resolveCatalogDatapoint(
|
|
2198
|
+
descriptor: MessageQueueMetricDescriptor,
|
|
2199
|
+
getAttribute: AttributeGetter,
|
|
2200
|
+
): ResolvedMessagingDestination | null {
|
|
2201
|
+
if (!matchesRequiredAttributes(descriptor, getAttribute)) {
|
|
2202
|
+
return null;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
// A finer-grained repeat of the metric counts toward no queue.
|
|
2206
|
+
if (
|
|
2207
|
+
descriptor.excludeSeriesWithAttributes &&
|
|
2208
|
+
readFirstText(getAttribute, descriptor.excludeSeriesWithAttributes) !== null
|
|
2209
|
+
) {
|
|
2210
|
+
return null;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
const raw: string | null = readFirstText(
|
|
2214
|
+
getAttribute,
|
|
2215
|
+
descriptor.destinationAttributes,
|
|
2216
|
+
);
|
|
2217
|
+
if (raw === null) {
|
|
2218
|
+
return null;
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2221
|
+
// A broker names its queues exactly: no RabbitMQ exchange:key parsing.
|
|
2222
|
+
const normalized: NormalizedDestination | null =
|
|
2223
|
+
normalizeDestinationForSystem(descriptor.system, raw, null);
|
|
2224
|
+
const destination: string | null = finishDestination(
|
|
2225
|
+
descriptor.system,
|
|
2226
|
+
normalized,
|
|
2227
|
+
);
|
|
2228
|
+
if (destination === null || !normalized) {
|
|
2229
|
+
return null;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
const brokerScope: string =
|
|
2233
|
+
(descriptor.scopeAttributes
|
|
2234
|
+
? canonicalizeMessageQueueBrokerScope(
|
|
2235
|
+
descriptor.system,
|
|
2236
|
+
readFirstText(getAttribute, descriptor.scopeAttributes),
|
|
2237
|
+
)
|
|
2238
|
+
: "") || "";
|
|
2239
|
+
|
|
2240
|
+
return {
|
|
2241
|
+
system: descriptor.system,
|
|
2242
|
+
destination,
|
|
2243
|
+
brokerScope,
|
|
2244
|
+
brokerAddress: resolveBrokerAddress(
|
|
2245
|
+
getAttribute,
|
|
2246
|
+
MESSAGE_QUEUE_METRIC_ADDRESS_ATTRIBUTES,
|
|
2247
|
+
),
|
|
2248
|
+
direction: directionFromSignal(descriptor.signal),
|
|
2249
|
+
consumerGroup: clampDisplay(normalized.consumerGroup),
|
|
2250
|
+
isDeadLetter: normalized.isDeadLetter,
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
/**
|
|
2255
|
+
* The destination a metric datapoint describes, or null. `getAttribute`
|
|
2256
|
+
* reads the STORED flattened map: datapoint keys bare, resource keys
|
|
2257
|
+
* `resource.`-prefixed.
|
|
2258
|
+
*
|
|
2259
|
+
* - A curated broker metric (MESSAGE_QUEUE_METRICS; the name is trimmed and
|
|
2260
|
+
* lowercased first) tries each of its entries: required attributes match,
|
|
2261
|
+
* then the destination (normalized, filtered and templated like a span's)
|
|
2262
|
+
* and the Azure namespace from the entry's keys. The direction follows
|
|
2263
|
+
* the entry's signal. A curated name that none of its entries resolves is
|
|
2264
|
+
* null — it never falls through to the attribute path below.
|
|
2265
|
+
* A datapoint carrying one of the entry's excludeSeriesWithAttributes is
|
|
2266
|
+
* a repeat of a coarser series (Pulsar's per-consumer copy) and is null.
|
|
2267
|
+
* - Anything else resolves like a span without a kind — the messaging
|
|
2268
|
+
* CLIENT metrics, and any application gauge that carries
|
|
2269
|
+
* `messaging.system` and a destination (OneUptime's own BullMQ
|
|
2270
|
+
* `queue.size`) — with the direction the metric name states ("sent",
|
|
2271
|
+
* "consumed", "process", "settlement", …), else the one its attributes
|
|
2272
|
+
* state. A messaging SDK metric that names no system (the Azure SDK for
|
|
2273
|
+
* Java's `messaging.servicebus.*`) gets the one its name implies
|
|
2274
|
+
* (MESSAGING_SDK_METRIC_SYSTEMS). A datapoint never carries RabbitMQ's
|
|
2275
|
+
* routing key, which decides how its joined names split (rabbitMqQueue);
|
|
2276
|
+
* its operation keys show whether they are joined at all
|
|
2277
|
+
* (JOINED_RABBITMQ_NAMING_EVIDENCE).
|
|
2278
|
+
*/
|
|
2279
|
+
export function resolveMessagingMetricDatapoint(input: {
|
|
2280
|
+
metricName: string;
|
|
2281
|
+
getAttribute: AttributeGetter;
|
|
2282
|
+
}): ResolvedMessagingDestination | null {
|
|
2283
|
+
if (!input || typeof input.getAttribute !== "function") {
|
|
2284
|
+
return null;
|
|
2285
|
+
}
|
|
2286
|
+
const metricName: string =
|
|
2287
|
+
typeof input.metricName === "string"
|
|
2288
|
+
? input.metricName.trim().toLowerCase()
|
|
2289
|
+
: "";
|
|
2290
|
+
|
|
2291
|
+
const descriptors: ReadonlyArray<MessageQueueMetricDescriptor> = metricName
|
|
2292
|
+
? getMessageQueueMetricDescriptorsByName(metricName)
|
|
2293
|
+
: [];
|
|
2294
|
+
if (descriptors.length > 0) {
|
|
2295
|
+
for (const descriptor of descriptors) {
|
|
2296
|
+
const resolved: ResolvedMessagingDestination | null =
|
|
2297
|
+
resolveCatalogDatapoint(descriptor, input.getAttribute);
|
|
2298
|
+
if (resolved) {
|
|
2299
|
+
return resolved;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
return null;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
return resolveFromAttributes(input.getAttribute, {
|
|
2306
|
+
kind: null,
|
|
2307
|
+
directionHint: directionFromMetricName(metricName),
|
|
2308
|
+
source: "datapoint",
|
|
2309
|
+
systemHint: MESSAGING_SDK_METRIC_SYSTEMS.get(metricName) || null,
|
|
2310
|
+
});
|
|
2311
|
+
}
|