数据库异常信息为啥没有写入log4j日志文件中

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库异常信息为啥没有写入log4j日志文件中相关的知识,希望对你有一定的参考价值。

参考技术A

log4j是一个优秀的开源日志记录项目,我们不仅可以对输出的日志的格式自定义,还可以自己定义日志输出的目的地,比如:屏幕,文本文件,数据库,甚至能通过socket输出。本节主要讲述如何将日志信息输入到数据库(可以插入任何数据库,在此主要以MSSQL为例进行详解)。
用log4j将日志写入数据库主要用到是log4j包下的JDBCAppender类,它提供了将日志信息异步写入数据的功能,我们可以直接使用这个类将我们的日志信息写入数据库;也可以扩展JDBCAppender类,就是将JDBCAppender类作为基类。下面将通过一个实例来讲解log4j是如何将日志信息写入数据库的。
我们的需求:我们在软件开发的过程中需要将调试信息、操作信息等记录下来,以便后面的审计,这些日志信息包括用户ID、用户姓名、操作类、路径、方法、操作时间、日志信息。
设计思想:我们采用JDBCAppender类直接将日志信息插入数据库,所有只需要在配置文件配置此类就可以;要获得用户信息需要用过滤器来实现;(假如不需要用户的信息,就不需要设计过滤器,其实大部分情况下都是需要这些用户信息,尤其是在web应用开发中)在日志信息中获得用户信息,就的通过过滤器的request或session对象,从session中拿到用户信息怎样传到log4j呢,log4j为我们提供了MDC(MDC是log4j种非常有用类,它们用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息。MDC内部使用了类似map的机制来存储信息,上下文信息也是每个线程独立地储存,所不同的是信息都是以它们的key值存储在”map”中。相对应的方法,

MDC.put(key, value); MDC.remove(key); MDC.get(key);

在配置PatternLayout的时候使用:%xkey来输出对应的value)。有了MDC,我们可以在过滤器中先获得用户信息,再用MDC.Put(“key”)方法,log在执行sql语句时通过%xkey来输出对应的value。


实现步骤:
1、在你的项目中要确保有log4j和commons-logging这两个jar文件;
2、设置要你要插入日志信息的表结构

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[WDZLOG]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[WDZLOG]
GO

CREATE TABLE [dbo].[WDZLOG] (
    [WDZLOGID] [int] IDENTITY (1, 1) NOT NULL ,
    [LogName] [varchar] (255) COLLATE Chinese_PRC_CI_AS NULL ,//用户ID
    [UserName] [varchar] (255) COLLATE Chinese_PRC_CI_AS NULL ,//用户姓名
    [Class] [varchar] (255) COLLATE Chinese_PRC_CI_AS NULL ,//类名
    [Mothod] [varchar] (255) COLLATE Chinese_PRC_CI_AS NULL //,方法名
    [CreateTime] [varchar] (255) COLLATE Chinese_PRC_CI_AS NULL ,//产生时间
    [LogLevel] [varchar] (20) COLLATE Chinese_PRC_CI_AS NULL ,//日志级别
    [MSG] [varchar] (555) COLLATE Chinese_PRC_CI_AS NULL //日志信息
) ON [PRIMARY]
GO

3、配置文件(摘自我们的项目)后面将对此配置文件进行详细讲解,它也log4j的核心部分。

log4j.properties
log4j.rootLogger=INFO,stdout
            
log4j.logger.org.springframework.web.servlet=INFO,db

log4j.logger.org.springframework.beans.factory.xml=INFO
log4j.logger.com.neam.stum.user=INFO,db

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%dyyyy-MM-dd HH:mm:ss %p [%c] - - <%m>%n

log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File=$webapp.root/WEB-INF/logs/exppower.log
log4j.appender.logfile.DatePattern=.yyyy-MM-dd
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] wang- <%m>%n

########################

# JDBC Appender

#######################


#log4j.logger.business=INFO,db
#log4j.appender.db=com.neam.commons.MyJDBCAppender
log4j.appender.db=JDBCExtAppender

log4j.appender.db.BufferSize=10

log4j.appender.db.sqlname=log

log4j.appender.db.driver=net.sourceforge.jtds.jdbc.Driver
                      
log4j.appender.db.URL=jdbc:jtds:SqlServer://localhost:1433;DatabaseName=pubs

log4j.appender.db.user=sa

log4j.appender.db.password=sa

log4j.appender.db.sql=insert into WDZLOG (LogName,UserName,Class,Mothod,createTime,LogLevel,MSG) values ('%XuserId','%XuserName','%C','%M','%dyyyy-MM-dd HH:mm:ss','%p','%m')

log4j.appender.db.layout=org.apache.log4j.PatternLayout

4、编写过滤器(ResFilter.java)

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.apache.log4j.MDC;

import com.neam.domain.User;

