You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-cvs@jakarta.apache.org by ce...@apache.org on 2001/02/16 13:00:31 UTC
cvs commit: jakarta-log4j/contribs/ThomasFenner JDBCAppender.java JDBCConnectionHandler.java JDBCIDHandler.java JDBCLogger.java Log4JTest.java code_example1.java code_example2.java configfile_example.txt
ceki 01/02/16 04:00:29
Modified: contribs CONTENTS
Added: contribs/CekiGulcu Transform.java
contribs/ThomasFenner JDBCAppender.java
JDBCConnectionHandler.java JDBCIDHandler.java
JDBCLogger.java Log4JTest.java code_example1.java
code_example2.java configfile_example.txt
Log:
Added Thomas Fenner's JDBCAppender and moved Transform.java to contribs/CekiGulcu.
Revision Changes Path
1.2 +26 -0 jakarta-log4j/contribs/CONTENTS
Index: CONTENTS
===================================================================
RCS file: /home/cvs/jakarta-log4j/contribs/CONTENTS,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- CONTENTS 2001/02/14 17:46:57 1.1
+++ CONTENTS 2001/02/16 12:00:22 1.2
@@ -1,4 +1,9 @@
+----------------------------------------------------------------------
+WARNING: The contents of the contribs/ directory is not guaranteed to
+work properly. Some files might even not compile.
+----------------------------------------------------------------------
+
Each directory corresponds to the name of an author containinghis/her
contributions.
@@ -30,6 +35,13 @@
to be displayed in the logging output. This is important for us as we are
using a single logging server with several clients.
+KevinSteppe/
+===========
+
+ JDBCAppender.java
+ JDBCTest.java
+
+
KitchingSimon/
=============
@@ -45,4 +57,18 @@
(a) the UDP server application does not have to be in java
(b) non-java clients can send messages to the same UDP server.
+ThomasFenner/
+============
+
+ Yet one more JDBCAppender.
+
+ JDBCAppender.java
+ JDBCConnectionHandler.java
+ JDBCIDHandler.java
+ JDBCLogger.java
+ Log4JTest.java
+ code_example1.java
+ code_example2.java
+ configfile_example.txt
+
\ No newline at end of file
1.1 jakarta-log4j/contribs/CekiGulcu/Transform.java
Index: Transform.java
===================================================================
package org.apache.log4j.xml;
import org.apache.log4j.Category;
import org.apache.log4j.Layout;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.helpers.DateLayout;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.SAXException;
import org.apache.xerces.parsers.SAXParser;
import org.apache.trax.Processor;
import org.apache.trax.TemplatesBuilder;
import org.apache.trax.Templates;
import org.apache.trax.Transformer;
import org.apache.trax.Result;
import org.apache.trax.ProcessorException;
import org.apache.trax.ProcessorFactoryException;
import org.apache.trax.TransformException;
import org.apache.serialize.SerializerFactory;
import org.apache.serialize.Serializer;
import org.apache.serialize.OutputFormat;
import org.xml.sax.helpers.AttributesImpl;
import java.io.FileOutputStream;
import java.io.IOException;
public class Transform {
public static void main(String[] args) throws Exception {
PropertyConfigurator.disableAll();
PropertyConfigurator.configure("x.lcf");
// I. Instantiate a stylesheet processor.
Processor processor = Processor.newInstance("xslt");
// II. Process the stylesheet. producing a Templates object.
// Get the XMLReader.
XMLReader reader = XMLReaderFactory.createXMLReader();
// Set the ContentHandler.
TemplatesBuilder templatesBuilder = processor.getTemplatesBuilder();
reader.setContentHandler(templatesBuilder);
// Set the ContentHandler to also function as a LexicalHandler, which
// includes "lexical" (e.g., comments and CDATA) events. The Xalan
// TemplatesBuilder -- org.apache.xalan.processor.StylesheetHandler -- is
// also a LexicalHandler).
if(templatesBuilder instanceof LexicalHandler) {
reader.setProperty("http://xml.org/sax/properties/lexical-handler",
templatesBuilder);
}
// Parse the stylesheet.
reader.parse(args[0]);
//Get the Templates object from the ContentHandler.
Templates templates = templatesBuilder.getTemplates();
// III. Use the Templates object to instantiate a Transformer.
Transformer transformer = templates.newTransformer();
// IV. Perform the transformation.
// Set up the ContentHandler for the output.
FileOutputStream fos = new FileOutputStream(args[2]);
Result result = new Result(fos);
Serializer serializer = SerializerFactory.getSerializer("xml");
serializer.setOutputStream(fos);
transformer.setContentHandler(serializer.asContentHandler());
// Set up the ContentHandler for the input.
org.xml.sax.ContentHandler chandler = transformer.getInputContentHandler();
DC dc = new DC(chandler);
reader.setContentHandler(dc);
if(chandler instanceof LexicalHandler) {
reader.setProperty("http://xml.org/sax/properties/lexical-handler",
chandler);
} else {
reader.setProperty("http://xml.org/sax/properties/lexical-handler",
null);
}
// Parse the XML input document. The input ContentHandler and
// output ContentHandler work in separate threads to optimize
// performance.
reader.parse(args[1]);
}
}
class DC implements ContentHandler {
static Category cat = Category.getInstance("DC");
ContentHandler chandler;
DC(ContentHandler chandler) {
this.chandler = chandler;
}
public
void characters(char[] ch, int start, int length)
throws org.xml.sax.SAXException {
cat.debug("characters: ["+new String(ch, start, length)+ "] called");
chandler.characters(ch, start, length);
}
public
void endDocument() throws org.xml.sax.SAXException {
cat.debug("endDocument called.");
chandler.endDocument();
}
public
void endElement(String namespaceURI, String localName, String qName)
throws org.xml.sax.SAXException {
cat.debug("endElement("+namespaceURI+", "+localName+", "+qName+") called");
chandler.endElement(namespaceURI, localName, qName);
}
public
void endPrefixMapping(String prefix) throws org.xml.sax.SAXException {
cat.debug("endPrefixMapping("+prefix+") called");
chandler.endPrefixMapping(prefix);
}
public
void ignorableWhitespace(char[] ch, int start, int length)
throws org.xml.sax.SAXException {
cat.debug("ignorableWhitespace called");
chandler.ignorableWhitespace(ch, start, length);
}
public
void processingInstruction(java.lang.String target, java.lang.String data)
throws org.xml.sax.SAXException {
cat.debug("processingInstruction called");
chandler.processingInstruction(target, data);
}
public
void setDocumentLocator(Locator locator) {
cat.debug("setDocumentLocator called");
chandler.setDocumentLocator(locator);
}
public
void skippedEntity(String name) throws org.xml.sax.SAXException {
cat.debug("skippedEntity("+name+") called");
chandler.skippedEntity(name);
}
public
void startDocument() throws org.xml.sax.SAXException {
cat.debug("startDocument called");
chandler.startDocument();
}
public
void startElement(String namespaceURI, String localName, String qName,
Attributes atts) throws org.xml.sax.SAXException {
cat.debug("startElement("+namespaceURI+", "+localName+", "+qName+")called");
if("log4j:event".equals(qName)) {
cat.debug("-------------");
if(atts instanceof org.xml.sax.helpers.AttributesImpl) {
AttributesImpl ai = (AttributesImpl) atts;
int i = atts.getIndex("timestamp");
ai.setValue(i, "hello");
}
String ts = atts.getValue("timestamp");
cat.debug("New timestamp is " + ts);
}
chandler.startElement(namespaceURI, localName, qName, atts);
}
public
void startPrefixMapping(String prefix, String uri)
throws org.xml.sax.SAXException {
cat.debug("startPrefixMapping("+prefix+", "+uri+") called");
chandler.startPrefixMapping(prefix, uri);
}
}
1.1 jakarta-log4j/contribs/ThomasFenner/JDBCAppender.java
Index: JDBCAppender.java
===================================================================
/*
* 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;
}
}
1.1 jakarta-log4j/contribs/ThomasFenner/JDBCConnectionHandler.java
Index: JDBCConnectionHandler.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*/
package com.klopotek.utils.log;
import java.sql.*;
/**
This interface has to be implemented for your own database-connection-handling and its used in class JDBCLogger.
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public interface JDBCConnectionHandler
{
/**Get a connection*/
Connection getConnection();
/**Get a defined connection*/
Connection getConnection(String _url, String _username, String _password);
}
1.1 jakarta-log4j/contribs/ThomasFenner/JDBCIDHandler.java
Index: JDBCIDHandler.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*/
package com.klopotek.utils.log;
/**
This interface has to be implemented to provide ID-columns with unique IDs and its used in class JDBCLogger.
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public interface JDBCIDHandler
{
/**Get a unique ID*/
Object getID();
}
1.1 jakarta-log4j/contribs/ThomasFenner/JDBCLogger.java
Index: JDBCLogger.java
===================================================================
/*
* 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.*;
/**
This class encapsulate the logic which is necessary to log into a table.
Used by JDBCAppender
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public class JDBCLogger
{
//All columns of the log-table
private ArrayList logcols = null;
//Only columns which will be provided by logging
private String column_list = null;
//Number of all columns
private int num = 0;
//Status for successful execution of method configure()
private boolean isconfigured = false;
//Status for ready to do logging with method append()
private boolean ready = false;
//This message will be filled with a error-string when method ready() failes, and can be got by calling getMsg()
private String errormsg = "";
private Connection con = null;
private Statement stmt = null;
private ResultSet rs = null;
private String table = null;
//Variables for static SQL-statement logging
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;
/**
Writes a message into the database table.
Throws an exception, if an database-error occurs !
*/
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();
}
/**
Writes a message into the database using a given sql-statement.
Throws an exception, if an database-error occurs !
*/
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;
}
}
/**
Configures this class, by reading in the structure of the log-table
Throws an exception, if an database-error occurs !
*/
public void configureTable(String _table) throws Exception
{
if(isconfigured) return;
//Fill logcols with META-informations of the table-columns
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;
}
/**
Configures this class, by storing and parsing the given sql-statement.
Throws an exception, if somethings wrong !
*/
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;
}
/**
Sets a connection. Throws an exception, if the connection is not open !
*/
public void setConnection(Connection _con) throws Exception
{
con = _con;
if(!isConnected()) throw new Exception("JDBCLogger::setConnection(), Given connection isnt connected to database !");
}
/**
Sets a columns logtype (LogTypes) and value, which depends on that logtype.
Throws an exception, if the given arguments arent correct !
*/
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;
}
}
}
}
/**
Return true, if this class is ready to append(), else false.
When not ready, a reason-String is stored in the instance-variable msg.
*/
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;
}
/**
Return true, if this class is configured, else false.
*/
public boolean isConfigured(){ return isconfigured;}
/**
Return true, if this connection is open, else false.
*/
public boolean isConnected()
{
try
{
return (con != null && !con.isClosed());
}
catch(Exception e){return false;}
}
/**
Return the internal error message stored in instance variable msg.
*/
public String getErrorMsg(){String r = new String(errormsg); errormsg = null; return r;}
}
/**
This class encapsulate all by class JDBCLogger needed data around a column
*/
class LogColumn
{
//column name
String name = null;
//column type
String type = null;
//not nullability means that this column is mandatory
boolean nullable = false;
//isWritable means that the column can be updated, else column is only readable
boolean isWritable = false;
//if ignore is true, this column will be ignored by building sql-statements.
boolean ignore = false;
//Must be filled for not nullable columns ! In other case it is optional.
int logtype = LogType.EMPTY;
Object value = null; //Generic storage for typewrapper-classes Long, String, etc...
JDBCIDHandler idhandler = null;
}
/**
This class contains all constants which are necessary to define a columns log-type.
*/
class LogType
{
//A column of this type will receive the message.
public static final int MSG = 1;
//A column of this type will be a unique identifier of the logged row.
public static final int ID = 2;
//A column of this type will contain a static, one-time-defined value.
public static final int STATIC = 3;
//A column of this type will be filled with an actual timestamp depending by the time the logging will be done.
public static final int TIMESTAMP = 4;
//A column of this type will contain no value and will not be included in logging insert-statement.
//This could be a column which will be filled not by creation but otherwhere...
public static final int EMPTY = 5;
public static boolean isLogType(int _lt)
{
if(_lt == MSG || _lt == STATIC || _lt == ID || _lt == TIMESTAMP || _lt == EMPTY) return true;
return false;
}
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;
}
}
1.1 jakarta-log4j/contribs/ThomasFenner/Log4JTest.java
Index: Log4JTest.java
===================================================================
/**
// Class JDBCAppender, writes messages into a database
// The JDBCAppender is configurable at runtime in two alternatives :
// 1. Configuration-file
// Define the options in a file and call a PropertyConfigurator.configure(file)-method.
// 2. method JDBCAppender::setOption(JDBCAppender.xxx_OPTION, String value)
// The sequence of some options is important :
// 1. Connector-option OR/AND Database-options
// Any database connection is required !
// 2. (Table-option AND Columns-option) OR SQL-option
// Any statement is required !
// 3. All other options can be set at any time...
// The other options are optional and have a default initialization, which can be custumized.
// All available options are defined as static String-constants in JDBCAppender named xxx_OPTION.
// Here is a description of all available options :
// 1. Database-options to connect to the database
// - URL_OPTION : a database url of the form jdbc:subprotocol:subname
// - USERNAME_OPTION : the database user on whose behalf the connection is being made
// - PASSWORD_OPTION : the user's password
//
// 2. Connector-option to specify your own JDBCConnectionHandler
// - CONNECTOR_OPTION : a classname which is implementing the JDBCConnectionHandler-interface
// This interface is used to get a customized connection.
// If in addition the database-options are given, these options will be used
// for invocation of the JDBCConnectionHandler-interface to get a connection.
// Else if no database-options are given, the JDBCConnectionHandler-interface is called without these options.
//
// 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
// - SQL_OPTION : a sql-statement which will be used to write to the database
// If you give this option, the table-option and columns-option will be ignored !
// Use the variable @MSG@ on that location in the statement, which has to be dynamically replaced by the message.
//
// 4. Table-option to specify one table contained by the database
// - TABLE_OPTION : the table in which the logging will be done
//
// 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
// - COLUMNS_OPTION : a formatted list of column-descriptions
// Each column description consists of
// - the name of the column (required)
// - a logtype which is a static constant of class LogType (required)
// - and a value which depends by the LogType (optional/required, depending by logtype)
// Here is a description of the available logtypes of class LogType :
// o MSG = a value will be ignored, the column will get the message. (One columns need to be of this type!)
// o STATIC = 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!)
// o ID = value must be a classname, which implements the JDBCIDHandler-interface.
// o TIMESTAMP = a value will be ignored, the column will be filled with a actually timestamp with every logged message.
// o EMPTY = 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!)
// If there are more than one column to describe, the columns must be separated by a TAB-delimiter (' ') !
// The arguments of a column-description must be separated by the delimiter '~' !
// (Example : name1~logtype1~value1 name2~logtype2~value2...)
//
// 6. Layout-options to define the layout of the messages (optional)
// - the layout wont be set by a xxx_OPTION
// Configuration-file : see at the following configuration-file example
// JDBCAppender::setOption() : see at the following code example
// The default is a layout of class org.apache.log4j.PatternLayout with ConversionPattern=%m
//
// 7. Buffer-option to define the size of the message-event-buffer (optional)
// - BUFFER_OPTION : define how many messages will be buffered until they will be updated to the database.
// The default is a update on every message (buffer=1=no buffer).
//
// 8. Commit-option to define a auto-commitment
// - COMMIT_OPTION : define whether updated messages should be committed to the database (Y) or not (N).
// The default is a commit on every buffer-flush.
// Here is a Configuration-file example, which can be used with the PropertyConfigurator :
//
// Declare a appender variable named JDBC
log4j.rootCategory=JDBC
// JDBC is a class of JDBCAppender, which writes messages into a database
log4j.appender.JDBC=JDBCAppender
// 1. Database-options to connect to the database
log4j.appender.JDBC.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))
log4j.appender.JDBC.username=mex_pr_dev60
log4j.appender.JDBC.password=mex_pr_dev60
// 2. Connector-option to specify your own JDBCConnectionHandler
log4j.appender.JDBC.connector=MyConnectionHandler
// 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
// 4. Table-option to specify one table contained by the database
log4j.appender.JDBC.table=logtest
// 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
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. Layout-options to define the layout of the messages (optional)
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%m
// 7. Buffer-option to define the size of the message-event-buffer (optional)
log4j.appender.JDBC.buffer=1
// 8. Commit-option to define a auto-commitment
log4j.appender.JDBC.commit=Y
*/
// Here is a code example to configure the JDBCAppender with a configuration-file :
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.INFO);
// Configuration with configuration-file
PropertyConfigurator.configure("log4jtestprops.txt");
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug"); //this not, because Priority DEBUG is less than INFO
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}
// Here is a code example to configure the JDBCAppender without a configuration-file :
/*
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// A JDBCIDHandler
MyIDHandler idhandler = new MyIDHandler();
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.DEBUG);
// Create a new instance of JDBCAppender
JDBCAppender ja = new JDBCAppender();
// Set options with method setOption()
ja.setOption(JDBCAppender.CONNECTOR_OPTION, "MyConnectionHandler");
ja.setOption(JDBCAppender.URL_OPTION, "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))");
ja.setOption(JDBCAppender.USERNAME_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.PASSWORD_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.TABLE_OPTION, "logtest");
// There are two ways to setup the column-descriptions :
// 1. Use the the method setOption(JDBCAppender.COLUMNS_OPTION, column-description)
//ja.setOption(JDBCAppender.COLUMNS_OPTION, "id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~:-) Thomas Fenner (t.fenner@klopotek.de)");
// 2. Use the better way of coding with method setLogType(String columnname, int LogType.xxx, Object xxx)
ja.setLogType("id_seq", LogType.EMPTY, "");
ja.setLogType("id", LogType.ID, idhandler);
ja.setLogType("msg", LogType.MSG, "");
ja.setLogType("created_on", LogType.TIMESTAMP, "");
ja.setLogType("created_by", LogType.STATIC, "FEN");
// If you just want to perform a static sql-statement, forget about the table- and columns-options,
// and use this one :
//ja.setOption(JDBCAppender.SQL_OPTION, "INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')");
// other options
//ja.setOption(JDBCAppender.BUFFER_OPTION, "1");
//ja.setOption(JDBCAppender.COMMIT_OPTION, "Y");
// Define a layout
//ja.setLayout(new PatternLayout("%m"));
// Add the appender to a category
cat.addAppender(ja);
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug");
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}
*/
// Implement a sample JDBCConnectionHandler
class MyConnectionHandler implements JDBCConnectionHandler
{
Connection con = null;
//Default connection
String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))";
String username = "mex_pr_dev60";
String password = "mex_pr_dev60";
public Connection getConnection()
{
return getConnection(url, username, password);
}
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;
}
}
// Implement a sample JDBCIDHandler
class MyIDHandler implements JDBCIDHandler
{
private static long id = 0;
public synchronized Object getID()
{
return new Long(++id);
}
}
1.1 jakarta-log4j/contribs/ThomasFenner/code_example1.java
Index: code_example1.java
===================================================================
// Here is a code example to configure the JDBCAppender with a configuration-file
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.INFO);
// Configuration with configuration-file
PropertyConfigurator.configure("log4jtestprops.txt");
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug"); //this not, because Priority DEBUG is less than INFO
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}
1.1 jakarta-log4j/contribs/ThomasFenner/code_example2.java
Index: code_example2.java
===================================================================
// Here is a code example to configure the JDBCAppender without a configuration-file
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// A JDBCIDHandler
MyIDHandler idhandler = new MyIDHandler();
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.DEBUG);
// Create a new instance of JDBCAppender
JDBCAppender ja = new JDBCAppender();
// Set options with method setOption()
ja.setOption(JDBCAppender.CONNECTOR_OPTION, "MyConnectionHandler");
ja.setOption(JDBCAppender.URL_OPTION, "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))");
ja.setOption(JDBCAppender.USERNAME_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.PASSWORD_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.TABLE_OPTION, "logtest");
// There are two ways to setup the column-descriptions :
// 1. Use the the method setOption(JDBCAppender.COLUMNS_OPTION, column-description)
//ja.setOption(JDBCAppender.COLUMNS_OPTION, "id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~:-) Thomas Fenner (t.fenner@klopotek.de)");
// 2. Use the better way of coding with method setLogType(String columnname, int LogType.xxx, Object xxx)
ja.setLogType("id_seq", LogType.EMPTY, "");
ja.setLogType("id", LogType.ID, idhandler);
ja.setLogType("msg", LogType.MSG, "");
ja.setLogType("created_on", LogType.TIMESTAMP, "");
ja.setLogType("created_by", LogType.STATIC, "FEN");
// If you just want to perform a static sql-statement, forget about the table- and columns-options,
// and use this one :
//ja.setOption(JDBCAppender.SQL_OPTION, "INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')");
// other options
//ja.setOption(JDBCAppender.BUFFER_OPTION, "1");
//ja.setOption(JDBCAppender.COMMIT_OPTION, "Y");
// Define a layout
//ja.setLayout(new PatternLayout("%m"));
// Add the appender to a category
cat.addAppender(ja);
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug");
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}
// Implement a sample JDBCConnectionHandler
class MyConnectionHandler implements JDBCConnectionHandler
{
Connection con = null;
//Default connection
String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))";
String username = "mex_pr_dev60";
String password = "mex_pr_dev60";
public Connection getConnection()
{
return getConnection(url, username, password);
}
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;
}
}
// Implement a sample JDBCIDHandler
class MyIDHandler implements JDBCIDHandler
{
private static long id = 0;
public synchronized Object getID()
{
return new Long(++id);
}
}
1.1 jakarta-log4j/contribs/ThomasFenner/configfile_example.txt
Index: configfile_example.txt
===================================================================
# Here is a Configuration-file example, which can be used with the PropertyConfigurator :
# Declare a appender variable named JDBC
log4j.rootCategory=JDBC
# JDBC is a class of JDBCAppender, which writes messages into a database
log4j.appender.JDBC=JDBCAppender
# 1. Database-options to connect to the database
log4j.appender.JDBC.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))
log4j.appender.JDBC.username=mex_pr_dev60
log4j.appender.JDBC.password=mex_pr_dev60
# 2. Connector-option to specify your own JDBCConnectionHandler
#log4j.appender.JDBC.connector=MyConnectionHandler
# 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
#log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
# 4. Table-option to specify one table contained by the database
log4j.appender.JDBC.table=logtest
# 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
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. Layout-options to define the layout of the messages (optional)
#log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
#log4j.appender.JDBC.layout.ConversionPattern=%m
# 7. Buffer-option to define the size of the message-event-buffer (optional)
#log4j.appender.JDBC.buffer=1
# 8. Commit-option to define a auto-commitment (optional)
#log4j.appender.JDBC.commit=Y