mirror of
https://github.com/goharbor/harbor
synced 2025-04-18 22:36:10 +00:00

add config item to set log forward endpoint fallback the audit log to default log when endpoint in error Signed-off-by: stonezdj <stonezdj@gmail.com>
368 lines
8.0 KiB
Go
368 lines
8.0 KiB
Go
// Copyright Project Harbor Authors
|
|
//
|
|
// Licensed 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 log
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// NOTE: the default depth for the logger is 3 so that we can get the correct file and line when use the logger to log message
|
|
var logger = New(os.Stdout, NewTextFormatter(), WarningLevel, 3)
|
|
|
|
const srcSeparator = "harbor" + string(os.PathSeparator) + "src"
|
|
|
|
func init() {
|
|
lvl := os.Getenv("LOG_LEVEL")
|
|
if len(lvl) == 0 {
|
|
logger.setLevel(InfoLevel)
|
|
return
|
|
}
|
|
|
|
level, err := parseLevel(lvl)
|
|
if err != nil {
|
|
logger.setLevel(InfoLevel)
|
|
return
|
|
}
|
|
|
|
logger.setLevel(level)
|
|
}
|
|
|
|
// Fields type alias to map[string]interface{}
|
|
type Fields = map[string]interface{}
|
|
|
|
// Logger provides a struct with fields that describe the details of logger.
|
|
type Logger struct {
|
|
out io.Writer
|
|
fmtter Formatter
|
|
lvl Level
|
|
callDepth int
|
|
skipLine bool
|
|
fields map[string]interface{}
|
|
fieldsStr string
|
|
mu *sync.Mutex // ptr here to share one sync.Mutex for clone method
|
|
fallback *Logger // fallback logger when current out fail
|
|
}
|
|
|
|
// New returns a customized Logger
|
|
func New(out io.Writer, fmtter Formatter, lvl Level, options ...interface{}) *Logger {
|
|
// Default set to be 3
|
|
depth := 3
|
|
// If passed in as option, then reset depth
|
|
// Use index 0
|
|
if len(options) > 0 {
|
|
d, ok := options[0].(int)
|
|
if ok && d > 0 {
|
|
depth = d
|
|
}
|
|
}
|
|
|
|
return &Logger{
|
|
out: out,
|
|
fmtter: fmtter,
|
|
lvl: lvl,
|
|
callDepth: depth,
|
|
fields: map[string]interface{}{},
|
|
mu: &sync.Mutex{},
|
|
}
|
|
}
|
|
|
|
// DefaultLogger returns the default logger within the pkg, i.e. the one used in log.Infof....
|
|
func DefaultLogger() *Logger {
|
|
return logger
|
|
}
|
|
|
|
// SetFallback enable fallback when error happen
|
|
func (l *Logger) SetFallback(logger *Logger) {
|
|
l.fallback = logger
|
|
}
|
|
|
|
func (l *Logger) clone() *Logger {
|
|
return &Logger{
|
|
out: l.out,
|
|
fmtter: l.fmtter,
|
|
lvl: l.lvl,
|
|
callDepth: l.callDepth,
|
|
skipLine: l.skipLine,
|
|
fields: l.fields,
|
|
fieldsStr: l.fieldsStr,
|
|
mu: l.mu,
|
|
}
|
|
}
|
|
|
|
// WithDepth returns cloned logger with new depth
|
|
func (l *Logger) WithDepth(depth int) *Logger {
|
|
r := l.clone()
|
|
r.callDepth = depth
|
|
|
|
return r
|
|
}
|
|
|
|
// WithFields returns cloned logger which fields merged with the new fields
|
|
func (l *Logger) WithFields(fields Fields) *Logger {
|
|
r := l.clone()
|
|
|
|
if len(fields) > 0 {
|
|
copyFields := make(map[string]interface{}, len(l.fields)+len(fields))
|
|
for key, value := range l.fields {
|
|
copyFields[key] = value
|
|
}
|
|
for key, value := range fields {
|
|
copyFields[key] = value
|
|
}
|
|
|
|
sortedKeys := make([]string, 0, len(copyFields))
|
|
for key := range copyFields {
|
|
sortedKeys = append(sortedKeys, key)
|
|
}
|
|
sort.Strings(sortedKeys)
|
|
|
|
parts := make([]string, 0, len(copyFields))
|
|
for _, key := range sortedKeys {
|
|
parts = append(parts, fmt.Sprintf(`%v="%v"`, key, copyFields[key]))
|
|
}
|
|
|
|
r.fields = copyFields
|
|
r.fieldsStr = "[" + strings.Join(parts, " ") + "]"
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
// WithField returns cloned logger which fields merged with field key=value
|
|
func (l *Logger) WithField(key string, value interface{}) *Logger {
|
|
return l.WithFields(Fields{key: value})
|
|
}
|
|
|
|
// SetOutput sets the output of Logger l
|
|
func (l *Logger) SetOutput(out io.Writer) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
l.out = out
|
|
}
|
|
|
|
// setFormatter sets the formatter of Logger l
|
|
func (l *Logger) setFormatter(fmtter Formatter) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
l.fmtter = fmtter
|
|
}
|
|
|
|
// setLevel sets the level of Logger l
|
|
func (l *Logger) setLevel(lvl Level) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
l.lvl = lvl
|
|
}
|
|
|
|
func (l *Logger) output(record *Record) (err error) {
|
|
b, err := l.fmtter.Format(record)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer func() {
|
|
if err := recover(); err != nil && l.fallback != nil {
|
|
_ = l.fallback.output(record)
|
|
}
|
|
}()
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
_, err = l.out.Write(b)
|
|
if err != nil && l.fallback != nil {
|
|
_ = l.fallback.output(record)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Debug ...
|
|
func (l *Logger) Debug(v ...interface{}) {
|
|
if l.lvl <= DebugLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprint(v...), l.getLine(), DebugLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Debugf ...
|
|
func (l *Logger) Debugf(format string, v ...interface{}) {
|
|
if l.lvl <= DebugLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), l.getLine(), DebugLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Info ...
|
|
func (l *Logger) Info(v ...interface{}) {
|
|
if l.lvl <= InfoLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprint(v...), l.getLine(), InfoLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Infof ...
|
|
func (l *Logger) Infof(format string, v ...interface{}) {
|
|
if l.lvl <= InfoLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), l.getLine(), InfoLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Warning ...
|
|
func (l *Logger) Warning(v ...interface{}) {
|
|
if l.lvl <= WarningLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprint(v...), l.getLine(), WarningLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Warningf ...
|
|
func (l *Logger) Warningf(format string, v ...interface{}) {
|
|
if l.lvl <= WarningLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), l.getLine(), WarningLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Error ...
|
|
func (l *Logger) Error(v ...interface{}) {
|
|
if l.lvl <= ErrorLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprint(v...), l.getLine(), ErrorLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Errorf ...
|
|
func (l *Logger) Errorf(format string, v ...interface{}) {
|
|
if l.lvl <= ErrorLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), l.getLine(), ErrorLevel)
|
|
_ = l.output(record)
|
|
}
|
|
}
|
|
|
|
// Fatal ...
|
|
func (l *Logger) Fatal(v ...interface{}) {
|
|
if l.lvl <= FatalLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprint(v...), l.getLine(), FatalLevel)
|
|
_ = l.output(record)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatalf ...
|
|
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
|
if l.lvl <= FatalLevel {
|
|
record := NewRecord(time.Now(), fmt.Sprintf(format, v...), l.getLine(), FatalLevel)
|
|
_ = l.output(record)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
// GetLevel returns the verbosity level of this logger
|
|
func (l *Logger) GetLevel() Level {
|
|
return l.lvl
|
|
}
|
|
|
|
func (l *Logger) getLine() string {
|
|
var str string
|
|
if !l.skipLine {
|
|
str = line(l.callDepth)
|
|
}
|
|
|
|
str = str + l.fieldsStr
|
|
|
|
if str != "" {
|
|
str = str + ":"
|
|
}
|
|
|
|
return str
|
|
}
|
|
|
|
// Debug ...
|
|
func Debug(v ...interface{}) {
|
|
logger.WithDepth(4).Debug(v...)
|
|
}
|
|
|
|
// Debugf ...
|
|
func Debugf(format string, v ...interface{}) {
|
|
logger.WithDepth(4).Debugf(format, v...)
|
|
}
|
|
|
|
// Info ...
|
|
func Info(v ...interface{}) {
|
|
logger.WithDepth(4).Info(v...)
|
|
}
|
|
|
|
// Infof ...
|
|
func Infof(format string, v ...interface{}) {
|
|
logger.WithDepth(4).Infof(format, v...)
|
|
}
|
|
|
|
// Warning ...
|
|
func Warning(v ...interface{}) {
|
|
logger.WithDepth(4).Warning(v...)
|
|
}
|
|
|
|
// Warningf ...
|
|
func Warningf(format string, v ...interface{}) {
|
|
logger.WithDepth(4).Warningf(format, v...)
|
|
}
|
|
|
|
// Error ...
|
|
func Error(v ...interface{}) {
|
|
logger.WithDepth(4).Error(v...)
|
|
}
|
|
|
|
// Errorf ...
|
|
func Errorf(format string, v ...interface{}) {
|
|
logger.WithDepth(4).Errorf(format, v...)
|
|
}
|
|
|
|
// Fatal ...
|
|
func Fatal(v ...interface{}) {
|
|
logger.WithDepth(4).Fatal(v...)
|
|
}
|
|
|
|
// Fatalf ...
|
|
func Fatalf(format string, v ...interface{}) {
|
|
logger.WithDepth(4).Fatalf(format, v...)
|
|
}
|
|
|
|
// GetLevel return the verbosity level of default logger
|
|
func GetLevel() Level {
|
|
return logger.GetLevel()
|
|
}
|
|
|
|
func line(callDepth int) string {
|
|
_, file, line, ok := runtime.Caller(callDepth)
|
|
if !ok {
|
|
file = "???"
|
|
|
|
line = 0
|
|
}
|
|
l := strings.SplitN(file, srcSeparator, 2)
|
|
if len(l) > 1 {
|
|
file = l[1]
|
|
}
|
|
return fmt.Sprintf("[%s:%d]", file, line)
|
|
}
|