44 * =============================================================================
55 *
66 * PURPOSE:
7- * Tracks simulation lifecycle events in Azure Application Insights , enabling
8- * users to filter and correlate telemetry by simulation ID using KQL queries.
7+ * Sets simulation context on OpenTelemetry spans , enabling users to filter
8+ * and correlate telemetry by simulation ID using KQL queries in App Insights .
99 *
1010 * HOW IT WORKS:
11- * 1. When a simulation starts, tracks a "SimulationStarted" custom event
12- * 2. When a simulation ends, tracks a "SimulationEnded" custom event
13- * 3. Both events include SimulationId and SimulationType as properties
14- * 4. Also sets OpenTelemetry span attributes for trace correlation
11+ * 1. When a simulation starts, sets SimulationId and SimulationType as span attributes
12+ * 2. These attributes flow to Application Insights as customDimensions
13+ * 3. Users can filter traces, requests, and dependencies by simulation ID
1514 *
1615 * APPLICATION INSIGHTS INTEGRATION:
17- * - Uses the applicationinsights SDK's trackEvent() for custom events
18- * - Custom events appear in the customEvents table in Log Analytics
19- * - Properties appear in customDimensions for KQL filtering
16+ * - Uses OpenTelemetry span attributes (via @azure/monitor-opentelemetry)
17+ * - Attributes appear in customDimensions on traces and requests tables
2018 * - Works transparently — if App Insights is not configured, calls are no-ops
2119 *
2220 * KQL QUERY EXAMPLES:
23- * // Find all simulation events
24- * customEvents
25- * | where name in ("SimulationStarted", "SimulationEnded")
26- * | project timestamp, name, customDimensions.SimulationId, customDimensions.SimulationType
27- *
28- * // Find all events for a specific simulation
29- * customEvents
21+ * // Find all requests for a specific simulation
22+ * requests
3023 * | where customDimensions.SimulationId == "abc123-def4-..."
3124 * | order by timestamp desc
3225 *
33- * // Find all CPU stress simulations
34- * customEvents
26+ * // Find all CPU stress simulation traces
27+ * traces
3528 * | where customDimensions.SimulationType == "CPU_STRESS"
3629 * | order by timestamp desc
3730 *
31+ * // Find dependencies triggered during a simulation
32+ * dependencies
33+ * | where customDimensions.SimulationId == "abc123-def4-..."
34+ * | order by timestamp desc
35+ *
3836 * PORTING NOTES:
39- * - .NET: Use TelemetryClient.TrackEvent()
40- * - Java: Use TelemetryClient.trackEvent ()
41- * - Python: Use azure.monitor. opentelemetry or applicationinsights SDK
37+ * - .NET: Use Activity.Current?.SetTag() or TelemetryClient
38+ * - Java: Use OpenTelemetry Span.setAttribute ()
39+ * - Python: Use opentelemetry span.set_attribute()
4240 *
4341 * @module services/simulation-context
4442 */
4543
4644import { trace , SpanStatusCode } from '@opentelemetry/api' ;
47- import appInsights from 'applicationinsights' ;
4845
4946/**
5047 * Simulation context information.
@@ -58,29 +55,10 @@ export interface SimulationContextInfo {
5855 * Service for tracking simulation context and correlating with Application Insights.
5956 *
6057 * This service provides methods to:
61- * - Track simulation start/end as custom events in App Insights
6258 * - Set simulation context on the current OpenTelemetry span
6359 * - Enable filtering of App Insights telemetry by simulation ID
6460 */
6561class SimulationContextServiceClass {
66- /**
67- * Gets the Application Insights telemetry client.
68- * The client is initialized in instrumentation.ts at application startup.
69- * Returns null if App Insights is not configured.
70- */
71- private getClient ( ) : appInsights . TelemetryClient | null {
72- try {
73- // Check if appInsights module and defaultClient exist
74- if ( appInsights && appInsights . defaultClient ) {
75- return appInsights . defaultClient ;
76- }
77- return null ;
78- } catch {
79- // App Insights not initialized - return null silently
80- return null ;
81- }
82- }
83-
8462 /**
8563 * Sets the simulation context on the current OpenTelemetry span.
8664 *
@@ -91,10 +69,6 @@ class SimulationContextServiceClass {
9169 * @param simulationType - Type of simulation (e.g., 'CPU_STRESS', 'MEMORY_PRESSURE')
9270 */
9371 setContext ( simulationId : string , simulationType : string ) : void {
94- // Track the SimulationStarted custom event
95- this . trackCustomEvent ( 'SimulationStarted' , simulationId , simulationType ) ;
96-
97- // Also set OpenTelemetry span attributes for trace correlation
9872 try {
9973 const activeSpan = trace . getActiveSpan ( ) ;
10074 if ( activeSpan ) {
@@ -108,51 +82,21 @@ class SimulationContextServiceClass {
10882 }
10983
11084 /**
111- * Tracks a simulation ended event.
85+ * Tracks a simulation ended event by setting attributes on the current span .
11286 *
11387 * @param simulationId - Unique identifier for the simulation
11488 * @param simulationType - Type of simulation
11589 */
11690 trackSimulationEnded ( simulationId : string , simulationType : string ) : void {
117- this . trackCustomEvent ( 'SimulationEnded' , simulationId , simulationType ) ;
118- }
119-
120- /**
121- * Tracks a custom event in Application Insights.
122- *
123- * @param eventName - Name of the event (e.g., 'SimulationStarted', 'SimulationEnded')
124- * @param simulationId - Unique identifier for the simulation
125- * @param simulationType - Type of simulation
126- * @param additionalProperties - Optional additional properties to include
127- */
128- private trackCustomEvent (
129- eventName : string ,
130- simulationId : string ,
131- simulationType : string ,
132- additionalProperties ?: Record < string , string >
133- ) : void {
134- const client = this . getClient ( ) ;
135- if ( ! client ) {
136- console . log ( `[SimulationContext] No client available - skipping ${ eventName } event for ${ simulationType } ` ) ;
137- return ;
138- }
139-
14091 try {
141- client . trackEvent ( {
142- name : eventName ,
143- properties : {
144- SimulationId : simulationId ,
145- SimulationType : simulationType ,
146- ...additionalProperties ,
147- } ,
148- } ) ;
149-
150- // Flush immediately to ensure event is sent (don't wait for batching)
151- client . flush ( ) ;
152-
153- console . log ( `[SimulationContext] Tracked and flushed ${ eventName } for ${ simulationType } (${ simulationId . substring ( 0 , 8 ) } ...)` ) ;
92+ const activeSpan = trace . getActiveSpan ( ) ;
93+ if ( activeSpan ) {
94+ activeSpan . setAttribute ( 'SimulationEnded' , true ) ;
95+ activeSpan . setAttribute ( 'SimulationId' , simulationId ) ;
96+ activeSpan . setAttribute ( 'SimulationType' , simulationType ) ;
97+ }
15498 } catch ( error ) {
155- console . error ( ` [SimulationContext] Failed to track ${ eventName } :` , error ) ;
99+ console . debug ( ' [SimulationContext] Failed to track simulation ended:' , error ) ;
156100 }
157101 }
158102
@@ -164,9 +108,7 @@ class SimulationContextServiceClass {
164108 * @returns A function to call when the simulation ends
165109 */
166110 trackSimulationStart ( simulationId : string , simulationType : string ) : ( ) => void {
167- this . trackCustomEvent ( 'SimulationStarted' , simulationId , simulationType ) ;
168-
169- // Also start an OpenTelemetry span for trace correlation
111+ // Start an OpenTelemetry span for trace correlation
170112 let span : ReturnType < ReturnType < typeof trace . getTracer > [ 'startSpan' ] > | null = null ;
171113 try {
172114 const tracer = trace . getTracer ( 'perfsimnode' ) ;
@@ -182,7 +124,6 @@ class SimulationContextServiceClass {
182124
183125 // Return cleanup function
184126 return ( ) => {
185- this . trackCustomEvent ( 'SimulationEnded' , simulationId , simulationType ) ;
186127 if ( span ) {
187128 span . setStatus ( { code : SpanStatusCode . OK } ) ;
188129 span . end ( ) ;
0 commit comments