在 Linux 服务器上执行hive命令后,屏幕第一行跳出来“Logging initialized using configuration in jar:file:/usr/local/hive/lib/hive-common...”,很多人第一反应是慌了:这是不是报错了?是不是路径配错了?其实不是,这是一条典型的日志初始化提示,说明 Hive 正在通过 log4j 加载日志配置,而且配置文件的来源竟然是 lib 目录下打包进 jar 里的一个配置。这条信息本身没有错,但顺着它往深里挖,能牵出一连串和 Hive 启动、日志、元数据库、环境变量相关的坑。这篇文章就是我实测过程中对这条日志的完整复盘,从它是什么、为什么会出现,到它背后关联的初始化机制和排查思路,一步步拆开讲清楚。如果你正要上手 Hive,或者启动时被各种莫名其妙的日志搞昏头,这篇能帮你省下不少瞎折腾的时间。
很多初学者以为 Hive 是数据库,实际它只是把 HiveQL 翻译成 MapReduce/Spark 等分布式作业的“翻译官”,元数据要另存到关系型库里(比如 MySQL 或自带的 Derby)。这条日志出现的位置,恰恰就是 Hive 这个“翻译官”准备开口翻译前,先把自己屋里的日志工具点亮的瞬间。
1. 这行日志到底在说什么
先别急着改配置,我们把这条日志拆开揉碎了看。它长这样:
Logging initialized using configuration in jar:file:/usr/local/hive/lib/hive-common-1.2.1.jar!/hive-log4j.properties1.1 逐段解读日志含义
Logging initialized:日志系统已经完成初始化,这是 log4j/log4j2 在启动阶段打印的状态信息。using configuration in jar:file::日志框架的配置文件来自一个 jar 包,而不是来自conf目录下的独立文件。/usr/local/hive/lib/hive-common-1.2.1.jar!/hive-log4j.properties:这个 jar 包的路径,以及 jar 内部存放日志配置的文件路径。
也就是说,Hive 在启动时把hive-common-*.jar加入了 classpath,而 log4j 在 classpath 里自动扫描,找到了 jar 包内的hive-log4j.properties,就把它当作默认配置加载了。
1.2 配置从 jar 包加载是好是坏
它本身不是错误,也没有任何警示含义,只是 log4j 的正常输出。但它暴露了一个信息:当前生效的日志配置是 jar 包内置的“出厂默认值”。
这意味着:
- 日志输出的格式、文件位置、滚动策略大概率不是你自己定义的。
- 如果你在
$HIVE_HOME/conf下放了自定义的hive-log4j.properties或hive-log4j2.properties,但启动时还是打印这行 jar 路径,说明你的自定义配置并没有被正确加载。 - 日志输出到了哪里?默认情况下可能是控制台,也可能是
$HIVE_HOME/logs或/tmp/用户名,取决于 Hive 启动脚本里的HIVE_LOG4J_FILE和hive.log.dir的设定。
所以判断的要点是:初次安装启动,看到这行很正常;如果你已经改过 conf 下的日志配置,仍然看到 This jar 路径,那就格式化有问题了。
2. 为什么配置文件会跑到 lib jar 包里去
好,核心问题来了:为什么一条 log4j 配置,不在 conf 目录里,而在 hive-common 的 jar 包里?
这要从 Hive 的启动流程说起。Hive 是一个用 Java 写的项目,所有功能都依托于 classpath 上的各种 jar 包。hive-common模块里包含一些通用工具类和基础配置文件,日志配置也以“模板”的形式被打进了 jar,作为最低保障。这样即使你的conf目录是空的,Hive 也能把日志系统跑起来,不至于启动过程静悄悄,出了事连日志都没有。
对应到 Java 项目中,这就是一种“内置默认配置兜底”机制。只要 classpath 里有这个 jar,log4j 就能自动找到它的配置文件。服务器安装时,unzip或tar解压后,bin/hive脚本会自动拼接 classpath,lib/*下所有 jar 都会加入,hive-common-*.jar自然也在里边。log4j 沿着 classpath 扫描到hive-log4j.properties,于是打印出那行日志。
这里还藏着一个细节:classpath 的扫描顺序。log4j 并非只扫描一个 jar,它按 classpath 的排列顺序从上往下找。如果你的 classpath 里同时有多个 jar 各带一份hive-log4j.properties,实际生效的是靠前的那一份。这就是为什么有同事在conf下放了配置文件却不起作用——因为 classpath 里排在更前面的 jar 配置先被命中了。
知识点回顾:遇到“日志配置不生效”时,不要先去怀疑配置文件语法,先确认你的配置文件是否真的在 classpath 中,且排在 jar 内置配置项之前。
3. 第一次启动 Hive 时,这行日志前后通常还跟着什么
单独一条日志看了半天,不如直接把它放进完整的“首次启动”场景里,看看这个日志在整个流程里处于什么位置,前后还会冒出哪些东西。
3.1 首次启动前的准备工作
按常规做法,某公司在测试服务器/usr/local/hive解压了 Hive 安装包,并完成了系统和 Java 环境的基础检查:
# 检查 Java 版本 java -version # 设置环境变量 export JAVA_HOME=/usr/local/java export HIVE_HOME=/usr/local/hive export PATH=$PATH:$HIVE_HOME/bin如果还装了 Hadoop,需要在hive-site.xml里设置元数据库连接。这一步常用 MySQL,执行初始化元数据:
cd $HIVE_HOME/conf cp hive-default.xml.template hive-site.xml # 修改 hive-site.xml 里的 javax.jdo.option.ConnectionURL、ConnectionDriverName、ConnectionUserName、ConnectionPassword # 初始化元数据 schema cd $HIVE_HOME $HIVE_HOME/bin/schematool -dbType mysql -initSchema3.2 实际执行 hive 命令后看到的信息流
完成准备后,在命令行敲下hive,大概率会看到这样的输出顺次出现:
Logging initialized using configuration in jar:file:/usr/local/hive/lib/hive-common-...jar!/hive-log4j.properties Hive-input: input = ... Hive-on-MR ... hive (default)>也就是说,最顶部那行日志刚要出来,紧接着就是 Hive 的版本及运行模式,然后进入hive (default)>提示符。看到这个,说明整个启动链路已经走通。
但这里有个很常见的陷阱:如果你把元数据库配成了 MySQL,而且没装对应的 JDBC 驱动 jar,或者连接串恰好写错了主机名,那这行日志之后就会出现密密麻麻的异常堆栈,包括Unable to open a test connection to the given database、MetastoreConnectionException等。新手很容易误以为是“日志初始化失败导致 Hive 崩溃”,其实日志初始化很成功,崩溃的是元数据库连接。
所以请记住:Logging initialized是一道“起跑线”,它之后的内容才是真正的悲喜剧。排查任何启动问题,优先看这行之后的完整输出,尤其是最早出现的堆栈第一行。
4. 从这行日志往下的常见连环坑与排查方法
有了完整场景,我们真正需要弄清的是:如果启动不顺利,下一步该怎么查?很多操作人员容易犯一个老毛病——直接去搜报错关键字,结果搜出来的全是环境差异很大的案例,越看越乱。活该冷静。
4.1 先看懂 Hive 日志的走向
Hive 的日志分两部分:一部分是刚才那行初始化日志代表的启动信息;另一部分才是真正的运行日志。默认情况下,Hive 的日志可能会存在:
- 启动时前台终端直接输出的 stdout/stderr
$HIVE_HOME/logs/目录下的文件/tmp/<当前系统用户>/hive.log(Hive 默认按用户创建临时日志目录)
确定到底是哪一只,最简单的办法是执行启动时带 debug 参数:
hive --hiveconf hive.root.logger=DEBUG,console这样会把日志级别临时调到 DEBUG 并打到控制台,启动过程的大部分内部信息都会暴露出来。如果你确实想看元数据连接、初始化 schema 的细节,这个参数非常有效。
4.2 启动日志速查表
我做了一个问题速查表,覆盖我在实际操作中处理过的高频故障:
| 日志特征 | 含义 | 处理方式 |
|---|---|---|
打印 Logging initialized 之后进入hive (default)> | 正常启动 | 直接执行 SQL 测试 |
| 紧接着出现 ClassNotFoundException,涉及 MySQL 或 JDBC 驱动 | 缺少 JDBC jar 包 | 下载对应版本驱动放到$HIVE_HOME/lib下 |
出现MalformedURLException或 URL 类异常 | hive-site.xml里的元数据库连接串写错 | 修正 jdbc:mysql:// 后的主机名、端口、库名 |
出现Derby相关锁异常 | 用了默认 Derby 元数据库,且重复并发启动 | 改用 MySQL,或把 derby.log 与 metastore_db 目录清理后重试 |
出现Schema initialization failed | 元数据表未初始化,或版本不匹配 | 先执行schematool -dbType mysql -initSchema,再检查 Hive 版本和 MySQL 版本兼容性 |
出现MetastoreConnectionException | 元数据库连接被拒 | 检查 MySQL 服务是否运行,账号密码是否允许远程连接 |
出现Please set HIVE_HOME | 环境变量缺失 | 在/etc/profile或当前用户.bashrc中导出 HIVE_HOME |
4.3 排查日志时的正确打开方式
如果你把日志级别调到 DEBUG,会产生大量信息。此时不要像看小说一样从头往下读,我自己的做法是:先用错误级别过滤,把完整输出重定向到文件里,再通过 grep 提取关键字:
hive --hiveconf hive.root.logger=DEBUG,console 2>&1 | tee /tmp/hive-startup.log grep -i -E "error|exception|failed" /tmp/hive-startup.log | head -30这样能很快定位到真正的异常点,比如Caused by:部分会准确指出是网络不通、权限不足还是 SQL 语法问题。注意,Hive 的异常栈经常很长,真正的根因往往在Caused by:之后,不要只看开头几行。
5. 让这条日志不再刷屏:日志级别与配置文件的正解
如果你已经确认 Hive 能正常进入hive (default)>,只是每次启动时那行日志和大量 INFO 级别的输出让人心烦,那我们可以通过自定义日志配置把它压下去。
5.1 找到真正生效的日志配置文件
在 Hive 2.x/3.x 中,日志配置默认有两种形式:老版本用hive-log4j.properties,新版本用hive-log4j2.properties(Log4j2 格式)。配置文件通常存在于:
$HIVE_HOME/conf/hive-log4j2.properties.template$HIVE_HOME/conf/hive-log4j.properties.template- jar 包的根目录,即刚才那行日志里显示的位置
要自定义,需要把模板复制成真正会生效的文件:
cd $HIVE_HOME/conf cp hive-log4j2.properties.template hive-log4j2.properties5.2 修改核心参数
编辑hive-log4j2.properties,找到rootLogger相关行。默认可能长这样:
rootLogger.level=INFO rootLogger.appenderRef.stdout.ref=console想减少控制台噪音,把rootLogger.level调成WARN。这样像Logging initialized这类 INFO 日志就不会再打到控制台上:
rootLogger.level=WARN rootLogger.appenderRef.stdout.ref=console如果想让日志同时写文件保留现场,可以在文件后面追加文件类型的 appender,或者直接使用hive.log.dir配置来指定日志输出目录:
property.hive.log.dir = /data/hive/logs5.3 一个容易忽略的坑
改完配置后再执行hive,如果控制台仍然打印 jar 包路径,说明配置文件没生效。原因多半是 bin/hive 脚本里指定的日志配置文件变量与文件名不一致。检查方法:
grep -rn "hive-log4j" $HIVE_HOME/bin/hivebin/hive脚本中通常有这样的逻辑:
if [ "$HIVE_LOG4J_FILE" = "" ]; then HIVE_LOG4J_FILE=hive-log4j2.properties fi如果你把模板复制成了hive-log4j.properties,但脚本期望的文件名是hive-log4j2.properties,那配置就不会被选中。所以复制粘贴时,最好参考脚本内默认的变量值来命名,避免白改一场。
注意:所有配置文件修改后,不需要重新编译任何代码,退出当前 Hive 会话重新执行
hive即可生效。但如果启动时已经被 bin 脚本注入环境变量,建议重启 shell 会话确保变量干净。
6. 从实际部署中总结的几条硬经验
部署和排障多了以后,我发现相比那行日志,更值得关注的是几个“环境级”的问题。它们不直接报错,但会让启动变得诡异,或者让日志配置莫名失效。
6.1 JDK 与 Hive 版本的匹配
很多 Hive 版本的启动异常并不是 Hive 本身的问题,而是 JDK 版本过高或过低。Hive 1.x 配合 JDK 8 是常见组合,Hive 3.x 也主要跑在 JDK 8 上,更高版本的 JDK 可能出现反射、模块化访问错误。检查方式:
java -version如果系统默认 JDK 版本不对,通过update-alternatives或手动设置JAVA_HOME来切换。
6.2 Classpath 覆盖顺序的坑
上文提到,日志配置是从 classpath 中的 jar 包加载的。如果你的服务器上还跑了其他大数据组件(例如 Spark、Presto、Flink),这些组件的 lib 目录如果在启动 Hive 时被引导加入了 classpath,并且里面也有相同名称的日志配置文件,Hive 可能加载到别的组件的配置,最终导致日志格式和输出路径完全不对劲。
排查这种问题时,可以打印实际的 classpath:
hive --version --verbose 2>&1 | grep -i classpath # 或 echo $CLASSPATH如果你发现/etc/hadoop/conf或其他第三方目录排在 Hive 的前面,并且干扰了配置,可以在$HIVE_HOME/bin/hive脚本里手动指定HIVEPATH或把自定义配置放到 Hive conf 目录,并确保在 classpath 中的位置靠前。
6.3 元数据库初始化之前不要急着连 Hive
如果你完全没有初始化元数据库,执行hive后大概率会在那行日志之后输出一长串异常,指向Unable to instantiate org.apache.hadoop.hive.metastore.HiveMetaStoreClient。很多人把它理解为“Hive 装坏了”,其实只是少了初始化动作。
我推荐的顺序是:
- 配置好
hive-site.xml里的元数据库连接信息。 - 将对应版本的 JDBC 驱动 jar 放到
$HIVE_HOME/lib。 - 执行
schematool -dbType mysql -initSchema。 - 初始化成功后,再执行
hive进入命令行。
先跑schematool还有一个好处:它能提前暴露元数据库连接问题,而且错误信息比较直接,不像 Hive CLI 那样层层包装。
6.4 用 beeline 做二次验证
如果 CLI 模式没问题,建议再用 beeline 连接 HiveServer2 验证一遍:
hive --service metastore & hive --service hiveserver2 & beeline -u jdbc:hive2://localhost:10000 -n adminbeeline 模式连接的是 HiveServer2,如果之前 CLI 模式只是单机进程,有很多权限、配置问题在 beeline 连接时会以更明确的形式暴露。配合hive --service metastore查看元数据日志,你就能比较清楚地把握 Hive 的状态。
7. 关于日志里出现“WARN”的自我安慰与真实警示
调整完日志级别后,控制台还会时不时出现 WARN 级别的内容,比如:
WARN: Hive-on-MR is deprecated in Hive 2.0 and may not be available in future versions.这是提醒 Hive-on-MR 模式逐渐退出历史舞台,建议改用 Spark 或 Tez 执行引擎。遇到这类 WARN,不用太紧张,它不影响当前的 SQL 执行,但如果你想追求更好的性能,可以在hive-site.xml中修改:
<property> <name>hive.execution.engine</name> <value>spark</value> </property>但要注意:切换执行引擎意味着 Hive 要能关联到对应的 Spark 安装,以及依赖包的版本必须匹配。如果没有搭好 Spark 环境,贸然切换会导致启动失败。所以在改执行引擎前,先确认 Spark 的相关配置本身是能用的。
实际上,我见过很多同事一看到控制台输出 WARN 就浑身难受,非要调到完全不输出才罢休。比较好的心态是:WARN 分两类,一类是 Hive 对将来行为的提前声明,另一类是真正影响运行的不兼容警告。前者可以不理会,后者必须搞清楚。怎么区分呢?最简单的方式:观察你的 SQL 执行完的结果。如果结果正确、执行性能正常、没有 Exception,那大部分 WARN 都可以暂时忽略;如果 SQL 执行中频繁报错、结果不对,再回头仔细排查 WARN 信息。
我个人踩过的坑是:为了“消灭 WARN”,盲目听从网上建议,把hive.metastore.schema.verification这样的安全检查关掉了,结果后续升级 Hive 时元数据库版本不一致,折腾了大半天。所以我的原则是:不影响功能的 WARN 不动,影响安全或升级的 WARN 优先排查。
8. 日志文件旋转与空间清理技巧
Hive 长时间运行后,日志文件可能持续增大。如果公司服务器/tmp分区不算大,很容易把磁盘写满。这虽然不是启动问题,但很多“突然起不来了”的故障都源于此。
默认情况下,Hive 的日志滚动配置也可以在hive-log4j2.properties里设置。例如用RollingFile按天或按大小滚动:
appender.rolling.type = RollingFile appender.rolling.fileName = ${sys:hive.log.dir}/hive.log appender.rolling.filePattern = ${sys:hive.log.dir}/hive.log.%d{yyyy-MM-dd}日志的目录也可以设置为固定目录,比如:
hive --hiveconf hive.log.dir=/data/logs/hive --hiveconf hive.root.logger=INFO,DRFA这样日志会写到/data/logs/hive而不是随机的/tmp目录,便于集中清理和排查。对于线上长期运行的 HiveServer2,这个参数建议在hive-site.xml里固定,而不是每次启动时手动传。
清理时不要简单地rm -rf hive.log,会导致文件句柄异常。常用的方法是通过日志滚动机制把旧日志切成.1、.2等后缀,然后定期删除符合规则的旧文件。或者执行:
# 先打断句柄占用,直接把文件置空 > $HIVE_HOME/logs/hive.log注意:直接在运行中的服务上删除日志文件,磁盘空间不一定会立刻释放。稳妥的做法是把大小超过阈值的日志文件归档到备份目录,然后清空原文件。
9. 修改配置后的验证姿势
最后分享一下每次改完配置后,我习惯走的验证步骤:
- 确认当前没有残余的 Hive 进程(比如后台挂着 metastore、hiveserver2)。
- 检查环境变量:
echo $HIVE_HOME、echo $JAVA_HOME。 - 用一个最小化命令验证日志配置是否生效:直接执行
hive --version。这个命令也会触发日志初始化,但是不会启动完整的 CLI,输出更少,适合确认日志配置是否正确加载。 - 查看日志文件目录是否生成,并观察文件内容:
find / -name "hive.log*" -mmin -2 2>/dev/null- 如果日志配置生效,控制台会少打印大量 INFO 级别的信息,格式化输出也会按照
hive-log4j2.properties中定义的 pattern 显示。
其实这一套验证流程不复杂,核心目的就是“在小范围内快速验证配置,不要把全部服务重启一遍才发现改错了”。
回顾我帮同事排查的多次问题,很多所谓“莫名其妙的日志”,本质上都是因为拿到了一个配置文件模板就开始改,却没有搞清楚正在修改的文件是否真的在生效路径上。那行Logging initialized using configuration in jar:file:/usr/local/hive/lib/hive-common...恰恰就是最好的“定位器”——它告诉你当前生效的配置来自哪里。顺着这个线索,你就能找到自己真正需要修改的文件,也能知道有没有改错地方。我在实际使用中还有个小习惯:每次改完日志配置后,会在/tmp之外固定一个日志目录,并把日志文件名加上日期后缀。这样即使一周后再回来看,也能快速判断出“当前这台机器上的 Hive 日志是在哪个目录下写的、写进了哪个文件”。对于多人维护的服务器来说,这个习惯能省掉很多“找不到日志”的沟通成本,建议你也试试。