CentOS,Node.js,init.d,ENOENT,应用程序自动启动时无法在系统重启时打开或写入日志文件

CentOS, Node.js, init.d, ENOENT, Can't open or write to log files on system reboot when app started automatically

我有一个 Node.js 服务脚本,它必须在我的 CentOS 7 服务器启动时执行。我使用 init.d 模板来创建启动守护进程。如果我以 root 身份登录服务器并使用以下命令在终端中手动执行脚本,init.d 脚本将完美运行:

sudo /etc/init.d/ServerStatusService start(stop,restart,status)

Node 脚本可以毫无问题地执行并执行其操作,其中包括写入应用程序目录中日志文件夹中的一些日志文件。

我遇到的问题是,当服务器重新启动时,init.d 脚本执行得很好,它也启动了 Node 脚本,只有 Node 脚本然后出错并出现类型为 'ENOENT' 说明它无法打开要写入的日志文件。

从我读过的所有内容来看,所有 init.d 脚本在启动时都以 root 身份执行,所以我希望它能正常工作,就像我在终端。

对于我的生活,我无法弄清楚这笔交易是什么。我假设它是系统启动时的权限或环境,与终端中的 运行 sudo 不同。

我还 运行 init.d 脚本使用以下选项复制机器在启动时的状态,但它失败了,就像在启动时一样。

env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" /etc/init.d/ServerStatusService start

我的init.d脚本

#!/bin/sh

NODE_ENV="staging"
PORT="8088"
APP_DIR="/var/www/ServerStatusClientn"
NODE_APP="serverStatusService.js"
CONFIG_DIR="$APP_DIR"
PID_DIR="$APP_DIR/pid"
PID_FILE="$PID_DIR/app.pid"
LOG_DIR="$APP_DIR/logs"
LOG_FILE="$LOG_DIR/app.log"
NODE_EXEC=$(which node)

USAGE="Usage: [=14=] {start|stop|restart|status} [--force]"
FORCE_OP=false

pid_file_exists() {
    [ -f "$PID_FILE" ]
}

get_pid() {
    echo "$(cat "$PID_FILE")"
}

is_running() {
    PID=$(get_pid)
    ! [ -z "$(ps aux | awk '{print }' | grep "^$PID$")" ]
}

start_it() {
    mkdir -p "$PID_DIR"
    mkdir -p "$LOG_DIR"

    echo "Starting node app ..."
    PORT="$PORT" NODE_ENV="$NODE_ENV" NODE_CONFIG_DIR="$CONFIG_DIR" $NODE_EXEC "$APP_DIR/$NODE_APP"  1>"$LOG_FILE" 2>&1 &
    echo $! > "$PID_FILE"
    echo "Node app started with pid $!"
}

stop_process() {
    PID=$(get_pid)
    echo "Killing process $PID"
    kill $PID
}

remove_pid_file() {
    echo "Removing pid file"
    rm -f "$PID_FILE"
}

start_app() {
    if pid_file_exists
    then
        if is_running
        then
            PID=$(get_pid)
            echo "Node app already running with pid $PID"
            exit 1
        else
            echo "Node app stopped, but pid file exists"
            if [ $FORCE_OP = true ]
            then
                echo "Forcing start anyways"
                remove_pid_file
                start_it
            fi
        fi
    else
        start_it
    fi
}

stop_app() {
    if pid_file_exists
    then
        if is_running
        then
            echo "Stopping node app ..."
            stop_process
            remove_pid_file
            echo "Node app stopped"
        else
            echo "Node app already stopped, but pid file exists"
            if [ $FORCE_OP = true ]
            then
                echo "Forcing stop anyways ..."
                remove_pid_file
                echo "Node app stopped"
            else
                exit 1
            fi
        fi
    else
        echo "Node app already stopped, pid file does not exist"
        exit 1
    fi
}

status_app() {
    if pid_file_exists
    then
        if is_running
        then
            PID=$(get_pid)
            echo "Node app running with pid $PID"
        else
            echo "Node app stopped, but pid file exists"
        fi
    else
        echo "Node app stopped"
    fi
}

case "" in
    --force)
        FORCE_OP=true
    ;;

    "")
    ;;

    *)
        echo $USAGE
        exit 1
    ;;
esac

case "" in
    start)
        start_app
    ;;

    stop)
        stop_app
    ;;

    restart)
        stop_app
        start_app
    ;;

    status)
        status_app
    ;;

    *)
        echo $USAGE
        exit 1
    ;;
