skywalking-backend-js-netease
Version:
The NodeJS agent for Apache SkyWalking
195 lines • 8.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandProfileDump = void 0;
const tslib_1 = require("tslib");
const http_grpc_pb_1 = require("../../../proto/tunnel/http_grpc_pb");
const http_pb_1 = require("../../../proto/tunnel/http_pb");
const AgentConfig_1 = tslib_1.__importDefault(require("../../../config/AgentConfig"));
const AuthInterceptor_1 = tslib_1.__importDefault(require("../grpc/AuthInterceptor"));
const logging_1 = require("../../../logging");
const grpc_js_1 = require("@grpc/grpc-js");
const inspector_1 = tslib_1.__importDefault(require("inspector"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const path_1 = tslib_1.__importDefault(require("path"));
const logger = logging_1.createLogger(__filename);
const propertyKeys = new Map();
propertyKeys.set('product', { name: 'product', type: 'string' });
propertyKeys.set('service', { name: 'service', type: 'string' });
propertyKeys.set('node', { name: 'node', type: 'string' });
propertyKeys.set('taskId', { name: 'taskId', type: 'string' });
propertyKeys.set('type', { name: 'type', type: 'string' });
propertyKeys.set('createTime', { name: 'createTime', type: 'number' });
propertyKeys.set('duration', { name: 'duration', type: 'number' });
class CommandProfileDump {
constructor() {
this.options = () => { return { deadline: Date.now() + 30000 }; };
this.session = new inspector_1.default.Session();
}
start() {
this.session.connect();
this.url = `http://127.0.0.1:11800/${AgentConfig_1.default.authorization}/profile/dump`;
this.httpReportServiceClient = new http_grpc_pb_1.HttpReportServiceClient(AgentConfig_1.default.collectorAddress, AgentConfig_1.default.secure ? grpc_js_1.credentials.createSsl() : grpc_js_1.credentials.createInsecure());
this.session.post('Profiler.enable', (err) => {
if (err) {
logger.error(`Error Profile cpu enable sampler`, err);
return;
}
});
this.session.post('HeapProfiler.enable', (err) => {
if (err) {
logger.error(`Error Profile heap enable sampler`, err);
return;
}
});
}
execute(cmd) {
const props = {};
cmd.getArgsList()
.sort((a, b) => a.getKey().localeCompare(b.getKey()))
.forEach(arg => {
if (arg.getKey() === 'SerialNumber') {
return;
}
if (propertyKeys.has(arg.getKey())) {
const prop = propertyKeys.get(arg.getKey());
props[prop.name] = convert(arg.getValue(), prop.type);
logger.info(`Profile dump config ${arg.getKey()} = ${arg.getValue()}`);
}
});
props['startTime'] = Date.now();
const fileName = `profile-${props['startTime']}`;
// do with profile
switch (props['type'] || 'cpu') {
case 'cpu':
this.profile(props, fileName);
break;
case 'heap':
this.heapprofile(props, fileName);
break;
case 'dump':
this.heapdump(props, fileName);
break;
}
}
profile(props = {}, fileName) {
const duration = (props['duration'] || 60 * 1000); // to millisecond
props['uri'] = path_1.default.join(AgentConfig_1.default.dumpFilePath, `${fileName}.cpuprofile`);
this.session.post('Profiler.start', (err) => {
if (err) {
logger.error(`Error Profile cpu start sampler`, err);
return;
}
setTimeout(() => {
// some time later...
this.session.post('Profiler.stop', (err, { profile }) => {
// Write profile to disk
props['stopTime'] = Date.now();
props['duration'] = props['stopTime'] - props['startTime'];
if (!err) {
this.writeData(props, profile);
logger.debug(`send profile result info to collector, ${JSON.stringify(props)}`);
this.send(props)
.then(() => {
logger.info(`Success Profile Node.js, file is ${props['uri']}`);
}).catch(err => {
logger.error(`Success Profile Node.js, file is ${props['uri']}, but send info to collector error`, err);
});
}
else {
logger.error(`Error Profile dump cpu do sampler for task ${props['uri']}`, err);
}
});
}, duration);
});
}
heapprofile(props = {}, fileName) {
const duration = (props['duration'] || 60 * 1000); // to millisecond
props['uri'] = path_1.default.join(AgentConfig_1.default.dumpFilePath, `${fileName}.heapprofile`);
this.session.post('HeapProfiler.startSampling', (err) => {
if (err) {
logger.error(`Error Profile heap start sampler`, err);
return;
}
setTimeout(() => {
// some time later...
this.session.post('HeapProfiler.stopSampling', (err, { profile }) => {
// Write profile to disk
props['stopTime'] = Date.now();
props['duration'] = props['stopTime'] - props['startTime'];
if (!err) {
this.writeData(props, profile);
logger.debug(`send profile result info to collector, ${JSON.stringify(props)}`);
this.send(props)
.then(() => {
logger.info(`Success Profile Node.js, file is ${props['uri']}`);
}).catch(err => {
logger.error(`Success Profile Node.js, file is ${props['uri']}, but send info to collector error`, err);
});
}
else {
logger.error(`Error Profile dump cpu do sampler for task ${props['uri']}`, err);
}
});
}, duration);
});
}
heapdump(props = {}, fileName) {
props['uri'] = path_1.default.join(AgentConfig_1.default.dumpFilePath, `${fileName}.heapsnapshot`);
const res = [];
const getChunk = (m) => {
res.push(m.params.chunk);
};
this.session.on('HeapProfiler.addHeapSnapshotChunk', getChunk);
this.session.post('HeapProfiler.takeHeapSnapshot', {}, (err) => {
this.session.removeListener('HeapProfiler.addHeapSnapshotChunk', getChunk);
if (err) {
logger.error(`Error Profile dump heap for task ${props['uri']}`, err);
}
else {
this.writeData(props, JSON.parse(res.join('')));
props['stopTime'] = Date.now();
props['duration'] = props['stopTime'] - props['startTime'];
logger.debug(`send profile result info to collector, ${JSON.stringify(props)}`);
this.send(props)
.then(() => {
logger.info(`Success Profile Node.js, file is ${props['uri']}`);
}).catch(err => {
logger.error(`Success Profile Node.js, file is ${props['uri']}, but send info to collector error`, err);
});
}
});
}
send(prop = {}) {
return new Promise((resolve, reject) => {
const stream = this.httpReportServiceClient.invoke(AuthInterceptor_1.default(), this.options(), (err, resp) => {
if (err) {
reject(err);
return;
}
resolve({ code: resp.getCode(), body: resp.getBody() });
});
const req = new http_pb_1.HttpReqObject()
.setMethod("PUT")
.setUrl(this.url)
.setBody(new TextEncoder().encode(JSON.stringify(prop)))
.setHeadersList([new http_pb_1.PairObject().setKey("Content-Type").setValue("application/json")]);
stream.write(req);
stream.end();
});
}
writeData(props = {}, data) {
fs_1.default.writeFileSync(props['uri'], JSON.stringify(data));
}
}
exports.CommandProfileDump = CommandProfileDump;
function convert(val, type) {
switch (type) {
case 'boolean':
return 'true' === val;
case 'number':
return +val || 0;
default:
return val;
}
}
//# sourceMappingURL=CommandProfileDump.js.map