Attached is a Zip file (renamed as log4plsql.dat to pass spam/virus
filters) with my modifications to the 3.1.2.1 version for people to
review and see what they think.  

We needed additional PL/SQL functionality like the Log4J MDC, and we
needed the Java listener to be thread safe and highly flexible in a J2EE
App Container context.  We didn't care if each instance in a cluster
started it's own listener on a pipe, as hopefully the logging would be
standardized/centralized with Log4J, but we did care that only one
listener per pipe was used in a given VM.

We also formatted and extended the DBMS_OUTPUT code a bit.

Things I've added/fixed/changed:

PMDC package
a package to add Log4J style Mapped Domain Context.  Currently only
output to the Log4J pipe, not to the table or other output locations, as
there is no pattern layout concept yet.

PLOG_PIPE package
Contains a procedure for reading the next Log4Plsql pipe message and
returning the different fields as OUT parameters.  This means that the
Java process only needs to make one call, not many to get each log
message, which is MUCH faster.

PLOG
Modified the pipe packing section to pass additional fields for the MDC
data (passed as two delimited strings, one for names, the other for
values, and a third field for the separator character).

The date is passed as a DATE object, instead of text, and the hundredths
of seconds are passed as a number.  The Java process I've written then
reads the date as a Timestamp, gets the milliseconds, and adds
hundredths*10 to get a raw timestamp to use for the log event.  This way
Log4J logs the message with the actual database timestamp it was created
with, and can still do any other date formatting you want.

The DBMS_OUTPUT code has been cleaned up and extended.  A sub-block of
code was created inside the IF statement to keep the local variables
used for processing the output text local to the code that uses them,
rather than declared in the procedure header.  Makes it easier to read
in my opinion.  

We (myself and another developer who is starting to use this in our
project) have added some formatting to the DMBS_OUTPUT section to make
the result more readable in SQLPLUS and other tools.

LOG_CTX, PLOG.INIT, and PLOGPARAM now know about a DBMS_OUTPUT line wrap
length value.  Messages longer than this value are output on multiple
lines.  This replaces the hard-coded max value of 255 with a value that
can be session-specific or even message-specific if desired.  The
default is still 255.

DBMS_OUTPUT data is logged with the header on the same line as the start
of the message.  If wrapping is needed, wrapped lines are left-padded to
the width of the header.  Here is an example of output:

13:33:33:09-ERROR- anonymous block  this is my really long error message
str
                                    ing that should force wrapping with
the 
                                    header text prepended to it.

We like this better, but it may compress log messages too much for some
people.  Let me know what you think.


Java BackgroundProcess
I completely re-wrote the pipe listening portion of this to take
advantage of the new PLOG_PIPE package to get a full log message in one
call and make the whole thing robust, safe, and scalable for our
enterprise application running in an App Container such as OC4J,
WebLogic, or WebSphere.

The existing use with the standalone, runable JAR file is still
available, but if you want to integrate the background process in a
larger application there is new functionality to do so.  

I have factory methods on the listener that enforce only one listener
per database URL/Username/PipeName combination, so you don't needlessly
start multiple listeners per pipe by accident, and added thread-safe
ways to stop/start listening on a given pipe.

The listener class DbLogListener uses a DbLogHelper instance to allow
for extensibility without needing to mess with the complex DbLogListener
class.  This helper has methods for getting the Logger instance to use
for a given message and pre- and post- logging methods to customize
things like the MDC and NDC, and clean up after logging a message.

The DbLogListener can store a base Logger name to use for getting the
desired Logger instance for each message read from the pipe.

The default is the RootLogger if none is supplied.

DbLogHelper creates a default Logger name for each message from 

DbLogListener.getRootLogger() + current message section

So by default the section name = logger name, if the root logger is the
base logger for the DbLogListener.

If you create the DbLogListener with a default Logger name of
"backgroundProcess" then all messages will use descendants of that
Logger.

Currently the Java process does not support custom levels from
Log4Plsql.  I needed it to work with log messages from possibly many
databases, each of which would have it's own Log4Plsql version
installed.  Dynamic Levels would have had to be read/stored by
connection, which would have added significant code overhead for very
little functionality.

We have chosen at this point to use the standard levels only, which is
what the Log4J project recommends anyway.  Besides, custom levels would
need to map to some custom Level in Java, otherwise they would not be
logged, and using some sort of reflection to dynamically load Level
classes would be very expensive from a performance standpoint.

With the re-write of the Java code, there is no SQLJ dependency anymore
- I just use straight JDBC code, as there isn't that much for it to do.

The constructor that uses the XML configuration parameters does need to
find the OracleDataSource class, which is in the JDBC driver JAR
classes12.jar, and it needs the XML parser in xmlparserv2.jar.

Other classes were only modified to reference the new DbLogListener
appropriately, they were otherwise unchanged.

Feedback is welcome, I'm happy to answer any questions or field
criticisms of this code.

Greg Woolsey
Sabrix, Inc.
Lake Oswego, Oregon
USA

Attachment: log4plsql.dat
Description: log4plsql.dat

Reply via email to