We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docker中运行pm2 日志磁盘过大,导致项目无法运行。但是我们并不需要pm2日志,日志是通过容器进行收集的。
运行 pm2-runtime --json package.json 和 pm2-runtime server.js 时,发现后者不会打印日志到磁盘上,因为指定的日志目录为 /dev/null, 但我们package.json也没有指定out_file和error_file,但却会打印日志到 $HOME/.pm2/logs下。
打开pm2源码进行分析。
首先找到pm2-runtime命令文件,位于/bin目录下,可以看到引用的是Runtime4Docker文件
require('../lib/binaries/Runtime4Docker.js');
查看Runtime4Docker文件,分析后可以看到最后执行的是pm2.start命令,根据引用找到 lib/API.js文件
// 这个是命令入口 commander.command('*') .action(function(cmd){ Runtime.instanciate(cmd); }); var Runtime = { pm2 : null, instanciate : function(cmd) { .... // 执行启动命令 Runtime.startApp(cmd, function(err) { if (err) { console.error(err.message || err); return Runtime.exit(); } }); }); } } ..... startApp : function(cmd, cb) { function exec() { // 执行pm2 start 命令 this.pm2.start(cmd, commander, function(err, obj) { .... }); } // via --delay <seconds> option setTimeout(exec.bind(this), commander.delay * 1000); },
在API.js文件中找到start命令,分析可知,根据cmd参数的不同,分别执行了_startJson和_startScript命令
start(cmd, opts, cb) { .... if (Common.isConfigFile(cmd) || typeof cmd === "object") { // 配置启动 pm2-runtime --json package.json that._startJson(cmd, opts, "restartProcessId", cb); } else { // js脚本启动 pm2-runtime start server.js that._startScript(cmd, opts, cb); } }
首先分析 --json 的情况,就是执行 _startJson 方法,各种回调流转过程就跳过了,最终会调用startApps,并在Common.resolveAppAttributes这里 去初始化了参数配置,下一步进入 lib/Common.js
_startJson(file, opts, action, pipe, cb) { ..... // app启动 function startApps(app_name_to_start, cb) { ..... eachLimit( apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) { ..... try { // 配置路径获取 resolved_paths = Common.resolveAppAttributes( { cwd: that.cwd, pm2_home: that.pm2_home, }, app ); } catch (e) { apps_errored.push(e); return next(); } ...... }, ); return false; } }
resolveAppAttributes 方法中又调用了prepareAppConf进行初始化配置
Common.resolveAppAttributes = function(opts, legacy_app) { var appConf = fclone(legacy_app); // 初始化配置 var app = Common.prepareAppConf(opts, appConf); if (app instanceof Error) { Common.printError(cst.PREFIX_MSG_ERR + app.message); throw new Error(app.message); } return app; };
根据用户是否设置out_file,error_file等参数来初始化
Common.prepareAppConf = function(opts, app) { ...... ["log", "out", "error", "pid"].forEach(function(f) { // 获取out_file error_file等参数是否存在 var af = app[f + "_file"], ps, ext = f == "pid" ? "pid" : "log", isStd = !~["log", "pid"].indexOf(f); if (af) af = resolveHome(af); // 如果没有设置文件路径参数,就配置默认参数 if ((f == "log" && typeof af == "boolean" && af) || (f != "log" && !af)) { // cst是默认配置文件,默认目录配置(DEFAULT_LOG_PATH、DEFAULT_PID_PATH)在 paths.js 中 constants.js 引用了 paths.js ps = [ // 默认文件目录 cst["DEFAULT_" + ext.toUpperCase() + "_PATH"], // 这里是默认文件日志文件名称 formated_app_name + (isStd ? "-" + f : "") + "." + ext, ]; } // 如果设置了文件路径参数,就检测目录是否存在,不存在就创建目录 else if (f != "log" || (f == "log" && af)) { ps = [cwd, af]; var dir = path.dirname(path.resolve(cwd, af)); if (!fs.existsSync(dir)) { Common.printError( cst.PREFIX_MSG_WARNING + "Folder does not exist: " + dir ); Common.printOut(cst.PREFIX_MSG + "Creating folder: " + dir); require("mkdirp")(dir, function(err) { if (!err) return; Common.printError( cst.PREFIX_MSG_ERR + "Could not create folder: " + path.dirname(af) ); throw new Error("Could not create folder"); }); } } // PM2 paths // 设置默认参数,删除用户设置参数,然后返回 ps && (app[ "pm_" + (isStd ? f.substr(0, 3) + "_" : "") + ext + "_path" ] = path.resolve.apply(null, ps)); delete app[f + "_file"]; }); return app; };
看到这里我们就知道通过 --json 的方式启动会设置默认日志目录,我们再看下js脚本的方式启动
首先命令后启动时,如果没有配置output和error默认为/dev/null,该参数会传到_startScript方法中,opts的output和error都为/dev/null
commander.version(pkg.version) ..... .option('--error <path>', 'error log file destination (default disabled)', '/dev/null') .option('--output <path>', 'output log file destination (default disabled)', '/dev/null') ....
_startScript 方法中通过 Config.filterOptions 进行了配置过滤和转换 ,Config位于 lib/tools/Config.js
_startScript(script, opts, cb) { if (typeof opts == "function") { cb = opts; opts = {}; } var that = this; /** * Commander.js tricks */ // 参数过滤 var app_conf = Config.filterOptions(opts); ...... }
查看 filterOptions 方法,通过schema进行过滤和转换,将output、error参数转换成了 out_file和error_file
Config.filterOptions = function(cmd) { var conf = {}; var schema = this.schema; for (var key in schema) { var aliases = schema[key].alias; aliases && aliases.forEach(function(alias){ if (typeof(cmd[alias]) !== 'undefined') { conf[key] || (conf[key] = cmd[alias]); } }); } return conf; };
然后再看 _startScript 的 restartExistingProcessPathOrStartNew方法,该方法启动和重启时都会调用 restartExistingProcessPathOrStartNew 可以看到 Common.resolveAppAttributes 方法,后续执行逻辑与 --json模式一样 这里app_conf就是之前过滤的参数,因为已经设置了out_file和error_file为 /dev/null,所以最终日志目录为 /dev/null
function restartExistingProcessPathOrStartNew(cb) { that.Client.executeRemote("getMonitorData", {}, function(err, procs) { ...... var resolved_paths = null; try { resolved_paths = Common.resolveAppAttributes( { cwd: that.cwd, pm2_home: that.pm2_home, }, app_conf ); } catch (e) { Common.printError(e); return cb(Common.retErr(e)); } ..... }
通过以上解析,可以使用以下方案
The text was updated successfully, but these errors were encountered:
No branches or pull requests
问题描述
docker中运行pm2 日志磁盘过大,导致项目无法运行。但是我们并不需要pm2日志,日志是通过容器进行收集的。
问题查找
运行 pm2-runtime --json package.json 和 pm2-runtime server.js 时,发现后者不会打印日志到磁盘上,因为指定的日志目录为 /dev/null,
但我们package.json也没有指定out_file和error_file,但却会打印日志到 $HOME/.pm2/logs下。
源码分析
打开pm2源码进行分析。
首先找到pm2-runtime命令文件,位于/bin目录下,可以看到引用的是Runtime4Docker文件
查看Runtime4Docker文件,分析后可以看到最后执行的是pm2.start命令,根据引用找到 lib/API.js文件
在API.js文件中找到start命令,分析可知,根据cmd参数的不同,分别执行了_startJson和_startScript命令
首先分析 --json 的情况,就是执行 _startJson 方法,各种回调流转过程就跳过了,最终会调用startApps,并在Common.resolveAppAttributes这里
去初始化了参数配置,下一步进入 lib/Common.js
resolveAppAttributes 方法中又调用了prepareAppConf进行初始化配置
根据用户是否设置out_file,error_file等参数来初始化
看到这里我们就知道通过 --json 的方式启动会设置默认日志目录,我们再看下js脚本的方式启动
首先命令后启动时,如果没有配置output和error默认为/dev/null,该参数会传到_startScript方法中,opts的output和error都为/dev/null
_startScript 方法中通过 Config.filterOptions 进行了配置过滤和转换 ,Config位于 lib/tools/Config.js
查看 filterOptions 方法,通过schema进行过滤和转换,将output、error参数转换成了 out_file和error_file
然后再看 _startScript 的 restartExistingProcessPathOrStartNew方法,该方法启动和重启时都会调用
restartExistingProcessPathOrStartNew 可以看到 Common.resolveAppAttributes 方法,后续执行逻辑与 --json模式一样
这里app_conf就是之前过滤的参数,因为已经设置了out_file和error_file为 /dev/null,所以最终日志目录为 /dev/null
解决方案
通过以上解析,可以使用以下方案
The text was updated successfully, but these errors were encountered: