فهرست منبع

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
Audrius Butkevicius 8 سال پیش
والد
کامیت
c58b383b6d

+ 2 - 0
gui/default/index.html

@@ -78,6 +78,7 @@
             <li><a href="" data-toggle="modal" data-target="#about"><span class="fa fa-fw fa-heart-o"></span>&nbsp;<span translate>About</span></a></li>
             <li class="divider" aria-hidden="true"></li>
             <li><a href="" ng-click="advanced()"><span class="fa fa-fw fa-cogs"></span>&nbsp;<span translate>Advanced</span></a></li>
+            <li><a href="" ng-click="logging.show()"><span class="fa fa-fw fa-book"></span>&nbsp;<span translate>Logs</span></a></li>
           </ul>
         </li>
       </ul>
@@ -734,6 +735,7 @@
   <ng-include src="'syncthing/core/discoveryFailuresModalView.html'"></ng-include>
   <ng-include src="'syncthing/folder/removeFolderDialogView.html'"></ng-include>
   <ng-include src="'syncthing/device/removeDeviceDialogView.html'"></ng-include>
+  <ng-include src="'syncthing/core/logViewerModalView.html'"></ng-include>
 
   <!-- vendor scripts -->
   <script type="text/javascript" src="vendor/jquery/jquery-2.2.2.js"></script>

+ 36 - 0
gui/default/syncthing/core/logViewerModalView.html

@@ -0,0 +1,36 @@
+<modal id="logViewer" icon="book" status="default" heading="{{'Logs' | translate}}" large="yes" closeable="yes">
+  <div class="modal-body">
+    <ul class="nav nav-tabs">
+      <li class="active"><a data-toggle="tab" href="#log-viewer-log" translate>Log</a></li>
+      <li><a data-toggle="tab" href="#log-viewer-facilities" translate>Debugging Facilities</a></li>
+    </ul>
+    <div class="tab-content">
+
+      <div id="log-viewer-log" class="tab-pane in active">
+        <label translate ng-if="logging.logEntries.length == 0">Loading...</label>
+        <textarea ng-focus="logging.paused = true" ng-blur="logging.paused = false" id="logViewerText" class="form-control" rows="20" ng-if="logging.logEntries.length != 0" readonly style="font-family: Consolas; font-size: 11px; overflow: auto;">{{ logging.content() }}</textarea>
+        <p translate class="help-block" ng-style="{'visibility': logging.paused ? 'visible' : 'hidden'}">Log tailing paused. Click here to continue.</p>
+      </div>
+
+      <div id="log-viewer-facilities" class="tab-pane">
+        <label>Available debug logging facilities:</label>
+        <table class="table table-condensed table-striped">
+          <tbody>
+            <tr ng-repeat="(name, data) in logging.facilities">
+              <td>
+                <input type="checkbox" ng-model="data.enabled" ng-change="logging.onFacilityChange(name)" ng-disabled="data.enabled == null"> <span>{{ name }}</span>
+              </td>
+              <td>{{ data.description }}</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+
+    </div>
+  </div>
+  <div class="modal-footer">
+    <button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
+      <span class="fa fa-times"></span>&nbsp;<span translate>Close</span>
+    </button>
+  </div>
+</modal>

+ 71 - 1
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);

+ 3 - 1
gui/default/syncthing/settings/settingsModalView.html

@@ -150,6 +150,7 @@
             </div>
           </div>
         </div>
+
         <div id="settings-connections" class="tab-pane">
           <div class="form-group">
             <label translate for="ListenAddressesStr">Sync Protocol Listen Addresses</label>&emsp;<a href="https://docs.syncthing.net/users/config.html#listen-addresses" target="_blank"><span class="fa fa-fw fa-book"></span>&nbsp;<span translate>Help</span></a>
@@ -221,11 +222,12 @@
                 <label translate for="GlobalAnnServersStr">Global Discovery Servers</label>
                 <input ng-disabled="!tmpOptions.globalAnnounceEnabled" id="GlobalAnnServersStr" class="form-control" type="text" ng-model="tmpOptions._globalAnnounceServersStr"/>
               </div>
-            <div>
+            </div>
             <div class="col-md-6">
             </div>
           </div>
         </div>
+
       </div>
     </form>
   </div>

+ 2 - 2
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")
 }

+ 14 - 12
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})
 	}
 }

+ 20 - 0
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])
+	}
+
 }