public class ResFilter implements Filter

     
    private final static double DEFAULT_USERID= Math.random()*100000.0;  

    public void destroy() 
    

    public void doFilter(ServletRequest request, ServletResponse response,
           FilterChain chain) throws IOException, ServletException 
       HttpServletRequest req=(HttpServletRequest)request;
        HttpSession session= req.getSession();
        if (session==null)
            MDC.put("userId",DEFAULT_USERID);  
        
        else
            User customer=(User)session.getAttribute("user");
            if (customer==null)
                MDC.put("userId",DEFAULT_USERID);
                MDC.put("userName",DEFAULT_USERID);
            
            else
            
                MDC.put("userId",customer.getName());
                MDC.put("userName",customer.getName());
            
        
        //logger.info("test for MDC.");

       chain.doFilter(request, response);
    
    public void init(FilterConfig Config) throws ServletException 
//     this.filterConfig = Config;
//     String ccc = Config.getServletContext().getInitParameter("cherset");
//     this.targetEncoding = Config.getInitParameter("cherset");

    

5、在需要写入日志的地方引入

private Log logger = LogFactory.getLog(this.getClass());

在具体方法中就可以写入日志
logger.info("");
logger.debug("");
logger.warn("");
logger.error("");

配置文件详解:
log4j.properties
log4j.properties
log4j.rootLogger=INFO,stdout


//配置根Logger,其语法为:
log4j.rootLogger = [ level ] , appenderName1, appenderName2, …
level : 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。
appenderName:就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。
例如:log4j.rootLogger=info,A1,B2,C3 配置了3个输出地方我们可以设置让A1在控制台输出;B2生产日志文件;C3让日志信息插入数据库中。
本例中是将所有的日志信息在控制台打印出来。 
log4j.logger.org.springframework.web.servlet=INFO,db
//设置将spring下包的某些类的日志信息写入数据库中,并且在控制台上打印出来。(是通过log4j.rootLogger=INFO,stdout来体现的)db是将日志信息写入数据库中
log4j.logger.org.springframework.beans.factory.xml=INFO
//本实例中为了让某些包下的日志信息能写入数据库
log4j.logger.com.neam.stum.user=INFO,db
//设置自己某个模块下的日志信息既在控制台上打印而且往数据库中保存

//下面是配置在控制台上打印日志信息,在这里就不再仔细描述了
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%dyyyy-MM-dd HH:mm:ss %p [%c] - - <%m>%n

//下面是配置将日志信息写入文件中,在这里也就不再仔细描述了
log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logfile.File=$webapp.root/WEB-INF/logs/exppower.log
log4j.appender.logfile.DatePattern=.yyyy-MM-dd
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] wang- <%m>%n

########################

# JDBC Appender

#######################


#log4j.appender.db=com.neam.commons.MyJDBCAppender
//下面是配置将日志信息插入数据库,
log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender
//配置输出目标为数据库(假如要将日志在控制台输出,配置为log4j.appender. stdout =org.apache.log4j.ConsoleAppender;将日志写入文件,配置为log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
这样的配置在许多地方都要有,需要可查有关资料),当然你也可以自己扩展org.apache.log4j.jdbc.JDBCAppender这个类,只需要在这里配置就可以了例如我们配置我自己扩展的MyJDBCAppender,配置为#log4j.appender.db=com.neam.commons.MyJDBCAppender

log4j.appender.db.BufferSize=10
//设置缓存大小,就是当有10条日志信息是才忘数据库插一次

log4j.appender.db.driver=net.sourceforge.jtds.jdbc.Driver
//设置要将日志插入到数据库的驱动                     
log4j.appender.db.URL=jdbc:jtds:SqlServer://localhost:1433;DatabaseName=pubs

log4j.appender.db.user=sa

log4j.appender.db.password=sa

log4j.appender.db.sql=insert into WDZLOG (LogName,UserName,Class,Mothod,createTime,LogLevel,MSG) values ('%XuserId','%XuserName','%C','%M','%dyyyy-MM-dd HH:mm:ss','%p','%m')
//设置要插入日志信息的格式和内容,%XuserId是置取MDC中的key值,因为我们在过滤器中是将用户id和用户姓名放入MDC中,所有在这里可以用%XuserId和%XuserName取出用户的ID和用户姓名;'%C'表示日志信息是来自于那个类;%M表示日志信息来自于那个方法中;%dyyyy-MM-dd HH:mm:ss表示日志信息产生的时间,yyyy-MM-dd HH:mm:ss表示一种时间格式,你也可以直接写成%d;%p表示日志信息的级别(debug info warn error);
%m表示你写入的日志信息
log4j.appender.db.layout=org.apache.log4j.PatternLayout

如何在java中将错误日志或异常写入文件

【中文标题】如何在java中将错误日志或异常写入文件【英文标题】:How to write error log or exception into file in java 【发布时间】:2012-03-10 21:05:35 【问题描述】:

有没有办法将错误日志或异常写入java中的文件。我已经通过了 Log4j。我用谷歌搜索了它,但没有找到一个好的解决方案。我写了一个简单的代码

catch (Exception e) 
    PrintWriter pw = new PrintWriter(new FileOutputStream("Log"));     
    e.printStackTrace(pw);
 

