@abtnode/core 1.16.14-beta-0c29907f → 1.16.14-beta-1936d3d0
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/blocklet/manager/disk.js +38 -18
- package/lib/states/audit-log.js +1 -1
- package/lib/util/blocklet.js +1 -2
- package/lib/util/log.js +403 -0
- package/package.json +23 -20
|
@@ -77,7 +77,7 @@ const {
|
|
|
77
77
|
SUSPENDED_REASON,
|
|
78
78
|
} = require('@blocklet/constant');
|
|
79
79
|
const isUndefined = require('lodash/isUndefined');
|
|
80
|
-
const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
|
|
80
|
+
const { WELLKNOWN_SERVICE_PATH_PREFIX, WELLKNOWN_BLOCKLET_ADMIN_PATH } = require('@abtnode/constant');
|
|
81
81
|
const { signV2 } = require('@arcblock/jwt');
|
|
82
82
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
83
83
|
const pLimit = require('p-limit');
|
|
@@ -477,18 +477,6 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
477
477
|
blocklet1.status = BlockletStatus.starting;
|
|
478
478
|
this.emit(BlockletEvents.statusChange, doc1);
|
|
479
479
|
|
|
480
|
-
if (blocklet1.mode === BLOCKLET_MODES.DEVELOPMENT) {
|
|
481
|
-
const { logsDir } = blocklet1.env;
|
|
482
|
-
|
|
483
|
-
try {
|
|
484
|
-
fs.removeSync(logsDir);
|
|
485
|
-
fs.mkdirSync(logsDir, { recursive: true });
|
|
486
|
-
} catch {
|
|
487
|
-
// Windows && Node.js 18.x 下会发生删除错误(ENOTEMPTY)
|
|
488
|
-
// 但是这个错误并不影响后续逻辑,所以这里对这个错误做了 catch
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
480
|
const blocklet = await ensureAppPortsNotOccupied({ blocklet: blocklet1, componentDids, states, manager: this });
|
|
493
481
|
|
|
494
482
|
const getHookFn =
|
|
@@ -622,6 +610,16 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
622
610
|
components: getComponentsInternalInfo(res),
|
|
623
611
|
});
|
|
624
612
|
|
|
613
|
+
this._createNotification(did, {
|
|
614
|
+
title: '',
|
|
615
|
+
description: `${
|
|
616
|
+
componentDids?.length ? getComponentNamesWithVersion(blocklet, componentDids) : 'All components'
|
|
617
|
+
} is successfully stopped for ${blocklet.meta.title}.`,
|
|
618
|
+
entityType: 'blocklet',
|
|
619
|
+
entityId: did,
|
|
620
|
+
severity: 'success',
|
|
621
|
+
});
|
|
622
|
+
|
|
625
623
|
return res;
|
|
626
624
|
}
|
|
627
625
|
|
|
@@ -1311,7 +1309,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1311
1309
|
const enabled = newConfig?.email?.enabled;
|
|
1312
1310
|
if (enabled) {
|
|
1313
1311
|
const { error } = emailConfigSchema.validate(
|
|
1314
|
-
pick(newConfig?.email || {}, ['from', 'host', 'port', 'user', 'password'])
|
|
1312
|
+
pick(newConfig?.email || {}, ['from', 'host', 'port', 'user', 'password', 'secure'])
|
|
1315
1313
|
);
|
|
1316
1314
|
if (error) {
|
|
1317
1315
|
logger.error('configNotification validate error', { error });
|
|
@@ -1510,7 +1508,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1510
1508
|
addToUpdates(component.meta.did, status);
|
|
1511
1509
|
logger.info('will sync status from pm2', { did, status, oldStatus, componentDid: component.meta.did });
|
|
1512
1510
|
}
|
|
1513
|
-
} catch {
|
|
1511
|
+
} catch (error) {
|
|
1514
1512
|
if (
|
|
1515
1513
|
![
|
|
1516
1514
|
BlockletStatus.added,
|
|
@@ -1519,11 +1517,21 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
1519
1517
|
BlockletStatus.installing,
|
|
1520
1518
|
BlockletStatus.installed,
|
|
1521
1519
|
BlockletStatus.upgrading,
|
|
1522
|
-
].includes(component.status)
|
|
1520
|
+
].includes(component.status) &&
|
|
1521
|
+
(error.code !== 'BLOCKLET_PROCESS_404' ||
|
|
1522
|
+
![BlockletStatus.stopped, BlockletStatus.error].includes(component.status))
|
|
1523
1523
|
) {
|
|
1524
|
+
const oldStatus = component.status;
|
|
1524
1525
|
const status = BlockletStatus.stopped;
|
|
1525
1526
|
component.status = status;
|
|
1526
1527
|
addToUpdates(component.meta.did, status);
|
|
1528
|
+
logger.info('will sync status from pm2', {
|
|
1529
|
+
did,
|
|
1530
|
+
status,
|
|
1531
|
+
oldStatus,
|
|
1532
|
+
componentDid: component.meta.did,
|
|
1533
|
+
error: error.message,
|
|
1534
|
+
});
|
|
1527
1535
|
}
|
|
1528
1536
|
}
|
|
1529
1537
|
},
|
|
@@ -2013,6 +2021,16 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2013
2021
|
|
|
2014
2022
|
this.emit(BlockletEvents.statusChange, res);
|
|
2015
2023
|
this.emit(BlockletEvents.started, { ...res, componentDids });
|
|
2024
|
+
this._createNotification(did, {
|
|
2025
|
+
title: '',
|
|
2026
|
+
description: `${getComponentNamesWithVersion(blocklet, componentDids)} is successfully started for ${
|
|
2027
|
+
blocklet.meta.title
|
|
2028
|
+
}.`,
|
|
2029
|
+
entityType: 'blocklet',
|
|
2030
|
+
entityId: did,
|
|
2031
|
+
severity: 'success',
|
|
2032
|
+
});
|
|
2033
|
+
|
|
2016
2034
|
logger.info('blocklet healthy', { did, name, time: Date.now() - startedAt });
|
|
2017
2035
|
} catch (error) {
|
|
2018
2036
|
const status = await states.blocklet.getBlockletStatus(did);
|
|
@@ -2543,7 +2561,7 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2543
2561
|
try {
|
|
2544
2562
|
// delete old process
|
|
2545
2563
|
try {
|
|
2546
|
-
await this.deleteProcess({ did }, context);
|
|
2564
|
+
await this.deleteProcess({ did, componentDids }, context);
|
|
2547
2565
|
logger.info('delete blocklet process for upgrading', { did, name });
|
|
2548
2566
|
} catch (err) {
|
|
2549
2567
|
logger.error('delete blocklet process for upgrading', { did, name, error: err });
|
|
@@ -2965,7 +2983,9 @@ class DiskBlockletManager extends BaseBlockletManager {
|
|
|
2965
2983
|
if (blocklet) {
|
|
2966
2984
|
const urls = blocklet.site?.domainAliases || [];
|
|
2967
2985
|
const customUrl = urls.find((x) => !x.isProtected)?.value;
|
|
2968
|
-
blockletUrl = `http://${
|
|
2986
|
+
blockletUrl = `http://${
|
|
2987
|
+
customUrl || getDidDomainForBlocklet({ appPid: blocklet.appPid })
|
|
2988
|
+
}${WELLKNOWN_BLOCKLET_ADMIN_PATH}`;
|
|
2969
2989
|
}
|
|
2970
2990
|
} catch (error) {
|
|
2971
2991
|
logger.error('[_createNotification] get blocklet url failed', { error });
|
package/lib/states/audit-log.js
CHANGED
|
@@ -128,7 +128,7 @@ const getLogContent = async (action, args, context, result, info, node) => {
|
|
|
128
128
|
case 'deleteComponent':
|
|
129
129
|
return `removed ${result.deletedComponent.meta.title}@${result.deletedComponent.meta.version} ${ args.keepData !== false ? 'but kept its data and config' : 'and its data and config' }`; // prettier-ignore
|
|
130
130
|
case 'configBlocklet':
|
|
131
|
-
return `updated following config for ${args.did?.length > 1 ? componentOrApplicationInfo(result, [args.did[1]]) : 'application'}: ${args.configs.map(x =>
|
|
131
|
+
return `updated following config for ${args.did?.length > 1 ? componentOrApplicationInfo(result, [args.did[1]]) : 'application'}: ${args.configs.map(x => `*${x.key}*`).join(', ')}`; // prettier-ignore
|
|
132
132
|
case 'backupToSpaces':
|
|
133
133
|
if (args?.success) {
|
|
134
134
|
return `Backup application to ${args.url} successfully:\n- Backup files have been stored [here](${args.backupUrl})`;
|
package/lib/util/blocklet.js
CHANGED
|
@@ -157,10 +157,9 @@ const getComponentDirs = (
|
|
|
157
157
|
// FIXME 这个函数做了太多的事
|
|
158
158
|
// get data dirs
|
|
159
159
|
|
|
160
|
-
const { name: appName } = ancestors.concat(component)[0].meta;
|
|
161
160
|
const componentName = getComponentName(component, ancestors);
|
|
162
161
|
|
|
163
|
-
const logsDir = path.join(dataDirs.logs,
|
|
162
|
+
const logsDir = path.join(dataDirs.logs, componentName);
|
|
164
163
|
const dataDir = path.join(dataDirs.data, componentName);
|
|
165
164
|
const cacheDir = path.join(dataDirs.cache, componentName);
|
|
166
165
|
|
package/lib/util/log.js
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const createArchive = require('archiver');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const glob = require('fast-glob');
|
|
6
|
+
const isEqual = require('lodash/isEqual');
|
|
7
|
+
const { Tail } = require('tail');
|
|
8
|
+
const readLastLines = require('read-last-lines');
|
|
9
|
+
const { BLOCKLET_MODES } = require('@blocklet/constant');
|
|
10
|
+
const { findComponentByIdV2 } = require('@blocklet/meta/lib/util');
|
|
11
|
+
const dayjs = require('@abtnode/util/lib/dayjs');
|
|
12
|
+
const logger = require('@abtnode/logger')(require('../../package.json').name);
|
|
13
|
+
|
|
14
|
+
class StreamLog {
|
|
15
|
+
constructor() {
|
|
16
|
+
this._files = null; // Object { <level>: <filePath> }
|
|
17
|
+
this._tails = null; // Object { <level>: <Tail> }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {Object} files Object { <level>: <filePath> }
|
|
22
|
+
* @return {Boolean} if files change
|
|
23
|
+
*/
|
|
24
|
+
setFiles(files) {
|
|
25
|
+
if (!isEqual(this._files, files)) {
|
|
26
|
+
this.clearTails();
|
|
27
|
+
this._files = files;
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getRecent(lineNum, callback) {
|
|
34
|
+
if (!this._files) {
|
|
35
|
+
callback(new Error('files is empty'));
|
|
36
|
+
}
|
|
37
|
+
Object.entries(this._files).forEach(([level, file]) => {
|
|
38
|
+
if (!file || !fs.existsSync(file)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
readLastLines
|
|
42
|
+
.read(file, lineNum || 0)
|
|
43
|
+
.then((data) => {
|
|
44
|
+
callback(null, level, data);
|
|
45
|
+
})
|
|
46
|
+
.catch((error) => {
|
|
47
|
+
callback(error, level);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ensureTails() {
|
|
53
|
+
if (this._tails) {
|
|
54
|
+
return this._tails;
|
|
55
|
+
}
|
|
56
|
+
if (!this._files) {
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
this._tails = {};
|
|
61
|
+
Object.entries(this._files).forEach(([level, file]) => {
|
|
62
|
+
if (!file || !fs.existsSync(file)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 测试发现 Windows 下 fs.watch 没有效果,但是 fs.watch 的性能更好,所以只在 windows 下使用 fs.watchFile
|
|
67
|
+
this._tails[level] = new Tail(file, {
|
|
68
|
+
useWatchFile: process.platform === 'win32',
|
|
69
|
+
fsWatchOptions: { interval: 1500 },
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
return this._tails;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
this._tails = null;
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
clearTails() {
|
|
80
|
+
if (this._tails) {
|
|
81
|
+
Object.values(this._tails).forEach((tail) => {
|
|
82
|
+
tail.unwatch();
|
|
83
|
+
});
|
|
84
|
+
this._tails = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const createFile = (files) => {
|
|
90
|
+
Object.values(files).forEach((file) => {
|
|
91
|
+
if (!fs.existsSync(file)) {
|
|
92
|
+
try {
|
|
93
|
+
const dir = path.dirname(file);
|
|
94
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
95
|
+
} catch (err) {
|
|
96
|
+
// Do nothing
|
|
97
|
+
}
|
|
98
|
+
fs.writeFileSync(file, '', 'utf8');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const getLogFiles = async ({ name, node }) => {
|
|
104
|
+
const dt = new Date();
|
|
105
|
+
const { yyyy, mm, dd } = {
|
|
106
|
+
yyyy: dt.getFullYear(),
|
|
107
|
+
mm: `${dt.getMonth() + 1}`.padStart(2, 0),
|
|
108
|
+
dd: `${dt.getDate()}`.padStart(2, 0),
|
|
109
|
+
};
|
|
110
|
+
const date = `${yyyy}-${mm}-${dd}`;
|
|
111
|
+
|
|
112
|
+
if (name === 'abtnode') {
|
|
113
|
+
const logDir = path.join(node.dataDirs.logs, '_abtnode');
|
|
114
|
+
|
|
115
|
+
const info = path.join(logDir, `daemon-${date}.log`);
|
|
116
|
+
const error = path.join(logDir, `daemon-error-${date}.log`);
|
|
117
|
+
const access = path.join(logDir, 'access.log');
|
|
118
|
+
const stdout = path.join(logDir, 'daemon.stdout.log');
|
|
119
|
+
const stderr = path.join(logDir, 'daemon.stderr.log');
|
|
120
|
+
|
|
121
|
+
createFile({
|
|
122
|
+
info,
|
|
123
|
+
error,
|
|
124
|
+
access,
|
|
125
|
+
stdout,
|
|
126
|
+
stderr,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
info,
|
|
131
|
+
error,
|
|
132
|
+
access,
|
|
133
|
+
stdout,
|
|
134
|
+
stderr,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (name === 'blocklet-services') {
|
|
139
|
+
const logDir = path.join(node.dataDirs.logs, '_abtnode');
|
|
140
|
+
const info = path.join(logDir, 'service.log');
|
|
141
|
+
const error = path.join(logDir, 'service.error.log');
|
|
142
|
+
createFile({ info, error });
|
|
143
|
+
return { info, error };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (name.indexOf('blocklet-') === 0) {
|
|
147
|
+
const did = name.substring('blocklet-'.length);
|
|
148
|
+
const [appId, componentId] = did.split('/');
|
|
149
|
+
const blocklet = await node.getBlocklet({ did: appId, useCache: true });
|
|
150
|
+
let { name: blockletName } = blocklet.meta;
|
|
151
|
+
|
|
152
|
+
if (componentId) {
|
|
153
|
+
const component = findComponentByIdV2(blocklet, componentId);
|
|
154
|
+
if (component) {
|
|
155
|
+
blockletName = path.join(blockletName, component.meta.name);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const dir = path.join(node.dataDirs.logs, blockletName);
|
|
160
|
+
const info = path.join(dir, `info-${date}.log`);
|
|
161
|
+
const error = path.join(dir, `info-error-${date}.log`);
|
|
162
|
+
const access = path.join(dir, 'access.log');
|
|
163
|
+
const stdout = path.join(dir, 'output.log');
|
|
164
|
+
const stderr = path.join(dir, 'error.log');
|
|
165
|
+
|
|
166
|
+
createFile({
|
|
167
|
+
info,
|
|
168
|
+
error,
|
|
169
|
+
access,
|
|
170
|
+
stdout,
|
|
171
|
+
stderr,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
info,
|
|
176
|
+
error,
|
|
177
|
+
access,
|
|
178
|
+
stdout,
|
|
179
|
+
stderr,
|
|
180
|
+
recent: blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? 0 : 100,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (name.startsWith('service-gateway-')) {
|
|
185
|
+
const providerName = name.substring('service-gateway-'.length);
|
|
186
|
+
const provider = node.getRouterProvider(providerName);
|
|
187
|
+
if (!provider) {
|
|
188
|
+
logger.error('router engine is empty', { name, providerName });
|
|
189
|
+
return {};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return provider.getLogFilesForToday();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {};
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const getDownloadLogFilesFromServer = async ({ dates, nodeInfo, node } = {}) => {
|
|
199
|
+
const logDir = path.join(node.dataDirs.logs, '_abtnode');
|
|
200
|
+
|
|
201
|
+
const pm2Log = path.join(logDir, 'pm2.log');
|
|
202
|
+
const pm2LogSrc = path.join(process.env.PM2_HOME, 'pm2.log');
|
|
203
|
+
if (fs.existsSync(pm2LogSrc)) {
|
|
204
|
+
await fs.copy(pm2LogSrc, pm2Log);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const provider = node.getRouterProvider(nodeInfo.routing.provider);
|
|
208
|
+
if (!provider) {
|
|
209
|
+
logger.error('router engine is empty');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const list = [];
|
|
213
|
+
|
|
214
|
+
dates.forEach((d) => {
|
|
215
|
+
// log file created by @abtnode/log
|
|
216
|
+
// log file created by abt-node-log-rotate
|
|
217
|
+
list.push(path.join(logDir, `*-${d}*`));
|
|
218
|
+
|
|
219
|
+
// log file create by router
|
|
220
|
+
const routerLogDir = provider.getLogDir();
|
|
221
|
+
if (routerLogDir) {
|
|
222
|
+
list.push(path.join(routerLogDir, `*-${d}*`));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// abt-node-daemon console & gateway
|
|
227
|
+
list.push(path.join(logDir, 'daemon.stdout.log*'));
|
|
228
|
+
list.push(path.join(logDir, 'daemon.stderr.log*'));
|
|
229
|
+
list.push(path.join(logDir, 'access.log*'));
|
|
230
|
+
|
|
231
|
+
// abt-node-service console & gateway
|
|
232
|
+
list.push(path.join(logDir, 'service.output.log*'));
|
|
233
|
+
list.push(path.join(logDir, 'service.error.log*'));
|
|
234
|
+
list.push(path.join(logDir, 'service.log*'));
|
|
235
|
+
|
|
236
|
+
// abt-node-db-hub console & backup
|
|
237
|
+
list.push(path.join(logDir, 'db.output.log*'));
|
|
238
|
+
list.push(path.join(logDir, 'db.error.log*'));
|
|
239
|
+
|
|
240
|
+
// abt-node-event-hub console
|
|
241
|
+
list.push(path.join(logDir, 'event.output.log*'));
|
|
242
|
+
list.push(path.join(logDir, 'event.error.log*'));
|
|
243
|
+
|
|
244
|
+
// abt-node-log-rotate console
|
|
245
|
+
list.push(path.join(logDir, 'pm2-logrotate.stdout.log*'));
|
|
246
|
+
list.push(path.join(logDir, 'pm2-logrotate.stderr.log*'));
|
|
247
|
+
|
|
248
|
+
// abt-node-updater console
|
|
249
|
+
list.push(path.join(logDir, 'updater.error.log*'));
|
|
250
|
+
list.push(path.join(logDir, 'updater.output.log*'));
|
|
251
|
+
|
|
252
|
+
// fallback log
|
|
253
|
+
list.push(path.join(logDir, 'stderr.log*'));
|
|
254
|
+
list.push(path.join(logDir, 'stdout.log*'));
|
|
255
|
+
|
|
256
|
+
// router
|
|
257
|
+
list.push(...Object.values(provider.getLogFilesForToday() || {}));
|
|
258
|
+
|
|
259
|
+
// pm2 log
|
|
260
|
+
list.push(pm2Log);
|
|
261
|
+
|
|
262
|
+
return glob(list);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const getDownloadLogFilesFromBlocklet = ({ dates, blocklet } = {}) => {
|
|
266
|
+
const appLogDir = path.join(blocklet.env.logsDir);
|
|
267
|
+
const componentLogDirs = blocklet.children.map((c) => path.join(c.env.logsDir));
|
|
268
|
+
|
|
269
|
+
const list = [];
|
|
270
|
+
|
|
271
|
+
[appLogDir, ...componentLogDirs].forEach((logDir) => {
|
|
272
|
+
dates.forEach((d) => {
|
|
273
|
+
// log file created by @abtnode/log
|
|
274
|
+
// log file created by abt-node-log-rotate
|
|
275
|
+
list.push(path.join(logDir, `*-${d}*`));
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
list.push(path.join(logDir, 'output.log*'));
|
|
279
|
+
list.push(path.join(logDir, 'error.log*'));
|
|
280
|
+
list.push(path.join(logDir, 'access.log*'));
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
return glob(list);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const getDownloadLogFiles = async ({ did, node, days = 1, now } = {}) => {
|
|
287
|
+
const dates = [dayjs(now).format('YYYY-MM-DD')];
|
|
288
|
+
|
|
289
|
+
for (let i = 1; i <= days; i++) {
|
|
290
|
+
dates.unshift(dayjs(now).subtract(i, 'day').format('YYYY-MM-DD'));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const nodeInfo = await node.getNodeInfo({ useCache: true });
|
|
294
|
+
|
|
295
|
+
if (nodeInfo.did === did) {
|
|
296
|
+
return getDownloadLogFilesFromServer({ dates, node, nodeInfo });
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const blocklet = await node.getBlocklet({ did });
|
|
300
|
+
if (!blocklet) {
|
|
301
|
+
throw new Error('blocklet not found');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return getDownloadLogFilesFromBlocklet({ dates, blocklet });
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const createStreamLogManager = ({ onLog, onGetLogFiles }) => {
|
|
308
|
+
const store = {}; // Object<name: streamLog>
|
|
309
|
+
|
|
310
|
+
const ensure = (name) => {
|
|
311
|
+
if (!store[name]) {
|
|
312
|
+
store[name] = new StreamLog();
|
|
313
|
+
}
|
|
314
|
+
return store[name];
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const destroy = (name) => {
|
|
318
|
+
logger.info('log stream: destroy', { name });
|
|
319
|
+
if (!store[name]) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const log = store[name];
|
|
323
|
+
try {
|
|
324
|
+
log.clearTails();
|
|
325
|
+
delete store[name];
|
|
326
|
+
} catch (error) {
|
|
327
|
+
logger.error('log stream: remove ref error ', error);
|
|
328
|
+
delete store[name];
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const add = async (name, topic, firstLogCb) => {
|
|
333
|
+
logger.info('log stream: add', { name });
|
|
334
|
+
const log = ensure(name);
|
|
335
|
+
try {
|
|
336
|
+
// update files
|
|
337
|
+
// push recent 100 log
|
|
338
|
+
const { recent = 100, ...logFiles } = await onGetLogFiles(name);
|
|
339
|
+
const changed = await log.setFiles(logFiles);
|
|
340
|
+
logger.info('log stream: added', { name, logFiles });
|
|
341
|
+
log.getRecent(recent, (error, level, data) => {
|
|
342
|
+
if (error) {
|
|
343
|
+
logger.error('log stream error ', error);
|
|
344
|
+
}
|
|
345
|
+
if (firstLogCb) {
|
|
346
|
+
firstLogCb(level, data, logFiles);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
// stream
|
|
350
|
+
if (changed) {
|
|
351
|
+
const tails = log.ensureTails();
|
|
352
|
+
Object.entries(tails).forEach(([level, tail]) => {
|
|
353
|
+
tail.on('line', (data) => {
|
|
354
|
+
onLog({ topic, level, logFiles, data });
|
|
355
|
+
});
|
|
356
|
+
tail.on('error', (error) => {
|
|
357
|
+
logger.error('log tail error ', { error });
|
|
358
|
+
destroy(name);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
} catch (error) {
|
|
363
|
+
logger.error('log stream error ', error);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
add,
|
|
369
|
+
destroy,
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const createDownloadLogStream = async ({ node, did, days, now }) => {
|
|
374
|
+
const files = await getDownloadLogFiles({ node, did, days, now });
|
|
375
|
+
|
|
376
|
+
if (!files.length) {
|
|
377
|
+
throw new Error('Log file does not found');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const cwd = path.dirname(node.dataDirs.logs);
|
|
381
|
+
|
|
382
|
+
const archive = createArchive('zip', { zlib: { level: 9 } });
|
|
383
|
+
files.forEach((x) => {
|
|
384
|
+
archive.file(x, {
|
|
385
|
+
name: x.replace(`${cwd}/logs`, '/logs').replace(`${cwd}`, '/logs').replace('/_abtnode', '/blocklet-server'),
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
archive.rawPipe = archive.pipe.bind(archive);
|
|
390
|
+
archive.pipe = (s) => {
|
|
391
|
+
archive.rawPipe(s);
|
|
392
|
+
archive.finalize();
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
return archive;
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
module.exports = {
|
|
399
|
+
createStreamLogManager,
|
|
400
|
+
getLogFiles,
|
|
401
|
+
getDownloadLogFiles,
|
|
402
|
+
createDownloadLogStream,
|
|
403
|
+
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.14-beta-
|
|
6
|
+
"version": "1.16.14-beta-1936d3d0",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,19 +19,19 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/analytics": "1.16.14-beta-
|
|
23
|
-
"@abtnode/auth": "1.16.14-beta-
|
|
24
|
-
"@abtnode/certificate-manager": "1.16.14-beta-
|
|
25
|
-
"@abtnode/constant": "1.16.14-beta-
|
|
26
|
-
"@abtnode/cron": "1.16.14-beta-
|
|
27
|
-
"@abtnode/logger": "1.16.14-beta-
|
|
28
|
-
"@abtnode/models": "1.16.14-beta-
|
|
29
|
-
"@abtnode/queue": "1.16.14-beta-
|
|
30
|
-
"@abtnode/rbac": "1.16.14-beta-
|
|
31
|
-
"@abtnode/router-provider": "1.16.14-beta-
|
|
32
|
-
"@abtnode/static-server": "1.16.14-beta-
|
|
33
|
-
"@abtnode/timemachine": "1.16.14-beta-
|
|
34
|
-
"@abtnode/util": "1.16.14-beta-
|
|
22
|
+
"@abtnode/analytics": "1.16.14-beta-1936d3d0",
|
|
23
|
+
"@abtnode/auth": "1.16.14-beta-1936d3d0",
|
|
24
|
+
"@abtnode/certificate-manager": "1.16.14-beta-1936d3d0",
|
|
25
|
+
"@abtnode/constant": "1.16.14-beta-1936d3d0",
|
|
26
|
+
"@abtnode/cron": "1.16.14-beta-1936d3d0",
|
|
27
|
+
"@abtnode/logger": "1.16.14-beta-1936d3d0",
|
|
28
|
+
"@abtnode/models": "1.16.14-beta-1936d3d0",
|
|
29
|
+
"@abtnode/queue": "1.16.14-beta-1936d3d0",
|
|
30
|
+
"@abtnode/rbac": "1.16.14-beta-1936d3d0",
|
|
31
|
+
"@abtnode/router-provider": "1.16.14-beta-1936d3d0",
|
|
32
|
+
"@abtnode/static-server": "1.16.14-beta-1936d3d0",
|
|
33
|
+
"@abtnode/timemachine": "1.16.14-beta-1936d3d0",
|
|
34
|
+
"@abtnode/util": "1.16.14-beta-1936d3d0",
|
|
35
35
|
"@arcblock/did": "1.18.87",
|
|
36
36
|
"@arcblock/did-auth": "1.18.87",
|
|
37
37
|
"@arcblock/did-ext": "^1.18.87",
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"@arcblock/pm2-events": "^0.0.5",
|
|
43
43
|
"@arcblock/validator": "^1.18.87",
|
|
44
44
|
"@arcblock/vc": "1.18.87",
|
|
45
|
-
"@blocklet/constant": "1.16.14-beta-
|
|
46
|
-
"@blocklet/meta": "1.16.14-beta-
|
|
47
|
-
"@blocklet/resolver": "1.16.14-beta-
|
|
48
|
-
"@blocklet/sdk": "1.16.14-beta-
|
|
45
|
+
"@blocklet/constant": "1.16.14-beta-1936d3d0",
|
|
46
|
+
"@blocklet/meta": "1.16.14-beta-1936d3d0",
|
|
47
|
+
"@blocklet/resolver": "1.16.14-beta-1936d3d0",
|
|
48
|
+
"@blocklet/sdk": "1.16.14-beta-1936d3d0",
|
|
49
49
|
"@did-space/client": "^0.2.129",
|
|
50
50
|
"@fidm/x509": "^1.2.1",
|
|
51
51
|
"@ocap/mcrypto": "1.18.87",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"node-stream-zip": "^1.15.0",
|
|
77
77
|
"p-limit": "^3.1.0",
|
|
78
78
|
"p-retry": "4.6.1",
|
|
79
|
+
"read-last-lines": "^1.8.0",
|
|
79
80
|
"semver": "^7.3.8",
|
|
80
81
|
"sequelize": "^6.31.0",
|
|
81
82
|
"shelljs": "^0.8.5",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
"stream-throttle": "^0.1.3",
|
|
84
85
|
"stream-to-promise": "^3.0.0",
|
|
85
86
|
"systeminformation": "^5.17.12",
|
|
87
|
+
"tail": "^2.2.4",
|
|
86
88
|
"tar": "^6.1.11",
|
|
87
89
|
"transliteration": "^2.3.5",
|
|
88
90
|
"ua-parser-js": "^1.0.2",
|
|
@@ -94,7 +96,8 @@
|
|
|
94
96
|
"compression": "^1.7.4",
|
|
95
97
|
"expand-tilde": "^2.0.2",
|
|
96
98
|
"express": "^4.18.2",
|
|
97
|
-
"jest": "^27.5.1"
|
|
99
|
+
"jest": "^27.5.1",
|
|
100
|
+
"unzipper": "^0.10.11"
|
|
98
101
|
},
|
|
99
|
-
"gitHead": "
|
|
102
|
+
"gitHead": "32bcf5b55889ebd7302887eedd306d331e9cd3b8"
|
|
100
103
|
}
|