Log日志框架的学习.正确使用日志的10个技巧

STM32日志log框架 采用CUBEMX+HAL将[原视频](https://www.bilibili.com/video/BV1EB4y1N7iD?p=15)中的日志框架运用至STM32F103C8T6中。能够自动打印日志所在的函数和行数 阅读详情

1)选择正确的Log开源框架
         在代码中为了知道程序的行为的状态, 我们一般会打印一条日志:

  1. log.info("Happy and carefree logging");  

        在所有的日志框架中, 我认为最好的是SLF4J. 比如在Log4J中我们会这样写:

  1. log.debug("Found " + records + " records matching filter: '" + filter + "'"); 

        而在SLF4J中我们会这样写:

  1. log.debug("Found {} records matching filter: '{}'", records, filter);  

        从可读性和系统效率来说, SLF4J( http://logback.qos.ch/ )比Log4J都要优秀(Log4J涉及到字符串连接和toString()方法的调用). 这里的{}带来的另一个好处, 我们在尽量不损失性能的情况, 不必为了不同的日志输出级别, 而加上类似isDebugEnabled()判断.

         SLF4J只是各种日志实现的一个接口抽象(facade), 而最佳的实现是Logback, 相对于Log4J的同门兄弟(皆出自Ceki Gülcü之手), 它在开源社区的活跃度更高.

         最后要推荐的是Perf4J( http://perf4j.codehaus.org/ ). 用一句话来概括就是:
         如果把log4j看做System.out.println()的话, 那么Perf4J就是System.currentTimeMillis()

         Perf4J可以帮助我们更方便的输出系统性能的日志信息. 然后借助其他报表工具将日志以图表的形式加以展现, 从而方便我们分析系统的性能瓶颈. 关于Perf4J的使用可以参考它的开发者指南( http://perf4j.codehaus.org/devguide.html).

下面是一份关于日志jar包依赖的pom.xml参考模板:

Xml代码   收藏代码
  1. <repositories>  
  2.     <repository>  
  3.         <id>Version99</id>  
  4.         <name>Version 99 Does Not Exist Maven repository</name>  
  5.         <layout>default</layout>  
  6.         <url>http://no-commons-logging.zapto.org/mvn2</url>  
  7.     </repository>  
  8. </repositories>  
  9.    
  10.    
  11. <dependency>  
  12.     <groupId>org.slf4j</groupId>  
  13.     <artifactId>slf4j-api</artifactId>  
  14.     <version>1.5.11</version>  
  15. </dependency>  
  16. <dependency>  
  17.     <groupId>ch.qos.logback</groupId>  
  18.     <artifactId>logback-classic</artifactId>  
  19.     <version>0.9.20</version>  
  20. </dependency>  
  21. <dependency>  
  22.     <groupId>org.slf4j</groupId>  
  23.     <artifactId>jul-to-slf4j</artifactId>  
  24.     <version>1.5.11</version>  
  25. </dependency>  
  26. <dependency>  
  27.     <groupId>org.slf4j</groupId>  
  28.     <artifactId>log4j-over-slf4j</artifactId>  
  29.     <version>1.5.11</version>  
  30. </dependency>  
  31. <dependency>  
  32.     <groupId>org.slf4j</groupId>  
  33.     <artifactId>jcl-over-slf4j</artifactId>  
  34.     <version>1.5.11</version>  
  35. </dependency>  
  36. <dependency>  
  37.     <groupId>commons-logging</groupId>  
  38.     <artifactId>commons-logging</artifactId>  
  39.     <version>99.0-does-not-exist</version>  
  40. </dependency>  


下面是测试代码:

  1. SLF4JBridgeHandler.install();  
  2.    
  3. org.apache.log4j.Logger.getLogger("A").info("Log4J");  
  4. java.util.logging.Logger.getLogger("B").info("java.util.logging");  
  5. org.apache.commons.logging.LogFactory.getLog("C").info("commons-logging");  
  6. org.slf4j.LoggerFactory.getLogger("D").info("Logback via SLF4J");  

上面的代码, 无论你采用哪个log框架输出日志, 底层采用的都是logback, 至于为什么, 可以看这里( http://www.slf4j.org/legacy.html), 另外这里为了在classpath里面不引入common-logging, 用了一个小技巧, 就是将依赖版本设置为99.0-does-not-exist, 关于这种用法的说明可以看这里( http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html), 不过log4j的作者认为最简单的做法就是直接去掉对common-logging的依赖, 相关内容可以看这里的说明( http://www.slf4j.org/faq.html#excludingJCL)   


2) 理解正确的日志输出级别
很多程序员都忽略了日志输出级别, 甚至不知道如何指定日志的输出级别. 相对于System.out来说, 日志框架有两个最大的优点就是可以指定输出类别(category)和级别(level). 对于日志输出级别来说, 下面是我们应该记住的一些原则:
ERROR:系统发生了严重的错误, 必须马上进行处理, 否则系统将无法继续运行. 比如, NPE, 数据库不可用等.

WARN:系统能继续运行, 但是必须引起关注. 对于存在的问题一般可以分为两类: 一种系统存在明显的问题(比如, 数据不可用), 另一种就是系统存在潜在的问题, 需要引起注意或者给出一些建议(比如, 系统运行在安全模式或者访问当前系统的账号存在安全隐患). 总之就是系统仍然可用, 但是最好进行检查和调整.

INFO:重要的业务逻辑处理完成. 在理想情况下, INFO的日志信息要能让高级用户和系统管理员理解, 并从日志信息中能知道系统当前的运行状态. 比如对于一个机票预订系统来说, 当一个用户完成一个机票预订操作之后, 提醒应该给出"谁预订了从A到B的机票". 另一个需要输出INFO信息的地方就是一个系统操作引起系统的状态发生了重大变化(比如数据库更新, 过多的系统请求).

DEBUG:主要给开发人员看, 下面会进一步谈到.

TRACE: 系统详细信息, 主要给开发人员用, 一般来说, 如果是线上系统的话, 可以认为是临时输出, 而且随时可以通过开关将其关闭. 有时候我们很难将DEBUG和TRACE区分开, 一般情况下, 如果是一个已经开发测试完成的系统, 再往系统中添加日志输出, 那么应该设为TRACE级别.

以上只是建议, 你也可以建立一套属于你自己的规则. 但是一套良好的日志系统, 应该首先是能根据情况快速灵活的调整日志内容的输出.

最后要提到的就是"臭名昭著"的is*Enabled()条件, 比如下面的写法:

  1. if(log.isDebugEnabled())  
  2.     log.debug("Place for your commercial");  

这种做法对性能的提高几乎微乎其微(前面在提到SLF4J的时候已经说明), 而且是一种过度优化的表现. 极少情况下需要这样写, 除非构造日志信息非常耗性能. 最后必须记住一点: 程序员应该专注于日志内容, 而将日志的输出的决定权交给日志框架去非处理.   

3) 你真的知道log输出的内容吗?
对于你输出的每一条log信息, 请仔细检查最终输出的内容是否存在问题, 其中最重要的就是避免NPE, 比如想下面这样:

  1. log.debug("Processing request with id: {}", request.getId());  

这里我们能否保证request不为null? 除了NPE之外, 有时候我们可能还需要考虑, 是否会导致OOM? 越界访问? 线程饥饿(log是同步的)? 延迟初始化异常? 日志打爆磁盘等等. 另外一个问题就是在日志中输出集合(collection), 有时候我们输出的集合内容可能是由Hibernate从数据库中取出来的, 比如下面这条日志信息:

  1. log.debug("Returning users: {}", users);  

这里最佳的处理方式是仅仅输出domain对象的id或者集合的大小(size), 而对Java来说, 不得不要吐槽几句, 要遍历访问集合中每一个元素的getId方法非常繁琐. 这一点Groovy就做的非常简单(users*.id), 不过我们可以借助Commons Beanutils工具包来帮我们简化:

  1. log.debug("Returning user ids: {}", collect(users, "id"));  

这里的collect方法的实现如下:

  1. public static Collection collect(Collection collection, String propertyName) {  
  2.     return CollectionUtils.collect(collection, new BeanToPropertyValueTransformer(propertyName));  
  3. }  

不过不幸的是, 在给Commons Beanutils提了一个patch(BEANUTILS-375 https://issues.apache.org/jira/browse/BEANUTILS-375)之后, 并没有被接受:(

最后是关于toString()方法. 为了让日志更容易理解, 最好为每一个类提供合适的toString()方法. 这里可以借助ToStringBuilder工具类. 另外一个就是关于数组和某些集合类型. 因为数组是使用的默认的toString方法. 而某些集合没有很好的实现toString方法. 对于数组我们可以使用JDK的Arrays.deepToString()方法( http://docs.oracle.com/javase/6/docs/api/java/util/Arrays.html#deepToString%28java.lang.Object[]%29).   

4) 小心日志的副作用

有时候日志或多或少的会影响系统的行为, 比如最近碰到的一个情况就是在某些条件下, Hibernate会抛出LazyInitializationException. 这是因为某些输出日志导致延迟初始化的集合在session建立时被加载. 而在某些场景下当提高日志输出级别时, 问题就会消失.

另一个副作用就是日志导致系统运行越来越慢. 比如不恰当的使用toString方法或者字符串连接, 使得系统出现性能问题, 曾经碰到的一个现象, 某个系统每隔15分钟重启一次, 这个主要是执行log输出出现线程饥饿导致, 一般情况下, 如果一个系统一小时内生成的日志有几百MB的时候, 就要小心了.

而如果因为日志输出本身的问题导致正常的业务逻辑被中断, 那就更严重了. 比如下面这种代码, 最好不要这样写:

  1. try {  
  2.     log.trace("Id=" + request.getUser().getId() + " accesses " + manager.getPage().getUrl().toString())  
  3. } catch(NullPointerException e) {}  

5) 日志信息应该简洁且可描述

一般, 每一条日志数据会包括描述和上下文两部分, 比如下面的日志:

  1. log.debug("Message processed");  
  2. log.debug(message.getJMSMessageID());  
  3.   
  4. log.debug("Message with id '{}' processed", message.getJMSMessageID());  

第一条只有描述, 第二条只有上下文, 第三条才算完整的一条日志, 还有下面这种日志:

  1. if(message instanceof TextMessage)  
  2.     //...  
  3. else  
  4.     log.warn("Unknown message type");  

在上面的警告日志中没有包含实际的message type, message id等信息, 只是表明程序有问题, 那么是什么导致了问题呢? 上下文是什么? 我们什么都不知道.

另外一个问题就是在日志中加上一个莫名其妙的内容, 即所谓的"magic log". 比如有些程序员会在日志中随手敲上"&&&!#"这样一串字符, 用来帮助他们定位.

一个日志文件中的内容应该易读, 清晰, 可描述. 而不要使用莫名其妙的字符, 日志要给出当前操作做了什么, 使用的什么数据. 好的日志应该被看成文档注释的一部分.

最后一点, 切记不要在日志中包含密码和个人隐私信息!

6) 正确的使用输出模式

log输出模式可以帮助我们在日志中增加一些清晰的上下文信息. 不过对添加的信息还是要多加小心. 比如说, 如果你是每小时输出一个文件, 这样你的日志文件名中已经包含了部分日期时间信息, 因此就没必要在日志中再包含这些信息. 另外在多线程环境下也不要在自己在日志中包含线程名称, 因为这个也可以在模式中配置.

根据我的经验, 一个理想的日志模式将包含下列信息:

  • 当前时间(不需要包含日志, 精确到毫秒)
  • 日志级别(如果你关心这个)
  • 线程名称
  • 简单的日志名(非全限定名的那种)
  • 日志描述信息

比如像下面这个logback配置:

  1. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">  
  2.     <encoder>  
  3.         <pattern>%d{HH:mm:ss.SSS} %-5level [%thread][%logger{0}] %m%n</pattern>  
  4.     </encoder>  
  5. </appender>  

千万不要在日志信息中包含下列内容:

  • 文件名
  • 类名(我想这个应该是全限定名吧)
  • 代码行号

还有下面这种写法也是要避免的:

  1. log.info("");  

因为程序员知道, 在日志模式中会指定行号, 因此他就可以根据日志输的行号出判断指定的方法是否被调用了(比如这里可能是authenticate()方法, 进而判断登录的用户已经经过了验证). 另外, 大家也要清楚一点, 在日志模式中指定类名, 方法名以及行号会带来严重的性能问题. 下面是我针对这个做的一个简单的测试, 配置如下: 

  1. <appender name="CLASS_INFO" class="ch.qos.logback.core.OutputStreamAppender">  
  2.     <encoder>  
  3.         <pattern>%d{HH:mm:ss.SSS} %-5level [%thread][%class{0}.%method\(\):%line][%logger{0}] %m%n</pattern>  
  4.     </encoder>  
  5.     <outputStream class="org.apache.commons.io.output.NullOutputStream"/>  
  6. </appender>  
  7. <appender name="NO_CLASS_INFO" class="ch.qos.logback.core.OutputStreamAppender">  
  8.     <encoder>  
  9.         <pattern>%d{HH:mm:ss.SSS} %-5level [%thread][LoggerTest.testClassInfo\(\):30][%logger{0}] %m%n</pattern>  
  10.     </encoder>  
  11.     <outputStream class="org.apache.commons.io.output.NullOutputStream"/>  
  12. </appender>  

下面是测试代码:

  1. import org.junit.Test;  
  2. import org.perf4j.StopWatch;  
  3. import org.perf4j.slf4j.Slf4JStopWatch;  
  4. import org.slf4j.Logger;  
  5. import org.slf4j.LoggerFactory;  
  6.    
  7. public class LoggerTest {  
  8.    
  9.     private static final Logger log = LoggerFactory.getLogger(LoggerTest.class);  
  10.     private static final Logger classInfoLog = LoggerFactory.getLogger("CLASS_INFO");  
  11.     private static final Logger noClassInfoLog = LoggerFactory.getLogger("NO_CLASS_INFO");  
  12.    
  13.     private static final int REPETITIONS = 15;  
  14.     private static final int COUNT = 100000;  
  15.    
  16.     @Test  
  17.     public void testClassInfo() throws Exception {  
  18.         for (int test = 0; test < REPETITIONS; ++test)  
  19.             testClassInfo(COUNT);  
  20.     }  
  21.    
  22.     private void testClassInfo(final int count) {  
  23.         StopWatch watch = new Slf4JStopWatch("Class info");  
  24.         for (int i = 0; i < count; ++i)  
  25.             classInfoLog.info("Example message");  
  26.         printResults(count, watch);  
  27.     }  
  28.    
  29.     @Test  
  30.     public void testNoClassInfo() throws Exception {  
  31.         for (int test = 0; test < REPETITIONS; ++test)  
  32.             testNoClassInfo(COUNT * 20);  
  33.     }  
  34.    
  35.     private void testNoClassInfo(final int count) {  
  36.         StopWatch watch = new Slf4JStopWatch("No class info");  
  37.         for (int i = 0; i < count; ++i)  
  38.             noClassInfoLog.info("Example message");  
  39.         printResults(count, watch);  
  40.     }  
  41.    
  42.     private void printResults(int count, StopWatch watch) {  
  43.         log.info("Test {} took {}ms (avg. {} ns/log)", new Object[]{  
  44.                 watch.getTag(),  
  45.                 watch.getElapsedTime(),  
  46.                 watch.getElapsedTime() * 1000 * 1000 / count});  
  47.     }  
  48.    
  49. }  

在上面的测试代码中, CLASS_INFO日志输出了1500万次, 而NO_CLASS_INFO输出了3亿次. 后者采用一个静态的文本来取代日志模式中的动态类信息.

从下面的对比图可以看出, 直接在日志模式中指定类名的日志比使用反射动态获取类名的要快13倍(平均输出每条日志耗时:8.8 vs 115微秒). 对于一个java程序员来说, 一条日志耗时100微秒是可以接受的. 这也就是说, 一个后台应用其中1%的时间消耗在了输出日志上. 因此我们有时候也需要权衡一下, 每秒100条日志输出是否是合理的.


最后要提到的是日志框架中比较高级的功能: Mapped Diagnostic Context. MDC( http://www.slf4j.org/api/org/slf4j/MDC.html )主要用来简化基于thread-local的map参数管理. 你可以往这个map中增加任何key-value内容, 然后在随后的日志输出中作为模式的一部分, 与当前线程一起输出. 


7) 给方法的输入输出加上日志

当我们在开发过程中发现了一个bug, 一般我们会采用debug的方式一步步的跟踪, 直到定位到最终的问题位置(如果能通过写一个失败的单元测试来暴露问题, 那就更帅了^_^). 但是如果实际情况不允许你debug时, 比如在客户的系统上几天前出现的bug. 如果你没有详细的日志的话, 你能找到问题的根源么?

如果你能根据一些简单的规则来输出每个方法的输入和输出(参数和返回值). 你基本上可以扔掉调试器了. 当然针对每一个方法加上日志必须是合理的: 访问外部资源(比如数据库), 阻塞, 等待等等, 这些地方可以考虑加上日志. 比如下面的代码:

  1. public String printDocument(Document doc, Mode mode) {  
  2.     log.debug("Entering printDocument(doc={}, mode={})", doc, mode);  
  3.     String id = //Lengthy printing operation  
  4.     log.debug("Leaving printDocument(): {}", id);  
  5.     return id;  
  6. }  


因为在方法调用前后加上了日志, 我们可以非常方便的发现代码的性能问题, 甚至找出死锁和线程饥饿(starvation)等严重问题:这种情况下都只有输入(entering)日志, 不会有输出(leaving)日志. 如果方法名类名使用得当, 那么输出的日志信息也将会非常赏心悦目. 因为你可以根据日志完整了解系统的运行情况, 因此分析问题的时候, 也将变得更加轻而易举. 为了减少日志代码, 也可以采用简单的AOP来做日志输出. 但是也要小心, 这种做法可能产生大量的日志.

对于这种日志, 一般采用DEBUG/TRACE级别. 当某些方法的调用非常频繁, 那么大量的日志输出将会影响到系统的性能, 此时我们可以提高相关类的日志级别或者干脆去掉日志输出. 不过一般情况下, 还是建议大家多输出一些日志. 另外也可以将日志看成一种单元测试. 输出的日志将像单元测试一样, 会覆盖到整个方法的执行过程. 没有日志的系统是不可想象的. 因此通过观察日志的输出将是我们了解系统是在正确的运行还是挂了的唯一方式.

8) 用日志检查外部系统

