Improve logs (#89)
This commit is contained in:
parent
66eadf35dc
commit
5d4e48f0f9
18 changed files with 343 additions and 33 deletions
69
assets/js/logs.js
Normal file
69
assets/js/logs.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
Distribyted.logs = {
|
||||||
|
loadView: function () {
|
||||||
|
fetch("/api/log")
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.body.getReader();
|
||||||
|
} else {
|
||||||
|
response.json().then(json => {
|
||||||
|
Distribyted.message.error('Error getting logs from server. Error: ' + json.error);
|
||||||
|
}).catch(error => {
|
||||||
|
Distribyted.message.error('Error getting logs from server. Error: ' + error);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(reader => {
|
||||||
|
var decoder = new TextDecoder()
|
||||||
|
var lastString = ''
|
||||||
|
reader.read().then(function processText({ done, value }) {
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string = `${lastString}${decoder.decode(value)}`
|
||||||
|
const lines = string.split(/\r\n|[\r\n]/g)
|
||||||
|
this.lastString = lines.pop() || ''
|
||||||
|
|
||||||
|
lines.forEach(element => {
|
||||||
|
try {
|
||||||
|
var json = JSON.parse(element)
|
||||||
|
var properties = ""
|
||||||
|
for (let [key, value] of Object.entries(json)) {
|
||||||
|
if (key == "level" || key == "component" || key == "message" || key == "time") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
properties += `<b>${key}</b>=${value} `
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableClass = "table-primary"
|
||||||
|
switch (json.level) {
|
||||||
|
case "info":
|
||||||
|
tableClass = ""
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
tableClass = "table-danger"
|
||||||
|
break;
|
||||||
|
case "warn":
|
||||||
|
tableClass = "table-warning"
|
||||||
|
break;
|
||||||
|
case "debug":
|
||||||
|
tableClass = "table-info"
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
template = `<tr class="${tableClass}"><td>${new Date(json.time*1000).toLocaleString()}</td><td>${json.level}</td><td>${json.component}</td><td>${json.message}</td><td>${properties}</td></tr>`;
|
||||||
|
document.getElementById("log_table").innerHTML += template;
|
||||||
|
} catch (err) {
|
||||||
|
// server can send some corrupted json line
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return reader.read().then(processText);
|
||||||
|
}).catch(err => console.log(err));
|
||||||
|
}).catch(err => console.log(err));
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,6 +129,7 @@ Distribyted.routes = {
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
Distribyted.message.info('Torrent deleted.')
|
Distribyted.message.info('Torrent deleted.')
|
||||||
|
Distribyted.routes.loadView();
|
||||||
} else {
|
} else {
|
||||||
response.json().then(json => {
|
response.json().then(json => {
|
||||||
Distribyted.message.error('Error deletting torrent. Response: ' + json.error)
|
Distribyted.message.error('Error deletting torrent. Response: ' + json.error)
|
||||||
|
@ -159,7 +160,8 @@ $("#new-magnet").submit(function (event) {
|
||||||
let url = '/api/routes/' + route + '/torrent'
|
let url = '/api/routes/' + route + '/torrent'
|
||||||
let body = JSON.stringify({ magnet: magnet })
|
let body = JSON.stringify({ magnet: magnet })
|
||||||
|
|
||||||
console.log("LOG", url, body)
|
document.getElementById("submit_magnet_loading").style = "display:block"
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: body
|
body: body
|
||||||
|
@ -167,6 +169,7 @@ $("#new-magnet").submit(function (event) {
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
Distribyted.message.info('New magnet added.')
|
Distribyted.message.info('New magnet added.')
|
||||||
|
Distribyted.routes.loadView();
|
||||||
} else {
|
} else {
|
||||||
response.json().then(json => {
|
response.json().then(json => {
|
||||||
Distribyted.message.error('Error adding new magnet. Response: ' + json.error)
|
Distribyted.message.error('Error adding new magnet. Response: ' + json.error)
|
||||||
|
@ -176,6 +179,8 @@ $("#new-magnet").submit(function (event) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(function (error) {
|
||||||
Distribyted.message.error('Error deletting torrent: ' + error.message)
|
Distribyted.message.error('Error adding torrent: ' + error.message)
|
||||||
|
}).then(function () {
|
||||||
|
document.getElementById("submit_magnet_loading").style = "display:none"
|
||||||
});
|
});
|
||||||
});
|
});
|
1
assets/plugins/js-yaml/js-yaml.min.js
vendored
1
assets/plugins/js-yaml/js-yaml.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -25,10 +25,6 @@ import (
|
||||||
"github.com/distribyted/distribyted/webdav"
|
"github.com/distribyted/distribyted/webdav"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
dlog.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
configFlag = "config"
|
configFlag = "config"
|
||||||
fuseAllowOther = "fuse-allow-other"
|
fuseAllowOther = "fuse-allow-other"
|
||||||
|
@ -96,6 +92,8 @@ func load(configPath string, port, webDAVPort int, fuseAllowOther bool) error {
|
||||||
return fmt.Errorf("error loading configuration: %w", err)
|
return fmt.Errorf("error loading configuration: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dlog.Load(conf.Log)
|
||||||
|
|
||||||
if err := os.MkdirAll(conf.Torrent.MetadataFolder, 0744); err != nil {
|
if err := os.MkdirAll(conf.Torrent.MetadataFolder, 0744); err != nil {
|
||||||
return fmt.Errorf("error creating metadata folder: %w", err)
|
return fmt.Errorf("error creating metadata folder: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -131,7 +129,7 @@ func load(configPath string, port, webDAVPort int, fuseAllowOther bool) error {
|
||||||
return fmt.Errorf("error starting magnet database: %w", err)
|
return fmt.Errorf("error starting magnet database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ts := torrent.NewService(cl, dbl, ss, c)
|
ts := torrent.NewService(cl, dbl, ss, c, conf.Torrent.AddTimeout)
|
||||||
|
|
||||||
mh := fuse.NewHandler(fuseAllowOther || conf.Fuse.AllowOther, conf.Fuse.Path)
|
mh := fuse.NewHandler(fuseAllowOther || conf.Fuse.AllowOther, conf.Fuse.Path)
|
||||||
|
|
||||||
|
@ -189,7 +187,9 @@ func load(configPath string, port, webDAVPort int, fuseAllowOther bool) error {
|
||||||
log.Warn().Msg("webDAV configuration not found!")
|
log.Warn().Msg("webDAV configuration not found!")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = http.New(fc, ss, ts, ch, port)
|
logFilename := filepath.Join(conf.Log.Path, dlog.FileName)
|
||||||
|
|
||||||
|
err = http.New(fc, ss, ts, ch, port, logFilename)
|
||||||
log.Error().Err(err).Msg("error initializing HTTP server")
|
log.Error().Err(err).Msg("error initializing HTTP server")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ const (
|
||||||
const (
|
const (
|
||||||
metadataFolder = "./distribyted-data/metadata"
|
metadataFolder = "./distribyted-data/metadata"
|
||||||
mountFolder = "./distribyted-data/mount"
|
mountFolder = "./distribyted-data/mount"
|
||||||
|
logsFolder = "./distribyted-data/logs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DefaultConfig() *Root {
|
func DefaultConfig() *Root {
|
||||||
|
@ -24,13 +25,19 @@ func DefaultConfig() *Root {
|
||||||
Pass: "admin",
|
Pass: "admin",
|
||||||
},
|
},
|
||||||
Torrent: &TorrentGlobal{
|
Torrent: &TorrentGlobal{
|
||||||
GlobalCacheSize: 1024,
|
GlobalCacheSize: 2048,
|
||||||
MetadataFolder: metadataFolder,
|
MetadataFolder: metadataFolder,
|
||||||
|
AddTimeout: 60,
|
||||||
},
|
},
|
||||||
Fuse: &FuseGlobal{
|
Fuse: &FuseGlobal{
|
||||||
AllowOther: false,
|
AllowOther: false,
|
||||||
Path: mountFolder,
|
Path: mountFolder,
|
||||||
},
|
},
|
||||||
|
Log: &Log{
|
||||||
|
Path: logsFolder,
|
||||||
|
MaxBackups: 2,
|
||||||
|
MaxSize: 50,
|
||||||
|
},
|
||||||
Routes: []*Route{
|
Routes: []*Route{
|
||||||
{
|
{
|
||||||
Name: "multimedia",
|
Name: "multimedia",
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/distribyted/distribyted"
|
"github.com/distribyted/distribyted"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,7 +42,7 @@ func (c *Handler) createFromTemplateFile() ([]byte, error) {
|
||||||
func (c *Handler) GetRaw() ([]byte, error) {
|
func (c *Handler) GetRaw() ([]byte, error) {
|
||||||
f, err := ioutil.ReadFile(c.p)
|
f, err := ioutil.ReadFile(c.p)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
log.Info().Str("file", c.p).Msg("configuration file does not exist, creating from template file")
|
fmt.Println("configuration file does not exist, creating from template file:", c.p)
|
||||||
return c.createFromTemplateFile()
|
return c.createFromTemplateFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,11 +68,3 @@ func (c *Handler) Get() (*Root, error) {
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Handler) Set(b []byte) error {
|
|
||||||
if err := yaml.Unmarshal(b, &Root{}); err != nil {
|
|
||||||
return fmt.Errorf("error parsing configuration file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ioutil.WriteFile(c.p, b, 0644)
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,11 +6,21 @@ type Root struct {
|
||||||
WebDAV *WebDAVGlobal `yaml:"webdav"`
|
WebDAV *WebDAVGlobal `yaml:"webdav"`
|
||||||
Torrent *TorrentGlobal `yaml:"torrent"`
|
Torrent *TorrentGlobal `yaml:"torrent"`
|
||||||
Fuse *FuseGlobal `yaml:"fuse"`
|
Fuse *FuseGlobal `yaml:"fuse"`
|
||||||
|
Log *Log `yaml:"log"`
|
||||||
|
|
||||||
Routes []*Route `yaml:"routes"`
|
Routes []*Route `yaml:"routes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Log struct {
|
||||||
|
Debug bool `yaml:"debug"`
|
||||||
|
MaxBackups int `yaml:"max_backups"`
|
||||||
|
MaxSize int `yaml:"max_size"`
|
||||||
|
MaxAge int `yaml:"max_age"`
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
type TorrentGlobal struct {
|
type TorrentGlobal struct {
|
||||||
|
AddTimeout int `yaml:"add_timeout,omitempty"`
|
||||||
GlobalCacheSize int64 `yaml:"global_cache_size,omitempty"`
|
GlobalCacheSize int64 `yaml:"global_cache_size,omitempty"`
|
||||||
MetadataFolder string `yaml:"metadata_folder,omitempty"`
|
MetadataFolder string `yaml:"metadata_folder,omitempty"`
|
||||||
DisableIPv6 bool `yaml:"disable_ipv6,omitempty"`
|
DisableIPv6 bool `yaml:"disable_ipv6,omitempty"`
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -105,4 +105,5 @@ require (
|
||||||
golang.org/x/text v0.3.6 // indirect
|
golang.org/x/text v0.3.6 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -598,6 +598,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
49
http/api.go
49
http/api.go
|
@ -1,7 +1,11 @@
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/anacrolix/missinggo/v2/filecache"
|
"github.com/anacrolix/missinggo/v2/filecache"
|
||||||
|
@ -61,3 +65,48 @@ var apiDelTorrentHandler = func(s *torrent.Service) gin.HandlerFunc {
|
||||||
ctx.JSON(http.StatusOK, nil)
|
ctx.JSON(http.StatusOK, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var apiLogHandler = func(path string) gin.HandlerFunc {
|
||||||
|
return func(ctx *gin.Context) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
max := math.Max(float64(-fi.Size()), -1024*8*8)
|
||||||
|
_, err = f.Seek(int64(max), os.SEEK_END)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
ctx.Stream(func(w io.Writer) bool {
|
||||||
|
_, err := b.ReadFrom(f)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = b.WriteTo(w)
|
||||||
|
if err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := f.Close(); err != nil {
|
||||||
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/shurcooL/httpfs/html/vfstemplate"
|
"github.com/shurcooL/httpfs/html/vfstemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(fc *filecache.Cache, ss *torrent.Stats, s *torrent.Service, ch *config.Handler, port int) error {
|
func New(fc *filecache.Cache, ss *torrent.Stats, s *torrent.Service, ch *config.Handler, port int, logPath string) error {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
|
@ -30,9 +30,11 @@ func New(fc *filecache.Cache, ss *torrent.Stats, s *torrent.Service, ch *config.
|
||||||
|
|
||||||
r.GET("/", indexHandler)
|
r.GET("/", indexHandler)
|
||||||
r.GET("/routes", routesHandler(ss))
|
r.GET("/routes", routesHandler(ss))
|
||||||
|
r.GET("/logs", logsHandler)
|
||||||
|
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
{
|
{
|
||||||
|
api.GET("/log", apiLogHandler(logPath))
|
||||||
api.GET("/status", apiStatusHandler(fc, ss))
|
api.GET("/status", apiStatusHandler(fc, ss))
|
||||||
api.GET("/routes", apiRoutesHandler(ss))
|
api.GET("/routes", apiRoutesHandler(ss))
|
||||||
api.POST("/routes/:route/torrent", apiAddTorrentHandler(s))
|
api.POST("/routes/:route/torrent", apiAddTorrentHandler(s))
|
||||||
|
|
|
@ -16,3 +16,7 @@ var routesHandler = func(ss *torrent.Stats) gin.HandlerFunc {
|
||||||
c.HTML(http.StatusOK, "routes.html", ss.RoutesStats())
|
c.HTML(http.StatusOK, "routes.html", ss.RoutesStats())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var logsHandler = func(c *gin.Context) {
|
||||||
|
c.HTML(http.StatusOK, "logs.html", nil)
|
||||||
|
}
|
||||||
|
|
40
log/log.go
40
log/log.go
|
@ -1,16 +1,50 @@
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/distribyted/distribyted/config"
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Load() {
|
const FileName = "distribyted.log"
|
||||||
|
|
||||||
|
func Load(config *config.Log) {
|
||||||
|
var writers []io.Writer
|
||||||
|
|
||||||
// fix console colors on windows
|
// fix console colors on windows
|
||||||
cso := colorable.NewColorableStdout()
|
cso := colorable.NewColorableStdout()
|
||||||
|
|
||||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: cso})
|
writers = append(writers, zerolog.ConsoleWriter{Out: cso})
|
||||||
|
writers = append(writers, newRollingFile(config))
|
||||||
|
mw := io.MultiWriter(writers...)
|
||||||
|
|
||||||
|
log.Logger = log.Output(mw)
|
||||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
|
||||||
|
l := zerolog.InfoLevel
|
||||||
|
if config.Debug {
|
||||||
|
l = zerolog.DebugLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
zerolog.SetGlobalLevel(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRollingFile(config *config.Log) io.Writer {
|
||||||
|
if err := os.MkdirAll(config.Path, 0744); err != nil {
|
||||||
|
log.Error().Err(err).Str("path", config.Path).Msg("can't create log directory")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &lumberjack.Logger{
|
||||||
|
Filename: filepath.Join(config.Path, FileName),
|
||||||
|
MaxBackups: config.MaxBackups, // files
|
||||||
|
MaxSize: config.MaxSize, // megabytes
|
||||||
|
MaxAge: config.MaxAge, // days
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# This is a configuration file example. You can edit it and add and remove torrents and magnet URIs. Read the following comments for more info.
|
# This is a configuration file example. You can edit it and add and remove torrents
|
||||||
|
# and magnet URIs. Read the following comments for more info.
|
||||||
|
|
||||||
# HTTP specific configuration.
|
# HTTP specific configuration.
|
||||||
http:
|
http:
|
||||||
|
@ -12,21 +13,48 @@ webdav:
|
||||||
|
|
||||||
# Specific configuration for torrent backend.
|
# Specific configuration for torrent backend.
|
||||||
torrent:
|
torrent:
|
||||||
# Size in MB for the cache. This is the maximum space used by distribyted to store torrent data. Less used torrent data will be discarded if this value is reached.
|
# Size in MB for the cache. This is the maximum space used by distribyted to store
|
||||||
|
# torrent data. Less used torrent data will be discarded if this value is reached.
|
||||||
# global_cache_size: -1 #No limit
|
# global_cache_size: -1 #No limit
|
||||||
global_cache_size: 1024
|
global_cache_size: 2048
|
||||||
|
|
||||||
# Folder where distribyted metadata will be stored.
|
# Folder where distribyted metadata will be stored.
|
||||||
metadata_folder: ./distribyted-data/metadata
|
metadata_folder: ./distribyted-data/metadata
|
||||||
# Disable IPv6
|
|
||||||
|
# Disable IPv6.
|
||||||
#disable_ipv6: true
|
#disable_ipv6: true
|
||||||
|
|
||||||
|
# Timeout in seconds when adding a magnet or a torrent.
|
||||||
|
add_timeout: 60
|
||||||
fuse:
|
fuse:
|
||||||
# Folder where fuse will mount torrent filesystem
|
# Folder where fuse will mount torrent filesystem
|
||||||
# For windows users: You can set here also a disk letter like X: or Z:
|
# For windows users: You can set here also a disk letter like X: or Z:
|
||||||
path: ./distribyted-data/mount
|
path: ./distribyted-data/mount
|
||||||
# Add this flag if you want to allow other users to access this fuse mountpoint. You need to add user_allow_other flag to /etc/fuse.conf file.
|
# Add this flag if you want to allow other users to access this fuse mountpoint.
|
||||||
|
# You need to add user_allow_other flag to /etc/fuse.conf file.
|
||||||
# allow_other: true
|
# allow_other: true
|
||||||
|
|
||||||
|
log:
|
||||||
|
path: ./distribyted-data/logs
|
||||||
|
|
||||||
|
# MaxBackups is the maximum number of old log files to retain. The default
|
||||||
|
# is to retain all old log files (though MaxAge may still cause them to get
|
||||||
|
# deleted.)
|
||||||
|
max_backups: 2
|
||||||
|
|
||||||
|
# MaxAge is the maximum number of days to retain old log files based on the
|
||||||
|
# timestamp encoded in their filename. Note that a day is defined as 24
|
||||||
|
# hours and may not exactly correspond to calendar days due to daylight
|
||||||
|
# savings, leap seconds, etc. The default is not to remove old log files
|
||||||
|
# based on age.
|
||||||
|
# max_age: 30
|
||||||
|
|
||||||
|
# MaxSize is the maximum size in megabytes of the log file before it gets
|
||||||
|
# rotated. It defaults to 100 megabytes.
|
||||||
|
max_size: 50
|
||||||
|
|
||||||
|
# debug: true
|
||||||
|
|
||||||
# List of folders where torrents will be mounted as a filesystem.
|
# List of folders where torrents will be mounted as a filesystem.
|
||||||
routes:
|
routes:
|
||||||
- name: multimedia
|
- name: multimedia
|
||||||
|
|
77
templates/logs.html
Normal file
77
templates/logs.html
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
{{template "header.html" "Logs"}}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
|
||||||
|
<body class="header-fixed sidebar-fixed sidebar-dark header-light" id="body">
|
||||||
|
<div class="wrapper">
|
||||||
|
{{template "navbar.html" "logs"}}
|
||||||
|
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<!-- Header -->
|
||||||
|
<header class="main-header " id="header">
|
||||||
|
<nav class="navbar navbar-static-top navbar-expand-lg">
|
||||||
|
<!-- Sidebar toggle button -->
|
||||||
|
<button id="sidebar-toggler" class="sidebar-toggle">
|
||||||
|
<span class="sr-only">Toggle navigation</span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<div class="content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="card card-default" data-scroll-height="1000"
|
||||||
|
style="height: 1000px; overflow: hidden;">
|
||||||
|
<div
|
||||||
|
class="card-header justify-content-between align-items-center card-header-border-bottom">
|
||||||
|
<h2>Logs</h2>
|
||||||
|
</div>
|
||||||
|
<div style="position: relative; overflow: hidden; width: auto; height: 100%;">
|
||||||
|
<div class="card-body"
|
||||||
|
style="overflow: hidden; width: auto; height: 100%; overflow-y: scroll; display: flex; flex-direction: column-reverse;">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col" style="width: 5%">Time</th>
|
||||||
|
<th scope="col" style="width: 5%">Type</th>
|
||||||
|
<th scope="col" style="width: 15%">Component</th>
|
||||||
|
<th scope="col" style="width: 40%">Message</th>
|
||||||
|
<th scope="col" style="width: 35%">Properties</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="log_table">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="slimScrollBar"
|
||||||
|
style="background: rgb(153, 153, 153) none repeat scroll 0% 0%; width: 5px; position: absolute; top: 0px; opacity: 0.4; display: none; border-radius: 7px; z-index: 99; right: 1px; height: 242.986px;">
|
||||||
|
</div>
|
||||||
|
<div class="slimScrollRail"
|
||||||
|
style="width: 5px; height: 100%; position: absolute; top: 0px; display: none; border-radius: 7px; background: rgb(51, 51, 51) none repeat scroll 0% 0%; opacity: 0.2; z-index: 90; right: 1px;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="footer mt-auto">
|
||||||
|
<div class="copyright bg-white">
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{template "footer.html"}}
|
||||||
|
|
||||||
|
<script src="assets/js/logs.js"></script>
|
||||||
|
<script>
|
||||||
|
Distribyted.logs.loadView();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -37,6 +37,16 @@
|
||||||
<span class="nav-text">Routes</span>
|
<span class="nav-text">Routes</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{{if eq . "logs"}}
|
||||||
|
<li class="active">
|
||||||
|
{{else}}
|
||||||
|
<li>
|
||||||
|
{{end}}
|
||||||
|
<a class="sidenav-item-link" href="/logs">
|
||||||
|
<i class="mdi mdi-information-outline"></i>
|
||||||
|
<span class="nav-text">Logs</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<!-- {{if eq . "watched-folders"}}
|
<!-- {{if eq . "watched-folders"}}
|
||||||
<li class="active">
|
<li class="active">
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<div class="card card-default">
|
<div class="card card-default">
|
||||||
<div class="card-header card-header-border-bottom">
|
<div class="card-header card-header-border-bottom">
|
||||||
<h2>Add New Magnet to a Route</h2>
|
<h2>Add New Magnet to a Route</h2>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form id="new-magnet">
|
<form id="new-magnet">
|
||||||
|
@ -43,9 +44,16 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-footer pt-4 pt-5 mt-4 border-top">
|
<div class="form-group">
|
||||||
<button type="submit" class="btn btn-primary btn-default">Add</button>
|
<button type="submit" class="btn btn-primary btn-default">Add</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-footer pt-4 pt-5 mt-4 border-top">
|
||||||
|
<div class="sk-double-bounce" id="submit_magnet_loading" style="display: none;">
|
||||||
|
<div class="double-bounce1"></div>
|
||||||
|
<div class="double-bounce2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/anacrolix/torrent"
|
"github.com/anacrolix/torrent"
|
||||||
"github.com/anacrolix/torrent/metainfo"
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
|
@ -25,11 +26,16 @@ type Service struct {
|
||||||
cfgLoader loader.Loader
|
cfgLoader loader.Loader
|
||||||
db loader.LoaderAdder
|
db loader.LoaderAdder
|
||||||
|
|
||||||
log zerolog.Logger
|
log zerolog.Logger
|
||||||
|
timeout int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(cfg loader.Loader, db loader.LoaderAdder, stats *Stats, c *torrent.Client) *Service {
|
func NewService(cfg loader.Loader, db loader.LoaderAdder, stats *Stats, c *torrent.Client, timeout int) *Service {
|
||||||
l := log.Logger.With().Str("component", "torrent-service").Logger()
|
l := log.Logger.With().Str("component", "torrent-service").Logger()
|
||||||
|
|
||||||
|
if timeout == 0 {
|
||||||
|
timeout = 60
|
||||||
|
}
|
||||||
return &Service{
|
return &Service{
|
||||||
log: l,
|
log: l,
|
||||||
s: stats,
|
s: stats,
|
||||||
|
@ -37,6 +43,7 @@ func NewService(cfg loader.Loader, db loader.LoaderAdder, stats *Stats, c *torre
|
||||||
fss: make(map[string]fs.Filesystem),
|
fss: make(map[string]fs.Filesystem),
|
||||||
cfgLoader: cfg,
|
cfgLoader: cfg,
|
||||||
db: db,
|
db: db,
|
||||||
|
timeout: timeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +121,14 @@ func (s *Service) addTorrent(r string, t *torrent.Torrent) error {
|
||||||
// only get info if name is not available
|
// only get info if name is not available
|
||||||
if t.Info() == nil {
|
if t.Info() == nil {
|
||||||
s.log.Info().Str("hash", t.InfoHash().String()).Msg("getting torrent info")
|
s.log.Info().Str("hash", t.InfoHash().String()).Msg("getting torrent info")
|
||||||
<-t.GotInfo()
|
select {
|
||||||
|
case <-time.After(time.Duration(s.timeout) * time.Second):
|
||||||
|
s.log.Error().Str("hash", t.InfoHash().String()).Msg("timeout getting torrent info")
|
||||||
|
return errors.New("timeout getting torrent info")
|
||||||
|
case <-t.GotInfo():
|
||||||
|
s.log.Info().Str("hash", t.InfoHash().String()).Msg("obtained torrent info")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to stats
|
// Add to stats
|
||||||
|
|
Loading…
Reference in a new issue