`
wangjian5748
  • 浏览: 206368 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

log4j的数据库Appender的实现代码

阅读更多

1.JDBCAppender.java

package com.benqguru.palau.log.jdbc.test;

import java.sql.*;
import java.util.*;

import org.apache.log4j.*;
import org.apache.log4j.spi.*;

/**
 这个JDBCAppender用来把消息写进数据库.
 <p><b>JDBCAppender在运行时可选项配置的,通过在下面两者之间选择:</b></p>
 <dir>
 <p><b>1. 用配置文件</b></p>
 <p>在文件中定义选项,并且在你的代码中调用<code>PropertyConfigurator.configure(filename)</code></p>
 <p><b>2. 用JDBCAppender的方法来完成</b></p>
 <p>调用 <code>JDBCAppender::setOption(JDBCAppender.xxx_OPTION, String value)</code> 来做相相似的事情,在没用配置文件的情况下</p>
 </dir>

 <p>在JDBCAppender中,所有有用变量被定义为静态字符串常量,并命名为xxx_OPTION.</p>

 <p><b>下面是所有的有用选项的描述:</b></p>
 <dir>
 <p><b>1. 连接到数据库的数据库可选项(Database-options)</b></p>
 <p>- <b>URL_OPTION</b>  : 一个形如jdbc:subprotocol:subname数据库url</p>
 <p>- <b>USERNAME_OPTION</b> : 数据库用户</p>
 <p>- <b>PASSWORD_OPTION</b> : 用户密码</p>

 <p><b>2. 指定你自己的JDBCConnectionHandler的连接器选项(Connector-option)</b></p>
 <p>- <b>CONNECTOR_OPTION</b> : 一个实现了JDBCConnectionHandler接口的类</p>
 <p>这个接口可以用来获得自定义连接.</p>
 <p>假如其余的数据库选项是给定的,这些选项将用作为参数传给JDBCConnectionHandler接口.</p>
 <p>另外假如没有给定数据库选项,JDBCConnectionHandler接口将不带这些参数被调用</p>
 <p>另外假如这个可选项没有定义,JDBCAppender需要数据库选项来打开一个连接</p>

 <p><b>3. SQL选项指定一个静态的sql语句,在每次发生消息事件时,将执行这个语句</b></p>
 <p>- <b>SQL_OPTION</b>   : 一个用来写入数据到数据库的sql语句</p>
 <p>这sql语句的某个地方用<b>@MSG@</b>这个变量,这个变量必须要用消息内容来动态的替换</p>
 <p>假如你给定整个选项,表选项和列选项将被忽略!</p>

 <p><b>4. 表选项用来指定数据库中包含的一个表</b></p>
 <p>- <b>TABLE_OPTION</b>  : 保存日志信息的表</p>

 <p><b>5. 列选项用来描述表中重要的列(非空列必须描述!)</b></p>
 <p>- <b>COLUMNS_OPTION</b>  : 列描述的一个格式化列表</p>
 <p>每个列描述包涵如下内容</p>
 <dir>
  <p>- 列的名字 <b><i>(name)</i></b> (必须)</p>
  <p>- LogType类中的一个静态常量日志类型<b><i>(logtype)</i></b> (必须)</p>
  <p>- 依赖与LogType类的一个值<b><i>(value)</i></b>(可选/必须, 依赖日志类型(logtype)</p>
 </dir>
 <p>这是<b>{@link LogType}</b>类中的有用日志类型(logtype)的描述,并且怎样处理处理值<b><i>(value)</i></b>:</p>
 <dir>
  <p>o <b>MSG</b>  = 一个可以忽略的值,列将获得消息. (有一个列需要这种类型!)</p>
  <p>o <b>STATIC</b>  = 这个值可以填充每一个日志消息的列. (保证这种值的类型可以被转换为列的sql类型!)</p>
  <p>o <b>ID</b>  = 值必须是一个实现JDBCIDHandler接口的类名.</p>
  <p>o <b>TIMESTAMP</b>         = 一个可以忽略的值,这个列将用每个记录日志消息的确切时间戳填充.</p>
  <p>o <b>EMPTY</b>  = 一个可以忽略的值,当写数据到数据库的时候,这个列将被忽略 (保证用数据库触发器填充非空类!)</p>
 </dir>
 <p>假如有根多的列需要描述, 列必须要Tab分界符隔开(unicode0008) !</p>
 <p>列描述的参数必须用'~'隔开 !</p>
 <p><i>(实例:  name1~logtype1~value1   name2~logtype2~value2...)</i></p>

 <p><b>6. 布局器选项用来定义消息的布局 (可选)</b></p>
 <p>- <b>_</b> : 布局器不是用形如xxx_OPTION来设置的</p>
 <p>参考下面的配置文件和代码示例...</p>
 <p>默认的布局器是类 {@link org.apache.log4j.PatternLayout}, 这个布局器用仅仅代表消息的%m模式.</p>

 <p><b>7. 缓冲器选项用来定义一个消息事件缓冲区(可选)</b></p>
 <p>- <b>BUFFER_OPTION</b>  : 定义多少条消息将被缓存,直到被更新到数据库.</p>
 <p>默认的缓冲区大小是1, 当消息事件发生时,将进行更新操作.</p>

 <p><b>8. 提交选项用来定义一个自动提交</b></p>
 <p>- <b>COMMIT_OPTION</b>  : 定义更新消息是(Y)否(N)自动提交到数据库.</p>
 <p>默认的时commit=N.</p>
 </dir>

 <p><b>下面选项的顺序时很重要的:</b></p>
 <dir>
 <p><b>1. 连接器选项(Connector-option) OR/AND 数据库选项(Database-options)</b></p>
 <p>数据库连接时必须的!</p>
 <p><b>2. (表选项(Table-option) AND 列选项(Columns-option)) OR SQL选项(SQL-option)</b></p>
 <p>上面的任意一项是必须的! 写在哪或者写什么...;-)</p>
 <p><b>3. 其他选项可以在任何时候设置...</b></p>
 <p>其他选项时可选的,并且都有一个可以自定义的默认值.</p>
 </dir>

 <p><b>这是一个可以作为PropertyConfigurator参数的配置文件示例</b> : <A HREF="log4j.properties"> log4j.properties</A></p>

 <p><b>这是一个用配置文件配置JDBCAppender类的代码示例</b> : <A HREF="Log4JTest.java"> Log4JTest.java</A>.</p>

 <p><b>这是另一个没用配置文件来配置JDBCAppender类的代码示例</b> : <A HREF="Log4JTest2.java"> Log4JTest2.java</A></p>

* <p>Title: log4j1.2.8源码解析</p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2004</p>
* <p>Company: 明基逐鹿</p>
* @author 王建
* @version 1.0
*/
public class JDBCAppender
    extends AppenderSkeleton {
  /**
   数据库选项,设置设置数据库url的形式为jdbc:subprotocol:subname
   */
  public static final String URL_OPTION = "url";

  /**
   数据库选项,设置可以连接这个数据库的用户
   */
  public static final String USERNAME_OPTION = "username";

  /**
   数据库选项,设置用户密码
   */
  public static final String PASSWORD_OPTION = "password";

  /**
   表选项,指定数据库中一个表
   */
  public static final String TABLE_OPTION = "table";

  /**
   连接器选项,指定你自己的JDBCConnectionHandler
   */
  public static final String CONNECTOR_OPTION = "connector";

  /**
   列选项,描述表中重要的列
   */
  public static final String COLUMNS_OPTION = "columns";

  /**
   sql选项,指定一个在发生消息事件时执行的静态sql语句
   */
  public static final String SQL_OPTION = "sql";

  /**
   缓冲器选项,定义消息事件缓冲区的大小
   */
  public static final String BUFFER_OPTION = "buffer";

  /**
   提交选项,定义一个自动提交
   */
  public static final String COMMIT_OPTION = "commit";

  //保存可以被setOption()方法设置的可选值的变量
  private String url = null;
  private String username = null;
  private String password = null;
  private String table = null;
  private String connection_class = null;
  private String sql = null;

//   若数据库不支持事务,择要设定为false,如Mysql
  private boolean docommit = false;
  private int buffer_size = 1;
  private JDBCConnectionHandler connectionHandler = null;

//   这个缓冲器保存了消息事件.当缓冲器达到一定大小时,缓冲器将被刷新,并且信息将被更新到数据库.
  private ArrayList buffer = new ArrayList();

//   数据库连接
  private Connection con = null;

//   这个类封装了记录日志进表中所必须的逻辑
  private JDBCLogger jlogger = new JDBCLogger();

//   标识:这是一个表明是否已经建立了数据库连接的标识
  private boolean connected = false;

//   一个表明配置状态的标识
  private boolean configured = false;

//   一个表明所有东西都已准备好,可以使用append()的标识
  private boolean ready = false;
//   假如程序结束,关闭数据库并且刷新缓冲器
  public void finalize() {
    close();
    super.finalize();
  }

  /**
   * 内部方法.返回一个包涵可以被方法setOption()设置的有用选项(option)的字符串数组.
   * @return String[]
   */
  public String[] getOptionStrings() {
    // The sequence of options in this string is important, because setOption() is called this way ...
    return new String[] {
        CONNECTOR_OPTION, URL_OPTION, USERNAME_OPTION, PASSWORD_OPTION,
        SQL_OPTION, TABLE_OPTION, COLUMNS_OPTION, BUFFER_OPTION, COMMIT_OPTION};
  }

  /**
   * 设置所有必须的选项
   * @param _option String
   * @param _value String
   */
  public void setOption(String _option, String _value) {
    _option = _option.trim();
    _value = _value.trim();

    if (_option == null || _value == null)return;
    if (_option.length() == 0 || _value.length() == 0)return;

    _value = _value.trim();

    if (_option.equals(CONNECTOR_OPTION)) {//连接器
      if (!connected) connection_class = _value;
    }
    else if (_option.equals(URL_OPTION)) {//URL
      if (!connected) url = _value;
    }
    else if (_option.equals(USERNAME_OPTION)) {//用户名
      if (!connected) username = _value;
    }
    else if (_option.equals(PASSWORD_OPTION)) {//密码
      if (!connected) password = _value;
    }
    else if (_option.equals(SQL_OPTION)) {//SQL语句
      sql = _value;
    }
    else if (_option.equals(TABLE_OPTION)) {//表
      if (sql != null)return;//若sql语句不是null,则返回
      table = _value;
    }
    else if (_option.equals(COLUMNS_OPTION)) {//列
      if (sql != null)return;

      String name = null;
      int logtype = -1;
      String value = null;
      String column = null;
      String arg = null;
      int num_args = 0;
      int num_columns = 0;
      StringTokenizer st_col;
      StringTokenizer st_arg;

      //Columns are TAB-separated
      st_col = new StringTokenizer(_value, " ");

      num_columns = st_col.countTokens();

      if (num_columns < 1) {
        errorHandler.error(
            "JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " +
            _value + " !");
        return;
      }

      for (int i = 1; i <= num_columns; i++) {
        column = st_col.nextToken();

        //Arguments are ~-separated
        st_arg = new StringTokenizer(column, "~");

        num_args = st_arg.countTokens();

        if (num_args < 2) {
          errorHandler.error(
              "JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " +
              _value + " !");
          return;
        }

        for (int j = 1; j <= num_args; j++) {
          arg = st_arg.nextToken();

          if (j == 1) name = arg;
          else if (j == 2) {
            try {
              logtype = Integer.parseInt(arg);
            }
            catch (Exception e) {
              logtype = LogType.parseLogType(arg);
            }

            if (!LogType.isLogType(logtype)) {
              errorHandler.error(
                  "JDBCAppender::setOption(), Invalid COLUMN_OPTION LogType : " +
                  arg + " !");
              return;
            }
          }
          else if (j == 3) value = arg;
        }

        if (!setLogType(name, logtype, value))return;
      }
    }
    else if (_option.equals(BUFFER_OPTION)) {//缓冲器
      try {
        buffer_size = Integer.parseInt(_value);
      }
      catch (Exception e) {
        errorHandler.error(
            "JDBCAppender::setOption(), Invalid BUFFER_OPTION value : " +
            _value + " !");
        return;
      }
    }
    else if (_option.equals(COMMIT_OPTION)) {//提交
      docommit = _value.equals("Y");
    }

    if (_option.equals(SQL_OPTION) || _option.equals(TABLE_OPTION)) {
      if (!configured)
        configure();
    }
  }

  /**
   * 内部方法,返回true,你可以定义你自己的layout
   * @return boolean
   */
  public boolean requiresLayout() {
    return true;
  }

  /**
   * 内部方法,关闭数据库连接并且冲刷(flush)缓冲器
   */
  public void close() {
    flush_buffer();
    if (connection_class == null) {
      try {
        con.close();
      }
      catch (Exception e) {
        errorHandler.error("JDBCAppender::close(), " + e);
      }
    }
    this.closed = true;
  }

  /**
   * 对日志表的所有列都必须调用这个方法
   * @param _name String
   * @param _logtype int
   * @param _value Object
   * @return boolean
   */
  public boolean setLogType(String _name, int _logtype, Object _value) {
    if (sql != null)return true;

    if (!configured) {//为什么?
      if (!configure())return false;
    }

    try {
      jlogger.setLogType(_name, _logtype, _value);
    }
    catch (Exception e) {
      errorHandler.error("JDBCAppender::setLogType(), " + e);
      return false;
    }

    return true;
  }

  /**
   * 内部方法.添加消息到数据库表
   * @param event LoggingEvent
   */
  public void append(LoggingEvent event) {
    if (!ready) {
      if (!ready()) {
        errorHandler.error("JDBCAppender::append(), Not ready to append !");
        return;
      }
    }

    buffer.add(event);

    if (buffer.size() >= buffer_size) flush_buffer();
  }

  /**
   * 内部方法.刷新缓冲器
   */
  public void flush_buffer() {
    try {
      int size = buffer.size();

      if (size < 1)return;

      for (int i = 0; i < size; i++) {
        LoggingEvent event = (LoggingEvent) buffer.get(i);

        //Insert message into database
        jlogger.append(layout.format(event));
      }

      buffer.clear();

      if (docommit) con.commit();
    }
    catch (Exception e) {
      errorHandler.error("JDBCAppender::flush_buffer(), " + e + " : " +
                         jlogger.getErrorMsg());
      try {
        con.rollback();
      }
      catch (Exception ex) {}
      return;
    }
  }

  /**
   * 内部方法.当JDBCAppender准备好可以添加消息到数据库,返回true;否则返回false
   * @return boolean
   */
  public boolean ready() {
    if (ready)return true;

    if (!configured)return false;

    ready = jlogger.ready();

    if (!ready) {
      errorHandler.error(jlogger.getErrorMsg());
    }

    return ready;
  }

  /**
   * 内部方法,连接数据库
   * @throws Exception
   */
  protected void connect() throws Exception {
    if (connected)return;

    try {
      if (connection_class == null) {
        if (url == null)throw new Exception(
            "JDBCAppender::connect(), No URL defined.");

        if (username == null)throw new Exception(
            "JDBCAppender::connect(), No USERNAME defined.");

        if (password == null)throw new Exception(
            "JDBCAppender::connect(), No PASSWORD defined.");

        connectionHandler = new DefaultConnectionHandler();
      }
      else {
        connectionHandler = (JDBCConnectionHandler) (Class.forName(
            connection_class).newInstance());
      }

      if (url != null && username != null && password != null) {
        con = connectionHandler.getConnection(url, username, password);
      }
      else {
        con = connectionHandler.getConnection();
      }

      if (con.isClosed()) {
        throw new Exception("JDBCAppender::connect(), JDBCConnectionHandler returns no connected Connection !");
      }
    }
    catch (Exception e) {
      throw new Exception("JDBCAppender::connect(), " + e);
    }
    //设置连接标识为true
    connected = true;
  }

  /**
   * 内部方法.检查是否配置,以便进行添加操作...
   * @return boolean
   */
  protected boolean configure() {
    if (configured)return true;

    if (!connected) {
      if ( (connection_class == null) &&
          (url == null || username == null || password == null)) {
        errorHandler.error(
            "JDBCAppender::configure(), Missing database-options or connector-option !");
        return false;
      }

      try {
        connect();
      }
      catch (Exception e) {
        connection_class = null;
        url = null;
        errorHandler.error("JDBCAppender::configure(), " + e);
        return false;
      }
    }

    if (sql == null && table == null) {
      errorHandler.error(
          "JDBCAppender::configure(), No SQL_OPTION or TABLE_OPTION given !");
      return false;
    }

    if (!jlogger.isConfigured()) {
      try {
        jlogger.setConnection(con);

        if (sql == null) {
          jlogger.configureTable(table);
        }
        else jlogger.configureSQL(sql);
      }
      catch (Exception e) {
        errorHandler.error("JDBCAppender::configure(), " + e);
        return false;
      }
    }

    //Default Message-Layout
    if (layout == null) {
      layout = new PatternLayout("%m");
    }

    //标明已经配置好了!
    configured = true;

    return true;
  }
  public String getUrl() {
    return url;
  }
  public void setUrl(String url) {
    this.url = url;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public String getConnection_class() {
    return connection_class;
  }
  public void setConnection_class(String connection_class) {
    this.connection_class = connection_class;
  }
  public String getSql() {
    return sql;
  }
  public void setSql(String sql) {
    this.sql = sql;
  }
  public String getTable() {
    return table;
  }
  public void setTable(String table) {
    this.table = table;
  }
  public int getBuffer_size() {
    return buffer_size;
  }
  public void setBuffer_size(int buffer_size) {
    this.buffer_size = buffer_size;
  }
  public String getDocommit() {
    if (docommit == true) {
      return "Y";
    }
    else {
      return "N";
    }
  }

  public void setDocommit(String commit) {
    if (commit.equals("N")) {
      this.docommit = false;
    }
    else {
      this.docommit = true;
    }
  }
}

/**
 * 这是一个JDBCAppender使用的默认JDBCConnectionHandler
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
class DefaultConnectionHandler
    implements JDBCConnectionHandler {
  Connection con = null;

  public Connection getConnection() {
    return con;
  }

  public Connection getConnection(String _url, String _username,
                                  String _password) {
    try {
      if (con != null && !con.isClosed()) con.close();
      con = DriverManager.getConnection(_url, _username, _password);
      con.setAutoCommit(true);
    }
    catch (Exception e) {
      e.printStackTrace();
    }

    return con;
  }
}

2.JDBCConnectionHandler.java

package com.benqguru.palau.log.jdbc.test;

import java.sql.*;

/**
 * 必须实现这个接口来处理数据库连接,在JDBCLogger类中会用到这个接口
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
public interface JDBCConnectionHandler {

  /**
   * 获得一个连接
   * @return Connection
   */
  Connection getConnection();

  /**
   * 获得一个自定义的连接
   * @param _url String
   * @param _username String
   * @param _password String
   * @return Connection
   */
  Connection getConnection(String _url, String _username, String _password);
}

3.JDBCIDHandler.java

package com.benqguru.palau.log.jdbc.test;

/**
 * 必须实现这个接口来提供带有独一无二ID的ID-column,在JDBCLogger类中会用到这个接口
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
public interface JDBCIDHandler {

  /**
   * 获得独一无二的ID
   * @return Object
   */
  Object getID();
}

4.JDBCLogger.java

package com.benqguru.palau.log.jdbc.test;

import java.sql.*;
import java.util.*;

/**
 * 这个类封装了必须要记录进表中的逻辑,JDBCAppender使用这个类
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
public class JDBCLogger {

//  日志表的所有列
  private ArrayList logcols = null;

//  通过日志记录提供的列
  private String column_list = null;

//  所有列的数目
  private int num = 0;

//  表示configure()方法执行成功
  private boolean isconfigured = false;

//表示准备好用append()来记录日志
  private boolean ready = false;

//  当ready()方法失败的时候,将用错误字符串填充这个消息, 可以用getMsg()方法返回这个值
  private String errormsg = "";

  private Connection con = null;
  private Statement stmt = null;
  private ResultSet rs = null;
  private String table = null;

  //静态SQL语句记录日志的变量
  private String sql = null;
  private String new_sql = null;
  private String new_sql_part1 = null;
  private String new_sql_part2 = null;
  private static final String msg_wildcard = "@MSG@";
  private int msg_wildcard_pos = 0;

  /**
   * 把一条消息写进数据库表中,假如发生数据库错误,将抛出异常!
   * @param _msg String
   * @throws Exception
   */
  public void append(String _msg) throws Exception {
    if (!ready)
      if (!ready())
        throw new Exception(
            "JDBCLogger::append(), Not ready to append !");

    if (sql != null) {
      appendSQL(_msg);
      return;
    }

    LogColumn logcol;

    rs.moveToInsertRow();

    for (int i = 0; i < num; i++) {
      logcol = (LogColumn) logcols.get(i);

      if (logcol.logtype == LogType.MSG) {
        rs.updateObject(logcol.name, _msg);
      }
      else if (logcol.logtype == LogType.ID) {
        rs.updateObject(logcol.name, logcol.idhandler.getID());
      }
      else if (logcol.logtype == LogType.STATIC) {
        rs.updateObject(logcol.name, logcol.value);
      }
      else if (logcol.logtype == LogType.TIMESTAMP) {
        rs.updateObject(logcol.name,
                        new Timestamp( (new java.util.Date()).getTime()));
      }
    }

    rs.insertRow();
  }

  /**
   * 用给定的sql语句把一条消息写进数据库中.假如发生数据库错误,将抛出异常!
   * @param _msg String
   * @throws Exception
   */
  public void appendSQL(String _msg) throws Exception {
    if (!ready)if (!ready())throw new Exception(
        "JDBCLogger::appendSQL(), Not ready to append !");

    if (sql == null)throw new Exception(
        "JDBCLogger::appendSQL(), No SQL-Statement configured !");

    if (msg_wildcard_pos > 0) {
      new_sql = new_sql_part1 + _msg + new_sql_part2;
    }
    else new_sql = sql;

    try {
      stmt.executeUpdate(new_sql);
    }
    catch (Exception e) {
      errormsg = new_sql;
      throw e;
    }
  }

  /**
   * 通过读取日志表的结构来配置这个类.假如发生数据库错误,将抛出异常!
   * @param _table String
   * @throws Exception
   */
  public void configureTable(String _table) throws Exception {
    if (isconfigured)return;

    //用表列的META-informations填充日志列
    stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
                               ResultSet.CONCUR_UPDATABLE);
    rs = stmt.executeQuery("SELECT * FROM " + _table + " WHERE 1 = 2");

    LogColumn logcol;

    ResultSetMetaData rsmd = rs.getMetaData();

    num = rsmd.getColumnCount();

    logcols = new ArrayList(num);

    for (int i = 1; i <= num; i++) {
      logcol = new LogColumn();
      logcol.name = rsmd.getColumnName(i).toUpperCase();
      logcol.type = rsmd.getColumnTypeName(i);
      logcol.nullable = (rsmd.isNullable(i) == rsmd.columnNullable);
      logcol.isWritable = rsmd.isWritable(i);
      if (!logcol.isWritable) logcol.ignore = true;
      logcols.add(logcol);
    }

    table = _table;

    isconfigured = true;
  }

  /**
   * 通过保存与解析给定的sql语句,来配置这个类.假如发生数据库错误,将抛出异常!
   * @param _sql String
   * @throws Exception
   */
  public void configureSQL(String _sql) throws Exception {
    if (isconfigured)return;

    if (!isConnected())throw new Exception(
        "JDBCLogger::configureSQL(), Not connected to database !");

    if (_sql == null || _sql.trim().equals(""))throw new Exception(
        "JDBCLogger::configureSQL(), Invalid SQL-Statement !");

    sql = _sql.trim();

    stmt = con.createStatement();

    msg_wildcard_pos = sql.indexOf(msg_wildcard);

    if (msg_wildcard_pos > 0) {
      new_sql_part1 = sql.substring(0, msg_wildcard_pos - 1) + "'";
      //between the message...
      new_sql_part2 = "'" +
          sql.substring(msg_wildcard_pos + msg_wildcard.length());
    }

    isconfigured = true;
  }

  /**
   * 设置一个连接.假如连接没打开,将抛出异常!
   * @param _con Connection
   * @throws Exception
   */
  public void setConnection(Connection _con) throws Exception {
    con = _con;

    if (!isConnected())throw new Exception(
        "JDBCLogger::setConnection(), Given connection isnt connected to database !");
  }

  /**
   * 设置一个列日志类型(LogTypes)并且依赖于logtype值.假如给定的参数不正确,将抛出异常!
   * @param _name String
   * @param _logtype int
   * @param _value Object
   * @throws Exception
   */
  public void setLogType(String _name, int _logtype, Object _value) throws
      Exception {
    if (!isconfigured)throw new Exception(
        "JDBCLogger::setLogType(), Not configured !");

    //setLogType() makes only sense for further configuration of configureTable()
    if (sql != null)return;

    _name = _name.toUpperCase();

    if (_name == null || ! (_name.trim().length() > 0))throw new Exception(
        "JDBCLogger::setLogType(), Missing argument name !");
    if (!LogType.isLogType(_logtype))throw new Exception(
        "JDBCLogger::setLogType(), Invalid logtype '" + _logtype + "' !");
    //除了消息类型和空类型,其他类型的值不能为空
    if ( (_logtype != LogType.MSG && _logtype != LogType.EMPTY) && _value == null)throw new
        Exception("JDBCLogger::setLogType(), Missing argument value !");

    LogColumn logcol;
    //设置列的值的来源
    for (int i = 0; i < num; i++) {
      logcol = (LogColumn) logcols.get(i);

      if (logcol.name.equals(_name)) {
        if (!logcol.isWritable)throw new Exception(
            "JDBCLogger::setLogType(), Column " + _name + " is not writeable !");

        //Column gets the message
        if (_logtype == LogType.MSG) {
          logcol.logtype = _logtype;
          return;
        }
        //Column will be provided by JDBCIDHandler::getID()
        else if (_logtype == LogType.ID) {
          logcol.logtype = _logtype;

          try {
            //Try to cast directly Object to JDBCIDHandler
            logcol.idhandler = (JDBCIDHandler) _value;
          }
          catch (Exception e) {
            try {
              //Assuming _value is of class string which contains the classname of a JDBCIDHandler
              logcol.idhandler = (JDBCIDHandler) (Class.forName( (String)
                  _value).newInstance());
            }
            catch (Exception e2) {
              throw new Exception(
                  "JDBCLogger::setLogType(), Cannot cast value of class " +
                  _value.getClass() + " to class JDBCIDHandler !");
            }
          }

          return;
        }

        //Column will be statically defined with Object _value
        else if (_logtype == LogType.STATIC) {
          logcol.logtype = _logtype;
          logcol.value = _value;
          return;
        }

        //Column will be provided with a actually timestamp
        else if (_logtype == LogType.TIMESTAMP) {
          logcol.logtype = _logtype;
          return;
        }

        //Column will be fully ignored during process.
        //If this column is not nullable, the column has to be filled by a database trigger,
        //else a database error occurs !
        //Columns which are not nullable, but should be not filled, must be explicit assigned with LogType.EMPTY,
        //else a value is required !
        else if (_logtype == LogType.EMPTY) {
          logcol.logtype = _logtype;
          logcol.ignore = true;
          return;
        }
      }
    }
  }

  /**
   * 假如这个类的append()方法准备好了,返回true,否则返回false.
   * 若没有准备好,一个原因字符串将被保存在实例变量msg中
   * @return boolean
   */
  public boolean ready() {
    if (ready)return true;

    if (!isconfigured) {
      errormsg = "Not ready to append ! Call configure() first !";
      return false;
    }

    //No need to doing the whole rest...
    if (sql != null) {
      ready = true;
      return true;
    }

    boolean msgcol_defined = false;

    LogColumn logcol;

    for (int i = 0; i < num; i++) {
      logcol = (LogColumn) logcols.get(i);

      if (logcol.ignore || !logcol.isWritable)continue;
      if (!logcol.nullable && logcol.logtype == LogType.EMPTY) {
        errormsg = "Not ready to append ! Column " + logcol.name +
            " is not nullable, and must be specified by setLogType() !";
        return false;
      }
      if (logcol.logtype == LogType.ID && logcol.idhandler == null) {
        errormsg = "Not ready to append ! Column " + logcol.name +
            " is specified as an ID-column, and a JDBCIDHandler has to be set !";
        return false;
      }
      else if (logcol.logtype == LogType.STATIC && logcol.value == null) {
        errormsg = "Not ready to append ! Column " + logcol.name +
            " is specified as a static field, and a value has to be set !";
        return false;
      }
      else if (logcol.logtype == LogType.MSG) msgcol_defined = true;
    }

    if (!msgcol_defined)return false;

    //create the column_list
    for (int i = 0; i < num; i++) {
      logcol = (LogColumn) logcols.get(i);

      if (logcol.ignore || !logcol.isWritable)continue;

      if (logcol.logtype != LogType.EMPTY) {
        if (column_list == null) {
          column_list = logcol.name;
        }
        else column_list += ", " + logcol.name;
      }
    }

    try {
      rs = stmt.executeQuery("SELECT " + column_list + " FROM " + table +
                             " WHERE 1 = 2");
    }
    catch (Exception e) {
      errormsg = "Not ready to append ! Cannot select columns '" + column_list +
          "' of table " + table + " !";
      return false;
    }

    ready = true;

    return true;
  }

  /**
   * 假如这个已经配置好了,返回true,否则返回false
   * @return boolean
   */
  public boolean isConfigured() {
    return isconfigured;
  }

  /**
   * 假如这个连接是打开的,返回true,否则返回false
   * @return boolean
   */
  public boolean isConnected() {
    try {
      return (con != null && !con.isClosed());
    }
    catch (Exception e) {
      return false;
    }
  }

  /**
   * 返回保存在实例变量msg中的内部错误信息
   * @return String
   */
  public String getErrorMsg() {
    String r = new String(errormsg);
    errormsg = null;
    return r;
  }
}

/**
 * 这个类封装了JDBCLogger类所需要的与列相关的数据
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
class LogColumn {

  /**
   列名
   */
  String name = null;

  /**
   列类型
   */
  String type = null;

  /**
   非空(not nullability)意味着这个列是必须的
   */
  boolean nullable = false;

  /**
   isWritable意味这个列可以更新,或者列仅仅可读.
   */
  boolean isWritable = false;

  /**
   假如ignore是true,这个列将在编译sql语句的时候忽略
   */
  boolean ignore = false;

  /**
   必须用非空列填充!其他情况是可选的.
   */
  int logtype = LogType.EMPTY;

  /**
   对应于包装器类Long,String等等的通用存贮器
   */
  Object value = null;
  /**
   JDBCIDHandler接口的实例
  */
  JDBCIDHandler idhandler = null;
}

/**
 * 这个类包含所有常量,这些常量对定义一个列日志类型是必须的.
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
class LogType {

  /**
   这个类型的列将接受消息
   */
  public static final int MSG = 1;

  /**
   这个类型的列将是记录行的唯一标识符
   */
  public static final int ID = 2;

  /**
   这个类型的列将包含一个静态的一次定义(one-time-defined)的值
   */
  public static final int STATIC = 3;

  /**
   这个类型的列将用一个的时间戳填充,这个时间戳依赖于日志记录的开始时间
   */
  public static final int TIMESTAMP = 4;

  /**
   这个类型的列将不包含值,并且不包含在日志记录的插入语句.这将是一个不需要创建来填充的列,而是在别的地方.
   */
  public static final int EMPTY = 5;

  /**
   * 检测_lt是否是日志类型
   * @param _lt int
   * @return boolean
   */
  public static boolean isLogType(int _lt) {
    if (_lt == MSG || _lt == STATIC || _lt == ID || _lt == TIMESTAMP ||
        _lt == EMPTY)return true;

    return false;
  }

  /**
   * 把字符串转换为日志类型
   * @param _lt String
   * @return int
   */
  public static int parseLogType(String _lt) {
    if (_lt.equals("MSG"))return MSG;
    if (_lt.equals("ID"))return ID;
    if (_lt.equals("STATIC"))return STATIC;
    if (_lt.equals("TIMESTAMP"))return TIMESTAMP;
    if (_lt.equals("EMPTY"))return EMPTY;

    return -1;
  }
}
5.log4j.properties

#这是一个配置文件实例,PropertyConfigurator将使用这个文件 :
#声明一个appender变量名为JDBC
log4j.rootLogger=DEBUG, JDBC

#JDBC是一个JDBCAppender类,这个类可以写消息到数据库
log4j.appender.JDBC=com.benqguru.palau.log.jdbc.test.JDBCAppender

#1.连接数据库的数据库选项
log4j.appender.JDBC.url=jdbc:mysql://localhost:3306/logtest
log4j.appender.JDBC.username=root
log4j.appender.JDBC.password=

#2.指定你自己的JDBCConnectionHandler的连接器选项
log4j.appender.JDBC.connection_class=com.benqguru.palau.log.jdbc.test.MyConnectionHandler

#3.指定一个静态的SQL语句的SQL选项,这个语句将在每次消息事件发生时被执行
log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')

#4. 指定数据库中一个表的表选项。
log4j.appender.JDBC.table=logtest

#5.描述表的重要列的列选项(非空列是必须被描述的)
log4j.appender.JDBC.columns=id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~Thomas Fenner (t.fenner@klopotek.de)

#6.定义消息布局器的布局器选项(可选)
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%m

#7.定义消息事件缓冲器的大小的缓冲器选项(可选)
log4j.appender.JDBC.buffer_size=1

#8.定义自动提交的提交选项(可选)
log4j.appender.JDBC.docommit=N

##########下面是英文说明#############
#Date - %d{DATE}[slf5s.DATE]
#Priority - %p[slf5s.PRIORITY]
#NDC - %x[slf5s.NDC]
#Thread - %t[slf5s.THREAD]
#Category - %c[slf5s.CATEGORY]
#Location - %l[slf5s.LOCATION]
#Message - %m[slf5s.MESSAGE]
#
#log4j.appender.R.layout.ConversionPattern=[slf5s.start]%d{DATE}[slf5s.DATE]%n\
#   %p[slf5s.PRIORITY]%n%x[slf5s.NDC]%n%t[slf5s.THREAD]%n\
#   %c[slf5s.CATEGORY]%n%l[slf5s.LOCATION]%n%m[slf5s.MESSAGE]%n%n
##########下面是中文说明#############
#%m 输出代码中指定的消息
#%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
#%r 输出自应用启动到输出该log信息耗费的毫秒数
#%c 输出所属的类目,通常就是所在类的全名
#%t 输出产生该日志事件的线程名
#%n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
#%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,
#比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
#%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)

6.Log4JTest.java


package com.benqguru.palau.log.jdbc.test;

import java.sql.*;

import org.apache.log4j.*;

/**
 * 这是一个用配置文件来配置JDBCAppender的实例代码
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
public class Log4JTest {

  /**
   * 根日志记录器
   */
  static Logger log = Logger.getLogger(Log4JTest.class.getName());

  public static void main(String[] args) {
    try {
      Driver d = (Driver) (Class.forName("org.gjt.mm.mysql.Driver").
                           newInstance());
      DriverManager.registerDriver(d);
    }
    catch (Exception e) {}

    // Configuration with configuration-file
    PropertyConfigurator.configure(
        "E:/Log4j_src/classes/com/benqguru/palau/log/jdbc/test/log4j.properties");

    // These messages with Priority >= setted priority will be logged to the database.
    log.debug("debug"); //this not, because Priority DEBUG is less than INFO
    log.info("info");
    log.error("error");
    log.fatal("fatal");
  }
}

7.Log4JTest2.java

package com.benqguru.palau.log.jdbc.test;

import java.sql.*;

import org.apache.log4j.*;

/**
 * 这是一个没有使用配置文件配置JDBCAppender的实例代码
 * <p>Title: log4j1.2.8源码解析</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: 明基逐鹿</p>
 * @author 王建
 * @version 1.0
 */
public class Log4JTest2 {

  /**
   * 根日志记录器
   */
  static Logger log = Logger.getLogger(Log4JTest2.class.getName());

  public static void main(String[] args) {
    //返回事务的序列号
    MyIDHandler idhandler = new MyIDHandler();

    //确保已安装了所需的驱动器
    try {
      Driver d = (Driver) (Class.forName("org.gjt.mm.mysql.Driver").
                           newInstance());
      DriverManager.registerDriver(d);
    }
    catch (Exception e) {
      e.printStackTrace();
    }

    //设置消息可以被记录的优先权
    log.setLevel(Level.DEBUG);

    //创建一个JDBCAppender实例
    JDBCAppender appender = new JDBCAppender();

    //用setOption()方法设置可选项
    appender.setOption(JDBCAppender.CONNECTOR_OPTION, "com.benqguru.palau.log.jdbc.test.MyConnectionHandler");
    appender.setOption(JDBCAppender.URL_OPTION, "jdbc:mysql://localhost:3306/logtest");
    appender.setOption(JDBCAppender.USERNAME_OPTION, "root");
    appender.setOption(JDB

分享到:
评论

相关推荐

    azkaban-3.38安装包(已编译)

    log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=%d{yyyy/MM/dd HH:mm:ss.SSS Z} %p [%c{1}] [Azkaban] %m%n 5. 在azkaban-exec-server目录下...

    基于jsp的通讯录系统

    2、修改totgb/WEB-INF/classes目录下的log4j.properties文件中log4j.appender.file.File为你的log文件存放目录。 3、安装数据库:mysql -u root -p 存放目录\install\totgb.sql 3、修改数据库的连接参数;打开totgb/...

    java代码实例-日志规范史上最全java日志攻略(附教程)

    Log4J "Log4J的入门使用 Log4j的配置 输出日志文件 将日志信息存入数据库" Apache JCL "common log介绍 log中的接口" SLF4J "日志门面的使用 日志的绑定 日志的桥接 日志门面的原理" log-back "log-back的日志框架 ...

    智能开发平台 DOROODO

    log4j.appender.appender3.URL=jdbc:mysql://127.0.0.1:3306/doroodo?characterEncoding=UTF-8 ----&gt;日志数据库链接 log4j.appender.appender3.user=root ----&gt;日志数据库用户名 log4j.appender.appender3.password=...

    应用分析监控平台 闪电狗.zip

    log4j配置:log4j.appender.MongoDB.layout.ConversionPattern={"timestamp":"%d","level":"%p","className":"%c","message":"%m","pid":"%V","ip":"%I",uuid:"%X{UUID}"} 如何监控tomcat访问日志 1.先配置将...

    ibatis 开发指南(pdf)

    在 CLASSPATH 中新建log4j.properties 配置文件,内容如下: log4j.rootLogger=DEBUG, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part4

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part2

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part5

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part3

    第19章 使用log4j进行日志操作 564 19.1 log4j介绍 564 19.1.1 logger组件 564 19.1.2 appender组件 566 19.1.3 layout组件 567 19.2 使用log4j 568 19.3 log4j使用实例 572 19.4 ndc和mdc 585 19.5 小结 ...

Global site tag (gtag.js) - Google Analytics