这里是针对前面的一种场景: 如果你的系统需要和其他系统通信, 那么需要考虑是否需要用日志记录这种交互. 一般情况下, 如果一个应用需要与多个系统进行集成, 那么诊断和分析问题将非常困难. 比如在最近的一个项目中, 由于我们在Apache CXF web服务上完整的记录了消息数据(包括SOAP和HTTP头信息), 使得我们在系统集成和测试阶段非常happy.

如果通过ESB的方式来多个系统进行集成, 那么可以在总线(bus)上使用日志来记录请求和响应. 这里可以参考Mules( http://www.mulesoft.org/)的<log-component/>( http://www.mulesoft.org/documentation/display/MULE2USER/Configuring+Components).

有时候与外部系统进行通信产生的大量日志可能让我们无法接受. 另一方面, 我们希望打开日志临时进行一下测试, 或者在系统出现问题的时候我们希望打开短暂的输出日志. 这样我们可以在输出日志和保证系统性能之间取得一个平衡. 这里我们需要借助日志日别. 比如像下面的这样做:

  1. Collection<Integer> requestIds = //...  
  2.    
  3. if(log.isDebugEnabled())  
  4.     log.debug("Processing ids: {}", requestIds);  
  5. else  
  6.     log.info("Processing ids size: {}", requestIds.size());  

在上面的代码中, 如果日志级别配置为DEBUG, 那么将但应所有的requestIds信息. 但是如果我们配置INFO级别, 那么只会输出requestId的数量. 不过就像我们前面提到的日志的副作用那样, 如果在INFO级别下requestIds为null将产生NullPointerException.

9) 正确输出异常日志

