log_logisim - CSDN
精华内容
参与话题
  • log详解

    2018-09-27 11:31:02
    $args #请求中的参数值 $query_string #同 $args $arg_NAME #GET请求中NAME的值 $is_args #如果请求中有参数,值为"?",否则为空字符串 KaTeX parse error: Expected 'EOF', got '#' a...

    $args #请求中的参数值
    $query_string #同 $args
    $arg_NAME #GET请求中NAME的值
    $is_args #如果请求中有参数,值为"?",否则为空字符串
    KaTeX parse error: Expected 'EOF', got '#' at position 25: … #̲请求中的当前URI(不带请求参…args),可以不同于浏览器传递的requesturi使indexrequest_uri的值,它可以通过内部重定向,或者使用index指令进行修改,uri不包含主机名,如"/foo/bar.html"。
    $document_uri #同 $uri
    $document_root #当前请求的文档根目录或别名
    $host #优先级:HTTP请求行的主机名>“HOST"请求头字段>符合请求的服务器名.请求中的主机头字段,如果请求中的主机头不可用,则为服务器处理请求的服务器名称
    $hostname #主机名
    $https #如果开启了SSL安全模式,值为"on”,否则为空字符串。
    $binary_remote_addr #客户端地址的二进制形式,固定长度为4个字节
    $body_bytes_sent #传输给客户端的字节数,响应头不计算在内;这个变量和Apache的mod_log_config模块中的"%B"参数保持兼容
    $bytes_sent #传输给客户端的字节数
    $connection #TCP连接的序列号
    $connection_requests #TCP连接当前的请求数量
    $content_length #“Content-Length” 请求头字段
    $content_type #“Content-Type” 请求头字段
    $cookie_name #cookie名称
    $limit_rate #用于设置响应的速度限制
    $msec #当前的Unix时间戳
    $nginx_version #nginx版本
    $pid #工作进程的PID
    $pipe #如果请求来自管道通信,值为"p",否则为"."
    $proxy_protocol_addr #获取代理访问服务器的客户端地址,如果是直接访问,该值为空字符串
    $realpath_root #当前请求的文档根目录或别名的真实路径,会将所有符号连接转换为真实路径
    $remote_addr #客户端地址
    $remote_port #客户端端口
    $remote_user #用于HTTP基础认证服务的用户名
    $request #代表客户端的请求地址
    $request_body #客户端的请求主体:此变量可在location中使用,将请求主体通过proxy_pass,fastcgi_pass,uwsgi_pass和scgi_pass传递给下一级的代理服务器
    $request_body_file #将客户端请求主体保存在临时文件中。文件处理结束后,此文件需删除。如果需要之一开启此功能,需要设置client_body_in_file_only。如果将次文件传 递给后端的代理服务器,需要禁用request body,即设置proxy_pass_request_body off,fastcgi_pass_request_body off,uwsgi_pass_request_body off,or scgi_pass_request_body off
    $request_completion #如果请求成功,值为"OK",如果请求未完成或者请求不是一个范围请求的最后一部分,则为空
    $request_filename #当前连接请求的文件路径,由root或alias指令与URI请求生成
    $request_length #请求的长度 (包括请求的地址,http请求头和请求主体)
    $request_method #HTTP请求方法,通常为"GET"或"POST"
    $request_time #处理客户端请求使用的时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。
    KaTeX parse error: Expected 'EOF', got '#' at position 25: …ri #̲这个变量等于包含一些客户端请求…uri更改或重写URI,不包含主机名,例如:"/cnphp/test.php?arg=freemouse"
    $scheme #请求使用的Web协议,“http” 或 “https”
    $server_addr #服务器端地址,需要注意的是:为了避免访问linux系统内核,应将ip地址提前设置在配置文件中
    $server_name #服务器名
    $server_port #服务器端口
    $server_protocol #服务器的HTTP版本,通常为 “HTTP/1.0” 或 “HTTP/1.1”
    $status #HTTP响应代码
    $time_iso8601 #服务器时间的ISO 8610格式
    $time_local #服务器时间(LOG Format 格式)
    KaTeX parse error: Expected 'EOF', got '#' at position 25: …ME #̲客户端请求Header头中的c…cookie_"加上cookie名称的变量,该变量的值即为cookie名称的值
    KaTeX parse error: Expected 'EOF', got '#' at position 25: … #̲匹配任意请求头字段;变量名中的…http_accept_language即可
    $http_cookie
    $http_host #请求地址,即浏览器中你输入的地址(IP或域名)
    $http_referer #url跳转来源,用来记录从那个页面链接访问过来的
    $http_user_agent #用户终端浏览器等信息
    $http_x_forwarded_for
    KaTeX parse error: Expected 'EOF', got '#' at position 25: …_NAME #̲可以设置任意http响应头字段…sent_http_content_length即可
    $sent_http_cache_control
    $sent_http_connection
    $sent_http_content_type
    $sent_http_keep_alive
    $sent_http_last_modified
    $sent_http_location
    $sent_http_transfer_encoding

    个人github地址:https://github.com/luoyan321                     
     感兴趣的同学可以去提提意见

    展开全文
  • 浅谈LOG日志的写法

    万次阅读 多人点赞 2015-05-21 17:27:02
    1 Log的用途 不管是使用何种编程语言,日志输出几乎无处不再。总结起来,日志大致有以下几种用途: l 问题追踪:通过日志不仅仅包括我们程序的一些bug,也可以在安装配置时,通过日志可以发现问题。 l 状态监控...

    文章来源于公司的大牛

    1 Log的用途

    不管是使用何种编程语言,日志输出几乎无处不再。总结起来,日志大致有以下几种用途:

    l  问题追踪:通过日志不仅仅包括我们程序的一些bug,也可以在安装配置时,通过日志可以发现问题。

    l  状态监控:通过实时分析日志,可以监控系统的运行状态,做到早发现问题、早处理问题。

    l  安全审计:审计主要体现在安全上,通过对日志进行分析,可以发现是否存在非授权的操作。

    2 记录Log的基本原则

    2.1 日志的级别划分

    Java日志通常可以分为:error、warn、info、debug、trace五个级别。在J2SE中预定义的级别更多,分别为:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST。两者的对应大致如下:

    Log4j、slf4j

    J2se

    使用场景

    error

    SEVERE

    问题已经影响到软件的正常运行,并且软件不能自行恢复到正常的运行状态,此时需要输出该级别的错误日志。

    warn

    WARNING

    与业务处理相关的失败,此次失败不影响下次业务的执行,通常的结果为外部的输入不能获得期望的结果。

    info

    INFO

    系统运行期间的系统运行状态变化,或关键业务处理记录等用户或管理员在系统运行期间关注的一些信息。

    CONFIG

    系统配置、系统运行环境信息,有助于安装实施人员检查配置是否正确。

    debug

    FINE

    软件调试信息,开发人员使用该级别的日志发现程序运行中的一些问题,排除故障。

    FINER

    基本同上,但显示的信息更详尽。

    trace

    FINEST

    基本同上,但显示的信息更详尽。

    2.2 日志对性能影响

    不管是多么优秀的日志工具,在日志输出时总会对性能产生或多或少的影响,为了将影响降低到最低,有以下几个准则需要遵守:

    l  如何创建Logger实例:创建Logger实例有是否static的区别,在log4j的早期版本,一般要求使用static,而在高版本以及后来的slf4j中,该问题已经得到优化,获取(创建)logger实例的成本已经很低。所以我们要求:对于可以预见的多数情况下单例运行的class,可以不添加static前缀;对于可能是多例居多,尤其是需要频繁创建的class,我们要求要添加static前缀

    l  判断日志级别:

    n对于可以预见的会频繁产生的日志输出,比如for、while循环,定期执行的job等,建议先使用if对日志级别进行判断后再输出。

    n对于日志输出内容需要复杂的序列化,或输出的某些信息获取成本较高时,需要对日志级别进行判断。比如日志中需要输出用户名,而用户名需要在日志输出时从数据库获取,此时就需要先判断一下日志级别,看看是否有必要获取这些信息。

    l  优先使用参数,减少字符串拼接:使用参数的方式输出日志信息,有助于在性能和代码简洁之间取得平衡。当日志级别限制输出该日志时,参数内容将不会融合到最终输出中,减少了字符串的拼接,从而提升执行效率。

    2.3 什么时候输出日志

    日志并不是越多越详细就越好。在分析运行日志,查找问题时,我们经常遇到该出现的日志没有,无用的日志一大堆,或者有效的日志被大量无意义的日志信息淹没,查找起来非常困难。那么什么时候输出日志呢?以下列出了一些常见的需要输出日志的情况,而且日志的级别基本都是>=INFO,至于Debug级别日志的使用场景,本节没有专门列出,需要具体情况具体分析,但也是要追求“恰如其分”,不是越多越好。

    2.3.1 系统启动参数、环境变量

    系统启动的参数、配置、环境变量、System.Properties等信息对于软件的正常运行至关重要,这些信息的输出有助于安装配置人员通过日志快速定位问题,所以程序有必要在启动过程中把使用到的关键参数、变量在日志中输出出来。在输出时需要注意,不是一股脑的全部输出,而是将软件运行涉及到的配置信息输出出来。比如,如果软件对jvm的内存参数比较敏感,对最低配置有要求,那么就需要在日志中将-Xms -Xmx -XX:PermSize这几个参数的值输出出来。

    2.3.2 异常捕获处

    在捕获异常处输出日志,大家在基本都能做到,唯一需要注意的是怎么输出一个简单明了的日志信息。这在后面的问题问题中有进一步说明。

    2.3.3 函数获得期望之外的结果时

    一个函数,尤其是供外部系统或远程调用的函数,通常都会有一个期望的结果,但如果内部系统或输出参数发生错误时,函数将无法返回期望的正确结果,此时就需要记录日志,日志的基本通常是warn。需要特别说明的是,这里的期望之外的结果不是说没有返回就不需要记录日志了,也不是说返回false就需要记录日志。比如函数:isXXXXX(),无论返回true、false记录日志都不是必须的,但是如果系统内部无法判断应该返回true还是false时,就需要记录日志,并且日志的级别应该至少是warn。

    2.3.4 关键操作

    关键操作的日志一般是INFO级别,如果数量、频度很高,可以考虑使用DEBUG级别。以下是一些关键操作的举例,实际的关键操作肯定不止这么多。

    n  删除:删除一个文件、删除一组重要数据库记录……

    n  添加:和外系统交互时,收到了一个文件、收到了一个任务……

    n  处理:开始、结束一条任务……

    n  ……

    2.4 日志输出的内容

    l  ERROR:错误的简短描述,和该错误相关的关键参数,如果有异常,要有该异常的StackTrace。

    l  WARN:告警的简短描述,和该错误相关的关键参数,如果有异常,要有该异常的StackTrace。

    l  INFO:言简意赅地信息描述,如果有相关动态关键数据,要一并输出,比如相关ID、名称等。

    l  DEBUG:简单描述,相关数据,如果有异常,要有该异常的StackTrace。

    在日志相关数据输出的时要特别注意对敏感信息的保护,比如修改密码时,不能将密码输出到日志中。

    2.5 什么时候使用J2SE自带的日志

    我们通常使用slf4j或log4j这两个工具记录日志,那么还需要使用J2SE的日志框架吗?当然需要,在我们编写一些通用的工具类时,为了减少对第三方的jar包的依赖,首先要考虑使用java.util.logging。

    考虑到slf4j等日志框架提供了日志bridge工具,为java.util.logging提供了Handler,所以普通应用的开发过程中也可以考虑使用J2SE自有日志,这样不但可以减少项目的编译依赖,同时在应用实施时可以更灵活的选择日志的输出工具包。


    3 典型问题分析

    3.1 该用日志的地方不用


    上图对异常的处理直接使用e.printStackTrace()显然是有问题的,正确的做法是:要么通过日志方式输出错误信息,要么直接抛出异常,要么创建新的自定义异常抛出。

    另:对于静态工具类函数中的异常处理,最简单的方式就是不捕获、不记录日志,直接向上抛出,如果认为异常类型太多,或者意义不明确,可以抛出自定义异常类的实例。


    3.2 啰嗦重复、没有重点


    首先上面不应该有error级别的日志。

    其次在日志中直接输出e.toString(),为定位问题提供的信息太少。

    另外需要明确一点:日志系统是一个多线程公用的系统,在两行日志输出之间有可能会被插入其他线程的日志记录,不会按照我们的意愿顺序输出,后面有更典型的例子。

     

    最后,上面的日志可以简化为:

    logger.debug(“从properties中...{}...{}...”,name, value, e);

    logger.warn(“从properties中获取{}发生错误:{}”,name, e.toString());

     

    或者直接一句:

    logger.warn(“从properties中...{}...{}...”,name, value, e);

     

    或者更完美的:

     

    if(logger.isDebugEnabled()){

    logger.warn(“从properties中...{}...”, name, e);

    }else{

    logger.warn(“从properties中获取{}发生错误:{}”, name, e.toString());

    }


     

    3.3 日志和异常处理的关系


    首先上面的日志信息不够充分,级别定义不够恰当。

    另外,既然将异常捕获并记录的日志,就不应该重新将一个一模一样的异常再次抛出去了。如果将异常再次抛出,那在上层肯定还需要对该异常进行处理,并记录日志,这样就重复了。如果没有特别原因,此处不应该捕获异常。


    3.4 System.out方式的日志


    上面的日志形式十分随意,只适合临时的代码调试,不允许提交到正式的代码库中。

    对于临时调试日志,建议在日志的输出信息中添加一些特殊的连续字符,也可以用自己的名称、代号,这样可以在调试完毕后,提交代码之前,方便地找到所有临时代码,一并删除。

    3.5 日志信息不明确


    上面的“添加任务出错。。。”既没有记录任务id,也没有任务名称,软件部署后发现错误后,根据该日志记录不能确认哪一条任务错误,给进一步的分析原因带来困难。

    另外第二个红圈中的问题有:要使用参数;一行日志就可以了。

    还有一些其他共性的错误,就不多说了。


    3.6 忘记日志输出是多线程公用的


    如果有另外一个线程正在输出日志,上面的记录就会被打断,最终显示输出和预想的就会不一致。正确的做法应是将这些信息放到一行,如果需要换行可以考虑使用“\r”,如果内容较多,考虑增加if (logger.isDebugEnabled())进行判断。而第二个例子中的输出有System.out的习惯,相关内容应该一行完成。


    3.7 多个参数的处理


    对于多参的日志输出,可以考虑:

    public void debug(String format, Object... arguments);

    但是在使用多参时,会创建一个对象数组,也会有一定的消耗,为此,在对性能敏感的场景,可以增加对日志级别的判断。

     

    展开全文
  • c++对数函数 log() 操作

    万次阅读 多人点赞 2018-03-23 22:51:49
    另外log函数包括两种函数 一种以e为低的log()函数另一种为以10为底的log 10()函数;具体用法见下面这个小程序#include<iostream> #include<cmath> using namespace std; int main()...

    首先要知道exp()函数

    exp(n)值为e^n次方;

    另外log函数包括两种函数 一种以e为低的log()函数

    另一种为以10为底的log 10()函数;

    具体用法见下面这个小程序

    #include<iostream>
    #include<cmath>
    using namespace std;
    int main()
    {
    	double a=9,b=10;
    	cout<<log(a)<<endl;
    	cout<<log(exp(a))<<endl;
    	cout<<log10(b)<<endl;
    	return 0;
    }

    另外如果自定义以m为底,求log n的值

    需要double a=log(n)/log(m);

    举例如下:

    #include<iostream>
    #include<cmath>
    using namespace std;
    int main()
    {
    	double a=2,b=2;//以2为底的对数函数 
    	for(b=2;b<=16;b=b+2)
    	{
    	       cout<<"b="<<b<<"时,以2为底的对数函数="<<log(b)/log(a)<<endl;
    	}
    	return 0;
    }

    展开全文
  • log日志的使用

    万次阅读 2018-02-11 22:13:18
    做一个苦逼的Java攻城师, 我们除了关心系统的架构这种high level的问题, 还需要了解一些语言的陷阱, 异常的处理, 以及日志的输出, 这些"鸡毛蒜皮"的细节.... 跟那篇" java编程最差实践"...

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

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

    Java代码   收藏代码
    1. log.info("Happy and carefree logging");  



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

    Java代码   收藏代码
    1. log.debug("Found " + records + " records matching filter: '" + filter + "'");  



    而在SLF4J中我们会这样写:

    Java代码   收藏代码
    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>  



    下面是测试代码:

    Java代码   收藏代码
    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()条件, 比如下面的写法:

    Java代码   收藏代码
    1. if(log.isDebugEnabled())  
    2.     log.debug("Place for your commercial");  



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

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

    Java代码   收藏代码
    1. log.debug("Processing request with id: {}", request.getId());  



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

    Java代码   收藏代码
    1. log.debug("Returning users: {}", users);  



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

    Java代码   收藏代码
    1. log.debug("Returning user ids: {}", collect(users, "id"));  



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

    Java代码   收藏代码
    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的时候, 就要小心了. 

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

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




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

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

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



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

    Java代码   收藏代码
    1. if(message instanceof TextMessage)  
    2.     //...  
    3. else  
    4.     log.warn("Unknown message type");  




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

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

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

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

    6) 正确的使用输出模式 

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

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

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



    比如像下面这个logback配置:

    Xml代码   收藏代码
    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>  



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

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



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

    Java代码   收藏代码
    1. log.info("");  



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

    Xml代码   收藏代码
    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>  



    下面是测试代码:

    Java代码   收藏代码
    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. 如果你没有详细的日志的话, 你能找到问题的根源么? 

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

    Java代码   收藏代码
    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). 

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

    Java代码   收藏代码
    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或者空字符串). 也可能在将异常包装一下再抛出. 比如像下面的代码这样:

    Java代码   收藏代码
    1. log.error("IO exception", e);  
    2. throw new MyCustomException(e);  



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

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

    Java代码   收藏代码
    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来分析日志. 如果可能, 我们应该让日志尽可能的被人和机器理解. 比如, 避免格式化数字, 使用日志模式则可以方便用正则表达式进行识别. 如果无法兼顾, 那么可以将数据用两种格式输出, 比如像下面这样:

    Java代码   收藏代码
    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), 一个非常不错的工具:) 

    展开全文
  • Android开发学习之Log的使用

    万次阅读 2018-05-23 22:02:00
    Android开发学习之Log的使用     Log(android.util.log)是Android Studio中的日志工具类,熟练使用log会对你以后的Android开发之旅有很大的帮助。 * log类有五个方法,分别是(级别由低到高): 1.Log.v():...
  • Log 日志级别

    千次阅读 2018-08-03 19:46:50
    log4j具有5种正常级别(Level)。 1. static Level DEBUG : DEBUG Level指出细粒度信息事件对调试应用程序是非常有帮助的,一般认为比较重要的方法执行需要详细查看运行情况的则开启debug。 2. static Level INFO ...
  • 导包1.commons-logging.jar包 下载2.log4j.jar包 下载配置log4j1.在src根目录下创建一个log4j.properties文件。文件全部内容如下:log4j.rootLogger=CONSOLE,stdout,logfile #stdout控制器 log4j.appender....
  • android.util.Log常用的方法有以下5个:Log.v() Log.d() Log.i() Log.w() 以及 Log.e() 。根据首字母对应VERBOSE,DEBUG,INFO, WARN,ERROR。 1、Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v...
  • Log.d的用法

    万次阅读 2019-09-27 13:44:14
    Log是Android中的日志工具类,用来打印日志 Log.d(String tag, String msg) 对应Debug调试,输出颜色为蓝色 第一个参数tag:打印信息的标签 第二个参数msg:表示需要打印出来的信息 ...
  • 日志-apache的access_log与error_log

    万次阅读 2017-02-17 20:47:32
    1. access_log 访问日志 access_log为访问日志,记录所有对apache服务器进行请求的访问,它的位置和内容由CustomLog指令控制,LogFormat指令可以用来简化该日志的内容和格式  2. error_log 错误日志 error_log为错误...
  • Log4j ERROR: log4j:ERROR Could not find value for key log4j.appender.CONSOLE 解决方法: 在log4j.properties 加入以下配置 log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j....
  • Android使用adb抓完整Log

    万次阅读 2014-12-29 08:41:54
     最新项目里一直在做 Android RIL 方面的研究,非常最终项目还是未能解决通信底层模块的问题,但是在使用adb抓log上还是有一些收获的,这里记录一下。 Log分类  Android日志主要分为kernel、radio、event、...
  • 在用log4j时报了这样的错误: log4j:ERROR Could not find value for key log4j.appender.Console log4j:ERROR Could not instantiate appender named "Console". 解决方法:也就是第一行存在大小写问题,改成大写后...
  • git log如何退出

    万次阅读 多人点赞 2018-03-19 13:04:21
    英文状态下按Q
  • python获取指定目录下的所有指定后缀的文件名使用到的函数有: os.path.splitext():分离文件名与扩展名代码如下:#! /usr/bin/python # -*- coding: utf-8 -*-import os def getFileName(path): ...
  • Redis设置日志目录及loglevel

    万次阅读 2020-04-23 18:43:39
    在redis.conf中添加配置: loglevel notice#日志等级 logfile "/usr/redis/log/redis.log"#日志保存路径
  • log4j 配置日志输出(log4j.properties)

    万次阅读 多人点赞 2018-05-27 11:19:27
    2018年5月27日一、入门log4j实例1.1 下载解压log4j.jar
  • 简单log4j.properties配置示例

    万次阅读 2017-01-16 18:27:19
    ### set log levels ### log4j.rootLogger = INFO , console , debug , error ### console ### log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.app
  • c语言里面log函数怎么用

    万次阅读 2016-11-29 19:51:28
    2、函数名: log10 功 能: 对数函数log,以10为底 用 法: double log10(double x); 程序示例: #include #include int main(void) { double result; double x = 800.6872; result = log10(x); printf("The common...
  • git log退出方法

    万次阅读 多人点赞 2016-08-11 09:24:21
    英文状态下按Q
1 2 3 4 5 ... 20
收藏数 3,239,141
精华内容 1,295,656
关键字:

log