diff --git a/api/runner/func_logger.go b/api/runner/func_logger.go index 75c3d1456..62a784ab8 100644 --- a/api/runner/func_logger.go +++ b/api/runner/func_logger.go @@ -1,18 +1,18 @@ package runner import ( - "bufio" - "fmt" + "bytes" + "context" + "errors" "io" - "context" "github.com/Sirupsen/logrus" "gitlab-odx.oracle.com/odx/functions/api/models" "gitlab-odx.oracle.com/odx/functions/api/runner/common" ) type FuncLogger interface { - Writer(ctx context.Context, appName, path, image, reqID string) io.Writer + Writer(ctx context.Context, appName, path, image, reqID string) io.WriteCloser } // FuncLogger reads STDERR output from a container and outputs it in a parsed structured log format, see: https://github.com/treeder/functions/issues/76 @@ -24,30 +24,61 @@ func NewFuncLogger(logDB models.FnLog) FuncLogger { return &DefaultFuncLogger{logDB} } -func (l *DefaultFuncLogger) persistLog(ctx context.Context, log logrus.FieldLogger, reqID, logText string) { - err := l.logDB.InsertLog(ctx, reqID, logText) - if err != nil { - log.WithError(err).Println(fmt.Sprintf( - "Unable to persist log for call %v. Error: %v", reqID, err)) +type writer struct { + bytes.Buffer + + db models.FnLog + ctx context.Context + reqID string + appName string + image string + path string +} + +func (w *writer) Close() error { + return w.db.InsertLog(context.TODO(), w.reqID, w.String()) +} + +func (w *writer) Write(b []byte) (int, error) { + n, err := w.Buffer.Write(b) + + // for now, also write to stderr so we can debug quick ;) + // TODO this should be a separate FuncLogger but time is running short ! + //log := common.Logger(w.ctx) + //log = log.WithFields(logrus.Fields{"user_log": true, "app_name": w.appName, + //"path": w.path, "image": w.image, "call_id": w.reqID}) + //log.Println(string(b)) + + return n, err +} + +// overrides Write, keeps Close +type limitWriter struct { + n, max int + io.WriteCloser +} + +func newLimitWriter(max int, w io.WriteCloser) io.WriteCloser { + return &limitWriter{max: max, WriteCloser: w} +} + +func (l *limitWriter) Write(b []byte) (int, error) { + if l.n > l.max { + return 0, errors.New("max log size exceeded, truncating log") } + n, err := l.WriteCloser.Write(b) + l.n += n + return n, err } -func (l *DefaultFuncLogger) Writer(ctx context.Context, appName, path, image, reqID string) io.Writer { - r, w := io.Pipe() - - go func(reader io.Reader) { - log := common.Logger(ctx) - log = log.WithFields(logrus.Fields{"user_log": true, "app_name": appName, - "path": path, "image": image, "call_id": reqID}) - - var res string - errMsg := "-------Unable to get full log, it's too big-------" - fmt.Fscanf(reader, "%v", &res) - if len(res) >= bufio.MaxScanTokenSize { - res = res[0:bufio.MaxScanTokenSize - len(errMsg)] + errMsg - } - - l.persistLog(ctx, log, reqID, res) - }(r) - return w +func (l *DefaultFuncLogger) Writer(ctx context.Context, appName, path, image, reqID string) io.WriteCloser { + const MB = 1 * 1024 * 1024 + return newLimitWriter(MB, &writer{ + db: l.logDB, + ctx: ctx, + appName: appName, + path: path, + image: image, + reqID: reqID, + }) } diff --git a/api/runner/runner.go b/api/runner/runner.go index 91d981ad6..fad4ef251 100644 --- a/api/runner/runner.go +++ b/api/runner/runner.go @@ -173,6 +173,7 @@ func (r *Runner) run(ctx context.Context, cfg *task.Config) (drivers.RunResult, } cfg.Stderr = r.flog.Writer(ctx, cfg.AppName, cfg.Path, cfg.Image, cfg.ID) + defer cfg.Stderr.Close() // TODO we should prob log this err but hey if cfg.Stdout == nil { cfg.Stdout = cfg.Stderr } diff --git a/api/runner/task/task.go b/api/runner/task/task.go index ed5b9c768..37def5e52 100644 --- a/api/runner/task/task.go +++ b/api/runner/task/task.go @@ -24,7 +24,7 @@ type Config struct { Stdin io.Reader Stdout io.Writer - Stderr io.Writer + Stderr io.WriteCloser // closer for flushy poo } // Request stores the task to be executed, It holds in itself the channel to