对于日志输出的第一条原则就是不要用日志输出异常, 而是让框架或者容器去处理. 记录异常本不应该是logger的工作. 而许多程序员都会用日志输出异常, 然后可能返回一个默认值(null, 0或者空字符串). 也可能在将异常包装一下再抛出. 比如像下面的代码这样:

  1. log.error("IO exception", e);  
  2. throw new MyCustomException(e);  

这样做的结果可能会导致异常信息打印两次(抛出的地方打一次, 捕获处理的地方再打一次).

但是有时候我们确实希望打印异常, 那么应该如何处理呢? 比如下面对NPE的处理:

  1. try {  
  2.     Integer x = null;  
  3.     ++x;  
  4. } catch (Exception e) {  
  5.     log.error(e);        //A  
  6.     log.error(e, e);        //B  
  7.     log.error("" + e);        //C  
  8.     log.error(e.toString());        //D  
  9.     log.error(e.getMessage());        //E  
  10.     log.error(null, e);        //F  
  11.     log.error("", e);        //G  
  12.     log.error("{}", e);        //H  
  13.     log.error("{}", e.getMessage());        //I  
  14.     log.error("Error reading configuration file: " + e);        //J  
  15.     log.error("Error reading configuration file: " + e.getMessage());        //K  
  16.     log.error("Error reading configuration file", e);        //L  
  17. }  

