The version of Apache log4j used by SoundHelix.
Clone
HTTPS:
git clone https://vervis.peers.community/repos/aEp6o
SSH:
git clone USERNAME@vervis.peers.community:aEp6o
Branches
Tags
- 1.3alpha-7
- CHAINSAW_2_SANDBOX_MERGE
- CORE_VERSION
- LEVEL_REPLACES_PRIORITY
- PREALPHA_1_3_AS_OF_2004_05_12
- PRE_CHAINSAW_MODEL_CONVERSION
- PRE_UGLI_MOVE
- TAG_CHAINSAW2_MOVE
- log4j-1.2.17
- log4j-1.2.17-rc1
- v1.3alpha8
- v1.3alpha8-temp
- v1_2_1
- v1_2_10-recalled
- v1_2_11
- v1_2_11_rc1
- v1_2_11rc3
- v1_2_12
- v1_2_12_rc1
- v1_2_12_rc2
- v1_2_12_rc3
- v1_2_12_rc4
- v1_2_12_rc5
- v1_2_12_rc6
- v1_2_13
- v1_2_13_rc1
- v1_2_13_rc2
- v1_2_13_site_update
- v1_2_14
- v1_2_14_maven
- v1_2_14_rc1
- v1_2_14_site_update
- v1_2_15
- v1_2_15_rc1
- v1_2_15_rc2
- v1_2_15_rc3
- v1_2_15_rc4
- v1_2_15_rc5
- v1_2_15_rc6
- v1_2_16
- v1_2_16_rc1
- v1_2_16_rc2
- v1_2_17
- v1_2_17-rc1
- v1_2_17_rc1
- v1_2_17_rc2
- v1_2_17_rc3
- v1_2_2
- v1_2_3
- v1_2_4
- v1_2_6
- v1_2_7
- v1_2_9
- v1_2_alpha0
- v1_2_alpha7
- v1_2beta1
- v1_2final
- v1_3alpha_1
- v1_3alpha_6
- v_1_0
- v_1_0_1
- v_1_0_4
- v_1_1
- v_1_1_1
- v_1_1_2
- v_1_1_3
- v_1_1_b1
- v_1_1b2
- v_1_1b3
- v_1_1b5
- v_1_1b6
- v_1_1b7
- v_1_2beta3
JDBCAppender.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*/
package com.klopotek.utils.log;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import org.apache.log4j.helpers.*;
import org.apache.log4j.spi.*;
/**
The JDBCAppender, writes messages into a database
<p><b>The JDBCAppender is configurable at runtime by setting options in two alternatives : </b></p>
<dir>
<p><b>1. Use a configuration-file</b></p>
<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>
<p><b>2. Use the methods of JDBCAppender to do it</b></p>
<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>
</dir>
<p>All available options are defined as static String-constants in JDBCAppender named xxx_OPTION.</p>
<p><b>Here is a description of all available options :</b></p>
<dir>
<p><b>1. Database-options to connect to the database</b></p>
<p>- <b>URL_OPTION</b> : a database url of the form jdbc:subprotocol:subname</p>
<p>- <b>USERNAME_OPTION</b> : the database user on whose behalf the connection is being made</p>
<p>- <b>PASSWORD_OPTION</b> : the user's password</p>
<p><b>2. Connector-option to specify your own JDBCConnectionHandler</b></p>
<p>- <b>CONNECTOR_OPTION</b> : a classname which is implementing the JDBCConnectionHandler-interface</p>
<p>This interface is used to get a customized connection.</p>
<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>
<p>Else if no database-options are given, the JDBCConnectionHandler-interface is called without them.</p>
<p>Else if this option is not defined, the database-options are required to open a connection by the JDBCAppender.</p>
<p><b>3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event</b></p>
<p>- <b>SQL_OPTION</b> : a sql-statement which will be used to write to the database</p>
<p>Use the variable <b>@MSG@</b> on a location in the statement, which has to be dynamically replaced by the message-text.</p>
<p>If you give this option, the table-option and columns-option will be ignored !</p>
<p><b>4. Table-option to specify a table contained by the database</b></p>
<p>- <b>TABLE_OPTION</b> : the table in which the logging will be done</p>
<p><b>5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)</b></p>
<p>- <b>COLUMNS_OPTION</b> : a formatted list of column-descriptions</p>
<p>Each column description consists of</p>
<dir>
<p>- the <b><i>name</i></b> of the column (required)</p>
<p>- a <b><i>logtype</i></b> which is a static constant of class LogType (required)</p>
<p>- and a <b><i>value</i></b> which depends by the LogType (optional/required, depending by logtype)</p>
</dir>
<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>
<dir>
<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>
<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>
<p>o <b>ID</b> = value must be a classname, which implements the JDBCIDHandler-interface.</p>
<p>o <b>TIMESTAMP</b> = a value will be ignored, the column will be filled with a actually timestamp with every logged message.</p>
<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>
</dir>
<p>If there are more than one column to describe, the columns must be separated by a Tabulator-delimiter (unicode0008) !</p>
<p>The arguments of a column-description must be separated by the delimiter '~' !</p>
<p><i>(Example : name1~logtype1~value1 name2~logtype2~value2...)</i></p>
<p><b>6. Layout-options to define the layout of the messages (optional)</b></p>
<p>- <b>_</b> : the layout wont be set by a xxx_OPTION</p>
<p>See the configuration-file and code examples below...</p>
<p>The default is a layout of the class {@link org.apache.log4j.PatternLayout} with the pattern=%m which representate only the message.</p>
<p><b>7. Buffer-option to define the size of the message-event-buffer (optional)</b></p>
<p>- <b>BUFFER_OPTION</b> : define how many messages will be buffered until they will be updated to the database.</p>
<p>The default is buffer=1, which will do a update with every happened message-event.</p>
<p><b>8. Commit-option to define a auto-commitment</b></p>
<p>- <b>COMMIT_OPTION</b> : define whether updated messages should be committed to the database (Y) or not (N).</p>
<p>The default is commit=Y.</p>
</dir>
<p><b>The sequence of some options is important :</b></p>
<dir>
<p><b>1. Connector-option OR/AND Database-options</b></p>
<p>Any database connection is required !</p>
<p><b>2. (Table-option AND Columns-option) OR SQL-option</b></p>
<p>Anything of that is required ! Whether where to write something OR what to write somewhere...;-)</p>
<p><b>3. All other options can be set at any time...</b></p>
<p>The other options are optional and have a default initialization, which can be customized.</p>
</dir>
<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>
<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>
<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>
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public class JDBCAppender extends AppenderSkeleton
{
/**
A database-option to to set a database url of the form jdbc:subprotocol:subname.
*/
public static final String URL_OPTION = "url";
/**
A database-option to set the database user on whose behalf the connection is being made.
*/
public static final String USERNAME_OPTION = "username";
/**
A database-option to set the user's password.
*/
public static final String PASSWORD_OPTION = "password";
/**
A table-option to specify a table contained by the database
*/
public static final String TABLE_OPTION = "table";
/**
A connector-option to specify your own JDBCConnectionHandler
*/
public static final String CONNECTOR_OPTION = "connector";
/**
A columns-option to describe the important columns of the table
*/
public static final String COLUMNS_OPTION = "columns";
/**
A sql-option to specify a static sql-statement which will be performed with every occuring message-event
*/
public static final String SQL_OPTION = "sql";
/**
A buffer-option to define the size of the message-event-buffer
*/
public static final String BUFFER_OPTION = "buffer";
/**
A commit-option to define a auto-commitment
*/
public static final String COMMIT_OPTION = "commit";
//Variables to store the options values setted by 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;
private boolean docommit = true;
private int buffer_size = 1;
private JDBCConnectionHandler connectionHandler = null;
//This buffer stores message-events.
//When the buffer_size is reached, the buffer will be flushed and the messages will updated to the database.
private ArrayList buffer = new ArrayList();
//Database-connection
private Connection con = null;
//This class encapsulate the logic which is necessary to log into a table
private JDBCLogger jlogger = new JDBCLogger();
//Flags :
//A flag to indicate a established database connection
private boolean connected = false;
//A flag to indicate configuration status
private boolean configured = false;
//A flag to indicate that everything is ready to get append()-commands.
private boolean ready = false;
/**
If program terminates close the database-connection and flush the buffer
*/
public void finalize()
{
close();
super.finalize();
}
/**
Internal method. Returns a array of strings containing the available options which can be set with method setOption()
*/
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};
}
/**
Sets all necessary options
*/
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))
{
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 = _value;
}
else if(_option.equals(TABLE_OPTION))
{
if(sql != null) return;
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();
}
}
/**
Internal method. Returns true, you may define your own layout...
*/
public boolean requiresLayout()
{
return true;
}
/**
Internal method. Close the database connection & flush the buffer.
*/
public void close()
{
flush_buffer();
if(connection_class == null)
{
try{con.close();}catch(Exception e){errorHandler.error("JDBCAppender::close(), " + e);}
}
this.closed = true;
}
/**
You have to call this function for all provided columns of your log-table !
*/
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;
}
/**
Internal method. Appends the message to the database table.
*/
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();
}
/**
Internal method. Flushes the 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;
}
}
/**
Internal method. Returns true, when the JDBCAppender is ready to append messages to the database, else false.
*/
public boolean ready()
{
if(ready) return true;
if(!configured) return false;
ready = jlogger.ready();
if(!ready){errorHandler.error(jlogger.getErrorMsg());}
return ready;
}
/**
Internal method. Connect to the database.
*/
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);
}
connected = true;
}
/**
Internal method. Configures for appending...
*/
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;
}
}
/**
This is a default JDBCConnectionHandler used by JDBCAppender
*/
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(false);
}
catch(Exception e){}
return con;
}
}