Skip to content

Commit da2cb23

Browse files
committed
Add command line argument to configure loggers and their level
Fixes #23
1 parent 0a4de81 commit da2cb23

File tree

6 files changed

+235
-9
lines changed

6 files changed

+235
-9
lines changed

pom.xml

+7-7
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@
145145
<scope>compile</scope>
146146
</dependency>
147147

148+
<dependency>
149+
<groupId>ch.qos.logback</groupId>
150+
<artifactId>logback-classic</artifactId>
151+
<version>${logback.version}</version>
152+
<optional>true</optional>
153+
</dependency>
154+
148155
<dependency>
149156
<groupId>io.dropwizard.metrics</groupId>
150157
<artifactId>metrics-core</artifactId>
@@ -220,13 +227,6 @@
220227
<scope>test</scope>
221228
</dependency>
222229

223-
<dependency>
224-
<groupId>ch.qos.logback</groupId>
225-
<artifactId>logback-classic</artifactId>
226-
<version>${logback.version}</version>
227-
<scope>test</scope>
228-
</dependency>
229-
230230
<dependency>
231231
<groupId>com.rabbitmq</groupId>
232232
<artifactId>amqp-client</artifactId>

src/docs/asciidoc/performance-tool.adoc

+31
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ tracking consumers would ignore the specified offset and would start where they
450450
(this is the purpose of offset tracking).
451451

452452
===== Producer Names
453+
453454
You can use the `--producer-names` option to set the producer names pattern and therefore
454455
enable <<api.adoc#outbound-message-deduplication, message deduplication>> (using the default
455456
publishing sequence starting at 0 and incremented for each message).
@@ -464,6 +465,36 @@ java -jar stream-perf-test.jar --producer-names %s-%d
464465

465466
The run will start one producer and will use the `stream-1` producer reference (default stream is `stream` and the number of the producer is 1.)
466467

468+
===== Logging
469+
470+
The performance tool binary uses Logback with an internal configuration file.
471+
The default log level is `warn` with a console appender.
472+
473+
It is possible to define loggers directly from the command line, this is useful for quick debugging.
474+
Use the `rabbitmq.streamperftest.loggers` system property with `name=level` pairs, e.g.:
475+
476+
----
477+
java -Drabbitmq.streamperftest.loggers=com.rabbitmq.stream=debug -jar stream-perf-test.jar
478+
----
479+
480+
It is possible to define several loggers by separating them with commas, e.g. `-Drabbitmq.streamperftest.loggers=com.rabbitmq.stream=debug,com.rabbitmq.stream.perf=info`.
481+
482+
It is also possible to use an environment variable:
483+
484+
----
485+
export RABBITMQ_STREAM_PERF_TEST_LOGGERS=com.rabbitmq.stream=debug
486+
----
487+
488+
The system property takes precedence over the environment variable.
489+
490+
Use the environment variable with the Docker image:
491+
492+
----
493+
docker run -it --rm --network host \
494+
--env RABBITMQ_STREAM_PERF_TEST_LOGGERS=com.rabbitmq.stream=debug \
495+
pivotalrabbitmq/stream-perf-test
496+
----
497+
467498
=== Building the Performance Tool
468499