esac

错误日志

error: uncaughtException: ENOENT, open 'logs/mediaServerStatus-debug.log' date=Mon Feb 09 2015 11:03:00 GMT-0700 (MST), pid=6329, uid=0, gid=0, cwd=/, execPath=/usr/bin/node, version=v0.10.33, argv=[/usr/bin/node, /var/www/MediaServerStatusService/service.js], rss=40230912, heapTotal=28055552, heapUsed=14903704, loadavg=[0.064453125, 0.18310546875, 0.181640625], uptime=6808.08772411, trace=[], stack=[Error: ENOENT, open 'logs/mediaServerStatus-debug.log']
error: uncaughtException: ENOENT, open 'logs/mediaServerStatus-debug.log' date=Mon Feb 09 2015 11:03:00 GMT-0700 (MST), pid=6329, uid=0, gid=0, cwd=/, execPath=/usr/bin/node, version=v0.10.33, argv=[/usr/bin/node, /var/www/MediaServerStatusService/service.js], rss=40230912, heapTotal=28055552, heapUsed=15008752, loadavg=[0.064453125, 0.18310546875, 0.181640625], uptime=6808.090709357, trace=[], stack=[Error: ENOENT, open 'logs/mediaServerStatus-debug.log']

使用 Winston 记录日志

var winston = require('winston'),
config = require('../config/config');
winston.emitErrs = true;

var debug = new winston.Logger({
    transports: [
        new winston.transports.Console({
            level: 'debug',
            handleExceptions: true,
            json: false,
            colorize: true
        })
    ]
});

if(config.logging.debugToFile){
    debug.add(
        winston.transports.File,
        {
            name: 'debug-file',
            level: 'debug',
            filename: config.logging.debug,
            handleExceptions: true,
            json: true,
            maxsize: 5242880, //5MB
            maxFiles: 5,
            colorize: false
        }
    );
}

var info = new winston.Logger({
    transports: [
        new winston.transports.Console({
            level: 'info',
            handleExceptions: true,
            json: false,
            colorize: true
        })
    ]
});

var warn = new winston.Logger({
    transports: [
        new winston.transports.File({
            name: 'warn-file',
            level: 'warn',
            filename: config.logging.warn,
            handleExceptions: true,
            json: true,
            maxsize: 5242880, //5MB
            maxFiles: 5,
            colorize: false
        }),
        new winston.transports.Console({
            level: 'warn',
            handleExceptions: true,
            json: false,
            colorize: true
        })
    ]
});

var error = new winston.Logger({
    transports: [
        new winston.transports.File({
            name: 'error-file',
            level: 'error',
            filename: config.logging.error,
            handleExceptions: true,
            json: true,
            maxsize: 5242880, //5MB
            maxFiles: 5,
            colorize: false
        }),
        new winston.transports.Console({
            level: 'error',
            handleExceptions: true,
            json: false,
            colorize: true
        })
    ]
});

var loggers = {
    debug: function(msg, callback){
        debug.debug(msg, callback);
    },
    info: function(msg, callback){
        info.info(msg, callback);
    },
    warn: function(msg, callback){
        warn.warn(msg, callback);
    },
    error: function(msg, callback){
        error.error(msg, callback);
    },
    log: function(level,msg, callback){
        var lvl = exports[level];
        lvl(msg, callback);
    }
};

// Logging
module.exports = loggers;

配置保存日志文件路径

logging: {
    debugToFile: true,
    debug: './logs/mediaServerStatus-debug.log',
    info: './logs/mediaServerStatus-info.log',
    warn: './logs/mediaServerStatus-warn.log',
    error: './logs/mediaServerStatus-error.log'
}

Init.d 脚本从 node 可执行文件 的路径启动 Node 脚本。

您需要通过__dirname指定脚本的目录以使路径成为绝对路径。

logging: {
    debugToFile: true,
    debug: path.join(__dirname, 'logs/mediaServerStatus-debug.log'),
    info:  path.join(__dirname, 'logs/mediaServerStatus-info.log'),
    warn:  path.join(__dirname, 'logs/mediaServerStatus-warn.log'),
    error: path.join(__dirname, 'logs/mediaServerStatus-error.log'),
}

一定要var path = require('path');在它之前。 (it's a native module,不需要安装)