Implement facility based logger, debugging via REST API
This implements a new debug/trace infrastructure based on a slightly
hacked up logger. Instead of the traditional "if debug { ... }" I've
rewritten the logger to have no-op Debugln and Debugf, unless debugging
has been enabled for a given "facility". The "facility" is just a
string, typically a package name.
This will be slightly slower than before; but not that much as it's
mostly a function call that returns immediately. For the cases where it
matters (the Debugln takes a hex.Dump() of something for example, and
it's not in a very occasional "if err != nil" branch) there is an
l.ShouldDebug(facility) that is fast enough to be used like the old "if
debug".
The point of all this is that we can now toggle debugging for the
various packages on and off at runtime. There's a new method
/rest/system/debug that can be POSTed a set of facilities to enable and
disable debug for, or GET from to get a list of facilities with
descriptions and their current debug status.
Similarly a /rest/system/log?since=... can grab the latest log entries,
up to 250 of them (hardcoded constant in main.go) plus the initial few.
Not implemented in this commit (but planned) is a simple debug GUI
available on /debug that shows the current log in an easily pasteable
format and has checkboxes to enable the various debug facilities.
The debug instructions to a user then becomes "visit this URL, check
these boxes, reproduce your problem, copy and paste the log". The actual
log viewer on the hypothetical /debug URL can poll regularly for new log
entries and this bypass the 250 line limit.
The existing STTRACE=foo variable is still obeyed and just sets the
start state of the system.
This commit is contained in:
@@ -10,10 +10,17 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/calmh/logger"
|
||||
"github.com/syncthing/syncthing/lib/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = strings.Contains(os.Getenv("STTRACE"), "upnp") || os.Getenv("STTRACE") == "all"
|
||||
l = logger.DefaultLogger
|
||||
l = logger.DefaultLogger.NewFacility("upnp", "UPnP discovery and port mapping")
|
||||
)
|
||||
|
||||
func init() {
|
||||
l.SetDebug("upnp", strings.Contains(os.Getenv("STTRACE"), "upnp") || os.Getenv("STTRACE") == "all")
|
||||
}
|
||||
|
||||
func shouldDebug() bool {
|
||||
return l.ShouldDebug("upnp")
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ nextResult:
|
||||
for result := range resultChan {
|
||||
for _, existingResult := range results {
|
||||
if existingResult.uuid == result.uuid {
|
||||
if debug {
|
||||
if shouldDebug() {
|
||||
l.Debugf("Skipping duplicate result %s with services:", result.uuid)
|
||||
for _, svc := range result.services {
|
||||
l.Debugf("* [%s] %s", svc.serviceID, svc.serviceURL)
|
||||
@@ -140,7 +140,7 @@ nextResult:
|
||||
}
|
||||
|
||||
results = append(results, result)
|
||||
if debug {
|
||||
if shouldDebug() {
|
||||
l.Debugf("UPnP discovery result %s with services:", result.uuid)
|
||||
for _, svc := range result.services {
|
||||
l.Debugf("* [%s] %s", svc.serviceID, svc.serviceURL)
|
||||
@@ -167,15 +167,11 @@ Mx: %d
|
||||
|
||||
search := []byte(strings.Replace(searchStr, "\n", "\r\n", -1))
|
||||
|
||||
if debug {
|
||||
l.Debugln("Starting discovery of device type " + deviceType + " on " + intf.Name)
|
||||
}
|
||||
l.Debugln("Starting discovery of device type", deviceType, "on", intf.Name)
|
||||
|
||||
socket, err := net.ListenMulticastUDP("udp4", intf, &net.UDPAddr{IP: ssdp.IP})
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugln(err)
|
||||
}
|
||||
l.Debugln(err)
|
||||
return
|
||||
}
|
||||
defer socket.Close() // Make sure our socket gets closed
|
||||
@@ -186,9 +182,7 @@ Mx: %d
|
||||
return
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugln("Sending search request for device type " + deviceType + " on " + intf.Name)
|
||||
}
|
||||
l.Debugln("Sending search request for device type", deviceType, "on", intf.Name)
|
||||
|
||||
_, err = socket.WriteTo(search, ssdp)
|
||||
if err != nil {
|
||||
@@ -196,9 +190,7 @@ Mx: %d
|
||||
return
|
||||
}
|
||||
|
||||
if debug {
|
||||
l.Debugln("Listening for UPnP response for device type " + deviceType + " on " + intf.Name)
|
||||
}
|
||||
l.Debugln("Listening for UPnP response for device type", deviceType, "on", intf.Name)
|
||||
|
||||
// Listen for responses until a timeout is reached
|
||||
for {
|
||||
@@ -217,15 +209,11 @@ Mx: %d
|
||||
}
|
||||
results <- igd
|
||||
}
|
||||
if debug {
|
||||
l.Debugln("Discovery for device type " + deviceType + " on " + intf.Name + " finished.")
|
||||
}
|
||||
l.Debugln("Discovery for device type", deviceType, "on", intf.Name, "finished.")
|
||||
}
|
||||
|
||||
func parseResponse(deviceType string, resp []byte) (IGD, error) {
|
||||
if debug {
|
||||
l.Debugln("Handling UPnP response:\n\n" + string(resp))
|
||||
}
|
||||
l.Debugln("Handling UPnP response:\n\n" + string(resp))
|
||||
|
||||
reader := bufio.NewReader(bytes.NewBuffer(resp))
|
||||
request := &http.Request{}
|
||||
@@ -368,7 +356,7 @@ func getIGDServices(rootURL string, device upnpDevice, wanDeviceURN string, wanC
|
||||
devices := getChildDevices(device, wanDeviceURN)
|
||||
|
||||
if len(devices) < 1 {
|
||||
l.Infoln("[" + rootURL + "] Malformed InternetGatewayDevice description: no WANDevices specified.")
|
||||
l.Infoln(rootURL, "- malformed InternetGatewayDevice description: no WANDevices specified.")
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -376,27 +364,23 @@ func getIGDServices(rootURL string, device upnpDevice, wanDeviceURN string, wanC
|
||||
connections := getChildDevices(device, wanConnectionURN)
|
||||
|
||||
if len(connections) < 1 {
|
||||
l.Infoln("[" + rootURL + "] Malformed " + wanDeviceURN + " description: no WANConnectionDevices specified.")
|
||||
l.Infoln(rootURL, "- malformed ", wanDeviceURN, "description: no WANConnectionDevices specified.")
|
||||
}
|
||||
|
||||
for _, connection := range connections {
|
||||
for _, serviceURN := range serviceURNs {
|
||||
services := getChildServices(connection, serviceURN)
|
||||
|
||||
if len(services) < 1 && debug {
|
||||
l.Debugln("[" + rootURL + "] No services of type " + serviceURN + " found on connection.")
|
||||
}
|
||||
l.Debugln(rootURL, "- no services of type", serviceURN, " found on connection.")
|
||||
|
||||
for _, service := range services {
|
||||
if len(service.ControlURL) == 0 {
|
||||
l.Infoln("[" + rootURL + "] Malformed " + service.ServiceType + " description: no control URL.")
|
||||
l.Infoln(rootURL+"- malformed", service.ServiceType, "description: no control URL.")
|
||||
} else {
|
||||
u, _ := url.Parse(rootURL)
|
||||
replaceRawPath(u, service.ControlURL)
|
||||
|
||||
if debug {
|
||||
l.Debugln("[" + rootURL + "] Found " + service.ServiceType + " with URL " + u.String())
|
||||
}
|
||||
l.Debugln(rootURL, "- found", service.ServiceType, "with URL", u)
|
||||
|
||||
service := IGDService{serviceID: service.ServiceID, serviceURL: u.String(), serviceURN: service.ServiceType}
|
||||
|
||||
@@ -456,24 +440,18 @@ func soapRequest(url, service, function, message string) ([]byte, error) {
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
|
||||
if debug {
|
||||
l.Debugln("SOAP Request URL: " + url)
|
||||
l.Debugln("SOAP Action: " + req.Header.Get("SOAPAction"))
|
||||
l.Debugln("SOAP Request:\n\n" + body)
|
||||
}
|
||||
l.Debugln("SOAP Request URL: " + url)
|
||||
l.Debugln("SOAP Action: " + req.Header.Get("SOAPAction"))
|
||||
l.Debugln("SOAP Request:\n\n" + body)
|
||||
|
||||
r, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
if debug {
|
||||
l.Debugln(err)
|
||||
}
|
||||
l.Debugln(err)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
resp, _ = ioutil.ReadAll(r.Body)
|
||||
if debug {
|
||||
l.Debugf("SOAP Response: %v\n\n%v\n\n", r.StatusCode, string(resp))
|
||||
}
|
||||
l.Debugf("SOAP Response: %s\n\n%s\n\n", r.Status, resp)
|
||||
|
||||
r.Body.Close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user