Merge PR #218: Improve live logs in debug interface and rework logging rules

This commit is contained in:
Jenkins nymea 2019-10-18 14:57:23 +02:00
commit d589b66e3c
12 changed files with 873 additions and 97 deletions

View File

@ -1,2 +0,0 @@
[Rules]
*.debug=false

View File

@ -24,5 +24,7 @@
<file>favicons/apple-touch-icon-76x76.png</file>
<file>favicons/apple-touch-icon-114x114.png</file>
<file>favicons/apple-touch-icon-120x120.png</file>
<file>edit-copy.svg</file>
<file>delete.svg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
viewBox="0 0 96 96.000001"
sodipodi:docname="delete.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="10.976561"
inkscape:cx="-8.0899692"
inkscape:cy="53.1906"
inkscape:document-units="px"
inkscape:current-layer="g4780"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:window-width="2880"
inkscape:window-height="1661"
inkscape:window-x="0"
inkscape:window-y="78"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="92,-8.0000001"
id="guide4075"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,12"
id="guide4078"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="84,-9.0000001"
id="guide4080"
inkscape:locked="false" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170"
inkscape:locked="false" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845"
style="display:inline">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="next01.png"
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
id="g4778"
inkscape:label="Layer 1">
<g
transform="matrix(-1,0,0,1,575.99999,611)"
id="g4780"
style="display:inline">
<path
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Ubuntu;-inkscape-font-specification:Ubuntu;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#f9f9f9;fill-opacity:1;stroke:none;stroke-width:0.99980223"
d="M 48,4 C 44.00014,4 39.99211,4.7461162 36,6.1503906 l 0,6.7109374 C 29.327124,13.810409 22.655558,15.52986 16,17.957031 L 16,24 l -4,0 0,4 2,0 1.998047,0 L 20,28 31.998047,28 36,28 45.998047,28 50,28 59.998047,28 64,28 75.998047,28 80,28 l 4,0 0,-4 -4,0 0,-6.042969 C 73.332931,15.582208 66.666543,13.849013 60,12.882812 L 60,6.1503906 C 55.99972,4.7760144 51.99985,4 48,4 Z m 0,4 c 2.66654,0 5.33317,0.5173461 8,1.4335938 l 0,2.9628902 C 53.333392,12.134797 50.666623,12 48,12 c -2.665975,0 -5.332361,0.129657 -8,0.384766 L 40,9.4335938 C 42.66138,8.497414 45.33345,8 48,8 Z m -32.001953,18 0,54 c 0,2.633248 0.244566,4.726438 0.910156,6.488281 0.66558,1.761843 1.859171,3.170898 3.369141,4.009766 C 23.297284,92.175793 27,92 32,92 l 16,0 15.998047,0 c 5,0 8.702716,0.175793 11.722656,-1.501953 1.50996,-0.838868 2.701608,-2.247923 3.367188,-4.009766 C 79.753481,84.726438 80,82.633248 80,80 l 0,-46 -4.001953,0 0,46 c 0,2.366744 -0.255434,4.023564 -0.652344,5.074219 -0.39692,1.050654 -0.828329,1.516607 -1.568359,1.927734 C 72.297284,87.824208 68.998047,88 63.998047,88 L 48,88 47.998047,88 32,88 c -5,0 -8.299237,-0.175792 -9.779297,-0.998047 -0.74004,-0.411127 -1.171449,-0.87708 -1.568359,-1.927734 C 20.255424,84.023564 20,82.366744 20,80 l 0,-54 z m 16,8 0,36 L 36,70 36,34 Z m 14,0 0,36 L 50,70 50,34 Z m 14,0 0,36 L 64,70 64,34 Z m 13.560547,41.115234 0.111328,0.144532 c -0.0213,-0.02629 -0.03163,-0.05945 -0.05273,-0.08594 -0.017,-0.02229 -0.04169,-0.0361 -0.05859,-0.05859 z m -51.16211,0.01367 c -0.0169,0.02239 -0.04159,0.03816 -0.05859,0.06055 -0.021,0.02639 -0.03143,0.05965 -0.05273,0.08594 z"
transform="matrix(0,-1,-1.0003957,0,438.00245,441.36222)"
id="path4157"
inkscape:connector-curvature="0"
sodipodi:nodetypes="scccccccccccccccccccccccssccsccscsssscssssccssssccssssccccccccccccccccccccccccc" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
viewBox="0 0 96 96.000001"
sodipodi:docname="edit-copy.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.0249991"
inkscape:cx="-44.875455"
inkscape:cy="51.594295"
inkscape:document-units="px"
inkscape:current-layer="g4780"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:window-width="2880"
inkscape:window-height="1661"
inkscape:window-x="0"
inkscape:window-y="78"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="92,-8.0000001"
id="guide4075"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,12"
id="guide4078"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="84,-9.0000001"
id="guide4080"
inkscape:locked="false" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170"
inkscape:locked="false" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845"
style="display:inline">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="next01.png"
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
id="g4778"
inkscape:label="Layer 1">
<g
transform="matrix(-1,0,0,1,575.99999,611)"
id="g4780"
style="display:inline">
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f9f9f9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate"
d="m 409.9914,405.39134 -4.00158,0 0,-36.07722 4.00158,0 z"
id="path4212"
inkscape:connector-curvature="0" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f9f9f9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate"
d="m 397.98665,405.39134 -4.00158,0 0,-36.07722 4.00158,0 z"
id="path4210"
inkscape:connector-curvature="0" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#f9f9f9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.00079107;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 345.9668,357.36133 0,59.98242 76.02929,0 0,-2 0,-57.98242 -76.02929,0 z m 4,4.00195 68.02929,0 0,51.97852 -68.02929,0 0,-51.97852 z"
id="rect4154"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f9f9f9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 434.0756,429.44282 -6.29888,0 -69.80592,0 0,-4.0806 72.02849,0 0,-56.0481 4.07631,0 0,54.11584 z"
id="rect4156"
sodipodi:nodetypes="ccccccccc" />
<path
inkscape:connector-curvature="0"
id="path4214"
d="m 385.98189,405.39134 -4.00158,0 0,-24.02912 4.00158,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f9f9f9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f9f9f9;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4158"
width="4"
height="16.00633"
x="-373.36221"
y="-434.00085"
transform="matrix(0,-1,-1,0,0,0)" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -67,13 +67,13 @@ function connectWebsocket() {
webSocket = new WebSocket(urlString);
webSocket.onopen = function(openEvent) {
console.log("WebSocket connected: " + JSON.stringify(openEvent, null, 4));
console.log("WebSocket connected");
webSocketConnected = true;
document.getElementById("toggleLogsButton").innerHTML = "Stop logs";
};
webSocket.onclose = function(closeEvent) {
console.log("WebSocket disconnected: " + JSON.stringify(closeEvent, null, 4));
console.log("WebSocket disconnected");
webSocketConnected = false;
document.getElementById("toggleLogsButton").innerHTML = "Start logs";
};
@ -84,7 +84,6 @@ function connectWebsocket() {
webSocket.onmessage = function (messageEvent) {
var message = messageEvent.data;
console.log("WebSocket data received: " + message);
document.getElementById("logsTextArea").value += message;
document.getElementById("logsTextArea").scrollTop = document.getElementById("logsTextArea").scrollHeight;
};
@ -92,7 +91,6 @@ function connectWebsocket() {
} catch (exception) {
console.error(exception);
}
}
@ -104,6 +102,86 @@ function disconnectWebsocket() {
}
function clearLogsContent() {
console.log("Clear live log content");
var logTextArea = document.getElementById("logsTextArea")
logTextArea.value = "";
}
function copyLogsContent() {
console.log("Copy live log content");
var logTextArea = document.getElementById("logsTextArea")
logTextArea.select();
logTextArea.setSelectionRange(0, 99999); /*For mobile devices*/
document.execCommand("copy");
console.log("Copied text:");
console.log(logTextArea.value);
/* Clear selection */
document.select();
}
function loadLoggingCategorySettings() {
// Request report file generation
var request = new XMLHttpRequest();
request.open("GET", "/debug/logging-categories", true);
request.send(null);
request.onreadystatechange = function() {
if (request.readyState == 4) {
console.log("Load logging category settings finished", request.status);
/* Check if the generation went fine */
if (request.status != 200) {
console.warn("Could not load logging category settings", request.status);
return;
}
var responseMap = JSON.parse(request.responseText);
for (var loggingCategory in responseMap['loggingCategories']) {
var loggingCategoryElement = document.getElementById("debug-category-" + loggingCategory)
loggingCategoryElement.checked = responseMap['loggingCategories'][loggingCategory]
}
for (var loggingCategory in responseMap['loggingCategoriesPlugins']) {
var loggingCategoryElement = document.getElementById("debug-category-" + loggingCategory)
loggingCategoryElement.checked = responseMap['loggingCategoriesPlugins'][loggingCategory]
}
}
}
}
function toggleLoggingCategory(categoryName) {
var switchElement = document.getElementById("debug-category-" + categoryName)
console.log("Toggle logging category", categoryName, switchElement.checked)
var fileRequestUrl = "/debug/logging-categories?" + categoryName + "=" + (switchElement.checked ? "true" : "false");
// Request report file generation
var request = new XMLHttpRequest();
request.open("GET", fileRequestUrl, true);
request.send(null);
request.onreadystatechange = function() {
if (request.readyState == 4) {
console.log("Set logging category settings finished", request.status);
/* Check if the generation went fine */
if (request.status != 200) {
console.warn("Could not set logging category settings", request.status);
return;
}
}
}
}
/* ========================================================================*/
/* File download / show functions
/* ========================================================================*/
@ -151,7 +229,7 @@ function pollReportResult() {
/* Check if the generation went fine */
if (reportGenerateRequest.status != 200) {
console.log("Report generation finished with error.");
console.warn("Report generation finished with error.");
clearTimeout(generateReportTimer);
textArea.value = "Something went wrong :(" + reportGenerateRequest.status;
button.disabled = false;
@ -342,5 +420,6 @@ function tracePathTimerTimeout() {
window.onload = function() {
console.log("Window loading finished.");
document.getElementById("informationTabButton").click();
loadLoggingCategorySettings();
};

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* Copyright (C) 2018-2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
@ -39,6 +39,14 @@ h1, h2, h3, h4, h5, h6 {
text-align: center;
}
table {
display: table;
border-collapse: colapse;
border-color: #efefef;
min-height: 100px;
width: 100%;
}
th, td {
padding: 3px;
padding-left: 10px;
@ -52,27 +60,10 @@ hr {
color: #efefef;
}
table {
display: table;
border-collapse: colapse;
border-color: #efefef;
min-height: 100px;
width: 100%;
}
button {
width: 100%;
}
textarea {
background-color: #3a4055;
width: 100%;
padding: 15px;
min-height: 100px;
text-align: left;
border-radius: 10px;
}
.tab {
overflow: hidden;
background-color: #efefef;
@ -106,8 +97,18 @@ textarea {
}
@keyframes fadeEffect {
from {opacity: 0;}
to {opacity: 1;}
from { opacity: 0; }
to { opacity: 1; }
}
textarea {
background-color: #3a4055;
width: 100%;
padding: 15px;
min-height: 100px;
text-align: left;
border-radius: 10px;
outline: none;
}
.console-textarea {
@ -212,8 +213,12 @@ textarea {
border-radius: 10px;
opacity: 0.8;
transition: 0.3s;
cursor: pointer;
outline: none;
}
button::-moz-focus-inner { border: 0; }
.button:hover {
opacity: 1
}
@ -224,8 +229,136 @@ textarea {
color: #676767;
}
.log-buttons {
display: -webkit-flex; /* Safari */
display: flex;
flex-flow: row nowrap;
}
.log-buttons button {
display: -webkit-flex; /* Safari */
display: flex;
justify-content: center;
align-items: center;
margin: 10px;
width: auto;
}
.tool-image {
width: 25px;
height: 25px;
}
#toggleLogsButton {
flex-grow: 1;
}
#copyLogsButton {
width: 20px;
}
#clearLogsButton {
width: 20px;
}
.categories-area {
display: -webkit-flex;
display: flex;
flex-flow: row wrap;
}
.debug-category {
display: -webkit-flex;
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
margin-top: 10px;
width: 33%;
}
.debug-category p {
margin: 10px;
text-align: right;
flex-grow: 1;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #57baae;
}
input:focus + .slider {
box-shadow: 0 0 1px #57baae;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
.container {
min-height:100%;
min-height: 100%;
position:relative;
}

View File

@ -5,8 +5,7 @@ After=network.target
Wants=network-online.target
[Service]
Environment=QT_LOGGING_CONF=/etc/nymea/logging.conf
ExecStart=/usr/bin/nymead -n --print-all
ExecStart=/usr/bin/nymead -n
StandardOutput=journal
StandardError=journal
Restart=on-failure

View File

@ -5,4 +5,3 @@ usr/lib/@DEB_HOST_MULTIARCH@/libnymea-core.so.1.0.0
data/systemd/nymead.service /lib/systemd/system/
data/logrotate/nymead /etc/logrotate.d/
data/dbus-1/io.guh.nymead.conf /etc/dbus-1/system.d/
data/config/logging.conf /etc/nymea/

View File

@ -1,6 +1,6 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* Copyright (C) 2018-2019 Simon Stürz <simon.stuerz@nymea.io> *
* *
* This file is part of nymea. *
* *
@ -379,6 +379,60 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
return m_tracePathReply;
}
if (requestPath.startsWith("/debug/logging-categories")) {
if (requestQuery.isEmpty()) {
// Return the list of debug category settings
NymeaSettings settings(NymeaSettings::SettingsRoleGlobal);
settings.beginGroup("LoggingRules");
qCDebug(dcDebugServer()) << "Request logging categories list";
QVariantMap dataMap;
QVariantMap loggingCategories;
foreach (const QString &loggingCategory, NymeaCore::loggingFilters()) {
loggingCategories.insert(loggingCategory, settings.value(QString("%1.debug").arg(loggingCategory), false).toBool());
}
dataMap.insert("loggingCategories", loggingCategories);
QVariantMap loggingCategoriesPlugins;
foreach (const QString &loggingCategory, NymeaCore::loggingFiltersPlugins()) {
loggingCategoriesPlugins.insert(loggingCategory, settings.value(QString("%1.debug").arg(loggingCategory), false).toBool());
}
dataMap.insert("loggingCategoriesPlugins", loggingCategoriesPlugins);
settings.endGroup();
HttpReply *reply = RestResource::createSuccessReply();
reply->setPayload(QJsonDocument::fromVariant(dataMap).toJson(QJsonDocument::Indented));
return reply;
} else {
NymeaSettings settings(NymeaSettings::SettingsRoleGlobal);
settings.beginGroup("LoggingRules");
for (int i = 0; i < requestQuery.queryItems().count(); i++) {
QString category = requestQuery.queryItems().at(i).first;
if (!NymeaCore::loggingFilters().contains(category) && !NymeaCore::loggingFiltersPlugins().contains(category)) {
qCWarning(dcDebugServer()) << "Invalid logging category in request query" << requestQuery.toString() << category;
continue;
}
bool enabled = QVariant(requestQuery.queryItems().at(i).second).toBool();
qCDebug(dcDebugServer()) << "Logging category" << category << (enabled ? "enabled" : "disabled");
settings.setValue(QString("%1.debug").arg(category), (enabled ? "true" : "false"));
}
// Update logging filter rules according to the nw settings
QStringList loggingRules;
loggingRules << "*.debug=false";
// Load the rules from nymead.conf file and append them to the rules
foreach (const QString &category, settings.childKeys()) {
loggingRules << QString("%1=%2").arg(category).arg(settings.value(category, "false").toString());
}
settings.endGroup();
QLoggingCategory::setFilterRules(loggingRules.join('\n'));
return RestResource::createSuccessReply();
}
}
if (requestPath.startsWith("/debug/report")) {
// The client can poll this url in order to get information about the current report generating process.
@ -562,6 +616,7 @@ void DebugServerHandler::onWebsocketClientConnected()
if (s_websocketClients.isEmpty()) {
qCDebug(dcDebugServer()) << "Install debug message handler for live logs.";
//QLoggingCategory::setFilterRules("*.debug=true");
s_oldLogMessageHandler = qInstallMessageHandler(&logMessageHandler);
}
@ -846,7 +901,6 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeStartElement("div");
writer.writeAttribute("class", "body");
// ---------------------------------------------------------------------------
writer.writeStartElement("div");
writer.writeAttribute("class", "tabcontent");
@ -1670,6 +1724,9 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeTextElement("p", tr("This section allows you to see the live logs of the nymea server."));
writer.writeStartElement("div");
writer.writeAttribute("class", "log-buttons");
// Toggle log button
writer.writeStartElement("button");
writer.writeAttribute("class", "button");
@ -1680,6 +1737,31 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeCharacters(tr("Start logs"));
writer.writeEndElement(); // button
// Copy log content button
writer.writeStartElement("button");
writer.writeAttribute("class", "button");
writer.writeAttribute("type", "button");
writer.writeAttribute("id", "copyLogsButton");
writer.writeAttribute("onClick", "copyLogsContent()");
writer.writeEmptyElement("img");
writer.writeAttribute("class", "tool-image");
writer.writeAttribute("src", "/debug/edit-copy.svg");
writer.writeEndElement(); // button
// Copy log content button
writer.writeStartElement("button");
writer.writeAttribute("class", "button");
writer.writeAttribute("type", "button");
writer.writeAttribute("id", "clearLogsButton");
writer.writeAttribute("onClick", "clearLogsContent()");
writer.writeEmptyElement("img");
writer.writeAttribute("class", "tool-image");
writer.writeAttribute("src", "/debug/delete.svg");
writer.writeEndElement(); // button
writer.writeEndElement(); // div log-buttons
// Logs output
writer.writeStartElement("textarea");
writer.writeAttribute("class", "console-textarea");
@ -1689,6 +1771,69 @@ QByteArray DebugServerHandler::createDebugXmlDocument()
writer.writeCharacters("");
writer.writeEndElement(); // textarea
writer.writeEmptyElement("hr");
//: The network section of the debug interface
writer.writeTextElement("h2", tr("Logging filters"));
writer.writeEmptyElement("hr");
writer.writeStartElement("div");
writer.writeAttribute("class", "categories-area");
QStringList loggingCategories = NymeaCore::loggingFilters();
loggingCategories.sort();
foreach (const QString &loggingCategory, loggingCategories) {
writer.writeStartElement("div");
writer.writeAttribute("class", "debug-category");
writer.writeTextElement("p", loggingCategory);
writer.writeStartElement("label");
writer.writeAttribute("class", "switch");
writer.writeStartElement("input");
writer.writeAttribute("id", QString("debug-category-%1").arg(loggingCategory));
writer.writeAttribute("type", "checkbox");
writer.writeAttribute("onclick", QString("toggleLoggingCategory('%1')").arg(loggingCategory));
writer.writeEndElement(); // input
writer.writeStartElement("span");
writer.writeAttribute("class", "slider round");
writer.writeCharacters("");
writer.writeEndElement(); // span
writer.writeEndElement(); // label
writer.writeEndElement(); // div debug-category
}
writer.writeEndElement(); // div categories-area
writer.writeEmptyElement("hr");
//: The network section of the debug interface
writer.writeTextElement("h2", tr("Logging filters plugins"));
writer.writeEmptyElement("hr");
writer.writeStartElement("div");
writer.writeAttribute("class", "categories-area");
QStringList loggingCategoriesPlugins = NymeaCore::loggingFiltersPlugins();
loggingCategoriesPlugins.sort();
foreach (const QString &loggingCategory, loggingCategoriesPlugins) {
writer.writeStartElement("div");
writer.writeAttribute("class", "debug-category");
writer.writeTextElement("p", loggingCategory);
writer.writeStartElement("label");
writer.writeAttribute("class", "switch");
writer.writeStartElement("input");
writer.writeAttribute("id", QString("debug-category-%1").arg(loggingCategory));
writer.writeAttribute("type", "checkbox");
writer.writeAttribute("onclick", QString("toggleLoggingCategory('%1')").arg(loggingCategory));
writer.writeEndElement(); // input
writer.writeStartElement("span");
writer.writeAttribute("class", "slider round");
writer.writeCharacters("");
writer.writeEndElement(); // span
writer.writeEndElement(); // label
writer.writeEndElement(); // div debug-category
}
writer.writeEndElement(); // div categories-area
writer.writeEndElement(); // logs-section
writer.writeEndElement(); // div body

View File

@ -640,6 +640,65 @@ QStringList NymeaCore::getAvailableLanguages()
return availableLanguages;
}
/*! Returns the list of logging categories from the core and the libnymea. */
QStringList NymeaCore::loggingFilters()
{
QStringList loggingFilters = {
"Warnings",
"Application",
"System",
"Platform",
"PlatformUpdate",
"PlatformZeroConf",
"Device",
"DeviceManager",
"RuleEngine",
"RuleEngineDebug",
"Hardware",
"Bluetooth",
"LogEngine",
"ServerManager",
"TcpServer",
"TcpServerTraffic",
"WebServer",
"WebServerTraffic",
"DebugServer",
"WebSocketServer",
"WebSocketServerTraffic",
"JsonRpc",
"JsonRpcTraffic",
"Rest",
"OAuth2",
"TimeManager",
"Coap",
"Avahi",
"AvahiDebug",
"UPnP",
"Cloud",
"CloudTraffic",
"NetworkManager",
"UserManager",
"AWS",
"AWSTraffic",
"BluetoothServer",
"BluetoothServerTraffic",
"Mqtt",
"Translations"
};
return loggingFilters;
}
QStringList NymeaCore::loggingFiltersPlugins()
{
QStringList loggingFiltersPlugins;
foreach (const QJsonObject &pluginMetadata, DeviceManagerImplementation::pluginsMetadata()) {
QString pluginName = pluginMetadata.value("name").toString();
loggingFiltersPlugins << pluginName.left(1).toUpper() + pluginName.mid(1);
}
return loggingFiltersPlugins;
}
/*! Returns a pointer to the \l{BluetoothServer} instance owned by NymeaCore. */
BluetoothServer *NymeaCore::bluetoothServer() const
{

View File

@ -96,6 +96,8 @@ public:
Platform *platform() const;
static QStringList getAvailableLanguages();
static QStringList loggingFilters();
static QStringList loggingFiltersPlugins();
signals:
void initialized();

View File

@ -97,55 +97,9 @@ int main(int argc, char *argv[])
application.setApplicationName("nymead");
application.setApplicationVersion(NYMEA_VERSION_STRING);
// logging filers for core and libnymea
QStringList loggingFilters = {
"Warnings",
"Application",
"System",
"Platform",
"PlatformUpdate",
"PlatformZeroConf",
"Device",
"DeviceManager",
"RuleEngine",
"RuleEngineDebug",
"Hardware",
"Bluetooth",
"LogEngine",
"ServerManager",
"TcpServer",
"TcpServerTraffic",
"WebServer",
"WebServerTraffic",
"DebugServer",
"WebSocketServer",
"WebSocketServerTraffic",
"JsonRpc",
"JsonRpcTraffic",
"Rest",
"OAuth2",
"TimeManager",
"Coap",
"Avahi",
"AvahiDebug",
"UPnP",
"Cloud",
"CloudTraffic",
"NetworkManager",
"UserManager",
"AWS",
"AWSTraffic",
"BluetoothServer",
"BluetoothServerTraffic",
"Mqtt",
"Translations"
};
QStringList loggingFiltersPlugins;
foreach (const QJsonObject &pluginMetadata, DeviceManagerImplementation::pluginsMetadata()) {
QString pluginName = pluginMetadata.value("name").toString();
loggingFiltersPlugins << pluginName.left(1).toUpper() + pluginName.mid(1);
}
// Logging filers for core + libnymea and plugins
QStringList loggingFilters = NymeaCore::loggingFilters();
QStringList loggingFiltersPlugins = NymeaCore::loggingFiltersPlugins();
// Translator for the server application
QTranslator translator;
@ -217,29 +171,54 @@ int main(int argc, char *argv[])
}
}
QStringList filterRules;
/* The logging rules will be evaluated sequentially
* 1. All debug categories off
* 2. Enable all debug categories if requested from command line (-p)
* 3. The stored categories from the nymead.conf will be appended
* 4. Add the individual command line params will be added (-d)
* 5. QT_LOGGING_CONF
* 6. QT_LOGGING_RULES
*
* The final filter rules will be set.
*/
// 1. All debug categories off
QStringList loggingRules;
loggingRules << "*.debug=false";
// 2. Enable all debug categories making sense if requested from command line (-p)
if (parser.isSet(allOption)) {
filterRules << "*.debug=true";
filterRules << "*Traffic.debug=false";
filterRules << "*Debug.debug=false";
} else {
filterRules << "*.debug=false";
loggingRules << "*.debug=true";
loggingRules << "*Traffic.debug=false";
loggingRules << "*Debug.debug=false";
}
// And allow overriding individual values
// 3. The stored categories from the nymead.conf will be appended
NymeaSettings nymeaSettings(NymeaSettings::SettingsRoleGlobal);
nymeaSettings.beginGroup("LoggingRules");
foreach (const QString &category, nymeaSettings.childKeys()) {
loggingRules << QString("%1=%2").arg(category).arg(nymeaSettings.value(category, "false").toString());
}
nymeaSettings.endGroup();
// 4. Add the individual command line params will be added (-d)
foreach (QString debugArea, parser.values(debugOption)) {
bool enable = !debugArea.startsWith("No");
bool isWarning = debugArea.endsWith("Warnings");
debugArea.remove(QRegExp("^No"));
debugArea.remove(QRegExp("Warnings$"));
if (loggingFilters.contains(debugArea) || loggingFiltersPlugins.contains(debugArea)) {
filterRules.append(QString("%1.%2=%3").arg(debugArea).arg(isWarning ? "warning" : "debug").arg(enable ? "true": "false"));
loggingRules.append(QString("%1.%2=%3").arg(debugArea).arg(isWarning ? "warning" : "debug").arg(enable ? "true": "false"));
} else {
qCWarning(dcApplication) << QCoreApplication::translate("nymea", "No such debug category:") << debugArea;
}
}
QLoggingCategory::setFilterRules(filterRules.join('\n'));
// Finally set the rules for the logging
QLoggingCategory::setFilterRules(loggingRules.join('\n'));
// Parse DBus option
if (parser.isSet(dbusOption)) {
NymeaDBusService::setBusType(QDBusConnection::SessionBus);
}
@ -254,13 +233,13 @@ int main(int argc, char *argv[])
fprintf(stdout, "Could not create nymea settings directory %s", qPrintable(NymeaSettings::settingsPath()));
exit(EXIT_FAILURE);
}
qCInfo(dcApplication) << "=====================================";
qCInfo(dcApplication) << "nymead" << NYMEA_VERSION_STRING << "started with user ID" << userId;
qCInfo(dcApplication) << "=====================================";
qCInfo(dcApplication()) << "=====================================";
qCInfo(dcApplication()) << "nymead" << NYMEA_VERSION_STRING << "started with user ID" << userId;
qCInfo(dcApplication()) << "=====================================";
} else {
qCInfo(dcApplication) << "=====================================";
qCInfo(dcApplication) << "nymead" << NYMEA_VERSION_STRING << "started as root.";
qCInfo(dcApplication) << "=====================================";
qCInfo(dcApplication()) << "=====================================";
qCInfo(dcApplication()) << "nymead" << NYMEA_VERSION_STRING << "started as root.";
qCInfo(dcApplication()) << "=====================================";
}
// If running in a snappy environment, print out some details about it.