解决方案:
进入 zookeeper 安装目录下,创建启动脚本文件 start.sh
start.sh
#!/usr/bin/env bash
ZOO_LOG_DIR='/data0/logs/zookeeper' ZOO_LOG4J_PROP='INFO,ROLLINGFILE' bin/zkServer.sh start
ZOO_LOG_DIR 设置了日志文件夹路径, ZOO_LOG4J_PROP='INFO,ROLLINGFILE'
设置日志输出级别是INFO,采用滚动文件更新的方式生成日志文件。
为了方便停止zookeeper 程序,我们也可以创建 stop.sh 文件,用来停止 zookeeper。这个文件可有可无,不影响解决本问题。
stop.sh
#!/usr/bin/env bash
bin/zkServer.sh stop
找解决方案的思路
我先给出了解决方案,方便读者解决问题。然后我再分享一下我自己是怎么找到这个解决方案的。这个章节主要讲一下解决的过程。
在zookeeper的安装文件夹里,我先进入配置文件夹 conf,里面有 log4j.properties 文件。log4j 是常见的日志组件,我猜测这是日志的配置文件。打开 log4j.properties 文件,
log4j.properties
# Copyright 2012 The Apache Software Foundation
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Define some default values that can be overridden by system properties
zookeeper.root.logger=INFO, CONSOLE
zookeeper.console.threshold=INFO
zookeeper.log.dir=.
zookeeper.log.file=zookeeper.log
zookeeper.log.threshold=INFO
zookeeper.log.maxfilesize=256MB
zookeeper.log.maxbackupindex=20
zookeeper.tracelog.dir=${zookeeper.log.dir}
zookeeper.tracelog.file=zookeeper_trace.log
log4j.rootLogger=${zookeeper.root.logger}
#
# console
# Add "console" to rootlogger above if you want to use this
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
#
# Add ROLLINGFILE to rootLogger to get log file output
#
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold}
log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
log4j.appender.ROLLINGFILE.MaxFileSize=${zookeeper.log.maxfilesize}
log4j.appender.ROLLINGFILE.MaxBackupIndex=${zookeeper.log.maxbackupindex}
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
#
# Add TRACEFILE to rootLogger to get log file output
# Log TRACE level and above messages to a log file
#
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.file}
log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
### Notice we are including log4j's NDC here (%x)
log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n
文件中,log4j.appender.CONSOLE
相关代码和 log4j.appender.ROLLINGFILE
相关代码表示log4j定义了两个appender:CONSOLE 和 ROLLINGFILE。
显然 zookeeper.root.logger=INFO, CONSOLE
设置了log4j 内部的 log4j.rootLogger 参数,这个参数控制日志级别和日志输出方式。INFO, CONSOLE 指默认是INFO级别和控制台输出(appender=CONSOLE)。
即 log4j.rootLogger
第二项说明了是用哪个appender。
zookeeper.log.dir=.
控制了输出日志的文件夹。
打开 bin/zkServer.sh
文件,我们可以先往下翻,找到下面这段代码:
nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
"-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
-XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
-cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
可见在启动zookeeper的时候,已经指定了 zookeeper.root.logger
和 zookeeper.log.dir
两个参数,变量分别是 ZOO_LOG4J_PROP
和 ZOO_LOG_DIR
。我们往上翻看文件:
# use POSTIX interface, symlink is followed automatically
ZOOBIN="${BASH_SOURCE-$0}"
ZOOBIN="$(dirname "${ZOOBIN}")"
ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)"
if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then
. "$ZOOBINDIR"/../libexec/zkEnv.sh
else
. "$ZOOBINDIR"/zkEnv.sh
fi
ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)"
ZOOBINDIR 变量是脚本当前路径,即 安装目录/bin
。
zkServer.sh 还要执行 zkEnv.sh 中的内容。看名称猜测 zkEnv.sh 文件中是zookeeper的环境参数。
打开 zkEnv.sh 文件,看下面的代码:
if [ "x${ZOO_LOG4J_PROP}" = "x" ]
then
ZOO_LOG4J_PROP="INFO,CONSOLE"
fi
"x${ZOO_LOG4J_PROP}" = "x"
这行代码是常见的判断变量是否赋值的方式。
如果前面的脚本或者用户输入的命令中没有赋值 ZOO_LOG4J_PROP ,使用默认值 "INFO,CONSOLE"
。
打开 zkEnv.sh 文件,看下面的代码:
if [ "x${ZOO_LOG_DIR}" = "x" ]
then
ZOO_LOG_DIR="$ZOOKEEPER_PREFIX/logs"
fi
ZOO_LOG_DIR 的逻辑类似上面提到的 ZOO_LOG4J_PROP,如果前面的脚本或者用户输入的命令中没有赋值ZOO_LOG_DIR,ZOO_LOG_DIR的默认值是"$ZOOKEEPER_PREFIX/logs"
。
我们在zkEnv.sh 文件开头可以看到变量 ZOOKEEPER_PREFIX 的定义:
ZOOBINDIR="${ZOOBINDIR:-/usr/bin}" # 28
ZOOKEEPER_PREFIX="${ZOOBINDIR}/.." # 29
29 行说明 ZOOKEEPER_PREFIX 是 ZOOBINDIR 的父级目录。
28 行看一下 ZOOBINDIR 的取值。如果脚本中已经赋值过 ZOOBINDIR ,使用原先的值。如果没有赋值过 ZOOBINDIR ,默认值是 /usr/bin。
因为 zkServer.sh 是调用 zkEnv.sh 的父级脚本文件,zkServer.sh 中已经赋值了ZOOBINDIR,ZOOBINDIR当前目录,也就是 安装目录/bin
,所以 ZOOKEEPER_PREFIX 就应该是安装目录。这与我的经验相符,默认情况下zookeeper会在安装目录下生成一个log文件夹,里面放置日志文件。
综上所述,我们只需要在启动zookeeper的时候设置 ZOO_LOG4J_PROP 和 ZOO_LOG_DIR 这两个变量就行。不需要改动zookeeper原有的脚本文件。