`
wangjian5748
  • 浏览: 209446 次
  • 性别: 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

分享到:
评论

相关推荐

    C#ASP.NET网络进销存管理系统源码数据库 SQL2008源码类型 WebForm

    ASP.NET网络进销存管理系统源码 内含一些新技术的使用,使用的是VS .NET 2008平台采用标准的三层架构设计,采用流行的AJAX技术 使操作更加流畅,统计报表使用FLASH插件美观大方专业。适合二次开发类似项目使用,可以节省您 开发项目周期,源码统计报表部分需要自己将正常功能注释掉的源码手工取消掉注释。这是我在调试程 序时留下的。也是上传源码前的疏忽。 您下载后可以用VS2008直接打开将注释取消掉即可正常使用。 技术特点:1、采用目前最流行的.net技术实现。2、采用B/S架构,三层无限量客户端。 3、配合SQLServer2005数据库支持 4、可实现跨越地域和城市间的系统应用。 5、二级审批机制,简单快速准确。 6、销售功能手写AJAX无刷新,快速稳定。 7、统计报表采用Flash插件美观大方。8、模板式开发,能够快速进行二次开发。权限、程序页面、 基础资料部分通过后台数据库直接维护,可单独拿出继续开发其他系统 9、数据字典,模块架构图,登录页面和主页的logo图片 分别放在DOC PSD 文件夹中

    (源码)基于ZooKeeper的分布式服务管理系统.zip

    # 基于ZooKeeper的分布式服务管理系统 ## 项目简介 本项目是一个基于ZooKeeper的分布式服务管理系统,旨在通过ZooKeeper的协调服务功能,实现分布式环境下的服务注册、发现、配置管理以及分布式锁等功能。项目涵盖了从ZooKeeper的基本操作到实际应用场景的实现,如分布式锁、商品秒杀等。 ## 项目的主要特性和功能 1. 服务注册与发现通过ZooKeeper实现服务的动态注册与发现,支持服务的动态上下线。 2. 分布式锁利用ZooKeeper的临时顺序节点特性,实现高效的分布式锁机制,避免传统锁机制中的“羊群效应”。 3. 统一配置管理通过ZooKeeper集中管理分布式系统的配置信息,实现配置的动态更新和实时同步。 4. 商品秒杀系统结合分布式锁和ZooKeeper的监听机制,实现高并发的商品秒杀功能,确保库存的一致性和操作的原子性。 ## 安装使用步骤 1. 环境准备

    23python3项目.zip

    23python3项目

    技术资料分享AL422B很好的技术资料.zip

    技术资料分享AL422B很好的技术资料.zip

    c语言俄罗斯方块.rar

    c语言俄罗斯方块

    【CPO栅格地图】基于matlab豪猪算法CPO栅格地图路径规划(目标函数:最短距离)【含Matlab源码 9152期】.mp4

    Matlab领域上传的视频均有对应的完整代码,皆可运行,亲测可用,适合小白; 1、代码压缩包内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作

    delphi人才信息管理系统.zip

    delphi人才信息管理系统

    安卓巴士总结了近百个Android优秀开源项.zip

    安卓巴士总结了近百个Android优秀开源项

    MATLAB蒙特卡洛仿真计算投资组合的VaR(Value at Risk )

    1.程序功能说明: MATLAB蒙特卡洛仿真计算投资组合的VaR(Value at Risk ) 根据协方差矩阵等参数先计算涨期权和看跌期权的价格,再采用蒙特卡洛模拟法计算该投资组合的VaR。 2.代码说明:注释清晰,参数和变量说明清晰,方便更改、方便初学者使用,模块化编程,方便替换目标函数。运行环境Windows7及以上操作系统,MATLAB2014a及以上版本。 3.适用专业:计算机、电子信息工程、数学、物理、机械工程、土木工程等专业的大学生、研究生毕业设计,各类专业的课程设计、海外留学生作业等。 4作者介绍:资深算法工程师, 从事Matlab、Python算法仿真工作15年,专业研究遗传算法、粒子群算法、蚁群算法、鲸鱼算法、狼群算法等. 有问题联系QQ: 1579325979

    SpringBoot - Async异步处理

    SpringBoot - Async异步处理

    完全退出程序的方法.zip

    完全退出程序的方法

    hutool工具操作excel表格的依赖jar包

    hutool.all操作excel的依赖包,一共18个jar包,懒得去排查包的可以直接下载直接导入使用;我用eclipse和ideal接导入外部jar包。亲测可用;如果工程里面有相同的jar包,保留版本号高的; commons-cli-1.0.jar commons-codec-1.15.jar commons-collections4-4.4.jar commons-compress-1.21.jar commons-io-2.11.0.jar commons-lang3-3.4.jar commons-logging-api-1.1.jar commons-math3-3.6.1.jar hutool-all-5.8.21.jar log4j-1.2.12.jar log4j-api-2.20.0.jar log4j-core-2.20.0.jar poi-5.2.3.jar poi-ooxml-5.2.3.jar ……

    (源码)基于Python的微信签到管理系统.zip

    # 基于Python的微信签到管理系统 ## 项目简介 本项目是一个基于Python的微信签到管理系统,旨在通过微信自动收集和管理学生的宿舍签到信息。系统通过微信的群聊功能,自动识别并记录符合特定格式的签到信息,并将其存储在Excel表格中。该系统特别适用于学校或组织需要定期收集学生签到信息的场景。 ## 项目的主要特性和功能 1. 自动签到信息收集系统能够自动检测微信中大于三人的群聊,识别并记录符合特定格式的签到信息。 2. 灵活的签到格式支持自定义签到格式,通过正则表达式匹配签到信息,确保信息的准确性和一致性。 3. 自动生成签到表格每周自动生成新的签到表格,并根据开学周数计算当前学期的周数。 4. 转发补录功能支持通过转发信息的方式补录错过的签到信息,提高信息的完整性。 5. 路径和模板配置允许用户自定义签到表格的存储路径和模板信息,方便管理和维护。 ## 安装使用步骤 ### 1. 环境搭建

    (源码)基于Arduino和OpenWeatherMap的天气LED显示系统.zip

    # 基于Arduino和OpenWeatherMap的天气LED显示系统 ## 项目简介 此项目是一个基于天气数据的LED显示系统,主要用于展示当前的天气状况。它使用了WiFi连接的Arduino和LED灯带,通过OpenWeatherMap提供的API获取天气数据。项目旨在提高技术技能和软技能模块的学习成果。 ## 项目的主要特性和功能 ### 天气数据的展示 实时天气展示系统可以展示不同的天气状况,如晴朗、多云、降雨、雷电、雾等。 地点设置用户可以通过Blynk应用设置想要展示的地点,并实时获取该地点的天气数据。 更新间隔设置用户还可以设置数据更新的时间间隔。 ### 动画展示功能 天气模拟动画系统具有动画展示功能,用户可以选择不同的动画模式,如天气模拟动画。 LED颜色变换支持单个LED灯的颜色变换以及色彩淡入淡出动画等。 自动切换动画模式可以手动选择或通过定时设置自动切换。 ## 安装和使用步骤

    将jar文件转换为exe

    将jar文件转换为exe

    trumtime安装所需的全部资源以及步骤

    tdm64-gcc-9.2.0是一个编译器,如果大家在安装truetime运行中无法直接下载官方的编译器,就参考配置方法文档用这个编译器 大家需要首先安装vc++,可参考安装VScode 2019 版本

    SAE AS5756C-2021 中文.pdf

    SAE AS5756C-2021 中文.pdf

    c语言实现水波纹显示效果.rar

    c语言实现水波纹显示效果

    基于C语言的winform界面火车票订票系统项目资源.zip

    基于C语言的winform界面火车票订票系统项目资源

    2024年企业竞争模拟大赛河南大学校赛的通知(1) (1).pdf

    2024年企业竞争模拟大赛河南大学校赛的通知(1) (1).pdf

Global site tag (gtag.js) - Google Analytics