55最佳实践系列:Logging最佳实践

本文详细阐述了日志记录的最佳实践,包括日志格式、日志级别、日志输出频率、日志内容的详细度以及如何利用日志进行性能监控。通过实例分析了如何在不同场景下合理配置日志,以及如何通过日志解决线上问题和优化系统性能。
@郑昀汇总 创建日期:2012/10
 
#意识

ASAP (As Soon As Possible)原则

当线上出现诡异问题,
当你意识到靠现有的日志无法定位问题时,
当现象难以在你的开发环境重现时,
请不要执著于枯坐肉眼看代码,因为:一)不一定是你代码逻辑问题,可能是脏数据造成的,是老业务数据造成的,是分布式环境造成的,是其他子系统造成的;二)线上业务处于不稳定中,条件不允许问题定位无限期。
此时, 请立即在问题相关的调用链条上,一次性:
  • 在函数的入口和出口打印日志,同时打印输入、输出参数
  • catch(){……}里打印stacktrace,同时打印try块中关键变量的值(避免你发现某个异常是问题第一原因,却不知道是什么变量传入导致的)
  • 与其他模块交互的接口入口处打印输入参数,
即, 解决线上问题归根结底要靠log、a lot of log output!
在logging的力度上切勿犹犹豫豫,我们的工程师习惯于吝啬地找两个函数打印日志、打包部署一把、没看出来、再找几个函数打印、再部署、等着现象重现再观察、……,一来二去时间流逝,闲庭信步,从客服知道的小事故变成了全国皆知的大事故。
所以,再强调一遍: 在你的调用链条上,逐层调用的函数入口和出口都打印详细日志,不怕多只怕少,然后部署,等待现象重现,毕其功于一役!
 
我们要记录什么?
1)完成某项操作所需的时间

通过它可以跟踪为什么系统响应变慢或者太快

  • 处理完一个incoming request所耗费的时间,精确到毫秒
  • 执行数据库查询的时间
  • 从磁盘或者存储介质获取数据的时间
  • 等等
2)异常和堆栈跟踪
 
3)Sessions
知道一个问题是由谁引起的非常重要,因此在日志中使用会话标识符就变得必不可少。它可以简单到是一个 IP 地址或者是一个更复杂的 UUID,只要能区分不同的请求者就足够。
 
4)版本号
 
#工具
推荐的Java Logging框架
1)log4j:我们的配置是,log4j.appender.CONSOLE.layout.ConversionPattern= [%-d{yyyy-MM-dd HH\:mm\:ss.SSS}] [%p] [%c] [%m]%n;%p是日志优先级,%c是类目名,%m是输出信息,%n是回车换行符。
2)logback:log4j创建人Ceki Gülcü后续推出了SLF4J+logback。SLF4J(Simple Logging Facade for Java)作为commons-logging的替代,为各种logging APIs提供了一个简单的统一接口,使得最终用户能够在部署的时候配置所希望的logging APIs的实现。logback胜在性能,据称“某些关键操作,比如判定是否记录一条日志语句的操作,其性能得到了显著的提高。这个操作在logback 中需要3纳秒,而在 log4j 中则需要30纳 秒。 logback 创建记录器(logger)的速度也更快:13毫秒,而在 log4j 中需要23毫秒。更重要的是,它获取已存在的记录器只需94纳秒, 而 log4j 需要2234纳秒,时间减少到了1/23。跟java.util.logging(JUL)相比性能提高也是显著的”。
 
#配置
不要随便从网上找一个log4j的配置文件,请确认你理解每一个配置项
我们既然输出日志,自然期望在面对“ 这个问题是否从过去几天开始出现?”这样的疑问时,不至于发现你的rollingPolicy错误设置导致只能看到最近几小时的日志,或者日志发生时间没有精确到毫秒。
后面会张贴主站生产环境里的log4j配置。
 
#理念
可用grep抽取的日志:独立的行!
我们总是希望能用grep处理日志文件。这意味着: 一个日志条目永远不应该跨多行,除非你是堆栈打印
我们会用grep问日志什么问题呢?如:
  • 用手机号13910******下单的顾客最近三天内都来自于哪些IP?
  • 浏览地址是****?from=kfapi的顾客,但referral却是搜索引擎域名,最近三天有多少次?
  • 最近一周内,订单中心执行的所有事务,耗时最长的一次是多长时间?
  • ××××的接口是否真的于18:00发送了一个请求,我们收到的参数是什么?
