www/nginx: add columns select button to the log viewer (#3411)

This commit is contained in:
kulikov-a
2023-10-10 13:17:00 +02:00
committed by GitHub
parent 1eacc53767
commit c00805fd25
10 changed files with 143 additions and 116 deletions
+18 -1
View File
@@ -26,7 +26,11 @@
thead.sticky-top th {
position: sticky;
top: 80px;
top: 50px;
}
tr.filter > th {
padding: 10px 10px 10px 10px !important;
}
thead.sticky-top tr:first-child th {
@@ -38,4 +42,17 @@ tfoot.sticky-bottom th {
bottom: 50px;
font-weight: normal;
font-family: inherit;
padding: 10px 10px 10px 10px !important;
}
td.referer, td.request_line {
word-break: break-all;
}
.ngx-dropdown-item {
cursor: pointer;
display: block;
margin: 0;
padding: 3px 20px;
white-space: nowrap;
}
File diff suppressed because one or more lines are too long
@@ -1,30 +1,32 @@
import accessLogLine from '../templates/AccessLogLine.html';
import streamAccessLogLine from '../templates/StreamAccessLogLine.html';
import errorLogLine from '../templates/ErrorLogLine.html';
import LogLine from '../templates/LogLine.html';
import LogColumn from '../templates/LogColumn.html';
import logViewer from '../templates/logviewer.html';
import LogLinesCollection from "../models/LogLinesCollection";
import LogColumnModel from "../models/LogColumn";
import noDataAvailable from '../templates/noDataAvailable.html';
const LogViewLine = Backbone.View.extend({
tagName: 'tr',
initialize: function (data) {
this.type = data.type;
this.log_fields_visible = data.log_fields_visible;
},
render: function () {
this.$el.html(this.get_template()({model: this.model}));
this.$el.html(LogLine({log_fields_visible: this.log_fields_visible, model: this.model}));
},
});
const LogViewColumns = Backbone.View.extend({
tagName: 'tr',
className: 'filter',
initialize: function (data) {
this.log_fields_visible = data.log_fields_visible;
},
get_template: function() {
if (this.type === 'accesses') {
return accessLogLine;
} else if (this.type === 'stream_accesses') {
return streamAccessLogLine;
} else {
return errorLogLine;
}
},
render: function() {
this.log_fields_visible.forEach((field) => this.$el.append(LogColumn({field: field, model: this.model})));
}
});
const LogView = Backbone.View.extend({
@@ -39,6 +41,7 @@ const LogView = Backbone.View.extend({
"click #paging_forward": "page_forward",
"click #paging_last": "page_last",
"change #entrycount": "change_entry_count",
"click .ngx-dropdown-item": "toggle_column",
},
page_entry_count: 100,
filter_delay: -1,
@@ -52,10 +55,60 @@ const LogView = Backbone.View.extend({
},
render: function() {
// set logline fields
// all the fields are visible by default
// users choice is stored in browser localStorage
this.logFields = [];
let uid = this.collection.uuid;
let type = this.type;
switch (type) {
case 'accesses':
this.logFields.push({id: "time", header: "Time"},
{id: "remote_ip", header: "Remote IP"},
{id: "username", header: "Username"},
{id: "status", header: "Status"},
{id: "size", header: "Size"},
{id: "referer", header: "Referer"},
{id: "user_agent", header: "User Agent"},
{id: "forwarded_for", header: "Forwarded For"},
{id: "request_line", header: "Request Line"});
break;
case 'errors':
case 'stream_errors':
this.logFields.push({id: "date", header: "Date"},
{id: "time", header: "Time"},
{id: "severity", header: "Severity"},
{id: "number", header: "Number"},
{id: "message", header: "Message"});
break;
default:
// stream access
this.logFields.push({id: "time", header: "Time"},
{id: "remote_ip", header: "Remote IP"},
{id: "status", header: "Status"},
{id: "bytes_sent", header: "Bytes Sent"},
{id: "bytes_received", header: "Bytes Rcvd"},
{id: "session_time", header: "Session Time"});
}
this.logFields.forEach( (field) => {
field.visible = localStorage.getItem('visibleColumns[' + type + '][' + uid + '][' + field.id + ']') !== 'false';
});
this.logFieldsVisible = _.filter(this.logFields, ['visible', true]);
// fields are ready
// create/update column headers
let thead = this.$('thead');
if (thead.children().length < 1) {
const logColumns = new LogViewColumns({log_fields_visible: this.logFieldsVisible, model: this.collection.filter_model});
logColumns.render();
thead.html(logColumns.$el);
}
let tbody = this.$('tbody');
if (tbody.length < 1) {
if (this.collection.length !== 0) {
this.$el.html(logViewer({log_type: this.type, model: this.collection.filter_model}));
this.$el.html(logViewer({log_type: this.type, log_fields: this.logFields, log_fields_visible: this.logFieldsVisible, model: this.collection.filter_model}));
tbody = this.$('tbody');
} else {
this.$el.html(noDataAvailable);
@@ -99,7 +152,7 @@ const LogView = Backbone.View.extend({
},
render_one: function(parent_element, model) {
const logline = new LogViewLine({type: this.type, model: model});
const logline = new LogViewLine({type: this.type, log_fields_visible: this.logFieldsVisible, model: model});
logline.render();
parent_element.append(logline.$el);
},
@@ -161,6 +214,20 @@ const LogView = Backbone.View.extend({
this.page_entry_count = event.target.value;
this.current_page = 0;
this.update();
},
toggle_column: function (event) {
event.stopPropagation();
let uid = this.collection.uuid;
let type = this.type;
let field = $(event.currentTarget).find('input').prop('value');
// toggle visibility
localStorage.setItem('visibleColumns[' + type + '][' + uid + '][' + field + ']', !_.find(this.logFields, { 'id': field }).visible);
// unset filter for this column (if any) so as not to confuse the user
this.collection.filter_model.unset(field, {silent: true});
// reset table header and update data
this.$('thead').html('');
this.update();
}
});
@@ -0,0 +1,5 @@
const LogColumnModel = Backbone.Model.extend({
});
export default LogColumnModel;
@@ -1,9 +0,0 @@
<td class="time"><%= model.escape('time') %></td>
<td class="remote_ip"><%= model.escape('remote_ip') %></td>
<td class="username"><%= model.escape('username') %></td>
<td class="status"><%= model.escape('status') %></td>
<td class="size"><%= model.escape('size') %></td>
<td class="referer"><%= model.escape('http_referer') %></td>
<td class="user_agent"><%= model.escape('user_agent') %></td>
<td class="forwarded_for"><%= model.escape('forwarded_for') %></td>
<td class="request_line"><%= model.escape('request_line') %></td>
@@ -1,5 +0,0 @@
<td class="date"><%= model.escape('date') %></td>
<td class="time"><%= model.escape('time') %></td>
<td class="severity"><%= model.escape('severity') %></td>
<td class="number"><%= model.escape('number') %></td>
<td class="message"><%= model.escape('message') %></td>
@@ -0,0 +1,6 @@
<th class="active">
<div class="form-group" style="margin-bottom: unset;">
<label for="<%= _.escape(field.id) %>"><%= _.escape(field.header) %></label>
<input type="text" class="form-control" id="<%= _.escape(field.id) %>" value="<%= model.escape(field.id) %>" name="<%= _.escape(field.id) %>" />
</div>
</th>
@@ -0,0 +1,3 @@
<% log_fields_visible.forEach( (field) => { %>
<td class="<%= _.escape(field.id) %>"><%= model.escape(field.id) %></td>
<% }) %>
@@ -1,6 +0,0 @@
<td class="time"><%= model.escape('time') %></td>
<td class="remote_ip"><%= model.escape('remote_ip') %></td>
<td class="status"><%= model.escape('status') %></td>
<td class="bytes_sent"><%= model.escape('bytes_sent') %></td>
<td class="bytes_received"><%= model.escape('bytes_received') %></td>
<td class="session_time"><%= model.escape('session_time') %></td>
@@ -1,93 +1,42 @@
<% let count = 0; %><table class="table table-striped">
<table class="table table-striped">
<thead class="sticky-top">
<tr>
<% if (log_type === 'errors' || log_type === 'stream_errors') {
count = 5; %>
<th>Date</th>
<th>Time</th>
<th>Severity</th>
<th>Number</th>
<th>Message</th>
<% } else if (log_type === 'accesses') {
count = 9; %>
<th>Time</th>
<th>Remote IP</th>
<th>Username</th>
<th>Status</th>
<th>Size</th>
<th>Referer</th>
<th>User Agent</th>
<th>Forwarded For</th>
<th>Request Line</th>
<% } else {
count = 6; %>
<th>Time</th>
<th>Remote IP</th>
<th>Status</th>
<th>Bytes Sent</th>
<th>Bytes Received</th>
<th>Session Time</th>
<% } %>
</tr>
<tr class="filter">
<% if (log_type === 'errors' || log_type === 'stream_errors') { %>
<th class="active"><input type="text" value="<%= model.escape('date') %>" name="date" /></th>
<th class="active"><input type="text" value="<%= model.escape('time') %>" name="time" /></th>
<th class="active"><input type="text" value="<%= model.escape('severity') %>" name="severity" /></th>
<th class="active"><input type="text" value="<%= model.escape('number') %>" name="number" /></th>
<th class="active"><input type="text" value="<%= model.escape('message') %>" name="message" /></th>
<% } else if (log_type === 'accesses') { %>
<th class="active"><input type="text" value="<%= model.escape('time') %>" name="time" /></th>
<th class="active"><input type="text" value="<%= model.escape('remote_ip') %>" name="remote_ip" /></th>
<th class="active"><input type="text" value="<%= model.escape('username') %>" name="username" /></th>
<th class="active"><input type="text" value="<%= model.escape('status') %>" name="status" /></th>
<th class="active"><input type="text" value="<%= model.escape('size') %>" name="size" /></th>
<th class="active"><input type="text" value="<%= model.escape('http_referer') %>" name="http_referer" /></th>
<th class="active"><input type="text" value="<%= model.escape('user_agent') %>" name="user_agent" /></th>
<th class="active"><input type="text" value="<%= model.escape('forwarded_for') %>" name="forwarded_for" /></th>
<th class="active"><input type="text" value="<%= model.escape('request_line') %>" name="request_line" /></th>
<% } else { %>
<th class="active"><input type="text" value="<%= model.escape('time') %>" name="time" /></th>
<th class="active"><input type="text" value="<%= model.escape('remote_ip') %>" name="remote_ip" /></th>
<th class="active"><input type="text" value="<%= model.escape('status') %>" name="status" /></th>
<th class="active"><input type="text" value="<%= model.escape('bytes_sent') %>" name="bytes_sent" /></th>
<th class="active"><input type="text" value="<%= model.escape('bytes_received') %>" name="bytes_received" /></th>
<th class="active"><input type="text" value="<%= model.escape('session_time') %>" name="session_time" /></th>
<% } %>
</tr>
</thead>
<tbody>
</tbody>
<tfoot class="sticky-bottom">
<tr>
<th class="text-nowrap active">
<th class="text-nowrap active" colspan="100">
<div class="btn-group dropup"><button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="dropdown-text"><span class="icon glyphicon glyphicon-th-list"></span></span> <span class="caret"></span></button>
<ul class="dropdown-menu">
<% log_fields.forEach( (field) => { %>
<li><label class="ngx-dropdown-item"><input type="checkbox" class="option" value="<%= _.escape(field.id) %>" <%= field.visible === true ? 'checked="checked"' : '' %>/> <%= _.escape(field.header) %></label></li>
<% }) %>
</ul>
</div>
<button class="btn btn-primary" id="paging_first" title="First Page"><i class="fa fa-fast-backward" aria-hidden="true"></i></button>
<button class="btn btn-primary" id="paging_back" title="Previous Page"><i class="fa fa-step-backward" aria-hidden="true"></i></button>
<button class="btn btn-primary" id="refresh" title="Refresh"><i class="fa fa-refresh" aria-hidden="true"></i></button>
<button class="btn btn-primary" id="paging_forward" title="Next Page"><i class="fa fa-step-forward" aria-hidden="true"></i></button>
<button class="btn btn-primary" id="paging_last" title="Last Page"><i class="fa fa-fast-forward " aria-hidden="true"></i></button>
<span style="padding-left: 10px;">Page </span><span id="currentpage">1</span>/<span id="pagecount">0</span>
<div style="float: right; display: flex;">
<label for="entrycount"><span id="entrycountdisplay">100</span> lines per page</label><input type="range" list="tickmarks" min="5" max="1000" step="5" id="entrycount" value="100" style="width: 200px;" />
<datalist id="tickmarks">
<option value="5" label="5">
<option value="100" label="100">
<option value="200" label="200">
<option value="300">
<option value="400">
<option value="500" label="500">
<option value="600">
<option value="700">
<option value="800">
<option value="900">
<option value="1000" label="1000">
</datalist>
</div>
<span style="padding-left: 10px;">Found lines: </span><span id="resultcount">0</span>/<span id="totalcount">0</span>
</th>
<th class="active">
Page <span id="currentpage">1</span>/<span id="pagecount">0</span>
</th>
<th class="active" colspan="2">
<label for="entrycount"><span id="entrycountdisplay">100</span> lines per page</label>
<input type="range" list="tickmarks" min="5" max="1000" step="5" id="entrycount" value="100" />
<datalist id="tickmarks">
<option value="5" label="5">
<option value="100" label="100">
<option value="200" label="200">
<option value="300">
<option value="400">
<option value="500" label="500">
<option value="600">
<option value="700">
<option value="800">
<option value="900">
<option value="1000" label="1000">
</datalist>
</th>
<th class="active" colspan="<%= count - 4 %>">Found lines: <span id="resultcount">0</span>/<span id="totalcount">0</span></th>
</tr>
</tfoot>
</table>