summaryrefslogtreecommitdiffstats
path: root/src/go/plugin/go.d/modules/vernemq
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-26 08:15:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-26 08:15:35 +0000
commitf09848204fa5283d21ea43e262ee41aa578e1808 (patch)
treec62385d7adf209fa6a798635954d887f718fb3fb /src/go/plugin/go.d/modules/vernemq
parentReleasing debian version 1.46.3-2. (diff)
downloadnetdata-f09848204fa5283d21ea43e262ee41aa578e1808.tar.xz
netdata-f09848204fa5283d21ea43e262ee41aa578e1808.zip
Merging upstream version 1.47.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/go/plugin/go.d/modules/vernemq')
l---------src/go/plugin/go.d/modules/vernemq/README.md1
-rw-r--r--src/go/plugin/go.d/modules/vernemq/charts.go911
-rw-r--r--src/go/plugin/go.d/modules/vernemq/collect.go288
-rw-r--r--src/go/plugin/go.d/modules/vernemq/config_schema.json183
-rw-r--r--src/go/plugin/go.d/modules/vernemq/init.go26
-rw-r--r--src/go/plugin/go.d/modules/vernemq/integrations/vernemq.md332
-rw-r--r--src/go/plugin/go.d/modules/vernemq/metadata.yaml670
-rw-r--r--src/go/plugin/go.d/modules/vernemq/metrics.go150
-rw-r--r--src/go/plugin/go.d/modules/vernemq/testdata/config.json20
-rw-r--r--src/go/plugin/go.d/modules/vernemq/testdata/config.yaml17
-rw-r--r--src/go/plugin/go.d/modules/vernemq/testdata/metrics-v1.10.1-mqtt5.txt416
-rw-r--r--src/go/plugin/go.d/modules/vernemq/testdata/non_vernemq.txt27
-rw-r--r--src/go/plugin/go.d/modules/vernemq/vernemq.go113
-rw-r--r--src/go/plugin/go.d/modules/vernemq/vernemq_test.go578
14 files changed, 3732 insertions, 0 deletions
diff --git a/src/go/plugin/go.d/modules/vernemq/README.md b/src/go/plugin/go.d/modules/vernemq/README.md
new file mode 120000
index 00000000..3d984de7
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/README.md
@@ -0,0 +1 @@
+integrations/vernemq.md \ No newline at end of file
diff --git a/src/go/plugin/go.d/modules/vernemq/charts.go b/src/go/plugin/go.d/modules/vernemq/charts.go
new file mode 100644
index 00000000..5d81a26b
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/charts.go
@@ -0,0 +1,911 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package vernemq
+
+import "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+
+type (
+ Charts = module.Charts
+ Chart = module.Chart
+ Dims = module.Dims
+ Dim = module.Dim
+)
+
+var charts = Charts{
+ chartOpenSockets.Copy(),
+ chartSocketEvents.Copy(),
+ chartClientKeepaliveExpired.Copy(),
+ chartSocketErrors.Copy(),
+ chartSocketCloseTimeout.Copy(),
+
+ chartQueueProcesses.Copy(),
+ chartQueueProcessesEvents.Copy(),
+ chartQueueProcessesOfflineStorage.Copy(),
+ chartQueueMessages.Copy(),
+ chartQueueUndeliveredMessages.Copy(),
+
+ chartRouterSubscriptions.Copy(),
+ chartRouterMatchedSubscriptions.Copy(),
+ chartRouterMemory.Copy(),
+
+ chartAverageSchedulerUtilization.Copy(),
+ chartSchedulerUtilization.Copy(),
+ chartSystemProcesses.Copy(),
+ chartSystemReductions.Copy(),
+ chartSystemContextSwitches.Copy(),
+ chartSystemIO.Copy(),
+ chartSystemRunQueue.Copy(),
+ chartSystemGCCount.Copy(),
+ chartSystemGCWordsReclaimed.Copy(),
+ chartSystemMemoryAllocated.Copy(),
+
+ chartBandwidth.Copy(),
+
+ chartRetainMessages.Copy(),
+ chartRetainMemoryUsage.Copy(),
+
+ chartClusterCommunicationBandwidth.Copy(),
+ chartClusterCommunicationDropped.Copy(),
+ chartNetSplitUnresolved.Copy(),
+ chartNetSplits.Copy(),
+
+ chartMQTTv5AUTH.Copy(),
+ chartMQTTv5AUTHReceivedReason.Copy(),
+ chartMQTTv5AUTHSentReason.Copy(),
+
+ chartMQTTv3v5CONNECT.Copy(),
+ chartMQTTv3v5CONNACKSentReason.Copy(),
+
+ chartMQTTv3v5DISCONNECT.Copy(),
+ chartMQTTv5DISCONNECTReceivedReason.Copy(),
+ chartMQTTv5DISCONNECTSentReason.Copy(),
+
+ chartMQTTv3v5SUBSCRIBE.Copy(),
+ chartMQTTv3v5SUBSCRIBEError.Copy(),
+ chartMQTTv3v5SUBSCRIBEAuthError.Copy(),
+
+ chartMQTTv3v5UNSUBSCRIBE.Copy(),
+ chartMQTTv3v5UNSUBSCRIBEError.Copy(),
+
+ chartMQTTv3v5PUBLISH.Copy(),
+ chartMQTTv3v5PUBLISHErrors.Copy(),
+ chartMQTTv3v5PUBLISHAuthErrors.Copy(),
+ chartMQTTv3v5PUBACK.Copy(),
+ chartMQTTv5PUBACKReceivedReason.Copy(),
+ chartMQTTv5PUBACKSentReason.Copy(),
+ chartMQTTv3v5PUBACKUnexpected.Copy(),
+ chartMQTTv3v5PUBREC.Copy(),
+ chartMQTTv5PUBRECReceivedReason.Copy(),
+ chartMQTTv5PUBRECSentReason.Copy(),
+ chartMQTTv3PUBRECUnexpected.Copy(),
+ chartMQTTv3v5PUBREL.Copy(),
+ chartMQTTv5PUBRELReceivedReason.Copy(),
+ chartMQTTv5PUBRELSentReason.Copy(),
+ chartMQTTv3v5PUBCOMP.Copy(),
+ chartMQTTv5PUBCOMPReceivedReason.Copy(),
+ chartMQTTv5PUBCOMPSentReason.Copy(),
+ chartMQTTv3v5PUBCOMPUnexpected.Copy(),
+
+ chartMQTTv3v5PING.Copy(),
+
+ chartUptime.Copy(),
+}
+
+// Sockets
+var (
+ chartOpenSockets = Chart{
+ ID: "sockets",
+ Title: "Open Sockets",
+ Units: "sockets",
+ Fam: "sockets",
+ Ctx: "vernemq.sockets",
+ Dims: Dims{
+ {ID: "open_sockets", Name: "open"},
+ },
+ }
+ chartSocketEvents = Chart{
+ ID: "socket_events",
+ Title: "Socket Open and Close Events",
+ Units: "events/s",
+ Fam: "sockets",
+ Ctx: "vernemq.socket_operations",
+ Dims: Dims{
+ {ID: metricSocketOpen, Name: "open", Algo: module.Incremental},
+ {ID: metricSocketClose, Name: "close", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartClientKeepaliveExpired = Chart{
+ ID: "client_keepalive_expired",
+ Title: "Closed Sockets due to Keepalive Time Expired",
+ Units: "sockets/s",
+ Fam: "sockets",
+ Ctx: "vernemq.client_keepalive_expired",
+ Dims: Dims{
+ {ID: metricClientKeepaliveExpired, Name: "closed", Algo: module.Incremental},
+ },
+ }
+ chartSocketCloseTimeout = Chart{
+ ID: "socket_close_timeout",
+ Title: "Closed Sockets due to no CONNECT Frame On Time",
+ Units: "sockets/s",
+ Fam: "sockets",
+ Ctx: "vernemq.socket_close_timeout",
+ Dims: Dims{
+ {ID: metricSocketCloseTimeout, Name: "closed", Algo: module.Incremental},
+ },
+ }
+ chartSocketErrors = Chart{
+ ID: "socket_errors",
+ Title: "Socket Errors",
+ Units: "errors/s",
+ Fam: "sockets",
+ Ctx: "vernemq.socket_errors",
+ Dims: Dims{
+ {ID: metricSocketError, Name: "errors", Algo: module.Incremental},
+ },
+ }
+)
+
+// Queues
+var (
+ chartQueueProcesses = Chart{
+ ID: "queue_processes",
+ Title: "Living Queues in an Online or an Offline State",
+ Units: "queue processes",
+ Fam: "queues",
+ Ctx: "vernemq.queue_processes",
+ Dims: Dims{
+ {ID: metricQueueProcesses, Name: "queue_processes"},
+ },
+ }
+ chartQueueProcessesEvents = Chart{
+ ID: "queue_processes_events",
+ Title: "Queue Processes Setup and Teardown Events",
+ Units: "events/s",
+ Fam: "queues",
+ Ctx: "vernemq.queue_processes_operations",
+ Dims: Dims{
+ {ID: metricQueueSetup, Name: "setup", Algo: module.Incremental},
+ {ID: metricQueueTeardown, Name: "teardown", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartQueueProcessesOfflineStorage = Chart{
+ ID: "queue_process_init_from_storage",
+ Title: "Queue Processes Initialized from Offline Storage",
+ Units: "queue processes/s",
+ Fam: "queues",
+ Ctx: "vernemq.queue_process_init_from_storage",
+ Dims: Dims{
+ {ID: metricQueueInitializedFromStorage, Name: "queue processes", Algo: module.Incremental},
+ },
+ }
+ chartQueueMessages = Chart{
+ ID: "queue_messages",
+ Title: "Received and Sent PUBLISH Messages",
+ Units: "messages/s",
+ Fam: "queues",
+ Ctx: "vernemq.queue_messages",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricQueueMessageIn, Name: "received", Algo: module.Incremental},
+ {ID: metricQueueMessageOut, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartQueueUndeliveredMessages = Chart{
+ ID: "queue_undelivered_messages",
+ Title: "Undelivered PUBLISH Messages",
+ Units: "messages/s",
+ Fam: "queues",
+ Ctx: "vernemq.queue_undelivered_messages",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: metricQueueMessageDrop, Name: "dropped", Algo: module.Incremental},
+ {ID: metricQueueMessageExpired, Name: "expired", Algo: module.Incremental},
+ {ID: metricQueueMessageUnhandled, Name: "unhandled", Algo: module.Incremental},
+ },
+ }
+)
+
+// Subscriptions
+var (
+ chartRouterSubscriptions = Chart{
+ ID: "router_subscriptions",
+ Title: "Subscriptions in the Routing Table",
+ Units: "subscriptions",
+ Fam: "subscriptions",
+ Ctx: "vernemq.router_subscriptions",
+ Dims: Dims{
+ {ID: metricRouterSubscriptions, Name: "subscriptions"},
+ },
+ }
+ chartRouterMatchedSubscriptions = Chart{
+ ID: "router_matched_subscriptions",
+ Title: "Matched Subscriptions",
+ Units: "subscriptions/s",
+ Fam: "subscriptions",
+ Ctx: "vernemq.router_matched_subscriptions",
+ Dims: Dims{
+ {ID: metricRouterMatchesLocal, Name: "local", Algo: module.Incremental},
+ {ID: metricRouterMatchesRemote, Name: "remote", Algo: module.Incremental},
+ },
+ }
+ chartRouterMemory = Chart{
+ ID: "router_memory",
+ Title: "Routing Table Memory Usage",
+ Units: "KiB",
+ Fam: "subscriptions",
+ Ctx: "vernemq.router_memory",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricRouterMemory, Name: "used", Div: 1024},
+ },
+ }
+)
+
+// Erlang VM
+var (
+ chartAverageSchedulerUtilization = Chart{
+ ID: "average_scheduler_utilization",
+ Title: "Average Scheduler Utilization",
+ Units: "percentage",
+ Fam: "erlang vm",
+ Ctx: "vernemq.average_scheduler_utilization",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricSystemUtilization, Name: "utilization"},
+ },
+ }
+ chartSchedulerUtilization = Chart{
+ ID: "scheduler_utilization",
+ Title: "Scheduler Utilization",
+ Units: "percentage",
+ Fam: "erlang vm",
+ Type: module.Stacked,
+ Ctx: "vernemq.system_utilization_scheduler",
+ }
+ chartSystemProcesses = Chart{
+ ID: "system_processes",
+ Title: "Erlang Processes",
+ Units: "processes",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_processes",
+ Dims: Dims{
+ {ID: metricSystemProcessCount, Name: "processes"},
+ },
+ }
+ chartSystemReductions = Chart{
+ ID: "system_reductions",
+ Title: "Reductions",
+ Units: "ops/s",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_reductions",
+ Dims: Dims{
+ {ID: metricSystemReductions, Name: "reductions", Algo: module.Incremental},
+ },
+ }
+ chartSystemContextSwitches = Chart{
+ ID: "system_context_switches",
+ Title: "Context Switches",
+ Units: "ops/s",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_context_switches",
+ Dims: Dims{
+ {ID: metricSystemContextSwitches, Name: "context switches", Algo: module.Incremental},
+ },
+ }
+ chartSystemIO = Chart{
+ ID: "system_io",
+ Title: "Received and Sent Traffic through Ports",
+ Units: "kilobits/s",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_io",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricSystemIOIn, Name: "received", Algo: module.Incremental, Mul: 8, Div: 1024},
+ {ID: metricSystemIOOut, Name: "sent", Algo: module.Incremental, Mul: 8, Div: -1024},
+ },
+ }
+ chartSystemRunQueue = Chart{
+ ID: "system_run_queue",
+ Title: "Processes that are Ready to Run on All Run-Queues",
+ Units: "processes",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_run_queue",
+ Dims: Dims{
+ {ID: metricSystemRunQueue, Name: "ready"},
+ },
+ }
+ chartSystemGCCount = Chart{
+ ID: "system_gc_count",
+ Title: "GC Count",
+ Units: "ops/s",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_gc_count",
+ Dims: Dims{
+ {ID: metricSystemGCCount, Name: "gc", Algo: module.Incremental},
+ },
+ }
+ chartSystemGCWordsReclaimed = Chart{
+ ID: "system_gc_words_reclaimed",
+ Title: "GC Words Reclaimed",
+ Units: "ops/s",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_gc_words_reclaimed",
+ Dims: Dims{
+ {ID: metricSystemWordsReclaimedByGC, Name: "words reclaimed", Algo: module.Incremental},
+ },
+ }
+ chartSystemMemoryAllocated = Chart{
+ ID: "system_allocated_memory",
+ Title: "Memory Allocated by the Erlang Processes and by the Emulator",
+ Units: "KiB",
+ Fam: "erlang vm",
+ Ctx: "vernemq.system_allocated_memory",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: metricVMMemoryProcesses, Name: "processes", Div: 1024},
+ {ID: metricVMMemorySystem, Name: "system", Div: 1024},
+ },
+ }
+)
+
+// Bandwidth
+var (
+ chartBandwidth = Chart{
+ ID: "bandwidth",
+ Title: "Bandwidth",
+ Units: "kilobits/s",
+ Fam: "bandwidth",
+ Ctx: "vernemq.bandwidth",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricBytesReceived, Name: "received", Algo: module.Incremental, Mul: 8, Div: 1024},
+ {ID: metricBytesSent, Name: "sent", Algo: module.Incremental, Mul: 8, Div: -1024},
+ },
+ }
+)
+
+// Retain
+var (
+ chartRetainMessages = Chart{
+ ID: "retain_messages",
+ Title: "Stored Retained Messages",
+ Units: "messages",
+ Fam: "retain",
+ Ctx: "vernemq.retain_messages",
+ Dims: Dims{
+ {ID: metricRetainMessages, Name: "messages"},
+ },
+ }
+ chartRetainMemoryUsage = Chart{
+ ID: "retain_memory",
+ Title: "Stored Retained Messages Memory Usage",
+ Units: "KiB",
+ Fam: "retain",
+ Ctx: "vernemq.retain_memory",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricRetainMemory, Name: "used", Div: 1024},
+ },
+ }
+)
+
+// Cluster
+var (
+ chartClusterCommunicationBandwidth = Chart{
+ ID: "cluster_bandwidth",
+ Title: "Communication with Other Cluster Nodes",
+ Units: "kilobits/s",
+ Fam: "cluster",
+ Ctx: "vernemq.cluster_bandwidth",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricClusterBytesReceived, Name: "received", Algo: module.Incremental, Mul: 8, Div: 1024},
+ {ID: metricClusterBytesSent, Name: "sent", Algo: module.Incremental, Mul: 8, Div: -1024},
+ },
+ }
+ chartClusterCommunicationDropped = Chart{
+ ID: "cluster_dropped",
+ Title: "Traffic Dropped During Communication with Other Cluster Nodes",
+ Units: "kilobits/s",
+ Fam: "cluster",
+ Type: module.Area,
+ Ctx: "vernemq.cluster_dropped",
+ Dims: Dims{
+ {ID: metricClusterBytesDropped, Name: "dropped", Algo: module.Incremental, Mul: 8, Div: 1024},
+ },
+ }
+ chartNetSplitUnresolved = Chart{
+ ID: "netsplit_unresolved",
+ Title: "Unresolved Netsplits",
+ Units: "netsplits",
+ Fam: "cluster",
+ Ctx: "vernemq.netsplit_unresolved",
+ Dims: Dims{
+ {ID: "netsplit_unresolved", Name: "unresolved"},
+ },
+ }
+ chartNetSplits = Chart{
+ ID: "netsplit",
+ Title: "Netsplits",
+ Units: "netsplits/s",
+ Fam: "cluster",
+ Ctx: "vernemq.netsplits",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: metricNetSplitResolved, Name: "resolved", Algo: module.Incremental},
+ {ID: metricNetSplitDetected, Name: "detected", Algo: module.Incremental},
+ },
+ }
+)
+
+// AUTH
+var (
+ chartMQTTv5AUTH = Chart{
+ ID: "mqtt_auth",
+ Title: "v5 AUTH",
+ Units: "packets/s",
+ Fam: "mqtt auth",
+ Ctx: "vernemq.mqtt_auth",
+ Dims: Dims{
+ {ID: metricAUTHReceived, Name: "received", Algo: module.Incremental},
+ {ID: metricAUTHSent, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv5AUTHReceivedReason = Chart{
+ ID: "mqtt_auth_received_reason",
+ Title: "v5 AUTH Received by Reason",
+ Units: "packets/s",
+ Fam: "mqtt auth",
+ Ctx: "vernemq.mqtt_auth_received_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricAUTHReceived, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv5AUTHSentReason = Chart{
+ ID: "mqtt_auth_sent_reason",
+ Title: "v5 AUTH Sent by Reason",
+ Units: "packets/s",
+ Fam: "mqtt auth",
+ Ctx: "vernemq.mqtt_auth_sent_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricAUTHSent, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+)
+
+// CONNECT
+var (
+ chartMQTTv3v5CONNECT = Chart{
+ ID: "mqtt_connect",
+ Title: "v3/v5 CONNECT and CONNACK",
+ Units: "packets/s",
+ Fam: "mqtt connect",
+ Ctx: "vernemq.mqtt_connect",
+ Dims: Dims{
+ {ID: metricCONNECTReceived, Name: "CONNECT", Algo: module.Incremental},
+ {ID: metricCONNACKSent, Name: "CONNACK", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv3v5CONNACKSentReason = Chart{
+ ID: "mqtt_connack_sent_reason",
+ Title: "v3/v5 CONNACK Sent by Reason",
+ Units: "packets/s",
+ Fam: "mqtt connect",
+ Ctx: "vernemq.mqtt_connack_sent_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricCONNACKSent, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+)
+
+// DISCONNECT
+var (
+ chartMQTTv3v5DISCONNECT = Chart{
+ ID: "mqtt_disconnect",
+ Title: "v3/v5 DISCONNECT",
+ Units: "packets/s",
+ Fam: "mqtt disconnect",
+ Ctx: "vernemq.mqtt_disconnect",
+ Dims: Dims{
+ {ID: metricDISCONNECTReceived, Name: "received", Algo: module.Incremental},
+ {ID: metricDISCONNECTSent, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv5DISCONNECTReceivedReason = Chart{
+ ID: "mqtt_disconnect_received_reason",
+ Title: "v5 DISCONNECT Received by Reason",
+ Units: "packets/s",
+ Fam: "mqtt disconnect",
+ Ctx: "vernemq.mqtt_disconnect_received_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricDISCONNECTReceived, "normal_disconnect"), Name: "normal_disconnect", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv5DISCONNECTSentReason = Chart{
+ ID: "mqtt_disconnect_sent_reason",
+ Title: "v5 DISCONNECT Sent by Reason",
+ Units: "packets/s",
+ Fam: "mqtt disconnect",
+ Ctx: "vernemq.mqtt_disconnect_sent_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricDISCONNECTSent, "normal_disconnect"), Name: "normal_disconnect", Algo: module.Incremental},
+ },
+ }
+)
+
+// SUBSCRIBE
+var (
+ chartMQTTv3v5SUBSCRIBE = Chart{
+ ID: "mqtt_subscribe",
+ Title: "v3/v5 SUBSCRIBE and SUBACK",
+ Units: "packets/s",
+ Fam: "mqtt subscribe",
+ Ctx: "vernemq.mqtt_subscribe",
+ Dims: Dims{
+ {ID: metricSUBSCRIBEReceived, Name: "SUBSCRIBE", Algo: module.Incremental},
+ {ID: metricSUBACKSent, Name: "SUBACK", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv3v5SUBSCRIBEError = Chart{
+ ID: "mqtt_subscribe_error",
+ Title: "v3/v5 Failed SUBSCRIBE Operations due to a Netsplit",
+ Units: "ops/s",
+ Fam: "mqtt subscribe",
+ Ctx: "vernemq.mqtt_subscribe_error",
+ Dims: Dims{
+ {ID: metricSUBSCRIBEError, Name: "failed", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5SUBSCRIBEAuthError = Chart{
+ ID: "mqtt_subscribe_auth_error",
+ Title: "v3/v5 Unauthorized SUBSCRIBE Attempts",
+ Units: "attempts/s",
+ Fam: "mqtt subscribe",
+ Ctx: "vernemq.mqtt_subscribe_auth_error",
+ Dims: Dims{
+ {ID: metricSUBSCRIBEAuthError, Name: "unauth", Algo: module.Incremental},
+ },
+ }
+)
+
+// UNSUBSCRIBE
+var (
+ chartMQTTv3v5UNSUBSCRIBE = Chart{
+ ID: "mqtt_unsubscribe",
+ Title: "v3/v5 UNSUBSCRIBE and UNSUBACK",
+ Units: "packets/s",
+ Fam: "mqtt unsubscribe",
+ Ctx: "vernemq.mqtt_unsubscribe",
+ Dims: Dims{
+ {ID: metricUNSUBSCRIBEReceived, Name: "UNSUBSCRIBE", Algo: module.Incremental},
+ {ID: metricUNSUBACKSent, Name: "UNSUBACK", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv3v5UNSUBSCRIBEError = Chart{
+ ID: "mqtt_unsubscribe_error",
+ Title: "v3/v5 Failed UNSUBSCRIBE Operations due to a Netsplit",
+ Units: "ops/s",
+ Fam: "mqtt unsubscribe",
+ Ctx: "vernemq.mqtt_unsubscribe_error",
+ Dims: Dims{
+ {ID: metricUNSUBSCRIBEError, Name: "failed", Algo: module.Incremental},
+ },
+ }
+)
+
+// PUBLISH
+var (
+ chartMQTTv3v5PUBLISH = Chart{
+ ID: "mqtt_publish",
+ Title: "v3/v5 QoS 0,1,2 PUBLISH",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_publish",
+ Dims: Dims{
+ {ID: metricPUBSLISHReceived, Name: "received", Algo: module.Incremental},
+ {ID: metricPUBSLIHSent, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv3v5PUBLISHErrors = Chart{
+ ID: "mqtt_publish_errors",
+ Title: "v3/v5 Failed PUBLISH Operations due to a Netsplit",
+ Units: "ops/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_publish_errors",
+ Dims: Dims{
+ {ID: metricPUBLISHError, Name: "failed", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5PUBLISHAuthErrors = Chart{
+ ID: "mqtt_publish_auth_errors",
+ Title: "v3/v5 Unauthorized PUBLISH Attempts",
+ Units: "attempts/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_publish_auth_errors",
+ Type: module.Area,
+ Dims: Dims{
+ {ID: metricPUBLISHAuthError, Name: "unauth", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5PUBACK = Chart{
+ ID: "mqtt_puback",
+ Title: "v3/v5 QoS 1 PUBACK",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_puback",
+ Dims: Dims{
+ {ID: metricPUBACKReceived, Name: "received", Algo: module.Incremental},
+ {ID: metricPUBACKSent, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv5PUBACKReceivedReason = Chart{
+ ID: "mqtt_puback_received_reason",
+ Title: "v5 PUBACK QoS 1 Received by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_puback_received_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBACKReceived, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv5PUBACKSentReason = Chart{
+ ID: "mqtt_puback_sent_reason",
+ Title: "v5 PUBACK QoS 1 Sent by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_puback_sent_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBACKSent, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5PUBACKUnexpected = Chart{
+ ID: "mqtt_puback_unexpected",
+ Title: "v3/v5 PUBACK QoS 1 Received Unexpected Messages",
+ Units: "messages/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_puback_invalid_error",
+ Dims: Dims{
+ {ID: metricPUBACKInvalid, Name: "unexpected", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5PUBREC = Chart{
+ ID: "mqtt_pubrec",
+ Title: "v3/v5 PUBREC QoS 2",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubrec",
+ Dims: Dims{
+ {ID: metricPUBRECReceived, Name: "received", Algo: module.Incremental},
+ {ID: metricPUBRECSent, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv5PUBRECReceivedReason = Chart{
+ ID: "mqtt_pubrec_received_reason",
+ Title: "v5 PUBREC QoS 2 Received by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubrec_received_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBRECReceived, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv5PUBRECSentReason = Chart{
+ ID: "mqtt_pubrec_sent_reason",
+ Title: "v5 PUBREC QoS 2 Sent by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubrec_sent_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBRECSent, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3PUBRECUnexpected = Chart{
+ ID: "mqtt_pubrec_unexpected",
+ Title: "v3 PUBREC QoS 2 Received Unexpected Messages",
+ Units: "messages/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubrec_invalid_error",
+ Dims: Dims{
+ {ID: metricPUBRECInvalid, Name: "unexpected", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5PUBREL = Chart{
+ ID: "mqtt_pubrel",
+ Title: "v3/v5 PUBREL QoS 2",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubrel",
+ Dims: Dims{
+ {ID: metricPUBRELReceived, Name: "received", Algo: module.Incremental},
+ {ID: metricPUBRELSent, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv5PUBRELReceivedReason = Chart{
+ ID: "mqtt_pubrel_received_reason",
+ Title: "v5 PUBREL QoS 2 Received by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubrel_received_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBRELReceived, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv5PUBRELSentReason = Chart{
+ ID: "mqtt_pubrel_sent_reason",
+ Title: "v5 PUBREL QoS 2 Sent by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubrel_sent_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBRELSent, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5PUBCOMP = Chart{
+ ID: "mqtt_pubcomp",
+ Title: "v3/v5 PUBCOMP QoS 2",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubcom",
+ Dims: Dims{
+ {ID: metricPUBCOMPReceived, Name: "received", Algo: module.Incremental},
+ {ID: metricPUBCOMPSent, Name: "sent", Algo: module.Incremental, Mul: -1},
+ },
+ }
+ chartMQTTv5PUBCOMPReceivedReason = Chart{
+ ID: "mqtt_pubcomp_received_reason",
+ Title: "v5 PUBCOMP QoS 2 Received by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubcomp_received_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBCOMPReceived, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv5PUBCOMPSentReason = Chart{
+ ID: "mqtt_pubcomp_sent_reason",
+ Title: "v5 PUBCOMP QoS 2 Sent by Reason",
+ Units: "packets/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubcomp_sent_reason",
+ Type: module.Stacked,
+ Dims: Dims{
+ {ID: join(metricPUBCOMPSent, "success"), Name: "success", Algo: module.Incremental},
+ },
+ }
+ chartMQTTv3v5PUBCOMPUnexpected = Chart{
+ ID: "mqtt_pubcomp_unexpected",
+ Title: "v3/v5 PUBCOMP QoS 2 Received Unexpected Messages",
+ Units: "messages/s",
+ Fam: "mqtt publish",
+ Ctx: "vernemq.mqtt_pubcomp_invalid_error",
+ Dims: Dims{
+ {ID: metricPUNCOMPInvalid, Name: "unexpected", Algo: module.Incremental},
+ },
+ }
+)
+
+// PING
+var (
+ chartMQTTv3v5PING = Chart{
+ ID: "mqtt_ping",
+ Title: "v3/v5 PING",
+ Units: "packets/s",
+ Fam: "mqtt ping",
+ Ctx: "vernemq.mqtt_ping",
+ Dims: Dims{
+ {ID: metricPINGREQReceived, Name: "PINGREQ", Algo: module.Incremental},
+ {ID: metricPINGRESPSent, Name: "PINGRESP", Algo: module.Incremental, Mul: -1},
+ },
+ }
+)
+
+var (
+ chartUptime = Chart{
+ ID: "node_uptime",
+ Title: "Node Uptime",
+ Units: "seconds",
+ Fam: "uptime",
+ Ctx: "vernemq.node_uptime",
+ Dims: Dims{
+ {ID: metricSystemWallClock, Name: "time", Div: 1000},
+ },
+ }
+)
+
+func (v *VerneMQ) notifyNewScheduler(name string) {
+ if v.cache[name] {
+ return
+ }
+ v.cache[name] = true
+
+ id := chartSchedulerUtilization.ID
+ num := name[len("system_utilization_scheduler_"):]
+
+ v.addAbsDimToChart(id, name, num)
+}
+
+func (v *VerneMQ) notifyNewReason(name, reason string) {
+ if reason == "success" || reason == "normal_disconnect" {
+ return
+ }
+ key := join(name, reason)
+ if v.cache[key] {
+ return
+ }
+ v.cache[key] = true
+
+ var chart Chart
+ switch name {
+ case metricAUTHReceived:
+ chart = chartMQTTv5AUTHReceivedReason
+ case metricAUTHSent:
+ chart = chartMQTTv5AUTHSentReason
+ case metricCONNACKSent:
+ chart = chartMQTTv3v5CONNACKSentReason
+ case metricDISCONNECTReceived:
+ chart = chartMQTTv5DISCONNECTReceivedReason
+ case metricDISCONNECTSent:
+ chart = chartMQTTv5DISCONNECTSentReason
+ case metricPUBACKReceived:
+ chart = chartMQTTv5PUBACKReceivedReason
+ case metricPUBACKSent:
+ chart = chartMQTTv5PUBACKSentReason
+ case metricPUBRECReceived:
+ chart = chartMQTTv5PUBRECReceivedReason
+ case metricPUBRECSent:
+ chart = chartMQTTv5PUBRECSentReason
+ case metricPUBRELReceived:
+ chart = chartMQTTv5PUBRELReceivedReason
+ case metricPUBRELSent:
+ chart = chartMQTTv5PUBRELSentReason
+ case metricPUBCOMPReceived:
+ chart = chartMQTTv5PUBCOMPReceivedReason
+ case metricPUBCOMPSent:
+ chart = chartMQTTv5PUBCOMPSentReason
+ default:
+ v.Warningf("unknown metric name, wont be added to the charts: '%s'", name)
+ return
+ }
+
+ v.addIncDimToChart(chart.ID, key, reason)
+}
+
+func (v *VerneMQ) addAbsDimToChart(chartID, dimID, dimName string) {
+ v.addDimToChart(chartID, dimID, dimName, false)
+}
+
+func (v *VerneMQ) addIncDimToChart(chartID, dimID, dimName string) {
+ v.addDimToChart(chartID, dimID, dimName, true)
+}
+
+func (v *VerneMQ) addDimToChart(chartID, dimID, dimName string, inc bool) {
+ chart := v.Charts().Get(chartID)
+ if chart == nil {
+ v.Warningf("add '%s' dim: couldn't find '%s' chart", dimID, chartID)
+ return
+ }
+
+ dim := &Dim{ID: dimID, Name: dimName}
+ if inc {
+ dim.Algo = module.Incremental
+ }
+
+ if err := chart.AddDim(dim); err != nil {
+ v.Warningf("add '%s' dim: %v", dimID, err)
+ return
+ }
+ chart.MarkNotCreated()
+}
diff --git a/src/go/plugin/go.d/modules/vernemq/collect.go b/src/go/plugin/go.d/modules/vernemq/collect.go
new file mode 100644
index 00000000..c6fb3ecb
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/collect.go
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package vernemq
+
+import (
+ "errors"
+ "strings"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/prometheus"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
+)
+
+func isValidVerneMQMetrics(pms prometheus.Series) bool {
+ return pms.FindByName(metricPUBLISHError).Len() > 0 && pms.FindByName(metricRouterSubscriptions).Len() > 0
+}
+
+func (v *VerneMQ) collect() (map[string]int64, error) {
+ pms, err := v.prom.ScrapeSeries()
+ if err != nil {
+ return nil, err
+ }
+
+ if !isValidVerneMQMetrics(pms) {
+ return nil, errors.New("returned metrics aren't VerneMQ metrics")
+ }
+
+ mx := v.collectVerneMQ(pms)
+
+ return stm.ToMap(mx), nil
+}
+
+func (v *VerneMQ) collectVerneMQ(pms prometheus.Series) map[string]float64 {
+ mx := make(map[string]float64)
+ collectSockets(mx, pms)
+ collectQueues(mx, pms)
+ collectSubscriptions(mx, pms)
+ v.collectErlangVM(mx, pms)
+ collectBandwidth(mx, pms)
+ collectRetain(mx, pms)
+ collectCluster(mx, pms)
+ collectUptime(mx, pms)
+
+ v.collectAUTH(mx, pms)
+ v.collectCONNECT(mx, pms)
+ v.collectDISCONNECT(mx, pms)
+ v.collectSUBSCRIBE(mx, pms)
+ v.collectUNSUBSCRIBE(mx, pms)
+ v.collectPUBLISH(mx, pms)
+ v.collectPING(mx, pms)
+ v.collectMQTTInvalidMsgSize(mx, pms)
+ return mx
+}
+
+func (v *VerneMQ) collectCONNECT(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricCONNECTReceived,
+ metricCONNACKSent,
+ )
+ v.collectMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectDISCONNECT(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricDISCONNECTReceived,
+ metricDISCONNECTSent,
+ )
+ v.collectMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectPUBLISH(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricPUBACKReceived,
+ metricPUBACKSent,
+ metricPUBACKInvalid,
+
+ metricPUBCOMPReceived,
+ metricPUBCOMPSent,
+ metricPUNCOMPInvalid,
+
+ metricPUBSLISHReceived,
+ metricPUBSLIHSent,
+ metricPUBLISHError,
+ metricPUBLISHAuthError,
+
+ metricPUBRECReceived,
+ metricPUBRECSent,
+ metricPUBRECInvalid,
+
+ metricPUBRELReceived,
+ metricPUBRELSent,
+ )
+ v.collectMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectSUBSCRIBE(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricSUBSCRIBEReceived,
+ metricSUBACKSent,
+ metricSUBSCRIBEError,
+ metricSUBSCRIBEAuthError,
+ )
+ v.collectMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectUNSUBSCRIBE(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricUNSUBSCRIBEReceived,
+ metricUNSUBACKSent,
+ metricUNSUBSCRIBEError,
+ )
+ v.collectMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectPING(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricPINGREQReceived,
+ metricPINGRESPSent,
+ )
+ v.collectMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectAUTH(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricAUTHReceived,
+ metricAUTHSent,
+ )
+ v.collectMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectMQTTInvalidMsgSize(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByName(metricMQTTInvalidMsgSizeError)
+ v.collectMQTT(mx, pms)
+}
+
+func collectSockets(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricSocketClose,
+ metricSocketCloseTimeout,
+ metricSocketError,
+ metricSocketOpen,
+ metricClientKeepaliveExpired,
+ )
+ collectNonMQTT(mx, pms)
+ mx["open_sockets"] = mx[metricSocketOpen] - mx[metricSocketClose]
+}
+
+func collectQueues(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricQueueInitializedFromStorage,
+ metricQueueMessageDrop,
+ metricQueueMessageExpired,
+ metricQueueMessageIn,
+ metricQueueMessageOut,
+ metricQueueMessageUnhandled,
+ metricQueueProcesses,
+ metricQueueSetup,
+ metricQueueTeardown,
+ )
+ collectNonMQTT(mx, pms)
+}
+
+func collectSubscriptions(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricRouterMatchesLocal,
+ metricRouterMatchesRemote,
+ metricRouterMemory,
+ metricRouterSubscriptions,
+ )
+ collectNonMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectErlangVM(mx map[string]float64, pms prometheus.Series) {
+ v.collectSchedulersUtilization(mx, pms)
+ pms = pms.FindByNames(
+ metricSystemContextSwitches,
+ metricSystemGCCount,
+ metricSystemIOIn,
+ metricSystemIOOut,
+ metricSystemProcessCount,
+ metricSystemReductions,
+ metricSystemRunQueue,
+ metricSystemUtilization,
+ metricSystemWordsReclaimedByGC,
+ metricVMMemoryProcesses,
+ metricVMMemorySystem,
+ )
+ collectNonMQTT(mx, pms)
+}
+
+func (v *VerneMQ) collectSchedulersUtilization(mx map[string]float64, pms prometheus.Series) {
+ for _, pm := range pms {
+ if isSchedulerUtilizationMetric(pm) {
+ mx[pm.Name()] += pm.Value
+ v.notifyNewScheduler(pm.Name())
+ }
+ }
+}
+
+func collectBandwidth(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricBytesReceived,
+ metricBytesSent,
+ )
+ collectNonMQTT(mx, pms)
+}
+
+func collectRetain(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricRetainMemory,
+ metricRetainMessages,
+ )
+ collectNonMQTT(mx, pms)
+}
+
+func collectCluster(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByNames(
+ metricClusterBytesDropped,
+ metricClusterBytesReceived,
+ metricClusterBytesSent,
+ metricNetSplitDetected,
+ metricNetSplitResolved,
+ )
+ collectNonMQTT(mx, pms)
+ mx["netsplit_unresolved"] = mx[metricNetSplitDetected] - mx[metricNetSplitResolved]
+}
+
+func collectUptime(mx map[string]float64, pms prometheus.Series) {
+ pms = pms.FindByName(metricSystemWallClock)
+ collectNonMQTT(mx, pms)
+}
+
+func collectNonMQTT(mx map[string]float64, pms prometheus.Series) {
+ for _, pm := range pms {
+ mx[pm.Name()] += pm.Value
+ }
+}
+
+func (v *VerneMQ) collectMQTT(mx map[string]float64, pms prometheus.Series) {
+ for _, pm := range pms {
+ if !isMQTTMetric(pm) {
+ continue
+ }
+ version := versionLabelValue(pm)
+ if version == "" {
+ continue
+ }
+
+ mx[pm.Name()] += pm.Value
+ mx[join(pm.Name(), "v", version)] += pm.Value
+
+ if reason := reasonCodeLabelValue(pm); reason != "" {
+ mx[join(pm.Name(), reason)] += pm.Value
+ mx[join(pm.Name(), "v", version, reason)] += pm.Value
+
+ v.notifyNewReason(pm.Name(), reason)
+ }
+ }
+}
+
+func isMQTTMetric(pm prometheus.SeriesSample) bool {
+ return strings.HasPrefix(pm.Name(), "mqtt_")
+}
+
+func isSchedulerUtilizationMetric(pm prometheus.SeriesSample) bool {
+ return strings.HasPrefix(pm.Name(), "system_utilization_scheduler_")
+}
+
+func reasonCodeLabelValue(pm prometheus.SeriesSample) string {
+ if v := pm.Labels.Get("reason_code"); v != "" {
+ return v
+ }
+ // "mqtt_connack_sent" v4 has return_code
+ return pm.Labels.Get("return_code")
+}
+
+func versionLabelValue(pm prometheus.SeriesSample) string {
+ return pm.Labels.Get("mqtt_version")
+}
+
+func join(a, b string, rest ...string) string {
+ v := a + "_" + b
+ switch len(rest) {
+ case 0:
+ return v
+ default:
+ return join(v, rest[0], rest[1:]...)
+ }
+}
diff --git a/src/go/plugin/go.d/modules/vernemq/config_schema.json b/src/go/plugin/go.d/modules/vernemq/config_schema.json
new file mode 100644
index 00000000..092d7f41
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/config_schema.json
@@ -0,0 +1,183 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "VerneMQ collector configuration.",
+ "type": "object",
+ "properties": {
+ "update_every": {
+ "title": "Update every",
+ "description": "Data collection interval, measured in seconds.",
+ "type": "integer",
+ "minimum": 1,
+ "default": 1
+ },
+ "url": {
+ "title": "URL",
+ "description": "The URL of the VerneMQ [metrics endpoint](https://docs.vernemq.com/monitoring/prometheus).",
+ "type": "string",
+ "default": "http://127.0.0.1:8888/metrics",
+ "format": "uri"
+ },
+ "timeout": {
+ "title": "Timeout",
+ "description": "The timeout in seconds for the HTTP request.",
+ "type": "number",
+ "minimum": 0.5,
+ "default": 1
+ },
+ "not_follow_redirects": {
+ "title": "Not follow redirects",
+ "description": "If set, the client will not follow HTTP redirects automatically.",
+ "type": "boolean"
+ },
+ "username": {
+ "title": "Username",
+ "description": "The username for basic authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "password": {
+ "title": "Password",
+ "description": "The password for basic authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "proxy_url": {
+ "title": "Proxy URL",
+ "description": "The URL of the proxy server.",
+ "type": "string"
+ },
+ "proxy_username": {
+ "title": "Proxy username",
+ "description": "The username for proxy authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "proxy_password": {
+ "title": "Proxy password",
+ "description": "The password for proxy authentication.",
+ "type": "string",
+ "sensitive": true
+ },
+ "headers": {
+ "title": "Headers",
+ "description": "Additional HTTP headers to include in the request.",
+ "type": [
+ "object",
+ "null"
+ ],
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "tls_skip_verify": {
+ "title": "Skip TLS verification",
+ "description": "If set, TLS certificate verification will be skipped.",
+ "type": "boolean"
+ },
+ "tls_ca": {
+ "title": "TLS CA",
+ "description": "The path to the CA certificate file for TLS verification.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "tls_cert": {
+ "title": "TLS certificate",
+ "description": "The path to the client certificate file for TLS authentication.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "tls_key": {
+ "title": "TLS key",
+ "description": "The path to the client key file for TLS authentication.",
+ "type": "string",
+ "pattern": "^$|^/"
+ },
+ "body": {
+ "title": "Body",
+ "type": "string"
+ },
+ "method": {
+ "title": "Method",
+ "type": "string"
+ }
+ },
+ "required": [
+ "url"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^name$": {}
+ }
+ },
+ "uiSchema": {
+ "ui:flavour": "tabs",
+ "ui:options": {
+ "tabs": [
+ {
+ "title": "Base",
+ "fields": [
+ "update_every",
+ "url",
+ "timeout",
+ "not_follow_redirects"
+ ]
+ },
+ {
+ "title": "Auth",
+ "fields": [
+ "username",
+ "password"
+ ]
+ },
+ {
+ "title": "TLS",
+ "fields": [
+ "tls_skip_verify",
+ "tls_ca",
+ "tls_cert",
+ "tls_key"
+ ]
+ },
+ {
+ "title": "Proxy",
+ "fields": [
+ "proxy_url",
+ "proxy_username",
+ "proxy_password"
+ ]
+ },
+ {
+ "title": "Headers",
+ "fields": [
+ "headers"
+ ]
+ }
+ ]
+ },
+ "uiOptions": {
+ "fullPage": true
+ },
+ "body": {
+ "ui:widget": "hidden"
+ },
+ "method": {
+ "ui:widget": "hidden"
+ },
+ "timeout": {
+ "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
+ },
+ "username": {
+ "ui:widget": "password"
+ },
+ "proxy_username": {
+ "ui:widget": "password"
+ },
+ "password": {
+ "ui:widget": "password"
+ },
+ "proxy_password": {
+ "ui:widget": "password"
+ }
+ }
+}
diff --git a/src/go/plugin/go.d/modules/vernemq/init.go b/src/go/plugin/go.d/modules/vernemq/init.go
new file mode 100644
index 00000000..64ed3418
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/init.go
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package vernemq
+
+import (
+ "errors"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/prometheus"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+func (v *VerneMQ) validateConfig() error {
+ if v.URL == "" {
+ return errors.New("url is not set")
+ }
+ return nil
+}
+
+func (v *VerneMQ) initPrometheusClient() (prometheus.Prometheus, error) {
+ client, err := web.NewHTTPClient(v.Client)
+ if err != nil {
+ return nil, err
+ }
+
+ return prometheus.New(client, v.Request), nil
+}
diff --git a/src/go/plugin/go.d/modules/vernemq/integrations/vernemq.md b/src/go/plugin/go.d/modules/vernemq/integrations/vernemq.md
new file mode 100644
index 00000000..f3b4c287
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/integrations/vernemq.md
@@ -0,0 +1,332 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/vernemq/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/modules/vernemq/metadata.yaml"
+sidebar_label: "VerneMQ"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/Message Brokers"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# VerneMQ
+
+
+<img src="https://netdata.cloud/img/vernemq.svg" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: vernemq
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+This collector monitors VerneMQ instances.
+
+
+
+
+This collector is supported on all platforms.
+
+This collector supports collecting metrics from multiple instances of this integration, including remote instances.
+
+
+### Default Behavior
+
+#### Auto-Detection
+
+This integration doesn't support auto-detection.
+
+#### Limits
+
+The default configuration for this integration does not impose any limits on data collection.
+
+#### Performance Impact
+
+The default configuration for this integration is not expected to impose a significant performance impact on the system.
+
+
+## Metrics
+
+Metrics grouped by *scope*.
+
+The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
+
+
+
+### Per VerneMQ instance
+
+These metrics refer to the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| vernemq.sockets | open | sockets |
+| vernemq.socket_operations | open, close | sockets/s |
+| vernemq.client_keepalive_expired | closed | sockets/s |
+| vernemq.socket_close_timeout | closed | sockets/s |
+| vernemq.socket_errors | errors | errors/s |
+| vernemq.queue_processes | queue_processes | queue processes |
+| vernemq.queue_processes_operations | setup, teardown | events/s |
+| vernemq.queue_process_init_from_storage | queue_processes | queue processes/s |
+| vernemq.queue_messages | received, sent | messages/s |
+| vernemq.queue_undelivered_messages | dropped, expired, unhandled | messages/s |
+| vernemq.router_subscriptions | subscriptions | subscriptions |
+| vernemq.router_matched_subscriptions | local, remote | subscriptions/s |
+| vernemq.router_memory | used | KiB |
+| vernemq.average_scheduler_utilization | utilization | percentage |
+| vernemq.system_utilization_scheduler | a dimension per scheduler | percentage |
+| vernemq.system_processes | processes | processes |
+| vernemq.system_reductions | reductions | ops/s |
+| vernemq.system_context_switches | context_switches | ops/s |
+| vernemq.system_io | received, sent | kilobits/s |
+| vernemq.system_run_queue | ready | processes |
+| vernemq.system_gc_count | gc | ops/s |
+| vernemq.system_gc_words_reclaimed | words_reclaimed | ops/s |
+| vernemq.system_allocated_memory | processes, system | KiB |
+| vernemq.bandwidth | received, sent | kilobits/s |
+| vernemq.retain_messages | messages | messages |
+| vernemq.retain_memory | used | KiB |
+| vernemq.cluster_bandwidth | received, sent | kilobits/s |
+| vernemq.cluster_dropped | dropped | kilobits/s |
+| vernemq.netsplit_unresolved | unresolved | netsplits |
+| vernemq.netsplits | resolved, detected | netsplits/s |
+| vernemq.mqtt_auth | received, sent | packets/s |
+| vernemq.mqtt_auth_received_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_auth_sent_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_connect | connect, connack | packets/s |
+| vernemq.mqtt_connack_sent_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_disconnect | received, sent | packets/s |
+| vernemq.mqtt_disconnect_received_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_disconnect_sent_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_subscribe | subscribe, suback | packets/s |
+| vernemq.mqtt_subscribe_error | failed | ops/s |
+| vernemq.mqtt_subscribe_auth_error | unauth | attempts/s |
+| vernemq.mqtt_unsubscribe | unsubscribe, unsuback | packets/s |
+| vernemq.mqtt_unsubscribe_error | mqtt_unsubscribe_error | ops/s |
+| vernemq.mqtt_publish | received, sent | packets/s |
+| vernemq.mqtt_publish_errors | failed | ops/s |
+| vernemq.mqtt_publish_auth_errors | unauth | attempts/s |
+| vernemq.mqtt_puback | received, sent | packets/s |
+| vernemq.mqtt_puback_received_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_puback_sent_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_puback_invalid_error | unexpected | messages/s |
+| vernemq.mqtt_pubrec | received, sent | packets/s |
+| vernemq.mqtt_pubrec_received_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_pubrec_sent_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_pubrec_invalid_error | unexpected | messages/s |
+| vernemq.mqtt_pubrel | received, sent | packets/s |
+| vernemq.mqtt_pubrel_received_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_pubrel_sent_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_pubcom | received, sent | packets/s |
+| vernemq.mqtt_pubcomp_received_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_pubcomp_sent_reason | a dimensions per reason | packets/s |
+| vernemq.mqtt_pubcomp_invalid_error | unexpected | messages/s |
+| vernemq.mqtt_ping | pingreq, pingresp | packets/s |
+| vernemq.node_uptime | time | seconds |
+
+
+
+## Alerts
+
+
+The following alerts are available:
+
+| Alert name | On metric | Description |
+|:------------|:----------|:------------|
+| [ vernemq_socket_errors ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.socket_errors | number of socket errors in the last minute |
+| [ vernemq_queue_message_drop ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.queue_undelivered_messages | number of dropped messaged due to full queues in the last minute |
+| [ vernemq_queue_message_expired ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.queue_undelivered_messages | number of messages which expired before delivery in the last minute |
+| [ vernemq_queue_message_unhandled ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.queue_undelivered_messages | number of unhandled messages (connections with clean session=true) in the last minute |
+| [ vernemq_average_scheduler_utilization ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.average_scheduler_utilization | average scheduler utilization over the last 10 minutes |
+| [ vernemq_cluster_dropped ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.cluster_dropped | amount of traffic dropped during communication with the cluster nodes in the last minute |
+| [ vernemq_netsplits ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vvernemq.netsplits | number of detected netsplits (split brain situation) in the last minute |
+| [ vernemq_mqtt_connack_sent_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_connack_sent_reason | number of sent unsuccessful v3/v5 CONNACK packets in the last minute |
+| [ vernemq_mqtt_disconnect_received_reason_not_normal ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_disconnect_received_reason | number of received not normal v5 DISCONNECT packets in the last minute |
+| [ vernemq_mqtt_disconnect_sent_reason_not_normal ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_disconnect_sent_reason | number of sent not normal v5 DISCONNECT packets in the last minute |
+| [ vernemq_mqtt_subscribe_error ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_subscribe_error | number of failed v3/v5 SUBSCRIBE operations in the last minute |
+| [ vernemq_mqtt_subscribe_auth_error ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_subscribe_auth_error | number of unauthorized v3/v5 SUBSCRIBE attempts in the last minute |
+| [ vernemq_mqtt_unsubscribe_error ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_unsubscribe_error | number of failed v3/v5 UNSUBSCRIBE operations in the last minute |
+| [ vernemq_mqtt_publish_errors ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_publish_errors | number of failed v3/v5 PUBLISH operations in the last minute |
+| [ vernemq_mqtt_publish_auth_errors ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_publish_auth_errors | number of unauthorized v3/v5 PUBLISH attempts in the last minute |
+| [ vernemq_mqtt_puback_received_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_puback_received_reason | number of received unsuccessful v5 PUBACK packets in the last minute |
+| [ vernemq_mqtt_puback_sent_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_puback_sent_reason | number of sent unsuccessful v5 PUBACK packets in the last minute |
+| [ vernemq_mqtt_puback_unexpected ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_puback_invalid_error | number of received unexpected v3/v5 PUBACK packets in the last minute |
+| [ vernemq_mqtt_pubrec_received_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubrec_received_reason | number of received unsuccessful v5 PUBREC packets in the last minute |
+| [ vernemq_mqtt_pubrec_sent_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubrec_sent_reason | number of sent unsuccessful v5 PUBREC packets in the last minute |
+| [ vernemq_mqtt_pubrec_invalid_error ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubrec_invalid_error | number of received unexpected v3 PUBREC packets in the last minute |
+| [ vernemq_mqtt_pubrel_received_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubrel_received_reason | number of received unsuccessful v5 PUBREL packets in the last minute |
+| [ vernemq_mqtt_pubrel_sent_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubrel_sent_reason | number of sent unsuccessful v5 PUBREL packets in the last minute |
+| [ vernemq_mqtt_pubcomp_received_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubcomp_received_reason | number of received unsuccessful v5 PUBCOMP packets in the last minute |
+| [ vernemq_mqtt_pubcomp_sent_reason_unsuccessful ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubcomp_sent_reason | number of sent unsuccessful v5 PUBCOMP packets in the last minute |
+| [ vernemq_mqtt_pubcomp_unexpected ](https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf) | vernemq.mqtt_pubcomp_invalid_error | number of received unexpected v3/v5 PUBCOMP packets in the last minute |
+
+
+## Setup
+
+### Prerequisites
+
+No action required.
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `go.d/vernemq.conf`.
+
+
+You can edit the configuration file using the `edit-config` script from the
+Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory).
+
+```bash
+cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
+sudo ./edit-config go.d/vernemq.conf
+```
+#### Options
+
+The following options can be defined globally: update_every, autodetection_retry.
+
+
+<details open><summary>Config options</summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| update_every | Data collection frequency. | 1 | no |
+| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no |
+| url | Server URL. | http://127.0.0.1:8888/metrics | yes |
+| timeout | HTTP request timeout. | 1 | no |
+| username | Username for basic HTTP authentication. | | no |
+| password | Password for basic HTTP authentication. | | no |
+| proxy_url | Proxy URL. | | no |
+| proxy_username | Username for proxy basic HTTP authentication. | | no |
+| proxy_password | Password for proxy basic HTTP authentication. | | no |
+| method | HTTP request method. | GET | no |
+| body | HTTP request body. | | no |
+| headers | HTTP request headers. | | no |
+| not_follow_redirects | Redirect handling policy. Controls whether the client follows redirects. | no | no |
+| tls_skip_verify | Server certificate chain and hostname validation policy. Controls whether the client performs this check. | no | no |
+| tls_ca | Certification authority that the client uses when verifying the server's certificates. | | no |
+| tls_cert | Client TLS certificate. | | no |
+| tls_key | Client TLS key. | | no |
+
+</details>
+
+#### Examples
+
+##### Basic
+
+An example configuration.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:8888/metrics
+
+```
+</details>
+
+##### HTTP authentication
+
+Local instance with basic HTTP authentication.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:8888/metrics
+ username: username
+ password: password
+
+```
+</details>
+
+##### Multi-instance
+
+> **Note**: When you define multiple jobs, their names must be unique.
+
+Local and remote instances.
+
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ url: http://127.0.0.1:8888/metrics
+
+ - name: remote
+ url: http://203.0.113.10:8888/metrics
+
+```
+</details>
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+**Important**: Debug mode is not supported for data collection jobs created via the UI using the Dyncfg feature.
+
+To troubleshoot issues with the `vernemq` collector, run the `go.d.plugin` with the debug option enabled. The output
+should give you clues as to why the collector isn't working.
+
+- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
+ your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
+
+ ```bash
+ cd /usr/libexec/netdata/plugins.d/
+ ```
+
+- Switch to the `netdata` user.
+
+ ```bash
+ sudo -u netdata -s
+ ```
+
+- Run the `go.d.plugin` to debug the collector:
+
+ ```bash
+ ./go.d.plugin -d -m vernemq
+ ```
+
+### Getting Logs
+
+If you're encountering problems with the `vernemq` collector, follow these steps to retrieve logs and identify potential issues:
+
+- **Run the command** specific to your system (systemd, non-systemd, or Docker container).
+- **Examine the output** for any warnings or error messages that might indicate issues. These messages should provide clues about the root cause of the problem.
+
+#### System with systemd
+
+Use the following command to view logs generated since the last Netdata service restart:
+
+```bash
+journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep vernemq
+```
+
+#### System without systemd
+
+Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name:
+
+```bash
+grep vernemq /var/log/netdata/collector.log
+```
+
+**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues.
+
+#### Docker Container
+
+If your Netdata runs in a Docker container named "netdata" (replace if different), use this command:
+
+```bash
+docker logs netdata 2>&1 | grep vernemq
+```
+
+
diff --git a/src/go/plugin/go.d/modules/vernemq/metadata.yaml b/src/go/plugin/go.d/modules/vernemq/metadata.yaml
new file mode 100644
index 00000000..2ec25fb7
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/metadata.yaml
@@ -0,0 +1,670 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-vernemq
+ plugin_name: go.d.plugin
+ module_name: vernemq
+ monitored_instance:
+ name: VerneMQ
+ link: https://vernemq.com
+ icon_filename: vernemq.svg
+ categories:
+ - data-collection.message-brokers
+ keywords:
+ - vernemq
+ - message brokers
+ related_resources:
+ integrations:
+ list: []
+ info_provided_to_referring_integrations:
+ description: ""
+ most_popular: false
+ overview:
+ data_collection:
+ metrics_description: |
+ This collector monitors VerneMQ instances.
+ method_description: ""
+ supported_platforms:
+ include: []
+ exclude: []
+ multi_instance: true
+ additional_permissions:
+ description: ""
+ default_behavior:
+ auto_detection:
+ description: ""
+ limits:
+ description: ""
+ performance_impact:
+ description: ""
+ setup:
+ prerequisites:
+ list: []
+ configuration:
+ file:
+ name: go.d/vernemq.conf
+ options:
+ description: |
+ The following options can be defined globally: update_every, autodetection_retry.
+ folding:
+ title: Config options
+ enabled: true
+ list:
+ - name: update_every
+ description: Data collection frequency.
+ default_value: 1
+ required: false
+ - name: autodetection_retry
+ description: Recheck interval in seconds. Zero means no recheck will be scheduled.
+ default_value: 0
+ required: false
+ - name: url
+ description: Server URL.
+ default_value: http://127.0.0.1:8888/metrics
+ required: true
+ - name: timeout
+ description: HTTP request timeout.
+ default_value: 1
+ required: false
+ - name: username
+ description: Username for basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: password
+ description: Password for basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: proxy_url
+ description: Proxy URL.
+ default_value: ""
+ required: false
+ - name: proxy_username
+ description: Username for proxy basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: proxy_password
+ description: Password for proxy basic HTTP authentication.
+ default_value: ""
+ required: false
+ - name: method
+ description: HTTP request method.
+ default_value: GET
+ required: false
+ - name: body
+ description: HTTP request body.
+ default_value: ""
+ required: false
+ - name: headers
+ description: HTTP request headers.
+ default_value: ""
+ required: false
+ - name: not_follow_redirects
+ description: Redirect handling policy. Controls whether the client follows redirects.
+ default_value: false
+ required: false
+ - name: tls_skip_verify
+ description: Server certificate chain and hostname validation policy. Controls whether the client performs this check.
+ default_value: false
+ required: false
+ - name: tls_ca
+ description: Certification authority that the client uses when verifying the server's certificates.
+ default_value: ""
+ required: false
+ - name: tls_cert
+ description: Client TLS certificate.
+ default_value: ""
+ required: false
+ - name: tls_key
+ description: Client TLS key.
+ default_value: ""
+ required: false
+ examples:
+ folding:
+ title: Config
+ enabled: true
+ list:
+ - name: Basic
+ description: An example configuration.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:8888/metrics
+ - name: HTTP authentication
+ description: Local instance with basic HTTP authentication.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:8888/metrics
+ username: username
+ password: password
+ - name: Multi-instance
+ description: |
+ > **Note**: When you define multiple jobs, their names must be unique.
+
+ Local and remote instances.
+ config: |
+ jobs:
+ - name: local
+ url: http://127.0.0.1:8888/metrics
+
+ - name: remote
+ url: http://203.0.113.10:8888/metrics
+ troubleshooting:
+ problems:
+ list: []
+ alerts:
+ - name: vernemq_socket_errors
+ metric: vernemq.socket_errors
+ info: number of socket errors in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_queue_message_drop
+ metric: vernemq.queue_undelivered_messages
+ info: number of dropped messaged due to full queues in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_queue_message_expired
+ metric: vernemq.queue_undelivered_messages
+ info: number of messages which expired before delivery in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_queue_message_unhandled
+ metric: vernemq.queue_undelivered_messages
+ info: "number of unhandled messages (connections with clean session=true) in the last minute"
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_average_scheduler_utilization
+ metric: vernemq.average_scheduler_utilization
+ info: average scheduler utilization over the last 10 minutes
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_cluster_dropped
+ metric: vernemq.cluster_dropped
+ info: amount of traffic dropped during communication with the cluster nodes in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_netsplits
+ metric: vvernemq.netsplits
+ info: "number of detected netsplits (split brain situation) in the last minute"
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_connack_sent_reason_unsuccessful
+ metric: vernemq.mqtt_connack_sent_reason
+ info: number of sent unsuccessful v3/v5 CONNACK packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_disconnect_received_reason_not_normal
+ metric: vernemq.mqtt_disconnect_received_reason
+ info: number of received not normal v5 DISCONNECT packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_disconnect_sent_reason_not_normal
+ metric: vernemq.mqtt_disconnect_sent_reason
+ info: number of sent not normal v5 DISCONNECT packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_subscribe_error
+ metric: vernemq.mqtt_subscribe_error
+ info: number of failed v3/v5 SUBSCRIBE operations in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_subscribe_auth_error
+ metric: vernemq.mqtt_subscribe_auth_error
+ info: number of unauthorized v3/v5 SUBSCRIBE attempts in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_unsubscribe_error
+ metric: vernemq.mqtt_unsubscribe_error
+ info: number of failed v3/v5 UNSUBSCRIBE operations in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_publish_errors
+ metric: vernemq.mqtt_publish_errors
+ info: number of failed v3/v5 PUBLISH operations in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_publish_auth_errors
+ metric: vernemq.mqtt_publish_auth_errors
+ info: number of unauthorized v3/v5 PUBLISH attempts in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_puback_received_reason_unsuccessful
+ metric: vernemq.mqtt_puback_received_reason
+ info: number of received unsuccessful v5 PUBACK packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_puback_sent_reason_unsuccessful
+ metric: vernemq.mqtt_puback_sent_reason
+ info: number of sent unsuccessful v5 PUBACK packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_puback_unexpected
+ metric: vernemq.mqtt_puback_invalid_error
+ info: number of received unexpected v3/v5 PUBACK packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubrec_received_reason_unsuccessful
+ metric: vernemq.mqtt_pubrec_received_reason
+ info: number of received unsuccessful v5 PUBREC packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubrec_sent_reason_unsuccessful
+ metric: vernemq.mqtt_pubrec_sent_reason
+ info: number of sent unsuccessful v5 PUBREC packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubrec_invalid_error
+ metric: vernemq.mqtt_pubrec_invalid_error
+ info: number of received unexpected v3 PUBREC packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubrel_received_reason_unsuccessful
+ metric: vernemq.mqtt_pubrel_received_reason
+ info: number of received unsuccessful v5 PUBREL packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubrel_sent_reason_unsuccessful
+ metric: vernemq.mqtt_pubrel_sent_reason
+ info: number of sent unsuccessful v5 PUBREL packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubcomp_received_reason_unsuccessful
+ metric: vernemq.mqtt_pubcomp_received_reason
+ info: number of received unsuccessful v5 PUBCOMP packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubcomp_sent_reason_unsuccessful
+ metric: vernemq.mqtt_pubcomp_sent_reason
+ info: number of sent unsuccessful v5 PUBCOMP packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ - name: vernemq_mqtt_pubcomp_unexpected
+ metric: vernemq.mqtt_pubcomp_invalid_error
+ info: number of received unexpected v3/v5 PUBCOMP packets in the last minute
+ link: https://github.com/netdata/netdata/blob/master/src/health/health.d/vernemq.conf
+ metrics:
+ folding:
+ title: Metrics
+ enabled: false
+ description: ""
+ availability: []
+ scopes:
+ - name: global
+ description: These metrics refer to the entire monitored application.
+ labels: []
+ metrics:
+ - name: vernemq.sockets
+ description: Open Sockets
+ unit: sockets
+ chart_type: line
+ dimensions:
+ - name: open
+ - name: vernemq.socket_operations
+ description: Socket Open and Close Events
+ unit: sockets/s
+ chart_type: line
+ dimensions:
+ - name: open
+ - name: close
+ - name: vernemq.client_keepalive_expired
+ description: Closed Sockets due to Keepalive Time Expired
+ unit: sockets/s
+ chart_type: line
+ dimensions:
+ - name: closed
+ - name: vernemq.socket_close_timeout
+ description: Closed Sockets due to no CONNECT Frame On Time
+ unit: sockets/s
+ chart_type: line
+ dimensions:
+ - name: closed
+ - name: vernemq.socket_errors
+ description: Socket Errors
+ unit: errors/s
+ chart_type: line
+ dimensions:
+ - name: errors
+ - name: vernemq.queue_processes
+ description: Living Queues in an Online or an Offline State
+ unit: queue processes
+ chart_type: line
+ dimensions:
+ - name: queue_processes
+ - name: vernemq.queue_processes_operations
+ description: Queue Processes Setup and Teardown Events
+ unit: events/s
+ chart_type: line
+ dimensions:
+ - name: setup
+ - name: teardown
+ - name: vernemq.queue_process_init_from_storage
+ description: Queue Processes Initialized from Offline Storage
+ unit: queue processes/s
+ chart_type: line
+ dimensions:
+ - name: queue_processes
+ - name: vernemq.queue_messages
+ description: Received and Sent PUBLISH Messages
+ unit: messages/s
+ chart_type: area
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.queue_undelivered_messages
+ description: Undelivered PUBLISH Messages
+ unit: messages/s
+ chart_type: stacked
+ dimensions:
+ - name: dropped
+ - name: expired
+ - name: unhandled
+ - name: vernemq.router_subscriptions
+ description: Subscriptions in the Routing Table
+ unit: subscriptions
+ chart_type: line
+ dimensions:
+ - name: subscriptions
+ - name: vernemq.router_matched_subscriptions
+ description: Matched Subscriptions
+ unit: subscriptions/s
+ chart_type: line
+ dimensions:
+ - name: local
+ - name: remote
+ - name: vernemq.router_memory
+ description: Routing Table Memory Usage
+ unit: KiB
+ chart_type: area
+ dimensions:
+ - name: used
+ - name: vernemq.average_scheduler_utilization
+ description: Average Scheduler Utilization
+ unit: percentage
+ chart_type: area
+ dimensions:
+ - name: utilization
+ - name: vernemq.system_utilization_scheduler
+ description: Scheduler Utilization
+ unit: percentage
+ chart_type: stacked
+ dimensions:
+ - name: a dimension per scheduler
+ - name: vernemq.system_processes
+ description: Erlang Processes
+ unit: processes
+ chart_type: line
+ dimensions:
+ - name: processes
+ - name: vernemq.system_reductions
+ description: Reductions
+ unit: ops/s
+ chart_type: line
+ dimensions:
+ - name: reductions
+ - name: vernemq.system_context_switches
+ description: Context Switches
+ unit: ops/s
+ chart_type: line
+ dimensions:
+ - name: context_switches
+ - name: vernemq.system_io
+ description: Received and Sent Traffic through Ports
+ unit: kilobits/s
+ chart_type: area
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.system_run_queue
+ description: Processes that are Ready to Run on All Run-Queues
+ unit: processes
+ chart_type: line
+ dimensions:
+ - name: ready
+ - name: vernemq.system_gc_count
+ description: GC Count
+ unit: ops/s
+ chart_type: line
+ dimensions:
+ - name: gc
+ - name: vernemq.system_gc_words_reclaimed
+ description: GC Words Reclaimed
+ unit: ops/s
+ chart_type: line
+ dimensions:
+ - name: words_reclaimed
+ - name: vernemq.system_allocated_memory
+ description: Memory Allocated by the Erlang Processes and by the Emulator
+ unit: KiB
+ chart_type: stacked
+ dimensions:
+ - name: processes
+ - name: system
+ - name: vernemq.bandwidth
+ description: Bandwidth
+ unit: kilobits/s
+ chart_type: area
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.retain_messages
+ description: Stored Retained Messages
+ unit: messages
+ chart_type: line
+ dimensions:
+ - name: messages
+ - name: vernemq.retain_memory
+ description: Stored Retained Messages Memory Usage
+ unit: KiB
+ chart_type: area
+ dimensions:
+ - name: used
+ - name: vernemq.cluster_bandwidth
+ description: Communication with Other Cluster Nodes
+ unit: kilobits/s
+ chart_type: area
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.cluster_dropped
+ description: Traffic Dropped During Communication with Other Cluster Nodes
+ unit: kilobits/s
+ chart_type: area
+ dimensions:
+ - name: dropped
+ - name: vernemq.netsplit_unresolved
+ description: Unresolved Netsplits
+ unit: netsplits
+ chart_type: line
+ dimensions:
+ - name: unresolved
+ - name: vernemq.netsplits
+ description: Netsplits
+ unit: netsplits/s
+ chart_type: stacked
+ dimensions:
+ - name: resolved
+ - name: detected
+ - name: vernemq.mqtt_auth
+ description: v5 AUTH
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.mqtt_auth_received_reason
+ description: v5 AUTH Received by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_auth_sent_reason
+ description: v5 AUTH Sent by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_connect
+ description: v3/v5 CONNECT and CONNACK
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: connect
+ - name: connack
+ - name: vernemq.mqtt_connack_sent_reason
+ description: v3/v5 CONNACK Sent by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_disconnect
+ description: v3/v5 DISCONNECT
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.mqtt_disconnect_received_reason
+ description: v5 DISCONNECT Received by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_disconnect_sent_reason
+ description: v5 DISCONNECT Sent by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_subscribe
+ description: v3/v5 SUBSCRIBE and SUBACK
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: subscribe
+ - name: suback
+ - name: vernemq.mqtt_subscribe_error
+ description: v3/v5 Failed SUBSCRIBE Operations due to a Netsplit
+ unit: ops/s
+ chart_type: line
+ dimensions:
+ - name: failed
+ - name: vernemq.mqtt_subscribe_auth_error
+ description: v3/v5 Unauthorized SUBSCRIBE Attempts
+ unit: attempts/s
+ chart_type: line
+ dimensions:
+ - name: unauth
+ - name: vernemq.mqtt_unsubscribe
+ description: v3/v5 UNSUBSCRIBE and UNSUBACK
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: unsubscribe
+ - name: unsuback
+ - name: vernemq.mqtt_unsubscribe_error
+ description: v3/v5 Failed UNSUBSCRIBE Operations due to a Netsplit
+ unit: ops/s
+ chart_type: line
+ dimensions:
+ - name: mqtt_unsubscribe_error
+ - name: vernemq.mqtt_publish
+ description: v3/v5 QoS 0,1,2 PUBLISH
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.mqtt_publish_errors
+ description: v3/v5 Failed PUBLISH Operations due to a Netsplit
+ unit: ops/s
+ chart_type: line
+ dimensions:
+ - name: failed
+ - name: vernemq.mqtt_publish_auth_errors
+ description: v3/v5 Unauthorized PUBLISH Attempts
+ unit: attempts/s
+ chart_type: area
+ dimensions:
+ - name: unauth
+ - name: vernemq.mqtt_puback
+ description: v3/v5 QoS 1 PUBACK
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.mqtt_puback_received_reason
+ description: v5 PUBACK QoS 1 Received by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_puback_sent_reason
+ description: v5 PUBACK QoS 1 Sent by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_puback_invalid_error
+ description: v3/v5 PUBACK QoS 1 Received Unexpected Messages
+ unit: messages/s
+ chart_type: line
+ dimensions:
+ - name: unexpected
+ - name: vernemq.mqtt_pubrec
+ description: v3/v5 PUBREC QoS 2
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.mqtt_pubrec_received_reason
+ description: v5 PUBREC QoS 2 Received by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_pubrec_sent_reason
+ description: v5 PUBREC QoS 2 Sent by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_pubrec_invalid_error
+ description: v3 PUBREC QoS 2 Received Unexpected Messages
+ unit: messages/s
+ chart_type: line
+ dimensions:
+ - name: unexpected
+ - name: vernemq.mqtt_pubrel
+ description: v3/v5 PUBREL QoS 2
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.mqtt_pubrel_received_reason
+ description: v5 PUBREL QoS 2 Received by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_pubrel_sent_reason
+ description: v5 PUBREL QoS 2 Sent by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_pubcom
+ description: v3/v5 PUBCOMP QoS 2
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: received
+ - name: sent
+ - name: vernemq.mqtt_pubcomp_received_reason
+ description: v5 PUBCOMP QoS 2 Received by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_pubcomp_sent_reason
+ description: v5 PUBCOMP QoS 2 Sent by Reason
+ unit: packets/s
+ chart_type: stacked
+ dimensions:
+ - name: a dimensions per reason
+ - name: vernemq.mqtt_pubcomp_invalid_error
+ description: v3/v5 PUBCOMP QoS 2 Received Unexpected Messages
+ unit: messages/s
+ chart_type: line
+ dimensions:
+ - name: unexpected
+ - name: vernemq.mqtt_ping
+ description: v3/v5 PING
+ unit: packets/s
+ chart_type: line
+ dimensions:
+ - name: pingreq
+ - name: pingresp
+ - name: vernemq.node_uptime
+ description: Node Uptime
+ unit: seconds
+ chart_type: line
+ dimensions:
+ - name: time
diff --git a/src/go/plugin/go.d/modules/vernemq/metrics.go b/src/go/plugin/go.d/modules/vernemq/metrics.go
new file mode 100644
index 00000000..863cc635
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/metrics.go
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package vernemq
+
+// Source Code Metrics:
+// - https://github.com/vernemq/vernemq/blob/master/apps/vmq_server/src/vmq_metrics.erl
+// - https://github.com/vernemq/vernemq/blob/master/apps/vmq_server/src/vmq_metrics.hrl
+
+// Source Code FSM:
+// - https://github.com/vernemq/vernemq/blob/master/apps/vmq_server/src/vmq_mqtt_fsm.erl
+// - https://github.com/vernemq/vernemq/blob/master/apps/vmq_server/src/vmq_mqtt5_fsm.erl
+
+// MQTT Packet Types:
+// - v4: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180834
+// - v5: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901019
+
+// Erlang VM:
+// - http://erlang.org/documentation/doc-5.7.1/erts-5.7.1/doc/html/erlang.html
+
+// Not used metrics (https://docs.vernemq.com/monitoring/introduction):
+// - "mqtt_connack_accepted_sent" // v4, not populated, "mqtt_connack_sent" used instead
+// - "mqtt_connack_unacceptable_protocol_sent" // v4, not populated, "mqtt_connack_sent" used instead
+// - "mqtt_connack_identifier_rejected_sent" // v4, not populated, "mqtt_connack_sent" used instead
+// - "mqtt_connack_server_unavailable_sent" // v4, not populated, "mqtt_connack_sent" used instead
+// - "mqtt_connack_bad_credentials_sent" // v4, not populated, "mqtt_connack_sent" used instead
+// - "mqtt_connack_not_authorized_sent" // v4, not populated, "mqtt_connack_sent" used instead
+// - "system_exact_reductions"
+// - "system_runtime"
+// - "vm_memory_atom"
+// - "vm_memory_atom_used"
+// - "vm_memory_binary"
+// - "vm_memory_code"
+// - "vm_memory_ets"
+// - "vm_memory_processes_used"
+// - "vm_memory_total"
+
+// -----------------------------------------------MQTT------------------------------------------------------------------
+const (
+ // AUTH
+ metricAUTHReceived = "mqtt_auth_received" // v5 has 'reason_code' label
+ metricAUTHSent = "mqtt_auth_sent" // v5 has 'reason_code' label
+
+ // CONNECT
+ metricCONNECTReceived = "mqtt_connect_received" // v4, v5
+ metricCONNACKSent = "mqtt_connack_sent" // v4 has 'return_code' label, v5 has 'reason_code'
+
+ // SUBSCRIBE
+ metricSUBSCRIBEReceived = "mqtt_subscribe_received" // v4, v5
+ metricSUBACKSent = "mqtt_suback_sent" // v4, v5
+ metricSUBSCRIBEError = "mqtt_subscribe_error" // v4, v5
+ metricSUBSCRIBEAuthError = "mqtt_subscribe_auth_error" // v4, v5
+
+ // UNSUBSCRIBE
+ metricUNSUBSCRIBEReceived = "mqtt_unsubscribe_received" // v4, v5
+ metricUNSUBACKSent = "mqtt_unsuback_sent" // v4, v5
+ metricUNSUBSCRIBEError = "mqtt_unsubscribe_error" // v4, v5
+
+ // PUBLISH
+ metricPUBSLISHReceived = "mqtt_publish_received" // v4, v5
+ metricPUBSLIHSent = "mqtt_publish_sent" // v4, v5
+ metricPUBLISHError = "mqtt_publish_error" // v4, v5
+ metricPUBLISHAuthError = "mqtt_publish_auth_error" // v4, v5
+
+ // Publish acknowledgment (QoS 1)
+ metricPUBACKReceived = "mqtt_puback_received" // v4, v5 has 'reason_code' label
+ metricPUBACKSent = "mqtt_puback_sent" // v4, v5 has 'reason_code' label
+ metricPUBACKInvalid = "mqtt_puback_invalid_error" // v4, v5
+
+ // Publish received (QoS 2 delivery part 1)
+ metricPUBRECReceived = "mqtt_pubrec_received" // v4, v5 has 'reason_code' label
+ metricPUBRECSent = "mqtt_pubrec_sent" // v4, v5 has 'reason_code' label
+ metricPUBRECInvalid = "mqtt_pubrec_invalid_error" // v4
+
+ // Publish release (QoS 2 delivery part 2)
+ metricPUBRELReceived = "mqtt_pubrel_received" // v4, v5 has 'reason_code' label
+ metricPUBRELSent = "mqtt_pubrel_sent" // v4, v5 has 'reason_code' label
+
+ // Publish complete (QoS 2 delivery part 3)
+ metricPUBCOMPReceived = "mqtt_pubcomp_received" // v4, v5 has 'reason_code' label
+ metricPUBCOMPSent = "mqtt_pubcomp_sent" // v4, v5 has 'reason_code' label
+ metricPUNCOMPInvalid = "mqtt_pubcomp_invalid_error" // v4, v5
+
+ // PING
+ metricPINGREQReceived = "mqtt_pingreq_received" // v4, v5
+ metricPINGRESPSent = "mqtt_pingresp_sent" // v4, v5
+
+ // DISCONNECT
+ metricDISCONNECTReceived = "mqtt_disconnect_received" // v4, v5 has 'reason_code' label
+ metricDISCONNECTSent = "mqtt_disconnect_sent" // v5 has 'reason_code' label
+
+ // Misc
+ metricMQTTInvalidMsgSizeError = "mqtt_invalid_msg_size_error" // v4, v5
+)
+
+const (
+ // Sockets
+ metricSocketOpen = "socket_open"
+ metricSocketClose = "socket_close"
+ metricSocketError = "socket_error"
+ metricSocketCloseTimeout = "socket_close_timeout"
+ metricClientKeepaliveExpired = "client_keepalive_expired" // v4, v5
+
+ // Queues
+ metricQueueProcesses = "queue_processes"
+ metricQueueSetup = "queue_setup"
+ metricQueueTeardown = "queue_teardown"
+ metricQueueMessageIn = "queue_message_in"
+ metricQueueMessageOut = "queue_message_out"
+ metricQueueMessageDrop = "queue_message_drop"
+ metricQueueMessageExpired = "queue_message_expired"
+ metricQueueMessageUnhandled = "queue_message_unhandled"
+ metricQueueInitializedFromStorage = "queue_initialized_from_storage"
+
+ // Subscriptions
+ metricRouterMatchesLocal = "router_matches_local"
+ metricRouterMatchesRemote = "router_matches_remote"
+ metricRouterMemory = "router_memory"
+ metricRouterSubscriptions = "router_subscriptions"
+
+ // Erlang VM
+ metricSystemUtilization = "system_utilization"
+ metricSystemProcessCount = "system_process_count"
+ metricSystemReductions = "system_reductions"
+ metricSystemContextSwitches = "system_context_switches"
+ metricSystemIOIn = "system_io_in"
+ metricSystemIOOut = "system_io_out"
+ metricSystemRunQueue = "system_run_queue"
+ metricSystemGCCount = "system_gc_count"
+ metricSystemWordsReclaimedByGC = "system_words_reclaimed_by_gc"
+ metricVMMemoryProcesses = "vm_memory_processes"
+ metricVMMemorySystem = "vm_memory_system"
+
+ // Bandwidth
+ metricBytesReceived = "bytes_received"
+ metricBytesSent = "bytes_sent"
+
+ // Retain
+ metricRetainMemory = "retain_memory"
+ metricRetainMessages = "retain_messages"
+
+ // Cluster
+ metricClusterBytesDropped = "cluster_bytes_dropped"
+ metricClusterBytesReceived = "cluster_bytes_received"
+ metricClusterBytesSent = "cluster_bytes_sent"
+ metricNetSplitDetected = "netsplit_detected"
+ metricNetSplitResolved = "netsplit_resolved"
+
+ // Uptime
+ metricSystemWallClock = "system_wallclock"
+)
diff --git a/src/go/plugin/go.d/modules/vernemq/testdata/config.json b/src/go/plugin/go.d/modules/vernemq/testdata/config.json
new file mode 100644
index 00000000..984c3ed6
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/testdata/config.json
@@ -0,0 +1,20 @@
+{
+ "update_every": 123,
+ "url": "ok",
+ "body": "ok",
+ "method": "ok",
+ "headers": {
+ "ok": "ok"
+ },
+ "username": "ok",
+ "password": "ok",
+ "proxy_url": "ok",
+ "proxy_username": "ok",
+ "proxy_password": "ok",
+ "timeout": 123.123,
+ "not_follow_redirects": true,
+ "tls_ca": "ok",
+ "tls_cert": "ok",
+ "tls_key": "ok",
+ "tls_skip_verify": true
+}
diff --git a/src/go/plugin/go.d/modules/vernemq/testdata/config.yaml b/src/go/plugin/go.d/modules/vernemq/testdata/config.yaml
new file mode 100644
index 00000000..8558b61c
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/testdata/config.yaml
@@ -0,0 +1,17 @@
+update_every: 123
+url: "ok"
+body: "ok"
+method: "ok"
+headers:
+ ok: "ok"
+username: "ok"
+password: "ok"
+proxy_url: "ok"
+proxy_username: "ok"
+proxy_password: "ok"
+timeout: 123.123
+not_follow_redirects: yes
+tls_ca: "ok"
+tls_cert: "ok"
+tls_key: "ok"
+tls_skip_verify: yes
diff --git a/src/go/plugin/go.d/modules/vernemq/testdata/metrics-v1.10.1-mqtt5.txt b/src/go/plugin/go.d/modules/vernemq/testdata/metrics-v1.10.1-mqtt5.txt
new file mode 100644
index 00000000..2e98a3e9
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/testdata/metrics-v1.10.1-mqtt5.txt
@@ -0,0 +1,416 @@
+# HELP socket_open The number of times an MQTT socket has been opened.
+# TYPE socket_open counter
+socket_open{node="VerneMQ@172.17.0.2"} 338956
+# HELP socket_close The number of times an MQTT socket has been closed.
+# TYPE socket_close counter
+socket_close{node="VerneMQ@172.17.0.2"} 338956
+# HELP socket_close_timeout The number of times VerneMQ closed an MQTT socket due to no CONNECT frame has been received on time.
+# TYPE socket_close_timeout counter
+socket_close_timeout{node="VerneMQ@172.17.0.2"} 0
+# HELP socket_error The total number of socket errors that have occurred.
+# TYPE socket_error counter
+socket_error{node="VerneMQ@172.17.0.2"} 0
+# HELP bytes_received The total number of bytes received.
+# TYPE bytes_received counter
+bytes_received{node="VerneMQ@172.17.0.2"} 36796908
+# HELP bytes_sent The total number of bytes sent.
+# TYPE bytes_sent counter
+bytes_sent{node="VerneMQ@172.17.0.2"} 23361693
+# HELP mqtt_connect_received The number of CONNECT packets received.
+# TYPE mqtt_connect_received counter
+mqtt_connect_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 338956
+# HELP mqtt_publish_received The number of PUBLISH packets received.
+# TYPE mqtt_publish_received counter
+mqtt_publish_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 537088
+# HELP mqtt_puback_received The number of PUBACK packets received.
+# TYPE mqtt_puback_received counter
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 525694
+# HELP mqtt_pubrec_received The number of PUBREC packets received.
+# TYPE mqtt_pubrec_received counter
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_pubrel_received The number of PUBREL packets received.
+# TYPE mqtt_pubrel_received counter
+mqtt_pubrel_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_pubcomp_received The number of PUBCOMP packets received.
+# TYPE mqtt_pubcomp_received counter
+mqtt_pubcomp_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_subscribe_received The number of SUBSCRIBE packets received.
+# TYPE mqtt_subscribe_received counter
+mqtt_subscribe_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 122
+# HELP mqtt_unsubscribe_received The number of UNSUBSCRIBE packets received.
+# TYPE mqtt_unsubscribe_received counter
+mqtt_unsubscribe_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 108
+# HELP mqtt_pingreq_received The number of PINGREQ packets received.
+# TYPE mqtt_pingreq_received counter
+mqtt_pingreq_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 205
+# HELP mqtt_disconnect_received The number of DISCONNECT packets received.
+# TYPE mqtt_disconnect_received counter
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="4"} 107
+# HELP mqtt_connack_accepted_sent The number of times a connection has been accepted.
+# TYPE mqtt_connack_accepted_sent counter
+mqtt_connack_accepted_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_connack_unacceptable_protocol_sent The number of times the broker is not able to support the requested protocol.
+# TYPE mqtt_connack_unacceptable_protocol_sent counter
+mqtt_connack_unacceptable_protocol_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_connack_identifier_rejected_sent The number of times a client was rejected due to a unacceptable identifier.
+# TYPE mqtt_connack_identifier_rejected_sent counter
+mqtt_connack_identifier_rejected_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_connack_server_unavailable_sent The number of times a client was rejected due the the broker being unavailable.
+# TYPE mqtt_connack_server_unavailable_sent counter
+mqtt_connack_server_unavailable_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_connack_bad_credentials_sent The number of times a client sent bad credentials.
+# TYPE mqtt_connack_bad_credentials_sent counter
+mqtt_connack_bad_credentials_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_connack_not_authorized_sent The number of times a client was rejected due to insufficient authorization.
+# TYPE mqtt_connack_not_authorized_sent counter
+mqtt_connack_not_authorized_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_publish_sent The number of PUBLISH packets sent.
+# TYPE mqtt_publish_sent counter
+mqtt_publish_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 525721
+# HELP mqtt_puback_sent The number of PUBACK packets sent.
+# TYPE mqtt_puback_sent counter
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 537068
+# HELP mqtt_pubrec_sent The number of PUBREC packets sent.
+# TYPE mqtt_pubrec_sent counter
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_pubrel_sent The number of PUBREL packets sent.
+# TYPE mqtt_pubrel_sent counter
+mqtt_pubrel_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_pubcomp_sent The number of PUBCOMP packets sent.
+# TYPE mqtt_pubcomp_sent counter
+mqtt_pubcomp_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_suback_sent The number of SUBACK packets sent.
+# TYPE mqtt_suback_sent counter
+mqtt_suback_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 122
+# HELP mqtt_unsuback_sent The number of UNSUBACK packets sent.
+# TYPE mqtt_unsuback_sent counter
+mqtt_unsuback_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 108
+# HELP mqtt_pingresp_sent The number of PINGRESP packets sent.
+# TYPE mqtt_pingresp_sent counter
+mqtt_pingresp_sent{node="VerneMQ@172.17.0.2",mqtt_version="4"} 205
+# HELP mqtt_publish_auth_error The number of unauthorized publish attempts.
+# TYPE mqtt_publish_auth_error counter
+mqtt_publish_auth_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_subscribe_auth_error The number of unauthorized subscription attempts.
+# TYPE mqtt_subscribe_auth_error counter
+mqtt_subscribe_auth_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_invalid_msg_size_error The number of packages exceeding the maximum allowed size.
+# TYPE mqtt_invalid_msg_size_error counter
+mqtt_invalid_msg_size_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_puback_invalid_error The number of unexpected PUBACK messages received.
+# TYPE mqtt_puback_invalid_error counter
+mqtt_puback_invalid_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_pubrec_invalid_error The number of unexpected PUBREC messages received.
+# TYPE mqtt_pubrec_invalid_error counter
+mqtt_pubrec_invalid_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_pubcomp_invalid_error The number of unexpected PUBCOMP messages received.
+# TYPE mqtt_pubcomp_invalid_error counter
+mqtt_pubcomp_invalid_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_publish_error The number of times a PUBLISH operation failed due to a netsplit.
+# TYPE mqtt_publish_error counter
+mqtt_publish_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_subscribe_error The number of times a SUBSCRIBE operation failed due to a netsplit.
+# TYPE mqtt_subscribe_error counter
+mqtt_subscribe_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP mqtt_unsubscribe_error The number of times an UNSUBSCRIBE operation failed due to a netsplit.
+# TYPE mqtt_unsubscribe_error counter
+mqtt_unsubscribe_error{node="VerneMQ@172.17.0.2",mqtt_version="4"} 0
+# HELP client_keepalive_expired The number of clients which failed to communicate within the keepalive time period.
+# TYPE client_keepalive_expired counter
+client_keepalive_expired{node="VerneMQ@172.17.0.2",mqtt_version="4"} 1
+mqtt_connect_received{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_invalid_msg_size_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_pingreq_received{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_pingresp_sent{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_puback_invalid_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_pubcomp_invalid_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_publish_auth_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_publish_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_publish_received{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_publish_sent{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_suback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_subscribe_auth_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_subscribe_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_subscribe_received{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_unsuback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_unsubscribe_error{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+mqtt_unsubscribe_received{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+client_keepalive_expired{node="VerneMQ@172.17.0.2",mqtt_version="5"} 0
+# HELP queue_setup The number of times a MQTT queue process has been started.
+# TYPE queue_setup counter
+queue_setup{node="VerneMQ@172.17.0.2"} 338948
+# HELP queue_initialized_from_storage The number of times a MQTT queue process has been initialized from offline storage.
+# TYPE queue_initialized_from_storage counter
+queue_initialized_from_storage{node="VerneMQ@172.17.0.2"} 0
+# HELP queue_teardown The number of times a MQTT queue process has been terminated.
+# TYPE queue_teardown counter
+queue_teardown{node="VerneMQ@172.17.0.2"} 338948
+# HELP queue_message_drop The number of messages dropped due to full queues.
+# TYPE queue_message_drop counter
+queue_message_drop{node="VerneMQ@172.17.0.2"} 0
+# HELP queue_message_expired The number of messages which expired before delivery.
+# TYPE queue_message_expired counter
+queue_message_expired{node="VerneMQ@172.17.0.2"} 0
+# HELP queue_message_unhandled The number of unhandled messages when connecting with clean session=true.
+# TYPE queue_message_unhandled counter
+queue_message_unhandled{node="VerneMQ@172.17.0.2"} 1
+# HELP queue_message_in The number of PUBLISH packets received by MQTT queue processes.
+# TYPE queue_message_in counter
+queue_message_in{node="VerneMQ@172.17.0.2"} 525722
+# HELP queue_message_out The number of PUBLISH packets sent from MQTT queue processes.
+# TYPE queue_message_out counter
+queue_message_out{node="VerneMQ@172.17.0.2"} 525721
+# HELP client_expired Not in use (deprecated)
+# TYPE client_expired counter
+client_expired{node="VerneMQ@172.17.0.2"} 0
+# HELP cluster_bytes_received The number of bytes received from other cluster nodes.
+# TYPE cluster_bytes_received counter
+cluster_bytes_received{node="VerneMQ@172.17.0.2"} 0
+# HELP cluster_bytes_sent The number of bytes send to other cluster nodes.
+# TYPE cluster_bytes_sent counter
+cluster_bytes_sent{node="VerneMQ@172.17.0.2"} 0
+# HELP cluster_bytes_dropped The number of bytes dropped while sending data to other cluster nodes.
+# TYPE cluster_bytes_dropped counter
+cluster_bytes_dropped{node="VerneMQ@172.17.0.2"} 0
+# HELP router_matches_local The number of matched local subscriptions.
+# TYPE router_matches_local counter
+router_matches_local{node="VerneMQ@172.17.0.2"} 525722
+# HELP router_matches_remote The number of matched remote subscriptions.
+# TYPE router_matches_remote counter
+router_matches_remote{node="VerneMQ@172.17.0.2"} 0
+# HELP mqtt_connack_sent The number of CONNACK packets sent.
+# TYPE mqtt_connack_sent counter
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="4",return_code="success"} 338948
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="4",return_code="unsupported_protocol_version"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="4",return_code="client_identifier_not_valid"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="4",return_code="server_unavailable"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="4",return_code="bad_username_or_password"} 4
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="4",return_code="not_authorized"} 4
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="normal_disconnect"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="disconnect_with_will_msg"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unspecified_error"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="malformed_packet"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="protocol_error"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="impl_specific_error"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_name_invalid"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="receive_max_exceeded"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_alias_invalid"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_too_large"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="message_rate_too_high"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="quota_exceeded"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="administrative_action"} 0
+mqtt_disconnect_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="payload_format_invalid"} 0
+# HELP mqtt_disconnect_sent The number of DISCONNECT packets sent.
+# TYPE mqtt_disconnect_sent counter
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="normal_disconnect"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unspecified_error"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="malformed_packet"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="protocol_error"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="impl_specific_error"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="not_authorized"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="server_busy"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="server_shutting_down"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="keep_alive_timeout"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="session_taken_over"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_filter_invalid"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_name_invalid"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="receive_max_exceeded"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_alias_invalid"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_too_large"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="message_rate_too_high"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="quota_exceeded"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="administrative_action"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="payload_format_invalid"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="retain_not_supported"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="qos_not_supported"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="use_another_server"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="server_moved"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="shared_subs_not_supported"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="connection_rate_exceeded"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="max_connect_time"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="subscription_ids_not_supported"} 0
+mqtt_disconnect_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="wildcard_subs_not_supported"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unspecified_error"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="malformed_packet"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="protocol_error"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="impl_specific_error"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unsupported_protocol_version"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="client_identifier_not_valid"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="bad_username_or_password"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="not_authorized"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="server_unavailable"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="server_busy"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="banned"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="bad_authentication_method"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_name_invalid"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_too_large"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="quota_exceeded"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="payload_format_invalid"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="retain_not_supported"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="qos_not_supported"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="use_another_server"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="server_moved"} 0
+mqtt_connack_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="connection_rate_exceeded"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="no_matching_subscribers"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unspecified_error"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="impl_specific_error"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="not_authorized"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_name_invalid"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_in_use"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="quota_exceeded"} 0
+mqtt_puback_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="payload_format_invalid"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="no_matching_subscribers"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unspecified_error"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="impl_specific_error"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="not_authorized"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_name_invalid"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_in_use"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="quota_exceeded"} 0
+mqtt_puback_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="payload_format_invalid"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="no_matching_subscribers"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unspecified_error"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="impl_specific_error"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="not_authorized"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_name_invalid"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_in_use"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="quota_exceeded"} 0
+mqtt_pubrec_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="payload_format_invalid"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="no_matching_subscribers"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="unspecified_error"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="impl_specific_error"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="not_authorized"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="topic_name_invalid"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_in_use"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="quota_exceeded"} 0
+mqtt_pubrec_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="payload_format_invalid"} 0
+mqtt_pubrel_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_pubrel_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_not_found"} 0
+mqtt_pubrel_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_pubrel_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_not_found"} 0
+mqtt_pubcomp_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_pubcomp_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_not_found"} 0
+mqtt_pubcomp_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_pubcomp_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="packet_id_not_found"} 0
+# HELP mqtt_auth_sent The number of AUTH packets sent.
+# TYPE mqtt_auth_sent counter
+mqtt_auth_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_auth_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="continue_authentication"} 0
+mqtt_auth_sent{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="reauthenticate"} 0
+# HELP mqtt_auth_received The number of AUTH packets received.
+# TYPE mqtt_auth_received counter
+mqtt_auth_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="success"} 0
+mqtt_auth_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="continue_authentication"} 0
+mqtt_auth_received{node="VerneMQ@172.17.0.2",mqtt_version="5",reason_code="reauthenticate"} 0
+# HELP queue_processes The number of MQTT queue processes.
+# TYPE queue_processes gauge
+queue_processes{node="VerneMQ@172.17.0.2"} 0
+# HELP retain_memory The number of bytes used for storing retained messages.
+# TYPE retain_memory gauge
+retain_memory{node="VerneMQ@172.17.0.2"} 11344
+# HELP retain_messages The number of currently stored retained messages.
+# TYPE retain_messages gauge
+retain_messages{node="VerneMQ@172.17.0.2"} 0
+# HELP router_memory The number of bytes used by the routing table.
+# TYPE router_memory gauge
+router_memory{node="VerneMQ@172.17.0.2"} 12752
+# HELP router_subscriptions The number of subscriptions in the routing table.
+# TYPE router_subscriptions gauge
+router_subscriptions{node="VerneMQ@172.17.0.2"} 0
+# HELP netsplit_resolved The number of resolved netsplits.
+# TYPE netsplit_resolved counter
+netsplit_resolved{node="VerneMQ@172.17.0.2"} 0
+# HELP netsplit_detected The number of detected netsplits.
+# TYPE netsplit_detected counter
+netsplit_detected{node="VerneMQ@172.17.0.2"} 0
+# HELP system_utilization_scheduler_8 Scheduler 8 utilization (percentage)
+# TYPE system_utilization_scheduler_8 gauge
+system_utilization_scheduler_8{node="VerneMQ@172.17.0.2"} 0
+# HELP system_utilization_scheduler_7 Scheduler 7 utilization (percentage)
+# TYPE system_utilization_scheduler_7 gauge
+system_utilization_scheduler_7{node="VerneMQ@172.17.0.2"} 0
+# HELP system_utilization_scheduler_6 Scheduler 6 utilization (percentage)
+# TYPE system_utilization_scheduler_6 gauge
+system_utilization_scheduler_6{node="VerneMQ@172.17.0.2"} 0
+# HELP system_utilization_scheduler_5 Scheduler 5 utilization (percentage)
+# TYPE system_utilization_scheduler_5 gauge
+system_utilization_scheduler_5{node="VerneMQ@172.17.0.2"} 0
+# HELP system_utilization_scheduler_4 Scheduler 4 utilization (percentage)
+# TYPE system_utilization_scheduler_4 gauge
+system_utilization_scheduler_4{node="VerneMQ@172.17.0.2"} 19
+# HELP system_utilization_scheduler_3 Scheduler 3 utilization (percentage)
+# TYPE system_utilization_scheduler_3 gauge
+system_utilization_scheduler_3{node="VerneMQ@172.17.0.2"} 14
+# HELP system_utilization_scheduler_2 Scheduler 2 utilization (percentage)
+# TYPE system_utilization_scheduler_2 gauge
+system_utilization_scheduler_2{node="VerneMQ@172.17.0.2"} 8
+# HELP system_utilization_scheduler_1 Scheduler 1 utilization (percentage)
+# TYPE system_utilization_scheduler_1 gauge
+system_utilization_scheduler_1{node="VerneMQ@172.17.0.2"} 34
+# HELP system_utilization The average system (scheduler) utilization (percentage).
+# TYPE system_utilization gauge
+system_utilization{node="VerneMQ@172.17.0.2"} 9
+# HELP vm_memory_ets The amount of memory allocated for ETS tables.
+# TYPE vm_memory_ets gauge
+vm_memory_ets{node="VerneMQ@172.17.0.2"} 6065944
+# HELP vm_memory_code The amount of memory allocated for code.
+# TYPE vm_memory_code gauge
+vm_memory_code{node="VerneMQ@172.17.0.2"} 11372082
+# HELP vm_memory_binary The amount of memory allocated for binaries.
+# TYPE vm_memory_binary gauge
+vm_memory_binary{node="VerneMQ@172.17.0.2"} 1293672
+# HELP vm_memory_atom_used The amount of memory used by atoms.
+# TYPE vm_memory_atom_used gauge
+vm_memory_atom_used{node="VerneMQ@172.17.0.2"} 755998
+# HELP vm_memory_atom The amount of memory allocated for atoms.
+# TYPE vm_memory_atom gauge
+vm_memory_atom{node="VerneMQ@172.17.0.2"} 768953
+# HELP vm_memory_system The amount of memory allocated for the emulator.
+# TYPE vm_memory_system gauge
+vm_memory_system{node="VerneMQ@172.17.0.2"} 27051848
+# HELP vm_memory_processes_used The amount of memory used by processes.
+# TYPE vm_memory_processes_used gauge
+vm_memory_processes_used{node="VerneMQ@172.17.0.2"} 8671232
+# HELP vm_memory_processes The amount of memory allocated for processes.
+# TYPE vm_memory_processes gauge
+vm_memory_processes{node="VerneMQ@172.17.0.2"} 8673288
+# HELP vm_memory_total The total amount of memory allocated.
+# TYPE vm_memory_total gauge
+vm_memory_total{node="VerneMQ@172.17.0.2"} 35725136
+# HELP system_process_count The number of Erlang processes.
+# TYPE system_process_count gauge
+system_process_count{node="VerneMQ@172.17.0.2"} 329
+# HELP system_wallclock The number of milli-seconds passed since the node was started.
+# TYPE system_wallclock counter
+system_wallclock{node="VerneMQ@172.17.0.2"} 163457858
+# HELP system_runtime The sum of the runtime for all threads in the Erlang runtime system.
+# TYPE system_runtime counter
+system_runtime{node="VerneMQ@172.17.0.2"} 1775355
+# HELP system_run_queue The total number of processes and ports ready to run on all run-queues.
+# TYPE system_run_queue gauge
+system_run_queue{node="VerneMQ@172.17.0.2"} 0
+# HELP system_reductions The number of reductions performed in the VM since the node was started.
+# TYPE system_reductions counter
+system_reductions{node="VerneMQ@172.17.0.2"} 3857458067
+# HELP system_io_out The total number of bytes sent through ports.
+# TYPE system_io_out counter
+system_io_out{node="VerneMQ@172.17.0.2"} 961001488
+# HELP system_io_in The total number of bytes received through ports.
+# TYPE system_io_in counter
+system_io_in{node="VerneMQ@172.17.0.2"} 68998296
+# HELP system_words_reclaimed_by_gc The number of words reclaimed by the garbage collector.
+# TYPE system_words_reclaimed_by_gc counter
+system_words_reclaimed_by_gc{node="VerneMQ@172.17.0.2"} 7158470019
+# HELP system_gc_count The number of garbage collections performed.
+# TYPE system_gc_count counter
+system_gc_count{node="VerneMQ@172.17.0.2"} 12189976
+# HELP system_exact_reductions The exact number of reductions performed.
+# TYPE system_exact_reductions counter
+system_exact_reductions{node="VerneMQ@172.17.0.2"} 3854024620
+# HELP system_context_switches The total number of context switches.
+# TYPE system_context_switches counter
+system_context_switches{node="VerneMQ@172.17.0.2"} 39088198 \ No newline at end of file
diff --git a/src/go/plugin/go.d/modules/vernemq/testdata/non_vernemq.txt b/src/go/plugin/go.d/modules/vernemq/testdata/non_vernemq.txt
new file mode 100644
index 00000000..f5f0ae08
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/testdata/non_vernemq.txt
@@ -0,0 +1,27 @@
+# HELP wmi_os_process_memory_limix_bytes OperatingSystem.MaxProcessMemorySize
+# TYPE wmi_os_process_memory_limix_bytes gauge
+wmi_os_process_memory_limix_bytes 1.40737488224256e+14
+# HELP wmi_os_processes OperatingSystem.NumberOfProcesses
+# TYPE wmi_os_processes gauge
+wmi_os_processes 124
+# HELP wmi_os_processes_limit OperatingSystem.MaxNumberOfProcesses
+# TYPE wmi_os_processes_limit gauge
+wmi_os_processes_limit 4.294967295e+09
+# HELP wmi_os_time OperatingSystem.LocalDateTime
+# TYPE wmi_os_time gauge
+wmi_os_time 1.57804974e+09
+# HELP wmi_os_timezone OperatingSystem.LocalDateTime
+# TYPE wmi_os_timezone gauge
+wmi_os_timezone{timezone="MSK"} 1
+# HELP wmi_os_users OperatingSystem.NumberOfUsers
+# TYPE wmi_os_users gauge
+wmi_os_users 2
+# HELP wmi_os_virtual_memory_bytes OperatingSystem.TotalVirtualMemorySize
+# TYPE wmi_os_virtual_memory_bytes gauge
+wmi_os_virtual_memory_bytes 5.770891264e+09
+# HELP wmi_os_virtual_memory_free_bytes OperatingSystem.FreeVirtualMemory
+# TYPE wmi_os_virtual_memory_free_bytes gauge
+wmi_os_virtual_memory_free_bytes 3.76489984e+09
+# HELP wmi_os_visible_memory_bytes OperatingSystem.TotalVisibleMemorySize
+# TYPE wmi_os_visible_memory_bytes gauge
+wmi_os_visible_memory_bytes 4.294496256e+09 \ No newline at end of file
diff --git a/src/go/plugin/go.d/modules/vernemq/vernemq.go b/src/go/plugin/go.d/modules/vernemq/vernemq.go
new file mode 100644
index 00000000..2f1de38f
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/vernemq.go
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package vernemq
+
+import (
+ _ "embed"
+ "errors"
+ "time"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/prometheus"
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
+)
+
+//go:embed "config_schema.json"
+var configSchema string
+
+func init() {
+ module.Register("vernemq", module.Creator{
+ JobConfigSchema: configSchema,
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *VerneMQ {
+ return &VerneMQ{
+ Config: Config{
+ HTTP: web.HTTP{
+ Request: web.Request{
+ URL: "http://127.0.0.1:8888/metrics",
+ },
+ Client: web.Client{
+ Timeout: web.Duration(time.Second),
+ },
+ },
+ },
+ charts: charts.Copy(),
+ cache: make(map[string]bool),
+ }
+}
+
+type Config struct {
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
+ web.HTTP `yaml:",inline" json:""`
+}
+
+type (
+ VerneMQ struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ charts *Charts
+
+ prom prometheus.Prometheus
+
+ cache map[string]bool
+ }
+)
+
+func (v *VerneMQ) Configuration() any {
+ return v.Config
+}
+
+func (v *VerneMQ) Init() error {
+ if err := v.validateConfig(); err != nil {
+ v.Errorf("error on validating config: %v", err)
+ return err
+ }
+
+ prom, err := v.initPrometheusClient()
+ if err != nil {
+ v.Error(err)
+ return err
+ }
+ v.prom = prom
+
+ return nil
+}
+
+func (v *VerneMQ) Check() error {
+ mx, err := v.collect()
+ if err != nil {
+ v.Error(err)
+ return err
+ }
+ if len(mx) == 0 {
+ return errors.New("no metrics collected")
+ }
+ return nil
+}
+
+func (v *VerneMQ) Charts() *Charts {
+ return v.charts
+}
+
+func (v *VerneMQ) Collect() map[string]int64 {
+ mx, err := v.collect()
+ if err != nil {
+ v.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+ return mx
+}
+
+func (v *VerneMQ) Cleanup() {
+ if v.prom != nil && v.prom.HTTPClient() != nil {
+ v.prom.HTTPClient().CloseIdleConnections()
+ }
+}
diff --git a/src/go/plugin/go.d/modules/vernemq/vernemq_test.go b/src/go/plugin/go.d/modules/vernemq/vernemq_test.go
new file mode 100644
index 00000000..13eb3dce
--- /dev/null
+++ b/src/go/plugin/go.d/modules/vernemq/vernemq_test.go
@@ -0,0 +1,578 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package vernemq
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+
+ dataVer1101MQTTv5Metrics, _ = os.ReadFile("testdata/metrics-v1.10.1-mqtt5.txt")
+ dataUnexpectedMetrics, _ = os.ReadFile("testdata/non_vernemq.txt")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ "dataVer1101MQTTv5Metrics": dataVer1101MQTTv5Metrics,
+ "dataUnexpectedMetrics": dataUnexpectedMetrics,
+ } {
+ require.NotNil(t, data, name)
+ }
+}
+
+func TestVerneMQ_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &VerneMQ{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestVerneMQ_Init(t *testing.T) {
+ verneMQ := prepareVerneMQ()
+
+ assert.NoError(t, verneMQ.Init())
+}
+
+func TestVerneMQ_Init_ReturnsFalseIfURLIsNotSet(t *testing.T) {
+ verneMQ := prepareVerneMQ()
+ verneMQ.URL = ""
+
+ assert.Error(t, verneMQ.Init())
+}
+
+func TestVerneMQ_Init_ReturnsFalseIfClientWrongTLSCA(t *testing.T) {
+ verneMQ := prepareVerneMQ()
+ verneMQ.Client.TLSConfig.TLSCA = "testdata/tls"
+
+ assert.Error(t, verneMQ.Init())
+}
+
+func TestVerneMQ_Check(t *testing.T) {
+ verneMQ, srv := prepareClientServerV1101(t)
+ defer srv.Close()
+
+ assert.NoError(t, verneMQ.Check())
+}
+
+func TestVerneMQ_Check_ReturnsFalseIfConnectionRefused(t *testing.T) {
+ verneMQ := prepareVerneMQ()
+ require.NoError(t, verneMQ.Init())
+
+ assert.Error(t, verneMQ.Check())
+}
+
+func TestVerneMQ_Check_ReturnsFalseIfMetricsAreNotVerneMQ(t *testing.T) {
+ verneMQ, srv := prepareClientServerNotVerneMQ(t)
+ defer srv.Close()
+ require.NoError(t, verneMQ.Init())
+
+ assert.Error(t, verneMQ.Check())
+}
+
+func TestVerneMQ_Charts(t *testing.T) {
+ assert.NotNil(t, New().Charts())
+}
+
+func TestVerneMQ_Cleanup(t *testing.T) {
+ assert.NotPanics(t, New().Cleanup)
+}
+
+func TestVerneMQ_Collect(t *testing.T) {
+ verneMQ, srv := prepareClientServerV1101(t)
+ defer srv.Close()
+
+ collected := verneMQ.Collect()
+ assert.Equal(t, v1101ExpectedMetrics, collected)
+ testCharts(t, verneMQ, collected)
+}
+
+func TestVerneMQ_Collect_ReturnsNilIfConnectionRefused(t *testing.T) {
+ verneMQ := prepareVerneMQ()
+ require.NoError(t, verneMQ.Init())
+
+ assert.Nil(t, verneMQ.Collect())
+}
+
+func TestVerneMQ_Collect_ReturnsNilIfMetricsAreNotVerneMQ(t *testing.T) {
+ verneMQ, srv := prepareClientServerNotVerneMQ(t)
+ defer srv.Close()
+
+ assert.Nil(t, verneMQ.Collect())
+}
+
+func TestVerneMQ_Collect_ReturnsNilIfReceiveInvalidResponse(t *testing.T) {
+ verneMQ, ts := prepareClientServerInvalid(t)
+ defer ts.Close()
+
+ assert.Nil(t, verneMQ.Collect())
+}
+
+func TestVerneMQ_Collect_ReturnsNilIfReceiveResponse404(t *testing.T) {
+ verneMQ, ts := prepareClientServerResponse404(t)
+ defer ts.Close()
+
+ assert.Nil(t, verneMQ.Collect())
+}
+
+func testCharts(t *testing.T, verneMQ *VerneMQ, collected map[string]int64) {
+ ensureCollectedHasAllChartsDimsVarsIDs(t, verneMQ, collected)
+}
+
+func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, verneMQ *VerneMQ, collected map[string]int64) {
+ for _, chart := range *verneMQ.Charts() {
+ for _, dim := range chart.Dims {
+ _, ok := collected[dim.ID]
+ assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID)
+ }
+ for _, v := range chart.Vars {
+ _, ok := collected[v.ID]
+ assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID)
+ }
+ }
+}
+
+func prepareVerneMQ() *VerneMQ {
+ verneMQ := New()
+ verneMQ.URL = "http://127.0.0.1:38001/metrics"
+ return verneMQ
+}
+
+func prepareClientServerV1101(t *testing.T) (*VerneMQ, *httptest.Server) {
+ t.Helper()
+ ts := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write(dataVer1101MQTTv5Metrics)
+ }))
+
+ verneMQ := New()
+ verneMQ.URL = ts.URL
+ require.NoError(t, verneMQ.Init())
+
+ return verneMQ, ts
+}
+
+func prepareClientServerNotVerneMQ(t *testing.T) (*VerneMQ, *httptest.Server) {
+ t.Helper()
+ ts := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write(dataUnexpectedMetrics)
+ }))
+
+ verneMQ := New()
+ verneMQ.URL = ts.URL
+ require.NoError(t, verneMQ.Init())
+
+ return verneMQ, ts
+}
+
+func prepareClientServerInvalid(t *testing.T) (*VerneMQ, *httptest.Server) {
+ t.Helper()
+ ts := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, _ = w.Write([]byte("hello and\n goodbye"))
+ }))
+
+ verneMQ := New()
+ verneMQ.URL = ts.URL
+ require.NoError(t, verneMQ.Init())
+
+ return verneMQ, ts
+}
+
+func prepareClientServerResponse404(t *testing.T) (*VerneMQ, *httptest.Server) {
+ t.Helper()
+ ts := httptest.NewServer(http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusNotFound)
+ }))
+
+ verneMQ := New()
+ verneMQ.URL = ts.URL
+ require.NoError(t, verneMQ.Init())
+ return verneMQ, ts
+}
+
+var v1101ExpectedMetrics = map[string]int64{
+ "bytes_received": 36796908,
+ "bytes_sent": 23361693,
+ "client_keepalive_expired": 1,
+ "cluster_bytes_dropped": 0,
+ "cluster_bytes_received": 0,
+ "cluster_bytes_sent": 0,
+ "mqtt_auth_received": 0,
+ "mqtt_auth_received_continue_authentication": 0,
+ "mqtt_auth_received_reauthenticate": 0,
+ "mqtt_auth_received_success": 0,
+ "mqtt_auth_received_v_5": 0,
+ "mqtt_auth_received_v_5_continue_authentication": 0,
+ "mqtt_auth_received_v_5_reauthenticate": 0,
+ "mqtt_auth_received_v_5_success": 0,
+ "mqtt_auth_sent": 0,
+ "mqtt_auth_sent_continue_authentication": 0,
+ "mqtt_auth_sent_reauthenticate": 0,
+ "mqtt_auth_sent_success": 0,
+ "mqtt_auth_sent_v_5": 0,
+ "mqtt_auth_sent_v_5_continue_authentication": 0,
+ "mqtt_auth_sent_v_5_reauthenticate": 0,
+ "mqtt_auth_sent_v_5_success": 0,
+ "mqtt_connack_sent": 338956,
+ "mqtt_connack_sent_bad_authentication_method": 0,
+ "mqtt_connack_sent_bad_username_or_password": 4,
+ "mqtt_connack_sent_banned": 0,
+ "mqtt_connack_sent_client_identifier_not_valid": 0,
+ "mqtt_connack_sent_connection_rate_exceeded": 0,
+ "mqtt_connack_sent_impl_specific_error": 0,
+ "mqtt_connack_sent_malformed_packet": 0,
+ "mqtt_connack_sent_not_authorized": 4,
+ "mqtt_connack_sent_packet_too_large": 0,
+ "mqtt_connack_sent_payload_format_invalid": 0,
+ "mqtt_connack_sent_protocol_error": 0,
+ "mqtt_connack_sent_qos_not_supported": 0,
+ "mqtt_connack_sent_quota_exceeded": 0,
+ "mqtt_connack_sent_retain_not_supported": 0,
+ "mqtt_connack_sent_server_busy": 0,
+ "mqtt_connack_sent_server_moved": 0,
+ "mqtt_connack_sent_server_unavailable": 0,
+ "mqtt_connack_sent_success": 338948,
+ "mqtt_connack_sent_topic_name_invalid": 0,
+ "mqtt_connack_sent_unspecified_error": 0,
+ "mqtt_connack_sent_unsupported_protocol_version": 0,
+ "mqtt_connack_sent_use_another_server": 0,
+ "mqtt_connack_sent_v_4": 338956,
+ "mqtt_connack_sent_v_4_bad_username_or_password": 4,
+ "mqtt_connack_sent_v_4_client_identifier_not_valid": 0,
+ "mqtt_connack_sent_v_4_not_authorized": 4,
+ "mqtt_connack_sent_v_4_server_unavailable": 0,
+ "mqtt_connack_sent_v_4_success": 338948,
+ "mqtt_connack_sent_v_4_unsupported_protocol_version": 0,
+ "mqtt_connack_sent_v_5": 0,
+ "mqtt_connack_sent_v_5_bad_authentication_method": 0,
+ "mqtt_connack_sent_v_5_bad_username_or_password": 0,
+ "mqtt_connack_sent_v_5_banned": 0,
+ "mqtt_connack_sent_v_5_client_identifier_not_valid": 0,
+ "mqtt_connack_sent_v_5_connection_rate_exceeded": 0,
+ "mqtt_connack_sent_v_5_impl_specific_error": 0,
+ "mqtt_connack_sent_v_5_malformed_packet": 0,
+ "mqtt_connack_sent_v_5_not_authorized": 0,
+ "mqtt_connack_sent_v_5_packet_too_large": 0,
+ "mqtt_connack_sent_v_5_payload_format_invalid": 0,
+ "mqtt_connack_sent_v_5_protocol_error": 0,
+ "mqtt_connack_sent_v_5_qos_not_supported": 0,
+ "mqtt_connack_sent_v_5_quota_exceeded": 0,
+ "mqtt_connack_sent_v_5_retain_not_supported": 0,
+ "mqtt_connack_sent_v_5_server_busy": 0,
+ "mqtt_connack_sent_v_5_server_moved": 0,
+ "mqtt_connack_sent_v_5_server_unavailable": 0,
+ "mqtt_connack_sent_v_5_success": 0,
+ "mqtt_connack_sent_v_5_topic_name_invalid": 0,
+ "mqtt_connack_sent_v_5_unspecified_error": 0,
+ "mqtt_connack_sent_v_5_unsupported_protocol_version": 0,
+ "mqtt_connack_sent_v_5_use_another_server": 0,
+ "mqtt_connect_received": 338956,
+ "mqtt_connect_received_v_4": 338956,
+ "mqtt_connect_received_v_5": 0,
+ "mqtt_disconnect_received": 107,
+ "mqtt_disconnect_received_administrative_action": 0,
+ "mqtt_disconnect_received_disconnect_with_will_msg": 0,
+ "mqtt_disconnect_received_impl_specific_error": 0,
+ "mqtt_disconnect_received_malformed_packet": 0,
+ "mqtt_disconnect_received_message_rate_too_high": 0,
+ "mqtt_disconnect_received_normal_disconnect": 0,
+ "mqtt_disconnect_received_packet_too_large": 0,
+ "mqtt_disconnect_received_payload_format_invalid": 0,
+ "mqtt_disconnect_received_protocol_error": 0,
+ "mqtt_disconnect_received_quota_exceeded": 0,
+ "mqtt_disconnect_received_receive_max_exceeded": 0,
+ "mqtt_disconnect_received_topic_alias_invalid": 0,
+ "mqtt_disconnect_received_topic_name_invalid": 0,
+ "mqtt_disconnect_received_unspecified_error": 0,
+ "mqtt_disconnect_received_v_4": 107,
+ "mqtt_disconnect_received_v_5": 0,
+ "mqtt_disconnect_received_v_5_administrative_action": 0,
+ "mqtt_disconnect_received_v_5_disconnect_with_will_msg": 0,
+ "mqtt_disconnect_received_v_5_impl_specific_error": 0,
+ "mqtt_disconnect_received_v_5_malformed_packet": 0,
+ "mqtt_disconnect_received_v_5_message_rate_too_high": 0,
+ "mqtt_disconnect_received_v_5_normal_disconnect": 0,
+ "mqtt_disconnect_received_v_5_packet_too_large": 0,
+ "mqtt_disconnect_received_v_5_payload_format_invalid": 0,
+ "mqtt_disconnect_received_v_5_protocol_error": 0,
+ "mqtt_disconnect_received_v_5_quota_exceeded": 0,
+ "mqtt_disconnect_received_v_5_receive_max_exceeded": 0,
+ "mqtt_disconnect_received_v_5_topic_alias_invalid": 0,
+ "mqtt_disconnect_received_v_5_topic_name_invalid": 0,
+ "mqtt_disconnect_received_v_5_unspecified_error": 0,
+ "mqtt_disconnect_sent": 0,
+ "mqtt_disconnect_sent_administrative_action": 0,
+ "mqtt_disconnect_sent_connection_rate_exceeded": 0,
+ "mqtt_disconnect_sent_impl_specific_error": 0,
+ "mqtt_disconnect_sent_keep_alive_timeout": 0,
+ "mqtt_disconnect_sent_malformed_packet": 0,
+ "mqtt_disconnect_sent_max_connect_time": 0,
+ "mqtt_disconnect_sent_message_rate_too_high": 0,
+ "mqtt_disconnect_sent_normal_disconnect": 0,
+ "mqtt_disconnect_sent_not_authorized": 0,
+ "mqtt_disconnect_sent_packet_too_large": 0,
+ "mqtt_disconnect_sent_payload_format_invalid": 0,
+ "mqtt_disconnect_sent_protocol_error": 0,
+ "mqtt_disconnect_sent_qos_not_supported": 0,
+ "mqtt_disconnect_sent_quota_exceeded": 0,
+ "mqtt_disconnect_sent_receive_max_exceeded": 0,
+ "mqtt_disconnect_sent_retain_not_supported": 0,
+ "mqtt_disconnect_sent_server_busy": 0,
+ "mqtt_disconnect_sent_server_moved": 0,
+ "mqtt_disconnect_sent_server_shutting_down": 0,
+ "mqtt_disconnect_sent_session_taken_over": 0,
+ "mqtt_disconnect_sent_shared_subs_not_supported": 0,
+ "mqtt_disconnect_sent_subscription_ids_not_supported": 0,
+ "mqtt_disconnect_sent_topic_alias_invalid": 0,
+ "mqtt_disconnect_sent_topic_filter_invalid": 0,
+ "mqtt_disconnect_sent_topic_name_invalid": 0,
+ "mqtt_disconnect_sent_unspecified_error": 0,
+ "mqtt_disconnect_sent_use_another_server": 0,
+ "mqtt_disconnect_sent_v_5": 0,
+ "mqtt_disconnect_sent_v_5_administrative_action": 0,
+ "mqtt_disconnect_sent_v_5_connection_rate_exceeded": 0,
+ "mqtt_disconnect_sent_v_5_impl_specific_error": 0,
+ "mqtt_disconnect_sent_v_5_keep_alive_timeout": 0,
+ "mqtt_disconnect_sent_v_5_malformed_packet": 0,
+ "mqtt_disconnect_sent_v_5_max_connect_time": 0,
+ "mqtt_disconnect_sent_v_5_message_rate_too_high": 0,
+ "mqtt_disconnect_sent_v_5_normal_disconnect": 0,
+ "mqtt_disconnect_sent_v_5_not_authorized": 0,
+ "mqtt_disconnect_sent_v_5_packet_too_large": 0,
+ "mqtt_disconnect_sent_v_5_payload_format_invalid": 0,
+ "mqtt_disconnect_sent_v_5_protocol_error": 0,
+ "mqtt_disconnect_sent_v_5_qos_not_supported": 0,
+ "mqtt_disconnect_sent_v_5_quota_exceeded": 0,
+ "mqtt_disconnect_sent_v_5_receive_max_exceeded": 0,
+ "mqtt_disconnect_sent_v_5_retain_not_supported": 0,
+ "mqtt_disconnect_sent_v_5_server_busy": 0,
+ "mqtt_disconnect_sent_v_5_server_moved": 0,
+ "mqtt_disconnect_sent_v_5_server_shutting_down": 0,
+ "mqtt_disconnect_sent_v_5_session_taken_over": 0,
+ "mqtt_disconnect_sent_v_5_shared_subs_not_supported": 0,
+ "mqtt_disconnect_sent_v_5_subscription_ids_not_supported": 0,
+ "mqtt_disconnect_sent_v_5_topic_alias_invalid": 0,
+ "mqtt_disconnect_sent_v_5_topic_filter_invalid": 0,
+ "mqtt_disconnect_sent_v_5_topic_name_invalid": 0,
+ "mqtt_disconnect_sent_v_5_unspecified_error": 0,
+ "mqtt_disconnect_sent_v_5_use_another_server": 0,
+ "mqtt_disconnect_sent_v_5_wildcard_subs_not_supported": 0,
+ "mqtt_disconnect_sent_wildcard_subs_not_supported": 0,
+ "mqtt_invalid_msg_size_error": 0,
+ "mqtt_invalid_msg_size_error_v_4": 0,
+ "mqtt_invalid_msg_size_error_v_5": 0,
+ "mqtt_pingreq_received": 205,
+ "mqtt_pingreq_received_v_4": 205,
+ "mqtt_pingreq_received_v_5": 0,
+ "mqtt_pingresp_sent": 205,
+ "mqtt_pingresp_sent_v_4": 205,
+ "mqtt_pingresp_sent_v_5": 0,
+ "mqtt_puback_invalid_error": 0,
+ "mqtt_puback_invalid_error_v_4": 0,
+ "mqtt_puback_invalid_error_v_5": 0,
+ "mqtt_puback_received": 525694,
+ "mqtt_puback_received_impl_specific_error": 0,
+ "mqtt_puback_received_no_matching_subscribers": 0,
+ "mqtt_puback_received_not_authorized": 0,
+ "mqtt_puback_received_packet_id_in_use": 0,
+ "mqtt_puback_received_payload_format_invalid": 0,
+ "mqtt_puback_received_quota_exceeded": 0,
+ "mqtt_puback_received_success": 0,
+ "mqtt_puback_received_topic_name_invalid": 0,
+ "mqtt_puback_received_unspecified_error": 0,
+ "mqtt_puback_received_v_4": 525694,
+ "mqtt_puback_received_v_5": 0,
+ "mqtt_puback_received_v_5_impl_specific_error": 0,
+ "mqtt_puback_received_v_5_no_matching_subscribers": 0,
+ "mqtt_puback_received_v_5_not_authorized": 0,
+ "mqtt_puback_received_v_5_packet_id_in_use": 0,
+ "mqtt_puback_received_v_5_payload_format_invalid": 0,
+ "mqtt_puback_received_v_5_quota_exceeded": 0,
+ "mqtt_puback_received_v_5_success": 0,
+ "mqtt_puback_received_v_5_topic_name_invalid": 0,
+ "mqtt_puback_received_v_5_unspecified_error": 0,
+ "mqtt_puback_sent": 537068,
+ "mqtt_puback_sent_impl_specific_error": 0,
+ "mqtt_puback_sent_no_matching_subscribers": 0,
+ "mqtt_puback_sent_not_authorized": 0,
+ "mqtt_puback_sent_packet_id_in_use": 0,
+ "mqtt_puback_sent_payload_format_invalid": 0,
+ "mqtt_puback_sent_quota_exceeded": 0,
+ "mqtt_puback_sent_success": 0,
+ "mqtt_puback_sent_topic_name_invalid": 0,
+ "mqtt_puback_sent_unspecified_error": 0,
+ "mqtt_puback_sent_v_4": 537068,
+ "mqtt_puback_sent_v_5": 0,
+ "mqtt_puback_sent_v_5_impl_specific_error": 0,
+ "mqtt_puback_sent_v_5_no_matching_subscribers": 0,
+ "mqtt_puback_sent_v_5_not_authorized": 0,
+ "mqtt_puback_sent_v_5_packet_id_in_use": 0,
+ "mqtt_puback_sent_v_5_payload_format_invalid": 0,
+ "mqtt_puback_sent_v_5_quota_exceeded": 0,
+ "mqtt_puback_sent_v_5_success": 0,
+ "mqtt_puback_sent_v_5_topic_name_invalid": 0,
+ "mqtt_puback_sent_v_5_unspecified_error": 0,
+ "mqtt_pubcomp_invalid_error": 0,
+ "mqtt_pubcomp_invalid_error_v_4": 0,
+ "mqtt_pubcomp_invalid_error_v_5": 0,
+ "mqtt_pubcomp_received": 0,
+ "mqtt_pubcomp_received_packet_id_not_found": 0,
+ "mqtt_pubcomp_received_success": 0,
+ "mqtt_pubcomp_received_v_4": 0,
+ "mqtt_pubcomp_received_v_5": 0,
+ "mqtt_pubcomp_received_v_5_packet_id_not_found": 0,
+ "mqtt_pubcomp_received_v_5_success": 0,
+ "mqtt_pubcomp_sent": 0,
+ "mqtt_pubcomp_sent_packet_id_not_found": 0,
+ "mqtt_pubcomp_sent_success": 0,
+ "mqtt_pubcomp_sent_v_4": 0,
+ "mqtt_pubcomp_sent_v_5": 0,
+ "mqtt_pubcomp_sent_v_5_packet_id_not_found": 0,
+ "mqtt_pubcomp_sent_v_5_success": 0,
+ "mqtt_publish_auth_error": 0,
+ "mqtt_publish_auth_error_v_4": 0,
+ "mqtt_publish_auth_error_v_5": 0,
+ "mqtt_publish_error": 0,
+ "mqtt_publish_error_v_4": 0,
+ "mqtt_publish_error_v_5": 0,
+ "mqtt_publish_received": 537088,
+ "mqtt_publish_received_v_4": 537088,
+ "mqtt_publish_received_v_5": 0,
+ "mqtt_publish_sent": 525721,
+ "mqtt_publish_sent_v_4": 525721,
+ "mqtt_publish_sent_v_5": 0,
+ "mqtt_pubrec_invalid_error": 0,
+ "mqtt_pubrec_invalid_error_v_4": 0,
+ "mqtt_pubrec_received": 0,
+ "mqtt_pubrec_received_impl_specific_error": 0,
+ "mqtt_pubrec_received_no_matching_subscribers": 0,
+ "mqtt_pubrec_received_not_authorized": 0,
+ "mqtt_pubrec_received_packet_id_in_use": 0,
+ "mqtt_pubrec_received_payload_format_invalid": 0,
+ "mqtt_pubrec_received_quota_exceeded": 0,
+ "mqtt_pubrec_received_success": 0,
+ "mqtt_pubrec_received_topic_name_invalid": 0,
+ "mqtt_pubrec_received_unspecified_error": 0,
+ "mqtt_pubrec_received_v_4": 0,
+ "mqtt_pubrec_received_v_5": 0,
+ "mqtt_pubrec_received_v_5_impl_specific_error": 0,
+ "mqtt_pubrec_received_v_5_no_matching_subscribers": 0,
+ "mqtt_pubrec_received_v_5_not_authorized": 0,
+ "mqtt_pubrec_received_v_5_packet_id_in_use": 0,
+ "mqtt_pubrec_received_v_5_payload_format_invalid": 0,
+ "mqtt_pubrec_received_v_5_quota_exceeded": 0,
+ "mqtt_pubrec_received_v_5_success": 0,
+ "mqtt_pubrec_received_v_5_topic_name_invalid": 0,
+ "mqtt_pubrec_received_v_5_unspecified_error": 0,
+ "mqtt_pubrec_sent": 0,
+ "mqtt_pubrec_sent_impl_specific_error": 0,
+ "mqtt_pubrec_sent_no_matching_subscribers": 0,
+ "mqtt_pubrec_sent_not_authorized": 0,
+ "mqtt_pubrec_sent_packet_id_in_use": 0,
+ "mqtt_pubrec_sent_payload_format_invalid": 0,
+ "mqtt_pubrec_sent_quota_exceeded": 0,
+ "mqtt_pubrec_sent_success": 0,
+ "mqtt_pubrec_sent_topic_name_invalid": 0,
+ "mqtt_pubrec_sent_unspecified_error": 0,
+ "mqtt_pubrec_sent_v_4": 0,
+ "mqtt_pubrec_sent_v_5": 0,
+ "mqtt_pubrec_sent_v_5_impl_specific_error": 0,
+ "mqtt_pubrec_sent_v_5_no_matching_subscribers": 0,
+ "mqtt_pubrec_sent_v_5_not_authorized": 0,
+ "mqtt_pubrec_sent_v_5_packet_id_in_use": 0,
+ "mqtt_pubrec_sent_v_5_payload_format_invalid": 0,
+ "mqtt_pubrec_sent_v_5_quota_exceeded": 0,
+ "mqtt_pubrec_sent_v_5_success": 0,
+ "mqtt_pubrec_sent_v_5_topic_name_invalid": 0,
+ "mqtt_pubrec_sent_v_5_unspecified_error": 0,
+ "mqtt_pubrel_received": 0,
+ "mqtt_pubrel_received_packet_id_not_found": 0,
+ "mqtt_pubrel_received_success": 0,
+ "mqtt_pubrel_received_v_4": 0,
+ "mqtt_pubrel_received_v_5": 0,
+ "mqtt_pubrel_received_v_5_packet_id_not_found": 0,
+ "mqtt_pubrel_received_v_5_success": 0,
+ "mqtt_pubrel_sent": 0,
+ "mqtt_pubrel_sent_packet_id_not_found": 0,
+ "mqtt_pubrel_sent_success": 0,
+ "mqtt_pubrel_sent_v_4": 0,
+ "mqtt_pubrel_sent_v_5": 0,
+ "mqtt_pubrel_sent_v_5_packet_id_not_found": 0,
+ "mqtt_pubrel_sent_v_5_success": 0,
+ "mqtt_suback_sent": 122,
+ "mqtt_suback_sent_v_4": 122,
+ "mqtt_suback_sent_v_5": 0,
+ "mqtt_subscribe_auth_error": 0,
+ "mqtt_subscribe_auth_error_v_4": 0,
+ "mqtt_subscribe_auth_error_v_5": 0,
+ "mqtt_subscribe_error": 0,
+ "mqtt_subscribe_error_v_4": 0,
+ "mqtt_subscribe_error_v_5": 0,
+ "mqtt_subscribe_received": 122,
+ "mqtt_subscribe_received_v_4": 122,
+ "mqtt_subscribe_received_v_5": 0,
+ "mqtt_unsuback_sent": 108,
+ "mqtt_unsuback_sent_v_4": 108,
+ "mqtt_unsuback_sent_v_5": 0,
+ "mqtt_unsubscribe_error": 0,
+ "mqtt_unsubscribe_error_v_4": 0,
+ "mqtt_unsubscribe_error_v_5": 0,
+ "mqtt_unsubscribe_received": 108,
+ "mqtt_unsubscribe_received_v_4": 108,
+ "mqtt_unsubscribe_received_v_5": 0,
+ "netsplit_detected": 0,
+ "netsplit_resolved": 0,
+ "netsplit_unresolved": 0,
+ "open_sockets": 0,
+ "queue_initialized_from_storage": 0,
+ "queue_message_drop": 0,
+ "queue_message_expired": 0,
+ "queue_message_in": 525722,
+ "queue_message_out": 525721,
+ "queue_message_unhandled": 1,
+ "queue_processes": 0,
+ "queue_setup": 338948,
+ "queue_teardown": 338948,
+ "retain_memory": 11344,
+ "retain_messages": 0,
+ "router_matches_local": 525722,
+ "router_matches_remote": 0,
+ "router_memory": 12752,
+ "router_subscriptions": 0,
+ "socket_close": 338956,
+ "socket_close_timeout": 0,
+ "socket_error": 0,
+ "socket_open": 338956,
+ "system_context_switches": 39088198,
+ "system_gc_count": 12189976,
+ "system_io_in": 68998296,
+ "system_io_out": 961001488,
+ "system_process_count": 329,
+ "system_reductions": 3857458067,
+ "system_run_queue": 0,
+ "system_utilization": 9,
+ "system_utilization_scheduler_1": 34,
+ "system_utilization_scheduler_2": 8,
+ "system_utilization_scheduler_3": 14,
+ "system_utilization_scheduler_4": 19,
+ "system_utilization_scheduler_5": 0,
+ "system_utilization_scheduler_6": 0,
+ "system_utilization_scheduler_7": 0,
+ "system_utilization_scheduler_8": 0,
+ "system_wallclock": 163457858,
+ "system_words_reclaimed_by_gc": 7158470019,
+ "vm_memory_processes": 8673288,
+ "vm_memory_system": 27051848,
+}