blob: 5024da8f47ff146ef3dc5b6576c2826c6f3286cd [file] [log] [blame]
Delivered-To: urba-cgu@urbanet.ch
Mailing-List: contact log4j-user-help@jakarta.apache.org; run by ezmlm
List-Post: <mailto:log4j-user@jakarta.apache.org>
List-Help: <mailto:log4j-user-help@jakarta.apache.org>
List-Unsubscribe: <mailto:log4j-user-unsubscribe@jakarta.apache.org>
List-Subscribe: <mailto:log4j-user-subscribe@jakarta.apache.org>
Reply-To: "LOG4J Users Mailing List" <log4j-user@jakarta.apache.org>
Delivered-To: mailing list log4j-user@jakarta.apache.org
Date: Thu, 01 Feb 2001 14:26:34 -0800
From: Kevin Steppe <ksteppe@pacbell.net>
X-Mailer: Mozilla 4.76 [en] (Windows NT 5.0; U)
X-Accept-Language: en
To: LOG4J Users Mailing List <log4j-user@jakarta.apache.org>
Subject: JDBC Appender
X-Spam-Rating: h31.sny.collab.net 1.6.2 0/1000/N
Ok, here it is. Since there will be differences in database schemas and
connection/execution methods, I wrote this with the intention that those
parts would be overriden by subclasses (that's what I'm doing for my
company), however it will work as is if you have a stored procedure
spLog @msg. I'm sure there are optimizations which could be done.
The code for org.apache.log4j.varia.JDBCAppender and
org.apache.log4j.varia.test.JDBCTest follow and files attached. At the
bottem is the SQL I used to test this on M$ SQL-Server.
I help this proves useful,
Kevin
package org.apache.log4j.varia;
import org.apache.log4j.*;
import org.apache.log4j.spi.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
/**
* Contribution from MD Data Direct.
*
* Implements an ArrayList buffer before storing messages to the DB.
* Override getSQL to fit your database schema (or implement spLog msg
on your DB)
* Override executeSQL to modify how DB connection and SQL execution is
made.
*
* @author: Kevin Steppe
*/
public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
implements org.apache.log4j.Appender
{
protected String databaseURL = "jdbc:odbc:myDB";
protected String databaseUser = "me";
protected String databasePassword = "mypassword";
public static final String URL_OPTION = "URL";
public static final String USER_OPTION = "User";
public static final String PASSWORD_OPTION = "Password";
public static final String BUFFER_OPTION = "Buffer";
protected int bufferSize = 1;
protected List buffer;
public JDBCAppender()
{
super();
buffer = new ArrayList();
}
public void append(LoggingEvent event)
{
buffer.add(event);
if (buffer.size() >= bufferSize)
flushBuffer();
}
public void close()
{
flushBuffer();
this.closed = true;
}
public void setOption(String key, String value)
{
super.setOption(key, value);
if (key.equalsIgnoreCase(URL_OPTION))
databaseURL = value;
else if (key.equalsIgnoreCase(USER_OPTION))
databaseUser = value;
else if (key.equalsIgnoreCase(PASSWORD_OPTION))
databasePassword = value;
else if (key.equalsIgnoreCase(BUFFER_OPTION))
bufferSize = Integer.parseInt(value);
}
/**
* Override this to create the SQL needed for your DB schema
*/
protected String getSQL(LoggingEvent event)
{
String msg = this.layout.format(event);
String sql = "spLog '" + msg + "'";
return sql;
}
/**
* Override this to provide an alertnate method of getting
connections (such as caching)
* This implementation creates a new connection and statement for
every execution which
* is very wastefull. One method to fix this is to open connections
at the start of
* flushBuffer() and close them at the end. MD Data uses a
connection pool outside
* of JDBCAppender which is accessed in the override of this method.
*/
protected void executeSQL(String sql) throws SQLException
{
Connection con = null;
Statement stmt = null;
try {
con = DriverManager.getConnection(databaseURL, databaseUser,
databasePassword);
stmt = con.createStatement();
stmt.executeUpdate(sql);
}
catch (SQLException e)
{
if (con != null)
con.close();
if (stmt != null)
stmt.close();
throw e;
}
stmt.close();
con.close();
}
public void flushBuffer()
{
//Do the actual logging
for (Iterator i = buffer.iterator(); i.hasNext();)
{
try {
String sql = getSQL((LoggingEvent)i.next());
executeSQL(sql);
}
catch (SQLException e)
{
errorHandler.error("Failed to excute sql", e,
ErrorCode.FLUSH_FAILURE);
}
}
buffer.clear();
}
public void finalize()
{
close();
}
public boolean requiresLayout()
{
return true;
}
}
package org.apache.log4j.varia.test;
import org.apache.log4j.varia.JDBCAppender;
import org.apache.log4j.*;
public class JDBCTest
{
public static void main (String argv[])
{
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println(e.toString());
}
Category rootLog = Category.getRoot();
Layout layout = new PatternLayout("%p [%t] %c - %m%n");
JDBCAppender appender = new JDBCAppender();
appender.setLayout(layout);
appender.setOption(JDBCAppender.URL_OPTION, "jdbc:odbc:someDB");
appender.setOption(JDBCAppender.USER_OPTION, "auser");
appender.setOption(JDBCAppender.PASSWORD_OPTION, "thepassword");
rootLog.addAppender(appender);
try {
Category log = Category.getInstance("main");
log.debug("Debug 1");
Thread.sleep(500);
log.info("info 1");
Thread.sleep(500);
log.warn("warn 1");
Thread.sleep(500);
log.error("error 1");
Thread.sleep(500);
log.fatal("fatal 1");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "5");
log.debug("Debug 2");
Thread.sleep(500);
log.info("info 2");
Thread.sleep(500);
log.warn("warn 2");
Thread.sleep(500);
log.error("error 2");
Thread.sleep(500);
log.fatal("fatal 2");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "2");
appender.setThreshold(Priority.WARN);
log.debug("Debug 3");
Thread.sleep(500);
log.info("info 3");
Thread.sleep(500);
log.warn("warn 3");
Thread.sleep(500);
log.error("error 3");
Thread.sleep(500);
log.fatal("fatal 3");
}
catch (InterruptedException e)
{
System.out.println("Interrupted");
}
}
}
drop table JDBCAppenderTest
go
create table JDBCAppenderTest (EventID int identity, entrytime datetime,
message varchar(255))
drop procedure spLog
go
create procedure spLog (@msg varchar(255)) as
insert into JDBCAppenderTest (message, entrytime) values (@msg,
getdate())
select * from JDBCAppenderTest
drop table JDBCAppenderTest
go
create table JDBCAppenderTest (EventID int identity, entrytime datetime, message varchar(255))
drop procedure spLog
go
create procedure spLog (@msg varchar(255)) as
insert into JDBCAppenderTest (message, entrytime) values (@msg, getdate())
select * from JDBCAppenderTest
package org.apache.log4j.varia;
import org.apache.log4j.*;
import org.apache.log4j.spi.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
/**
* Contribution from MD Data Direct.
*
* Implements an ArrayList buffer before storing messages to the DB.
* Override getSQL to fit your database schema (or implement spLog msg on your DB)
* Override executeSQL to modify how DB connection and SQL execution is made.
*
* @author: Kevin Steppe
*/
public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
implements org.apache.log4j.Appender
{
protected String databaseURL = "jdbc:odbc:myDB";
protected String databaseUser = "me";
protected String databasePassword = "mypassword";
public static final String URL_OPTION = "URL";
public static final String USER_OPTION = "User";
public static final String PASSWORD_OPTION = "Password";
public static final String BUFFER_OPTION = "Buffer";
protected int bufferSize = 1;
protected List buffer;
public JDBCAppender()
{
super();
buffer = new ArrayList();
}
public void append(LoggingEvent event)
{
buffer.add(event);
if (buffer.size() >= bufferSize)
flushBuffer();
}
public void close()
{
flushBuffer();
this.closed = true;
}
public void setOption(String key, String value)
{
super.setOption(key, value);
if (key.equalsIgnoreCase(URL_OPTION))
databaseURL = value;
else if (key.equalsIgnoreCase(USER_OPTION))
databaseUser = value;
else if (key.equalsIgnoreCase(PASSWORD_OPTION))
databasePassword = value;
else if (key.equalsIgnoreCase(BUFFER_OPTION))
bufferSize = Integer.parseInt(value);
}
/**
* Override this to create the SQL needed for your DB schema
*/
protected String getSQL(LoggingEvent event)
{
String msg = this.layout.format(event);
String sql = "spLog '" + msg + "'";
System.out.println(sql); //DEBUG
return sql;
}
/**
* Override this to provide an alertnate method of getting connections (such as caching)
* This implementation creates a new connection and statement for every execution which
* is very wastefull. One method to fix this is to open connections at the start of
* flushBuffer() and close them at the end. MD Data uses a connection pool outside
* of JDBCAppender which is accessed in the override of this method.
*/
protected void executeSQL(String sql) throws SQLException
{
Connection con = null;
Statement stmt = null;
try {
con = DriverManager.getConnection(databaseURL, databaseUser, databasePassword);
stmt = con.createStatement();
stmt.executeUpdate(sql);
}
catch (SQLException e)
{
if (con != null)
con.close();
if (stmt != null)
stmt.close();
throw e;
}
stmt.close();
con.close();
}
public void flushBuffer()
{
//Do the actual logging
for (Iterator i = buffer.iterator(); i.hasNext();)
{
try {
String sql = getSQL((LoggingEvent)i.next());
executeSQL(sql);
}
catch (SQLException e)
{
errorHandler.error("Failed to excute sql", e, ErrorCode.FLUSH_FAILURE);
}
}
buffer.clear();
}
public void finalize()
{
close();
}
public boolean requiresLayout()
{
return true;
}
}package org.apache.log4j.varia.test;
import org.apache.log4j.varia.JDBCAppender;
import org.apache.log4j.*;
public class JDBCTest
{
public static void main (String argv[])
{
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println(e.toString());
}
Category rootLog = Category.getRoot();
Layout layout = new PatternLayout("%p [%t] %c - %m%n");
JDBCAppender appender = new JDBCAppender();
appender.setLayout(layout);
appender.setOption(JDBCAppender.URL_OPTION, "jdbc:odbc:someDB");
appender.setOption(JDBCAppender.USER_OPTION, "auser");
appender.setOption(JDBCAppender.PASSWORD_OPTION, "thepassword");
rootLog.addAppender(appender);
try {
Category log = Category.getInstance("main");
log.debug("Debug 1");
Thread.sleep(500);
log.info("info 1");
Thread.sleep(500);
log.warn("warn 1");
Thread.sleep(500);
log.error("error 1");
Thread.sleep(500);
log.fatal("fatal 1");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "5");
log.debug("Debug 2");
Thread.sleep(500);
log.info("info 2");
Thread.sleep(500);
log.warn("warn 2");
Thread.sleep(500);
log.error("error 2");
Thread.sleep(500);
log.fatal("fatal 2");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "2");
appender.setThreshold(Priority.WARN);
log.debug("Debug 3");
Thread.sleep(500);
log.info("info 3");
Thread.sleep(500);
log.warn("warn 3");
Thread.sleep(500);
log.error("error 3");
Thread.sleep(500);
log.fatal("fatal 3");
}
catch (InterruptedException e)
{
System.out.println("Interrupted");
}
}
}