storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/logger/logonce.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2018 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    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 error.
    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 {
    45  		if prevErr.Error() != err.Error() {
    46  			l.IDMap[id] = err
    47  			shouldLog = true
    48  		}
    49  	}
    50  	l.Unlock()
    51  
    52  	if shouldLog {
    53  		LogIf(ctx, err, errKind...)
    54  	}
    55  }
    56  
    57  // Cleanup the map every 30 minutes so that the log message is printed again for the user to notice.
    58  func (l *logOnceType) cleanupRoutine() {
    59  	for {
    60  		l.Lock()
    61  		l.IDMap = make(map[interface{}]error)
    62  		l.Unlock()
    63  
    64  		time.Sleep(30 * time.Minute)
    65  	}
    66  }
    67  
    68  // Returns logOnceType
    69  func newLogOnceType() *logOnceType {
    70  	l := &logOnceType{IDMap: make(map[interface{}]error)}
    71  	go l.cleanupRoutine()
    72  	return l
    73  }
    74  
    75  var logOnce = newLogOnceType()
    76  
    77  // LogOnceIf - Logs notification errors - once per error.
    78  // id is a unique identifier for related log messages, refer to cmd/notification.go
    79  // on how it is used.
    80  func LogOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) {
    81  	if err == nil {
    82  		return
    83  	}
    84  
    85  	if errors.Is(err, context.Canceled) {
    86  		return
    87  	}
    88  
    89  	if err.Error() == http.ErrServerClosed.Error() || err.Error() == "disk not found" {
    90  		return
    91  	}
    92  
    93  	logOnce.logOnceIf(ctx, err, id, errKind...)
    94  }