469500
To build the uber JAR:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) 2021 VMware, Inc. or its affiliates. All rights reserved.
2+
//
3+
// This software, the RabbitMQ Stream Java client library, is dual-licensed under the
4+
// Mozilla Public License 2.0 ("MPL"), and the Apache License version 2 ("ASL").
5+
// For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL,
6+
// please see LICENSE-APACHE2.
7+
//
8+
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
9+
// either express or implied. See the LICENSE file for specific language governing
10+
// rights and limitations of this software.
11+
//
12+
// If you have any questions regarding licensing, please contact us at
13+
14+
package com.rabbitmq.stream.perf;
15+
16+
import ch.qos.logback.classic.LoggerContext;
17+
import ch.qos.logback.classic.joran.JoranConfigurator;
18+
import ch.qos.logback.core.joran.spi.JoranException;
19+
import ch.qos.logback.core.util.StatusPrinter;
20+
import java.io.BufferedReader;
21+
import java.io.ByteArrayInputStream;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.InputStreamReader;
25+
import java.nio.charset.StandardCharsets;
26+
import java.util.HashMap;
27+
import java.util.Map;
28+
import org.slf4j.LoggerFactory;
29+
30+
public class LogUtils {
31+
32+
static void configureLog() throws IOException {
33+
String loggers =
34+
System.getProperty("rabbitmq.streamperftest.loggers") == null
35+
? System.getenv("RABBITMQ_STREAM_PERF_TEST_LOGGERS")
36+
: System.getProperty("rabbitmq.streamperftest.loggers");
37+
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
38+
InputStream configurationFile = StreamPerfTest.class.getResourceAsStream("/logback.xml");
39+
try {
40+
String configuration =
41+
processConfigurationFile(configurationFile, convertKeyValuePairs(loggers));
42+
JoranConfigurator configurator = new JoranConfigurator();
43+
configurator.setContext(context);
44+
context.reset();
45+
configurator.doConfigure(
46+
new ByteArrayInputStream(configuration.getBytes(StandardCharsets.UTF_8)));
47+
} catch (JoranException je) {
48+
// StatusPrinter will handle this
49+
} finally {
50+
configurationFile.close();
51+
}
52+
StatusPrinter.printInCaseOfErrorsOrWarnings(context);
53+
}
54+
55+
private static Map<String, Object> convertKeyValuePairs(String arg) {
56+
if (arg == null || arg.trim().isEmpty()) {
57+
return null;
58+
}
59+
Map<String, Object> properties = new HashMap<>();
60+
for (String entry : arg.split(",")) {
61+
String[] keyValue = entry.split("=");
62+
try {
63+
properties.put(keyValue[0], Long.parseLong(keyValue[1]));
64+
} catch (NumberFormatException e) {
65+
properties.put(keyValue[0], keyValue[1]);
66+
}
67+
}
68+
return properties;
69+
}
70+
71+
static String processConfigurationFile(InputStream configurationFile, Map<String, Object> loggers)
72+
throws IOException {
73+
StringBuilder loggersConfiguration = new StringBuilder();
74+
if (loggers != null) {
75+
for (Map.Entry<String, Object> logger : loggers.entrySet()) {
76+
loggersConfiguration.append(
77+
String.format(
78+
"\t<logger name=\"%s\" level=\"%s\" />%s",
79+
logger.getKey(),
80+
logger.getValue().toString(),
81+
System.getProperty("line.separator")));
82+
}
83+
}
84+
85+
BufferedReader in = new BufferedReader(new InputStreamReader(configurationFile));
86+
final int bufferSize = 1024;
87+
final char[] buffer = new char[bufferSize];
88+
StringBuilder builder = new StringBuilder();
89+
int charsRead;
90+
while ((charsRead = in.read(buffer, 0, buffer.length)) > 0) {
91+
builder.append(buffer, 0, charsRead);
92+
}
93+
94+
return builder.toString().replace("${loggers}", loggersConfiguration);
95+
}
96+
}

src/main/java/com/rabbitmq/stream/perf/StreamPerfTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import io.netty.handler.ssl.SslContextBuilder;
5151
import io.netty.handler.ssl.SslHandler;
5252
import io.netty.util.internal.PlatformDependent;
53+
import java.io.IOException;
5354
import java.io.PrintStream;
5455
import java.io.PrintWriter;
5556
import java.net.URI;
@@ -340,7 +341,8 @@ public StreamPerfTest(String[] arguments, PrintStream consoleOut, PrintStream co
340341
this.err = new PrintWriter(consoleErr, true);
341342
}
342343

