mirror of
				https://github.com/minio/minio.git
				synced 2025-11-04 10:11:09 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			159 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (c) 2015-2021 MinIO, Inc.
 | 
						|
//
 | 
						|
// This file is part of MinIO Object Storage stack
 | 
						|
//
 | 
						|
// This program is free software: you can redistribute it and/or modify
 | 
						|
// it under the terms of the GNU Affero General Public License as published by
 | 
						|
// the Free Software Foundation, either version 3 of the License, or
 | 
						|
// (at your option) any later version.
 | 
						|
//
 | 
						|
// This program is distributed in the hope that it will be useful
 | 
						|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
// GNU Affero General Public License for more details.
 | 
						|
//
 | 
						|
// You should have received a copy of the GNU Affero General Public License
 | 
						|
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
package logger
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
// LogOnce provides the function type for logger.LogOnceIf() function
 | 
						|
type LogOnce func(ctx context.Context, err error, id string, errKind ...interface{})
 | 
						|
 | 
						|
type onceErr struct {
 | 
						|
	Err   error
 | 
						|
	Count int
 | 
						|
}
 | 
						|
 | 
						|
// Holds a map of recently logged errors.
 | 
						|
type logOnceType struct {
 | 
						|
	IDMap map[string]onceErr
 | 
						|
	sync.Mutex
 | 
						|
}
 | 
						|
 | 
						|
func (l *logOnceType) logOnceConsoleIf(ctx context.Context, err error, id string, errKind ...interface{}) {
 | 
						|
	if err == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	nerr := unwrapErrs(err)
 | 
						|
	l.Lock()
 | 
						|
	shouldLog := true
 | 
						|
	prev, ok := l.IDMap[id]
 | 
						|
	if !ok {
 | 
						|
		l.IDMap[id] = onceErr{
 | 
						|
			Err:   nerr,
 | 
						|
			Count: 1,
 | 
						|
		}
 | 
						|
	} else if prev.Err.Error() == nerr.Error() {
 | 
						|
		// if errors are equal do not log.
 | 
						|
		prev.Count++
 | 
						|
		l.IDMap[id] = prev
 | 
						|
		shouldLog = false
 | 
						|
	}
 | 
						|
	l.Unlock()
 | 
						|
 | 
						|
	if shouldLog {
 | 
						|
		consoleLogIf(ctx, err, errKind...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
const unwrapErrsDepth = 3
 | 
						|
 | 
						|
// unwrapErrs upto the point where errors.Unwrap(err) returns nil
 | 
						|
func unwrapErrs(err error) (leafErr error) {
 | 
						|
	uerr := errors.Unwrap(err)
 | 
						|
	depth := 1
 | 
						|
	for uerr != nil {
 | 
						|
		// Save the current `uerr`
 | 
						|
		leafErr = uerr
 | 
						|
		// continue to look for leaf errors underneath
 | 
						|
		uerr = errors.Unwrap(leafErr)
 | 
						|
		depth++
 | 
						|
		if depth == unwrapErrsDepth {
 | 
						|
			// If we have reached enough depth we
 | 
						|
			// do not further recurse down, this
 | 
						|
			// is done to avoid any unnecessary
 | 
						|
			// latencies this might bring.
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if uerr == nil {
 | 
						|
		leafErr = err
 | 
						|
	}
 | 
						|
	return leafErr
 | 
						|
}
 | 
						|
 | 
						|
// One log message per error.
 | 
						|
func (l *logOnceType) logOnceIf(ctx context.Context, err error, id string, errKind ...interface{}) {
 | 
						|
	if err == nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	nerr := unwrapErrs(err)
 | 
						|
	l.Lock()
 | 
						|
	shouldLog := true
 | 
						|
	prev, ok := l.IDMap[id]
 | 
						|
	if !ok {
 | 
						|
		l.IDMap[id] = onceErr{
 | 
						|
			Err:   nerr,
 | 
						|
			Count: 1,
 | 
						|
		}
 | 
						|
	} else if prev.Err.Error() == nerr.Error() {
 | 
						|
		// if errors are equal do not log.
 | 
						|
		prev.Count++
 | 
						|
		l.IDMap[id] = prev
 | 
						|
		shouldLog = false
 | 
						|
	}
 | 
						|
	l.Unlock()
 | 
						|
 | 
						|
	if shouldLog {
 | 
						|
		logIf(ctx, err, errKind...)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Cleanup the map every one hour so that the log message is printed again for the user to notice.
 | 
						|
func (l *logOnceType) cleanupRoutine() {
 | 
						|
	for {
 | 
						|
		time.Sleep(time.Hour)
 | 
						|
 | 
						|
		l.Lock()
 | 
						|
		l.IDMap = make(map[string]onceErr)
 | 
						|
		l.Unlock()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Returns logOnceType
 | 
						|
func newLogOnceType() *logOnceType {
 | 
						|
	l := &logOnceType{IDMap: make(map[string]onceErr)}
 | 
						|
	go l.cleanupRoutine()
 | 
						|
	return l
 | 
						|
}
 | 
						|
 | 
						|
var logOnce = newLogOnceType()
 | 
						|
 | 
						|
// LogOnceIf - Logs notification errors - once per error.
 | 
						|
// id is a unique identifier for related log messages, refer to cmd/notification.go
 | 
						|
// on how it is used.
 | 
						|
func LogOnceIf(ctx context.Context, err error, id string, errKind ...interface{}) {
 | 
						|
	if logIgnoreError(err) {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	logOnce.logOnceIf(ctx, err, id, errKind...)
 | 
						|
}
 | 
						|
 | 
						|
// LogOnceConsoleIf - similar to LogOnceIf but exclusively only logs to console target.
 | 
						|
func LogOnceConsoleIf(ctx context.Context, err error, id string, errKind ...interface{}) {
 | 
						|
	if logIgnoreError(err) {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	logOnce.logOnceConsoleIf(ctx, err, id, errKind...)
 | 
						|
}
 |