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

Signals -Log rotate enhancement has been added #266

Open
wants to merge 11 commits into
base: primary
Choose a base branch
from
42 changes: 35 additions & 7 deletions sender/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*
* Copyright (c) 2019-2020 Telenor Norge AS
* Author(s):
* - Roshini Narasimha Raghavan <[email protected]>
* - Kristian Lyngstøl <[email protected]>
* - Håkon Solbjørg <[email protected]>
*
* This library is free software; you can redistribute it and/or
Expand All @@ -26,7 +28,9 @@ package sender
import (
"fmt"
"os"
"os/signal"
"sync"
"syscall"

"github.com/telenornms/skogul"
"github.com/telenornms/skogul/encoder"
Expand All @@ -49,6 +53,7 @@ type File struct {
once sync.Once
f *os.File
c chan []byte
sighup chan os.Signal
}

func (f *File) init() {
Expand Down Expand Up @@ -104,15 +109,38 @@ func (f *File) startChan() {
fileLog.Trace("Starting file writer channel")
// Making sure we close the file if this function exits
defer f.f.Close()
for b := range f.c {
written, err := f.f.Write(append(b, newLineChar))
if err != nil {
f.ok = false
fileLog.WithField("path", f.File).WithError(err).Errorf("Failed to write to file. Wrote %d of %d bytes", written, len(b))
f.sighup = make(chan os.Signal, 1)
signal.Notify(f.sighup, syscall.SIGHUP)
for {
select {
case b := <-f.c:
written, err := f.f.Write(append(b, newLineChar))
if err != nil {
f.ok = false
fileLog.WithField("path", f.File).WithError(err).Errorf("Failed to write to file. Wrote %d of %d bytes", written, len(b))
}
f.f.Sync()
case <-f.sighup:
f.f.Close()
var file *os.File
if finfo, err := os.Stat(f.File); !os.IsNotExist(err) && f.Append {
fileLog.WithField("path", f.File).Trace("File exists, let's open it for writing")
file, err = os.OpenFile(f.File, os.O_APPEND|os.O_WRONLY, finfo.Mode())
f.f = file
f.ok = true
} else {
// Otherwise, create the file (which will truncate it if it already exists)
fileLog.WithField("path", f.File).Trace("Creating file since it doesn't exist or we don't want to append to it")
_, err = os.Create(f.File)
if err != nil {
fmt.Errorf("%s: Error reopening: %s\n", skogul.Now(), err)
f.ok = false
return
}
f.ok = true
}
}
f.f.Sync()
}
fileLog.WithField("path", f.File).Warning("File writer chan closed, not handling any more writes!")
}

// Send receives a skogul container and writes it to file.
Expand Down
67 changes: 67 additions & 0 deletions sender/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*
* Copyright (c) 2019-2020 Telenor Norge AS
* Author(s):
* - Roshini Narasimha Raghavan <[email protected]>
* - Kristian Lyngstøl <[email protected]>
* - Håkon Solbjørg <[email protected]>
*
* This library is free software; you can redistribute it and/or
Expand All @@ -29,6 +31,7 @@ import (
"os"
"path"
"strings"
"syscall"
"testing"
"time"

Expand Down Expand Up @@ -140,3 +143,67 @@ func TestAppendToExistingFile(t *testing.T) {
t.Errorf("Test file does not contain test string 'some data', was it overwritten? Contents: %s", str)
}
}

func TestSignals(t *testing.T) {
filename := "skogul-file-1.txt"
path := path.Join(os.TempDir(), filename)

f, err := os.Create(path)
if err != nil {
t.Error(err)
return
}

f.Write([]byte("some data\n"))
f.Sync()

time.Sleep(time.Second)

sender := &sender.File{
File: path,
Append: false,
}

c := createContainer()

sender.Send(&c)

os.Rename(path, path+".old")

// file is closed and it will just log that file is closed and wrote 0 bytes.
sender.Send(&c)

// the file should not exists since we renamed. If it continues to exists, then fail the test.
if _, err := os.Stat(path); err == nil {
t.Error(err)
t.FailNow()
}

// Since the write is done by a goroutine
// we have to make sure it is properly
// flushed before we try to read it back
time.Sleep(time.Second)
proc, err := os.FindProcess(os.Getpid())
if err != nil {
t.Error(err)
return
}

// provides an interrup signal to the current process handling the file.
if err := proc.Signal(syscall.SIGHUP); err != nil {
t.Error(err)
return
}

// give enough time for the sighup signal to be handled and send the container again. This should reopen the file and write the values sent by the container.
time.Sleep(time.Second * 4)
sender.Send(&c)

// If the file remains closed, then the following commands will fail the test.
_, err = os.Stat(path)
if err != nil {
t.Error(err)
return
}

}