@hydranium/core 1.0.0-next.267 → 1.0.0-next.269
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/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +4 -3
- package/lib/documents/ast-document-manager.js.map +1 -1
- package/lib/documents/document-release-handler.d.ts +2 -2
- package/lib/documents/document-release-handler.d.ts.map +1 -1
- package/lib/documents/document-release-handler.js +14 -19
- package/lib/documents/document-release-handler.js.map +1 -1
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +7 -10
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/language-client-shadow.d.ts.map +1 -1
- package/lib/documents/language-client-shadow.js +2 -1
- package/lib/documents/language-client-shadow.js.map +1 -1
- package/lib/documents/version-sync-service.d.ts +18 -14
- package/lib/documents/version-sync-service.d.ts.map +1 -1
- package/lib/documents/version-sync-service.js +15 -13
- package/lib/documents/version-sync-service.js.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.js +2 -2
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
- package/lib/langium/document-builder/document-builder.d.ts +2 -2
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +23 -10
- package/lib/langium/document-builder/document-builder.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +5 -1
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/model-service/client-session.d.ts.map +1 -1
- package/lib/langium/model-service/client-session.js +20 -3
- package/lib/langium/model-service/client-session.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +4 -3
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +49 -19
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/project/abstract-project-manager.d.ts.map +1 -1
- package/lib/langium/project/abstract-project-manager.js +4 -4
- package/lib/langium/project/abstract-project-manager.js.map +1 -1
- package/lib/langium/scope/hydranium-scope-computation.d.ts +6 -0
- package/lib/langium/scope/hydranium-scope-computation.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-computation.js +6 -10
- package/lib/langium/scope/hydranium-scope-computation.js.map +1 -1
- package/lib/langium/validation/document-validator.d.ts.map +1 -1
- package/lib/langium/validation/document-validator.js +5 -3
- package/lib/langium/validation/document-validator.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.d.ts +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.js +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +4 -5
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.js +2 -1
- package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -1
- package/lib/launcher/integrated-server.d.ts +3 -1
- package/lib/launcher/integrated-server.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.js +5 -5
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
- package/lib/node/event-loop-monitor.d.ts.map +1 -1
- package/lib/node/event-loop-monitor.js +2 -1
- package/lib/node/event-loop-monitor.js.map +1 -1
- package/lib/node/log-preamble-node.d.ts +5 -3
- package/lib/node/log-preamble-node.d.ts.map +1 -1
- package/lib/node/log-preamble-node.js +13 -10
- package/lib/node/log-preamble-node.js.map +1 -1
- package/lib/node/memory-monitor.d.ts +2 -2
- package/lib/node/memory-monitor.d.ts.map +1 -1
- package/lib/node/memory-monitor.js +3 -5
- package/lib/node/memory-monitor.js.map +1 -1
- package/lib/node/server-diagnostics.js +1 -1
- package/lib/node/server-diagnostics.js.map +1 -1
- package/lib/node/socket-launcher.d.ts.map +1 -1
- package/lib/node/socket-launcher.js +4 -4
- package/lib/node/socket-launcher.js.map +1 -1
- package/lib/node/stdio-launcher.d.ts.map +1 -1
- package/lib/node/stdio-launcher.js +12 -3
- package/lib/node/stdio-launcher.js.map +1 -1
- package/lib/testing/playwright/server-log-capture.d.ts +7 -0
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-capture.js +12 -0
- package/lib/testing/playwright/server-log-capture.js.map +1 -1
- package/lib/testing/playwright/server-log-fixture.d.ts +16 -0
- package/lib/testing/playwright/server-log-fixture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-fixture.js +25 -6
- package/lib/testing/playwright/server-log-fixture.js.map +1 -1
- package/lib/testing/playwright/server-log-region.d.ts +15 -0
- package/lib/testing/playwright/server-log-region.d.ts.map +1 -0
- package/lib/testing/playwright/server-log-region.js +30 -0
- package/lib/testing/playwright/server-log-region.js.map +1 -0
- package/lib/util/connection-liveness.d.ts.map +1 -1
- package/lib/util/connection-liveness.js +3 -2
- package/lib/util/connection-liveness.js.map +1 -1
- package/package.json +5 -5
- package/src/documents/ast-document-manager.ts +7 -3
- package/src/documents/document-release-handler.ts +15 -21
- package/src/documents/hydranium-text-documents.ts +13 -12
- package/src/documents/language-client-shadow.ts +2 -1
- package/src/documents/version-sync-service.ts +32 -24
- package/src/langium/diagnostics/lsp-logger.ts +2 -1
- package/src/langium/document-builder/document-builder.ts +30 -11
- package/src/langium/integrity/integrity-service.ts +4 -1
- package/src/langium/model-service/client-session.ts +20 -3
- package/src/langium/model-service/model-service.ts +55 -24
- package/src/langium/project/abstract-project-manager.ts +4 -4
- package/src/langium/scope/hydranium-scope-computation.ts +12 -12
- package/src/langium/validation/document-validator.ts +5 -3
- package/src/langium/workspace/hydranium-workspace-lock.ts +2 -2
- package/src/langium/workspace/hydranium-workspace-manager.ts +4 -5
- package/src/langium/workspace/persistent-file-system-provider.ts +2 -1
- package/src/launcher/integrated-server.ts +3 -1
- package/src/lsp/hydranium-document-update-handler.ts +15 -7
- package/src/node/event-loop-monitor.ts +2 -1
- package/src/node/log-preamble-node.ts +20 -10
- package/src/node/memory-monitor.ts +4 -5
- package/src/node/server-diagnostics.ts +1 -1
- package/src/node/socket-launcher.ts +4 -4
- package/src/node/stdio-launcher.ts +11 -3
- package/src/testing/playwright/server-log-capture.ts +14 -0
- package/src/testing/playwright/server-log-fixture.ts +46 -6
- package/src/testing/playwright/server-log-region.ts +32 -0
- package/src/util/connection-liveness.ts +4 -2
|
@@ -7,7 +7,14 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
type Clock,
|
|
12
|
+
type LogThreshold,
|
|
13
|
+
type MaybeObservableValue,
|
|
14
|
+
ObservableValue,
|
|
15
|
+
type Tracer,
|
|
16
|
+
describeErrorWithStack
|
|
17
|
+
} from '@hydranium/protocol';
|
|
11
18
|
import {
|
|
12
19
|
type AstNode,
|
|
13
20
|
type BuildOptions,
|
|
@@ -356,7 +363,7 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
|
|
|
356
363
|
try {
|
|
357
364
|
listener(event);
|
|
358
365
|
} catch (error: unknown) {
|
|
359
|
-
this.tracer.error(`A build-ended listener failed: ${
|
|
366
|
+
this.tracer.error(`A build-ended listener failed: ${describeErrorWithStack(error)}`);
|
|
360
367
|
}
|
|
361
368
|
}
|
|
362
369
|
}
|
|
@@ -488,12 +495,15 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
|
|
|
488
495
|
// land while a busy workspace keeps cancelling builds.
|
|
489
496
|
let stalledRequeues = 0;
|
|
490
497
|
let lastRequeueState: DocumentState | undefined;
|
|
491
|
-
|
|
498
|
+
let settled = false;
|
|
499
|
+
let cancelled = false;
|
|
500
|
+
const giveUp = (reason: string, cause?: unknown): void => {
|
|
492
501
|
cleanup();
|
|
493
502
|
reject(
|
|
494
503
|
new Error(
|
|
495
504
|
`Gave up building ${this.formatUri(uri)} to '${DocumentState[state]}' ` +
|
|
496
|
-
`(at '${DocumentState[document.state]}'): ${reason}
|
|
505
|
+
`(at '${DocumentState[document.state]}'): ${reason}`,
|
|
506
|
+
{ cause }
|
|
497
507
|
)
|
|
498
508
|
);
|
|
499
509
|
};
|
|
@@ -517,9 +527,16 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
|
|
|
517
527
|
stalledRequeues = 0;
|
|
518
528
|
lastRequeueState = document.state;
|
|
519
529
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
530
|
+
this.requeueOrphaned(document, state, reason).catch((error: unknown) => {
|
|
531
|
+
const line = `A recovery build of ${this.formatUri(uri)} failed. ${describeErrorWithStack(error)}`;
|
|
532
|
+
if (rejectWhenStuck && !settled) {
|
|
533
|
+
giveUp('its recovery build failed', error);
|
|
534
|
+
} else if (settled && !cancelled) {
|
|
535
|
+
// The wait ended with an outcome its caller reports, and the retry warned.
|
|
536
|
+
this.tracer.withUri(uri.toString()).debug(line);
|
|
537
|
+
} else {
|
|
538
|
+
// The waiter has gone, or waits on its cancellation token alone, so nothing else reports this.
|
|
539
|
+
this.tracer.withUri(uri.toString()).error(line);
|
|
523
540
|
}
|
|
524
541
|
});
|
|
525
542
|
};
|
|
@@ -559,10 +576,12 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
|
|
|
559
576
|
};
|
|
560
577
|
const buildDisposable = this.onBuildEnded(buildEnded);
|
|
561
578
|
const cancelDisposable = cancelToken.onCancellationRequested(() => {
|
|
579
|
+
cancelled = true;
|
|
562
580
|
cleanup();
|
|
563
581
|
reject(OperationCancelled);
|
|
564
582
|
});
|
|
565
583
|
const cleanup = (): void => {
|
|
584
|
+
settled = true;
|
|
566
585
|
phaseDisposable.dispose();
|
|
567
586
|
skipDisposable.dispose();
|
|
568
587
|
buildDisposable.dispose();
|
|
@@ -628,11 +647,11 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
|
|
|
628
647
|
* the build neither runs beside a locked one nor cancels the build a
|
|
629
648
|
* re-queue is fired from, and skipped if a write that ran meanwhile carried
|
|
630
649
|
* the document to `state`. The waiter resolves off the resulting phase
|
|
631
|
-
* notification; the
|
|
650
|
+
* notification; the promise only tells it whether the build was given up.
|
|
632
651
|
*/
|
|
633
|
-
protected requeueOrphaned(document: LangiumDocument, state: DocumentState, reason: string): Promise<
|
|
652
|
+
protected requeueOrphaned(document: LangiumDocument, state: DocumentState, reason: string): Promise<void> {
|
|
634
653
|
const tracer = this.tracer.withUri(document.uri.toString());
|
|
635
|
-
tracer.
|
|
654
|
+
tracer.debug(`Re-queuing orphaned document (at '${DocumentState[document.state]}', needs '${DocumentState[state]}'): ${reason}`);
|
|
636
655
|
return this.versionSyncService().requestRecoveryBuild(document.uri, {
|
|
637
656
|
// Skipped on purpose: a waiting caller is answered sooner, at the cost
|
|
638
657
|
// of a possible duplicate build once the debounce fires.
|
|
@@ -1391,7 +1410,7 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
|
|
|
1391
1410
|
if (cancelledListeners > 0) {
|
|
1392
1411
|
this.tracer
|
|
1393
1412
|
.withUri(document.uri.toString())
|
|
1394
|
-
.
|
|
1413
|
+
.debug(this.cancelledDocumentPhaseLine(document, state, cancelledListeners, listeners.length));
|
|
1395
1414
|
}
|
|
1396
1415
|
if (totalMs >= this.slowPhaseMs.value) {
|
|
1397
1416
|
this.log(this.slowDocumentPhaseLine(document, state, listeners, perListenerMs, totalMs));
|
|
@@ -213,8 +213,11 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
|
|
|
213
213
|
await interruptAndCheck(cancelToken);
|
|
214
214
|
}
|
|
215
215
|
const totalMs = Math.round(performance.now() - phaseStart);
|
|
216
|
-
|
|
216
|
+
// A repair is the operator's to know of; the timing alone is diagnosis.
|
|
217
|
+
if (mutatedCount > 0) {
|
|
217
218
|
this.tracer.info(`enforce ${DocumentState[phase]}: docs=${documents.length}, mutated=${mutatedCount} [${totalMs}ms]`);
|
|
219
|
+
} else if (totalMs >= 25) {
|
|
220
|
+
this.tracer.debug(`enforce ${DocumentState[phase]}: docs=${documents.length}, mutated=${mutatedCount} [${totalMs}ms]`);
|
|
218
221
|
}
|
|
219
222
|
// One line per rule (count + self-% + self-ms), sorted by cost — only when profiling.
|
|
220
223
|
session?.report('debug');
|
|
@@ -17,9 +17,10 @@ import {
|
|
|
17
17
|
ObservableValue,
|
|
18
18
|
type TextVersion,
|
|
19
19
|
type Tracer,
|
|
20
|
-
type TransferElement
|
|
20
|
+
type TransferElement,
|
|
21
|
+
describeErrorWithStack
|
|
21
22
|
} from '@hydranium/protocol';
|
|
22
|
-
import { type AstNode, UriUtils } from '@hydranium/langium';
|
|
23
|
+
import { type AstNode, isOperationCancelled, UriUtils } from '@hydranium/langium';
|
|
23
24
|
import { type CancellationToken, type Disposable, Emitter, type Event } from 'vscode-languageserver';
|
|
24
25
|
import { type AstDocument, type SavedAstDocument } from '../../documents/ast-document-manager.js';
|
|
25
26
|
import { DocumentNotOpenError, SessionClosedError } from '../../documents/client-session-errors.js';
|
|
@@ -410,7 +411,23 @@ export class DefaultClientSession<
|
|
|
410
411
|
const applied = uris.map((uri, i) => this.services.workspace.AstDocumentManager.update(uri, texts[i], this.clientId));
|
|
411
412
|
await Promise.all(applied);
|
|
412
413
|
const state = this.services.workspace.DocumentBuilder.finalBuildState();
|
|
413
|
-
|
|
414
|
+
const rebuilt = await Promise.allSettled(uris.map(uri => service.rebuild(uri, state, cancelToken)));
|
|
415
|
+
const failures = rebuilt.flatMap((result, i) => (result.status === 'rejected' ? [{ uri: uris[i], reason: result.reason }] : []));
|
|
416
|
+
if (failures.length === 0) {
|
|
417
|
+
return rebuilt.flatMap(result => (result.status === 'fulfilled' ? [result.value] : []));
|
|
418
|
+
}
|
|
419
|
+
// The first is thrown as it is, so a typed failure keeps its type; one build
|
|
420
|
+
// usually carries the set and fails each with the same error. Any other
|
|
421
|
+
// failure reaches no one else, so it is logged here.
|
|
422
|
+
const [{ uri: firstUri, reason: firstReason }] = failures;
|
|
423
|
+
for (const { uri, reason } of failures.slice(1)) {
|
|
424
|
+
if (isOperationCancelled(reason)) {
|
|
425
|
+
this.tracer.with(uri).debug(`Rebuild of ${uri} cancelled beside that of ${firstUri}`);
|
|
426
|
+
} else if (reason !== firstReason) {
|
|
427
|
+
this.tracer.with(uri).error(`Rebuild of ${uri} failed beside that of ${firstUri}. ${describeErrorWithStack(reason)}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
throw firstReason;
|
|
414
431
|
}
|
|
415
432
|
|
|
416
433
|
/**
|
|
@@ -18,9 +18,18 @@ import {
|
|
|
18
18
|
type TransferElement,
|
|
19
19
|
type TextVersion,
|
|
20
20
|
type Tracer,
|
|
21
|
-
UNRECORDED_VERSION
|
|
21
|
+
UNRECORDED_VERSION,
|
|
22
|
+
describeErrorWithStack
|
|
22
23
|
} from '@hydranium/protocol';
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
type AstNode,
|
|
26
|
+
DocumentState,
|
|
27
|
+
type LangiumDocument,
|
|
28
|
+
OperationCancelled,
|
|
29
|
+
isOperationCancelled,
|
|
30
|
+
UriUtils,
|
|
31
|
+
type URI
|
|
32
|
+
} from '@hydranium/langium';
|
|
24
33
|
import { type AstDiagnostic } from '../validation/document-validator.js';
|
|
25
34
|
import { type DocumentUriPolicy } from '../workspace/document-uri-policy.js';
|
|
26
35
|
import { ReentrantWriteLockError, isInsideWriteLock, isWriteLockScopeInstalled } from '../workspace/write-lock-scope.js';
|
|
@@ -571,23 +580,37 @@ export class DefaultModelService<
|
|
|
571
580
|
* given up. A `Parsed` phase listener cannot serve: a cancel right after the
|
|
572
581
|
* parse skips it, and the resumed build does not parse again.
|
|
573
582
|
*/
|
|
574
|
-
protected nextParseOrDeletion(
|
|
575
|
-
uri: URI,
|
|
576
|
-
cancelToken: CancellationToken = CancellationToken.None,
|
|
577
|
-
build?: Promise<boolean>
|
|
578
|
-
): Promise<void> {
|
|
583
|
+
protected nextParseOrDeletion(uri: URI, cancelToken: CancellationToken = CancellationToken.None, build?: Promise<void>): Promise<void> {
|
|
579
584
|
const builder = this.services.workspace.DocumentBuilder;
|
|
580
585
|
return new Promise<void>((resolve, reject) => {
|
|
586
|
+
let ended = false;
|
|
587
|
+
let cancelled = false;
|
|
588
|
+
// Handled before any return, so a build given up after the wait ended is not an unhandled rejection.
|
|
589
|
+
build?.catch((error: unknown) => {
|
|
590
|
+
const line = `The build that would sync the root to its text failed. ${describeErrorWithStack(error)}`;
|
|
591
|
+
if (cancelled) {
|
|
592
|
+
// The waiter has gone, so nothing else reports this.
|
|
593
|
+
this.tracer.withUri(uri.toString()).error(line);
|
|
594
|
+
} else if (ended) {
|
|
595
|
+
// A parse or deletion came first: the waiter goes on and reports its own outcome, and the retry warned.
|
|
596
|
+
this.tracer.withUri(uri.toString()).debug(line);
|
|
597
|
+
} else {
|
|
598
|
+
done(() => reject(new Error(`The build that would sync ${uri.toString()} to its text failed`, { cause: error })));
|
|
599
|
+
}
|
|
600
|
+
});
|
|
581
601
|
// A cancelled token's listener runs a macrotask later, after a build
|
|
582
602
|
// that started meanwhile may have parsed.
|
|
583
603
|
if (cancelToken.isCancellationRequested) {
|
|
604
|
+
ended = true;
|
|
605
|
+
cancelled = true;
|
|
584
606
|
reject(OperationCancelled);
|
|
585
607
|
return;
|
|
586
608
|
}
|
|
587
609
|
const done = (settle: () => void): void => {
|
|
610
|
+
ended = true;
|
|
588
611
|
parsed.dispose();
|
|
589
612
|
deleted.dispose();
|
|
590
|
-
|
|
613
|
+
cancelling.dispose();
|
|
591
614
|
settle();
|
|
592
615
|
};
|
|
593
616
|
const parsed = this.services.workspace.VersionSyncService.onDidRecordModel(document => {
|
|
@@ -600,11 +623,9 @@ export class DefaultModelService<
|
|
|
600
623
|
done(resolve);
|
|
601
624
|
}
|
|
602
625
|
});
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
done(() => reject(new Error(`The build that would sync ${uri.toString()} to its text failed`)));
|
|
607
|
-
}
|
|
626
|
+
const cancelling = cancelToken.onCancellationRequested(() => {
|
|
627
|
+
cancelled = true;
|
|
628
|
+
done(() => reject(OperationCancelled));
|
|
608
629
|
});
|
|
609
630
|
});
|
|
610
631
|
}
|
|
@@ -808,8 +829,9 @@ export class DefaultModelService<
|
|
|
808
829
|
* so every language client reflects the latest content before a save returns.
|
|
809
830
|
* No-op tail for headless adopters — `syncChains` is empty without an LSP
|
|
810
831
|
* client. Bounded by {@link SAVE_SETTLE_TIMEOUT_MS} so a hung build, or an
|
|
811
|
-
* applyEdit reverse-RPC deadlock, can't freeze the caller;
|
|
812
|
-
*
|
|
832
|
+
* applyEdit reverse-RPC deadlock, can't freeze the caller; it returns rather
|
|
833
|
+
* than throwing, warning on timeout. A failed wait is logged at `error`, at
|
|
834
|
+
* `debug` when cancelled and at `warn` when the document is gone.
|
|
813
835
|
*
|
|
814
836
|
* Adopters with a save flow that must converge editor + disk before returning
|
|
815
837
|
* (e.g. a dual form/code editor that would otherwise show a content-conflict
|
|
@@ -824,15 +846,26 @@ export class DefaultModelService<
|
|
|
824
846
|
await this.waitForDocumentStateCanonical(canonical, IntegrityService.SettledState, cancelToken, { sync: true });
|
|
825
847
|
await this.syncChains.get(key);
|
|
826
848
|
})();
|
|
849
|
+
// The settle ends a failed wait, so it logs it, at one level whether the wait failed inside the bound or after it.
|
|
850
|
+
const reportFailedWait = (error: unknown, when: string): void => {
|
|
851
|
+
const tracer = this.tracer.withUri(key);
|
|
852
|
+
if (isOperationCancelled(error)) {
|
|
853
|
+
tracer.debug(`Save settle cancelled ${when}`);
|
|
854
|
+
} else if (!this.services.workspace.LangiumDocuments.hasDocument(UriUtils.toUri(canonical))) {
|
|
855
|
+
tracer.warn(`Save settle failed ${when}; the document is gone. ${describeErrorWithStack(error)}`);
|
|
856
|
+
} else {
|
|
857
|
+
tracer.error(`Save settle failed ${when}. ${describeErrorWithStack(error)}`);
|
|
858
|
+
}
|
|
859
|
+
};
|
|
827
860
|
try {
|
|
828
861
|
if ((await this.services.Clock.raceTimer(settled, SAVE_SETTLE_TIMEOUT_MS)) === TIMED_OUT) {
|
|
829
862
|
this.tracer.withUri(key).warn(`Save settle exceeded ${SAVE_SETTLE_TIMEOUT_MS}ms — returning anyway`);
|
|
863
|
+
// The race has settled, so a later failure of the wait, such as a given-up sync build, reaches no one else.
|
|
864
|
+
void settled.catch((error: unknown) => reportFailedWait(error, 'after the timeout'));
|
|
830
865
|
}
|
|
831
866
|
} catch (err: unknown) {
|
|
832
|
-
//
|
|
833
|
-
|
|
834
|
-
const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
835
|
-
this.tracer.withUri(key).warn(`Save settle failed before the timeout — returning anyway. ${detail}`);
|
|
867
|
+
// Its own line, so the failure is not blamed on the bound.
|
|
868
|
+
reportFailedWait(err, 'before the timeout — returning anyway');
|
|
836
869
|
}
|
|
837
870
|
}
|
|
838
871
|
|
|
@@ -1123,7 +1156,7 @@ export class DefaultModelService<
|
|
|
1123
1156
|
if (isClientGoneError(err)) {
|
|
1124
1157
|
uriLogger.debug(`applyEdit to ${LANGUAGE_CLIENT_ID} skipped: client disconnected`);
|
|
1125
1158
|
} else {
|
|
1126
|
-
uriLogger.error(`applyEdit to ${LANGUAGE_CLIENT_ID} failed: ${err}`);
|
|
1159
|
+
uriLogger.error(`applyEdit to ${LANGUAGE_CLIENT_ID} failed: ${describeErrorWithStack(err)}`);
|
|
1127
1160
|
}
|
|
1128
1161
|
}
|
|
1129
1162
|
}
|
|
@@ -1162,7 +1195,7 @@ export class DefaultModelService<
|
|
|
1162
1195
|
try {
|
|
1163
1196
|
listener(Object.freeze({ uris: built.map(document => this.uriPolicy.canonicalUri(document.uri.toString())), phase }));
|
|
1164
1197
|
} catch (err: unknown) {
|
|
1165
|
-
this.tracer.error(`onModelsBuilt listener threw: ${
|
|
1198
|
+
this.tracer.error(`onModelsBuilt listener threw: ${describeErrorWithStack(err)}`);
|
|
1166
1199
|
}
|
|
1167
1200
|
});
|
|
1168
1201
|
}
|
|
@@ -1267,9 +1300,7 @@ export class DefaultModelService<
|
|
|
1267
1300
|
try {
|
|
1268
1301
|
listener(event);
|
|
1269
1302
|
} catch (err: unknown) {
|
|
1270
|
-
this.tracer
|
|
1271
|
-
.with(uri)
|
|
1272
|
-
.error(`onModelUpdated listener threw: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`);
|
|
1303
|
+
this.tracer.with(uri).error(`onModelUpdated listener threw: ${describeErrorWithStack(err)}`);
|
|
1273
1304
|
}
|
|
1274
1305
|
}
|
|
1275
1306
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import { Deferred, type Project, type Tracer, UNQUALIFIED_PROJECT_REFERENCE } from '@hydranium/protocol';
|
|
10
|
+
import { Deferred, type Project, type Tracer, UNQUALIFIED_PROJECT_REFERENCE, describeErrorWithStack } from '@hydranium/protocol';
|
|
11
11
|
import {
|
|
12
12
|
type AstNode,
|
|
13
13
|
AstUtils,
|
|
@@ -226,7 +226,7 @@ export abstract class AbstractProjectManager<TProject extends Project = Project>
|
|
|
226
226
|
try {
|
|
227
227
|
entries = await this.fileSystemProvider.readDirectory(folder);
|
|
228
228
|
} catch (error) {
|
|
229
|
-
this.tracer.warn(`Failed to read directory ${folder.toString()}: ${error}`);
|
|
229
|
+
this.tracer.warn(`Failed to read directory ${folder.toString()}: ${describeErrorWithStack(error)}`);
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
232
|
for (const entry of entries) {
|
|
@@ -395,7 +395,7 @@ export abstract class AbstractProjectManager<TProject extends Project = Project>
|
|
|
395
395
|
}
|
|
396
396
|
return project;
|
|
397
397
|
} catch (error) {
|
|
398
|
-
this.tracer.error(`Failed to parse ${this.conceptName()} descriptor at ${uri.toString()}: ${error}`);
|
|
398
|
+
this.tracer.error(`Failed to parse ${this.conceptName()} descriptor at ${uri.toString()}: ${describeErrorWithStack(error)}`);
|
|
399
399
|
return undefined;
|
|
400
400
|
}
|
|
401
401
|
}
|
|
@@ -620,7 +620,7 @@ export abstract class AbstractProjectManager<TProject extends Project = Project>
|
|
|
620
620
|
try {
|
|
621
621
|
listener(event);
|
|
622
622
|
} catch (error) {
|
|
623
|
-
this.tracer.error(`Project change listener failed: ${error}`);
|
|
623
|
+
this.tracer.error(`Project change listener failed: ${describeErrorWithStack(error)}`);
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
}
|
|
@@ -59,6 +59,10 @@ import { type HydraniumAstNodeDescriptionProvider } from './ast-node-description
|
|
|
59
59
|
* duplicated walks.
|
|
60
60
|
*/
|
|
61
61
|
export class HydraniumScopeComputation extends DefaultScopeComputation {
|
|
62
|
+
/** The framework's name provider, which its language module binds to the slot. */
|
|
63
|
+
declare protected readonly nameProvider: NameProvider;
|
|
64
|
+
/** The framework's description provider, which its language module binds to the slot. */
|
|
65
|
+
declare protected readonly descriptions: HydraniumAstNodeDescriptionProvider;
|
|
62
66
|
protected readonly tracer: Tracer;
|
|
63
67
|
|
|
64
68
|
constructor(
|
|
@@ -155,17 +159,15 @@ export class HydraniumScopeComputation extends DefaultScopeComputation {
|
|
|
155
159
|
* workspace-visible rather than being filtered out everywhere.
|
|
156
160
|
*/
|
|
157
161
|
protected exportProject(node: AstNode, exports: AstNodeDescription[], document: LangiumDocument): void {
|
|
158
|
-
const
|
|
159
|
-
const documentName = nameProvider.getDocumentQualifiedName(node);
|
|
162
|
+
const documentName = this.nameProvider.getDocumentQualifiedName(node);
|
|
160
163
|
if (!documentName) {
|
|
161
164
|
return;
|
|
162
165
|
}
|
|
163
166
|
const projectId = this.getProjectIdForDocument(document);
|
|
164
|
-
const descriptions = this.descriptions as HydraniumAstNodeDescriptionProvider;
|
|
165
167
|
exports.push(
|
|
166
168
|
projectId !== undefined
|
|
167
|
-
? descriptions.createProject({ node, name: documentName, document, projectId })
|
|
168
|
-
: descriptions.createUniversal({ node, name: documentName, document })
|
|
169
|
+
? this.descriptions.createProject({ node, name: documentName, document, projectId })
|
|
170
|
+
: this.descriptions.createUniversal({ node, name: documentName, document })
|
|
169
171
|
);
|
|
170
172
|
}
|
|
171
173
|
|
|
@@ -182,16 +184,14 @@ export class HydraniumScopeComputation extends DefaultScopeComputation {
|
|
|
182
184
|
* document-qualified name.
|
|
183
185
|
*/
|
|
184
186
|
protected exportPublic(node: AstNode, exports: AstNodeDescription[], document: LangiumDocument): void {
|
|
185
|
-
const nameProvider = this.nameProvider as NameProvider;
|
|
186
187
|
const projectId = this.getProjectIdForDocument(document);
|
|
187
188
|
if (projectId === undefined) {
|
|
188
189
|
return;
|
|
189
190
|
}
|
|
190
|
-
const documentName = nameProvider.getDocumentQualifiedName(node);
|
|
191
|
-
const projectName = nameProvider.getProjectQualifiedName(node);
|
|
191
|
+
const documentName = this.nameProvider.getDocumentQualifiedName(node);
|
|
192
|
+
const projectName = this.nameProvider.getProjectQualifiedName(node);
|
|
192
193
|
if (projectName && projectName !== documentName) {
|
|
193
|
-
|
|
194
|
-
exports.push(descriptions.createPublic({ node, name: projectName, document, projectId }));
|
|
194
|
+
exports.push(this.descriptions.createPublic({ node, name: projectName, document, projectId }));
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -239,9 +239,9 @@ export class HydraniumScopeComputation extends DefaultScopeComputation {
|
|
|
239
239
|
if (!container) {
|
|
240
240
|
return;
|
|
241
241
|
}
|
|
242
|
-
const name =
|
|
242
|
+
const name = this.nameProvider.getOwnName(node);
|
|
243
243
|
if (name) {
|
|
244
|
-
symbols.add(container,
|
|
244
|
+
symbols.add(container, this.descriptions.createLocal({ node, name, document }));
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
}
|
|
@@ -698,9 +698,11 @@ export class HydraniumDocumentValidator extends DefaultDocumentValidator {
|
|
|
698
698
|
*/
|
|
699
699
|
protected buildElementPath<N extends AstNode>(node: N, info: DiagnosticInfo<N, string>): string {
|
|
700
700
|
const directPath = this.astNodeLocator.getAstNodePath(node);
|
|
701
|
-
|
|
702
|
-
const
|
|
703
|
-
|
|
701
|
+
// The locator answers '' for the root, which is the root's path rather than a miss.
|
|
702
|
+
const isRoot = node.$container === undefined;
|
|
703
|
+
const containerPath = directPath || isRoot ? undefined : this.astNodeLocator.getAstNodePath(node.$container);
|
|
704
|
+
const nodePath = directPath || containerPath || '';
|
|
705
|
+
if (!nodePath && !isRoot) {
|
|
704
706
|
this.tracer.warn('Cannot create diagnostic element path: Unable to determine AST node path.');
|
|
705
707
|
return '';
|
|
706
708
|
}
|
|
@@ -18,7 +18,7 @@ import { runInWriteLockScope } from './write-lock-scope.js';
|
|
|
18
18
|
export interface HydraniumWorkspaceLockOptions extends LogNameOptions {
|
|
19
19
|
/**
|
|
20
20
|
* How long a write may keep running after a later write cancelled it before
|
|
21
|
-
* the lock logs
|
|
21
|
+
* the lock logs a warning. Every write and read waits behind it meanwhile, so
|
|
22
22
|
* one that never ends stops the server. Defaults to 10 s; `0` turns the
|
|
23
23
|
* check off.
|
|
24
24
|
*/
|
|
@@ -109,7 +109,7 @@ export class HydraniumWorkspaceLock extends DefaultWorkspaceLock {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
protected reportStalledWrite(): void {
|
|
112
|
-
this.tracer.
|
|
112
|
+
this.tracer.warn(
|
|
113
113
|
`A write is still running ${this.stalledWriteWarnMs} ms after it was cancelled, and every write and read waits behind it. ` +
|
|
114
114
|
'Likely causes: a model-service write or rebuild from inside a build in a host with no write-lock scope tracker, ' +
|
|
115
115
|
'which waits for the build it runs in, or a write action that ignores its cancellation token. ' +
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import { Deferred, type Logger, type Tracer } from '@hydranium/protocol';
|
|
10
|
+
import { Deferred, type Logger, type Tracer, describeError, describeErrorWithStack } from '@hydranium/protocol';
|
|
11
11
|
import {
|
|
12
12
|
DefaultWorkspaceManager,
|
|
13
13
|
DocumentState,
|
|
@@ -69,7 +69,7 @@ export function handleProcessUnhandledRejection(reason: unknown, logger: Logger)
|
|
|
69
69
|
if (typeof reason === 'symbol' && String(reason) === 'Symbol(OperationCancelled)') {
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
|
-
logger.error('Unhandled promise rejection: ' + (reason
|
|
72
|
+
logger.error('Unhandled promise rejection: ' + describeErrorWithStack(reason));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -507,11 +507,10 @@ export class HydraniumWorkspaceManager extends DefaultWorkspaceManager {
|
|
|
507
507
|
await super.initializeWorkspace(folders, cancelToken);
|
|
508
508
|
this.workspaceInitializedDeferred.resolve(undefined);
|
|
509
509
|
} catch (error: unknown) {
|
|
510
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
511
510
|
if (isOperationCancelled(error) || isConnectionGoneError(error)) {
|
|
512
|
-
this.tracer.debug(`Initial workspace build did not complete: ${
|
|
511
|
+
this.tracer.debug(`Initial workspace build did not complete: ${describeError(error)}`);
|
|
513
512
|
} else {
|
|
514
|
-
this.tracer.error(`Initial workspace build failed: ${
|
|
513
|
+
this.tracer.error(`Initial workspace build failed: ${describeErrorWithStack(error)}`);
|
|
515
514
|
}
|
|
516
515
|
this.workspaceInitializedDeferred.reject(error);
|
|
517
516
|
throw error;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
10
|
import { URI } from '@hydranium/langium';
|
|
11
|
+
import { describeErrorWithStack } from '@hydranium/protocol';
|
|
11
12
|
import { type WritableFileSystemProvider } from './file-system-provider.js';
|
|
12
13
|
import { serverSharedFactory, type ServerSharedServicesMinimal } from '../shared-services.js';
|
|
13
14
|
import { type FileSystemSeed, InMemoryFileSystemProvider, type InMemoryFileSystemOptions } from './in-memory-file-system-provider.js';
|
|
@@ -250,7 +251,7 @@ export class PersistentFileSystemProvider extends InMemoryFileSystemProvider {
|
|
|
250
251
|
}
|
|
251
252
|
|
|
252
253
|
protected reportStoreFailure(operation: string, uri: URI, error: unknown): void {
|
|
253
|
-
this.tracer.error(`Failed to ${operation} ${uri.toString()}: ${
|
|
254
|
+
this.tracer.error(`Failed to ${operation} ${uri.toString()}: ${describeErrorWithStack(error)}`);
|
|
254
255
|
}
|
|
255
256
|
}
|
|
256
257
|
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
* primarily by tests waiting for tidy teardown.
|
|
21
21
|
*
|
|
22
22
|
* Either promise may reject if startup or shutdown encounters an error
|
|
23
|
-
* the head can't recover from.
|
|
23
|
+
* the head can't recover from. A launcher given a logger logs its own startup
|
|
24
|
+
* failure, so a rejected `started` is control flow: the caller stops what
|
|
25
|
+
* depends on it without logging the failure again.
|
|
24
26
|
*/
|
|
25
27
|
export interface IntegratedServer {
|
|
26
28
|
readonly started: Promise<void>;
|
|
@@ -7,7 +7,15 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
Debouncer,
|
|
12
|
+
Deferred,
|
|
13
|
+
type Logger,
|
|
14
|
+
ObservableValue,
|
|
15
|
+
type MaybeObservableValue,
|
|
16
|
+
TIMED_OUT,
|
|
17
|
+
describeErrorWithStack
|
|
18
|
+
} from '@hydranium/protocol';
|
|
11
19
|
import { URI, UriUtils } from '@hydranium/langium';
|
|
12
20
|
import { DefaultDocumentUpdateHandler } from '@hydranium/langium/lsp';
|
|
13
21
|
import { HYDRANIUM_BUILD_REASONS, type HydraniumDocumentBuilder } from '../langium/document-builder/document-builder.js';
|
|
@@ -221,7 +229,9 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
|
|
|
221
229
|
for (const change of filtered.changes) {
|
|
222
230
|
this.textDocuments
|
|
223
231
|
.reloadDiskBaseline(change.uri)
|
|
224
|
-
.catch((err: unknown) =>
|
|
232
|
+
.catch((err: unknown) =>
|
|
233
|
+
this.logger.error(`Disk baseline not reloaded for ${change.uri}. ${describeErrorWithStack(err)}`)
|
|
234
|
+
);
|
|
225
235
|
}
|
|
226
236
|
// External file-system changes are authoritative — overwrite any
|
|
227
237
|
// pending reason (typically empty here; could be `didChangeContent`
|
|
@@ -238,7 +248,7 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
|
|
|
238
248
|
// `unhandledRejection` listener, which reports neither the URIs nor
|
|
239
249
|
// the fact that a watched-file rebuild was dropped — leaving the
|
|
240
250
|
// workspace diverged from disk with no diagnostic and no retry.
|
|
241
|
-
const detail =
|
|
251
|
+
const detail = describeErrorWithStack(err);
|
|
242
252
|
const uris = params.changes.map(change => change.uri).join(', ');
|
|
243
253
|
this.logger.error(`Watched-file update dropped for ${uris}. ${detail}`);
|
|
244
254
|
});
|
|
@@ -293,9 +303,7 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
|
|
|
293
303
|
});
|
|
294
304
|
} catch (err) {
|
|
295
305
|
cap.dispose();
|
|
296
|
-
this.logger.error(
|
|
297
|
-
`willSaveWaitUntil for ${uri} answered without waiting. ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`
|
|
298
|
-
);
|
|
306
|
+
this.logger.error(`willSaveWaitUntil for ${uri} answered without waiting. ${describeErrorWithStack(err)}`);
|
|
299
307
|
release();
|
|
300
308
|
resolve([]);
|
|
301
309
|
}
|
|
@@ -417,7 +425,7 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
|
|
|
417
425
|
if (isConnectionGoneError(err)) {
|
|
418
426
|
return;
|
|
419
427
|
}
|
|
420
|
-
const detail =
|
|
428
|
+
const detail = describeErrorWithStack(err);
|
|
421
429
|
this.logger.error(`Workspace initialization failed. Could not perform document update. ${detail}`);
|
|
422
430
|
});
|
|
423
431
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
10
|
import { monitorEventLoopDelay } from 'node:perf_hooks';
|
|
11
|
+
import { describeErrorWithStack } from '@hydranium/protocol';
|
|
11
12
|
import { formatMemory } from '../langium/diagnostics/logger.js';
|
|
12
13
|
import { type ServerSharedServicesMinimal } from '../langium/shared-services.js';
|
|
13
14
|
import { formatServerState } from './server-state-snapshot.js';
|
|
@@ -74,7 +75,7 @@ export function startEventLoopMonitor(shared: ServerSharedServicesMinimal, optio
|
|
|
74
75
|
try {
|
|
75
76
|
onStallSnapshot(maxMs);
|
|
76
77
|
} catch (error) {
|
|
77
|
-
logger.warn(`Stall snapshot failed: ${
|
|
78
|
+
logger.warn(`Stall snapshot failed: ${describeErrorWithStack(error)}`);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from './event-loop-monitor.js';
|
|
18
18
|
import { MEMORY_DELTA_BYTES } from '../langium/diagnostics/logger.js';
|
|
19
19
|
import { DEFAULT_MEMORY_CRITICAL_THRESHOLD, DEFAULT_MEMORY_WARN_THRESHOLD } from './memory-monitor.js';
|
|
20
|
+
import { type ServerDiagnosticsOptions } from './server-diagnostics.js';
|
|
20
21
|
import { type LogPreambleOptions } from '../langium/diagnostics/log-preamble.js';
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -37,19 +38,25 @@ export function nodeSystemInfoLines(): string[] {
|
|
|
37
38
|
* Node monitor + memory-suffix convention lines for
|
|
38
39
|
* {@link LogPreambleOptions.conventionLines}. These describe the `[EventLoop]`
|
|
39
40
|
* / `[Memory]` monitors and the heap/rss suffix — all Node-only behaviour, so
|
|
40
|
-
* they're omitted from the portable preamble.
|
|
41
|
+
* they're omitted from the portable preamble. Pass the options the monitors
|
|
42
|
+
* were started with, or the lines state the defaults.
|
|
41
43
|
*/
|
|
42
|
-
export function nodeConventionLines(
|
|
44
|
+
export function nodeConventionLines(
|
|
45
|
+
phaseDetailThresholdMs = 50,
|
|
46
|
+
monitors: Pick<ServerDiagnosticsOptions, 'eventLoop' | 'memory'> = {}
|
|
47
|
+
): string[] {
|
|
43
48
|
const memoryDeltaMb = MEMORY_DELTA_BYTES / (1024 * 1024);
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
49
|
+
const thresholdMs = monitors.eventLoop?.thresholdMs ?? DEFAULT_EVENT_LOOP_THRESHOLD_MS;
|
|
50
|
+
const intervalSeconds = (monitors.eventLoop?.intervalMs ?? DEFAULT_EVENT_LOOP_INTERVAL_MS) / 1000;
|
|
51
|
+
const snapshotAboveMs = monitors.eventLoop?.snapshotAboveMs ?? DEFAULT_EVENT_LOOP_SNAPSHOT_ABOVE_MS;
|
|
52
|
+
const warnPercent = Math.round((monitors.memory?.warnThreshold ?? DEFAULT_MEMORY_WARN_THRESHOLD) * 100);
|
|
53
|
+
const criticalPercent = Math.round((monitors.memory?.criticalThreshold ?? DEFAULT_MEMORY_CRITICAL_THRESHOLD) * 100);
|
|
47
54
|
return [
|
|
48
55
|
`• Memory suffix: heap/rss included on [done] only when heap moved >${memoryDeltaMb}MB since last log,`,
|
|
49
56
|
` always included on rebuilds >=${phaseDetailThresholdMs}ms and on [EventLoop] stall warnings`,
|
|
50
|
-
`• [EventLoop]: warns when a stall >=${
|
|
51
|
-
` stalls >=${
|
|
52
|
-
`• [Memory]: warns >=${warnPercent}% of V8 heap_size_limit,
|
|
57
|
+
`• [EventLoop]: warns when a stall >=${thresholdMs}ms is observed within a ${intervalSeconds}s window;`,
|
|
58
|
+
` stalls >=${snapshotAboveMs}ms also trigger a [Process] server-state snapshot`,
|
|
59
|
+
`• [Memory]: warns >=${warnPercent}% of V8 heap_size_limit, again as imminent OOM >=${criticalPercent}%, info on recovery`
|
|
53
60
|
];
|
|
54
61
|
}
|
|
55
62
|
|
|
@@ -58,6 +65,9 @@ export function nodeConventionLines(phaseDetailThresholdMs = 50): string[] {
|
|
|
58
65
|
* the portable `logPreamble`'s options on a Node host:
|
|
59
66
|
* `logPreamble(logger, { productName, version, ...nodeLogPreambleSections() })`.
|
|
60
67
|
*/
|
|
61
|
-
export function nodeLogPreambleSections(
|
|
62
|
-
|
|
68
|
+
export function nodeLogPreambleSections(
|
|
69
|
+
phaseDetailThresholdMs = 50,
|
|
70
|
+
monitors: Pick<ServerDiagnosticsOptions, 'eventLoop' | 'memory'> = {}
|
|
71
|
+
): Pick<LogPreambleOptions, 'systemInfoLines' | 'conventionLines'> {
|
|
72
|
+
return { systemInfoLines: nodeSystemInfoLines(), conventionLines: nodeConventionLines(phaseDetailThresholdMs, monitors) };
|
|
63
73
|
}
|