Skip to content

Commit

Permalink
Merge pull request #46 from Omniaevo/develop
Browse files Browse the repository at this point in the history
Log monitored topics to file beside system notifications
  • Loading branch information
BlazeRed authored May 2, 2024
2 parents d7ecf40 + ee8659b commit 8bf679b
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 19 deletions.
13 changes: 11 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mqtt5-explorer",
"version": "1.13.1",
"version": "1.14.0",
"private": false,
"license": "GPLv3",
"description": "A simple MQTT client that supports MQTT5 protocol.",
Expand Down Expand Up @@ -37,6 +37,7 @@
"core-js": "^3.27.0",
"electron-store": "^8.1.0",
"electron-updater": "^6.1.7",
"moment": "^2.30.1",
"mqtt": "^4.3.7",
"register-service-worker": "^1.7.2",
"uuid": "^8.3.2",
Expand Down
80 changes: 80 additions & 0 deletions src/utils/MessageLogger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import os from "os";
import path from "path";
import fs from "fs";
import * as moment from "moment";

class MessageLogger {
#subPath = "";
#basePath = "";
#separator = path.sep;

#writing = false;
#writingCache = [];

constructor(connectionName = moment().valueOf().toString()) {
this.#subPath = this.#pathSafe(connectionName);
this.#basePath = os.homedir() + "/mqtt5-explorer-logs";
}

get logsFolder() {
return this.#basePath + this.#separator + this.#subPath;
}

enqueue({ topic, payload, properties }) {
const now = moment();
this.#writingCache.unshift({
topic,
payload,
properties: properties ? JSON.stringify(properties) : "",
when: now.valueOf(),
date: now.format("YYYY-MM-DD HH:mm:ss.SSS"),
});
}

async startLogging() {
this.#writing = true;
this.#subPath += this.#separator + moment().format("YYYY-MM-DD_HH-mm-ss");

// Create folders path
fs.mkdirSync(this.logsFolder, { recursive: true });

while (this.#writing) {
if (this.#writingCache.length === 0) {
await this.#sleep(1000);
continue;
}

const { topic, payload, properties, when, date } =
this.#writingCache.pop();
const fileName =
this.logsFolder + this.#separator + this.#pathSafe(topic) + ".csv";

// Create file if not exists
if (!fs.existsSync(fileName)) {
fs.writeFileSync(fileName, "Timestamp;Date;Value;Properties\n", {
flag: "w",
});
}

// Update log file
fs.writeFileSync(fileName, `${when};${date};${payload};${properties}\n`, {
flag: "a",
});
}
}

stopLogging() {
this.#writing = false;
this.#writingCache = [];
}

#pathSafe = (topic) => {
return topic?.replace(/\//g, "_") || "unknown-topic";
};

#sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
}

export default MessageLogger;
84 changes: 68 additions & 16 deletions src/views/MqttViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@
v-on="on"
icon
>
<v-icon>mdi-bell-cog-outline</v-icon>
<v-icon>mdi-message-badge-outline</v-icon>
</v-btn>
</template>
<span>Enable notifications</span>
<span>Notifications and logging</span>
</v-tooltip>
</div>
<div v-if="selectedId !== -1" center-vertical class="ms-2">
Expand Down Expand Up @@ -470,7 +470,7 @@