上面的代码, 正确输出异常信息的只有G和L, A和B甚至不能在SLF4J中编译通过, 其他的都会丢失异常堆栈信息或者打印了不恰当的信息. 这里只要记住一条, 在日志中输出异常信息, 第一个参数一定是一个字符串, 一般都是对问题的描述信息, 而不能是异常message(因为堆栈里面会有), 第二个参数才是具体的异常实例.

注: 对于远程调用类型的服务抛出的异常,一定要注意实现序列化, 否则在客户端将抛出NoClassDefFoundError异常, 而掩盖了真实的异常信息

10) 让日志易读,易解析

对日志感兴趣的可以分为两类:

  • 人(比如程序员)
  • 机器(系统管理员写的shell脚本)

日志的内容必须照顾到这两个群体. 引用鲍勃大叔"Clean Code( http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)"一书的话来说:日志应该像代码一样可读并且容易理解.

另一方面, 如果一个系统每小时要输出几百MB甚至上G的日志, 那么我们需要借助grep, sed以及awk来分析日志. 如果可能, 我们应该让日志尽可能的被人和机器理解. 比如, 避免格式化数字, 使用日志模式则可以方便用正则表达式进行识别. 如果无法兼顾, 那么可以将数据用两种格式输出, 比如像下面这样:

  1. log.debug("Request TTL set to: {} ({})", new Date(ttl), ttl);  
  2. // Request TTL set to: Wed Apr 28 20:14:12 CEST 2010 (1272478452437)  
  3.    
  4. final String duration = DurationFormatUtils.formatDurationWords(durationMillis, true, true);  
  5. log.info("Importing took: {}ms ({})", durationMillis, duration);  
  6. //Importing took: 123456789ms (1 day 10 hours 17 minutes 36 seconds)  

