首页 > 其他分享 >记一次服务日志乱码异常的排查过程

记一次服务日志乱码异常的排查过程

时间:2022-12-20 23:45:30浏览次数:40  
标签:编码 UTF LC CTYPE 乱码 排查 设置 机器 日志

记一次服务日志乱码异常的排查过程

问题浮现

某天工作中,突然接收到了用户对于内部测试环境上java进程服务日志乱码异常的反馈,经过查看相关文件,发现中文字体无法正常显示,严重影响到了用户的日常工作,因此开始处理该问题。

图片

排查过程

通过查看Java进程的详细信息,发现编码设置成了ASCII 编码,【图中的ANS_X3.4. 是JDK对ASCII的别名】

图片

但是服务包含的所有文件均编码正常,为正常的UTF-8。因此并不是服务本身导致的问题,

由于Java服务的编码发生异常,并且转转内部的Java服务启动时并没有指定编码,从官方文档上入手,确认在默认情况下的JVM对编码的处理,基于文档中对于编码参数解释,服务的编码将会读取系统的配置进而确定服务的编码配置. 因此我怀疑可能是这台机器上的配置被进行了修改,是个例问题,首先,我先设置了服务编码配置 -Dfile.encoding=UTF-8,指定编码为UTF-8后,服务的日志输出立刻恢复了正常, 后续,在业务使用的低峰期时,去掉编码配置,对这台服务器进行了重启。重启后观察服务重启的状态,发现服务状态依旧正常,日志输出正常,便认为这次的问题已经解决。

但是仅仅几天后,用户又开始频繁的反馈,再次出现了编码异常的问题,并且异常的机器较之上次,又有新机器出现了这种异常情况,这就确认了遇到的编码异常问题并不是简单的指定编码和重启机器就能彻底有效的解决该问题。

深入排查

在转转内部的测试环境中,我们分别使用KVM虚拟机和Docker容器来作为测试机器使用,而Docker容器未出现该问题,出现问题的机器均是KVM机器。在KVM虚拟机上我们都部署了一个自研的agent,借助该agent,我们可以打印出进程在运行中使用的环境变量,因此我们可以拿到机器上的环境变量,通过针对问题机器和正常机器的对比。

图片

发现LC_CTYPE的设置和LANG的配置存在差异,为了定位问题的源头,我将问题机器上的LC_CTYPE 环境变量硬编码为 en_US.UTF-8, 随后再次进行测试,发现问题机器的编码恢复正常,日志输出正常。可怀疑问题的源头是由机器上的LC_CTYPE发生了错误设置,进而导致Java服务设置默认编码时错误的设置为ASCII。Linux上的LC_CTYPE变量的作用是会去更改文字,符号的编码,将其变成我们设置的编码值。但是异常机器上的 LC_CTYPE 的编码是怎么被设置成UTF-8呢?

在针对MacOS上常用的开源终端软件ITerm2的一个Issue的问题讨论中:在ssh连接中的LC_CTYPE错误设置, 发现了关键的信息,Issue主要讨论的是 ssh指令登录服务器的时候,默认会将本地的环境变量传递到远程机器,尝试同步两方的环境变量设置,至此,我们可以确认问题的根源是由于环境变量的错误同步导致的。

其中客户端传递哪些变量取决于本地的ssh_config文件中的SendEnv配置,而远程机器接收哪些变量取决于服务端sshd_config文件的AcceptEnv配置,详细信息可以查看文档以了解。

问题处理

当我们了解了问题的根源,对于问题的解决也十分的清晰,最终的问题解决方案应该从三个方向出发,分别是 客户端不发送错误的编码 & 服务端不接收错误的编码 & 执行兜底,避免异常情况

  1. 客户端不发送错误的编码:
    1. 设置 iterm2,不让iterm2 给远程主机发送 UTF-8 编码尝试进行同步图片

      图片

    2. 直接修改客户端(本地)的ssh_config配置,如果你有本地的管理员权限,可以修改/etc/ssh/路径下ssh_config文件中的SendEnv,如果你不想对远端机器发送任何变量同步,可以直接注释该行。\

      图片

  2. 不接收错误的编码:
    1. 如果你有远程机器的管理员权限,可以修改远程机器/etc/ssh/路径下的sshd_config文件,去掉你不想接收的变量

      图片

  3. 升级agent,在agent侧在服务启动前兜底处理,检查是否存在"LC_CTYPE":"UTF-8"的设置,如果存在,修改为"en_US.UTF-8",让JVM读取到正确的环境变量。

抽丝剥茧

即使Iterm2错误设置导致了远程机器上的LC_CTYPE 变成了UTF-8,和Java服务的编码变成ASCII有什么因果关系吗?让我们看一下Linux官方文档。

在关于 locale 的官方文档中, 有这么一句话, 意思为如果程序设置 locale 环境变量,将会调用 setlocale()函数,为其传递对应的正确参数。

