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,不需要安装)
我有一个 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,不需要安装)