github.com/minio/console@v1.4.1/pkg/logger/logonce.go (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2022 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package logger
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"net/http"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  // Holds a map of recently logged errors.
    28  type logOnceType struct {
    29  	IDMap map[interface{}]error
    30  	sync.Mutex
    31  }
    32  
    33  // One log message per errors.
    34  func (l *logOnceType) logOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) {
    35  	if err == nil {
    36  		return
    37  	}
    38  	l.Lock()
    39  	shouldLog := false
    40  	prevErr := l.IDMap[id]
    41  	if prevErr == nil {
    42  		l.IDMap[id] = err
    43  		shouldLog = true
    44  	} else if prevErr.Error() != err.Error() {
    45  		l.IDMap[id] = err
    46  		shouldLog = true
    47  	}
    48  	l.Unlock()
    49  
    50  	if shouldLog {
    51  		LogIf(ctx, err, errKind...)
    52  	}
    53  }
    54  
    55  // Cleanup the map every 30 minutes so that the log message is printed again for the user to notice.
    56  func (l *logOnceType) cleanupRoutine() {
    57  	for {
    58  		l.Lock()
    59  		l.IDMap = make(map[interface{}]error)
    60  		l.Unlock()
    61  
    62  		time.Sleep(30 * time.Minute)
    63  	}
    64  }
    65  
    66  // Returns logOnceType
    67  func newLogOnceType() *logOnceType {
    68  	l := &logOnceType{IDMap: make(map[interface{}]error)}
    69  	go l.cleanupRoutine()
    70  	return l
    71  }
    72  
    73  var logOnce = newLogOnceType()
    74  
    75  // LogOnceIf - Logs notification errors - once per errors.
    76  // id is a unique identifier for related log messages, refer to cmd/notification.go
    77  // on how it is used.
    78  func LogOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) {
    79  	if err == nil {
    80  		return
    81  	}
    82  
    83  	if errors.Is(err, context.Canceled) {
    84  		return
    85  	}
    86  
    87  	if err.Error() == http.ErrServerClosed.Error() || err.Error() == "disk not found" {
    88  		return
    89  	}
    90  
    91  	logOnce.logOnceIf(ctx, err, id, errKind...)
    92  }