图片

继续深挖,让我们看一下setlocale() 函数的细节,这里我们能够发现两个重点

  1. 在程序启动期间,会在所有的用户代码执行之前,默认执行一次 setlocale(LC_ALL, "C")

    图片

  2. setlocale 函数存在于 locale.h 头文件里,接收两个参数,第一个参数是用于标识是哪个lcoale环境变量 例如“LC_ALL / LC_CTYPE等等”,第二个参数是变量真实的值。

当我们错误的从本地向远程机器发出同步环境变量LC_CTYPE命令,setlocale函数尝试为LC_CTYPE设置UTF-8的值,但是发现UTF-8并不是一个合法的期望值。这是因为LC_COLLATE, LC_CTYPE, LC_MESSAGES, LC_MONETARY, LC_NUMERIC , LC_TIME 这些变量对于设置的value 是有格式要求的!

具体格式为:[language[_territory][.codeset][@modifier]],其中@modifier是一个可选的附加字段。例如en_US.UTF-8就是正常的,符合要求的格式。

  1. 当我们设置了一个不合法的变量,基于linux中的 setlocale 文档, 我们可以发现如下结果:

    图片

当我们调用setlocale函数时,传入的UTF-8是一个不合法的值,这时对于该项环境变量的操作不作改动,函数返回null。这时LC_CTYPE便会沿用我们之前设置的setlocale(LC_ALL, "C");LC_CTYPE的值被设置成为了 “C”语言环境。在这个语言环境下LC_CTYPE等价于 7位的ASCII码,只支持 128个字符。因此结合之前LC_CTYPE的作用, 它将会更改文字,符号,将其变为你设置的编码。这种情况下Java服务的编码被错误的设置为了ASCII。

个人收获

在工作过程中,我们总会遇到各种各样的问题,但更重要的是遇到问题时,不要将就逃避,当问题发生了,一定要抱着严谨的态度去彻底解决,并对于遇到的问题也多进行总结。

作者:刘希宁

转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。

关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

来源:https://juejin.cn/post/7141374842019774495

标签:编码,UTF,LC,CTYPE,乱码,排查,设置,机器,日志
From: https://www.cnblogs.com/konglxblog/p/16995363.html

相关文章

  • 502问题怎么排查?
    本文为掘金社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!刚工作那会,有一次,上游调用我服务的老哥说,你的服务报"502错误了,快去看看是为什么吧"。当时那......
  • spring mvc+ELK从头开始搭建日志平台
    springmvc+ELK从头开始搭建日志平台最近由于之前协助前公司做了点力所能及的事情,居然收到了一份贵重的端午礼物,是给我女儿的一个乐高积木,整个有7大包物件,我花了接近一天的......
  • 日志切割 logrotate
    TOCSre@iddcipl01043:~$cat/etc/.d/nginx/var/log/nginx/*.log{dailymissingokrotate14compressdelaycompres......
  • 微课程 | 第十八课《慢查询日志》
    微课程|第十八课《慢查询日志》今天是微课程《DBLE快速上手》的最后一课:慢查询日志。我们DBLE的慢查询日志兼容这两个工具:MySQL的mysqldumpslowPercona的pt-query......
  • 分布式 | 从 dble 日志分析到 MySQL 源码学习
    作者:袁琳铸爱可生DBLE团队开发成员,主要负责DBLE需求开发,故障排查和社区问题解答。背景在客户的生产环境中,dble.log时常出现nohandler日志。虽然没有影响客户业务的......
  • 故障分析 | MySQL 无监听端口故障排查
    作者:王向爱可生DBA团队成员,负责公司DMP产品的运维和客户MySQL问题的处理。擅长数据库故障处理。对数据库技术和python有着浓厚的兴趣。前言最近解决了一个比较基础......
  • 项目中引进这玩意,排查日志又快又准!
    大家好,我是三友~~背景随着微服务盛行,很多公司都把系统按照业务边界拆成了很多微服务,在排错查日志的时候,因为业务链路贯穿着很多微服务节点,导致定位某个请求的日志以及上......
  • 日志切割: logrotate、python、shell实现
    对于Linux系统安全来说,日志文件是极其重要的工具。不知为何,我发现很多运维同学的服务器上都运行着一些诸如每天切分Nginx日志之类的CRON脚本,大家似乎遗忘了Logrotate,争相发......
  • Java 如何在日志中优雅的打印 Exception
    一、使用log库打印使用log库如slf4j@Slf4jpublicclassMyDemo{publicvoiddemo(){try{inta=10/0;}catch......
  • 利用log4j+mongodb实现分布式系统中日志统
    背景   在分布式系统当中,我们有各种各样的WebService,这些服务可能分别部署在不同的服务器上,并且有各自的日志输出。为了方便对这些日志进行统一管理和分析。我们可以将......