@omen.foundation/node-microservice-runtime 0.1.49 → 0.1.51
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/dist/collector-manager.cjs +65 -45
- package/dist/collector-manager.d.ts.map +1 -1
- package/dist/collector-manager.js +88 -57
- package/dist/collector-manager.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +9 -1
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
- package/src/collector-manager.ts +52 -18
- package/src/runtime.ts +9 -1
|
@@ -82,6 +82,7 @@ let globalCollectorProcess = null;
|
|
|
82
82
|
let globalCollectorStartError = null;
|
|
83
83
|
let globalCollectorExitCode = null;
|
|
84
84
|
let globalCollectorStderr = [];
|
|
85
|
+
let globalCollectorStartupPromise = null;
|
|
85
86
|
let globalCollectorInitError = null;
|
|
86
87
|
function calculateSignature(pid, secret, uriPathAndQuery, body = null, version = '1') {
|
|
87
88
|
let dataToSign = `${secret}${pid}${version}${uriPathAndQuery}`;
|
|
@@ -640,6 +641,18 @@ async function discoverOrStartCollector(logger, standardOtelEnabled, env) {
|
|
|
640
641
|
if (!standardOtelEnabled) {
|
|
641
642
|
return null;
|
|
642
643
|
}
|
|
644
|
+
if (globalCollectorStartupPromise) {
|
|
645
|
+
logger.info('[Collector] Collector startup already in progress, waiting for existing startup to complete...');
|
|
646
|
+
try {
|
|
647
|
+
const result = await globalCollectorStartupPromise;
|
|
648
|
+
globalCollectorStartupPromise = null;
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
logger.error(`[Collector] Existing startup promise failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
653
|
+
globalCollectorStartupPromise = null;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
643
656
|
let existingEndpoint;
|
|
644
657
|
if (globalCollectorProcess) {
|
|
645
658
|
const processAlive = globalCollectorProcess.exitCode === null &&
|
|
@@ -664,59 +677,66 @@ async function discoverOrStartCollector(logger, standardOtelEnabled, env) {
|
|
|
664
677
|
return `http://${status.otlpEndpoint}`;
|
|
665
678
|
}
|
|
666
679
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
680
|
+
const startupPromise = (async () => {
|
|
681
|
+
try {
|
|
682
|
+
globalCollectorInitError = null;
|
|
683
|
+
let endpoint;
|
|
684
|
+
if (existingEndpoint) {
|
|
685
|
+
endpoint = existingEndpoint;
|
|
686
|
+
logger.info(`[Collector] Waiting for existing collector to become ready at ${endpoint}...`);
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
logger.info('[Collector] Starting OpenTelemetry collector...');
|
|
690
|
+
const startResult = await startCollector(logger, undefined, env);
|
|
691
|
+
endpoint = startResult.endpoint;
|
|
692
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
693
|
+
if (globalCollectorExitCode !== null && globalCollectorExitCode !== 0) {
|
|
694
|
+
const errorMsg = `Collector process exited immediately with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
695
|
+
globalCollectorInitError = errorMsg;
|
|
696
|
+
logger.error(`[Collector] ${errorMsg}`);
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
const maxWaitTime = 60000;
|
|
701
|
+
const checkInterval = 500;
|
|
702
|
+
const maxChecks = Math.floor(maxWaitTime / checkInterval);
|
|
703
|
+
logger.info('[Collector] Waiting for collector to become ready...');
|
|
704
|
+
for (let i = 0; i < maxChecks; i++) {
|
|
705
|
+
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
706
|
+
if (globalCollectorExitCode !== null && globalCollectorExitCode !== 0) {
|
|
707
|
+
const errorMsg = `Collector process exited during startup with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
708
|
+
globalCollectorInitError = errorMsg;
|
|
709
|
+
logger.error(`[Collector] ${errorMsg}`);
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const newStatus = await isCollectorRunning();
|
|
713
|
+
if (newStatus.isRunning && newStatus.isReady) {
|
|
714
|
+
logger.info(`[Collector] Collector is ready at ${newStatus.otlpEndpoint || endpoint}`);
|
|
715
|
+
return newStatus.otlpEndpoint ? `http://${newStatus.otlpEndpoint}` : endpoint;
|
|
716
|
+
}
|
|
717
|
+
if (i > 0 && i % 4 === 0) {
|
|
718
|
+
logger.info(`[Collector] Still waiting for collector to become ready... (${(i * checkInterval) / 1000}s elapsed)`);
|
|
719
|
+
}
|
|
684
720
|
}
|
|
685
|
-
}
|
|
686
|
-
const maxWaitTime = 60000;
|
|
687
|
-
const checkInterval = 500;
|
|
688
|
-
const maxChecks = Math.floor(maxWaitTime / checkInterval);
|
|
689
|
-
logger.info('[Collector] Waiting for collector to become ready...');
|
|
690
|
-
for (let i = 0; i < maxChecks; i++) {
|
|
691
|
-
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
692
721
|
if (globalCollectorExitCode !== null && globalCollectorExitCode !== 0) {
|
|
693
|
-
const errorMsg = `Collector process exited
|
|
722
|
+
const errorMsg = `Collector process exited with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
694
723
|
globalCollectorInitError = errorMsg;
|
|
695
724
|
logger.error(`[Collector] ${errorMsg}`);
|
|
696
725
|
return null;
|
|
697
726
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
logger.info(`[Collector] Collector is ready at ${newStatus.otlpEndpoint || endpoint}`);
|
|
701
|
-
return newStatus.otlpEndpoint ? `http://${newStatus.otlpEndpoint}` : endpoint;
|
|
702
|
-
}
|
|
703
|
-
if (i > 0 && i % 4 === 0) {
|
|
704
|
-
logger.info(`[Collector] Still waiting for collector to become ready... (${(i * checkInterval) / 1000}s elapsed)`);
|
|
705
|
-
}
|
|
727
|
+
logger.error(`[Collector] Collector did not become ready within ${maxWaitTime / 1000} seconds`);
|
|
728
|
+
return null;
|
|
706
729
|
}
|
|
707
|
-
|
|
708
|
-
const errorMsg =
|
|
730
|
+
catch (err) {
|
|
731
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
709
732
|
globalCollectorInitError = errorMsg;
|
|
710
|
-
logger.error(`[Collector] ${errorMsg}`);
|
|
733
|
+
logger.error(`[Collector] Failed to start collector: ${errorMsg}`);
|
|
711
734
|
return null;
|
|
712
735
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
logger.error(`[Collector] Failed to start collector: ${errorMsg}`);
|
|
720
|
-
return null;
|
|
721
|
-
}
|
|
736
|
+
})();
|
|
737
|
+
globalCollectorStartupPromise = startupPromise;
|
|
738
|
+
startupPromise.finally(() => {
|
|
739
|
+
globalCollectorStartupPromise = null;
|
|
740
|
+
});
|
|
741
|
+
return await startupPromise;
|
|
722
742
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collector-manager.d.ts","sourceRoot":"","sources":["../src/collector-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,YAAY,EAAE,MAAM,eAAe,CAAC;AAOpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAYpD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"collector-manager.d.ts","sourceRoot":"","sources":["../src/collector-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,YAAY,EAAE,MAAM,eAAe,CAAC;AAOpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAYpD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuCD;;GAEG;AACH,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqCD;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,qBAAqB,CAAC,CAwChC;AA0OD;;GAEG;AACH,wBAAgB,8BAA8B,IAAI;IAChD,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,aAAa,GAAG,KAAK,GAAG,SAAS,CAAC;CAC3C,CAoBA;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CAyEnE;AA0BD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAYjG;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,iBAAiB,EACtB,SAAS,GAAE,MAAc,GACxB,MAAM,GAAG,IAAI,CA8Ef;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,MAAM,EACrB,GAAG,CAAC,EAAE,iBAAiB,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgNtD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI;IAC3C,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CASA;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,mBAAmB,EAAE,OAAO,EAC5B,GAAG,CAAC,EAAE,iBAAiB,GACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmJxB"}
|
|
@@ -41,6 +41,8 @@ let globalCollectorProcess = null;
|
|
|
41
41
|
let globalCollectorStartError = null;
|
|
42
42
|
let globalCollectorExitCode = null;
|
|
43
43
|
let globalCollectorStderr = [];
|
|
44
|
+
// Track if collector startup is in progress to prevent duplicate starts
|
|
45
|
+
let globalCollectorStartupPromise = null;
|
|
44
46
|
let globalCollectorInitError = null; // Tracks errors from discoverOrStartCollector
|
|
45
47
|
/**
|
|
46
48
|
* Calculates Beamable signature for signed requests
|
|
@@ -744,6 +746,24 @@ export async function discoverOrStartCollector(logger, standardOtelEnabled, env)
|
|
|
744
746
|
if (!standardOtelEnabled) {
|
|
745
747
|
return null;
|
|
746
748
|
}
|
|
749
|
+
// CRITICAL: Check if collector startup is already in progress
|
|
750
|
+
// This prevents duplicate collector starts if this function is called multiple times
|
|
751
|
+
// (e.g., if setupCollectorBeforeLogging times out but the promise is still running)
|
|
752
|
+
if (globalCollectorStartupPromise) {
|
|
753
|
+
logger.info('[Collector] Collector startup already in progress, waiting for existing startup to complete...');
|
|
754
|
+
try {
|
|
755
|
+
const result = await globalCollectorStartupPromise;
|
|
756
|
+
// Clear the promise after it completes (success or failure)
|
|
757
|
+
globalCollectorStartupPromise = null;
|
|
758
|
+
return result;
|
|
759
|
+
}
|
|
760
|
+
catch (error) {
|
|
761
|
+
logger.error(`[Collector] Existing startup promise failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
762
|
+
// Clear the promise so we can retry
|
|
763
|
+
globalCollectorStartupPromise = null;
|
|
764
|
+
// Fall through to start a new one
|
|
765
|
+
}
|
|
766
|
+
}
|
|
747
767
|
// CRITICAL: Check if we already have a collector process starting/running
|
|
748
768
|
// This prevents duplicate collector starts if this function is called multiple times
|
|
749
769
|
let existingEndpoint;
|
|
@@ -776,72 +796,83 @@ export async function discoverOrStartCollector(logger, standardOtelEnabled, env)
|
|
|
776
796
|
}
|
|
777
797
|
}
|
|
778
798
|
// Collector not running - start it (or wait for existing one to be ready)
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
endpoint
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
logger.info('[Collector] Starting OpenTelemetry collector...');
|
|
791
|
-
const startResult = await startCollector(logger, undefined, env);
|
|
792
|
-
endpoint = startResult.endpoint;
|
|
793
|
-
// Check if collector process exited immediately (crashed)
|
|
794
|
-
// Wait a bit longer to see if it crashes right after starting
|
|
795
|
-
await new Promise(resolve => setTimeout(resolve, 200));
|
|
796
|
-
if (globalCollectorExitCode !== null && globalCollectorExitCode !== 0) {
|
|
797
|
-
const errorMsg = `Collector process exited immediately with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
798
|
-
globalCollectorInitError = errorMsg;
|
|
799
|
-
logger.error(`[Collector] ${errorMsg}`);
|
|
800
|
-
return null;
|
|
799
|
+
// Wrap the entire startup logic in a promise that we track globally
|
|
800
|
+
// This prevents duplicate starts if this function is called multiple times
|
|
801
|
+
const startupPromise = (async () => {
|
|
802
|
+
try {
|
|
803
|
+
// Clear any previous init error
|
|
804
|
+
globalCollectorInitError = null;
|
|
805
|
+
let endpoint;
|
|
806
|
+
if (existingEndpoint) {
|
|
807
|
+
// Collector already starting, just wait for it to be ready
|
|
808
|
+
endpoint = existingEndpoint;
|
|
809
|
+
logger.info(`[Collector] Waiting for existing collector to become ready at ${endpoint}...`);
|
|
801
810
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
811
|
+
else {
|
|
812
|
+
// Start a new collector
|
|
813
|
+
logger.info('[Collector] Starting OpenTelemetry collector...');
|
|
814
|
+
const startResult = await startCollector(logger, undefined, env);
|
|
815
|
+
endpoint = startResult.endpoint;
|
|
816
|
+
// Check if collector process exited immediately (crashed)
|
|
817
|
+
// Wait a bit longer to see if it crashes right after starting
|
|
818
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
819
|
+
if (globalCollectorExitCode !== null && globalCollectorExitCode !== 0) {
|
|
820
|
+
const errorMsg = `Collector process exited immediately with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
821
|
+
globalCollectorInitError = errorMsg;
|
|
822
|
+
logger.error(`[Collector] ${errorMsg}`);
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
// CRITICAL: Wait for collector to be fully ready before returning
|
|
827
|
+
// We'll wait up to 60 seconds, checking every 500ms
|
|
828
|
+
// This ensures the collector is actually ready to receive logs before we continue
|
|
829
|
+
const maxWaitTime = 60000; // 60 seconds
|
|
830
|
+
const checkInterval = 500; // Check every 500ms
|
|
831
|
+
const maxChecks = Math.floor(maxWaitTime / checkInterval);
|
|
832
|
+
logger.info('[Collector] Waiting for collector to become ready...');
|
|
833
|
+
for (let i = 0; i < maxChecks; i++) {
|
|
834
|
+
await new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
835
|
+
// Check if process exited during wait
|
|
836
|
+
if (globalCollectorExitCode !== null && globalCollectorExitCode !== 0) {
|
|
837
|
+
const errorMsg = `Collector process exited during startup with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
838
|
+
globalCollectorInitError = errorMsg;
|
|
839
|
+
logger.error(`[Collector] ${errorMsg}`);
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
const newStatus = await isCollectorRunning();
|
|
843
|
+
if (newStatus.isRunning && newStatus.isReady) {
|
|
844
|
+
logger.info(`[Collector] Collector is ready at ${newStatus.otlpEndpoint || endpoint}`);
|
|
845
|
+
return newStatus.otlpEndpoint ? `http://${newStatus.otlpEndpoint}` : endpoint;
|
|
846
|
+
}
|
|
847
|
+
// Log progress every 2 seconds
|
|
848
|
+
if (i > 0 && i % 4 === 0) {
|
|
849
|
+
logger.info(`[Collector] Still waiting for collector to become ready... (${(i * checkInterval) / 1000}s elapsed)`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
// Check one more time if process exited
|
|
813
853
|
if (globalCollectorExitCode !== null && globalCollectorExitCode !== 0) {
|
|
814
|
-
const errorMsg = `Collector process exited
|
|
854
|
+
const errorMsg = `Collector process exited with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
815
855
|
globalCollectorInitError = errorMsg;
|
|
816
856
|
logger.error(`[Collector] ${errorMsg}`);
|
|
817
857
|
return null;
|
|
818
858
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
return newStatus.otlpEndpoint ? `http://${newStatus.otlpEndpoint}` : endpoint;
|
|
823
|
-
}
|
|
824
|
-
// Log progress every 2 seconds
|
|
825
|
-
if (i > 0 && i % 4 === 0) {
|
|
826
|
-
logger.info(`[Collector] Still waiting for collector to become ready... (${(i * checkInterval) / 1000}s elapsed)`);
|
|
827
|
-
}
|
|
859
|
+
// Collector did not become ready within timeout
|
|
860
|
+
logger.error(`[Collector] Collector did not become ready within ${maxWaitTime / 1000} seconds`);
|
|
861
|
+
return null;
|
|
828
862
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
const errorMsg = `Collector process exited with code ${globalCollectorExitCode}. ${globalCollectorStderr.length > 0 ? `Stderr: ${globalCollectorStderr.join('; ')}` : 'No stderr output.'}`;
|
|
863
|
+
catch (err) {
|
|
864
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
832
865
|
globalCollectorInitError = errorMsg;
|
|
833
|
-
logger.error(`[Collector] ${errorMsg}`);
|
|
866
|
+
logger.error(`[Collector] Failed to start collector: ${errorMsg}`);
|
|
834
867
|
return null;
|
|
835
868
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
return null;
|
|
845
|
-
}
|
|
869
|
+
})();
|
|
870
|
+
// Store the promise globally so other calls to this function can wait for it
|
|
871
|
+
globalCollectorStartupPromise = startupPromise;
|
|
872
|
+
// Clear the promise when it completes (so we don't keep waiting on old promises)
|
|
873
|
+
startupPromise.finally(() => {
|
|
874
|
+
globalCollectorStartupPromise = null;
|
|
875
|
+
});
|
|
876
|
+
return await startupPromise;
|
|
846
877
|
}
|
|
847
878
|
//# sourceMappingURL=collector-manager.js.map
|