Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] Test PR for teamcity link #3808

Closed
wants to merge 11 commits into from
6 changes: 5 additions & 1 deletion cmd/node/config/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,11 @@
MaxOpenFiles = 10

[Logs]
LogFileLifeSpanInSec = 86400
[Logs.LifeSpan]
#available options: second, epoch or megabyte
Type="epoch"
#recreate every: 86400 second, 1 epoch, 10 megabyte etc.
RecreateEvery = 1

[TrieSync]
NumConcurrentTrieSyncers = 200
Expand Down
11 changes: 8 additions & 3 deletions cmd/node/factory/interface.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package factory

import (
"time"

"github.com/ElrondNetwork/elrond-go-core/core"
"github.com/ElrondNetwork/elrond-go-core/data"
"github.com/ElrondNetwork/elrond-go-core/data/typeConverters"
Expand Down Expand Up @@ -40,7 +38,7 @@ type P2PAntifloodHandler interface {

// FileLoggingHandler will handle log file rotation
type FileLoggingHandler interface {
ChangeFileLifeSpan(newDuration time.Duration) error
ChangeFileLifeSpan(LogLifeSpanner) error
Close() error
IsInterfaceNil() bool
}
Expand All @@ -60,3 +58,10 @@ type StatusHandlersUtils interface {
type StatusHandlerUtilsFactory interface {
Create(marshalizer marshal.Marshalizer, converter typeConverters.Uint64ByteSliceConverter) (StatusHandlersUtils, error)
}

// LogLifeSpanner defines a notification channel for the file logging lifespan
type LogLifeSpanner interface {
GetNotification() <-chan string
SetCurrentFile(string)
IsInterfaceNil() bool
}
12 changes: 4 additions & 8 deletions cmd/node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"os"
"runtime"
"time"

"github.com/ElrondNetwork/elrond-go-core/core"
"github.com/ElrondNetwork/elrond-go-core/core/check"
Expand Down Expand Up @@ -91,13 +90,6 @@ func startNodeRunner(c *cli.Context, log logger.Logger, version string) error {
return errCfg
}

if !check.IfNil(fileLogging) {
err := fileLogging.ChangeFileLifeSpan(time.Second * time.Duration(cfgs.GeneralConfig.Logs.LogFileLifeSpanInSec))
if err != nil {
return err
}
}

err := applyFlags(c, cfgs, flagsConfig, log)
if err != nil {
return err
Expand All @@ -110,6 +102,10 @@ func startNodeRunner(c *cli.Context, log logger.Logger, version string) error {
return errRunner
}

if !check.IfNil(fileLogging) {
nodeRunner.SetFileLoggingData(fileLogging)
}

err = nodeRunner.Start()
if err != nil {
log.Error(err.Error())
Expand Down
6 changes: 5 additions & 1 deletion cmd/seednode/config/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
SizeCheckDelta = 10

[Logs]
LogFileLifeSpanInSec = 86400
[Logs.LifeSpan]
#available options: second, epoch or megabyte
Type="epoch"
#recreate every: 86400 second, 1 epoch, 10 megabyte etc.
RecreateEvery = 1
14 changes: 13 additions & 1 deletion cmd/seednode/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ElrondNetwork/elrond-go-core/marshal"
factoryMarshalizer "github.com/ElrondNetwork/elrond-go-core/marshal/factory"
logger "github.com/ElrondNetwork/elrond-go-logger"
"github.com/ElrondNetwork/elrond-go-logger/lifespan"
"github.com/ElrondNetwork/elrond-go/cmd/node/factory"
"github.com/ElrondNetwork/elrond-go/cmd/seednode/api"
"github.com/ElrondNetwork/elrond-go/common"
Expand Down Expand Up @@ -156,7 +157,18 @@ func startNode(ctx *cli.Context) error {
return fmt.Errorf("%w creating a log file", err)
}

err = fileLogging.ChangeFileLifeSpan(time.Second * time.Duration(generalConfig.Logs.LogFileLifeSpanInSec))
args := logger.LogLifeSpanFactoryArgs{
LifeSpanType: "second",
RecreateEvery: generalConfig.Logs.LifeSpan.RecreateEvery,
}

lifeSpanFactory := lifespan.NewTypeLogLifeSpanFactory()
lls, errLifeSpan := lifeSpanFactory.CreateLogLifeSpanner(args)
if errLifeSpan != nil {
return errLifeSpan
}

err = fileLogging.ChangeFileLifeSpan(lls)
if err != nil {
return err
}
Expand Down
96 changes: 63 additions & 33 deletions common/logging/fileLogging.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,66 +10,94 @@ import (
"time"

"github.com/ElrondNetwork/elrond-go-core/core"
"github.com/ElrondNetwork/elrond-go-core/core/check"
logger "github.com/ElrondNetwork/elrond-go-logger"
"github.com/ElrondNetwork/elrond-go-logger/lifespan"
"github.com/ElrondNetwork/elrond-go-logger/redirects"
"github.com/ElrondNetwork/elrond-go/cmd/node/factory"
)

const minFileLifeSpan = time.Second
const defaultFileLifeSpan = time.Hour * 24

var log = logger.GetOrCreate("common/logging")

// fileLogging is able to rotate the log files
type fileLogging struct {
chLifeSpanChanged chan time.Duration
mutFile sync.Mutex
currentFile *os.File
workingDir string
defaultLogsPath string
logFilePrefix string
cancelFunc func()
mutIsClosed sync.Mutex
isClosed bool
logLifeSpanner lifeSpanWrapper
mutFile sync.Mutex
currentFile *os.File
workingDir string
defaultLogsPath string
logFilePrefix string
cancelFunc func()
mutIsClosed sync.Mutex
isClosed bool
}

// NewFileLogging creates a file log watcher used to break the log file into multiple smaller files
func NewFileLogging(workingDir string, defaultLogsPath string, logFilePrefix string) (*fileLogging, error) {
fl := &fileLogging{
workingDir: workingDir,
defaultLogsPath: defaultLogsPath,
logFilePrefix: logFilePrefix,
chLifeSpanChanged: make(chan time.Duration),
isClosed: false,
workingDir: workingDir,
defaultLogsPath: defaultLogsPath,
logFilePrefix: logFilePrefix,
isClosed: false,
}
fl.recreateLogFile()
identifier := ""
fl.recreateLogFile(identifier)

//we need this function as to call file.Close() when the code panics and the defer func associated
//with the file pointer in the main func will never be reached
runtime.SetFinalizer(fl, func(fileLogHandler *fileLogging) {
_ = fileLogHandler.currentFile.Close()
})

lifeSpanFactory := lifespan.NewTypeLogLifeSpanFactory()

lifeSpanner, err := lifeSpanFactory.CreateLogLifeSpanner(logger.LogLifeSpanFactoryArgs{
LifeSpanType: "second",
RecreateEvery: int(defaultFileLifeSpan.Seconds()),
})

if err != nil {
log.Debug("autoRecreateFile NewSecondsLifeSpanner failed", "err", err)
}

startRecreateFile(fl, lifeSpanner)

return fl, nil
}

func startRecreateFile(fl *fileLogging, lifeSpanner logger.LogLifeSpanner) {
if fl.cancelFunc != nil {
fl.cancelFunc()
}

fl.logLifeSpanner.SetLifeSpanner(lifeSpanner)

ctx, cancelFunc := context.WithCancel(context.Background())
go fl.autoRecreateFile(ctx)
fl.cancelFunc = cancelFunc

return fl, nil
fl.logLifeSpanner.SetCurrentFile(fl.currentFile.Name())
}

func (fl *fileLogging) createFile() (*os.File, error) {
func (fl *fileLogging) createFile(identifier string) (*os.File, error) {
logDirectory := filepath.Join(fl.workingDir, fl.defaultLogsPath)

if len(identifier) > 0 {
identifier = fmt.Sprintf("-%s", identifier)
}
return core.CreateFile(
core.ArgCreateFileArgument{
Prefix: fl.logFilePrefix,
Prefix: fmt.Sprintf("%s%s", fl.logFilePrefix, identifier),
Directory: logDirectory,
FileExtension: "log",
},
)
}

func (fl *fileLogging) recreateLogFile() {
newFile, err := fl.createFile()
func (fl *fileLogging) recreateLogFile(identifier string) {
newFile, err := fl.createFile(identifier)
if err != nil {
log.Error("error creating new log file", "error", err)
return
Expand Down Expand Up @@ -102,34 +130,36 @@ func (fl *fileLogging) recreateLogFile() {
}

func (fl *fileLogging) autoRecreateFile(ctx context.Context) {
fileLifeSpan := defaultFileLifeSpan
lifeSpannerChannel := fl.logLifeSpanner.GetNotificationChannel()
for {
select {
case <-ctx.Done():
log.Debug("closing fileLogging.autoRecreateFile go routine")
log.Info("closing fileLogging.autoRecreateFile go routine")
return
case <-time.After(fileLifeSpan):
fl.recreateLogFile()
case fileLifeSpan = <-fl.chLifeSpanChanged:
log.Debug("changed log file span", "new value", fileLifeSpan)
case identifier := <-lifeSpannerChannel:
log.Debug("autoRecreateFile - recreate log from lifeSpanner", "identifier", identifier)
fl.recreateLogFile(identifier)

fl.logLifeSpanner.SetCurrentFile(fl.currentFile.Name())
}
}
}

// ChangeFileLifeSpan changes the log file span
func (fl *fileLogging) ChangeFileLifeSpan(newDuration time.Duration) error {
if newDuration < minFileLifeSpan {
return fmt.Errorf("%w, provided %v", core.ErrInvalidLogFileMinLifeSpan, newDuration)
}

func (fl *fileLogging) ChangeFileLifeSpan(lifeSpanner factory.LogLifeSpanner) error {
fl.mutIsClosed.Lock()
defer fl.mutIsClosed.Unlock()

if fl.isClosed {
return core.ErrFileLoggingProcessIsClosed
}

fl.chLifeSpanChanged <- newDuration
if check.IfNil(lifeSpanner) {
return fmt.Errorf("%w, error: nil lifespan", core.ErrInvalidLogFileMinLifeSpan)
}

startRecreateFile(fl, lifeSpanner)

return nil
}

Expand Down
24 changes: 20 additions & 4 deletions common/logging/fileLogging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (

"github.com/ElrondNetwork/elrond-go-core/core"
"github.com/ElrondNetwork/elrond-go-core/core/check"
logger "github.com/ElrondNetwork/elrond-go-logger"
"github.com/ElrondNetwork/elrond-go-logger/lifespan"
"github.com/ElrondNetwork/elrond-go/testscommon"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -40,7 +43,7 @@ func TestNewFileLogging_CloseShouldStopCreatingLogFiles(t *testing.T) {
}()

fl, _ := NewFileLogging(dir, logsDirectory, "elrond-go")
_ = fl.ChangeFileLifeSpan(time.Second)
_ = fl.ChangeFileLifeSpan(createSecondsLogLifeSpan(time.Second))
time.Sleep(time.Second*3 + time.Millisecond*200)

err := fl.Close()
Expand Down Expand Up @@ -83,7 +86,7 @@ func TestFileLogging_ChangeFileLifeSpanInvalidValueShouldErr(t *testing.T) {
}()

fl, _ := NewFileLogging(dir, logsDirectory, "elrond-go")
err := fl.ChangeFileLifeSpan(time.Millisecond)
err := fl.ChangeFileLifeSpan(createSecondsLogLifeSpan(time.Millisecond))

assert.True(t, errors.Is(err, core.ErrInvalidLogFileMinLifeSpan))
}
Expand All @@ -98,11 +101,24 @@ func TestFileLogging_ChangeFileLifeSpanAfterCloseShouldErr(t *testing.T) {
}()

fl, _ := NewFileLogging(dir, logsDirectory, "elrond-go")
err := fl.ChangeFileLifeSpan(time.Second)
err := fl.ChangeFileLifeSpan(createSecondsLogLifeSpan(time.Second))
assert.Nil(t, err)

_ = fl.Close()

err = fl.ChangeFileLifeSpan(time.Second)
err = fl.ChangeFileLifeSpan(createSecondsLogLifeSpan(time.Second))
assert.True(t, errors.Is(err, core.ErrFileLoggingProcessIsClosed))
}

func createSecondsLogLifeSpan(duration time.Duration) logger.LogLifeSpanner {
args := logger.LogLifeSpanFactoryArgs{
EpochStartNotifierWithConfirm: &testscommon.EpochStartNotifierStub{},
LifeSpanType: "second",
RecreateEvery: int(duration.Seconds()),
}

factory := lifespan.NewTypeLogLifeSpanFactory()
lls, _ := factory.CreateLogLifeSpanner(args)

return lls
}
35 changes: 35 additions & 0 deletions common/logging/lifeSpanWrapper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package logging

import (
"sync"

"github.com/ElrondNetwork/elrond-go/cmd/node/factory"
)

type lifeSpanWrapper struct {
mutLogLifeSpanner sync.Mutex
lifeSpanner factory.LogLifeSpanner
}

// SetLifeSpanner sets the current lifeSpanner
func (llsw *lifeSpanWrapper) SetLifeSpanner(spanner factory.LogLifeSpanner) {
llsw.mutLogLifeSpanner.Lock()
log.Debug("lifeSpanWrapper - set life spanner", "spanner", spanner)
llsw.lifeSpanner = spanner
llsw.mutLogLifeSpanner.Unlock()
}

// SetCurrentFile sets the new logger file
func (llsw *lifeSpanWrapper) SetCurrentFile(newFile string) {
llsw.mutLogLifeSpanner.Lock()
log.Debug("lifeSpanWrapper - set new file", "new file", newFile)
llsw.lifeSpanner.SetCurrentFile(newFile)
llsw.mutLogLifeSpanner.Unlock()
}

// GetNotificationChannel gets the notification channel for the log lifespan
func (llsw *lifeSpanWrapper) GetNotificationChannel() <-chan string {
llsw.mutLogLifeSpanner.Lock()
defer llsw.mutLogLifeSpanner.Unlock()
return llsw.lifeSpanner.GetNotification()
}
8 changes: 7 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,13 @@ type Config struct {

// LogsConfig will hold settings related to the logging sub-system
type LogsConfig struct {
LogFileLifeSpanInSec int
LifeSpan LifeSpanConfig
}

// LifeSpanConfig will hold settings regarding the lifespan of the log
type LifeSpanConfig struct {
Type string
RecreateEvery int
}

// StoragePruningConfig will hold settings related to storage pruning
Expand Down
Loading