Skip to content

Commit f67caa3

Browse files
authored
Merge pull request #51 from hivemq/feature/25334-add-config-via-xml
Make extension configurable via XML
2 parents ab31a81 + d60c9f8 commit f67caa3

32 files changed

+1985
-552
lines changed

README.adoc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,20 @@ This is useful for debugging and development purposes.
5151

5252
* Download the extension from the {hivemq-extension-download}[HiveMQ Marketplace^].
5353
* Copy the content of the zip file to the `extensions` folder of your HiveMQ nodes.
54-
* Modify the `mqttMessageLog.properties` file as needed.
54+
* Configure the extension by adding a `config.xml` file in the extension's `conf` folder.
55+
** For your convenience, we provide an example configuration `conf/examples/config.xml` that you can copy and modify as desired.
56+
** The file must be located in `HIVEMQ_HOME/extensions/hivemq-mqtt-message-log-extension/conf/config.xml`.
57+
58+
NOTE: Starting with release 1.2.0 of the extension, the legacy configuration file `mqttMessageLog.properties` is deprecated.
59+
Support for the legacy configuration will be removed in a future release.
5560

5661
== Configuration
5762

5863
=== Event Configuration
5964

6065
By default, all MQTT events are logged.
61-
It is possible to opt out of specific log event types by adding an *mqttMessageLog.properties* file to your `hivemq-mqtt-message-log-extension` home folder.
62-
There is an example file available which removes the logging of PUBLISH messages.
66+
It is possible to opt out of specific log event types by adding a `config.xml` file to your `hivemq-mqtt-message-log-extension/conf` folder.
67+
An example configuration file is available in `conf/examples/`, which removes the logging of PUBLISH messages.
6368

6469
=== General Configuration
6570

build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ hivemqExtension {
1616

1717
resources {
1818
from("LICENSE")
19+
from("src/main/resources/config.xsd") { into("conf") }
1920
}
2021
}
2122

2223
dependencies {
2324
compileOnly(libs.jetbrains.annotations)
2425

2526
implementation(libs.commonsLang)
27+
implementation(libs.jaxb.api)
28+
runtimeOnly(libs.jaxb.impl)
2629
}
2730

2831
@Suppress("UnstableApiUsage")

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=1.1.6
1+
version=1.2.0

gradle/libs.versions.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ awaitility = "4.2.2"
33
commonsLang = "3.15.0"
44
hivemq-extensionSdk = "4.3.0"
55
hivemq-mqttClient = "1.3.3"
6+
jaxb-api = "4.0.2"
7+
jaxb-impl = "4.0.5"
68
jetbrains-annotations = "24.1.0"
7-
junit-jupiter = "5.10.0"
9+
junit-jupiter = "5.10.3"
810
logback = "1.5.8"
911
mockito = "5.12.0"
1012
okhttp = "4.12.0"
@@ -14,6 +16,8 @@ testcontainers = "1.20.2"
1416
awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" }
1517
commonsLang = { module = "org.apache.commons:commons-lang3", version.ref = "commonsLang" }
1618
hivemq-mqttClient = { module = "com.hivemq:hivemq-mqtt-client", version.ref = "hivemq-mqttClient" }
19+
jaxb-api = { module = "jakarta.xml.bind:jakarta.xml.bind-api", version.ref = "jaxb-api" }
20+
jaxb-impl = { module = "org.glassfish.jaxb:jaxb-runtime", version.ref = "jaxb-impl" }
1721
jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" }
1822
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
1923
mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" }
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<hivemq-mqtt-message-log-extension xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="config.xsd">
4+
<!--
5+
Example properties file that will prevent the logging of
6+
both incoming and outgoing messages, but all other
7+
events, such as connection and subscription events, will be logged.
8+
-->
9+
<!--General settings-->
10+
<verbose>false</verbose>
11+
<payload>true</payload>
12+
13+
<!--Event settings-->
14+
<publish-received>false</publish-received>
15+
<publish-send>false</publish-send>
16+
17+
<client-connect>true</client-connect>
18+
<connack-send>true</connack-send>
19+
<client-disconnect>true</client-disconnect>
20+
<subscribe-received>true</subscribe-received>
21+
<suback-send>true</suback-send>
22+
<unsubscribe-received>true</unsubscribe-received>
23+
<unsuback-send>true</unsuback-send>
24+
<ping-request-received>true</ping-request-received>
25+
<ping-response-send>true</ping-response-send>
26+
<puback-received>true</puback-received>
27+
<puback-send>true</puback-send>
28+
<pubrec-received>true</pubrec-received>
29+
<pubrec-send>true</pubrec-send>
30+
<pubrel-received>true</pubrel-received>
31+
<pubrel-send>true</pubrel-send>
32+
<pubcomp-received>true</pubcomp-received>
33+
<pubcomp-send>true</pubcomp-send>
34+
35+
</hivemq-mqtt-message-log-extension>