确保你的日志能回答这样的问题。
 
不同关注领域写不同的日志文件
当访问和调用极其频繁,有时候你会发现把你的工程里什么信息都打印到一个日志文件里,会让你看得头昏脑胀。
最简单的示范就是Apache的访问日志和错误日志是分开的。
同样,你也可以把更加安静的事件(偶尔出现)与更加喧闹的事件分开存储。
如,对外的开放平台可以打印三种日志文件:connection log(建立链接和关闭链接,附带接入参数),message log(内部调用链),stacktrace log(异常的堆栈打印)。
 
#具体实现
至少精确到毫秒
日志必须包含时间戳,精确到至少毫秒级。
如果只是记录到秒级,我们曾明知代码因缺乏并发控制而产生BUG,却只能郁闷地看着精确到秒级的日志。
对Java来说,最好配置为: yyyy-MM-dd/HH:mm:ss.SSS
 
请尽可能打印明确的会话标识
日志条目里打印一个会话标识(A certain session identifier),当有许多并发请求打过来时,你就能基于此字段过滤 client 了。比如,我们司南日志会补充打印一个浏览器 cookies 里种下的 UUID 。
 
log4j的isDebugEnabled判断
如果打印信息是常量字符串或简单字符串拼接,那么不需要if ( log.isDebugEnabled() )。
如果你拼装的动作比较耗资源,请用if ( log.isDebugEnabled() )。
 
如有可能,请将性能数据标准化输出
这样更方便grep或hadoop做性能数据抽取和挖掘,从而能很轻松地转换为图形监控。
比如,订单中心的性能数据格式为: 树枝标志 当前节点起始时间 [当前节点持续时间, 当前节点自身消耗时间, 在父节点中所占的时间比例]
 
哪些位置需要部署性能检测点 
(1)访问数据库的dao层;
(2)访问外部资源的ext层;
(3)访问mq的方法;
(4)等等,一切不在你自己负责的工程掌握的部分(外部),或一切你认为自己工程的性能危险点,都需要加入性能监控日志。
 
 
#Sample
一个好的启动日志
https://i-blog.csdnimg.cn/blog_migrate/85b17013b71f54f69b761fd823755918.png
打印了应用的版本号,客户端的会话标识,关键步骤的执行时长。
 
一个好的堆栈跟踪日志
https://i-blog.csdnimg.cn/blog_migrate/ad0b355d56f0b0c1880bca8651eadd88.png
 
 
参考资源:

1) 55最佳实践系列:MongoDB最佳实践  (2012-12-15 15:48)
2) 55最佳实践系列:Logging最佳实践  (2012-12-15 16:43)
3)

