@sentry/profiling-node 9.7.0-alpha.0 → 9.8.0

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/lib/cjs/index.js CHANGED
@@ -375,6 +375,7 @@ function createEventEnvelopeHeaders(
375
375
  * Creates a standalone profile_chunk envelope.
376
376
  */
377
377
  function makeProfileChunkEnvelope(
378
+ platform,
378
379
  chunk,
379
380
  sdkInfo,
380
381
  tunnel,
@@ -382,6 +383,7 @@ function makeProfileChunkEnvelope(
382
383
  ) {
383
384
  const profileChunkHeader = {
384
385
  type: 'profile_chunk',
386
+ platform,
385
387
  };
386
388
 
387
389
  return core.createEnvelope(createEventEnvelopeHeaders(sdkInfo, tunnel, dsn), [
@@ -404,6 +406,8 @@ function applyDebugMetadata(client, resource_paths) {
404
406
  return core.getDebugImagesForResources(options.stackParser, resource_paths);
405
407
  }
406
408
 
409
+ /* eslint-disable deprecation/deprecation */
410
+
407
411
  const MAX_PROFILE_DURATION_MS = 30 * 1000;
408
412
 
409
413
  /**
@@ -540,174 +544,361 @@ function takeFromProfileQueue(profile_id) {
540
544
  return profile;
541
545
  }
542
546
 
543
- /**
544
- * Instruments the client to automatically invoke the profiler on span start and stop events.
545
- * @param client
546
- */
547
- function setupAutomatedSpanProfiling(client) {
548
- const spanToProfileIdMap = new WeakMap();
549
-
550
- client.on('spanStart', span => {
551
- if (span !== core.getRootSpan(span)) {
552
- return;
553
- }
554
-
555
- const profile_id = maybeProfileSpan(client, span);
547
+ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.call(this);ContinuousProfiler.prototype.__init2.call(this);ContinuousProfiler.prototype.__init3.call(this);ContinuousProfiler.prototype.__init4.call(this);ContinuousProfiler.prototype.__init5.call(this);ContinuousProfiler.prototype.__init6.call(this);ContinuousProfiler.prototype.__init7.call(this); }
556
548
 
557
- if (profile_id) {
558
- const options = client.getOptions();
559
- // Not intended for external use, hence missing types, but we want to profile a couple of things at Sentry that
560
- // currently exceed the default timeout set by the SDKs.
561
- const maxProfileDurationMs = options._experiments?.maxProfileDurationMs || MAX_PROFILE_DURATION_MS;
549
+ __init() {this._client = undefined;}
550
+ __init2() {this._chunkData = undefined;}
551
+ __init3() {this._mode = undefined;}
552
+ __init4() {this._legacyProfilerMode = undefined;}
553
+ __init5() {this._profileLifecycle = undefined;}
554
+ __init6() {this._sampled = undefined;}
555
+ __init7() {this._sessionSamplingRate = undefined;}
556
+ /**
557
+ * Called when the profiler is attached to the client (continuous mode is enabled). If of the profiler
558
+ * methods called before the profiler is initialized will result in a noop action with debug logs.
559
+ * @param client
560
+ */
561
+ initialize(client) {
562
+ this._client = client;
563
+ const options = client.getOptions();
564
+
565
+ this._mode = getProfilingMode(options);
566
+ this._sessionSamplingRate = Math.random();
567
+ this._sampled = this._sessionSamplingRate < (options.profileSessionSampleRate ?? 0);
568
+ this._profileLifecycle = options.profileLifecycle ?? 'manual';
569
+
570
+ switch (this._mode) {
571
+ case 'legacy': {
572
+ this._legacyProfilerMode =
573
+ 'profilesSampleRate' in options || 'profilesSampler' in options ? 'span' : 'continuous';
574
+
575
+ DEBUG_BUILD && core.logger.log(`[Profiling] Profiling mode is ${this._legacyProfilerMode}.`);
576
+
577
+ switch (this._legacyProfilerMode) {
578
+ case 'span': {
579
+ this._setupAutomaticSpanProfiling();
580
+ break;
581
+ }
582
+ case 'continuous': {
583
+ // Continous mode requires manual calls to profiler.start() and profiler.stop()
584
+ break;
585
+ }
586
+ default: {
587
+ DEBUG_BUILD &&
588
+ core.logger.warn(
589
+ `[Profiling] Unknown profiler mode: ${this._legacyProfilerMode}, profiler was not initialized`,
590
+ );
591
+ break;
592
+ }
593
+ }
594
+ break;
595
+ }
562
596
 
563
- if (PROFILE_TIMEOUTS[profile_id]) {
564
- global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
565
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
566
- delete PROFILE_TIMEOUTS[profile_id];
597
+ case 'current': {
598
+ this._setupSpanChunkInstrumentation();
599
+
600
+ DEBUG_BUILD && core.logger.log(`[Profiling] Profiling mode is ${this._profileLifecycle}.`);
601
+
602
+ switch (this._profileLifecycle) {
603
+ case 'trace': {
604
+ this._startTraceLifecycleProfiling();
605
+ break;
606
+ }
607
+ case 'manual': {
608
+ // Manual mode requires manual calls to profiler.startProfiler() and profiler.stopProfiler()
609
+ break;
610
+ }
611
+ default: {
612
+ DEBUG_BUILD &&
613
+ core.logger.warn(`[Profiling] Unknown profiler mode: ${this._profileLifecycle}, profiler was not initialized`);
614
+ break;
615
+ }
616
+ }
617
+ break;
567
618
  }
619
+ default: {
620
+ DEBUG_BUILD && core.logger.warn(`[Profiling] Unknown profiler mode: ${this._mode}, profiler was not initialized`);
621
+ break;
622
+ }
623
+ }
624
+
625
+ // Attaches a listener to beforeSend which will add the threadId data to the event being sent.
626
+ // This adds a constant overhead to all events being sent which could be improved to only attach
627
+ // and detach the listener during a profiler session
628
+ this._client.on('beforeSendEvent', this._onBeforeSendThreadContextAssignment.bind(this));
629
+ }
568
630
 
569
- // Enqueue a timeout to prevent profiles from running over max duration.
570
- const timeout = global.setTimeout(() => {
571
- DEBUG_BUILD &&
572
- core.logger.log('[Profiling] max profile duration elapsed, stopping profiling for:', core.spanToJSON(span).description);
631
+ /**
632
+ * Initializes a new profilerId session and schedules chunk profiling.
633
+ * @returns void
634
+ */
635
+ start() {
636
+ if (this._mode === 'current') {
637
+ this._startProfiler();
638
+ return;
639
+ }
573
640
 
574
- const profile = stopSpanProfile(span, profile_id);
575
- if (profile) {
576
- addToProfileQueue(profile_id, profile);
577
- }
578
- }, maxProfileDurationMs);
641
+ if (!this._client) {
642
+ DEBUG_BUILD && core.logger.log('[Profiling] Failed to start, sentry client was never attached to the profiler.');
643
+ return;
644
+ }
579
645
 
580
- // Unref timeout so it doesn't keep the process alive.
581
- timeout.unref();
646
+ if (this._mode !== 'legacy') {
647
+ DEBUG_BUILD && core.logger.log('[Profiling] Continuous profiling is not supported in the current mode.');
648
+ return;
649
+ }
582
650
 
583
- core.getIsolationScope().setContext('profile', { profile_id });
584
- spanToProfileIdMap.set(span, profile_id);
651
+ if (this._legacyProfilerMode === 'span') {
652
+ DEBUG_BUILD && core.logger.log('[Profiling] Calls to profiler.start() are not supported in span profiling mode.');
653
+ return;
585
654
  }
586
- });
587
655
 
588
- client.on('spanEnd', span => {
589
- const profile_id = spanToProfileIdMap.get(span);
656
+ // Flush any existing chunks before starting a new one.
590
657
 
591
- if (profile_id) {
592
- if (PROFILE_TIMEOUTS[profile_id]) {
593
- global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
594
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
595
- delete PROFILE_TIMEOUTS[profile_id];
596
- }
597
- const profile = stopSpanProfile(span, profile_id);
658
+ this._stopChunkProfiling();
598
659
 
599
- if (profile) {
600
- addToProfileQueue(profile_id, profile);
601
- }
660
+ // Restart the profiler session
661
+ this._setupSpanChunkInstrumentation();
662
+ this._restartChunkProfiling();
663
+ }
664
+
665
+ /**
666
+ * Stops the current chunk and flushes the profile to Sentry.
667
+ * @returns void
668
+ */
669
+ stop() {
670
+ if (this._mode === 'current') {
671
+ this._stopProfiler();
672
+ return;
602
673
  }
603
- });
604
674
 
605
- client.on('beforeEnvelope', (envelope) => {
606
- // if not profiles are in queue, there is nothing to add to the envelope.
607
- if (!PROFILE_MAP.size) {
675
+ if (!this._client) {
676
+ DEBUG_BUILD && core.logger.log('[Profiling] Failed to stop, sentry client was never attached to the profiler.');
608
677
  return;
609
678
  }
610
679
 
611
- const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
612
- if (!profiledTransactionEvents.length) {
680
+ if (this._mode !== 'legacy') {
681
+ DEBUG_BUILD && core.logger.log('[Profiling] Continuous profiling is not supported in the current mode.');
613
682
  return;
614
683
  }
615
684
 
616
- const profilesToAddToEnvelope = [];
685
+ if (this._legacyProfilerMode === 'span') {
686
+ DEBUG_BUILD && core.logger.log('[Profiling] Calls to profiler.stop() are not supported in span profiling mode.');
687
+ return;
688
+ }
617
689
 
618
- for (const profiledTransaction of profiledTransactionEvents) {
619
- const profileContext = profiledTransaction.contexts?.profile;
620
- const profile_id = profileContext?.profile_id;
690
+ this._stopChunkProfiling();
691
+ this._teardownSpanChunkInstrumentation();
692
+ }
621
693
 
622
- if (!profile_id) {
623
- throw new TypeError('[Profiling] cannot find profile for a transaction without a profile context');
624
- }
694
+ _startProfiler() {
695
+ if (this._mode !== 'current') {
696
+ DEBUG_BUILD && core.logger.log('[Profiling] Continuous profiling is not supported in the current mode.');
697
+ return;
698
+ }
625
699
 
626
- // Remove the profile from the transaction context before sending, relay will take care of the rest.
627
- if (profileContext) {
628
- delete profiledTransaction.contexts?.profile;
629
- }
700
+ if (this._chunkData !== undefined) {
701
+ DEBUG_BUILD && core.logger.log('[Profiling] Profile session already running, no-op.');
702
+ return;
703
+ }
630
704
 
631
- const cpuProfile = takeFromProfileQueue(profile_id);
632
- if (!cpuProfile) {
633
- DEBUG_BUILD && core.logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
634
- continue;
705
+ if (this._mode === 'current') {
706
+ if (!this._sampled) {
707
+ DEBUG_BUILD && core.logger.log('[Profiling] Profile session not sampled, no-op.');
708
+ return;
635
709
  }
710
+ }
636
711
 
637
- const profile = createProfilingEvent(client, cpuProfile, profiledTransaction);
638
- if (!profile) return;
639
-
640
- profilesToAddToEnvelope.push(profile);
641
-
642
- // @ts-expect-error profile does not inherit from Event
643
- client.emit('preprocessEvent', profile, {
644
- event_id: profiledTransaction.event_id,
645
- });
646
-
647
- // @ts-expect-error profile does not inherit from Event
648
- client.emit('postprocessEvent', profile, {
649
- event_id: profiledTransaction.event_id,
650
- });
712
+ if (this._profileLifecycle === 'trace') {
713
+ DEBUG_BUILD &&
714
+ core.logger.log(
715
+ '[Profiling] You are using the trace profile lifecycle, manual calls to profiler.startProfiler() and profiler.stopProfiler() will be ignored.',
716
+ );
717
+ return;
651
718
  }
652
719
 
653
- addProfilesToEnvelope(envelope, profilesToAddToEnvelope);
654
- });
655
- }
720
+ this._startChunkProfiling();
721
+ }
656
722
 
657
- class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.call(this);ContinuousProfiler.prototype.__init2.call(this); }
723
+ _stopProfiler() {
724
+ if (this._mode !== 'current') {
725
+ DEBUG_BUILD && core.logger.log('[Profiling] Continuous profiling is not supported in the current mode.');
726
+ return;
727
+ }
658
728
 
659
- __init() {this._client = undefined;}
660
- __init2() {this._chunkData = undefined;}
729
+ if (this._profileLifecycle === 'trace') {
730
+ DEBUG_BUILD &&
731
+ core.logger.log(
732
+ '[Profiling] You are using the trace profile lifecycle, manual calls to profiler.startProfiler() and profiler.stopProfiler() will be ignored.',
733
+ );
734
+ return;
735
+ }
661
736
 
662
- /**
663
- * Called when the profiler is attached to the client (continuous mode is enabled). If of the profiler
664
- * methods called before the profiler is initialized will result in a noop action with debug logs.
665
- * @param client
666
- */
667
- initialize(client) {
668
- this._client = client;
737
+ if (!this._chunkData) {
738
+ DEBUG_BUILD && core.logger.log('[Profiling] No profile session running, no-op.');
739
+ return;
740
+ }
669
741
 
670
- // Attaches a listener to beforeSend which will add the threadId data to the event being sent.
671
- // This adds a constant overhead to all events being sent which could be improved to only attach
672
- // and detach the listener during a profiler session
673
- this._client.on('beforeSendEvent', this._onBeforeSendThreadContextAssignment.bind(this));
742
+ this._stopChunkProfiling();
674
743
  }
675
744
 
676
745
  /**
677
- * Initializes a new profilerId session and schedules chunk profiling.
678
- * @returns void
746
+ * Starts trace lifecycle profiling. Profiling will remain active as long as there is an active span.
679
747
  */
680
- start() {
748
+ _startTraceLifecycleProfiling() {
681
749
  if (!this._client) {
682
- DEBUG_BUILD && core.logger.log('[Profiling] Failed to start, sentry client was never attached to the profiler.');
750
+ DEBUG_BUILD &&
751
+ core.logger.log(
752
+ '[Profiling] Failed to start trace lifecycle profiling, sentry client was never attached to the profiler.',
753
+ );
683
754
  return;
684
755
  }
685
756
 
686
- // Flush any existing chunks before starting a new one.
687
- this._chunkStop();
757
+ let activeSpanCounter = 0;
758
+ this._client.on('spanStart', _span => {
759
+ if (activeSpanCounter === 0) {
760
+ this._startChunkProfiling();
761
+ }
762
+ activeSpanCounter++;
763
+ });
688
764
 
689
- // Restart the profiler session
690
- this._setupSpanChunkInstrumentation();
691
- this._chunkStart();
765
+ this._client.on('spanEnd', _span => {
766
+ if (activeSpanCounter === 1) {
767
+ this._stopChunkProfiling();
768
+ }
769
+ activeSpanCounter--;
770
+ });
692
771
  }
693
772
 
694
- /**
695
- * Stops the current chunk and flushes the profile to Sentry.
696
- * @returns void
697
- */
698
- stop() {
773
+ _setupAutomaticSpanProfiling() {
699
774
  if (!this._client) {
700
- DEBUG_BUILD && core.logger.log('[Profiling] Failed to stop, sentry client was never attached to the profiler.');
775
+ DEBUG_BUILD &&
776
+ core.logger.log(
777
+ '[Profiling] Failed to setup automatic span profiling, sentry client was never attached to the profiler.',
778
+ );
701
779
  return;
702
780
  }
703
- this._chunkStop();
704
- this._teardownSpanChunkInstrumentation();
781
+
782
+ const spanToProfileIdMap = new WeakMap();
783
+
784
+ this._client.on('spanStart', span => {
785
+ if (span !== core.getRootSpan(span)) {
786
+ return;
787
+ }
788
+
789
+ const profile_id = maybeProfileSpan(this._client, span);
790
+
791
+ if (profile_id) {
792
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
793
+ const options = this._client.getOptions();
794
+ // Not intended for external use, hence missing types, but we want to profile a couple of things at Sentry that
795
+ // currently exceed the default timeout set by the SDKs.
796
+ const maxProfileDurationMs = options._experiments?.maxProfileDurationMs || MAX_PROFILE_DURATION_MS;
797
+
798
+ if (PROFILE_TIMEOUTS[profile_id]) {
799
+ global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
800
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
801
+ delete PROFILE_TIMEOUTS[profile_id];
802
+ }
803
+
804
+ // Enqueue a timeout to prevent profiles from running over max duration.
805
+ const timeout = global.setTimeout(() => {
806
+ DEBUG_BUILD &&
807
+ core.logger.log(
808
+ '[Profiling] max profile duration elapsed, stopping profiling for:',
809
+ core.spanToJSON(span).description,
810
+ );
811
+
812
+ const profile = stopSpanProfile(span, profile_id);
813
+ if (profile) {
814
+ addToProfileQueue(profile_id, profile);
815
+ }
816
+ }, maxProfileDurationMs);
817
+
818
+ // Unref timeout so it doesn't keep the process alive.
819
+ timeout.unref();
820
+
821
+ core.getIsolationScope().setContext('profile', { profile_id });
822
+ spanToProfileIdMap.set(span, profile_id);
823
+ }
824
+ });
825
+
826
+ this._client.on('spanEnd', span => {
827
+ const profile_id = spanToProfileIdMap.get(span);
828
+
829
+ if (profile_id) {
830
+ if (PROFILE_TIMEOUTS[profile_id]) {
831
+ global.clearTimeout(PROFILE_TIMEOUTS[profile_id]);
832
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
833
+ delete PROFILE_TIMEOUTS[profile_id];
834
+ }
835
+ const profile = stopSpanProfile(span, profile_id);
836
+
837
+ if (profile) {
838
+ addToProfileQueue(profile_id, profile);
839
+ }
840
+ }
841
+ });
842
+
843
+ this._client.on('beforeEnvelope', (envelope) => {
844
+ // if not profiles are in queue, there is nothing to add to the envelope.
845
+ if (!PROFILE_MAP.size) {
846
+ return;
847
+ }
848
+
849
+ const profiledTransactionEvents = findProfiledTransactionsFromEnvelope(envelope);
850
+ if (!profiledTransactionEvents.length) {
851
+ return;
852
+ }
853
+
854
+ const profilesToAddToEnvelope = [];
855
+
856
+ for (const profiledTransaction of profiledTransactionEvents) {
857
+ const profileContext = profiledTransaction.contexts?.profile;
858
+ const profile_id = profileContext?.profile_id;
859
+
860
+ if (!profile_id) {
861
+ throw new TypeError('[Profiling] cannot find profile for a transaction without a profile context');
862
+ }
863
+
864
+ // Remove the profile from the transaction context before sending, relay will take care of the rest.
865
+ if (profileContext) {
866
+ delete profiledTransaction.contexts?.profile;
867
+ }
868
+
869
+ const cpuProfile = takeFromProfileQueue(profile_id);
870
+ if (!cpuProfile) {
871
+ DEBUG_BUILD && core.logger.log(`[Profiling] Could not retrieve profile for transaction: ${profile_id}`);
872
+ continue;
873
+ }
874
+
875
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
876
+ const profile = createProfilingEvent(this._client, cpuProfile, profiledTransaction);
877
+ if (!profile) return;
878
+
879
+ profilesToAddToEnvelope.push(profile);
880
+
881
+ // @ts-expect-error profile does not inherit from Event
882
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
883
+ this._client.emit('preprocessEvent', profile, {
884
+ event_id: profiledTransaction.event_id,
885
+ });
886
+
887
+ // @ts-expect-error profile does not inherit from Event
888
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
889
+ this._client.emit('postprocessEvent', profile, {
890
+ event_id: profiledTransaction.event_id,
891
+ });
892
+ }
893
+
894
+ addProfilesToEnvelope(envelope, profilesToAddToEnvelope);
895
+ });
705
896
  }
706
897
 
707
898
  /**
708
899
  * Stop profiler and initializes profiling of the next chunk
709
900
  */
710
- _chunkStart() {
901
+ _restartChunkProfiling() {
711
902
  if (!this._client) {
712
903
  // The client is not attached to the profiler if the user has not enabled continuous profiling.
713
904
  // In this case, calling start() and stop() is a noop action.The reason this exists is because
@@ -715,12 +906,13 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
715
906
  DEBUG_BUILD && core.logger.log('[Profiling] Profiler was never attached to the client.');
716
907
  return;
717
908
  }
909
+
718
910
  if (this._chunkData) {
719
911
  DEBUG_BUILD &&
720
912
  core.logger.log(
721
913
  `[Profiling] Chunk with chunk_id ${this._chunkData.id} is still running, current chunk will be stopped a new chunk will be started.`,
722
914
  );
723
- this._chunkStop();
915
+ this._stopChunkProfiling();
724
916
  }
725
917
 
726
918
  this._startChunkProfiling();
@@ -729,32 +921,44 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
729
921
  /**
730
922
  * Stops profiling of the current chunks and flushes the profile to Sentry
731
923
  */
732
- _chunkStop() {
924
+ _stopChunkProfiling() {
925
+ if (!this._chunkData) {
926
+ DEBUG_BUILD && core.logger.log('[Profiling] No chunk data found, no-op.');
927
+ return;
928
+ }
929
+
733
930
  if (this._chunkData?.timer) {
734
931
  global.clearTimeout(this._chunkData.timer);
735
932
  this._chunkData.timer = undefined;
736
933
  DEBUG_BUILD && core.logger.log(`[Profiling] Stopping profiling chunk: ${this._chunkData.id}`);
737
934
  }
935
+
738
936
  if (!this._client) {
739
937
  DEBUG_BUILD &&
740
938
  core.logger.log('[Profiling] Failed to collect profile, sentry client was never attached to the profiler.');
939
+ this._resetChunkData();
741
940
  return;
742
941
  }
942
+
743
943
  if (!this._chunkData?.id) {
744
944
  DEBUG_BUILD &&
745
945
  core.logger.log(`[Profiling] Failed to collect profile for: ${this._chunkData?.id}, the chunk_id is missing.`);
946
+ this._resetChunkData();
746
947
  return;
747
948
  }
748
949
 
749
950
  const profile = nodeCpuProfiler.CpuProfilerBindings.stopProfiling(this._chunkData.id, nodeCpuProfiler.ProfileFormat.CHUNK);
750
951
 
751
952
  if (!profile) {
752
- DEBUG_BUILD && core.logger.log(`[Profiling] _chunkiledStartTraceID to collect profile for: ${this._chunkData.id}`);
953
+ DEBUG_BUILD && core.logger.log(`[Profiling] Failed to collect profile for: ${this._chunkData.id}`);
954
+ this._resetChunkData();
753
955
  return;
754
956
  }
957
+
755
958
  if (!this._profilerId) {
756
959
  DEBUG_BUILD &&
757
960
  core.logger.log('[Profiling] Profile chunk does not contain a valid profiler_id, this is a bug in the SDK');
961
+ this._resetChunkData();
758
962
  return;
759
963
  }
760
964
  if (profile) {
@@ -809,7 +1013,7 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
809
1013
  const metadata = this._client.getSdkMetadata();
810
1014
  const tunnel = this._client.getOptions().tunnel;
811
1015
 
812
- const envelope = makeProfileChunkEnvelope(chunk, metadata?.sdk, tunnel, dsn);
1016
+ const envelope = makeProfileChunkEnvelope('node', chunk, metadata?.sdk, tunnel, dsn);
813
1017
  transport.send(envelope).then(null, reason => {
814
1018
  DEBUG_BUILD && core.logger.error('Error while sending profile chunk envelope:', reason);
815
1019
  });
@@ -820,6 +1024,11 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
820
1024
  * @param chunk
821
1025
  */
822
1026
  _startChunkProfiling() {
1027
+ if (this._chunkData) {
1028
+ DEBUG_BUILD && core.logger.log('[Profiling] Chunk is already running, no-op.');
1029
+ return;
1030
+ }
1031
+
823
1032
  const traceId =
824
1033
  core.getCurrentScope().getPropagationContext().traceId || core.getIsolationScope().getPropagationContext().traceId;
825
1034
  const chunk = this._initializeChunk(traceId);
@@ -829,9 +1038,9 @@ class ContinuousProfiler {constructor() { ContinuousProfiler.prototype.__init.ca
829
1038
 
830
1039
  chunk.timer = global.setTimeout(() => {
831
1040
  DEBUG_BUILD && core.logger.log(`[Profiling] Stopping profiling chunk: ${chunk.id}`);
832
- this._chunkStop();
1041
+ this._stopChunkProfiling();
833
1042
  DEBUG_BUILD && core.logger.log('[Profiling] Starting new profiling chunk.');
834
- setImmediate(this._chunkStart.bind(this));
1043
+ setImmediate(this._restartChunkProfiling.bind(this));
835
1044
  }, CHUNK_INTERVAL_MS);
836
1045
 
837
1046
  // Unref timeout so it doesn't keep the process alive.
@@ -937,36 +1146,31 @@ const _nodeProfilingIntegration = (() => {
937
1146
  _profiler: new ContinuousProfiler(),
938
1147
  setup(client) {
939
1148
  DEBUG_BUILD && core.logger.log('[Profiling] Profiling integration setup.');
940
- const options = client.getOptions();
941
-
942
- const mode =
943
- (options.profilesSampleRate === undefined ||
944
- options.profilesSampleRate === null ||
945
- options.profilesSampleRate === 0) &&
946
- !options.profilesSampler
947
- ? 'continuous'
948
- : 'span';
949
- switch (mode) {
950
- case 'continuous': {
951
- DEBUG_BUILD && core.logger.log('[Profiling] Continuous profiler mode enabled.');
952
- this._profiler.initialize(client);
953
- break;
954
- }
955
- // Default to span profiling when no mode profiler mode is set
956
- case 'span':
957
- case undefined: {
958
- DEBUG_BUILD && core.logger.log('[Profiling] Span profiler mode enabled.');
959
- setupAutomatedSpanProfiling(client);
960
- break;
961
- }
962
- default: {
963
- DEBUG_BUILD && core.logger.warn(`[Profiling] Unknown profiler mode: ${mode}, profiler was not initialized`);
964
- }
965
- }
1149
+ this._profiler.initialize(client);
1150
+ return;
966
1151
  },
967
1152
  };
968
1153
  }) ;
969
1154
 
1155
+ /**
1156
+ * Determines the profiling mode based on the options.
1157
+ * @param options
1158
+ * @returns 'legacy' if the options are using the legacy profiling API, 'current' if the options are using the current profiling API
1159
+ */
1160
+ function getProfilingMode(options) {
1161
+ // Legacy mode takes precedence over current mode
1162
+ if ('profilesSampleRate' in options || 'profilesSampler' in options) {
1163
+ return 'legacy';
1164
+ }
1165
+
1166
+ if ('profileSessionSampleRate' in options || 'profileLifecycle' in options) {
1167
+ return 'current';
1168
+ }
1169
+
1170
+ // If neither are set, we are in the legacy continuous profiling mode
1171
+ return 'legacy';
1172
+ }
1173
+
970
1174
  /**
971
1175
  * We need this integration in order to send data to Sentry. We hook into the event processor
972
1176
  * and inspect each event to see if it is a transaction event and if that transaction event