Logs inside the WebUI (#634)

This commit is contained in:
Elia Zammuto 2023-01-01 02:12:36 +01:00 committed by GitHub
parent 248b1bfa19
commit a5213c6225
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 125 additions and 13 deletions

View File

@ -93,6 +93,20 @@ min_log_level
min_log_level = info
log_path
^^^^^^^^
**Description**
The path where the sunshine log is stored.
**Default**
``sunshine.log``
**Example**
.. code-block:: text
log_path = sunshine.log
Controls
--------

View File

@ -336,6 +336,7 @@ sunshine_t sunshine {
platf::appdata().string() + "/sunshine.conf", // config file
{}, // cmd args
47989,
platf::appdata().string() + "/sunshine.log", // log file
};
bool endline(char ch) {
@ -788,7 +789,7 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
path_f(vars, "pkey", nvhttp.pkey);
path_f(vars, "cert", nvhttp.cert);
string_f(vars, "sunshine_name", nvhttp.sunshine_name);
path_f(vars, "log_path", config::sunshine.log_file);
path_f(vars, "file_state", nvhttp.file_state);
// Must be run after "file_state"

View File

@ -122,6 +122,7 @@ struct sunshine_t {
} cmd;
std::uint16_t port;
std::string log_file;
};
extern video_t video;

View File

@ -274,6 +274,17 @@ void getApps(resp_https_t response, req_https_t request) {
response->write(content);
}
void getLogs(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::string content = read_file(config::sunshine.log_file.c_str());
SimpleWeb::CaseInsensitiveMultimap headers;
headers.emplace("Content-Type", "text/plain");
response->write(SimpleWeb::StatusCode::success_ok, content, headers);
}
void saveApp(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
@ -648,6 +659,7 @@ void start() {
server.resource["^/troubleshooting$"]["GET"] = getTroubleshootingPage;
server.resource["^/api/pin$"]["POST"] = savePin;
server.resource["^/api/apps$"]["GET"] = getApps;
server.resource["^/api/logs$"]["GET"] = getLogs;
server.resource["^/api/apps$"]["POST"] = saveApp;
server.resource["^/api/config$"]["GET"] = getConfig;
server.resource["^/api/config$"]["POST"] = saveConfig;

View File

@ -101,6 +101,7 @@ int entry(const char *name, int argc, char *argv[]) {
}
} // namespace version
void log_flush() {
sink->flush();
}
@ -219,6 +220,7 @@ int main(int argc, char *argv[]) {
boost::shared_ptr<std::ostream> stream { &std::cout, NoDelete {} };
sink->locked_backend()->add_stream(stream);
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(config::sunshine.log_file));
sink->set_filter(severity >= config::sunshine.min_log_level);
sink->set_formatter([message = "Message"s, severity = "Severity"s](const bl::record_view &view, bl::formatting_ostream &os) {

View File

@ -3,6 +3,7 @@
#ifndef SUNSHINE_MAIN_H
#define SUNSHINE_MAIN_H
#include <filesystem>
#include <string_view>
#include "thread_pool.h"
@ -50,6 +51,4 @@ MAIL(idr);
MAIL(rumble);
#undef MAIL
} // namespace mail
#endif // SUNSHINE_MAIN_H

View File

@ -49,6 +49,20 @@
The minimum log level printed to standard out
</div>
</div>
<!--Log Path-->
<div class="mb-3">
<label for="log_path" class="form-label">Logfile Path</label>
<input
type="text"
class="form-control"
id="log_path"
placeholder="sunshine.log"
v-model="config.log_path"
/>
<div class="form-text">
The file where the current logs of Sunshine are stored.
</div>
</div>
<!--Origin Web UI Allowed-->
<div class="mb-3">
<label for="origin_web_ui_allowed" class="form-label"

View File

@ -16,11 +16,7 @@
Error while closing Appplication
</div>
<div>
<button
class="btn btn-warning"
:disabled="closeAppPressed"
@click="closeApp"
>
<button class="btn btn-warning" :disabled="closeAppPressed" @click="closeApp">
Force Close
</button>
</div>
@ -39,16 +35,28 @@
Error while unpairing
</div>
<div>
<button
class="btn btn-danger"
:disabled="unpairAllPressed"
@click="unpairAll"
>
<button class="btn btn-danger" :disabled="unpairAllPressed" @click="unpairAll">
Unpair All
</button>
</div>
</div>
</div>
<!--Logs-->
<div class="card p-2 my-4">
<div class="card-body">
<h2>Logs</h2>
<br />
<div class="d-flex justify-content-between align-items-baseline py-2">
<p>See the logs uploaded by Sunshine</p>
<input type="text" class="form-control" v-model="logFilter" placeholder="Find..." style="width: 300px">
</div>
<div>
<div class="troubleshooting-logs">
<button class="copy-icon"><i class="fas fa-copy " @click="copyLogs"></i></button>{{actualLogs}}
</div>
</div>
</div>
</div>
</div>
<script>
@ -60,9 +68,36 @@
closeAppStatus: null,
unpairAllPressed: false,
unpairAllStatus: null,
logs: 'Loading...',
logFilter: null,
logInterval: null,
};
},
computed: {
actualLogs(){
if(!this.logFilter)return this.logs;
let lines = this.logs.split("\n");
lines = lines.filter(x => x.indexOf(this.logFilter) != -1);
return lines.join("\n");
}
},
created() {
this.logInterval = setInterval(() => {
this.refreshLogs();
}, 5000);
this.refreshLogs();
},
beforeDestroy(){
clearInterval(this.logInterval);
},
methods: {
refreshLogs() {
fetch("/api/logs",)
.then((r) => r.text())
.then((r) => {
this.logs = r;
});
},
closeApp() {
this.closeAppPressed = true;
fetch("/api/apps/close", { method: "POST" })
@ -87,6 +122,40 @@
}, 5000);
});
},
copyLogs(){
navigator.clipboard.writeText(this.actualLogs);
}
},
});
</script>
<style>
.troubleshooting-logs {
white-space: pre;
font-family: monospace;
overflow: auto;
max-height: 500px;
min-height: 500px;
font-size: 16px;
position: relative;
}
.copy-icon {
position: absolute;
top: 8px;
right: 8px;
padding: 8px;
cursor: pointer;
color: rgba(0,0,0,1);
appearance: none;
border: none;
background: none;
}
.copy-icon:hover {
color: rgba(0,0,0,0.75);
}
.copy-icon:active {
color: rgba(0,0,0,1);
}
</style>