2

In case my Node.js v.13.12 app faces some technical issue, I'd like:

  1. to write a log message in the DB;
  2. to exit from the Node.js app with a specific code.

To achieve such behaviour I use the following code:

const onError = async function onError(error) {

    await logger.log("Critical error: " + error);

    process.exit(UNCAUGHT_FATAL_EXCEPTION);
}

The problem is that when I execute the code above, I reach await logger.log(…), go inside and then immediately continue to process.exit(UNCAUGHT_FATAL_EXCEPTION) and execute it.

As a result, the app is getting closed before the writing to the DB is finished, although I expect from await to wait until the logger.log(…) is done.

How to ensure that process.exit(UNCAUGHT_FATAL_EXCEPTION) will be executed only after logger.log(…) is done without using a callback?

Update:
The entire logger.log(…) code-chain:

export const log = async function log(source, severityNum, severityLevel, limitSeverityNum, functionName, message) {

    if ((severityNum > LOG_LEVELS.OFF.intLevel) && (severityNum <= limitSeverityNum)) {

        await writeLogToDB(source, severityNum, severityLevel, functionName, message);

    }

};

const writeLogToDB = async function writeLogToDB(source, severityNum, severityLevel, functionName, message) {

    try {

        const con = await getConnection();

        con.connect(function (err) {

            if (err) throw err;

            con.query(qryDict.QUERIES.setAddNewLog, [source, severityNum, severityLevel, functionName, message], function (err) {

                try {
                    if (err) {
                        console.error("addNewLog", err);
                    }
                    let response = JSON.stringify({
                        "result": true,
                        "message": "success"
                    });
                } catch (err) {
                    let response = JSON.stringify({
                        "result": false,
                        "message": err
                    });
                    return response;
                } finally {
                    con.close();
                }
            });
        });
    } catch (err) {
        con.close();
        console.error(err);
    }
};
Mike
  • 14,010
  • 29
  • 101
  • 161
  • Does `logger.log` return a promise? – nem035 Apr 12 '20 at 18:06
  • No, it's kind of `void` async-function, just writes to the DB and that's all. – Mike Apr 12 '20 at 18:07
  • Any async function returns a promise. That's what makes it an async function. Can you post the code for `logger.log`? – nem035 Apr 12 '20 at 18:08
  • @nem035 is right, to make it wait you have to make it return as a promise otherwise it wont wait for it to complete. – Furqan Aziz Apr 12 '20 at 18:10
  • In order for the caller function to successfully `await`, `logger.log()` must return a `Promise` that is fulfilled when when writing to the DB has successfully completed. – Roamer-1888 Apr 12 '20 at 18:15
  • 1
    All you do `await` is `getConnection`. Your async function is not waiting for the callback-style `con.connect` and `con.query` calls. Use promises here. – Bergi Apr 12 '20 at 18:17
  • 1
    You appear not to need both `log()` and `_writeLog()`. One appears effectively to be a synonym for the other. You could avoid bundling/unbundling args (into a js object) and simply write `export log = _writeLog;` – Roamer-1888 Apr 12 '20 at 18:54
  • 1
    @Roamer-1888, good point. Done. – Mike Apr 12 '20 at 19:07

1 Answers1

1

The problem is in the writeLogToDB function.

The function is async, but internally it uses the con.connect callback-based mechanism, which no code is await-ing on.

You have to use promises all the way for the await-ing to propagate:

const writeLogToDB = async function writeLogToDB(
  source,
  severityNum,
  severityLevel,
  functionName,
  message
) {
  const con = await getConnection();

  return new Promise((resolve, reject) => {
    con.connect(function(err) {
      if (err) return reject(err);

      con.query(
        qryDict.QUERIES.setAddNewLog,
        [source, severityNum, severityLevel, functionName, message],
        function(err) {
          if (err) return reject(err);

          resolve(
            JSON.stringify({
              result: true,
              message: "success"
            })
          );
        }
      );
    });
  }).finally(() => {
    // note: add a check here to do it only
    // if connection is opened to handle edge cases
    con.close();
  });
};

A cleaner solution would be to implement the connection methods themselves to be promise-based, not callback. (maybe util/promisify can help)

Then you'd have cleaner code:

const writeLogToDB = async function writeLogToDB(
  source,
  severityNum,
  severityLevel,
  functionName,
  message
) {
  const con = await getConnection();

  try {
    await con.connect();
    await con.query(qryDict.QUERIES.setAddNewLog, [
      source,
      severityNum,
      severityLevel,
      functionName,
      message
    ]);
    return JSON.stringify({
      result: true,
      message: "success"
    });
  } finally {
    // note: add a check here to do it only
    // if connection is opened to handle edge cases
    con.close();
  }
};
nem035
  • 34,790
  • 6
  • 87
  • 99
  • thanks for the code-snippet. You're right, the codebase currently is in a stage of migrating from callbacks to `await`/`async`, that's why some inconsistency takes a place. – Mike Apr 12 '20 at 18:29