Project: pro-desktop · Branch: master · Mode: · Model: claude-haiku-4-5-20251001 Stats: 1 user messages, 35 tool calls, tools used: Grep, Glob, Read. Conversation Turn 1 — User Find how Datadog events and metrics are sent in this codebase. Look for: - Existing Datadog event tracking implementations - Patterns for sending events to Datadog - Event naming conventions - Where Datadog client/service is configured - Examples of tracking user/account-level events This will help understand how to implement the new tracking event for TI certificate types. Turn 1 — Assistant I'll search the codebase for Datadog event and metrics implementations. Let me start with a comprehensive search. Tools used: - Grep: datadog|Datadog Tools used: - Glob: /datadog Tools used: - Grep: sendEvent|trackEvent|recordEvent Tool results: → result (ok): Found 54 files tools/skope/src/shared/components/icons/Icons.tsx tools/skope/src/routing/routeTree.gen.ts tools/skope/src/routing/navigation.ts tools/skope/src/features/feature-management/frontend/__tests__/api.test.ts tools/skope/src/features/bug-hunter/frontend/types.ts tools/skope/src/features/bug-hunter/frontend/components/index.ts tools/skope/src/features/bug-hunter/frontend/hooks/useBugHunterProgress.ts tools/skope/src/features/bug-hunter/frontend/components/BugListComponents.tsx tools/sko …(truncated, 2571 more chars) Tool results: → result (ok): Found 16 files src/modules/kbv-mobile-card-reader/renderer/settings/MobileCardReaderView.tsx src/modules/kbv-check-encryption-module/renderer/components/preferences/sections/KbvCheckModule.tsx src/modules/kbv-check-encryption-module/renderer/components/preferences/sections/KbvEncryptionModule.tsx src/modules/gematik-ti-environment/renderer/components/preferences/sections/TiEnvironment.tsx src/core/print/renderer/settings/shared/test/PrintTestFileCard.tsx packages/desktop-bridge/src/client/compat …(truncated, 504 more chars) Tool results: → result (ok): /Users/USER/workspace/doctolib/doctolib-pro-desktop/.git/refs/remotes/origin/FHIR-1702-send-received-payload-on-qes-gematik-to-datadog /Users/USER/workspace/doctolib/doctolib-pro-desktop/.git/logs/refs/remotes/origin/FHIR-1702-send-received-payload-on-qes-gematik-to-datadog /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/services/__tests__/datadogTransport.test.ts /Users/USER/workspace/doctolib/doctolib-pro-desktop/.claude/agents/monitoring/datadog-query.md /Users/USER …(truncated, 730 more chars) Good! I found key files. Let me examine the main Datadog service files: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/services/sendToDatadog.ts Tool results: → result (ok): 1→import { flatten } from 'flat' 2→ 3→import { isDevOrTest } from 'src/constants' 4→ 5→import { getDoctolibWebContents } from '@core/app/main/helpers/webcontents-helper' 6→import { fetch } from '@core/network/services/network' 7→ 8→import { ZipperWebRepository } from '@module/zipper-web/services/zipperWebRepository' 9→import { ZipperDesktopRepo } from '@module/zipper-desktop/services/zipperDesktopRepo' 10→ 11→import { isNodeError } from 'src/utils' …(truncated, 3857 more chars) Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/product-events/services/capturePro.ts Tool results: → result (ok): 1→import { 2→ Conf, 3→ DataCaptureEventPayload, 4→ EventKind, 5→ EventError, 6→ EventPayload, 7→ ClientStore, 8→ EnvironmentType, 9→ capturePro as productCapturePro, 10→ defaults as productEventsDefaults, 11→} from '@doctolib/product-events' 12→ 13→import { isDev, isProd, isStaging } from 'src/constants' 14→ 15→import { createLogger, errorIsIgnorable } from '@core/logger' 16→import { userConsentedToAnalytics } from '@co …(truncated, 16496 more chars) Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/main/ObservabilityQueue.ts Tool results: → result (ok): 1→import { TrackingLabel } from '@core/monitoring/constants/tracking' 2→ 3→import getSystemInformationObservability from '../services/get-system-information' 4→import { Label, ObservabilityTrackingResult } from '../interfaces/tracking' 5→import sendToDatadog from '../services/sendToDatadog' 6→import getFeatureUsageObservability from '../services/get-feature-usage' 7→ 8→const MAX_RETRIES = 5 9→ 10→interface ObservabilityEvent { 11→ trackingLabel: L …(truncated, 3380 more chars) Now let me look for event tracking examples and constants: Tools used: - Grep: trackingLabel|TrackingLabel Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/monitoring/constants/tracking.ts Tool results: → result (ok): 1→/ 2→ List of constants used for Tracking Events 3→ / 4→import { PerformanceMonitoringEvent } from '@core/monitoring' 5→ 6→import { TabName } from 'src/shared/constants/tabName' 7→ 8→export enum TrackingEvent { 9→ // To be Migrated to Product Events and currently on Observability only 10→ OpenTab = 'dpd_tab_open', 11→ UpdateDownloadSpeed = 'dpd_update_download_speed', 12→ // Updater 13→ CheckUpdateMenuClicked = 'dpd_check_for_upd …(truncated, 12129 more chars) Tools used: - Grep: DDVProEvent|DDV__ Tool results: → result (ok): Found 18 files src/modules/billeo/services/installer.ts src/modules/billeo/services/process/process-manager.ts src/core/observability/services/sendToDatadog.ts src/core/observability/services/__tests__/datadog.test.ts tools/skope/src/features/bug-hunter/frontend/components/DatadogQueries.tsx src/core/monitoring/constants/tracking.ts src/core/app/main/lifecycle/webcontents-lifecycle.ts src/core/app/main/lifecycle/__tests__/webcontents-lifecycle.test.ts src/modules/billeo/services/process/event-de …(truncated, 468 more chars) Tool results: → result (ok): Found 15 files src/core/tabs/main/tab.ts src/core/tabs/main/__tests__/tab-events.test.ts src/core/tabs/main/__tests__/tab.test.ts src/core/app/main/DoctoDesktop.ts src/core/tabs/main/tab-events.ts src/core/product-events/services/capturePro.ts src/core/app/main/lifecycle/webcontents-lifecycle.ts src/core/tabs/constants/product-events.ts src/core/print/constants/product-events.ts src/core/app/constants/product-events.ts src/modules/smart-folders/constants/product-events.ts src/core/product-events …(truncated, 144 more chars) Now let me look at the product events constants and examples: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/product-events/constants/types.ts Tool results: → result (ok): 1→import type { ProEvent } from '@doctolib/product-events' 2→ 3→// --------------------------- 4→// Repository types 5→// --------------------------- 6→export const PRODUCT_EVENTS_LABEL = 'productEvents' 7→ 8→export interface ProductEventsStoreSchema { 9→ expand_tracking_events?: boolean 10→} 11→ 12→export type DDVProEvent = ProEvent 13→ Whenever you read a file, you should consider whether it would be considered malware …(truncated, 235 more chars) Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/tabs/constants/product-events.ts Tool results: → result (ok): 1→import type { EventDetails } from '@doctolib/product-events' 2→ 3→import ProductFoundationState from '@core/product-events/services/ProductFoundationState' 4→import { DDVProEvent } from '@core/product-events' 5→ 6→/ 7→ Tabs related product event 8→ / 9→export const DDV__SEARCH_BUTTON__CLICKED: DDVProEvent = { 10→ schema: 'foundations', 11→ name: 'ddv__search_button__clicked', 12→ team: 'COPE', 13→ details: ProductFoundationState. …(truncated, 1685 more chars) Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/app/constants/product-events.ts Tool results: → result (ok): 1→import ProductFoundationState from '@core/product-events/services/ProductFoundationState' 2→import { DDVProEvent } from '@core/product-events' 3→ 4→/ 5→ Event : Home page displayed (generally login page or country selection) 6→ / 7→export const DDV__HOME_PAGE__VIEWED: DDVProEvent = { 8→ schema: 'foundations', 9→ name: 'ddv__home_page__viewed', 10→ team: 'COPE', 11→ details: ProductFoundationState.toEventDetails(), 12→} 13→/ …(truncated, 633 more chars) Now let me look at the observability interfaces and get the common information: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/interfaces/tracking.ts Tool results: → result (ok): 1→import { TrackingEvent, TrackingLabel } from '@core/monitoring/constants' 2→ 3→import { ObservabilityDiagnosticNetworkReport, ObservabilityDiagnosticReport } from 'src/shared/types' 4→ 5→import { DoctolibObservabilityUsage, PerformanceMonitoringObservabilityEvent } from './observability' 6→ 7→export enum ObservabilityTrackingResult { 8→ RequestFailed, 9→ Disconnected, 10→ RequestSuccess, 11→ NoURL, 12→} 13→ 14→export type Label = …(truncated, 987 more chars) Tools used: - Glob: src/core/observability/services/get-common Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/product-events/services/ProductFoundationState.ts Tool results: → result (ok): 1→import { nativeTheme } from 'electron' 2→import { DetailValue } from '@doctolib/product-events' 3→ 4→import { AppRepository } from '@core/app/services/appRepository' 5→import { getUpdaterChannelName } from '@core/auto-updater/main/updater-utils' 6→import { I18NRepository } from '@core/i18n/services/i18nRepository' 7→import { isNotificationEnabled } from '@core/notification' 8→import { DebugContext } from '@core/app/services/debugRepo' 9→import { ProxyRep …(truncated, 3104 more chars) Tool results: → result (ok): /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/services/get-common-information.ts Now let me check the common information and ObservabilityState: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/services/get-common-information.ts Tool results: → result (ok): 1→import { APP_VERSION } from 'src/constants' 2→ 3→import { installerVersion } from '@core/system-information/services/get-os-architecture' 4→import { Country } from '@core/variation-management' 5→import { hostnameInfo, osInfo } from '@core/system-information' 6→ 7→import State from 'src/utils/State' 8→ 9→import { 10→ CommonInfoObservability, 11→ ddvInfoObservability, 12→ DDV_EVENT_TYPE, 13→ DoctolibAccountObservability, 14→ Doctol …(truncated, 4498 more chars) Tools used: - Glob: src/core/observability/main/ObservabilityState Tools used: - Grep: ObservabilityState|userId Tool results: → result (ok): Found 20 files limit: 20, offset: 0 src/modules/padok-integration/renderer/settings/__tests__/PadokSettings.test.tsx src/modules/kbv-ti-connect/services/types.ts src/modules/gematik-kim/services/index.ts src/modules/gematik-kim/services/kim-payloads.ts src/modules/gematik-kim/services/client/email.ts src/core/sentry/services/sentry-transport.ts src/core/observability/services/sendToDatadog.ts src/core/observability/services/__tests__/datadog.test.ts src/core/logger/services/__tests__/logger-tran …(truncated, 701 more chars) Tool results: → result (ok): /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/main/ObservabilityState.ts Let me read the ObservabilityState file: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/main/ObservabilityState.ts Tool results: → result (ok): 1→import { 2→ CommonInfoObservability, 3→ SystemCPUInfoObservability, 4→ SystemMemoryInfoObservability, 5→ SystemRuntimeInfoObservability, 6→ SystemStorageInfoObservability, 7→ updaterObservability, 8→} from '@core/observability/interfaces/observability' 9→import { ProductLineConfig } from '@core/variation-management' 10→import { getAnonymisedHostname } from '@core/system-information/services/get-hostname' 11→ 12→function getAnonymisedUse …(truncated, 1862 more chars) Tools used: - Grep: accountLevel|account_level|certificate.type|ti.certificate|TI.cert Tool results: → result (ok): Found 66 files src/modules/kbv-ti-connect/services/soap-client-helpers.ts src/modules/kbv-ti-connect/services/service-directory-service/__tests__/service-directory-service.test.ts src/modules/kbv-ti-connect/services/mock/mock-certificate.ts src/modules/kbv-ti-connect/services/service-directory-service/__tests__/connectorConfigurationValidator.test.ts src/modules/kbv-ti-connect/services/__tests__/soap-client-helpers.test.ts src/modules/kbv-ti-connect/services/__tests__/ti-connect.test.ts src/modu …(truncated, 3993 more chars) Let me search for TI certificate type tracking and look at how events are currently sent: Tools used: - Grep: ti.certificate.type|certificate_type|TI_CERT|certificateType Tools used: - Grep: trackingLabel\s[=:]|add\s\(\s\{\strackingLabel Tool results: → result (ok): Found 1 file src/modules/kbv-ti-connect/services/server-certificate-helpers.ts Tool results: → result (ok): Found 9 files limit: 30, offset: 0 src/core/observability/services/sendToDatadog.ts src/core/observability/services/__tests__/datadog.test.ts tools/skope/src/features/bug-hunter/frontend/components/DatadogQueries.tsx src/modules/zipper/main/__tests__/install.test.ts src/modules/icanopee-json/services/icanopee-json.test.ts src/modules/icanopee-json/services/icanopee-json.ts src/core/observability/services/__tests__/observabilityQueue.test.ts src/core/observability/main/ObservabilityQueue.ts src/c …(truncated, 26 more chars) Let me look at real examples of how events are currently being tracked: Tools used: - Grep: observabilityQueue\.add|sendTracking|capturePro\( Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/modules/icanopee-json/services/icanopee-json.ts Tool results: → result (ok): 1→import { ChildProcess, fork } from 'node:child_process' 2→import i18n from 'i18next' 3→import { join } from 'node:path' 4→import { v4 as uuid } from 'uuid' 5→ 6→import { errorToString } from '@core/logger' 7→import { TrackingLabel } from '@core/monitoring' 8→import { sendToDatadog } from '@core/observability' 9→import createLogger from '@core/logger/services' 10→ 11→import { isSubdomainProd } from 'src/utils/subdomains' 12→ 13→import { …(truncated, 10289 more chars) Tool results: → result (ok): Found 17 files limit: 30, offset: 0 src/core/tabs/main/tab.ts src/core/print/services/printManager.ts src/core/print/main/handleFileDownloadAndPrint.ts src/core/print/main/index.ts src/core/app/main/DoctoDesktop.ts src/core/tabs/main/tab-events.ts src/core/product-events/services/capturePro.ts src/core/app/main/lifecycle/webcontents-lifecycle.ts src/core/app/main/helpers/__tests__/deeplink-helper.test.ts src/modules/installers/main/installer.ts src/modules/smart-folders/services/folder.ts src/co …(truncated, 245 more chars) Now let me look at how product events are tracked with dynamic details: Tools used: - Grep: capturePro\(\sDDV__.\(|DDV__.=.\(.details Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/tabs/main/tab-events.ts Tool results: → result (ok): 1→import { getTabsManager } from '@core/tabs/main/getTabsManager' 2→import { typeSafeIpcMain } from '@core/type-safe' 3→import { TrackingAction } from '@core/monitoring' 4→import { AppEvent } from '@core/app' 5→import { createLogger } from '@core/logger' 6→import { capturePro } from '@core/product-events' 7→import { ipcManager } from '@core/ipc-manager' 8→ 9→import { DDV__REFRESH_BUTTON__CLICKED } from '../constants/product-events' 10→import { TabChrom …(truncated, 7204 more chars) Tool results: → result (ok): Found 3 files limit: 30, offset: 0 src/core/product-events/services/capturePro.ts src/core/tabs/services/TabCreationService.ts src/core/tabs/services/TabNavigationService.ts Now let me check how events with dynamic details are implemented: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/tabs/services/TabCreationService.ts (1–100) Tool results: → result (ok): 1→import { BrowserWindow } from 'electron' 2→ 3→import { isDev } from 'src/constants' 4→ 5→import { createLogger } from '@core/logger' 6→import { sendCustomTrackingEventToObservability, TrackingAction, TrackingEvent } from '@core/monitoring' 7→import { capturePro } from '@core/product-events' 8→import { handleFileDownloadAndPrint } from '@core/print/main/handleFileDownloadAndPrint' 9→ 10→import { TCSModalListener } from '@module/telehealth' 11→impo …(truncated, 3638 more chars) Tools used: - Grep: sendToDatadog\(|observabilityQueue\.add\( Tool results: → result (ok): Found 5 files limit: 50, offset: 0 src/core/observability/services/sendToDatadog.ts src/core/observability/services/__tests__/datadog.test.ts src/modules/icanopee-json/services/icanopee-json.ts src/core/observability/main/ObservabilityQueue.ts src/core/observability/index.ts Let me check where product events with dynamic details are sent and look at sendCustomTrackingEventToObservability: Tools used: - Grep: sendCustomTrackingEventToObservability Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/tabs/main/tab-utils.ts Tool results: → result (ok): 1→import { EventDetails } from '@doctolib/product-events' 2→ 3→import { getDoctolibURL } from 'src/utils' 4→ 5→import { TabName } from 'src/shared/constants/tabName' 6→ 7→/ 8→ Mapping between pathname & localization key 9→ / 10→const tabTitleMapping = new Map([ 11→ ['activity_page', 'shortcuts.activity'], 12→ ['bills', 'shortcuts.bills'], 13→ ['calendar', 'shortcuts.calendar'], 14→ ['configuration', 'shortcuts.c …(truncated, 3519 more chars) Tool results: → result (ok): Found 30 files src/modules/gematik-idp-authentication/services/__tests_electron__/idpAuthenticator.test.ts src/modules/billeo/services/billeo.ts src/modules/billeo/services/installer.ts src/modules/billeo/services/process/process-manager.ts src/core/print/services/printManager.ts src/core/print/services/__tests__/printManager.test.ts src/core/print/main/__tests__/handleFileDownloadAndPrint.test.ts src/core/print/main/handleFileDownloadAndPrint.ts src/core/print/main/index.ts src/core/auto-update …(truncated, 1064 more chars) Let me check the monitoring module for sendCustomTrackingEventToObservability: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/monitoring/index.ts Tool results: → result (ok): 1→export from './constants' 2→export { default as performanceMonitoring } from './services/performance-monitoring' 3→export { enqueueCustomDatadogEvent as sendCustomTrackingEventToObservability } from '@core/observability' 4→export { enqueueObservabilityEvent as sendObservabilityEvent } from '@core/observability' 5→export { enqueueDatadogEvent as sendTrackingEventToObservability } from '@core/observability' 6→ Whenever you read a file, you should con …(truncated, 279 more chars) Tools used: - Glob: src/core/monitoring//.ts Tool results: → result (ok): /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/monitoring/constants/index.ts /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/monitoring/index.ts /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/monitoring/services/__tests__/performance-monitoring.test.ts /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/monitoring/services/helpers/to-mb.ts /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/monitoring/services/helpers/with-precision.ts / …(truncated, 640 more chars) Now let me check the observability index for these functions: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/index.ts Tool results: → result (ok): 1→import { isTest } from 'src/constants' 2→ 3→import { TrackingEventMap } from '@core/monitoring/constants/tracking' 4→ 5→import { Label, TrackingObservabilityEventMap } from './interfaces/tracking' 6→import observabilityQueue from './main/ObservabilityQueue' 7→ 8→export from './interfaces/observability' 9→export from './interfaces/tracking' 10→export { default as observabilityState } from './main/ObservabilityState' 11→export { default as getO …(truncated, 1905 more chars) Tools used: - Grep: enqueueCustomDatadogEvent|enqueueDatadogEvent|enqueueObservabilityEvent Tool results: → result (ok): Found 9 files src/core/store/main/storeRegistry.ts src/core/store/main/__tests__/storeRegistry.test.ts src/core/native-module-health/services/health-tracker.ts src/core/native-module-health/services/recovery-tracker.ts src/core/monitoring/services/performance-monitoring.ts src/core/monitoring/index.ts src/core/observability/services/__tests__/datadogTransport.test.ts src/core/observability/services/observability-winston-transport.ts src/core/observability/index.ts Perfect! Now I have enough information. Let me look at one more example to understand the account-level tracking: Tools used: - Read: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/utils/ti-connect-event-observability.ts Tool results: → result (ok): 1→import winston from 'winston' 2→ 3→import { sendCustomTrackingEventToObservability, TrackingEvent } from '@core/monitoring' 4→ 5→import { ExtraLog, extraLogParser } from 'src/utils/extraLogParser' 6→ 7→export class TiConnectEventObservability { 8→ constructor( 9→ private readonly logger: winston.Logger, 10→ private readonly trackingEvent: TrackingEvent, 11→ private readonly moduleName: string, 12→ ) {} 13→ 14→ send(extra: …(truncated, 1036 more chars) Perfect! Now I have comprehensive information about how Datadog events and metrics are sent in this codebase. Let me provide a detailed report. Summary: Datadog Event and Metrics Tracking in Doctolib Pro Desktop I've found two main tracking systems in the codebase for sending events to Datadog: 1. Observability Events (sendToDatadog - Direct HTTP to Datadog) Location: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/observability/ Key Components: - sendToDatadog.ts - Main service that sends events directly to Datadog via HTTP POST - ObservabilityQueue.ts - Queues events and sends them with retry logic (max 5 retries, exponential backoff) - ObservabilityState.ts - Manages caching of observability info (userId, account info, product line) Configuration: - URL is retrieved from Doctolib web interface: window.dl_constants?.NGINX_NEWRELIC_EVENT_URL - Uses fetch API to send POST requests - Flattens event structure before sending Event Structure (Observability): // From sendToDatadog.ts - Line 20-23 interface ObservabilityEvent { trackingLabel: string // Event type/label [key: string]: unknown // Dynamic properties } // Sends with common info automatically enriched: { eventType: 'DDV_EVENT_TYPE', ddv: { version, updater, installer }, hostname: anonymisedHostname, doctolib: { account, organization }, os: { version, architecture, platform, release, edition }, zipper: { web, desktop, pms }, trackingLabel: 'YOUR_LABEL', // ... custom event properties } How to Send (Observability): import { sendToDatadog } from '@core/observability' await sendToDatadog({ trackingLabel: TrackingLabel.IcanopeeProcess, source: 'source_name', status: 'OK', command: 'command_name', duration: 1234, // ... other fields }) --- 2. Product Events (capturePro - High-Level Analytics) Location: /Users/USER/workspace/doctolib/doctolib-pro-desktop/src/core/product-events/ Key Components: - capturePro.ts - Main entry point for product events - ProductFoundationState.ts - Caches app configuration state for events - CommonDetailsState.ts - Retrieves common details for enrichment Event Structure (Product Events): // From product-events/constants/types.ts type DDVProEvent = ProEvent // Typical event definition: { schema: 'foundations', // Schema name name: 'ddv__tab_close_button__clicked', // Event name team: 'COPE', // Owning team details: { // Event details auto_updater_enabled: boolean, debug_mode_enabled: boolean, is_dark_mode_enabled: boolean, notifications_enabled: boolean, open_doctolib_at_start: boolean, proxy_enabled: boolean, spell_check_activated: boolean, updater_channel: string, // ... custom fields } } How to Send (Product Events): import { capturePro } from '@core/product-events' import { DDV__TAB_CLOSE_BUTTON__CLICKED } from '@core/tabs/constants/product-events' // Simple event (no dynamic details) capturePro(DDV__REFRESH_BUTTON__CLICKED) // Event with dynamic details capturePro(DDV__TAB_CLOSE_BUTTON__CLICKED({ ddv_tab_type: 'doctolib', ddv_tab_count: 5 })) --- 3. Custom Observability Events (Flexible Tracking) For one-off tracking without predefined event types: import { sendCustomTrackingEventToObservability } from '@core/monitoring' // Uses TrackingEvent enum or custom labels sendCustomTrackingEventToObservability(TrackingEvent.KbvTiConnect, { action: 'certificate_loaded', certificate_type: 'SMC-K', status: 'success', duration_ms: 1234 }) Alternative (raw): import { enqueueCustomDatadogEvent } from '@core/observability' enqueueCustomDatadogEvent('CUSTOM_LABEL', { any_field: 'value', certificate_type: 'SMC-K' }) --- Event Naming Conventions Observability Events: - Format: dpd_, ddv_, kbv_, ehr_, etc. - Examples: dpd_tab_open, kbv_ti_connect, ddv_native_module_health_check_completed - Located in: /src/core/monitoring/constants/tracking.ts Product Events: - Format: ddv____ (snake_case) - Examples: ddv__tab_close_button__clicked, ddv__home_page__viewed - Located in: /constants/product-events.ts (per-feature) --- Account/User-Level Tracking User Info: - Tracked via ObservabilityState.userId (anonymised hostname-based) - Account ID from Doctolib: doctolib_account_id (from monolith observability attributes) - Country and product line from: ObservabilityState.productLineConfig How to Access: import ObservabilityState from '@core/observability/main/ObservabilityState' // Static properties automatically included in all events: ObservabilityState.userId ObservabilityState.observabilityAttributes // Contains doctolib_account_id ObservabilityState.productLineConfig?.country --- Where Datadog Client is Configured 1. Datadog URL: Retrieved from Doctolib web interface at runtime - window.dl_constants?.NGINX_NEWRELIC_EVENT_URL - Falls back to ObservabilityTrackingResult.NoURL if unavailable 2. Network: Uses @core/network/services/network.fetch() 3. User Consent: Both systems check userConsentedToAnalytics() before sending 4. Environment Handling: - Dev/Test: Events logged locally, not sent to Datadog - Staging: Events shown in console with env flag - Production: Events sent with environment detection via subdomain --- Example: Implementation Pattern for TI Certificate Type Tracking Option 1: Using Observability (simpler, direct): // In tracking.ts export enum TrackingEvent { TiCertificateTypeDetected = 'ti_certificate_type_detected' } // In tracking interfaces [TrackingEvent.TiCertificateTypeDetected]: { certificate_type: string account_id?: string country?: string status: 'success' | 'error' } // In your module import { sendCustomTrackingEventToObservability, TrackingEvent } from '@core/monitoring' sendCustomTrackingEventToObservability(TrackingEvent.TiCertificateTypeDetected, { certificate_type: 'SMC-K', account_id: String(accountId), country: productLineConfig?.country, status: 'success' }) Option 2: Using Product Events (for feature-level tracking): // In constants/product-events.ts export const DDV__TI_CERTIFICATE__DETECTED: DDVProEvent = { schema: 'ti_connect', name: 'ddv__ti_certificate__detected', team: 'FIRE', // Appropriate team details: ProductFoundationState.toEventDetails() } // In your module import { capturePro } from '@core/product-events' capturePro(DDV__TI_CERTIFICATE__DETECTED) --- Key Files Reference | File | Purpose | |------|---------| | /src/core/observability/services/sendToDatadog.ts | HTTP sender to Datadog | | /src/core/observability/main/ObservabilityQueue.ts | Event queueing with retry | | /src/core/product-events/services/capturePro.ts | High-level product event API | | /src/core/monitoring/constants/tracking.ts | Event type definitions | | /src/core/product-events/index.ts | Product events exports | | /src/utils/ti-connect-event-observability.ts | TI-specific tracking example |