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→
<system-reminder> 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*\{\s*trackingLabel
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\(\s*DDV__.*\(|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<string, string>([ 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→
<system-reminder> 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__<feature>__<action> (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
-
Datadog URL: Retrieved from Doctolib web interface at runtime -
window.dl_constants?.NGINX_NEWRELIC_EVENT_URL- Falls back toObservabilityTrackingResult.NoURLif unavailable -
Network: Uses
@core/network/services/network.fetch() -
User Consent: Both systems check
userConsentedToAnalytics()before sending -
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 |