blob: a0937b8eb14a27fbdb45db2223414c862e5bcec6 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tools
import (
"context"
"log"
"os"
"strings"
"time"
)
const (
initialLogSize int = 255
defaultFlushInterval time.Duration = 15 * time.Second
)
// BufferedLogger is a wrapper around the FnAPI logging client meant to be used
// in place of stdout and stderr in bootloader subprocesses. Not intended for
// Beam end users.
type BufferedLogger struct {
logger *Logger
builder strings.Builder
logs []string
lastFlush time.Time
flushInterval time.Duration
periodicFlushContext context.Context
now func() time.Time
}
// NewBufferedLogger returns a new BufferedLogger type by reference.
func NewBufferedLogger(logger *Logger) *BufferedLogger {
return &BufferedLogger{logger: logger, lastFlush: time.Now(), flushInterval: defaultFlushInterval, periodicFlushContext: context.Background(), now: time.Now}
}
// NewBufferedLoggerWithFlushInterval returns a new BufferedLogger type by reference. This type will
// flush logs periodically on Write() calls as well as when Flush*() functions are called.
func NewBufferedLoggerWithFlushInterval(ctx context.Context, logger *Logger, interval time.Duration) *BufferedLogger {
return &BufferedLogger{logger: logger, lastFlush: time.Now(), flushInterval: interval, periodicFlushContext: ctx, now: time.Now}
}
// Write implements the io.Writer interface, converting input to a string
// and storing it in the BufferedLogger's buffer. If a logger is not provided,
// the output is sent directly to os.Stderr.
func (b *BufferedLogger) Write(p []byte) (int, error) {
if b.logger == nil {
return os.Stderr.Write(p)
}
n, err := b.builder.Write(p)
if b.logs == nil {
b.logs = make([]string, 0, initialLogSize)
}
b.logs = append(b.logs, b.builder.String())
b.builder.Reset()
if b.now().Sub(b.lastFlush) > b.flushInterval {
b.FlushAtDebug(b.periodicFlushContext)
}
return n, err
}
// FlushAtError flushes the contents of the buffer to the logging
// service at Error.
func (b *BufferedLogger) FlushAtError(ctx context.Context) {
if b.logger == nil {
return
}
for _, message := range b.logs {
b.logger.Errorf(ctx, "%s", message)
}
b.logs = nil
b.lastFlush = time.Now()
}
// FlushAtDebug flushes the contents of the buffer to the logging
// service at Debug.
func (b *BufferedLogger) FlushAtDebug(ctx context.Context) {
if b.logger == nil {
return
}
for _, message := range b.logs {
b.logger.Printf(ctx, "%s", message)
}
b.logs = nil
b.lastFlush = time.Now()
}
// Prints directly to the logging service. If the logger is nil, prints directly to the
// console. Used for the container pre-build workflow.
func (b *BufferedLogger) Printf(ctx context.Context, format string, args ...any) {
if b.logger == nil {
log.Printf(format, args...)
return
}
b.logger.Printf(ctx, format, args...)
}