feat: switch logging framework (#10220)
This updates our logging framework from legacy freetext strings using the `log` package to structured log entries using `log/slog`. I have updated all INFO or higher level entries, but not yet DEBUG (😓)... So, at a high level: There is a slight change in log levels, effectively adding a new warning level: - DEBUG is still debug (ideally not for users but developers, though this is something we need to work on) - INFO is still info, though I've added more data here, effectively making Syncthing more verbose by default (more on this below) - WARNING is a new log level that is different from the _old_ WARNING (more below) - ERROR is what was WARNING before -- problems that must be dealt with, and also bubbled as a popup in the GUI. A new feature is that the logging level can be set per package to something other than just debug or info, and hence I feel that we can add a bit more things into INFO while moving some (in fact, most) current INFO level warnings into WARNING. For example, I think it's justified to get a log of synced files in INFO and sync failures in WARNING. These are things that have historically been tricky to debug properly, and having more information by default will be useful to many, while still making it possible get close to told level of inscrutability by setting the log level to WARNING. I'd like to get to a stage where DEBUG is never necessary to just figure out what's going on, as opposed to trying to narrow down a likely bug. Code wise: - Our logging object, generally known as `l` in each package, is now a new adapter object that provides the old API on top of the newer one. (This should go away once all old log entries are migrated.) This is only for `l.Debugln` and `l.Debugf`. - There is a new level tracker that keeps the log level for each package. - There is a nested setup of handlers, since the structure mandated by `log/slog` is slightly convoluted (imho). We do this because we need to do formatting at a "medium" level internally so we can buffer log lines in text format but with separate timestamp and log level for the API/GUI to consume. - The `debug` API call becomes a `loglevels` API call, which can set the log level to `DEBUG`, `INFO`, `WARNING` or `ERROR` per package. The GUI is updated to handle this. - Our custom `sync` package provided some debugging of mutexes quite strongly integrated into the old logging framework, only turned on when `STTRACE` was set to certain values at startup, etc. It's been a long time since this has been useful; I removed it. - The `STTRACE` env var remains and can be used the same way as before, while additionally permitting specific log levels to be specified, `STTRACE=model:WARN,scanner:DEBUG`. - There is a new command line option `--log-level=INFO` to set the default log level. - The command line options `--log-flags` and `--verbose` go away, but are currently retained as hidden & ignored options since we set them by default in some of our startup examples and Syncthing would otherwise fail to start. Sample format messages: ``` 2009-02-13 23:31:30 INF A basic info line (attr1="val with spaces" attr2=2 attr3="val\"quote" a=a log.pkg=slogutil) 2009-02-13 23:31:30 INF An info line with grouped values (attr1=val1 foo.attr2=2 foo.bar.attr3=3 a=a log.pkg=slogutil) 2009-02-13 23:31:30 INF An info line with grouped values via logger (foo.attr1=val1 foo.attr2=2 a=a log.pkg=slogutil) 2009-02-13 23:31:30 INF An info line with nested grouped values via logger (bar.foo.attr1=val1 bar.foo.attr2=2 a=a log.pkg=slogutil) 2009-02-13 23:31:30 WRN A warning entry (a=a log.pkg=slogutil) 2009-02-13 23:31:30 ERR An error (a=a log.pkg=slogutil) ``` --------- Co-authored-by: Ross Smith II <ross@smithii.com>
This commit is contained in:
co-authored by
Ross Smith II
parent
49462448d0
commit
836045ee87
@@ -6,12 +6,10 @@
|
||||
|
||||
package syncthing
|
||||
|
||||
import (
|
||||
"github.com/syncthing/syncthing/lib/logger"
|
||||
)
|
||||
import "github.com/syncthing/syncthing/internal/slogutil"
|
||||
|
||||
var l = logger.DefaultLogger.NewFacility("app", "Main run facility")
|
||||
var l = slogutil.NewAdapter("Main run facility")
|
||||
|
||||
func shouldDebug() bool {
|
||||
return l.ShouldDebug("app")
|
||||
return l.ShouldDebug("syncthing")
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
package syncthing
|
||||
|
||||
import "syscall"
|
||||
import (
|
||||
"log/slog"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/secauthz/well-known-sids
|
||||
const securityLocalSystemRID = "S-1-5-18"
|
||||
@@ -26,7 +29,7 @@ func isSuperUser() bool {
|
||||
}
|
||||
|
||||
if user.User.Sid == nil {
|
||||
l.Debugln("sid is nil")
|
||||
slog.Debug("Sid is nil")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
+28
-36
@@ -12,6 +12,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/thejerf/suture/v4"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/db"
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
"github.com/syncthing/syncthing/lib/api"
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
@@ -31,7 +33,6 @@ import (
|
||||
"github.com/syncthing/syncthing/lib/discover"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/locations"
|
||||
"github.com/syncthing/syncthing/lib/logger"
|
||||
"github.com/syncthing/syncthing/lib/model"
|
||||
"github.com/syncthing/syncthing/lib/osutil"
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
@@ -55,7 +56,6 @@ type Options struct {
|
||||
NoUpgrade bool
|
||||
ProfilerAddr string
|
||||
ResetDeltaIdxs bool
|
||||
Verbose bool
|
||||
DBMaintenanceInterval time.Duration
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func New(cfg config.Wrapper, sdb db.DB, evLogger events.Logger, cert tls.Certifi
|
||||
func (a *App) Start() error {
|
||||
// Create a main service manager. We'll add things to this as we go along.
|
||||
// We want any logging it does to go through our log system.
|
||||
spec := svcutil.SpecWithDebugLogger(l)
|
||||
spec := svcutil.SpecWithDebugLogger()
|
||||
a.mainService = suture.New("main", spec)
|
||||
|
||||
// Start the supervisor and wait for it to stop to handle cleanup.
|
||||
@@ -123,13 +123,6 @@ func (a *App) startup() error {
|
||||
a.mainService.Add(newAuditService(a.opts.AuditWriter, a.evLogger))
|
||||
}
|
||||
|
||||
if a.opts.Verbose {
|
||||
a.mainService.Add(newVerboseService(a.evLogger))
|
||||
}
|
||||
|
||||
errors := logger.NewRecorder(l, logger.LevelWarn, maxSystemErrors, 0)
|
||||
systemLog := logger.NewRecorder(l, logger.LevelDebug, maxSystemLog, initialSystemLog)
|
||||
|
||||
// Event subscription for the API; must start early to catch the early
|
||||
// events. The LocalChangeDetected event might overwhelm the event
|
||||
// receiver in some situations so we will not subscribe to it here.
|
||||
@@ -141,10 +134,9 @@ func (a *App) startup() error {
|
||||
// report the error if there is one.
|
||||
osutil.MaximizeOpenFileLimit()
|
||||
|
||||
// Figure out our device ID, set it as the log prefix and log it.
|
||||
// Figure out our device ID and log it.
|
||||
a.myID = protocol.NewDeviceID(a.cert.Certificate[0])
|
||||
l.SetPrefix(fmt.Sprintf("[%s] ", a.myID.String()[:5]))
|
||||
l.Infoln("My ID:", a.myID)
|
||||
slog.Info("Calculated our device ID", a.myID.LogAttr())
|
||||
|
||||
// Emit the Starting event, now that we know who we are.
|
||||
|
||||
@@ -154,7 +146,7 @@ func (a *App) startup() error {
|
||||
})
|
||||
|
||||
if err := checkShortIDs(a.cfg); err != nil {
|
||||
l.Warnln("Short device IDs are in conflict. Unlucky!\n Regenerate the device ID of one of the following:\n ", err)
|
||||
slog.Error("Short device IDs are in conflict; regenerate the device ID of one of the conflicting devices", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -164,19 +156,19 @@ func (a *App) startup() error {
|
||||
runtime.SetBlockProfileRate(1)
|
||||
err := http.ListenAndServe(a.opts.ProfilerAddr, nil)
|
||||
if err != nil {
|
||||
l.Warnln(err)
|
||||
slog.Warn("Failed to listen and serve for profiles", slogutil.Error(err))
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
perf := ur.CpuBench(context.Background(), 3, 150*time.Millisecond)
|
||||
l.Infof("Hashing performance is %.02f MB/s", perf)
|
||||
slog.Info("Measured hashing performance", "perf", fmt.Sprintf("%.02f MB/s", perf))
|
||||
|
||||
if a.opts.ResetDeltaIdxs {
|
||||
l.Infoln("Reinitializing delta index IDs")
|
||||
slog.Info("Reinitializing delta index IDs")
|
||||
if err := a.sdb.DropAllIndexIDs(); err != nil {
|
||||
l.Warnln("Drop index IDs:", err)
|
||||
slog.Error("Failed to drop index IDs", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -192,12 +184,12 @@ func (a *App) startup() error {
|
||||
cfgFolders := a.cfg.Folders()
|
||||
dbFolders, err := a.sdb.ListFolders()
|
||||
if err != nil {
|
||||
l.Warnln("Listing folders:", err)
|
||||
slog.Warn("Failed to list folders", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
for _, folder := range dbFolders {
|
||||
if _, ok := cfgFolders[folder]; !ok {
|
||||
l.Infof("Cleaning metadata for dropped folder %q", folder)
|
||||
slog.Info("Cleaning metadata for dropped folder", "folder", folder)
|
||||
a.sdb.DropFolder(folder)
|
||||
}
|
||||
}
|
||||
@@ -207,7 +199,7 @@ func (a *App) startup() error {
|
||||
miscDB := db.NewMiscDB(a.sdb)
|
||||
prevVersion, _, err := miscDB.String("prevVersion")
|
||||
if err != nil {
|
||||
l.Warnln("Database:", err)
|
||||
slog.Error("Database error when getting previous version", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -219,14 +211,14 @@ func (a *App) startup() error {
|
||||
curParts := strings.Split(build.Version, "-")
|
||||
if rel := upgrade.CompareVersions(prevParts[0], curParts[0]); rel != upgrade.Equal {
|
||||
if prevVersion != "" {
|
||||
l.Infoln("Detected upgrade from", prevVersion, "to", build.Version)
|
||||
slog.Info("Detected upgrade", "from", prevVersion, "to", build.Version)
|
||||
}
|
||||
|
||||
if a.cfg.Options().SendFullIndexOnUpgrade {
|
||||
// Drop delta indexes in case we've changed random stuff we
|
||||
// shouldn't have. We will resend our index on next connect.
|
||||
if err := a.sdb.DropAllIndexIDs(); err != nil {
|
||||
l.Warnln("Drop index IDs:", err)
|
||||
slog.Warn("Failed to drop index IDs", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -238,7 +230,7 @@ func (a *App) startup() error {
|
||||
}
|
||||
|
||||
if err := globalMigration(a.sdb, a.cfg); err != nil {
|
||||
l.Warnln("Global migration:", err)
|
||||
slog.Warn("Failed to perform global migration", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -277,7 +269,7 @@ func (a *App) startup() error {
|
||||
a.cfg.Modify(func(cfg *config.Configuration) {
|
||||
// Candidate builds always run with usage reporting.
|
||||
if build.IsCandidate {
|
||||
l.Infoln("Anonymous usage reporting is always enabled for candidate releases.")
|
||||
slog.Info("Anonymous usage reporting is always enabled for candidate releases")
|
||||
if cfg.Options.URAccepted != ur.Version {
|
||||
cfg.Options.URAccepted = ur.Version
|
||||
// Unique ID will be set and config saved below if necessary.
|
||||
@@ -290,21 +282,21 @@ func (a *App) startup() error {
|
||||
|
||||
// GUI
|
||||
|
||||
if err := a.setupGUI(m, defaultSub, diskSub, discoveryManager, connectionsService, usageReportingSvc, errors, systemLog, miscDB); err != nil {
|
||||
l.Warnln("Failed starting API:", err)
|
||||
if err := a.setupGUI(m, defaultSub, diskSub, discoveryManager, connectionsService, usageReportingSvc, slogutil.ErrorRecorder, slogutil.GlobalRecorder, miscDB); err != nil {
|
||||
slog.Error("Failed to start API", slogutil.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
myDev, _ := a.cfg.Device(a.myID)
|
||||
l.Infof(`My name is "%v"`, myDev.Name)
|
||||
slog.Info("Loaded configuration", "name", myDev.Name)
|
||||
for _, device := range a.cfg.Devices() {
|
||||
if device.DeviceID != a.myID {
|
||||
l.Infof(`Device %s is "%v" at %v`, device.DeviceID, device.Name, device.Addresses)
|
||||
slog.Info("Loaded peer device configuration", device.DeviceID.LogAttr(), slog.String("name", device.Name), slogutil.Address(device.Addresses))
|
||||
}
|
||||
}
|
||||
|
||||
if isSuperUser() {
|
||||
l.Warnln("Syncthing should not run as a privileged or system user. Please consider using a normal user account.")
|
||||
slog.Warn("Syncthing should not run as a privileged or system user; please consider using a normal user account")
|
||||
}
|
||||
|
||||
a.evLogger.Log(events.StartupComplete, map[string]string{
|
||||
@@ -313,7 +305,7 @@ func (a *App) startup() error {
|
||||
|
||||
if a.cfg.Options().SetLowPriority {
|
||||
if err := osutil.SetLowPriority(); err != nil {
|
||||
l.Warnln("Failed to lower process priority:", err)
|
||||
slog.Warn("Failed to lower process priority", slogutil.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,10 +324,10 @@ func (a *App) wait(errChan <-chan error) {
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(10 * time.Second):
|
||||
l.Warnln("Database failed to stop within 10s")
|
||||
slog.Warn("Database failed to stop within 10s")
|
||||
}
|
||||
|
||||
l.Infoln("Exiting")
|
||||
slog.Info("Exiting")
|
||||
|
||||
close(a.stopped)
|
||||
}
|
||||
@@ -383,7 +375,7 @@ func (a *App) stopWithErr(stopReason svcutil.ExitStatus, err error) svcutil.Exit
|
||||
a.exitStatus = stopReason
|
||||
a.err = err
|
||||
if shouldDebug() {
|
||||
l.Debugln("Services before stop:")
|
||||
slog.Debug("Services before stop:")
|
||||
printServiceTree(os.Stdout, a.mainService, 0)
|
||||
}
|
||||
a.mainServiceCancel()
|
||||
@@ -392,7 +384,7 @@ func (a *App) stopWithErr(stopReason svcutil.ExitStatus, err error) svcutil.Exit
|
||||
return a.exitStatus
|
||||
}
|
||||
|
||||
func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, errors, systemLog logger.Recorder, miscDB *db.Typed) error {
|
||||
func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscription, discoverer discover.Manager, connectionsService connections.Service, urService *ur.Service, errors, systemLog slogutil.Recorder, miscDB *db.Typed) error {
|
||||
guiCfg := a.cfg.GUI()
|
||||
|
||||
if !guiCfg.Enabled {
|
||||
@@ -400,7 +392,7 @@ func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscri
|
||||
}
|
||||
|
||||
if guiCfg.InsecureAdminAccess {
|
||||
l.Warnln("Insecure admin access is enabled.")
|
||||
slog.Warn("Insecure admin access is enabled")
|
||||
}
|
||||
|
||||
summaryService := model.NewFolderSummaryService(a.cfg, m, a.myID, a.evLogger)
|
||||
|
||||
+16
-17
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
"github.com/syncthing/syncthing/internal/db/olddb"
|
||||
"github.com/syncthing/syncthing/internal/db/olddb/backend"
|
||||
"github.com/syncthing/syncthing/internal/db/sqlite"
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
"github.com/syncthing/syncthing/lib/build"
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
@@ -44,7 +46,7 @@ func EnsureDir(dir string, mode fs.FileMode) error {
|
||||
err := fs.Chmod(".", mode)
|
||||
// This can fail on crappy filesystems, nothing we can do about it.
|
||||
if err != nil {
|
||||
l.Warnln(err)
|
||||
slog.Warn("Failed to correct directory permissions", slogutil.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +62,7 @@ func LoadOrGenerateCertificate(certFile, keyFile string) (tls.Certificate, error
|
||||
}
|
||||
|
||||
func GenerateCertificate(certFile, keyFile string) (tls.Certificate, error) {
|
||||
l.Infof("Generating key and certificate for %s...", tlsDefaultCommonName)
|
||||
slog.Info("Generating key and certificate", "cn", tlsDefaultCommonName)
|
||||
return tlsutil.NewCertificate(certFile, keyFile, tlsDefaultCommonName, deviceCertLifetimeDays, false)
|
||||
}
|
||||
|
||||
@@ -68,7 +70,7 @@ func DefaultConfig(path string, myID protocol.DeviceID, evLogger events.Logger,
|
||||
newCfg := config.New(myID)
|
||||
|
||||
if skipPortProbing {
|
||||
l.Infoln("Using default network port numbers instead of probing for free ports")
|
||||
slog.Info("Using default network port numbers instead of probing for free ports")
|
||||
// Record address override initially
|
||||
newCfg.GUI.RawAddress = newCfg.GUI.Address()
|
||||
} else if err := newCfg.ProbeFreePorts(); err != nil {
|
||||
@@ -94,19 +96,16 @@ func LoadConfigAtStartup(path string, cert tls.Certificate, evLogger events.Logg
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save default config: %w", err)
|
||||
}
|
||||
l.Infof("Default config saved. Edit %s to taste (with Syncthing stopped) or use the GUI", cfg.ConfigPath())
|
||||
} else if err == io.EOF {
|
||||
slog.Info("Default config saved; edit to taste (with Syncthing stopped) or use the GUI", slogutil.FilePath(cfg.ConfigPath()))
|
||||
} else if errors.Is(err, io.EOF) {
|
||||
return nil, errors.New("failed to load config: unexpected end of file. Truncated or empty configuration?")
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
if originalVersion != config.CurrentVersion {
|
||||
if originalVersion == config.CurrentVersion+1101 {
|
||||
l.Infof("Now, THAT's what we call a config from the future! Don't worry. As long as you hit that wire with the connecting hook at precisely eighty-eight miles per hour the instant the lightning strikes the tower... everything will be fine.")
|
||||
}
|
||||
if originalVersion > config.CurrentVersion && !allowNewerConfig {
|
||||
return nil, fmt.Errorf("config file version (%d) is newer than supported version (%d). If this is expected, use --allow-newer-config to override.", originalVersion, config.CurrentVersion)
|
||||
return nil, fmt.Errorf("config file version (%d) is newer than supported version (%d); if this is expected, use --allow-newer-config to override", originalVersion, config.CurrentVersion)
|
||||
}
|
||||
err = archiveAndSaveConfig(cfg, originalVersion)
|
||||
if err != nil {
|
||||
@@ -120,7 +119,7 @@ func LoadConfigAtStartup(path string, cert tls.Certificate, evLogger events.Logg
|
||||
func archiveAndSaveConfig(cfg config.Wrapper, originalVersion int) error {
|
||||
// Copy the existing config to an archive copy
|
||||
archivePath := cfg.ConfigPath() + fmt.Sprintf(".v%d", originalVersion)
|
||||
l.Infoln("Archiving a copy of old config file format at:", archivePath)
|
||||
slog.Info("Archiving a copy of old config file format", slogutil.FilePath(archivePath))
|
||||
if err := copyFile(cfg.ConfigPath(), archivePath); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -179,11 +178,11 @@ func TryMigrateDatabase(deleteRetention time.Duration) error {
|
||||
|
||||
miscDB := db.NewMiscDB(sdb)
|
||||
if when, ok, err := miscDB.Time("migrated-from-leveldb-at"); err == nil && ok {
|
||||
l.Warnf("Old-style database present but already migrated at %v; please manually move or remove %s.", when, oldDBDir)
|
||||
slog.Error("Old-style database present but already migrated; please manually move or remove.", slog.Any("migratedAt", when), slogutil.FilePath(oldDBDir))
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Infoln("Migrating old-style database to SQLite; this may take a while...")
|
||||
slog.Info("Migrating old-style database to SQLite; this may take a while...")
|
||||
t0 := time.Now()
|
||||
|
||||
ll, err := olddb.NewLowlevel(be)
|
||||
@@ -217,7 +216,7 @@ func TryMigrateDatabase(deleteRetention time.Duration) error {
|
||||
if time.Since(t1) > 10*time.Second {
|
||||
d := time.Since(t0) + 1
|
||||
t1 = time.Now()
|
||||
l.Infof("Migrating folder %s... (%d files and %dk blocks in %v, %.01f files/s)", folder, files, blocks/1000, d.Truncate(time.Second), float64(files)/d.Seconds())
|
||||
slog.Info("Still migrating folder", "folder", folder, "files", files, "blocks", blocks, "duration", d.Truncate(time.Second), "filesrate", float64(files)/d.Seconds())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,7 +224,7 @@ func TryMigrateDatabase(deleteRetention time.Duration) error {
|
||||
writeErr = sdb.Update(folder, protocol.LocalDeviceID, batch)
|
||||
}
|
||||
d := time.Since(t0) + 1
|
||||
l.Infof("Migrated folder %s; %d files and %dk blocks in %v, %.01f files/s", folder, files, blocks/1000, d.Truncate(time.Second), float64(files)/d.Seconds())
|
||||
slog.Info("Migrated folder", "folder", folder, "files", files, "blocks", blocks, "duration", d.Truncate(time.Second), "filesrate", float64(files)/d.Seconds())
|
||||
totFiles += files
|
||||
totBlocks += blocks
|
||||
}()
|
||||
@@ -258,9 +257,9 @@ func TryMigrateDatabase(deleteRetention time.Duration) error {
|
||||
}
|
||||
}
|
||||
|
||||
l.Infoln("Migrating virtual mtimes...")
|
||||
slog.Info("Migrating virtual mtimes...")
|
||||
if err := ll.IterateMtimes(sdb.PutMtime); err != nil {
|
||||
l.Warnln("Failed to migrate mtimes:", err)
|
||||
slog.Warn("Failed to migrate mtimes", slogutil.Error(err))
|
||||
}
|
||||
|
||||
_ = miscDB.PutTime("migrated-from-leveldb-at", time.Now())
|
||||
@@ -269,6 +268,6 @@ func TryMigrateDatabase(deleteRetention time.Duration) error {
|
||||
_ = be.Close()
|
||||
_ = os.Rename(oldDBDir, oldDBDir+"-migrated")
|
||||
|
||||
l.Infof("Migration complete, %d files and %dk blocks in %s", totFiles, totBlocks/1000, time.Since(t0).Truncate(time.Second))
|
||||
slog.Info("Migration complete", "files", totFiles, "blocks", totBlocks/1000, "duration", time.Since(t0).Truncate(time.Second))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
// Copyright (C) 2015 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package syncthing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/model"
|
||||
)
|
||||
|
||||
// The verbose logging service subscribes to events and prints these in
|
||||
// verbose format to the console using INFO level.
|
||||
type verboseService struct {
|
||||
evLogger events.Logger
|
||||
}
|
||||
|
||||
func newVerboseService(evLogger events.Logger) *verboseService {
|
||||
return &verboseService{
|
||||
evLogger: evLogger,
|
||||
}
|
||||
}
|
||||
|
||||
// serve runs the verbose logging service.
|
||||
func (s *verboseService) Serve(ctx context.Context) error {
|
||||
sub := s.evLogger.Subscribe(events.AllEvents)
|
||||
defer sub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case ev, ok := <-sub.C():
|
||||
if !ok {
|
||||
<-ctx.Done()
|
||||
return ctx.Err()
|
||||
}
|
||||
formatted := s.formatEvent(ev)
|
||||
if formatted != "" {
|
||||
l.Verboseln(formatted)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var folderSummaryRemoveDeprecatedRe = regexp.MustCompile(`(Invalid|IgnorePatterns|StateChanged):\S+\s?`)
|
||||
|
||||
func (*verboseService) formatEvent(ev events.Event) string {
|
||||
switch ev.Type {
|
||||
case events.DownloadProgress:
|
||||
// Skip
|
||||
return ""
|
||||
|
||||
case events.Starting:
|
||||
return fmt.Sprintf("Starting up (%s)", ev.Data.(map[string]string)["home"])
|
||||
|
||||
case events.StartupComplete:
|
||||
return "Startup complete"
|
||||
|
||||
case events.DeviceDiscovered:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
return fmt.Sprintf("Discovered device %v at %v", data["device"], data["addrs"])
|
||||
|
||||
case events.DeviceConnected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Connected to device %v at %v (type %s)", data["id"], data["addr"], data["type"])
|
||||
|
||||
case events.DeviceDisconnected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Disconnected from device %v", data["id"])
|
||||
|
||||
case events.StateChanged:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
return fmt.Sprintf("Folder %q is now %v", data["folder"], data["to"])
|
||||
|
||||
case events.LocalChangeDetected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Local change detected in folder %q: %s %s %s", data["folder"], data["action"], data["type"], data["path"])
|
||||
|
||||
case events.RemoteChangeDetected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Remote change detected in folder %q: %s %s %s", data["folder"], data["action"], data["type"], data["path"])
|
||||
|
||||
case events.LocalIndexUpdated:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
return fmt.Sprintf("Local index update for %q with %d items (seq: %d)", data["folder"], data["items"], data["sequence"])
|
||||
|
||||
case events.RemoteIndexUpdated:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
return fmt.Sprintf("Device %v sent an index update for %q with %d items (seq: %d)", data["device"], data["folder"], data["items"], data["sequence"])
|
||||
|
||||
case events.DeviceRejected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Rejected connection from device %v at %v", data["device"], data["address"])
|
||||
|
||||
case events.FolderRejected:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Rejected unshared folder %q from device %v", data["folder"], data["device"])
|
||||
|
||||
case events.ItemStarted:
|
||||
data := ev.Data.(map[string]string)
|
||||
return fmt.Sprintf("Started syncing %q / %q (%v %v)", data["folder"], data["item"], data["action"], data["type"])
|
||||
|
||||
case events.ItemFinished:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
if err, ok := data["error"].(*string); ok && err != nil {
|
||||
// If the err interface{} is not nil, it is a string pointer.
|
||||
// Dereference it to get the actual error or Sprintf will print
|
||||
// the pointer value....
|
||||
return fmt.Sprintf("Finished syncing %q / %q (%v %v): %v", data["folder"], data["item"], data["action"], data["type"], *err)
|
||||
}
|
||||
return fmt.Sprintf("Finished syncing %q / %q (%v %v): Success", data["folder"], data["item"], data["action"], data["type"])
|
||||
|
||||
case events.ConfigSaved:
|
||||
return "Configuration was saved"
|
||||
|
||||
case events.FolderCompletion:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
return fmt.Sprintf("Completion for folder %q on device %v is %v%% (state: %s, seq: %d)", data["folder"], data["device"], data["completion"], data["remoteState"], data["sequence"])
|
||||
|
||||
case events.FolderSummary:
|
||||
data := ev.Data.(model.FolderSummaryEventData)
|
||||
return folderSummaryRemoveDeprecatedRe.ReplaceAllString(fmt.Sprintf("Summary for folder %q is %+v", data.Folder, data.Summary), "")
|
||||
|
||||
case events.FolderScanProgress:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
folder := data["folder"].(string)
|
||||
current := data["current"].(int64)
|
||||
total := data["total"].(int64)
|
||||
rate := data["rate"].(float64) / 1024 / 1024
|
||||
var pct int64
|
||||
if total > 0 {
|
||||
pct = 100 * current / total
|
||||
}
|
||||
return fmt.Sprintf("Scanning folder %q, %d%% done (%.01f MiB/s)", folder, pct, rate)
|
||||
|
||||
case events.DevicePaused:
|
||||
data := ev.Data.(map[string]string)
|
||||
device := data["device"]
|
||||
return fmt.Sprintf("Device %v was paused", device)
|
||||
|
||||
case events.DeviceResumed:
|
||||
data := ev.Data.(map[string]string)
|
||||
device := data["device"]
|
||||
return fmt.Sprintf("Device %v was resumed", device)
|
||||
|
||||
case events.ClusterConfigReceived:
|
||||
data := ev.Data.(model.ClusterConfigReceivedEventData)
|
||||
return fmt.Sprintf("Received ClusterConfig from device %v", data.Device)
|
||||
|
||||
case events.FolderPaused:
|
||||
data := ev.Data.(map[string]string)
|
||||
id := data["id"]
|
||||
label := data["label"]
|
||||
return fmt.Sprintf("Folder %v (%v) was paused", id, label)
|
||||
|
||||
case events.FolderResumed:
|
||||
data := ev.Data.(map[string]string)
|
||||
id := data["id"]
|
||||
label := data["label"]
|
||||
return fmt.Sprintf("Folder %v (%v) was resumed", id, label)
|
||||
|
||||
case events.ListenAddressesChanged:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
address := data["address"]
|
||||
lan := data["lan"]
|
||||
wan := data["wan"]
|
||||
return fmt.Sprintf("Listen address %s resolution has changed: lan addresses: %s wan addresses: %s", address, lan, wan)
|
||||
|
||||
case events.LoginAttempt:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
username := data["username"].(string)
|
||||
var success string
|
||||
if data["success"].(bool) {
|
||||
success = "successful"
|
||||
} else {
|
||||
success = "failed"
|
||||
}
|
||||
return fmt.Sprintf("Login %s for username %s.", success, username)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %#v", ev.Type, ev)
|
||||
}
|
||||
|
||||
func (s *verboseService) String() string {
|
||||
return fmt.Sprintf("verboseService@%p", s)
|
||||
}
|
||||
Reference in New Issue
Block a user