Problem description

The pM2 log disk in docker is too large, causing the project to fail. Procedure But we don’t need PM2 logs; logs are collected by the container.

The problem to find

When you run pm2-Runtime –json package.json and pm2-Runtime server.js, it is found that the latter does not print logs to disk. Because the specified log directory is /dev/null, package.json does not specify out_file and error_file, but prints logs to $HOME/.pm2/logs.

Source code analysis

Open pM2 source code for analysis.

Start by finding the PM2-Runtime command file in the /bin directory. You can see that the Runtime4Docker file is referenced

require('.. /lib/binaries/Runtime4Docker.js');

Copy the code

If you look at the Runtime4Docker file, you can see that the last command executed is m2. Start. Find the lib/ api.js file by reference

// This is the command entry

commander.command(The '*')

  .action(function(cmd){

    Runtime.instanciate(cmd);

  });





var Runtime = {

  pm2 : null.

  instanciate : function(cmd{

.

      // Run the start command

      Runtime.startApp(cmd, function(err{

        if (err) {

          console.error(err.message || err);

          return Runtime.exit();

        }

      });

    });

  }

}



.



startApp : function(cmd, cb{

    function exec({

      Run the pm2 start command

      this.pm2.start(cmd, commander, function(err, obj{

.

      });

    }

    // via --delay <seconds> option

    setTimeout(exec.bind(this), commander.delay * 1000);

  },

Copy the code

Find the start command in the api. js file. According to the CMD parameter, _startJson and _startScript commands are executed respectively

 start(cmd, opts, cb) {

.

    if (Common.isConfigFile(cmd) || typeof cmd === "object") {

      Pm2-runtime --json package.json

      that._startJson(cmd, opts, "restartProcessId", cb);

    } else {

      Pm2 - Runtime start server.js

      that._startScript(cmd, opts, cb);

    }

  }

Copy the code

First analysis – json, perform the _startJson method, various callbacks transfer process is skipped, will eventually call startApps, and in Common. ResolveAppAttributes here to initialize the parameters configuration, Next go to lib/ common.js

_startJson(file, opts, action, pipe, cb) {

   

.



    / / app started

    function startApps(app_name_to_start, cb{

     

.



      eachLimit(

        apps_to_start,

        conf.CONCURRENT_ACTIONS,

        function(app, next{

      

.



          try {

            // Configure path acquisition

            resolved_paths = Common.resolveAppAttributes(

              {

                cwd: that.cwd,

                pm2_home: that.pm2_home,

              },

              app

            );

          } catch (e) {

            apps_errored.push(e);

            return next();

          }

          

.

        },

       

      );

      return false;

    }

  }

Copy the code

PrepareAppConf is called in the resolveAppAttributes method to initialize the configuration

Common.resolveAppAttributes = function(opts, legacy_app{

  var appConf = fclone(legacy_app);

  // Initialize the configuration

  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;

};

Copy the code

Depending on whether the user sets out_file,error_file, etc

Common.prepareAppConf = function(opts, app{

.

    ["log"."out"."error"."pid"].forEach(function(f{

      // Check whether parameters such as out_file error_file exist

      var af = app[f + "_file"].

        ps,

        ext = f == "pid" ? "pid" : "log".

isStd = ! ~ ["log"."pid"].indexOf(f);



      if (af) af = resolveHome(af);

  

      // If no file path parameter is set, set the default parameter

      if ((f == "log" && typeof af == "boolean"&& af) || (f ! ="log" && !af)) {

        // CST is the default configuration file. The default directory configuration (DEFAULT_LOG_PATH, DEFAULT_PID_PATH) is referenced in paths.js with constants

        ps = [

          // Default file directory

          cst["DEFAULT_" + ext.toUpperCase() + "_PATH"].

          // Here is the default file log file name

          formated_app_name + (isStd ? "-" + f : "") + "." + ext,

        ];

      } 

      // If the file path parameter is set, check if the directory exists, and create the directory if it does not

      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

      

      // Set the default parameters, delete the user Settings parameters, and return

      ps &&

        (app[

          "pm_" + (isStd ? f.substr(0.3) + "_" : "") + ext + "_path"

        ] = path.resolve.apply(null, ps));

      delete app[f + "_file"];

    });

  

    return app;

  };

Copy the code

The default log directory is set to json. Let’s look at javascript

If output and error are not configured, the default value is /dev/null. This parameter is passed to the _startScript method. Opts output and error are /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')

.

Copy the code

The _startScript method filters and transforms the configuration through config.filterOptions, which is located in lib/tools/ config.js

_startScript(script, opts, cb) {

    if (typeof opts == "function") {

      cb = opts;

      opts = {};

    }

    var that = this;



    / * *

     * Commander.js tricks

* /


    // Filter parameters

    var app_conf = Config.filterOptions(opts);

   

.

  }

Copy the code

Look at the filterOptions method, filter and convert the output and error parameters to out_file and error_file through the schema

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;

};

Copy the code

Then look at _startScript restartExistingProcessPathOrStartNew method, Start and restart the method will be called restartExistingProcessPathOrStartNew can see Common. ResolveAppAttributes method, The subsequent execution logic is the same as the — JSON mode. In this case, app_conf is the filtered parameter. Out_file and error_file are set to /dev/null, so the final log directory is /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));

        }

.



    }

Copy the code

The solution

With the above parsing, the following scenarios can be used

  • Set out_file and error_file to /dev/null in package.json
  • Run the pm2-Runtime start server.js command to start the server