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 }