还有其他方法可以记录错误或异常吗?任何机构都可以为我提供 Log4j 的示例示例吗?

【问题讨论】:

【参考方案1】:

先读log4j Manual,配置滚动日志文件很简单。您不必执行任何显式文件操作。

#SET LEVEL of ROOT-LOGGER, you will like to have Debug in local, but in prod you may just want WARN and ABOVE. This setting is done here!
log4j.rootLogger=debug, stdout, R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number. (basically, format of log)
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

# THIS IS WHERE YOU WILL HAVE ALL THE LOG WRITTEN
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=/var/log/applogs/example.log

# Maximum size of log file, usually we keep 10MB
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file, usually we keep 10
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

其次,每当你捕捉到异常时,都这样做

public class MyClass

  private static Logger logger = Logger.getLogger(MyClass.class);

  public ReturnType myMethod(Param p, Param2 p2) 
    ....
    ....
    try 
      ..    
     catch(MyException e) 
       logger.log("Exceptions happen!", e); //this will put all the details in log file configured earlier
    
    ....
  

  ....

值得阅读手册。更好的阅读Complete log4j Manual

【讨论】:

当出现未经检查的异常时我该怎么办?我怎样才能确定所有内容都会发送到记录器? 使用Throwable 捕捉一切。虽然不建议。您可能想阅读以下帖子:1. ***.com/questions/6115896/… 2. ***.com/questions/2274102/… @Nishant 日志文件在哪里?是在apache文件夹里面吗?【参考方案2】:

使用log4j,您可以很容易地记录异常:

try 
    ...
 catch(Exception e) 
    log.error("An exception! Oops!", e);

【讨论】:

【参考方案3】:

您可以将异常作为参数添加到您的 log4j 语句中,例如

catch(Exception e)

    logger.error("Unexpected error", e);

如果你有一个运行正常的文件追加器,这将输出异常的完整堆栈跟踪。

【讨论】:

【参考方案4】:
try 
       System.setErr(new PrintStream(new FileOutputStream(System.getProperty("user.home")+"/error.log")));
 catch (FileNotFoundException ex) 
        ex.printStackTrace();

现在所有错误输出都写入这个文件

【讨论】:

虽然技术上是正确的,但这比使用日志框架(log4j、juli、commons-logging 等)有什么好处? 当你可以用 java 自己的方法解决它时,为什么要使用框架? 1:不支持不同的日志级别 2:不支持诊断上下文(线程、类、方法等) 3:不支持输出格式 我可以继续。你可以用螺丝刀敲钉子,但如果你有锤子,为什么不用锤子呢? 耶,但如果您只想将所有错误写入文件,则可以使用这行简单的代码。如果您想拥有所有这些功能,请选择一个框架。如果你想挖一个小洞,就用铲子,如果你想挖一个大洞,就用电铲。 您称它们为“功能”,我称它们为应用程序日志记录的最低要求。我猜是不同的野心。【参考方案5】:

Look this tutorial about "File Appender"

请参阅official Log4j short introduction 和“配置”部分。

您也可以搜索“RollingFileAppender”或“File appender”。

您将记录器配置为将其消息发送到附加程序。这个 appender 可以将消息转发到控制台(stdin)、文件(FileAppender、RollingFileAppender...)...

使用它来执行错误日志:

try
    throw new Exception("bla bla bla...");
 catch( Exception e )
     // log without stack trace
     mLogger.error("Your log message");

     // log with stack trace
     mLogger.error("Your log message", e);

【讨论】:

【参考方案6】:

您可以使用 log4j 依赖项记录数据。 Go this link

https://logging.apache.org/log4j/2.x/manual/configuration.html
Pom dependency ==>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.2</version>
</dependency>

Properties File eg ==>

status = error
dest = err
name = PropertiesConfig

property.filename = target/rolling/rollingtest.log

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug

appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %m%n
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = error

appender.rolling.type = RollingFile
appender.rolling.name = RollingFile
appender.rolling.fileName = $filename
appender.rolling.filePattern = target/rolling2/test1-%dMM-dd-yy-HH-mm-ss- 
%i.log.gz
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d %p %C1. [%t] %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 2
appender.rolling.policies.time.modulate = true
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=100MB
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 5

logger.rolling.name = com.example.my.app //  Change this to your own package 
name otherwise will not work
logger.rolling.level = debug
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = RollingFile

rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT

Java code ==>
private static final Logger logger = 
LogManager.getLogger(MyClass.class.getName());
logger.info("Entering application.");
logger.trace("Entering application.");
logger.debug("Debugg application.");
logger.error("Did it again!");

【讨论】:

以上是关于数据库异常信息为啥没有写入log4j日志文件中的主要内容,如果未能解决你的问题,请参考以下文章

如何在java中将错误日志或异常写入文件

Log4j写入数据库详解

开源日志工具log4j

log日志使用

日志及log4j框架

java 中怎么将程序出现的异常信息保存到日志文件中