Logging
Introduction
The Grinder 3.7 replaced a previous custom logging framework with Logback. Scripts now use a standard logging API (SLF4J), and Logback can be configured to alter the output format, manage archiving of log files, and direct log streams to alternative locations.
Changing the Logback configuration
The Grinder uses two Logback configuration files:
logback.xml
- Used by all processes. Logs to the terminal (stdout
,stderr
).logback-worker.xml
- Used by worker processes. Configures logging to the log file and the data log file.
Both configuration files are located in the
grinder-core.jar
file. Refer to the Logback manual for full details of the
configuration file settings.
Let's change the archive settings for the output log to keep more than one archive file. First, extract the configuration file.
cd lib jar xf grinder-core-3.7.jar logback-worker.xml
Open the logback-worker.xml
file in a text editor and
locate the log-file
appender. To keep five archive files,
simply change the maxIndex
setting to5
so it
matches the following:
<appender name="log-file" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${PREFIX}.log</file> <encoder> <pattern>%d %-5level %logger{0} %marker: %message%n</pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>${PREFIX}.log%i</fileNamePattern> <minIndex>1</minIndex> <maxIndex>5</maxIndex> </rollingPolicy> <triggeringPolicy class="net.grinder.util.logback.RollOnStartUp" /> </appender>
Save the file under the same name
(logback-worker.xml
). To use the modified configuration,
add the file's directory to the CLASSPATH
used to start
The Grinder. We extracted the file into the lib
directory, so we could start the agent process with something like the
following:
cd $GRINDER_HOME java -classpath lib:lib/grinder.jar net.grinder.Grinder
Logging data to a database
The logback-worker.xml
file configures two Logback
loggers: worker
for the main log file, and
data
for the data log file. Let's change the
configuration to send test data to a database. To do this, we'll
configure a new appender and add it to the data logger. Logback's
database appender supports several databases; here's a suitable
configuration for an Oracle database.
<appender name="data-db" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>oracle.jdbc.OracleDriver</driverClass> <url>jdbc:oracle:thin:@localhost:1521:XE</url> <user>grinder</user> <password>grinder</password> </connectionSource> </appender> <logger name="data" additivity="false"> <appender-ref ref="data-file" /> <appender-ref ref="data-db" /> </logger>
To expose any problems with the configuration, we'll also enable the Logback debug output by changing the first line of the configuration.
<configuration debug="true">
Before we can use the database appender, we need to set up the
appropriate database tables. To do this, create a suitable database
account (the configuration above uses an account called
grinder), download the Logback distribution, and locate and
execute the appropriate DDL
(logback-classic/src/main/java/ch/qos/logback/classic/db/dialect/oracle.sql
for Oracle).
To run the configuration, add the directory containing
logback-worker.xml
to the CLASSPATH
, along
with the appropriate database driver. For example:
java -classpath /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/jdbc/lib/ojdbc14.jar:lib:lib/grinder.jar \ net.grinder.Grinder
Writing a custom appender for data logs
If you tried out the database configuration in the previous section you may have noted the following drawbacks.
- It's not particularly fast. Maximum logging throughput is of the order of a hundred log events per second, and this severely constrains the rate at which a worker process can perform tests.
- The log data is written as a string to a single
formatted_message
column. This is not amenable to further processing.
To address these problems, you will have to write a custom database appender, perhaps by modifying the standard Logback-supplied appender. If you write such an appender, please consider making it generic and contributing it back to The Grinder. The following sections contain some implementation ideas.
Improving database logging performance
The most beneficial change from a performance perspective would be to buffer the log events, and write many events to the database at once. JDBC batching would further improve performance. I suspect that this change alone would allow tens of thousands of events to be logged per second.
The standard appender includes caller data (filename, class, method, line) that is expensive to obtain and is of little use for The Grinder data log. It also logs exception and property information. These features can be removed.
To support the secondary exception and property tables, the standard appender needs to obtain the primary key of the newly logged event. Unfortunately this uses an appender level lock (unnecessarily, it could have synchronised on the database connection instead), and becomes a bottleneck when many worker threads are using the appender. Since the exception and property tables are unnecessary, this complexity can also be removed.
Customising data log output
The Grinder data logger generates ILoggingEvent
s with
the formatted string set to a comma-separated string (formatted as in
the standard data log). It also supplies an instance of
net.grinder.engine.process.DataLogArguments
as the first
and only argument. This can be accessed using
ILoggingEvent.getArgumentArray()[0]
.
The DataLogArguments
object provides all the
information you might need about a particular data log event,
including the thread and run numbers, the Test
, and the
raw statistics. Refer to the
net.grinder.engine.processs.ThreadDataLogger
source code
for an example of how to extract the appropriate statistics values
from the raw statistics.