赠图1枚:
61e40352jw1dz3xp4brtbj.jpg (440×440)
【无人机】基于改进粒子群算法的无人机路径规划研究[和遗传算法、粒子群算法进行比较](Matlab代码实现)内容概要:本文围绕基于改进粒子群算法的无人机路径规划展开研究,重点探讨了在复杂环境中利用改进粒子群算法(PSO)实现无人机三维路径规划的方法,并将其与遗传算法(GA)、标准粒子群算法等传统优化算法进行对比分析。研究内容涵盖路径规划的多目标优化、避障策略、航路点约束以及算法收敛性和寻优能力的评估,所有实验均通过Matlab代码实现,提供了完整的仿真验证流程。文章还提到了多种智能优化算法在无人机路径规划中的应用比较,突出了改进PSO在收敛速度和全局寻优方面的优势。; 适合人群:具备一定Matlab编程基础和优化算法知识的研究生、科研人员及从事无人机路径规划、智能优化算法研究的相关技术人员。; 使用场景及目标:①用于无人机在复杂地形或动态环境下的三维路径规划仿真研究;②比较不同智能优化算法(如PSO、GA、蚁群算法、RRT等)在路径规划中的性能差异;③为多目标优化问题提供算法选型和改进思路。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注算法的参数设置、适应度函数设计及路径约束处理方式,同时可参考文中提到的多种算法对比思路,拓展到其他智能优化算法的研究与改进中。
<think>首先,用户的问题是关于Python中的ValueError异常,具体是"unsupported format character 'U' at index"。用户提到了格式字符'U'不受支持。 我需要回顾系统级指令:所有行内数学表达式用$...$,独立公式用$$...$$,LaTeX语法正确,使用中文回答,生成相关问题,并在引用段落末尾添加引用标识。 用户的问题集中在Python错误上,所以我应该专注于解决这个ValueError。 参考站内引用: - 引用[1]: 关于日志配置错误,ValueError: unsupported format character ‘[‘ (0x5b) at index 29。错误代码是format字符串中有一个'['字符。 - 引用[2]: 关于数据库操作错误,ValueError: unsupported format character ‘Y‘ (0x59) at index 89。错误语句中使用了'%Y'。 这些引用表明,错误通常发生在字符串格式化时,使用了不支持的格式字符。 用户的具体错误是"unsupported format character 'U' at index",这很可能是在字符串格式化中使用了'%U',而Python的字符串格式化不支持'U'作为格式字符。 在Python中,字符串格式化使用%操作符或str.format()方法时,有特定的格式说明符。常见的如%s、%d、%f等。'U'不是标准格式字符,所以当它出现在格式字符串中时,会引发ValueError。 常见原因: - 在格式字符串中意外使用了'%U',可能是打字错误或误解。 - 用户可能想使用'%Y'或类似的东西,但写成了'%U'。 - 在SQL查询或日志格式化中,错误地包含了'%U'。 解决方案: 1. **检查格式字符串**:找出在代码中哪里使用了'%U'。错误消息通常会给出索引,比如"at index",这表示在字符串中的位置。用户应检查那个位置的字符。 2. **修正格式字符**:如果'%U'是打字错误,应该替换为正确的格式字符。例如,如果想表示年份,应该用'%Y';如果是其他,根据上下文调整。 3. **使用参数化查询**:对于数据库操作,建议使用参数化查询而不是字符串格式化,以避免SQL注入和格式错误。例如,使用execute()方法的参数。 4. **转义百分号**:如果字符串中需要字面意义的百分号,应该使用'%%'来转义。 5. **使用f-strings或str.format()**:在Python 3.6+中,推荐使用f-strings或str.format()方法,它们更安全、更灵活。 用户的问题中提到了'U',所以可能是一个具体的例子。我需要给出一般性建议。 回答结构要清晰:逐步解决问题。 1. 解释错误原因。 2. 提供诊断步骤。 3. 给出解决方案。 4. 生成相关问题。 使用中文回答。 生成相关问题:基于这个错误,生成相关问题,如如何避免类似错误、其他常见格式错误等。 引用标识:由于用户提供了引用,我应该引用它们,但用户说“上述引用仅供参考”,所以我需要自然地融入。 在回答中,如果提到引用,添加引用标识。 最后,添加相关问题。 大纲回答: - 介绍错误:解释ValueError: unsupported format character 'U' 的含义。 - 原因分析:为什么会出现这个错误。 - 解决方案步骤: - 步骤1: 定位错误位置。 - 步骤2: 检查格式字符串。 - 步骤3: 修正格式字符或转义。 - 步骤4: 使用更好的方法如参数化查询。 - 示例代码:给出修正的示例。 - 相关问题:生成2-3个相关问题。 确保回答真实可靠:基于Python知识。</think>### 解决Python ValueError: unsupported format character 'U' at index 错误 在Python编程中,`ValueError: unsupported format character 'U' at index` 是一个常见的字符串格式化错误。它通常发生在使用`%`操作符进行字符串格式化时,Python解释器检测到格式字符串中包含了不支持的格式字符(如`%U`)。在本回答中,我将逐步解释错误原因、诊断方法、解决方案,并提供代码示例。回答基于Python官方文档和常见实践,确保可靠[^1]。 #### 错误原因分析 Python的字符串格式化使用`%`操作符时,格式字符串(如`"..." % values`)必须遵循特定规则: - 格式字符(如`%s`、`%d`、`%f`)是预定义的,用于指定数据类型(如字符串、整数、浮点数)。 - 如果格式字符串中包含未定义的字符(如`%U`),Python会抛出`ValueError`,因为`U`不是标准格式字符(标准字符包括`Y`、`m`、`d`等用于日期,或`s`、`d`用于通用类型)。 - 常见触发场景: - 在日志配置、数据库查询或字符串拼接中,误用了`%U`(可能是打字错误,或意图使用`%Y`但写错)。 - 字符串中包含字面百分号`%`,但未正确转义(例如,应使用`%%`表示`%`)。 - 索引(如`at index`)指示错误位置,帮助快速定位问题。 例如,在引用[1]中,日志配置错误是因为格式字符串中的`%[`不被支持;类似地,引用[2]中的数据库错误是由于`%Y`被错误使用。您的错误`%U`同样属于格式字符无效问题[^1][^2]。 #### 逐步解决方案 以下步骤将帮助您诊断和修复此错误。请根据您的代码上下文调整。 1. **定位错误位置**: - 错误消息(如`unsupported format character 'U' at index`)中的`index`表示问题字符在字符串中的位置(从0开始计数)。例如,如果索引是`10`,则检查字符串的第11个字符。 - 在代码中搜索使用`%`操作符的字符串格式化语句。常见位置包括: - 日志配置(如`logging.basicConfig(format=...)`)。 - 数据库查询(如SQL语句拼接)。 - 通用字符串拼接(如`"Value: %U" % value`)。 - 使用Python调试器(如`pdb`)或打印语句输出格式字符串,确认其内容。 2. **检查并修正格式字符串**: - **修正无效字符**:如果`%U`是打字错误,替换为正确的格式字符。例如: - 如果意图表示年份,应使用`%Y`(如日期格式化)。 - 如果意图表示通用字符串,应使用`%s`。 - 如果`U`是字面字符(如URL的一部分),需转义为`%%U`。 - **转义百分号**:如果字符串中包含非格式化的百分号(如`%`),必须写成`%%`以避免误解析。 - **示例修正**: - 错误代码:`format_str = "User ID: %U" % user_id`(这里`%U`无效)。 - 修正后:`format_str = "User ID: %s" % user_id`(使用`%s`表示字符串)。 3. **使用更安全的字符串格式化方法**(推荐): - 避免`%`操作符,改用现代方法减少错误: - **f-strings**(Python 3.6+):直接在字符串中嵌入变量,如`f"User ID: {user_id}"`。 - **str.format()方法**:更灵活,如`"User ID: {}".format(user_id)`。 - **参数化查询(针对数据库)**:使用数据库API的参数化功能,防止SQL注入和格式错误。例如: - 错误方式:`sql = "SELECT * FROM users WHERE date = '%U'" % date`(易出错)。 - 正确方式(使用SQLite示例):`sql = "SELECT * FROM users WHERE date = ?"; cursor.execute(sql, (date,))`。 4. **完整代码示例**: 假设您的错误发生在日志配置或数据库查询中,以下是修正示例: ```python # 错误示例:引发 ValueError: unsupported format character 'U' at index # 假设索引指向了 '%U' bad_format = "Error: %U - something wrong" # %U 无效 # 修正方案1: 使用正确的格式字符(如%s) fixed_format_1 = "Error: %s - something wrong" % ("example") # 替换%U为%s # 修正方案2: 使用f-string(更安全) user_data = "example" fixed_format_2 = f"Error: {user_data} - something wrong" # 针对数据库查询的修正(引用[2]类似场景) # 错误:sql = "SELECT date_format(createtime, '%U') FROM user" # %U 无效 # 修正:使用%Y表示年份,并参数化查询 import sqlite3 conn = sqlite3.connect('test.db') cursor = conn.cursor() # 使用参数化避免格式化错误 sql = "SELECT date_format(createtime, '%Y-%m-%d') FROM user WHERE id = ?" # %Y 正确 cursor.execute(sql, (user_id,)) # 安全传递参数 ``` 5. **测试与验证**: - 运行修正后的代码,确保错误不再出现。 - 使用单元测试覆盖字符串格式化逻辑。 - 如果错误在第三方库中,检查库文档或更新到最新版本。 #### 预防措施 - **最佳实践**:在Python 3中,优先使用f-strings或`str.format()`,而非`%`操作符,以减少此类错误。 - **日志配置提示**:如引用[1]所示,确保日志格式字符串中所有`%`字符都有效或转义。 - **数据库操作提示**:如引用[2],避免在SQL字符串中直接嵌入变量,改用参数化查询[^1][^2]。 通过以上步骤,您应该能解决`ValueError: unsupported format character 'U' at index`错误。如果问题持续,请提供更多代码上下文以便进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值