上面的日志, 既照顾了计算机("ms after 1970 epoch"这种时间格式), 又能更好的让人能理解("1 day 10 hours 17 minutes 36 seconds") . 另外, 这里顺便广告一下DurationFormatUtils( http://commons.apache.org/lang/api-release/org/apache/commons/lang/time/DateFormatUtils.html), 一个非常不错的工具:) 


原文链接:http://itindex.net/detail/48502-log-%E6%97%A5%E5%BF%97-%E6%A1%86%E6%9E%B6




使用Log10进行Langchain调用的日志管理和调试 Log10是什么?它是一个开源平台,支持无代理的LLM数据管理和应用开发。Log10专注于记录、调试和标记Langchain调用,帮助开发者更好地理解和优化AI模型的表现。 阅读详情

相关推荐

一文详解 C++ 日志框架

日志框架 - 一个经过专门设计的实用程序,用于规范应用程序的日志记录过程。

青春不老,奋斗不止! 8923

Log日志详解分析

这里需要了解到grep命令的几个参数含义:grep ‘name’ -A 10 显示匹配内容和后面的10行grep ‘name’ -B 10 显示匹配内容和前面的10行grep ‘name’ -C 10 显示匹配内容和前后面的10行tail -n 11命令则是将当前显示的10行内容以及匹配的那一行内容展示出现。

weixin_44203609的博客 6379

Win11系统下Abaqus 2025子程序开发环境一站式配置指南

本文详细介绍了在Win11系统下配置Abaqus 2025子程序开发环境的完整流程,包括VS2022和Intel oneAPI的安装与优化,以及Abaqus 2025的目录规划与环境联调。通过避坑指南和实战案例,帮助用户高效完成开发环境配置,避免常见错误,提升工作效率。

weixin_28717431的博客 333

如何使用log.debug()

log4j是一个开源的日志,共分为六个等级:LOG、DEBUG、INFO、WARN、ERROR、和FATAL。 DEBUG是其中的一种日志级别。一般我们用这个方法的时候都是这样的: if(log.isDebugEnabled()){ log.debug("debug!"); } 意思是:如果log4j的配置中开启debug级别日志,那么我们就打印输出debug日志,其在输出日志中会...

斜阳雨陌 5万+

遥感图像处理实战:DOTA数据集旋转框转YOLO格式全流程解析(附代码)

本文详细解析了将DOTA遥感数据集的旋转框标注转换为YOLO格式的全流程。针对遥感图像中物体朝向任意性的特点,文章重点阐述了将旋转框转换为外接水平框的数学原理与代码实现,并提供了批量转换脚本和可视化验证方法,帮助开发者高效处理数据,为基于YOLO的遥感目标检测项目奠定基础。

weixin_29082143的博客 398

钙钛矿太阳能电池稳定性测试:ISOS协议详解与标准化实践指南

在光伏器件研发中,稳定性测试是评估材料与器件长期可靠性的核心环节,其关键在于实现测试条件的标准化与数据的可比性。通过控制变量和模拟真实环境应力,标准化测试协议能够系统揭示材料衰减机理与器件失效模式,为技术迭代提供可靠依据。在钙钛矿太阳能电池领域,ISOS协议作为国际公认的测试框架,通过分级体系(如ISOS-L系列光照测试与ISOS-D系列暗态测试)和关键参数定义(如光源光谱匹配、MPP跟踪、结温控制),解决了不同实验室数据无法横向对比的痛点。该协议不仅规范了测试流程,还通过硬件系统搭建、自动化数据采集与归一

weixin_33600816的博客 319

log的info、error打印的是对象还是toString

都是调用对象的toString方法,如果没有重写toString,则调用顶级父类Object的toString, Hello hello=new hello(); LOGGER.info("hello:{}",hello); LOGGER.error("hello:{}",hello,e); System.out.println(hello); public String toString() { return getClass().getName() + "@" + Inte.

当我谈编程时我谈些什么 2763

扩展卡尔曼滤波 (EKF, Extended Kalman Filter) 算法详解及案例分析

扩展卡尔曼滤波是一种用于估计非线性动态系统状态的递归算法。其目标是通过结合非线性系统模型和观测数据,估计系统的最优状态。

闲人编程的博客 2385

Springboot日志相关

文章目录市场上的日志框架SLF4j同一个系统统一日志框架 市场上的日志框架 日志门面(日志抽象层) 日志实现 JCL(Jakarta Commons Logging)、SLF4j(Simple Logging Facade for Java)、jboss-logging Log4j、JUL(Java.util.logging)、Log4j2、Logback Log4j、Logback 、SLF4j都是出自同一个人的手,Logback时Log4j的升级版 建议使用SLF4j+Logbac

weixin_43119903的博客 474

YOLOv11涨点改进 | 首发全网创新、主干改进篇 | AAAI 2026 | PartialNet 主干让 YOLOv11 更加强大!引入部分通道机制 和 部分注意力卷积,全方面提升了模型的性能

本文提出PartialNet主干网络改进YOLOv11的方法,通过部分通道机制(PCM)和部分注意力卷积(PATConv)提升模型效率。PartialNet将特征图通道拆分并应用不同操作,显著减少计算量和内存占用。主要创新包括:1)部分通道机制实现计算优化;2)三种注意力模块(PATch/PATsp/PATsf)增强特征提取;3)动态部分卷积(DPConv)自适应调整计算复杂度。文章详细提供了代码实现和YOLOv11集成方法。

