Home » apache-log4j-1.2.15 » com.klopotek.utils » log » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   /*
   19    * Copyright (C) The Apache Software Foundation. All rights reserved.
   20   */
   21   
   22   package com.klopotek.utils.log;
   23   
   24   import java.sql;
   25   import java.util;
   26   import org.apache.log4j;
   27   import org.apache.log4j.helpers;
   28   import org.apache.log4j.spi;
   29   
   30   /**
   31   The JDBCAppender, writes messages into a database
   32   
   33   <p><b>The JDBCAppender is configurable at runtime by setting options in two alternatives : </b></p>
   34   <dir>
   35   	<p><b>1. Use a configuration-file</b></p>
   36   	<p>Define the options in a file (<A HREF="configfile_example.txt">example</A>) and call a <code>PropertyConfigurator.configure(filename)</code> in your code.</p>
   37   	<p><b>2. Use the methods of JDBCAppender to do it</b></p>
   38   	<p>Call <code>JDBCAppender::setOption(JDBCAppender.xxx_OPTION, String value)</code> to do it analogically without a configuration-file (<A HREF="code_example2.java">example</A>)</p>
   39   </dir>
   40   
   41   <p>All available options are defined as static String-constants in JDBCAppender named xxx_OPTION.</p>
   42   
   43   <p><b>Here is a description of all available options :</b></p>
   44   <dir>
   45   	<p><b>1. Database-options to connect to the database</b></p>
   46   	<p>- <b>URL_OPTION</b>			: a database url of the form jdbc:subprotocol:subname</p>
   47   	<p>- <b>USERNAME_OPTION</b>	: the database user on whose behalf the connection is being made</p>
   48   	<p>- <b>PASSWORD_OPTION</b>	: the user's password</p>
   49   
   50   	<p><b>2. Connector-option to specify your own JDBCConnectionHandler</b></p>
   51   	<p>- <b>CONNECTOR_OPTION</b>	: a classname which is implementing the JDBCConnectionHandler-interface</p>
   52   	<p>This interface is used to get a customized connection.</p>
   53   	<p>If in addition the database-options are given, these options will be used as arguments for the JDBCConnectionHandler-interface to get a connection.</p>
   54   	<p>Else if no database-options are given, the JDBCConnectionHandler-interface is called without them.</p>
   55   	<p>Else if this option is not defined, the database-options are required to open a connection by the JDBCAppender.</p>
   56   
   57   	<p><b>3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event</b></p>
   58   	<p>- <b>SQL_OPTION</b>			: a sql-statement which will be used to write to the database</p>
   59   	<p>Use the variable <b>@MSG@</b> on a location in the statement, which has to be dynamically replaced by the message-text.</p>
   60   	<p>If you give this option, the table-option and columns-option will be ignored !</p>
   61   
   62   	<p><b>4. Table-option to specify a table contained by the database</b></p>
   63   	<p>- <b>TABLE_OPTION</b>		: the table in which the logging will be done</p>
   64   
   65   	<p><b>5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)</b></p>
   66   	<p>- <b>COLUMNS_OPTION</b>		: a formatted list of column-descriptions</p>
   67   	<p>Each column description consists of</p>
   68   	<dir>
   69   		<p>- the <b><i>name</i></b> of the column (required)</p>
   70   		<p>- a <b><i>logtype</i></b> which is a static constant of class LogType (required)</p>
   71   		<p>- and a <b><i>value</i></b> which depends by the LogType (optional/required, depending by logtype)</p>
   72   	</dir>
   73   	<p>Here is a description of the available logtypes of class <b>{@link LogType}</b> and how to handle the <b><i>value</i></b>:</p>
   74   	<dir>
   75   		<p>o <b>MSG</b>			= a value will be ignored, the column will get the message. (One columns need to be of this type!)</p>
   76   		<p>o <b>STATIC</b>		= the value will be filled into the column with every logged message. (Ensure that the type of value can be casted into the sql-type of the column!)</p>
   77   		<p>o <b>ID</b>			= value must be a classname, which implements the JDBCIDHandler-interface.</p>
   78   		<p>o <b>TIMESTAMP</b>	= a value will be ignored, the column will be filled with a actually timestamp with every logged message.</p>
   79   		<p>o <b>EMPTY</b>		= a value will be ignored, the column will be ignored when writing to the database (Ensure to fill not nullable columns by a database trigger!)</p>
   80   	</dir>
   81   	<p>If there are more than one column to describe, the columns must be separated by a Tabulator-delimiter (unicode0008) !</p>
   82   	<p>The arguments of a column-description must be separated by the delimiter '~' !</p>
   83   	<p><i>(Example :  name1~logtype1~value1   name2~logtype2~value2...)</i></p>
   84   
   85   	<p><b>6. Layout-options to define the layout of the messages (optional)</b></p>
   86   	<p>- <b>_</b> : the layout wont be set by a xxx_OPTION</p>
   87   	<p>See the configuration-file and code examples below...</p>
   88   	<p>The default is a layout of the class {@link org.apache.log4j.PatternLayout} with the pattern=%m which representate only the message.</p>
   89   
   90   	<p><b>7. Buffer-option to define the size of the message-event-buffer (optional)</b></p>
   91   	<p>- <b>BUFFER_OPTION</b>		: define how many messages will be buffered until they will be updated to the database.</p>
   92   	<p>The default is buffer=1, which will do a update with every happened message-event.</p>
   93   
   94   	<p><b>8. Commit-option to define a auto-commitment</b></p>
   95   	<p>- <b>COMMIT_OPTION</b>		: define whether updated messages should be committed to the database (Y) or not (N).</p>
   96   	<p>The default is commit=Y.</p>
   97   </dir>
   98   
   99   <p><b>The sequence of some options is important :</b></p>
  100   <dir>
  101   	<p><b>1. Connector-option OR/AND Database-options</b></p>
  102   	<p>Any database connection is required !</p>
  103   	<p><b>2. (Table-option AND Columns-option) OR SQL-option</b></p>
  104   	<p>Anything of that is required ! Whether where to write something OR what to write somewhere...;-)</p>
  105   	<p><b>3. All other options can be set at any time...</b></p>
  106   	<p>The other options are optional and have a default initialization, which can be customized.</p>
  107   </dir>
  108   
  109   <p><b>Here is a <b>configuration-file example</b>, which can be used as argument for the <b>PropertyConfigurator</b> : </b><A HREF="configfile_example.txt"> configfile_example.txt</A></p>
  110   
  111   <p><b>Here is a <b>code-example</b> to configure the JDBCAppender <b>with a configuration-file</b> : </b><A HREF="code_example1.java"> code_example1.java</A></p>
  112   
  113   <p><b>Here is a <b>another code-example</b> to configure the JDBCAppender <b>without a configuration-file</b> : </b><A HREF="code_example2.java"> code_example2.java</A></p>
  114   
  115   
  116   
  117   <p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
  118   
  119   @since 1.0
  120   */
  121   public class JDBCAppender extends AppenderSkeleton
  122   {
  123   	/**
  124   	A database-option to to set a database url of the form jdbc:subprotocol:subname.
  125   	*/
  126   	public static final String URL_OPTION			= "url";
  127   
  128   	/**
  129   	A database-option to set the database user on whose behalf the connection is being made.
  130   	*/
  131   	public static final String USERNAME_OPTION	= "username";
  132   
  133   	/**
  134   	A database-option to set the user's password.
  135   	*/
  136   	public static final String PASSWORD_OPTION	= "password";
  137   
  138   	/**
  139   	A table-option to specify a table contained by the database
  140   	*/
  141   	public static final String TABLE_OPTION		= "table";
  142   
  143   	/**
  144   	A connector-option to specify your own JDBCConnectionHandler
  145   	*/
  146   	public static final String CONNECTOR_OPTION	= "connector";
  147   
  148   	/**
  149      A columns-option to describe the important columns of the table
  150   	*/
  151   	public static final String COLUMNS_OPTION		= "columns";
  152   
  153   	/**
  154   	A sql-option to specify a static sql-statement which will be performed with every occuring message-event
  155      */
  156   	public static final String SQL_OPTION			= "sql";
  157   
  158   	/**
  159      A buffer-option to define the size of the message-event-buffer
  160   	*/
  161   	public static final String BUFFER_OPTION		= "buffer";
  162   
  163   	/**
  164      A commit-option to define a auto-commitment
  165   	*/
  166   	public static final String COMMIT_OPTION		= "commit";
  167   
  168   
  169   	//Variables to store the options values setted by setOption() :
  170   	private String url		= null;
  171   	private String username	= null;
  172   	private String password	= null;
  173   	private String table		= null;
  174   	private String connection_class = null;
  175   	private String sql		= null;
  176   	private boolean docommit = true;
  177   	private int buffer_size	= 1;
  178      private JDBCConnectionHandler connectionHandler = null;
  179   
  180   	//This buffer stores message-events.
  181      //When the buffer_size is reached, the buffer will be flushed and the messages will updated to the database.
  182   	private ArrayList buffer = new ArrayList();
  183   
  184      //Database-connection
  185   	private Connection con = null;
  186   
  187   	//This class encapsulate the logic which is necessary to log into a table
  188   	private JDBCLogger jlogger = new JDBCLogger();
  189   
  190      //Flags :
  191      //A flag to indicate a established database connection
  192   	private boolean connected = false;
  193      //A flag to indicate configuration status
  194   	private boolean configured = false;
  195      //A flag to indicate that everything is ready to get append()-commands.
  196   	private boolean ready = false;
  197   
  198   	/**
  199   	If program terminates close the database-connection and flush the buffer
  200      */
  201   	public void finalize()
  202   	{
  203   		close();
  204         super.finalize();
  205   	}
  206   
  207   	/**
  208   	Internal method. Returns a array of strings containing the available options which can be set with method setOption()
  209   	*/
  210   	public String[] getOptionStrings()
  211      {
  212      	// The sequence of options in this string is important, because setOption() is called this way ...
  213   		return new String[]{CONNECTOR_OPTION, URL_OPTION, USERNAME_OPTION, PASSWORD_OPTION, SQL_OPTION, TABLE_OPTION, COLUMNS_OPTION, BUFFER_OPTION, COMMIT_OPTION};
  214   	}
  215   
  216   
  217   	/**
  218        Sets all necessary options
  219   	*/
  220   	public void setOption(String _option, String _value)
  221   	{
  222      	_option = _option.trim();
  223         _value = _value.trim();
  224   
  225   		if(_option == null || _value == null) return;
  226   		if(_option.length() == 0 || _value.length() == 0) return;
  227   
  228         _value = _value.trim();
  229   
  230   		if(_option.equals(CONNECTOR_OPTION))
  231         {
  232         	if(!connected) connection_class = _value;
  233         }
  234   		else if(_option.equals(URL_OPTION))
  235   		{
  236   			if(!connected) url = _value;
  237   		}
  238   		else if(_option.equals(USERNAME_OPTION))
  239   		{
  240   			if(!connected) username = _value;
  241   		}
  242   		else if(_option.equals(PASSWORD_OPTION))
  243   		{
  244   			if(!connected) password = _value;
  245   		}
  246   		else if(_option.equals(SQL_OPTION))
  247         {
  248   			sql = _value;
  249         }
  250   		else if(_option.equals(TABLE_OPTION))
  251         {
  252         	if(sql != null) return;
  253         	table = _value;
  254         }
  255   		else if(_option.equals(COLUMNS_OPTION))
  256         {
  257         	if(sql != null) return;
  258   
  259   			String name = null;
  260            int logtype = -1;
  261            String value = null;
  262            String column = null;
  263            String arg = null;
  264            int num_args = 0;
  265            int num_columns = 0;
  266   			StringTokenizer st_col;
  267   			StringTokenizer st_arg;
  268   
  269            //Columns are TAB-separated
  270   			st_col = new StringTokenizer(_value,  "	");
  271   
  272   			num_columns = st_col.countTokens();
  273   
  274            if(num_columns < 1)
  275     	      {
  276        	   	errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " + _value + " !");
  277               return;
  278           	}
  279   
  280            for(int i=1; i<=num_columns; i++)
  281            {
  282   				column = st_col.nextToken();
  283   
  284               //Arguments are ~-separated
  285   				st_arg = new StringTokenizer(column, "~");
  286   
  287   				num_args = st_arg.countTokens();
  288   
  289   	         if(num_args < 2)
  290      	      {
  291         	   	errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " + _value + " !");
  292                  return;
  293            	}
  294   
  295   	         for(int j=1; j<=num_args; j++)
  296      	      {
  297   					arg = st_arg.nextToken();
  298   
  299   					if(j == 1) name = arg;
  300   					else if(j == 2)
  301         	      {
  302            	   	try
  303               	   {
  304   							logtype = Integer.parseInt(arg);
  305   	               }
  306      	            catch(Exception e)
  307         	         {
  308            	      	logtype = LogType.parseLogType(arg);
  309   	               }
  310   
  311   						if(!LogType.isLogType(logtype))
  312      	            {
  313   	   	            errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION LogType : " + arg + " !");
  314                        return;
  315            	      }
  316               	}
  317   					else if(j == 3) value = arg;
  318      	      }
  319   
  320   	         if(!setLogType(name, logtype, value)) return;
  321            }
  322         }
  323   		else if(_option.equals(BUFFER_OPTION))
  324         {
  325           	try
  326            {
  327   				buffer_size = Integer.parseInt(_value);
  328            }
  329            catch(Exception e)
  330            {
  331   	         errorHandler.error("JDBCAppender::setOption(), Invalid BUFFER_OPTION value : " + _value + " !");
  332   				return;
  333            }
  334         }
  335   		else if(_option.equals(COMMIT_OPTION))
  336         {
  337         	docommit = _value.equals("Y");
  338         }
  339   
  340         if(_option.equals(SQL_OPTION) || _option.equals(TABLE_OPTION))
  341         {
  342   			if(!configured) configure();
  343         }
  344   	}
  345   
  346   	/**
  347   	Internal method. Returns true, you may define your own layout...
  348   	*/
  349   	public boolean requiresLayout()
  350   	{
  351   		return true;
  352   	}
  353   
  354   
  355   	/**
  356   	Internal method. Close the database connection & flush the buffer.
  357   	*/
  358   	public void close()
  359   	{
  360   	   flush_buffer();
  361         if(connection_class == null)
  362         {
  363   			try{con.close();}catch(Exception e){errorHandler.error("JDBCAppender::close(), " + e);}
  364         }
  365   		this.closed = true;
  366   	}
  367   
  368   
  369   	/**
  370   	You have to call this function for all provided columns of your log-table !
  371      */
  372   	public boolean setLogType(String _name, int _logtype, Object _value)
  373   	{
  374      	if(sql != null) return true;
  375   
  376   		if(!configured)
  377   		{
  378   			if(!configure()) return false;
  379   		}
  380   
  381   		try
  382   		{
  383   			jlogger.setLogType(_name, _logtype, _value);
  384   		}
  385   		catch(Exception e)
  386   		{
  387   			errorHandler.error("JDBCAppender::setLogType(), " + e);
  388   			return false;
  389   		}
  390   
  391   		return true;
  392   	}
  393   
  394   
  395   	/**
  396   	Internal method. Appends the message to the database table.
  397   	*/
  398   	public void append(LoggingEvent event)
  399   	{
  400   		if(!ready)
  401         {
  402         	if(!ready())
  403            {
  404   				errorHandler.error("JDBCAppender::append(), Not ready to append !");
  405            	return;
  406   			}
  407         }
  408   
  409   		buffer.add(event);
  410   
  411   		if(buffer.size() >= buffer_size) flush_buffer();
  412   	}
  413   
  414   
  415   	/**
  416   	Internal method. Flushes the buffer.
  417   	*/
  418      public void flush_buffer()
  419      {
  420      	try
  421         {
  422         	int size = buffer.size();
  423   
  424            if(size < 1) return;
  425   
  426           	for(int i=0; i<size; i++)
  427            {
  428   				LoggingEvent event = (LoggingEvent)buffer.get(i);
  429   
  430   				//Insert message into database
  431   				jlogger.append(layout.format(event));
  432            }
  433   
  434            buffer.clear();
  435   
  436   			if(docommit) con.commit();
  437         }
  438   		catch(Exception e)
  439   		{
  440   			errorHandler.error("JDBCAppender::flush_buffer(), " + e + " : " + jlogger.getErrorMsg());
  441   			try{con.rollback();} catch(Exception ex){}
  442   			return;
  443   		}
  444      }
  445   
  446   
  447   	/**
  448   	Internal method. Returns true, when the JDBCAppender is ready to append messages to the database, else false.
  449   	*/
  450   	public boolean ready()
  451   	{
  452      	if(ready) return true;
  453   
  454   		if(!configured) return false;
  455   
  456   		ready = jlogger.ready();
  457   
  458         if(!ready){errorHandler.error(jlogger.getErrorMsg());}
  459   
  460         return ready;
  461   	}
  462   
  463   
  464   	/**
  465   	Internal method. Connect to the database.
  466   	*/
  467   	protected void connect() throws Exception
  468   	{
  469      	if(connected) return;
  470   
  471   		try
  472   		{
  473         	if(connection_class == null)
  474            {
  475   				if(url == null)		throw new Exception("JDBCAppender::connect(), No URL defined.");
  476   
  477   				if(username == null)	throw new Exception("JDBCAppender::connect(), No USERNAME defined.");
  478   
  479   				if(password == null)	throw new Exception("JDBCAppender::connect(), No PASSWORD defined.");
  480   
  481   				connectionHandler = new DefaultConnectionHandler();
  482   			}
  483            else
  484            {
  485   				connectionHandler = (JDBCConnectionHandler)(Class.forName(connection_class).newInstance());
  486            }
  487   
  488            if(url != null && username != null && password != null)
  489            {
  490   				con = connectionHandler.getConnection(url, username, password);
  491            }
  492            else
  493            {
  494   	     		con = connectionHandler.getConnection();
  495            }
  496   
  497            if(con.isClosed())
  498            {
  499            	throw new Exception("JDBCAppender::connect(), JDBCConnectionHandler returns no connected Connection !");
  500   			}
  501   		}
  502   		catch(Exception e)
  503   		{
  504   			throw new Exception("JDBCAppender::connect(), " + e);
  505   		}
  506   
  507         connected = true;
  508   	}
  509   
  510   	/**
  511   	Internal method. Configures for appending...
  512   	*/
  513   	protected boolean configure()
  514   	{
  515   		if(configured) return true;
  516   
  517   		if(!connected)
  518   		{
  519         	if((connection_class == null) && (url == null || username == null || password == null))
  520   			{
  521   				errorHandler.error("JDBCAppender::configure(), Missing database-options or connector-option !");
  522   				return false;
  523            }
  524   
  525            try
  526            {
  527   				connect();
  528            }
  529            catch(Exception e)
  530            {
  531            	connection_class = null;
  532               url = null;
  533   				errorHandler.error("JDBCAppender::configure(), " + e);
  534               return false;
  535            }
  536   		}
  537   
  538   		if(sql == null && table == null)
  539   		{
  540   			errorHandler.error("JDBCAppender::configure(), No SQL_OPTION or TABLE_OPTION given !");
  541   			return false;
  542   		}
  543   
  544   		if(!jlogger.isConfigured())
  545   		{
  546   			try
  547            {
  548            	jlogger.setConnection(con);
  549   
  550            	if(sql == null)
  551               {
  552   	         	jlogger.configureTable(table);
  553               }
  554               else jlogger.configureSQL(sql);
  555            }
  556            catch(Exception e)
  557            {
  558   	         errorHandler.error("JDBCAppender::configure(), " + e);
  559            	return false;
  560            }
  561   		}
  562   
  563         //Default Message-Layout
  564         if(layout == null)
  565         {
  566         	layout = new PatternLayout("%m");
  567         }
  568   
  569         configured = true;
  570   
  571   		return true;
  572   	}
  573   }
  574   
  575   /**
  576   This is a default JDBCConnectionHandler used by JDBCAppender
  577   */
  578   class DefaultConnectionHandler implements JDBCConnectionHandler
  579   {
  580   	Connection con = null;
  581   
  582      public Connection getConnection()
  583      {
  584      	return con;
  585      }
  586   
  587      public Connection getConnection(String _url, String _username, String _password)
  588      {
  589      	try
  590         {
  591      		if(con != null && !con.isClosed()) con.close();
  592   			con = DriverManager.getConnection(_url, _username, _password);
  593   			con.setAutoCommit(false);
  594         }
  595         catch(Exception e){}
  596   
  597      	return con;
  598      }
  599   }
  600   
  601   
  602   
  603   
  604   
  605   

Save This Page
Home » apache-log4j-1.2.15 » com.klopotek.utils » log » [javadoc | source]