丽江网站开发找千素网,建设路小学家校互动平台网站,考研培训班哪个机构比较好,上海建设工程招标网文章目录 概述选择合适的日志等级打印函数的入参、出参打印日志对象要做判空处理#xff0c;避免阻断流程推荐使用 Slf4j不用e.printStackTrace()打印日志低级别的日志输出#xff0c;必须进行日志级别开关判断不打印重复日志打印全部的异常信息#xff0c;方便定位问题核心… 文章目录 概述选择合适的日志等级打印函数的入参、出参打印日志对象要做判空处理避免阻断流程推荐使用 Slf4j不用e.printStackTrace()打印日志低级别的日志输出必须进行日志级别开关判断不打印重复日志打印全部的异常信息方便定位问题核心业务逻辑在每个分支首行都打印日志不打印无意义的日志不携带上下文、日志链路 id日志尽量使用英文 概述
记录日志是任何应用程序中至关重要的一部分它可以帮助开发人员了解应用程序的行为、调试问题以及监控系统的健康状态。 使用日志框架 选择一个成熟、广泛使用且功能丰富的日志框架如Log4j2、Logback或java.util.logging (JUL)。Log4j2和Logback是目前较为流行的选择它们提供了丰富的功能和灵活的配置选项。 使用SLF4J进行日志抽象 SLF4J (Simple Logging Facade for Java) 提供了一种日志框架的抽象可以在运行时绑定到不同的日志框架。这样可以使得你的应用程序代码与具体的日志框架解耦方便后期切换日志框架。 选择合适的日志级别 根据日志信息的重要性选择合适的日志级别常见的级别包括DEBUG、INFO、WARN、ERROR和FATAL。DEBUG用于调试信息INFO用于一般的信息记录WARN用于警告ERROR用于错误信息FATAL用于严重的致命错误。 记录有意义的信息 确保记录的日志信息具有可读性和实用性包括时间戳、线程信息、异常信息等。避免记录过于冗长或无用的信息以免日志文件变得过大。 避免直接打印日志 避免在代码中直接使用System.out.println()或System.err.println()等方式打印日志而是应该使用日志框架提供的API来记录日志。这样可以更好地控制日志的输出格式、级别和目的地。 使用合适的日志格式 配置日志格式以适应你的应用程序需求包括时间戳格式、日志级别、线程信息等。可以考虑使用JSON格式或者结构化日志格式以便后续的日志分析和处理。 配置日志输出 配置日志输出目的地可以输出到控制台、文件、数据库等不同的地方。针对不同的环境如开发、测试、生产可以配置不同的日志输出策略和目的地。 定期维护日志 定期清理和归档日志文件以防止日志文件过大影响系统性能和存储空间。 记录异常信息 在捕获和处理异常时确保记录足够的信息以便于后续排查问题。可以记录异常的堆栈跟踪、异常类型、异常发生的位置等信息。 开启日志异步记录 对于高并发的应用程序可以考虑开启日志的异步记录以减少对系统性能的影响。 选择合适的日志等级 Error: 严重的问题可能导致系统崩溃或者业务受到重大影响。应该关注系统的稳定性和安全性运维团队需要重点监控并及时处理。例如数据库连接失败、关键服务无法启动、未处理的异常等。 Warn: 不会导致系统崩溃但可能会影响系统的正常运行。开发人员需要关注可能需要进一步调查和处理以防问题进一步恶化。例如潜在的性能问题、不符合预期的业务流程、资源使用超出预期等。 Info: 关键的系统运行信息用于保留系统运行的关键指标。记录重要的业务流程、函数的入参和出参、关键操作的执行情况等。这些信息可以帮助开发人员了解系统的运行情况以及后续的故障排查和性能优化。例如用户登录、订单创建、支付成功等重要操作的记录。 Debug: 用于开发和调试阶段记录开发人员在关键处理步骤中的变化情况便于快速定位问题。包含详细的调试信息如对象数据的变化、条件语句的执行结果等。在生产环境中应该关闭或者限制输出避免影响系统性能和日志文件大小。例如方法的参数值、中间变量的取值、特定条件下的执行路径等。
根据具体情况选择合适的日志级别以确保日志既能够提供足够的信息用于故障排查和性能分析又不会造成过多的日志噪音。 打印函数的入参、出参
在日志记录过程中关键是确保只记录关键有效的信息而不是把所有信息都记录下来。过多的无效日志会导致日志文件变得庞大增加了存储和维护的成本也会增加后续日志分析的难度。
因此有效日志应该是日志记录中的杀手锏它们提供了足够的信息用于故障排查、性能分析和业务监控而不会造成不必要的负担。
举个例子
public String doBiz(Request req, Integer id){// 记录函数入参log.debug(Entering GetName method. Request: {}, req);// 在打印日志时避免直接打印敏感信息如 uid、traceId可以考虑在日志配置中做处理或者在代码中做脱敏处理// 执行业务逻辑String name artisan;// 记录函数出参及执行时间log.debug(Exiting GetName method. Result: {}, Execution time: {}ms, name, System.currentTimeMillis());return name;
}有效日志-------》比如函数的入口处打印入参还包括用户唯一标识 uid、链路标识 traceId 等。函数出口打印返回值及时间等 函数入参记录 使用log.debug()记录函数的入参时将整个请求对象req作为参数传入确保了记录了函数的所有入参信息。 函数出参及执行时间记录 使用log.debug()记录函数的出参时打印了方法的返回值name和执行时间。打印了方法的执行时间以便于后续性能分析。
这样做的好处是保留了关键有效的日志信息同时避免了记录过多的日志导致日志文件过大。 打印日志对象要做判空处理避免阻断流程
通过在日志记录之前进行null检查可以避免空指针异常的发生同时在日志中记录了警告信息表明接收到了空的book对象。这样既确保了程序的健壮性又不会因为一行简单的日志记录而引发异常。
为了避免这种情况可以先检查对象是否为null然后再进行日志记录。
public void doSome(Book book){if (book ! null) {log.info(do do and print log: {}, book.getName());} else {log.warn(Received null book object.);}// do something......
}推荐使用 Slf4j
Slf4j是一种使用门面模式的日志框架它提供了统一的API接口可以在不修改代码的情况下灵活地切换底层的日志实现。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;private static final Logger logger LoggerFactory.getLogger(Artisan.class);LoggerFactory.getLogger(JavaPub.class)会返回一个与Artisan类相关联的Logger对象通过这个Logger对象我们可以记录日志。
通过这种方式我们可以利用Slf4j的门面模式来记录日志而无需关心具体的日志实现从而实现了日志框架的解耦。 不用e.printStackTrace()打印日志
在日志记录中应避免使用e.printStackTrace()来打印异常信息。这种方式打印的日志包含了完整的堆栈信息使得日志不够规整增加了定位问题的难度。同时如果使用ELK等日志分析工具处理这种格式的日志也会非常困难。
看个错误的例子
public void doBiz(){try{// 业务代码...} catch (Exception e){e.printStackTrace();}
}另外e.printStackTrace()产生的字符串记录的是堆栈信息如果信息过长过多会导致字符串常量池所在的内存块溢出从而使系统请求被阻塞。
推荐做法
public void doBiz(){try{// 业务逻辑...} catch (Exception e){log.error(程序异常 failed, e);}
}建议使用日志框架提供的相应方法来记录异常信息如log.error(程序异常 failed, e)。这样可以将异常信息记录在日志中方便查看和分析同时保持日志的规整性和可读性。 低级别的日志输出必须进行日志级别开关判断
在低级别的日志输出如trace、debug中必须进行日志级别开关的判断以避免不必要的资源浪费。这样的开关判断逻辑通常放在日志工具类中。
示例代码
public void doSomething(){User user new User(1, aritsan, log practice);if (logger.isDebugEnabled()) {logger.debug(print debug log. name is {}, user.getName());}
}我们通过判断日志级别是否为DEBUG来决定是否记录DEBUG级别的日志。这样做可以避免在日志级别不符合条件时执行字符串拼接操作或者执行对象的toString()方法从而避免不必要的资源浪费。
public void doSth(){String name artisan;logger.trace(print debug log name);logger.debug(print debug log name);logger.info(print info log name);// 业务逻辑...
}这个栗子中没有进行日志级别开关的判断即使日志级别为WARN时仍然会执行字符串拼接操作可能会浪费系统资源。因此建议在低级别的日志输出中加上日志级别开关判断以提高系统的性能和效率。 不打印重复日志
在嵌套逻辑代码中重复打印日志会增加系统资源消耗因此应避免这种情况的发生。
对于重复的日志可以直接删除或者将其级别设置为debug这样就不会在生产环境中打印出这些冗余的信息。
举个例子
public void doSomething(String s){log.info(do something and print log: {}, s);doSubSomething(s);
}private void doSubSomething(String s){log.debug(do sub something and print log: {}, s);// 写点业务逻辑...
}这样做可以减少不必要的日志输出提高系统的性能和效率。 打印全部的异常信息方便定位问题
在异常处理中应该打印完整的异常信息以便更好地定位问题。
看个错误的示例
public void doBiz(){try{// 业务逻辑...} catch (Exception e){log.error(发生了一个异常);}
}反例中的代码没有打印具体的异常信息e这样就无法准确地了解到底发生了什么类型的异常。
建议修改为
public void doSth(){try{// 业务逻辑...} catch (Exception e){log.error(发生了一个异常, e);}
}这样做可以打印出完整的异常信息包括异常类型、异常消息和堆栈信息有助于更快地定位和解决问题。 核心业务逻辑在每个分支首行都打印日志
在编写核心业务逻辑代码时在行首打印日志可以帮助快速排查和定位异常。
public void doBiz(User user){if(user.isVip()){log.info(User is a JavaPub member. Id: {}. Starting processing for member logic., user.getUserId());// TODO: Member logic}else{log.info(User is not a member. Id: {}. Starting processing for non-member logic., user.getUserId());// TODO: Non-member logic}
}通过这样的日志记录方式可以清晰地了解到程序的执行流程便于后续的排查和定位异常。 不打印无意义的日志不携带上下文、日志链路 id
在编写日志时确保日志携带有意义的业务信息这样可以帮助快速定位问题原因。
看个反例 日志并没有携带任何业务信息因此对故障排查没有太大的帮助。
public void doBiz(Request req, User user){log.info(do something and print log. );// TODO 业务逻辑...
}正例中的日志携带了业务相关的信息如用户ID和日志链路ID这样可以在出现异常时更容易地定位到具体的业务场景有利于快速解决问题。
public void doBiz(Request req, User user){log.info(do something and print log, id{}, trace_id{}, user.getId(), req.getTraceId());// TODO 业务逻辑...
}通过在日志中打印关键信息可以让程序运行过程更加透明有利于快速定位问题提高系统的可维护性和可靠性。 日志尽量使用英文
建议在打印日志时尽量使用英文以避免中文编码与终端不一致导致打印出现乱码从而影响排查故障的效率。
比如
log.info(Start processing...);
log.debug(Processing data: {}, data);
log.error(An error occurred while processing data: {}, error);通过使用英文打印日志可以确保日志在不同环境中都能正常显示有利于排查和解决问题。