Ai缝合怪、助力小伙伴高效发论文 268

【XCharts插件】3-1、制作图表及代码生成图表(v1.0)

XCharts插件是一款基于UGUI的功能强大、易用、参数可配置的数据可视化图表插件。【Unity3D插件】XCharts插件完全教程《全网最全》-专栏文章目录OK,这是v1.0版本的XCharts插件的创建图表,代码生成图表的教程。可能有人要问了,既然都更新到v3.0了,还写啥v1.0教程呢。因为有些项目就是用v1.0去制作图表了,也没有太多的精力去更新插件,所以就一直用的v1.0,而且v1.0已经能实现大部分的图标样式了,所以,博主还是单独写了一篇文章来介绍v1.0的功能。

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 1293

log中打印异常信息的正确姿势

感谢https://blog.csdn.net/weixin_37364740/article/details/84621447 之前使用了浑身解数也没能把详细信息打印到log中: log.error(e.getMessage()); log.error(e.getStackTrace(...

chouqin3721的博客 6347

【YOLOv8改进[Conv]】使用OREPA助力V8更优秀

使用OREPA助力V8更优秀,降低训练成本,在不引入任何推理时间成本的情况下提高深度模型的性能。

静谧、淡雅 1917

java看log技巧_Log日志框架的学习五.正确使用日志的10个技巧