src/hivemq-extension/mqttMessageLog.properties

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright 2019-present HiveMQ GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.hivemq.extensions.log;
17+
18+
import com.hivemq.client.mqtt.datatypes.MqttQos;
19+
import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient;
20+
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
21+
import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5PayloadFormatIndicator;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.junit.jupiter.api.Test;
24+
import org.slf4j.event.Level;
25+
import org.testcontainers.hivemq.HiveMQContainer;
26+
import org.testcontainers.junit.jupiter.Container;
27+
import org.testcontainers.junit.jupiter.Testcontainers;
28+
import org.testcontainers.utility.MountableFile;
29+
30+
import java.nio.charset.StandardCharsets;
31+
32+
import static com.hivemq.extensions.log.DockerImageNames.HIVEMQ;
33+
import static org.awaitility.Awaitility.await;
34+
35+
/**
36+
* @since 1.2.0
37+
*/
38+
@Testcontainers
39+
public class FullConfigXmlIT {
40+
41+
@Container
42+
final @NotNull HiveMQContainer hivemq = new HiveMQContainer(HIVEMQ) //
43+
.withExtension(MountableFile.forClasspathResource("hivemq-mqtt-message-log-extension"))
44+
.waitForExtension("HiveMQ Mqtt Message Log Extension")
45+
.withFileInExtensionHomeFolder(MountableFile.forClasspathResource("fullConfig.xml"),
46+
"hivemq-mqtt-message-log-extension",
47+
"/conf/config.xml")
48+
.withLogConsumer(outputFrame -> System.out.print("HiveMQ: " + outputFrame.getUtf8String()));
49+
50+
@Test
51+
void test() {
52+
final Mqtt5BlockingClient client = Mqtt5Client.builder()
53+
.identifier("test-client")
54+
.serverHost(hivemq.getHost())
55+
.serverPort(hivemq.getMqttPort())
56+
.buildBlocking();
57+
58+
client.connectWith()
59+
.willPublish()
60+
.topic("will")
61+
.qos(MqttQos.EXACTLY_ONCE)
62+
.payload("willPayload".getBytes(StandardCharsets.UTF_8))
63+
.contentType("text/plain")
64+
.correlationData("willCorrelationData".getBytes(StandardCharsets.UTF_8))
65+
.payloadFormatIndicator(Mqtt5PayloadFormatIndicator.UTF_8)
66+
.responseTopic("willResponse")
67+
.retain(false)
68+
.messageExpiryInterval(10_000)
69+
.userProperties()
70+
.add("willProperty", "willValue")
71+
.applyUserProperties()
72+
.delayInterval(50_000)
73+
.applyWillPublish()
74+
.send();
75+
await().until(() -> hivemq.getLogs()
76+
.contains(
77+
"Received CONNECT from client 'test-client': Protocol version: 'V_5', Clean Start: 'true', Session Expiry Interval: '0', Keep Alive: '60', Maximum Packet Size: '268435460', Receive Maximum: '65535', Topic Alias Maximum: '0', Request Problem Information: 'true', Request Response Information: 'false', Username: 'null', Password: 'null', Auth Method: 'null', Auth Data (Base64): 'null', User Properties: 'null', Will: { Topic: 'will', Payload: 'willPayload', QoS: '2', Retained: 'false', Message Expiry Interval: '10000', Duplicate Delivery: 'false', Correlation Data: 'willCorrelationData', Response Topic: 'willResponse', Content Type: 'text/plain', Payload Format Indicator: 'UTF_8', Subscription Identifiers: '[]', User Properties: [Name: 'willProperty', Value: 'willValue'], Will Delay: '50000' }"));
78+
await().until(() -> hivemq.getLogs()
79+
.contains(
80+
"Sent CONNACK to client 'test-client': Reason Code: 'SUCCESS', Session Present: 'false', Session Expiry Interval: 'null', Assigned ClientId 'null', Maximum QoS: 'EXACTLY_ONCE', Maximum Packet Size: '268435460', Receive Maximum: '10', Topic Alias Maximum: '5', Reason String: 'null', Response Information: 'null', Server Keep Alive: 'null', Server Reference: 'null', Shared Subscription Available: 'true', Wildcards Available: 'true', Retain Available: 'true', Subscription Identifiers Available: 'true', Auth Method: 'null', Auth Data (Base64): 'null', User Properties: 'null'"));
81+
82+
client.subscribeWith().topicFilter("#").send();
83+
await().until(() -> hivemq.getLogs()
84+
.contains(
85+
"Received SUBSCRIBE from client 'test-client': Topics: { [Topic: '#', QoS: '2', Retain As Published: 'false', No Local: 'false', Retain Handling: 'SEND'] }, Subscription Identifier: '1', User Properties: 'null'"));
86+
await().until(() -> hivemq.getLogs()
87+
.contains(
88+
"Sent SUBACK to client 'test-client': Suback Reason Codes: { [Reason Code: 'GRANTED_QOS_2'] }, Reason String: 'null', User Properties: 'null'"));
89+
90+
client.publishWith()
91+
.topic("publish")
92+
.qos(MqttQos.EXACTLY_ONCE)
93+
.payload("payload1".getBytes(StandardCharsets.UTF_8))
94+
.contentType("text/plain")
95+
.correlationData("willCorrelationData".getBytes(StandardCharsets.UTF_8))
96+
.payloadFormatIndicator(Mqtt5PayloadFormatIndicator.UTF_8)
97+
.responseTopic("publishResponse")
98+
.retain(false)
99+
.messageExpiryInterval(10_000)
100+
.userProperties()
101+
.add("publishProperty", "publishValue")
102+
.applyUserProperties()
103+
.send();
104+
await().until(() -> hivemq.getLogs()
105+
.contains(
106+
"Received PUBLISH from client 'test-client' for topic 'publish': Payload: 'payload1', QoS: '2', Retained: 'false', Message Expiry Interval: '10000', Duplicate Delivery: 'false', Correlation Data: 'willCorrelationData', Response Topic: 'publishResponse', Content Type: 'text/plain', Payload Format Indicator: 'UTF_8', Subscription Identifiers: '[]', User Properties: [Name: 'publishProperty', Value: 'publishValue']"));
107+
await().until(() -> hivemq.getLogs()
108+
.contains(
109+
"Sent PUBREC to client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
110+
await().until(() -> hivemq.getLogs()
111+
.contains(
112+
"Received PUBREL from client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
113+
await().until(() -> hivemq.getLogs()
114+
.contains(
115+
"Sent PUBCOMP to client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
116+
117+
await().until(() -> hivemq.getLogs()
118+
.contains(
119+
"Sent PUBLISH to client 'test-client' on topic 'publish': Payload: 'payload1', QoS: '2', Retained: 'false', Message Expiry Interval: '10000', Duplicate Delivery: 'false', Correlation Data: 'willCorrelationData', Response Topic: 'publishResponse', Content Type: 'text/plain', Payload Format Indicator: 'UTF_8', Subscription Identifiers: '[1]', User Properties: [Name: 'publishProperty', Value: 'publishValue']"));
120+
await().until(() -> hivemq.getLogs()
121+
.contains(
122+
"Received PUBREC from client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
123+
await().until(() -> hivemq.getLogs()
124+
.contains(
125+
"Sent PUBREL to client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
126+
await().until(() -> hivemq.getLogs()
127+
.contains(
128+
"Received PUBCOMP from client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
129+
130+
client.publishWith()
131+
.topic("publish")
132+
.qos(MqttQos.AT_LEAST_ONCE)
133+
.payload("payload2".getBytes(StandardCharsets.UTF_8))
134+
.contentType("text/plain")
135+
.correlationData("willCorrelationData".getBytes(StandardCharsets.UTF_8))
136+
.payloadFormatIndicator(Mqtt5PayloadFormatIndicator.UTF_8)
137+
.responseTopic("publishResponse")
138+
.retain(false)
139+
.messageExpiryInterval(10_000)
140+
.userProperties()
141+
.add("publishProperty", "publishValue")
142+
.applyUserProperties()
143+
.send();
144+
await().until(() -> hivemq.getLogs()
145+
.contains(
146+
"Received PUBLISH from client 'test-client' for topic 'publish': Payload: 'payload2', QoS: '1', Retained: 'false', Message Expiry Interval: '10000', Duplicate Delivery: 'false', Correlation Data: 'willCorrelationData', Response Topic: 'publishResponse', Content Type: 'text/plain', Payload Format Indicator: 'UTF_8', Subscription Identifiers: '[]', User Properties: [Name: 'publishProperty', Value: 'publishValue']"));
147+
await().until(() -> hivemq.getLogs()
148+
.contains(
149+
"Sent PUBACK to client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
150+
await().until(() -> hivemq.getLogs()
151+
.contains(
152+
"Sent PUBLISH to client 'test-client' on topic 'publish': Payload: 'payload2', QoS: '1', Retained: 'false', Message Expiry Interval: '10000', Duplicate Delivery: 'false', Correlation Data: 'willCorrelationData', Response Topic: 'publishResponse', Content Type: 'text/plain', Payload Format Indicator: 'UTF_8', Subscription Identifiers: '[1]', User Properties: [Name: 'publishProperty', Value: 'publishValue']"));
153+
await().until(() -> hivemq.getLogs()
154+
.contains(
155+
"Received PUBACK from client 'test-client': Reason Code: 'SUCCESS', Reason String: 'null', User Properties: 'null'"));
156+
157+
client.unsubscribeWith().topicFilter("#").send();
158+
await().until(() -> hivemq.getLogs()
159+
.contains(
160+
"Received UNSUBSCRIBE from client 'test-client': Topics: { [Topic: '#'] }, User Properties: 'null'"));
161+
await().until(() -> hivemq.getLogs()
162+
.contains(
163+
"Sent UNSUBACK to client 'test-client': Unsuback Reason Codes: { [Reason Code: 'SUCCESS'] }, Reason String: 'null', User Properties: 'null'"));
164+
165+
client.disconnect();
166+
await().until(() -> hivemq.getLogs()
167+
.contains(
168+
"Received DISCONNECT from client 'test-client': Reason Code: 'NORMAL_DISCONNECTION', Reason String: 'null', Server Reference: 'null', Session Expiry: 'null', User Properties: 'null'"));
169+
}
170+
}

0 commit comments

Comments
 (0)