-
Notifications
You must be signed in to change notification settings - Fork 6
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
PB-8293 : Implement a new log writer which flushes the logs in every 30 seconds (for real time logs) #397
PB-8293 : Implement a new log writer which flushes the logs in every 30 seconds (for real time logs) #397
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,14 +4,21 @@ import ( | |
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"sync" | ||
"time" | ||
|
||
cmdexec "github.com/portworx/kdmp/pkg/executor" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
const ( | ||
commandExecLogInterval = 30 * time.Second | ||
) | ||
|
||
// BackupSummaryResponse describes single snapshot entry. | ||
type BackupSummaryResponse struct { | ||
ID string `json:"id"` | ||
|
@@ -74,6 +81,34 @@ type backupExecutor struct { | |
lastError error | ||
} | ||
|
||
type logWriter struct { | ||
logger *log.Logger | ||
lastLogTime time.Time | ||
interval time.Duration | ||
mu sync.Mutex | ||
} | ||
|
||
func newLogWriter(l *log.Logger, interval time.Duration) *logWriter { | ||
return &logWriter{ | ||
logger: l, | ||
interval: interval, | ||
} | ||
} | ||
|
||
func (lw *logWriter) Write(p []byte) (n int, err error) { | ||
lw.mu.Lock() | ||
defer lw.mu.Unlock() | ||
|
||
now := time.Now() | ||
// log if the interval has passed since the last log | ||
// this is to avoid bloating the logs | ||
if now.Sub(lw.lastLogTime) >= lw.interval { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will be the behavior if backup gets completed with 30 sec. Nothing will be written ? Say some error in the command line happens and command is done. I am confused about the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First write will not wait for 30secs, it will be written as when it comes. |
||
lw.logger.Println(string(p), time.Now()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a test case showcasing failure of kopia command if it can happen within 30 sec and log appears as expected then we are good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please refer the ticket, have attached for the same |
||
lw.lastLogTime = now | ||
} | ||
return len(p), nil | ||
} | ||
|
||
// GetBackupCommand returns a wrapper over the kopia backup command | ||
func GetBackupCommand(path, repoName, password, provider, sourcePath string) (*Command, error) { | ||
if repoName == "" { | ||
|
@@ -103,8 +138,13 @@ func NewBackupExecutor(cmd *Command) Executor { | |
|
||
func (b *backupExecutor) Run() error { | ||
b.execCmd = b.cmd.BackupCmd() | ||
b.execCmd.Stdout = b.outBuf | ||
b.execCmd.Stderr = b.errBuf | ||
|
||
// Create multi-writers to stream output to both buffer and CLI | ||
stdoutWriter := io.MultiWriter(b.outBuf, newLogWriter(log.New(os.Stdout, "", 0), commandExecLogInterval)) | ||
stderrWriter := io.MultiWriter(b.errBuf, newLogWriter(log.New(os.Stderr, "", 0), commandExecLogInterval)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not doubting the golang primitives here but it would be good if we can check outBuf errBuf are working as expected by checking summary response also populated as expected. A unit test around that would be great. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not doubting , but still doubting :) . Have attached the logs for the same in the ticket. Thanks |
||
|
||
b.execCmd.Stdout = stdoutWriter | ||
b.execCmd.Stderr = stderrWriter | ||
|
||
if err := b.execCmd.Start(); err != nil { | ||
b.lastError = err | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we need this mutex. What is the scenario when write will be called from two parallel path.
Isn't multi writer already takes care of writing to two file descriptor in parallel with synchronization on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though we create two separate objects by calling newLogWriter for stderr and stdout, there is a chance that write can be called by multiple goroutines at the same time if the snapshot create command generates output quickly. It's unlikely to happen, but I have added a mutex for extra integrity of the output data