Include more (and customizable) context with backup logs

This commit is contained in:
Dane Everitt 2020-12-27 16:16:40 -08:00
parent b26db99ee7
commit 902f9f5944
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
6 changed files with 65 additions and 51 deletions

View File

@ -1,7 +1,7 @@
package router package router
import ( import (
"errors" "emperror.dev/errors"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
@ -38,9 +38,14 @@ func postServerBackup(c *gin.Context) {
return return
} }
// Attach the server ID to the backup log output for easier parsing.
adapter.WithLogContext(map[string]interface{}{
"server": s.Id(),
})
go func(b backup.BackupInterface, serv *server.Server) { go func(b backup.BackupInterface, serv *server.Server) {
if err := serv.Backup(b); err != nil { if err := serv.Backup(b); err != nil {
serv.Log().WithField("error", err).Error("failed to generate backup for server") serv.Log().WithField("error", errors.WithStackIf(err)).Error("failed to generate backup for server")
} }
}(adapter, s) }(adapter, s)

View File

@ -70,10 +70,7 @@ func (s *Server) Backup(b backup.BackupInterface) error {
"error": notifyError, "error": notifyError,
}).Warn("failed to notify panel of failed backup state") }).Warn("failed to notify panel of failed backup state")
} else { } else {
s.Log().WithFields(log.Fields{ s.Log().WithFields(log.Fields{"backup": b.Identifier()}).Info("notified panel of failed backup state")
"backup": b.Identifier(),
"error": err,
}).Info("notified panel of failed backup state")
} }
_ = s.Events().PublishJson(BackupCompletedEvent+":"+b.Identifier(), map[string]interface{}{ _ = s.Events().PublishJson(BackupCompletedEvent+":"+b.Identifier(), map[string]interface{}{

View File

@ -12,9 +12,11 @@ import (
"sync" "sync"
) )
type AdapterType string
const ( const (
LocalBackupAdapter = "wings" LocalBackupAdapter AdapterType = "wings"
S3BackupAdapter = "s3" S3BackupAdapter AdapterType = "s3"
) )
type ArchiveDetails struct { type ArchiveDetails struct {
@ -41,6 +43,9 @@ type Backup struct {
// An array of files to ignore when generating this backup. This should be // An array of files to ignore when generating this backup. This should be
// compatible with a standard .gitignore structure. // compatible with a standard .gitignore structure.
Ignore string `json:"ignore"` Ignore string `json:"ignore"`
adapter AdapterType
logContext map[string]interface{}
} }
// noinspection GoNameStartsWithPackageName // noinspection GoNameStartsWithPackageName
@ -48,6 +53,9 @@ type BackupInterface interface {
// Returns the UUID of this backup as tracked by the panel instance. // Returns the UUID of this backup as tracked by the panel instance.
Identifier() string Identifier() string
// Attaches additional context to the log output for this backup.
WithLogContext(map[string]interface{})
// Generates a backup in whatever the configured source for the specific // Generates a backup in whatever the configured source for the specific
// implementation is. // implementation is.
Generate(string, string) (*ArchiveDetails, error) Generate(string, string) (*ArchiveDetails, error)
@ -160,3 +168,13 @@ func (b *Backup) Details() *ArchiveDetails {
func (b *Backup) Ignored() string { func (b *Backup) Ignored() string {
return b.Ignore return b.Ignore
} }
// Returns a logger instance for this backup with the additional context fields
// assigned to the output.
func (b *Backup) log() *log.Entry {
l := log.WithField("backup", b.Identifier()).WithField("adapter", b.adapter)
for k, v := range b.logContext {
l = l.WithField(k, v)
}
return l
}

View File

@ -2,7 +2,6 @@ package backup
import ( import (
"errors" "errors"
"github.com/apex/log"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
"os" "os"
) )
@ -40,6 +39,11 @@ func (b *LocalBackup) Remove() error {
return os.Remove(b.Path()) return os.Remove(b.Path())
} }
// Attaches additional context to the log output for this backup.
func (b *LocalBackup) WithLogContext(c map[string]interface{}) {
b.logContext = c
}
// Generates a backup of the selected files and pushes it to the defined location // Generates a backup of the selected files and pushes it to the defined location
// for this instance. // for this instance.
func (b *LocalBackup) Generate(basePath, ignore string) (*ArchiveDetails, error) { func (b *LocalBackup) Generate(basePath, ignore string) (*ArchiveDetails, error) {
@ -48,16 +52,11 @@ func (b *LocalBackup) Generate(basePath, ignore string) (*ArchiveDetails, error)
Ignore: ignore, Ignore: ignore,
} }
l := log.WithFields(log.Fields{ b.log().Info("creating backup for server...")
"backup_id": b.Uuid,
"adapter": "local",
})
l.Info("creating backup for server...")
if err := a.Create(b.Path()); err != nil { if err := a.Create(b.Path()); err != nil {
return nil, err return nil, err
} }
l.Info("created backup successfully") b.log().Info("created backup successfully")
return b.Details(), nil return b.Details(), nil
} }

View File

@ -6,7 +6,7 @@ import (
) )
type Request struct { type Request struct {
Adapter string `json:"adapter"` Adapter AdapterType `json:"adapter"`
Uuid string `json:"uuid"` Uuid string `json:"uuid"`
Ignore string `json:"ignore"` Ignore string `json:"ignore"`
} }
@ -21,6 +21,7 @@ func (r *Request) NewLocalBackup() (*LocalBackup, error) {
Backup{ Backup{
Uuid: r.Uuid, Uuid: r.Uuid,
Ignore: r.Ignore, Ignore: r.Ignore,
adapter: LocalBackupAdapter,
}, },
}, nil }, nil
} }
@ -35,6 +36,7 @@ func (r *Request) NewS3Backup() (*S3Backup, error) {
Backup: Backup{ Backup: Backup{
Uuid: r.Uuid, Uuid: r.Uuid,
Ignore: r.Ignore, Ignore: r.Ignore,
adapter: S3BackupAdapter,
}, },
}, nil }, nil
} }

