编程思想之日志记录
什么是log?
相信你一定用日记写过点滴心事,或是用空间、微信、微博刷着动态,记录你每天的喜怒哀乐!在程序中也有一种类似的东西,记录着他主人(应用程序)每天的行踪,他叫日志(log)。日记——是人类生活的记事本,日志(log)——是程序运行状况的记事本。
顾名思义,日志(log,后面均以log称之)就是用来记录程序每天的运行状况的,比如程序出现异常的情况,或是某个关键点,功某个重要的数据或交易等。这里的每天不是说每天一记,可以是伴随着程序运行的始终,只要程序在运行着就一直在记录,一般一天的日志记录在同一个文件中,每天的日志以不同的文件名分开。
下面让我们看看log在C++、Java、JavaScript中的记录方式。
给程序加log
C++中的log:
C++中有很多记录log的库,比较常用的有log4cpp(log4cxx)、Google Glog 。下面主要讲一个log4cpp(log4cxx)的用法。
log4cxx是apache软件基金会的一个开源库,是由log4j移植过来的跨平台的日志处理跟踪项目。从官网下载的log4cxx只有源码,需要自己编译。
log4cxx的编译
1.下载apache-log4cxx-0.10.0.zip、apr-1.2.11-win32-src.zip、apr-util-1.2.10-win32-src.zip。
2.解压apache-log4cxx-0.10.0.zip、apr-1.2.11-win32-src.zip、apr-util-1.2.10-win32-src.zip。
3.重命名apr-1.2.11为apr,重命名apr-util-1.2.10为apr-util;并将apr和apr-util拷贝apache-log4cxx-0.10.0根目录下。
4.以管理员的身份运行cmd.exe,并定位到log4cxx的当前apache-log4cxx-0.10.0目录下,执行以下命令:
configure configure-aprutil |
当执行configure-aprutil时,提示:“ ‘sed‘ 不是内部或外部命令,也不是可运行的程序或批处理文件”。这是因为 configure-aprutil.bat 文件中使用了 Linux 下的 sed 命令,windows下找不到sed命令。解决的方法有以下几种:1.下载一个sed for windows的工具;2.使用Cygwin(windows平台上运行的类UNIX模拟环境 ),再运行 configure-aprutil.bat;3.手动修改。
还是手动改来的方便,将.\apr-util\include\apu.hw下的#define APU_HAVE_APR_ICONV 1改为#define APU_HAVE_APR_ICONV 0 将.\apr-util\include\apr_ldap.hw下的#define APR_HAS_LDAP 1改为#define APR_HAS_LDAP 0。
5.用VC6或更高版本的Visual Studio工具打开 projects/log4cxx.dsw,在打开的时候会依次要求你选择apr、aprutil、xml工程的工作目录,在apache-log4cxx-0.10.0的子目录中找到对应的apr.dsp、aprutil.dsp、xml.dsp文件打开即可。
6.选择log4cxx为Active工程并编译。
7.在编译过程中可能报错:无法打开包括文件:“apr.h”和“apu.h”,找到对应的apr.hw重命名为apr.h,apu.h重命名为apu.h即可。
log4cxx的使用
<1>简单样例:
创建一个工程,设置log4cxx的头文件路径和lib库的路径,并加载log4cxx.lib文件。测试代码如下,日志信息会在命令行中输出。
#include "stdafx.h" #include "log4cxx/logger.h" #include "log4cxx/basicconfigurator.h" #include "log4cxx/helpers/exception.h" using namespace std; using namespace log4cxx; using namespace log4cxx::helpers; int main() { //@todo 重定向到文件 LoggerPtr logger(Logger::getLogger("LogTest")); //初始化配制 BasicConfigurator::configure(); //输出DEBUG级别的日志 LOG4CXX_DEBUG(logger, "debug log"); //输出TRACE级别的日志 LOG4CXX_TRACE(logger, "debug log"); //输出INFO级别的日志 LOG4CXX_INFO(logger, "info log"); //输出WARN级别的日志 LOG4CXX_WARN(logger, "debug log"); //输出ERROR级别的日志 LOG4CXX_ERROR(logger, "debug log"); //输出FATAL级别的日志 LOG4CXX_FATAL(logger, "debug log"); //ASSERT判断条件是否正确, 条件为false时,输出信息 LOG4CXX_ASSERT(logger, 1 == 2, "1 == 2"); return 0; }
注:我用VC6.0编译log4cxx的源码,然后在VS2010中使用log4cxx库时,编译会报错:unresolved external symbol "__declspec(dllimport) public: void __thiscall log4cxx::Logger::forcedLog
这是因为不同版本的编译器编译的库可能会有不同,要在VS2010上使用,需要在VS2010上重新编译源码。
<2>.设置配制文件:
如果要将日志信息输出到文件,或同时输出到命令行和文件,则要修改配制文件。
log4cxx.properties:
# 设置root logger为DEBUG级别,使用了ca和fa两个Appender log4j.rootLogger=DEBUG, consoleAppender, fileAppender #对Appender fileAppender(输出到文件)进行设置: # 这是一个文件类型的Appender, log4j.appender.fileAppender=org.apache.log4j.FileAppender # 将日志信息输出到./log4cxxTest.log, log4j.appender.fileAppender.File=./log4cxxTest.log # 输出方式为文件末尾追加 log4j.appender.fileAppender.Append=true # 设置输出格式(layout)为PatternLayout log4j.appender.fileAppender.layout=org.apache.log4j.PatternLayout #log4j.appender.fileAppender.layout.ConversionPattern=%d [%t] %-5p %.16c - %m%n #对Appender consoleAppender(输出到控制台)进行设置 log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender # 这是一个控制台类型的Appender log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout # 输出格式(layout)为PatternLayout log4j.appender.consoleAppender.layout.ConversionPattern=%d [%t] %-5p %.16c - %m%n
代码:
#include "stdafx.h" #include "log4cxx/logger.h" #include "log4cxx/PropertyConfigurator.h" using namespace std; using namespace log4cxx; using namespace log4cxx::helpers; int main() { //用指定的文件加载配制 log4cxx::PropertyConfigurator::configure("E:/Test/log4cxx.properties"); //创建logger LoggerPtr logger = Logger::getLogger("test"); //输出DEBUG级别的日志 LOG4CXX_DEBUG(logger, "debug log"); //输出TRACE级别的日志 LOG4CXX_TRACE(logger, "debug log"); //输出INFO级别的日志 LOG4CXX_INFO(logger, "info log"); //输出WARN级别的日志 LOG4CXX_WARN(logger, "debug log"); //输出ERROR级别的日志 LOG4CXX_ERROR(logger, "debug log"); //输出FATAL级别的日志 LOG4CXX_FATAL(logger, "debug log"); //ASSERT判断条件是否正确, 条件为false时,输出信息 LOG4CXX_ASSERT(logger, 1 == 2, "1 == 2"); return 0; }
Java中的log:
Java中给项目程序添加log主要有三种方式,一使用JDK中的java.util.logging包,一种是log4j,一种是commons-logging。其中log4j和commons-logging都是apache软件基金会的开源项目。这三种方式的区别如下:
Java.util.logging,JDK标准库中的类,是JDK 1.4 版本之后添加的日志记录的功能包。
log4j,最强大的记录日志的方式。可以通过配置 .properties 或是 .xml 的文件, 配置日志的目的地,格式等等。
commons-logging,最综合和常见的日志记录方式,是Java中的一个日志接口,一般会与log4j一起使用。自带SimpleLog可用于日志记录。
1.Java.util.logging
【例1.1】:日志的简单使用
package lwf.log.test; import java.util.logging.Logger; public class LogTest { static String strClassName = LogTest.class.getName(); static Logger logger = Logger.getLogger(strClassName); public static double division(int value1, int value2) { double result = 0; try { result = value1 / value2; } catch(ArithmeticException e) { logger.warning("除数不能为0."); e.printStackTrace(); } return result; } public static void main(String[] args) { System.out.println(division(5, 0)); } }
结果:
从这个例子中你会看到控制台上输出了日期时间,类名,方法名和“[warning]除数不能为0.”的信息。
Logger是Java Logging API中的一个类,Logger.getLogger方法创建了一个Logger实例。每一个Logger实例都必须有个名称,通常的做法是使用类名称定义Logger实例。
logger.warning方法用来输出日志信息,除了warning处,还有severe、info等。我们可以把【例1】再改一下,让其输出各种日志信息。
【例1.2】:日志的级别
public static double division(int value1, int value2) { double result = 0; try { result = value1 / value2; } catch(ArithmeticException e) { logger.severe("[severe]除数不能为0."); logger.warning("[warning]除数不能为0."); logger.info("[info]除数不能为0."); logger.config("[config]除数不能为0."); logger.fine("[fine]除数不能为0."); logger.finer("[finer]除数不能为0."); logger.finest("[finest]除数不能为0."); e.printStackTrace(); } return result; }
结果:
Java Logging API提供了七个日志级别用来控制输出。这七个级别分别是:
级别 |
SEVERE |
WARNING |
INFO |
CONFIG |
FINE |
FINER |
FINEST |
调用方法 |
severe() |
warning() |
info() |
config() |
fine() |
finer() |
finest() |
含意 |
严重 |
警告 |
信息 |
配置 |
良好 |
较好 |
最好 |
但在上面的例子中我们可以看到只输出了SEVERE、WARNING、INFO三个等级的日志,并没有如我们相像中的好样输出各个级别的日志信息。这是因为默认日志输出级别的设置是info,也就是说只有info或它以上的级别被输出,它以下的级别不被输出。那如何修改这个设置呢?
日志(Log)的配制:
1.代码设置
使用setLevel();但这种方式不能改变console的级别,只能改变输出到文件的日志的级别。
2.修改logging.properties
默认的外部配置文件 是JRE中lib/logging.properties文件。你可以打开这个文件,修改以下两行为:
.level=ALL //... |
这种方式会影响jre下所有用户。
为了不影响到所有的用户,我们还可以通过LogManager的readConfiguration(InputStream ins)读取指定的配制文件。
【例1.3】:LogManager管理日志
package lwf.log.test; import java.io.IOException; import java.io.InputStream; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; public class LogTest { static String strClassName = LogTest.class.getName(); static Logger logger = Logger.getLogger(strClassName); static LogManager logManager = LogManager.getLogManager(); static { InputStream inputStream = null; try { //读取配制文件 inputStream = LogTest.class.getClassLoader().getResourceAsStream("log.properties"); logManager.readConfiguration(inputStream); //添加Logger logManager.addLogger(logger); } catch (SecurityException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static double division(int value1, int value2) { double result = 0; try { result = value1 / value2; } catch(ArithmeticException e) { logger.severe("[severe]除数不能为0."); logger.warning("[warning]除数不能为0."); logger.info("[info]除数不能为0."); logger.config("[config]除数不能为0."); logger.fine("[fine]除数不能为0."); logger.finer("[finer]除数不能为0."); logger.finest("[finest]除数不能为0."); e.printStackTrace(); } return result; } public static void main(String[] args) { System.out.println(division(5, 0)); } }
log.properties:
# "handlers" specifies a comma separated list of log Handler #handlers= java.util.logging.ConsoleHandler handlers= java.util.logging.FileHandler # Default logging level. .level= CONFIG # default file output is in "E:\Test" directory. java.util.logging.FileHandler.pattern = E:/Test/Log%u.log java.util.logging.FileHandler.limit = 50000 java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter # Limit the message that are printed on the console to CONFIG and above. java.util.logging.ConsoleHandler.level = CONFIG java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # Facility specific properties.Provides extra control for each logger. # For example, set the com.xyz.foo logger to only log SEVERE messages: com.xyz.foo.level = SEVERE |
这样,用户就可以自己定义配制文件了。在E:\Test下可以看到输出的日志文件Log0.log
java.util.logging包中类的关系图如下:
参考文章:
http://blog.csdn.net/dl88250/article/details/1843813
2.log4j
1.项目串导入log4j的jar包
如Eclipse下项目名右键,Build Path\Add Libraries,添加一组用户自己的jar包。项目结构如下:
2.修改log4j的配制文件,设置日志输出的级别、格式等
log4j的log有5个级别:FATAL(严重的 )、ERROR(错误 )、WARN(警告)、INFO(信息)、DEBUG(调试 )。
3.在项目代码中适当添加日志。
【例2.1】
log4j.properties:
#set log level: show debug, info, error log4j.rootLogger=DEBUG, A1 # A1 is set to be a ConsoleAppender which outputs to System.out. #log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1=org.apache.log4j.FileAppender # A1 uses PatternLayout. log4j.appender.A1.layout=org.apache.log4j.PatternLayout #out log4j.appender.A1.File=E:/test/log4j.log # set log output format‘s style log4j.appender.A1.layout=org.apache.log4j.TTCCLayout |
代码:
package lwf.log.test; import org.apache.log4j.Logger; public class Log4jTest { private static Logger logger = Logger.getLogger(Log4jTest.class); public static void main(String[] args) { System.out.println("This is log4j test."); // 记录debug级别的信息 logger.debug("This is debug message."); // 记录info级别的信息 logger.info("This is info message."); // 记录error级别的信息 logger.error("This is error message."); } }
log4j的使用和配制另参见:http://blog.csdn.net/luoweifu/article/details/43638495
3.commons-logging
commons-logging提供的是一个日志(Log)接口(interface),是为那些需要建立在不同环境下使用不同日志架构的组件或库的开发者创建的,其中包括Apache Log4j以及Java log的日志架构。把日志信息抽象成commons-logging的Log接口,并由commons-logging在运行时决定使用哪种日志架构。因为Log4j的强大功能,commons-logging一般会和Log4j一起使用,这几乎成为了Java日志的标准工具。
commons-logging有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。当commons-logging.jar被加入到CLASSPATH(通常将commons-logging.jar放在web project下的WebContent\WEB-INF\lib目录中)之后,它会合理地猜测你想用的日志工具,然后进行自我设置,用户根本不需要做任何设置。默认的LogFactory是按照下列的步骤去发现并决定那个日志工具将被使用的(按照顺序,寻找过程会在找到第一个工具时中止,这个顺序非常重要):
00001. 寻找当前factory中名叫org.apache.commons.logging.Log配置属性的值
00002. 寻找系统中属性中名叫org.apache.commons.logging.Log的值
00003. 如果应用程序的classpath中有log4j,则使用相关的包装(wrapper)类(Log4JLogger)
00004. 如果应用程序运行在jdk1.4的系统中,使用相关的包装类(Jdk14Logger)
00005. 使用简易日志包装类(SimpleLog)
commons-logging与log4j的配合使用:
项目目录结构:
common-logging.properties:
#use commons-logging default SimpleLog # org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog #use log4j org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JCategoryLog #JDK1.4 Logger #org.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger |
代码:
package lwf.log.test; import org.apache.log4j.Logger; public class Log4jTest { private static Logger logger = Logger.getLogger(Log4jTest.class); public static void main(String[] args) { System.out.println("This is log4j test."); // 记录debug级别的信息 logger.debug("This is debug message."); // 记录info级别的信息 logger.info("This is info message."); // 记录error级别的信息 logger.error("This is error message."); } }
参考:
http://www.cnblogs.com/xwdreamer/archive/2011/12/28/2304598.html
http://shift8.iteye.com/blog/1316802
JavaScript中的log:
JavaScript是一种解释性语言,一般作为浏览器的脚本用于web前端中,日志记录用的相对较少,当然也有,如log4js。这个日后有时间再补上……
coding.... |
log的作用
1.程序错误报告
一般应用程序都会在程序发生异常或崩溃时,自动弹出错误报告的对话框,用户可选择是否提交报告。如果用户提交错误报告,应用程序的开发人员,就可根据报告的日志信息,查看程序出错的原因,从而更好的改善程序。
2.统计程序的访问量、使用人数等
虽然这不是一种最好的方式,但也算是一种可行的方式。
log的应用场景
1.异常,常常与try...catch...结合使用
2.输出一此必要的信息,替代命令行的输出
PS:春节将至,祝大家新年快乐,在新的一年里更加漂亮,更加健康,更加进步,更加智慧!
如果您有什么疑惑和想法,请在评论处给予反馈,您的反馈就是最好的测评师!由于本人技术和能力有限,如果本博文有错误或不足之处,敬请谅解并给出您宝贵的建议!
========================编程思想系列文章回顾========================