343-
public static void main(String[] args) {
344+
public static void main(String[] args) throws IOException {
345+
LogUtils.configureLog();
344346
int exitCode = run(args, System.out, System.err);
345347
System.exit(exitCode);
346348
}

src/performance-tool/resources/logback.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
55
</encoder>
66
</appender>
7-
7+
8+
${loggers}
9+
810
<root level="warn">
911
<appender-ref ref="STDOUT" />
1012
</root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) 2021 VMware, Inc. or its affiliates. All rights reserved.
2+
//
3+
// This software, the RabbitMQ Stream Java client library, is dual-licensed under the
4+
// Mozilla Public License 2.0 ("MPL"), and the Apache License version 2 ("ASL").
5+
// For the MPL, please see LICENSE-MPL-RabbitMQ. For the ASL,
6+
// please see LICENSE-APACHE2.
7+
//
8+
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
9+
// either express or implied. See the LICENSE file for specific language governing
10+
// rights and limitations of this software.
11+
//
12+
// If you have any questions regarding licensing, please contact us at
13+
14+
package com.rabbitmq.stream.perf;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
18+
import java.io.ByteArrayInputStream;
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.Collections;
23+
import java.util.HashMap;
24+
import java.util.Map;
25+
import javax.xml.parsers.DocumentBuilder;
26+
import javax.xml.parsers.DocumentBuilderFactory;
27+
import org.assertj.core.api.Condition;
28+
import org.junit.jupiter.api.Test;
29+
30+
public class LogUtilsTest {
31+
32+
static final String XML =
33+
"<configuration>\n"
34+
+ " <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n"
35+
+ " <encoder>\n"
36+
+ " <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n"
37+
+ " </encoder>\n"
38+
+ " </appender>\n"
39+
+ "\n"
40+
+ "${loggers}\n"
41+
+ "\n"
42+
+ " <root level=\"warn\">\n"
43+
+ " <appender-ref ref=\"STDOUT\" />\n"
44+
+ " </root>\n"
45+
+ "</configuration>";
46+
47+
static InputStream xml() {
48+
return new ByteArrayInputStream(XML.getBytes(StandardCharsets.UTF_8));
49+
}
50+
51+
private static Condition<String> validXml() {
52+
return new Condition<>(
53+
xml -> {
54+
try {
55+
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
56+
DocumentBuilder builder = factory.newDocumentBuilder();
57+
builder.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
58+
return true;
59+
} catch (Exception e) {
60+
return false;
61+
}
62+
},
63+
"Not a valid XML document");
64+
}
65+
66+
@Test
67+
void processConfigurationFileNoLoggers() throws Exception {
68+
assertThat(LogUtils.processConfigurationFile(xml(), null))
69+
.isEqualTo(XML.replace("${loggers}", ""))
70+
.is(validXml());
71+
assertThat(LogUtils.processConfigurationFile(xml(), new HashMap<>()))
72+
.isEqualTo(XML.replace("${loggers}", ""))
73+
.is(validXml());
74+
}
75+
76+
@Test
77+
void processConfigurationFileOneLogger() throws IOException {
78+
assertThat(
79+
LogUtils.processConfigurationFile(
80+
xml(), Collections.singletonMap("com.rabbitmq.stream", "info")))
81+
.contains("<logger name=\"com.rabbitmq.stream\" level=\"info\"")
82+
.is(validXml());
83+
}
84+
85+
@Test
86+
void processConfigurationFileSeveralLoggers() throws IOException {
87+
Map<String, Object> loggers = new HashMap<>();
88+
loggers.put("com.rabbitmq.stream", "debug");
89+
loggers.put("com.rabbitmq.stream.perf", "info");
90+
assertThat(LogUtils.processConfigurationFile(xml(), loggers))
91+
.contains("<logger name=\"com.rabbitmq.stream\" level=\"debug\"")
92+
.contains("<logger name=\"com.rabbitmq.stream.perf\" level=\"info\"")
93+
.is(validXml());
94+
}
95+
}

0 commit comments

Comments
 (0)