[log4plsql] FW: Modifications you may want to use
Brought to you by:
gmoulard
From: <log...@li...> - 2004-03-19 23:40:29
|
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. =20 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. =20 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=20 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. =20 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=20 DbLogListener.getRootLogger() + current message section So by default the section name =3D 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 |