This implementation contains three log4j appenders that make it possible to log into a Notes database from either a servlet running in a Domino server, an agent running in a Domino server and a remote location (e.g. a EJB or a Servlet running in a Tomcat). The first two appenders use native Notes APIs, the remote appender uses IIOP to connect to the server.
All appenders use a caching based on, first, the numbers of entries in a single log document and, second, a timeout value. Therefore, if either the maximum numbers of lines are reached in a single log document or a certain timeout value since the last write action has past, the cached log entries are written to the Domino database.
The following table contains the three supported appender classes. They can be used as the class property in a log4j configuration, here is an example:
log4j.appender.appendername=org.goetz.domino.log4j.AgentAppender
Class Name | Description |
org.goetz.domino.log4j.AgentAppender | Appender implementation to be used in a Domino agent. |
org.goetz.domino.log4j.ServletAppender | Appender implementation to be used in a serlvet running in the Domino server context. |
org.goetz.domino.log4j.RemoteAppender | Appender implementation to be used from remote locations of any kind, e.g. a J2EE container (servlets or EJBs). |
Here is a sample configuration for an agent appender:
# Agent appender: log4j.appender.testagent1=org.goetz.domino.log4j.AgentAppender log4j.appender.testagent1.applicationName=TestAgent1 log4j.appender.testagent1.database=DOMAPP/applog00.nsf log4j.appender.testagent1.maxLines=500 log4j.appender.testagent1.flushTimeout=10000 log4j.appender.testagent1.message=%d %u %p - %m
Here is a sample configuration for a Domino servlet appender:
# Servlet appender: log4j.appender.testservlet1=org.goetz.domino.log4j.ServletAppender log4j.appender.testservlet1.applicationName=DominoTestServlet1 log4j.appender.testservlet1.database=DOMAPP/applog00.nsf log4j.appender.testservlet1.maxLines=1000 log4j.appender.testservlet1.flushTimeout=60000 log4j.appender.testservlet1.message=%d %u %p - %m
Here is a sample configuration for a remote appender:
# Remote appender (Serlvet): log4j.appender.testremote1=org.goetz.domino.log4j.RemoteAppender log4j.appender.testremote1.applicationName=RemoteTestServlet1 log4j.appender.testremote1.server=localhost log4j.appender.testremote1.database=DOMAPP/applog00.nsf log4j.appender.testremote1.userName=userid log4j.appender.testremote1.keystore=c:/data/protect/dataprotector.keystore log4j.appender.testremote1.userPassword=password log4j.appender.testremote1.maxLines=1000 log4j.appender.testremote1.flushTimeout=10000 log4j.appender.testremote1.message=%d %u %p - %m
Please note: If you have multiple remote appenders defined, they must all point to the same keystore at the moment, because the decryption of the password is done via a static class being initialized by all appenders. If you have different keystores, some appender initializations will sporadically fail because of multiple concurrent access to the singleton decrypting class.
The following properties apply to all appenders:
Property | Description |
applicationName | Name of the application written as document attribute AppName. If not set, the first used logger category is being used as application name. I.e. the target category name might be 'comp.sample.comp' for a whole component (e.g. servlet). However, it could be that the first log entry is being written by another package called 'com.sample.comp.ejb'. Then, unfortunately, the application name would be initialized with the name 'com.sample.comp.ejb'. That's why you have the possibility to override that behavior by setting an explicit name by using this property. |
server | Server name where the log database is located. Optional. If not defined, localhost is used. |
database | Name of the Domino database to log to, relative to the data directory of the Domino server. |
maxLines | Maximum number of lines per log document. Optional. If not set, 1000 is used as the default. If the fixed maximum size of a log document of about 30 kB is reached, the document is written to the database, with no regards to the current number of lines. The reason for this limit is a) rich text fields in Domino are able to contain about 30 kB without a |
flushTimeout | Number of miliseconds until a non-full log document is written to the database. Optional. If not set, 20 seconds is used as the default. |
message | Log4j message definition. The placeholder %u is being replaced
by the current user id. For the RemoteAgent implementation,
the user id is read from a log4j MDC context. You need to
insert the following code line into your code for each
thread:
MDC.put("id", request.getRemoteUser()); |
formName | Name of the Domino log document form. Optional. If not defined, the default 'frmEvents' is used. |
The following properties apply to the RemoteAppender only:
Property | Description |
connectUserName | Technical user id to connect to the log database via IIOP. If not set, the IIOP connection tries to connect anonymously. |
connectPassword | Password of the technical user id. If not set, the IIOP connection tries to connect anonymously. |
The target of this project also was to have a single source of log4j configuration for all Java-based code running in the context of the Domino server. Java code can run in the form of a servlet or an agent. Unfortunately, HTTP based calls to agents and servlets on one hand and RunOnServer calls to agents on the other run in different VMs. Hence, loading a log4j configuraiton in one VM does not update the current configuration in the other VM.
For that reason, I introduced a socket based communication between the configuration agent (ran as RunOnServer method) and its counterpart VM, the one responsible for servlets and agents called vai HTTP. To make something listen to update requests, you need to install a specific servlet in your Domino server as follows:
servlet.log4jconfiglistener.code=org.goetz.domino.log4j.config.Log4JReloadServlet servlet.log4jconfiglistener.initArgs=port=4444,configdir=C:/opt/domino65/data/conf servlets.startup=log4jconfiglistener
The init method of this servlet creates a daemon thread that listens on a socket on a specific port. When a client connects to that socket, the thread tries to reload the log4j configuration from the directory specified by the second servlet argument called configdir.
Another solution is to use the log4j configuration file watcher thread. This can be configured as follows:
servlet.log4jconfiglistener.code=org.goetz.domino.log4j.config.Log4JReloadServlet servlet.log4jconfiglistener.initArgs=delay=60000,configdir=C:/opt/domino65/data/conf servlets.startup=log4jconfiglistener
This makes the servlet listen to log4j configuration file changes every 60 seconds (60000 ms). A listener port is then not created.
Unfortunately, preparation of the Domino Server got quite complicated. To be able to carry agents and servlets that use the appenders provided in this component, you must do the following things:
Add the following libraries to the Domino server's JVM extension directory (i.e. C:\Lotus\Domino\jvm\lib\ext):
Add the following entries in the server's notes.ini:
Log4jDirectory=directory relative to the domino directory JavaUserClasses=class path including the absolute directory from above
e.g.
Log4jDirectory=conf JavaUserClasses=C:\Lotus\Domino\data\conf
Add the log4j config servlet to the servlets.properties file in your data directory, e.g. as follows:
servlet.log4jconfiglistener.code=org.goetz.domino.log4j.config.Log4JReloadServlet servlet.log4jconfiglistener.initArgs=delay=60000,configdir=C:/Lotus/Domino/data/conf servlets.startup=log4jconfiglistener
Restart the Domino server.
Install the Notes database applog00.nsf in the nsf directory of this project in your Domino server. It contains the configuration agent and views to look at log documents created by the appenders.
The database nsf/applog00.nsf provided in the distribution contains a configuration and a logging part. Both parts are contained in one database for convenience reasons. You would, however, in your own environments separate configurations and logging into two or more databases.
The agent "LogConfigLoaderAgent" is called by using the button in the configuration view in the provided database. It activates the selected log4j configuration, stores this configuration to a log4j.properties configuration file and calls the other VM via the socket provided by the servlet described above. The port to which the agent connects is hard coded into the agent source code.
The agent 'TestAgent1' uses the Domino agent appender as an example. You can call it by using the test button in the configuration view or calling it via HTTP as follows:
http://server/domapp/applog00.nsf/TestAgent1?OpenAgent
The following table contains numbers taken from some performance tests that were run locally on a Thinkpad T42. The test client, the Tomcat instance and the Domino server were located on that Thinkpad. Each request/response pair contains the following log method calls:
log.debug("This is a debug message"); log.info("This is an info message"); log.warn("This is a warn message"); log.error("This is an error message"); log.fatal("This is a fatal message"); ... try { throw new Exception("Meine Ausnahme"); } catch (Exception e) { log.error("Error in TestServlet!", e); }
This logging scenario was executed using JMeter having 5 client threads in parallel for a couple of minutes.
IIOP/RemoteAppender (Tomcat, Sun JDK 1.5.0)
Value | Measures for Domino Appender (ms) | Measures for File Appender (ms) |
Avg (ms) | 39 | 32 |
Median (ms) | 20 | 30 |
90% (ms) | 61 | 40 |
Max (ms) | 1443 | 621 |
Throughput (reqests per second) | 126.2 | 151.8 |
KB per second | 283.03 | 340.46 |
Local/ServletAppender (Domino 6.5, IBM JDK 1.3.1)
Value | Measures for Domino Appender (ms) | Measures for File Appender (ms) |
Avg (ms) | 65 | 58 |
Median (ms) | 60 | 60 |
90% (ms) | 80 | 70 |
Max (ms) | 821 | 230 |
Throughput (reqests per second) | 75.0 | 85.0 |
KB per second | 119.32 | 135.41 |
Local/AgentAppender (Domino 6.5, IBM JDK 1.3.1)
Value | Measures for Domino Appender (ms) | Measures for File Appender (ms) |
Avg (ms) | 254 | 257 |
Median (ms) | 161 | 160 |
90% (ms) | 551 | 581 |
Max (ms) | 1221 | 1312 |
Throughput (reqests per second) | 19.6 | 19.4 |
KB per second | 67.17 | 66.50 |