<v-dialog v-model="notifySettings" max-width="70ch" persistent scrollable>
<v-card>
<v-card-title>Notifications</v-card-title>
<v-card-title>Notifications and logging</v-card-title>
<v-card-text class="d-flex flex-column">
<div class="d-flex align-center">
<v-text-field
Expand Down Expand Up @@ -540,7 +540,7 @@
</div>
<div class="mt-2">
<div class="d-flex justify-space-between align-center">
<span>Notification conditions:</span>
<span>Trigger conditions:</span>
<v-btn-toggle v-model="notifyJoinType">
<v-btn
v-bind:value="joinModes.OR"
Expand Down Expand Up @@ -618,9 +618,31 @@
</div>
</v-list>
</div>
<div class="mt-2 d-flex align-center justify-space-between">
<v-switch
v-model="notifySwitch"
label="Enable notifications"
inset
/>
<v-switch
v-model="fileLoggingSwitch"
label="Enable file logging"
inset
/>
</div>
<v-slide-y-transition>
<div v-if="fileLoggingSwitch">
<v-text-field
v-model="messageLogger.logsFolder"
v-bind:outlined="outline"
label="Logs folder location"
readonly
/>
</div>
</v-slide-y-transition>
</v-card-text>
<v-card-actions>
<v-btn v-on:click="notifyEntries = []" color="error" text>
<v-btn v-on:click="resetNotifyAndLogging" color="error" text>
Reset
</v-btn>
<v-spacer />
Expand Down Expand Up @@ -769,6 +791,7 @@ import Connection from "../utils/Connection";
import ConnectionProperties from "../models/ConnectionProperties";
import SearchEngine from "../utils/SearchEngine";
import TreeNode from "../models/TreeNode";
import MessageLogger from "../utils/MessageLogger";
import { ipcRenderer } from "electron";
export default {
Expand All @@ -794,6 +817,8 @@ export default {
searchModes: SearchEngine.modes,
searchQuery: SearchEngine.QUERY,
notifySettings: false,
notifySwitch: false,
fileLoggingSwitch: false,
notifyFilterType: undefined,
notifyEntry: undefined,
notifyEntries: [],
Expand All @@ -802,6 +827,7 @@ export default {
OR: "or",
AND: "and",
},
messageLogger: undefined,
}),
computed: {
Expand Down Expand Up @@ -839,6 +865,15 @@ export default {
},
},
watch: {
fileLoggingSwitch(newValue, oldValue) {
if (oldValue === newValue) return;
if (newValue) this.messageLogger?.startLogging();
else this.messageLogger?.stopLogging();
},
},
beforeMount() {
this.connectionProperties.init(
this.$store.getters.getConnectionByIndex(this.$route.params.index)
Expand All @@ -850,6 +885,8 @@ export default {
() => this.treeData.length
);
this.messageLogger = new MessageLogger(this.connectionProperties.name);
ipcRenderer.send("enterViewerPage");
ipcRenderer.on("searchPressed", this.toggleSearchField);
},
Expand All @@ -863,6 +900,7 @@ export default {
},
beforeDestroy() {
this.messageLogger?.stopLogging();
ipcRenderer.removeListener("searchPressed", this.toggleSearchField);
},
Expand Down Expand Up @@ -965,7 +1003,7 @@ export default {
}
if (!toDelete) {
this.notify(node);
this.notifyAndWrite(node);
}
return toDelete;
Expand Down Expand Up @@ -999,10 +1037,15 @@ export default {
return "mdi-equal";
}
},
resetNotifyAndLogging() {
this.notifySwitch = false;
this.fileLoggingSwitch = false;
this.notifyEntries = [];
},
deleteNotifyEntry(entry) {
this.notifyEntries = this.notifyEntries.filter((item) => item !== entry);
},
notify(node) {
notifyAndWrite(node) {
let foundNodes = this.notifyEntries.flatMap((entry) => {
return node.deepSearch(entry.notifyEntry, entry.filterType);
});
Expand All @@ -1022,15 +1065,24 @@ export default {
foundNodes.forEach((foundNode) => {
if (!foundNode?.value) return;
this.sendNotification(
foundNode.value.topic,
foundNode.value.payload,
() => {
// Open the app if closed/minimized and select the topic
this.getProperties(foundNode);
ipcRenderer.send("focusWindow");
}
);
if (this.fileLoggingSwitch) {
// Write entry to file
this.messageLogger.enqueue(foundNode.value);
}
if (this.notifySwitch) {
// Send a system notification
this.sendNotification(
foundNode.value.topic,
foundNode.value.payload,
() => {
// Open the app if closed/minimized and select the topic
this.getProperties(foundNode);
ipcRenderer.send("focusWindow");
}
);
}
});
},
publishItem() {
Expand Down

0 comments on commit 8bf679b

Please sign in to comment.