View File

@ -2,7 +2,6 @@ package backup
import ( import (
"fmt" "fmt"
"github.com/apex/log"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/server/filesystem" "github.com/pterodactyl/wings/server/filesystem"
"io" "io"
@ -17,6 +16,16 @@ type S3Backup struct {
var _ BackupInterface = (*S3Backup)(nil) var _ BackupInterface = (*S3Backup)(nil)
// Removes a backup from the system.
func (s *S3Backup) Remove() error {
return os.Remove(s.Path())
}
// Attaches additional context to the log output for this backup.
func (s *S3Backup) WithLogContext(c map[string]interface{}) {
s.logContext = c
}
// Generates a new backup on the disk, moves it into the S3 bucket via the provided // Generates a new backup on the disk, moves it into the S3 bucket via the provided
// presigned URL, and then deletes the backup from the disk. // presigned URL, and then deletes the backup from the disk.
func (s *S3Backup) Generate(basePath, ignore string) (*ArchiveDetails, error) { func (s *S3Backup) Generate(basePath, ignore string) (*ArchiveDetails, error) {
@ -27,16 +36,11 @@ func (s *S3Backup) Generate(basePath, ignore string) (*ArchiveDetails, error) {
Ignore: ignore, Ignore: ignore,
} }
l := log.WithFields(log.Fields{ s.log().Info("creating backup for server...")
"backup_id": s.Uuid,
"adapter": "s3",
})
l.Info("creating backup for server...")
if err := a.Create(s.Path()); err != nil { if err := a.Create(s.Path()); err != nil {
return nil, err return nil, err
} }
l.Info("created backup successfully") s.log().Info("created backup successfully")
rc, err := os.Open(s.Path()) rc, err := os.Open(s.Path())
if err != nil { if err != nil {
@ -51,11 +55,6 @@ func (s *S3Backup) Generate(basePath, ignore string) (*ArchiveDetails, error) {
return s.Details(), nil return s.Details(), nil
} }
// Removes a backup from the system.
func (s *S3Backup) Remove() error {
return os.Remove(s.Path())
}
// Reader provides a wrapper around an existing io.Reader // Reader provides a wrapper around an existing io.Reader
// but implements io.Closer in order to satisfy an io.ReadCloser. // but implements io.Closer in order to satisfy an io.ReadCloser.
type Reader struct { type Reader struct {
@ -70,26 +69,20 @@ func (Reader) Close() error {
func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error { func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error {
defer rc.Close() defer rc.Close()
l := log.WithFields(log.Fields{ s.log().Debug("attempting to get size of backup...")
"backup_id": s.Uuid,
"adapter": "s3",
})
l.Debug("attempting to get size of backup...")
size, err := s.Backup.Size() size, err := s.Backup.Size()
if err != nil { if err != nil {
return err return err
} }
l.WithField("size", size).Debug("got size of backup") s.log().WithField("size", size).Debug("got size of backup")
l.Debug("attempting to get S3 upload urls from Panel...") s.log().Debug("attempting to get S3 upload urls from Panel...")
urls, err := api.New().GetBackupRemoteUploadURLs(s.Backup.Uuid, size) urls, err := api.New().GetBackupRemoteUploadURLs(s.Backup.Uuid, size)
if err != nil { if err != nil {
return err return err
} }
l.Debug("got S3 upload urls from the Panel") s.log().Debug("got S3 upload urls from the Panel")
partCount := len(urls.Parts) s.log().WithField("parts", len(urls.Parts)).Info("attempting to upload backup to s3 endpoint...")
l.WithField("parts", partCount).Info("attempting to upload backup...")
handlePart := func(part string, size int64) (string, error) { handlePart := func(part string, size int64) (string, error) {
r, err := http.NewRequest(http.MethodPut, part, nil) r, err := http.NewRequest(http.MethodPut, part, nil)
@ -125,7 +118,7 @@ func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error {
for i, part := range urls.Parts { for i, part := range urls.Parts {
// Get the size for the current part. // Get the size for the current part.
var partSize int64 var partSize int64
if i+1 < partCount { if i+1 < len(urls.Parts) {
partSize = urls.PartSize partSize = urls.PartSize
} else { } else {
// This is the remaining size for the last part, // This is the remaining size for the last part,
@ -135,14 +128,14 @@ func (s *S3Backup) generateRemoteRequest(rc io.ReadCloser) error {
// Attempt to upload the part. // Attempt to upload the part.
if _, err := handlePart(part, partSize); err != nil { if _, err := handlePart(part, partSize); err != nil {
l.WithField("part_id", i+1).WithError(err).Warn("failed to upload part") s.log().WithField("part_id", i+1).WithError(err).Warn("failed to upload part")
return err return err
} }
l.WithField("part_id", i+1).Info("successfully uploaded backup part") s.log().WithField("part_id", i+1).Info("successfully uploaded backup part")
} }
l.WithField("parts", partCount).Info("backup has been successfully uploaded") s.log().WithField("parts", len(urls.Parts)).Info("backup has been successfully uploaded")
return nil return nil
} }