做一个苦逼的Java攻城师, 我们除了关心系统的架构这种high level的问题, 还需要了解一些语言的陷阱, 异常的处理, 以及日志的输出, 这些"鸡毛蒜皮"的细节. 这篇文章是JCP成员, Tomasz Nurkiewicz( http://nurkiewicz.blogspot.com/ )总结的10条如何正确使用日志的技巧(参见 原文). 跟那篇" java编程最差实践"一样...

weixin_31486261的博客 670

STM32开发 -- GPIO详解

之前有简单讲过GPIO工作模式和寄存器,还是有点没搞明白。这次需要全方位的看一下了。 一、数据手册 1、STM32F105RC引脚说明 可知:STM32F105RC 一共有4组IO口 一共16X3+3=51个IO GPIOA0~A15 GPIOB0~B15 GPIOC0~C15 GPIOD0~D2 2、程序定义 GPIO端口号 STM32F1...

不积跬步,无以至千里 5万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Log日志框架的学习五.正确使用日志的10个技巧

做一个苦逼的Java攻城师, 我们除了关心系统的架构这种high level的问题, 还需要了解一些语言的陷阱, 异常的处理, 以及日志的输出, 这些"鸡毛蒜皮"的细节. 这篇文章是JCP成员, Tomasz Nurkiewicz(http://nurkiewicz.blogspot.com/ )总结的10条如何正确使用日志的技巧(参见原文). 跟那篇"java编程最差实践"一样, 也是针对一...

zyjustin9的专栏 476

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

正确使用日志的10个技巧

做一个苦逼的Java攻城师, 我们除了关心系统的架构这种high level的问题, 还需要了解一些语言的陷阱, 异常的处理, 以及日志的输出, 这些"鸡毛蒜皮"的细节. 这篇文章是JCP成员, Tomasz Nurkiewicz( [url]http://nurkiewicz.blogspot.com/[/url] )总结的10条如何正确使用日志的技巧(参见[url=http://nurkiew...

疯狂的菠菜 4436

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

java看log技巧_正确使用日志的10个技巧(转)

做一个苦逼的Java攻城师, 我们除了关心系统的架构这种high level的问题, 还需要了解一些语言的陷阱, 异常的处理, 以及日志的输出, 这些"鸡毛蒜皮"的细节. 这篇文章是JCP成员, Tomasz Nurkiewicz(http://nurkiewicz.blogspot.com/)总结的10条如何正确使用日志的技巧(参见原文). 跟那篇"java编程最差实践"一样, 也是针对一些细节...

weixin_42416119的博客 993

Android日志工具Log的使用

Androi中的日志工具类是Log(android.util.Log),这个类提供了如下5种方法来供我们打印日志。 用于打印那些最为繁琐的、意义最小的日志信息。对应级别是verbose,是Android日志里面级别最低的一种。 Log.d() 用于打印一些调试信息,这些信息对你调试程序和分析问题应该是有帮助的。对应级别debug,比verbose高一级。 Log.i() 用于打印一些比较重要的数据,这些数据应该是你非常想看到的、可以帮助你分析用户行为数据

振华OPPO的博客世界 1万+

Android中日志打印 Log的使用

【Android Studio】日志打印 Log的使用

Errol's Blog 2万+

Android开发笔记之Log日志输出及其正确使用方法

博客介绍了Android日志输出的正确使用方法,以及为什么要使用Android日志输出

飞机火车巴雷特的博客 1万+

ESP32学习笔记——LOG日志库的使用

注:本文由CHATGPT辅助创作,未经验证,实际工程使用请仔细甄别。对于设置日志级别的几种方式(esp_log_level_set、CONFIG_LOG_DEFAULT_LEVEL、CONFIG_LOG_MAXIMUM_LEVEL、LOG_LOCAL_LEVEL )容易混淆,特此学习并记录。

River的博客 3702

js 日志框架—— si-log学习笔记

文章目录问题描述javascript 日志框架文章参考安装vue.config.js 在全局中引用定义打印级别配置说明 问题描述 工作中很少在JavaScript中写log日志,但是后台JAVA日志是很重要的一部分,是方便在运维发现问题,解决问题,那么前端是否有这方面的需要呢?带着这个疑问,我想找找类似的框架,方便后面工作中学习使用 javascript 日志框架 log4js log4java...

黄彪博客 1883

正确使用日志的十个技巧

转载至:原文链接 做一个苦逼的Java攻城师, 我们除了关心系统的架构这种high level的问题, 还需要了解一些语言的陷阱, 异常的处理, 以及日志的输出, 这些"鸡毛蒜皮"的细节. 这篇文章是JCP成员, Tomasz Nurkiewicz( 链接 )总结的10条如何正确使用日志的技巧(参见原文). 跟那篇"java编程最差实践"一样, 也是针对一些细节的, 因为日志是我们排查问题, 了解系统状况的重要线索. 我觉得对我们平常coding非常有借鉴意义. 所以转换成中文, 加深一下印象, 也作为自己

kungFuApe的博客 696

LOG日志详解

欢迎关注作者博客 简书传送门 文章目录Log的用途记录Log的基本原则日志的级别划分日志对性能的影响什么时候输出日志系统启动参数、环境变量异常捕获处函数获得期望之外的结果时关键操作日志输出的内容什么时候使用J2SE自带的日志典型问题分析该用日志的地方不用啰嗦重复、没有重点日志和异常处理的关系System.out方式的日志日志信息不明确忘记日志输出是多线程公用的多个参数的处理 参考文档 Log的用途...

阿祥小王子的博客 1万+
上一篇: Java异常
下一篇: Java编程思想8-多态
feir23
博客等级 码龄15年 3粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值