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