From c58b383b6df4c718a6ea24f3de6699f9b77d5582 Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Sun, 24 Dec 2017 22:26:05 +0000 Subject: [PATCH] gui: Add debug tab to settings (ref #2644) Just because there are a ton of people struggling to set env vars. Perhaps this should live in advanced settings, and perhaps we should have a button to view the log. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4604 LGTM: calmh, imsodin --- gui/default/index.html | 2 + .../syncthing/core/logViewerModalView.html | 36 ++++++++++ .../syncthing/core/syncthingController.js | 72 ++++++++++++++++++- .../syncthing/settings/settingsModalView.html | 4 +- lib/fs/debug.go | 4 +- lib/logger/logger.go | 26 +++---- lib/logger/logger_test.go | 20 ++++++ 7 files changed, 148 insertions(+), 16 deletions(-) create mode 100644 gui/default/syncthing/core/logViewerModalView.html diff --git a/gui/default/index.html b/gui/default/index.html index 38969c13a..193f126f8 100644 --- a/gui/default/index.html +++ b/gui/default/index.html @@ -78,6 +78,7 @@
  •  About
  •  Advanced
  • +
  •  Logs
  • @@ -734,6 +735,7 @@ + diff --git a/gui/default/syncthing/core/logViewerModalView.html b/gui/default/syncthing/core/logViewerModalView.html new file mode 100644 index 000000000..962299c3f --- /dev/null +++ b/gui/default/syncthing/core/logViewerModalView.html @@ -0,0 +1,36 @@ + + + + diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js index ad333b1fb..78c7cadab 100755 --- a/gui/default/syncthing/core/syncthingController.js +++ b/gui/default/syncthing/core/syncthingController.js @@ -2,7 +2,7 @@ angular.module('syncthing.core') .config(function($locationProvider) { $locationProvider.html5Mode({enabled: true, requireBase: false}).hashPrefix('!'); }) - .controller('SyncthingController', function ($scope, $http, $location, LocaleService, Events, $filter, $q) { + .controller('SyncthingController', function ($scope, $http, $location, LocaleService, Events, $filter, $q, $interval) { 'use strict'; // private/helper definitions @@ -1090,6 +1090,76 @@ angular.module('syncthing.core') $('#discovery-failures').modal(); }; + $scope.logging = { + facilities: {}, + refreshFacilities: function() { + $http.get(urlbase + '/system/debug').success(function (data) { + var facilities = {}; + data.enabled = data.enabled || []; + $.each(data.facilities, function(key, value) { + facilities[key] = { + description: value, + enabled: data.enabled.indexOf(key) > -1 + } + }) + $scope.logging.facilities = facilities; + }).error($scope.emitHTTPError); + }, + show: function() { + $scope.logging.refreshFacilities(); + $scope.logging.timer = $interval($scope.logging.fetch, 0, 1); + $('#logViewer').modal().on('hidden.bs.modal', function () { + $interval.cancel($scope.logging.timer); + $scope.logging.timer = null; + $scope.logging.entries = []; + }); + }, + onFacilityChange: function(facility) { + var enabled = $scope.logging.facilities[facility].enabled; + // Disable checkboxes while we're in flight. + $.each($scope.logging.facilities, function(key) { + $scope.logging.facilities[key].enabled = null; + }) + $http.post(urlbase + '/system/debug?' + (enabled ? 'enable=':'disable=') + facility) + .success($scope.logging.refreshFacilities) + .error($scope.emitHTTPError); + }, + timer: null, + entries: [], + paused: false, + content: function() { + var content = ""; + $.each($scope.logging.entries, function (idx, entry) { + content += entry.when.split('.')[0].replace('T', ' ') + ' ' + entry.message + "\n"; + }); + return content; + }, + fetch: function() { + var textArea = $('#logViewerText'); + if (textArea.is(":focus")) { + if (!$scope.logging.timer) return; + $scope.logging.timer = $interval($scope.logging.fetch, 500, 1); + return; + } + + var last = null; + if ($scope.logging.entries.length > 0) { + last = $scope.logging.entries[$scope.logging.entries.length-1].when; + } + + $http.get(urlbase + '/system/log' + (last ? '?since=' + encodeURIComponent(last) : '')).success(function (data) { + if (!$scope.logging.timer) return; + $scope.logging.timer = $interval($scope.logging.fetch, 2000, 1); + if (!textArea.is(":focus")) { + if (data.messages) { + $scope.logging.entries.push.apply($scope.logging.entries, data.messages); + } + textArea.scrollTop(textArea[0].scrollHeight); + } + }); + } + } + $scope.editSettings = function () { // Make a working copy $scope.tmpOptions = angular.copy($scope.config.options); diff --git a/gui/default/syncthing/settings/settingsModalView.html b/gui/default/syncthing/settings/settingsModalView.html index 4a6575f55..57261dc09 100644 --- a/gui/default/syncthing/settings/settingsModalView.html +++ b/gui/default/syncthing/settings/settingsModalView.html @@ -150,6 +150,7 @@ +
     Help @@ -221,11 +222,12 @@
    -
    +
    + diff --git a/lib/fs/debug.go b/lib/fs/debug.go index 52c3687e9..075954bbc 100644 --- a/lib/fs/debug.go +++ b/lib/fs/debug.go @@ -14,9 +14,9 @@ import ( ) var ( - l = logger.DefaultLogger.NewFacility("filesystem", "Filesystem access") + l = logger.DefaultLogger.NewFacility("fs", "Filesystem access") ) func init() { - l.SetDebug("filesystem", strings.Contains(os.Getenv("STTRACE"), "filesystem") || os.Getenv("STTRACE") == "all") + l.SetDebug("fs", strings.Contains(os.Getenv("STTRACE"), "fs") || os.Getenv("STTRACE") == "all") } diff --git a/lib/logger/logger.go b/lib/logger/logger.go index d09b86621..f5207ea05 100644 --- a/lib/logger/logger.go +++ b/lib/logger/logger.go @@ -308,6 +308,7 @@ type recorder struct { type Line struct { When time.Time `json:"when"` Message string `json:"message"` + Level LogLevel `json:"level"` } func NewRecorder(l Logger, level LogLevel, size, initial int) Recorder { @@ -324,18 +325,18 @@ func (r *recorder) Since(t time.Time) []Line { defer r.mut.Unlock() res := r.lines - for i := 0; i < len(res) && res[i].When.Before(t); i++ { - // nothing, just incrementing i - } - if len(res) == 0 { - return nil - } - // We must copy the result as r.lines can be mutated as soon as the lock - // is released. - cp := make([]Line, len(res)) - copy(cp, res) - return cp + for i := 0; i < len(res); i++ { + if res[i].When.After(t) { + // We must copy the result as r.lines can be mutated as soon as the lock + // is released. + res = res[i:] + cp := make([]Line, len(res)) + copy(cp, res) + return cp + } + } + return nil } func (r *recorder) Clear() { @@ -348,6 +349,7 @@ func (r *recorder) append(l LogLevel, msg string) { line := Line{ When: time.Now(), Message: msg, + Level: l, } r.mut.Lock() @@ -367,6 +369,6 @@ func (r *recorder) append(l LogLevel, msg string) { r.lines = append(r.lines, line) if len(r.lines) == r.initial { - r.lines = append(r.lines, Line{time.Now(), "..."}) + r.lines = append(r.lines, Line{time.Now(), "...", l}) } } diff --git a/lib/logger/logger_test.go b/lib/logger/logger_test.go index d807213be..4071c0360 100644 --- a/lib/logger/logger_test.go +++ b/lib/logger/logger_test.go @@ -130,4 +130,24 @@ func TestRecorder(t *testing.T) { } } + // Check that since works + now := time.Now() + + time.Sleep(time.Millisecond) + + lines = r1.Since(now) + if len(lines) != 0 { + t.Error("unexpected lines") + } + + l.Infoln("hah") + + lines = r1.Since(now) + if len(lines) != 1 { + t.Fatalf("unexpected line count: %d", len(lines)) + } + if lines[0].Message != "hah" { + t.Errorf("incorrect line: %s", lines[0]) + } + }