@harperfast/harper 5.3.0-alpha.1 → 5.3.0-beta.1
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/bin/cliOperations.ts +12 -0
- package/bin/restart.ts +66 -6
- package/components/Application.ts +1134 -112
- package/components/OptionsWatcher.ts +368 -102
- package/components/Scope.ts +20 -1
- package/components/componentLoader.ts +43 -4
- package/components/deploymentOperations.ts +4 -1
- package/components/deploymentRecorder.ts +9 -2
- package/components/operations.js +284 -52
- package/components/operationsValidation.js +49 -2
- package/components/packageComponent.ts +25 -1
- package/components/requestRestart.ts +11 -0
- package/config/RootConfigWatcher.ts +191 -37
- package/config/configReadRetry.ts +62 -0
- package/config/configUtils.ts +78 -26
- package/config/parseConfigFile.ts +34 -0
- package/config/readConfigFileSync.ts +44 -0
- package/config/watcherArming.ts +59 -0
- package/config-root.schema.json +4 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +28 -2
- package/dist/bin/cliOperations.js +13 -0
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/restart.js +42 -6
- package/dist/bin/restart.js.map +1 -1
- package/dist/components/Application.d.ts +104 -9
- package/dist/components/Application.js +954 -102
- package/dist/components/Application.js.map +1 -1
- package/dist/components/OptionsWatcher.d.ts +4 -1
- package/dist/components/OptionsWatcher.js +378 -104
- package/dist/components/OptionsWatcher.js.map +1 -1
- package/dist/components/Scope.js +15 -1
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/componentLoader.js +35 -3
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/deploymentOperations.js +4 -1
- package/dist/components/deploymentOperations.js.map +1 -1
- package/dist/components/deploymentRecorder.d.ts +4 -2
- package/dist/components/deploymentRecorder.js +1 -0
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/operations.d.ts +28 -0
- package/dist/components/operations.js +263 -54
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +48 -2
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/packageComponent.js +24 -0
- package/dist/components/packageComponent.js.map +1 -1
- package/dist/components/requestRestart.d.ts +1 -0
- package/dist/components/requestRestart.js +7 -0
- package/dist/components/requestRestart.js.map +1 -1
- package/dist/config/RootConfigWatcher.d.ts +2 -0
- package/dist/config/RootConfigWatcher.js +189 -35
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configReadRetry.d.ts +8 -0
- package/dist/config/configReadRetry.js +62 -0
- package/dist/config/configReadRetry.js.map +1 -0
- package/dist/config/configUtils.d.ts +10 -9
- package/dist/config/configUtils.js +63 -27
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/parseConfigFile.d.ts +4 -0
- package/dist/config/parseConfigFile.js +35 -0
- package/dist/config/parseConfigFile.js.map +1 -0
- package/dist/config/readConfigFileSync.d.ts +1 -0
- package/dist/config/readConfigFileSync.js +47 -0
- package/dist/config/readConfigFileSync.js.map +1 -0
- package/dist/config/watcherArming.d.ts +15 -0
- package/dist/config/watcherArming.js +59 -0
- package/dist/config/watcherArming.js.map +1 -0
- package/dist/dataLayer/harperBridge/ResourceBridge.js +21 -2
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +3 -0
- package/dist/resources/DatabaseTransaction.d.ts +25 -0
- package/dist/resources/DatabaseTransaction.js +224 -18
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +2 -1
- package/dist/resources/LMDBTransaction.js +22 -3
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.js +22 -6
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +1 -1
- package/dist/resources/RecordEncoder.js +21 -5
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/Resource.js +97 -13
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +8 -0
- package/dist/resources/RocksIndexStore.js +2 -1
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +10 -0
- package/dist/resources/RocksTransactionLogStore.js +104 -33
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +58 -7
- package/dist/resources/Table.js +1095 -346
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.js +10 -3
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/auditStore.d.ts +170 -0
- package/dist/resources/auditStore.js +457 -11
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/dataLoader.js +3 -4
- package/dist/resources/dataLoader.js.map +1 -1
- package/dist/resources/databases.d.ts +16 -13
- package/dist/resources/databases.js +624 -176
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/derivedIndexRegistry.d.ts +5 -0
- package/dist/resources/derivedIndexRegistry.js +68 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -0
- package/dist/resources/derivedIndexRuntime.d.ts +215 -0
- package/dist/resources/derivedIndexRuntime.js +2027 -0
- package/dist/resources/derivedIndexRuntime.js.map +1 -0
- package/dist/resources/graphql.js +3 -2
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +102 -9
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +848 -39
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
- package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
- package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
- package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
- package/dist/resources/nodeIdMapping.d.ts +5 -0
- package/dist/resources/nodeIdMapping.js +49 -0
- package/dist/resources/nodeIdMapping.js.map +1 -1
- package/dist/resources/recordLock.d.ts +47 -4
- package/dist/resources/recordLock.js +138 -7
- package/dist/resources/recordLock.js.map +1 -1
- package/dist/resources/recordLockCoordinator.d.ts +557 -0
- package/dist/resources/recordLockCoordinator.js +2565 -0
- package/dist/resources/recordLockCoordinator.js.map +1 -0
- package/dist/resources/replayLogs.js +5 -0
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/replicatedApplyFailure.d.ts +16 -0
- package/dist/resources/replicatedApplyFailure.js +63 -0
- package/dist/resources/replicatedApplyFailure.js.map +1 -0
- package/dist/resources/scheduler/scheduler.js +3 -3
- package/dist/resources/scheduler/scheduler.js.map +1 -1
- package/dist/resources/search.d.ts +10 -4
- package/dist/resources/search.js +160 -40
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/tracked.d.ts +5 -1
- package/dist/resources/tracked.js +74 -23
- package/dist/resources/tracked.js.map +1 -1
- package/dist/security/jsLoader.js +6 -4
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/server/REST.js +33 -2
- package/dist/server/REST.js.map +1 -1
- package/dist/server/http.d.ts +5 -1
- package/dist/server/http.js +34 -2
- package/dist/server/http.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +2 -0
- package/dist/server/serverHelpers/Headers.js +6 -0
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
- package/dist/server/serverHelpers/Request.d.ts +5 -10
- package/dist/server/serverHelpers/Request.js +38 -136
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +2 -0
- package/dist/server/serverHelpers/contentTypes.js +189 -15
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.js +96 -17
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/storageReclamation.js +1 -1
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/isolatedApplications.d.ts +47 -0
- package/dist/server/threads/isolatedApplications.js +171 -0
- package/dist/server/threads/isolatedApplications.js.map +1 -0
- package/dist/server/threads/logRotationTransport.d.ts +1 -0
- package/dist/server/threads/logRotationTransport.js +33 -0
- package/dist/server/threads/logRotationTransport.js.map +1 -0
- package/dist/server/threads/manageThreads.d.ts +64 -6
- package/dist/server/threads/manageThreads.js +261 -12
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.d.ts +1 -0
- package/dist/server/threads/socketRouter.js +196 -13
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +30 -7
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +24 -0
- package/dist/utility/errors/hdbError.js +58 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/hdbTerms.d.ts +2 -0
- package/dist/utility/hdbTerms.js +2 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.js +217 -38
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
- package/dist/utility/logging/logGenerationCoordinator.js +184 -0
- package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
- package/dist/utility/logging/logRotation.d.ts +46 -0
- package/dist/utility/logging/logRotation.js +365 -0
- package/dist/utility/logging/logRotation.js.map +1 -0
- package/dist/utility/logging/logRotator.d.ts +1 -1
- package/dist/utility/logging/logRotator.js +172 -92
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/npmUtilities.js +6 -4
- package/dist/utility/npmUtilities.js.map +1 -1
- package/dist/utility/watcherFallback.d.ts +0 -45
- package/dist/utility/watcherFallback.js +1 -125
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.js +6 -3
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +6 -0
- package/json/systemSchema.json +3 -0
- package/npm-shrinkwrap.json +131 -41
- package/package.json +10 -3
- package/resources/DESIGN.md +124 -19
- package/resources/DatabaseTransaction.ts +230 -17
- package/resources/LMDBTransaction.ts +21 -3
- package/resources/PrimaryRocksDatabase.ts +20 -7
- package/resources/RecordEncoder.ts +25 -5
- package/resources/Resource.ts +97 -13
- package/resources/ResourceInterface.ts +8 -0
- package/resources/RocksIndexStore.ts +2 -1
- package/resources/RocksTransactionLogStore.ts +111 -31
- package/resources/Table.ts +1224 -393
- package/resources/analytics/write.ts +10 -3
- package/resources/auditStore.ts +460 -11
- package/resources/dataLoader.ts +3 -4
- package/resources/databases.ts +610 -146
- package/resources/derivedIndexRegistry.ts +56 -0
- package/resources/derivedIndexRuntime.ts +2292 -0
- package/resources/graphql.ts +3 -2
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +905 -46
- package/resources/indexes/hnswDerivedIndex.ts +531 -0
- package/resources/indexes/hnswPlaneBinding.ts +174 -0
- package/resources/nodeIdMapping.ts +50 -0
- package/resources/recordLock.ts +173 -7
- package/resources/recordLockCoordinator.ts +3043 -0
- package/resources/replayLogs.ts +5 -0
- package/resources/replicatedApplyFailure.ts +77 -0
- package/resources/scheduler/scheduler.ts +4 -4
- package/resources/search.ts +169 -49
- package/resources/tracked.ts +73 -22
- package/security/jsLoader.ts +6 -4
- package/server/DESIGN.md +11 -0
- package/server/REST.ts +36 -3
- package/server/http.ts +34 -2
- package/server/serverHelpers/Headers.ts +5 -1
- package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
- package/server/serverHelpers/Request.ts +33 -131
- package/server/serverHelpers/contentTypes.ts +188 -15
- package/server/serverHelpers/serverUtilities.ts +143 -24
- package/server/storageReclamation.ts +2 -2
- package/server/threads/isolatedApplications.ts +157 -0
- package/server/threads/logRotationTransport.ts +40 -0
- package/server/threads/manageThreads.js +254 -12
- package/server/threads/socketRouter.ts +217 -11
- package/server/threads/threadServer.js +30 -7
- package/studio/web/assets/{Chat-BnCBegQz.js → Chat-D3j-1yY1.js} +1 -1
- package/studio/web/assets/{FloatingChat-CoDW1ySS.js → FloatingChat-BxJGYcfB.js} +3 -3
- package/studio/web/assets/{apiToken-Bwk5BLXW.js → apiToken-CT55oWOe.js} +1 -1
- package/studio/web/assets/{applications-DHxGi7JH.js → applications-D9Ct9_vm.js} +1 -1
- package/studio/web/assets/{cssMode-s0cWI-_M.js → cssMode-DV8H7VwA.js} +1 -1
- package/studio/web/assets/{editor-DNcRHK54.js → editor-uatc0unt.js} +1 -1
- package/studio/web/assets/{html-Bdssedlg.js → html-Bm6D6paN.js} +1 -1
- package/studio/web/assets/{htmlMode-CoDlJ3fw.js → htmlMode-CEn7tpLG.js} +1 -1
- package/studio/web/assets/{index-D6sxmFLR.js → index-BIXW6Pu4.js} +5 -5
- package/studio/web/assets/{index.lazy-tmU5BS8s.js → index.lazy-UI7L-Vrk.js} +1 -1
- package/studio/web/assets/{javascript-B8meVSTH.js → javascript-CJ0G3AFZ.js} +1 -1
- package/studio/web/assets/{jsonMode-DpIPd35T.js → jsonMode-DQADAYEa.js} +1 -1
- package/studio/web/assets/{languageServices-C_5FMJzQ.js → languageServices-CAQJXWcI.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-BIzNBzPK.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
- package/studio/web/assets/{notifications-CQf18QKb.js → notifications-BbxTU6Aw.js} +1 -1
- package/studio/web/assets/{notifications-CvZivSbh.js → notifications-Cvb3P1lB.js} +1 -1
- package/studio/web/assets/{profile-DdOwtntb.js → profile-Yyb7gsvL.js} +1 -1
- package/studio/web/assets/{regions-n69fwagr.js → regions-OgjGHlU5.js} +1 -1
- package/studio/web/assets/{register-PfWTCXWB.js → register-6qwNEOY3.js} +2 -2
- package/studio/web/assets/{setComponentFile-Bg6O7X0S.js → setComponentFile-BilDMtgB.js} +1 -1
- package/studio/web/assets/{setup-CUx_aUDl.js → setup-J6qJ7OIU.js} +2 -2
- package/studio/web/assets/{status-D7BVKqX9.js → status-0RWGcfyD.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-DBSyXzMr.js → toggleHighContrast-BIn-vErT.js} +1 -1
- package/studio/web/assets/{tsMode-BByKCjBS.js → tsMode-DgUXku4d.js} +1 -1
- package/studio/web/assets/{typescript-DDLnLpw9.js → typescript-C9orXcsM.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-31CHGaHk.js → useEntityRestURL-BEoXXbUB.js} +1 -1
- package/studio/web/assets/{workers-pR3jRY9D.js → workers-JVzSDmgx.js} +1 -1
- package/studio/web/assets/{xml-2iRnMhQO.js → xml-Cq-S8S4X.js} +1 -1
- package/studio/web/assets/{yaml-Bf92gJpd.js → yaml-sfoRdh1M.js} +1 -1
- package/studio/web/index.html +1 -1
- package/utility/errors/hdbError.ts +54 -0
- package/utility/hdbTerms.ts +2 -0
- package/utility/logging/harper_logger.ts +209 -30
- package/utility/logging/logGenerationCoordinator.ts +196 -0
- package/utility/logging/logRotation.ts +367 -0
- package/utility/logging/logRotator.ts +196 -91
- package/utility/npmUtilities.ts +6 -4
- package/utility/watcherFallback.ts +0 -122
- package/validation/configValidator.ts +6 -3
|
@@ -73,6 +73,8 @@ const packageUtils_js_1 = require("../../utility/packageUtils.js");
|
|
|
73
73
|
const globals_js_1 = require("../../globals.js");
|
|
74
74
|
const console_1 = require("console");
|
|
75
75
|
const node_util_1 = require("node:util");
|
|
76
|
+
const logRotation_ts_1 = require("./logRotation.js");
|
|
77
|
+
const logGenerationCoordinator_ts_1 = require("./logGenerationCoordinator.js");
|
|
76
78
|
const { isNativeError } = node_util_1.types;
|
|
77
79
|
// store the native write function so we can call it after we write to the log file (and store it on process.stdout
|
|
78
80
|
// because unit tests will create multiple instances of this module)
|
|
@@ -101,6 +103,17 @@ exports.OUTPUTS = {
|
|
|
101
103
|
// Location of default config YAML.
|
|
102
104
|
const DEFAULT_CONFIG_FILE = join(packageUtils_js_1.PACKAGE_ROOT, 'static', hdbTerms.HDB_DEFAULT_CONFIG_FILE);
|
|
103
105
|
const CLOSE_LOG_FD_TIMEOUT = 10000;
|
|
106
|
+
// Above the module-scope initLogSettings() call below, which reaches getFileLogger: a const
|
|
107
|
+
// declared after it is still in its temporal dead zone when the file sink first reads one.
|
|
108
|
+
const LOG_TIME_USAGE_THRESHOLD = 100;
|
|
109
|
+
// How long to write straight to stdio after the log file refuses an append.
|
|
110
|
+
const APPEND_RETRY_COOLDOWN = 5000;
|
|
111
|
+
// Ceiling on how often a failing rotation may report itself; the failure repeats every check point.
|
|
112
|
+
const ROTATION_REPORT_INTERVAL = 60000;
|
|
113
|
+
const SET_ROTATION_POLICY = Symbol('setRotationPolicy');
|
|
114
|
+
const GET_ROTATION_POLICY = Symbol('getRotationPolicy');
|
|
115
|
+
const FILE_ROTATION_SOURCE = Symbol('fileRotationSource');
|
|
116
|
+
const SERVICE_NAME = worker_threads_1.workerData?.name?.replace(/ /g, '-') || 'main';
|
|
104
117
|
let logConsole;
|
|
105
118
|
let log_to_file;
|
|
106
119
|
let logToStdstreams;
|
|
@@ -159,7 +172,24 @@ function updateLogger(logger, logOptions, name, mainLoggerRef = mainLogger) {
|
|
|
159
172
|
// rotation entirely (#1877). Excluded when `logger` IS mainLoggerRef: the fallback would just
|
|
160
173
|
// reassign mainLogger.rotation to itself, making it impossible to ever clear main's rotation.
|
|
161
174
|
const inheritedRotation = logOptions.rotation ?? (logger === mainLoggerRef ? undefined : mainLoggerRef?.rotation);
|
|
162
|
-
|
|
175
|
+
const loggerPolicy = logger[GET_ROTATION_POLICY]?.();
|
|
176
|
+
const mainPolicy = mainLoggerRef?.[GET_ROTATION_POLICY]?.();
|
|
177
|
+
const inheritsRotation = logOptions.rotation == null && logger !== mainLoggerRef;
|
|
178
|
+
const rotationSource = inheritsRotation ? mainPolicy?.source : loggerPolicy?.ownSource;
|
|
179
|
+
let path = logOptions.path;
|
|
180
|
+
if (path) {
|
|
181
|
+
if (!logOptions.root)
|
|
182
|
+
logOptions.root = pathModule.dirname(path);
|
|
183
|
+
}
|
|
184
|
+
else if (logOptions.root) {
|
|
185
|
+
path = join(logOptions.root, logName);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
path = mainLoggerRef.path;
|
|
189
|
+
if (!logOptions.root)
|
|
190
|
+
logOptions.root = pathModule.dirname(path);
|
|
191
|
+
}
|
|
192
|
+
if (inheritedRotation && inheritedRotation === mainLoggerRef?.rotation && path !== mainLoggerRef?.path) {
|
|
163
193
|
// This logger has no rotation block of its own and is inheriting main's wholesale — but
|
|
164
194
|
// inheriting `rotation.path` too would point this logger's archives at wherever main
|
|
165
195
|
// archives to. If this logger's file lives on a different filesystem/volume than that
|
|
@@ -170,25 +200,18 @@ function updateLogger(logger, logOptions, name, mainLoggerRef = mainLogger) {
|
|
|
170
200
|
// goes through `logOptions.rotation` above, not this fallback. Clone rather than mutate:
|
|
171
201
|
// `inheritedRotation` is the literal object shared by mainLogger.rotation and every other
|
|
172
202
|
// component that inherited it.
|
|
203
|
+
//
|
|
204
|
+
// Only when the paths differ. A logger writing to main's own file shares main's rotation
|
|
205
|
+
// state — file loggers are cached per path — so stripping there discards the operator's
|
|
206
|
+
// configured rotation.path for the main log itself, which no EXDEV risk justifies.
|
|
173
207
|
const { path: _inheritedPath, ...rotationWithoutPath } = inheritedRotation;
|
|
174
|
-
|
|
208
|
+
setRotationPolicy(rotationWithoutPath);
|
|
175
209
|
}
|
|
176
210
|
else {
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
let path = logOptions.path;
|
|
180
|
-
if (path) {
|
|
181
|
-
if (!logOptions.root)
|
|
182
|
-
logOptions.root = pathModule.dirname(path);
|
|
183
|
-
}
|
|
184
|
-
else if (logOptions.root) {
|
|
185
|
-
path = join(logOptions.root, logName);
|
|
186
|
-
}
|
|
187
|
-
else {
|
|
188
|
-
path = mainLoggerRef.path;
|
|
189
|
-
if (!logOptions.root)
|
|
190
|
-
logOptions.root = pathModule.dirname(path);
|
|
211
|
+
setRotationPolicy(inheritedRotation);
|
|
191
212
|
}
|
|
213
|
+
// After the rotation above: assigning `path` goes through the setter that rebuilds this path's
|
|
214
|
+
// file logger, and it reads `logger.rotation` as it stands at that moment.
|
|
192
215
|
if (path)
|
|
193
216
|
logger.path = path;
|
|
194
217
|
else
|
|
@@ -199,6 +222,12 @@ function updateLogger(logger, logOptions, name, mainLoggerRef = mainLogger) {
|
|
|
199
222
|
// if there is a configured tag or if a component is logging to default/main log path, use the component name as the tag
|
|
200
223
|
// to differentiate it
|
|
201
224
|
logger.tag = logOptions.tag ?? ((mainLoggerRef.path === logger.path || exports.externalLogger.path === logger.path) && name);
|
|
225
|
+
function setRotationPolicy(rotation) {
|
|
226
|
+
if (logger[SET_ROTATION_POLICY])
|
|
227
|
+
logger[SET_ROTATION_POLICY](rotation, rotationSource, inheritsRotation);
|
|
228
|
+
else
|
|
229
|
+
logger.rotation = rotation;
|
|
230
|
+
}
|
|
202
231
|
}
|
|
203
232
|
// creates a logger where the methods are only defined if they are within the log level.
|
|
204
233
|
// Using this conditional logger means that every method call must be optional like log.trace?.('message),
|
|
@@ -233,7 +262,19 @@ async function updateLogSettings() {
|
|
|
233
262
|
// TODO: Any way to differentiate changes that we can and can't handle?
|
|
234
263
|
rootConfig.on('change', updateLogSettings);
|
|
235
264
|
}
|
|
236
|
-
|
|
265
|
+
applyLogSettings(rootConfig.config);
|
|
266
|
+
}
|
|
267
|
+
// Separate from the watcher wiring above because the watcher it builds reads a process-wide path,
|
|
268
|
+
// which leaves no way to exercise these settings against a given config.
|
|
269
|
+
function applyLogSettings(rootConfigObject) {
|
|
270
|
+
// `ready` settles on every terminal read outcome, including ones that carry no config at all —
|
|
271
|
+
// see DESIGN.md, "Every terminal read outcome settles the barrier". Applying that as settings
|
|
272
|
+
// would not be "the defaults": `updateLogger` reads an absent `rotation` as rotation off and an
|
|
273
|
+
// absent `console` as console off, so an unreadable config would silently disable logging.
|
|
274
|
+
// What `initLogSettings()` established at startup stands until a real config arrives, which
|
|
275
|
+
// is why the watcher settles those outcomes with no config rather than an empty one.
|
|
276
|
+
if (!rootConfigObject || typeof rootConfigObject !== 'object')
|
|
277
|
+
return;
|
|
237
278
|
const logOptions = rootConfigObject.logging ?? {};
|
|
238
279
|
// Resolve relative paths against rootPath from the same config
|
|
239
280
|
const rootPath = rootConfigObject.rootPath;
|
|
@@ -256,7 +297,9 @@ async function updateLogSettings() {
|
|
|
256
297
|
for (const name in rootConfigObject) {
|
|
257
298
|
// we now scan each component to see if it has logging individual configured
|
|
258
299
|
const component = rootConfigObject[name];
|
|
259
|
-
|
|
300
|
+
// `myComponent:` with nothing under it parses to null, and an async listener's TypeError
|
|
301
|
+
// escapes the watcher's emit guard as an unhandled rejection.
|
|
302
|
+
if (component?.logging) {
|
|
260
303
|
updateLogger(mainLogger.forComponent(name), component.logging, name);
|
|
261
304
|
}
|
|
262
305
|
else if (mainLogger.hasComponent(name)) {
|
|
@@ -456,6 +499,8 @@ module.exports = {
|
|
|
456
499
|
// we can start using the RootConfigWatcher
|
|
457
500
|
start: updateLogSettings,
|
|
458
501
|
startOnMainThread: updateLogSettings,
|
|
502
|
+
// Test-only: applies a config without the process-wide watcher `updateLogSettings` builds.
|
|
503
|
+
_applyLogSettingsForTests: applyLogSettings,
|
|
459
504
|
errorToString,
|
|
460
505
|
errorForLog,
|
|
461
506
|
inspectForLog,
|
|
@@ -516,6 +561,14 @@ function logsAtLevel(level) {
|
|
|
516
561
|
function initLogSettings(forceInit = false) {
|
|
517
562
|
try {
|
|
518
563
|
if (hdbProperties === undefined || forceInit) {
|
|
564
|
+
if (forceInit && mainLogger?.[SET_ROTATION_POLICY]) {
|
|
565
|
+
const currentPath = mainLogger.path;
|
|
566
|
+
if (currentPath) {
|
|
567
|
+
const { ownSource } = mainLogger[GET_ROTATION_POLICY]();
|
|
568
|
+
mainLogger[SET_ROTATION_POLICY](undefined, ownSource, false);
|
|
569
|
+
mainLogger.path = currentPath;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
519
572
|
closeLogFile();
|
|
520
573
|
const bootPropsFilePath = getPropsFilePath();
|
|
521
574
|
let properties = (0, assignCmdEnvVariables_ts_1.default)(['ROOTPATH']);
|
|
@@ -692,14 +745,18 @@ function suppressLogging(callback) {
|
|
|
692
745
|
loggingEnabled = true;
|
|
693
746
|
}
|
|
694
747
|
}
|
|
695
|
-
const SERVICE_NAME = worker_threads_1.workerData?.name?.replace(/ /g, '-') || 'main';
|
|
696
748
|
// these are used to store information about the current service and tag so we can prepend them to the log during
|
|
697
749
|
// the writes, without having to pass the information through the Console instance
|
|
698
750
|
let currentLevel = 'info'; // default is info
|
|
699
751
|
let currentServiceName;
|
|
700
752
|
let currentTag;
|
|
701
753
|
function createLogger(options = {}) {
|
|
702
|
-
let { path: logFilePath, level: logLevel, stdStreams: logToStdstreams, rotation, isExternalInstance, writeToLog, component, } = options;
|
|
754
|
+
let { path: logFilePath, level: logLevel, stdStreams: logToStdstreams, rotation, isExternalInstance, writeToLog, component, rotationSource, rotationInherited = false, } = options;
|
|
755
|
+
const ownRotationSource = Symbol('rotationPolicySource');
|
|
756
|
+
let currentRotationSource = rotationSource ?? ownRotationSource;
|
|
757
|
+
let previousRotationSource;
|
|
758
|
+
let currentRotationInherited = rotationInherited;
|
|
759
|
+
let rotationChangeIsAuthoritative = rotation !== undefined;
|
|
703
760
|
if (!logLevel)
|
|
704
761
|
logLevel = 'info';
|
|
705
762
|
let level = typeof logLevel === 'number' ? logLevel : LOG_LEVEL_HIERARCHY[logLevel];
|
|
@@ -750,7 +807,8 @@ function createLogger(options = {}) {
|
|
|
750
807
|
else if (logToStdstreams)
|
|
751
808
|
process.stderr.write(log);
|
|
752
809
|
}
|
|
753
|
-
let logToFile = logFilePath && getFileLogger(logFilePath, rotation, isExternalInstance);
|
|
810
|
+
let logToFile = logFilePath && getFileLogger(logFilePath, rotation, isExternalInstance, rotationPolicy());
|
|
811
|
+
rotationPolicyApplied();
|
|
754
812
|
function logPrepend(write) {
|
|
755
813
|
return {
|
|
756
814
|
write(log) {
|
|
@@ -772,6 +830,14 @@ function createLogger(options = {}) {
|
|
|
772
830
|
stderr: logPrepend(writeToLog ?? logStdErr),
|
|
773
831
|
colorMode: (logToStdstreams && colorMode) || false,
|
|
774
832
|
}, level);
|
|
833
|
+
logger[SET_ROTATION_POLICY] = function (newRotation, source = ownRotationSource, inherited = false) {
|
|
834
|
+
previousRotationSource = currentRotationSource;
|
|
835
|
+
currentRotationSource = source ?? ownRotationSource;
|
|
836
|
+
currentRotationInherited = inherited;
|
|
837
|
+
rotationChangeIsAuthoritative = true;
|
|
838
|
+
logger.rotation = newRotation;
|
|
839
|
+
};
|
|
840
|
+
logger[GET_ROTATION_POLICY] = () => ({ ownSource: ownRotationSource, source: currentRotationSource });
|
|
775
841
|
updateConditional(logger);
|
|
776
842
|
logger.path = logFilePath;
|
|
777
843
|
Object.defineProperty(logger, 'path', {
|
|
@@ -780,7 +846,10 @@ function createLogger(options = {}) {
|
|
|
780
846
|
},
|
|
781
847
|
set(path) {
|
|
782
848
|
logFilePath = path;
|
|
783
|
-
|
|
849
|
+
if (!logFilePath)
|
|
850
|
+
return;
|
|
851
|
+
logToFile = getFileLogger(logFilePath, logger.rotation, isExternalInstance, rotationPolicy());
|
|
852
|
+
rotationPolicyApplied();
|
|
784
853
|
if (isExternalInstance)
|
|
785
854
|
writeToLogFile = logToFile;
|
|
786
855
|
},
|
|
@@ -794,12 +863,15 @@ function createLogger(options = {}) {
|
|
|
794
863
|
let componentLogger = components.get(name);
|
|
795
864
|
if (!componentLogger) {
|
|
796
865
|
const protoLogger = isExternal ? exports.externalLogger : logger;
|
|
866
|
+
const protoRotationPolicy = protoLogger[GET_ROTATION_POLICY]?.();
|
|
797
867
|
componentLogger = createLogger({
|
|
798
868
|
path: protoLogger.path,
|
|
799
869
|
level: protoLogger.level,
|
|
800
870
|
stdStreams: protoLogger.logToStdstreams,
|
|
801
871
|
isExternalInstance: isExternal || name === 'external',
|
|
802
872
|
rotation: protoLogger.rotation,
|
|
873
|
+
rotationSource: protoRotationPolicy?.source,
|
|
874
|
+
rotationInherited: true,
|
|
803
875
|
writeToLog,
|
|
804
876
|
component: true,
|
|
805
877
|
});
|
|
@@ -816,35 +888,80 @@ function createLogger(options = {}) {
|
|
|
816
888
|
logger.components = components;
|
|
817
889
|
}
|
|
818
890
|
return logger;
|
|
891
|
+
function rotationPolicy() {
|
|
892
|
+
return {
|
|
893
|
+
source: currentRotationSource,
|
|
894
|
+
previousSource: previousRotationSource,
|
|
895
|
+
inherited: currentRotationInherited,
|
|
896
|
+
authoritative: rotationChangeIsAuthoritative,
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function rotationPolicyApplied() {
|
|
900
|
+
previousRotationSource = currentRotationSource;
|
|
901
|
+
rotationChangeIsAuthoritative = false;
|
|
902
|
+
}
|
|
819
903
|
}
|
|
820
|
-
const LOG_TIME_USAGE_THRESHOLD = 100;
|
|
821
|
-
// How long to write straight to stdio after the log file refuses an append.
|
|
822
|
-
const APPEND_RETRY_COOLDOWN = 5000;
|
|
823
904
|
/**
|
|
824
905
|
* Get the file logger for the given path. If it doesn't exist, create it.
|
|
825
906
|
* @param path
|
|
826
907
|
* @param isExternalInstance
|
|
827
908
|
* @return {any}
|
|
828
909
|
*/
|
|
829
|
-
function getFileLogger(path, rotation, isExternalInstance) {
|
|
910
|
+
function getFileLogger(path, rotation, isExternalInstance, rotationPolicy) {
|
|
830
911
|
let logger = fileLoggers.get(path);
|
|
831
912
|
let logFD, loggedFDError, loggedAppendError, logTimer, retryAppendAfter;
|
|
832
913
|
let logBuffer;
|
|
914
|
+
let rotationProblemNotice;
|
|
833
915
|
let logTimeUsage = 0;
|
|
916
|
+
let rotationGuard, logFDIdentity, nextRotationReport = 0;
|
|
834
917
|
if (!logger) {
|
|
835
918
|
logger = logToFile;
|
|
836
919
|
logger.closeLogFile = closeLogFile;
|
|
920
|
+
// The guard has to be installed through the live closure: later calls for this path get the
|
|
921
|
+
// cached logger back, and their own logFD/logBuffer are dead.
|
|
922
|
+
logger.installRotationGuard = installRotationGuard;
|
|
837
923
|
logger.path = path;
|
|
838
924
|
fileLoggers.set(path, logger);
|
|
839
|
-
|
|
840
|
-
|
|
925
|
+
// Registered by the sink, not by the size guard: a thread that writes this file but has no
|
|
926
|
+
// guard (no maxSize, or rotation driven only by interval) still holds a descriptor, and an
|
|
927
|
+
// answer from it has to mean "released" rather than only "handler ran".
|
|
928
|
+
(0, logGenerationCoordinator_ts_1.registerLogSink)(path, { identity: () => logFDIdentity, close: closeLogFile });
|
|
929
|
+
}
|
|
930
|
+
const currentSource = logger[FILE_ROTATION_SOURCE];
|
|
931
|
+
const sameSource = currentSource === rotationPolicy.source;
|
|
932
|
+
const replacesPreviousSource = currentSource !== undefined && currentSource === rotationPolicy.previousSource;
|
|
933
|
+
const canInstall = rotation !== undefined &&
|
|
934
|
+
(!rotationPolicy.inherited || currentSource === undefined || sameSource || replacesPreviousSource);
|
|
935
|
+
const canClear = rotation === undefined && rotationPolicy.authoritative && (sameSource || replacesPreviousSource);
|
|
936
|
+
let reconfigured = false;
|
|
937
|
+
if (canInstall) {
|
|
938
|
+
logger[FILE_ROTATION_SOURCE] = rotationPolicy.source;
|
|
939
|
+
reconfigured = JSON.stringify(rotation) !== JSON.stringify(logger.rotation);
|
|
940
|
+
}
|
|
941
|
+
if (reconfigured) {
|
|
841
942
|
logger.rotation = rotation;
|
|
943
|
+
// Every writing thread, and synchronously: request logging is produced by the HTTP workers, and
|
|
944
|
+
// at the rates that make maxSize matter a deferred guard misses megabytes before it exists.
|
|
945
|
+
logger.installRotationGuard(rotation);
|
|
946
|
+
}
|
|
947
|
+
else if (canClear) {
|
|
948
|
+
logger.rotation = undefined;
|
|
949
|
+
logger[FILE_ROTATION_SOURCE] = undefined;
|
|
950
|
+
logger.installRotationGuard(undefined);
|
|
951
|
+
reconfigured = true;
|
|
952
|
+
}
|
|
953
|
+
if (worker_threads_1.isMainThread && reconfigured) {
|
|
842
954
|
setTimeout(() => {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const { logRotator } = require('./logRotator');
|
|
955
|
+
// Everything inside the try: a throw from a timer callback is unhandled, and neither
|
|
956
|
+
// require('./logRotator') (which reaches environmentManager's synchronous init) nor a
|
|
957
|
+
// rotator teardown may take the process down over log rotation (#847).
|
|
847
958
|
try {
|
|
959
|
+
const previousRotator = logger.rotator;
|
|
960
|
+
logger.rotator = undefined;
|
|
961
|
+
previousRotator?.end();
|
|
962
|
+
if (!rotation)
|
|
963
|
+
return;
|
|
964
|
+
const { logRotator } = require('./logRotator');
|
|
848
965
|
logger.rotator = logRotator({
|
|
849
966
|
logger,
|
|
850
967
|
...rotation,
|
|
@@ -856,6 +973,50 @@ function getFileLogger(path, rotation, isExternalInstance) {
|
|
|
856
973
|
}, 100);
|
|
857
974
|
}
|
|
858
975
|
return logger;
|
|
976
|
+
function installRotationGuard(newRotation) {
|
|
977
|
+
rotationGuard = undefined;
|
|
978
|
+
if (!newRotation || newRotation.enabled === false)
|
|
979
|
+
return;
|
|
980
|
+
const maxBytes = (0, logRotation_ts_1.parseMaxSize)(newRotation.maxSize);
|
|
981
|
+
if (!maxBytes) {
|
|
982
|
+
if (newRotation.maxSize != null)
|
|
983
|
+
reportRotationProblem(`logging.rotation.maxSize: ${logRotation_ts_1.INVALID_MAX_SIZE_MSG}`);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
try {
|
|
987
|
+
rotationGuard = (0, logRotation_ts_1.createRotationGuard)({
|
|
988
|
+
logPath: path,
|
|
989
|
+
maxBytes,
|
|
990
|
+
rotatedLogDir: (0, logRotation_ts_1.resolveRotatedLogDir)(path, newRotation.path),
|
|
991
|
+
// From the rotation block rather than environmentManager (which logRotator still reads):
|
|
992
|
+
// environmentManager imports this module, so it cannot be reached from the write path.
|
|
993
|
+
compress: newRotation.compress,
|
|
994
|
+
getLogIdentity: () => logFDIdentity,
|
|
995
|
+
closeLogFile,
|
|
996
|
+
report: reportRotationProblem,
|
|
997
|
+
onRotated(archivePath) {
|
|
998
|
+
// Same shape as every other line in the file: written straight to the sink, this
|
|
999
|
+
// bypasses the prefix createLogger's logPrepend adds, and readLog parses by level.
|
|
1000
|
+
logToFile(`[${SERVICE_NAME}/${worker_threads_1.threadId}] [notify]: hdb.log rotated, old log moved to ${archivePath}`);
|
|
1001
|
+
},
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
catch (error) {
|
|
1005
|
+
reportRotationProblem(`log rotation is disabled for ${path}: ${error}`);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function reportRotationProblem(text) {
|
|
1009
|
+
// Straight to stdio, never back through this sink: the sink is what rotation is failing on.
|
|
1010
|
+
// Rate-limited rather than once-only, so a later, different failure is still reported.
|
|
1011
|
+
if (nextRotationReport > performance.now())
|
|
1012
|
+
return;
|
|
1013
|
+
nextRotationReport = performance.now() + ROTATION_REPORT_INTERVAL;
|
|
1014
|
+
const message = `Harper log rotation problem — ${text}`;
|
|
1015
|
+
// Scripted services discard stdio. Preserve the warning in the active file on the next
|
|
1016
|
+
// successful append as well, without recursing through the rotation guard.
|
|
1017
|
+
rotationProblemNotice = `${new Date().toISOString()} [${SERVICE_NAME}/${worker_threads_1.threadId}] [error]: ${message}\n`;
|
|
1018
|
+
writeToStdioDirectly(process.stderr, `${message}\n`);
|
|
1019
|
+
}
|
|
859
1020
|
function logToFile(log) {
|
|
860
1021
|
let entry = `${new Date().toISOString()} ${log}${log.endsWith('\n') ? '' : '\n'}`;
|
|
861
1022
|
if (logBuffer) {
|
|
@@ -885,18 +1046,29 @@ function getFileLogger(path, rotation, isExternalInstance) {
|
|
|
885
1046
|
}
|
|
886
1047
|
// this is called on a timer, and will write the log buffer to the file
|
|
887
1048
|
function logQueuedData(entry) {
|
|
888
|
-
openLogFile(undefined);
|
|
889
1049
|
const payload = logBuffer ? logBuffer.join('') : entry;
|
|
1050
|
+
// Released before anything can re-enter: the rotation notice is written from inside the append
|
|
1051
|
+
// below, and a re-entrant flush still holding this batch would write it twice.
|
|
1052
|
+
logBuffer = null;
|
|
1053
|
+
if (payload === undefined)
|
|
1054
|
+
return;
|
|
890
1055
|
// A file that just refused a write will refuse the next one too, and every attempt costs a
|
|
891
1056
|
// failed syscall plus a thrown error on whatever path is logging - which, on a full volume,
|
|
892
1057
|
// is the request path at full rate. Go straight to stdio until the cooldown expires.
|
|
893
|
-
|
|
1058
|
+
// Opened after the guard: a guard recovering by rotation closes this descriptor first.
|
|
1059
|
+
const mayAppend = !(retryAppendAfter > performance.now()) && (rotationGuard?.beforeAppend() ?? true);
|
|
1060
|
+
openLogFile(undefined);
|
|
1061
|
+
if (logFD && mayAppend) {
|
|
894
1062
|
let startTime = performance.now();
|
|
895
1063
|
try {
|
|
896
|
-
|
|
1064
|
+
const appendPayload = rotationProblemNotice ? rotationProblemNotice + payload : payload;
|
|
1065
|
+
const appendBuffer = Buffer.from(appendPayload);
|
|
1066
|
+
fs.appendFileSync(logFD, appendBuffer);
|
|
1067
|
+
rotationProblemNotice = undefined;
|
|
897
1068
|
// Both cleared, so a volume that fills again months later reports itself again
|
|
898
1069
|
retryAppendAfter = undefined;
|
|
899
1070
|
loggedAppendError = false;
|
|
1071
|
+
rotationGuard?.recordWrite(appendBuffer.length);
|
|
900
1072
|
}
|
|
901
1073
|
catch (error) {
|
|
902
1074
|
retryAppendAfter = performance.now() + APPEND_RETRY_COOLDOWN;
|
|
@@ -910,7 +1082,6 @@ function getFileLogger(path, rotation, isExternalInstance) {
|
|
|
910
1082
|
writeToStdioDirectly(process.stderr, `Harper cannot write to its log file: ${error}\n`);
|
|
911
1083
|
}
|
|
912
1084
|
writeToStdioDirectly(process.stdout, payload);
|
|
913
|
-
logBuffer = null;
|
|
914
1085
|
return;
|
|
915
1086
|
}
|
|
916
1087
|
let endTime = performance.now();
|
|
@@ -920,8 +1091,6 @@ function getFileLogger(path, rotation, isExternalInstance) {
|
|
|
920
1091
|
}
|
|
921
1092
|
else
|
|
922
1093
|
writeToStdioDirectly(process.stdout, payload);
|
|
923
|
-
if (logBuffer)
|
|
924
|
-
logBuffer = null;
|
|
925
1094
|
}
|
|
926
1095
|
function closeLogFile(_unused) {
|
|
927
1096
|
try {
|
|
@@ -929,6 +1098,7 @@ function getFileLogger(path, rotation, isExternalInstance) {
|
|
|
929
1098
|
}
|
|
930
1099
|
catch { }
|
|
931
1100
|
logFD = null;
|
|
1101
|
+
logFDIdentity = null;
|
|
932
1102
|
if (isExternalInstance)
|
|
933
1103
|
mainLogFd = null;
|
|
934
1104
|
}
|
|
@@ -936,6 +1106,15 @@ function getFileLogger(path, rotation, isExternalInstance) {
|
|
|
936
1106
|
if (!logFD) {
|
|
937
1107
|
try {
|
|
938
1108
|
logFD = fs.openSync(path, 'a');
|
|
1109
|
+
// Which generation this descriptor belongs to, recorded once here so the size guard's
|
|
1110
|
+
// checkpoint needs a single pathname stat to tell whether the file has moved under it.
|
|
1111
|
+
try {
|
|
1112
|
+
const opened = fs.fstatSync(logFD);
|
|
1113
|
+
logFDIdentity = { ino: opened.ino, dev: opened.dev };
|
|
1114
|
+
}
|
|
1115
|
+
catch {
|
|
1116
|
+
logFDIdentity = null;
|
|
1117
|
+
}
|
|
939
1118
|
if (isExternalInstance)
|
|
940
1119
|
mainLogFd = logFD;
|
|
941
1120
|
}
|