github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/consolelogger.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "container/ring" 22 "context" 23 "sync" 24 "sync/atomic" 25 26 "github.com/minio/madmin-go/v3" 27 "github.com/minio/minio/internal/logger" 28 "github.com/minio/minio/internal/logger/target/console" 29 "github.com/minio/minio/internal/logger/target/types" 30 "github.com/minio/minio/internal/pubsub" 31 "github.com/minio/pkg/v2/logger/message/log" 32 xnet "github.com/minio/pkg/v2/net" 33 ) 34 35 // number of log messages to buffer 36 const defaultLogBufferCount = 10000 37 38 // HTTPConsoleLoggerSys holds global console logger state 39 type HTTPConsoleLoggerSys struct { 40 totalMessages int64 41 failedMessages int64 42 43 sync.RWMutex 44 pubsub *pubsub.PubSub[log.Info, madmin.LogMask] 45 console *console.Target 46 nodeName string 47 logBuf *ring.Ring 48 } 49 50 // NewConsoleLogger - creates new HTTPConsoleLoggerSys with all nodes subscribed to 51 // the console logging pub sub system 52 func NewConsoleLogger(ctx context.Context) *HTTPConsoleLoggerSys { 53 return &HTTPConsoleLoggerSys{ 54 pubsub: pubsub.New[log.Info, madmin.LogMask](8), 55 console: console.New(), 56 logBuf: ring.New(defaultLogBufferCount), 57 } 58 } 59 60 // IsOnline always true in case of console logger 61 func (sys *HTTPConsoleLoggerSys) IsOnline(_ context.Context) bool { 62 return true 63 } 64 65 // SetNodeName - sets the node name if any after distributed setup has initialized 66 func (sys *HTTPConsoleLoggerSys) SetNodeName(nodeName string) { 67 if !globalIsDistErasure { 68 sys.nodeName = "" 69 return 70 } 71 72 host, err := xnet.ParseHost(globalLocalNodeName) 73 if err != nil { 74 logger.FatalIf(err, "Unable to start console logging subsystem") 75 } 76 77 sys.nodeName = host.Name 78 } 79 80 // HasLogListeners returns true if console log listeners are registered 81 // for this node or peers 82 func (sys *HTTPConsoleLoggerSys) HasLogListeners() bool { 83 return sys != nil && sys.pubsub.Subscribers() > 0 84 } 85 86 // Subscribe starts console logging for this node. 87 func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan log.Info, doneCh <-chan struct{}, node string, last int, logKind madmin.LogMask, filter func(entry log.Info) bool) error { 88 // Enable console logging for remote client. 89 if !sys.HasLogListeners() { 90 logger.AddSystemTarget(GlobalContext, sys) 91 } 92 93 cnt := 0 94 // by default send all console logs in the ring buffer unless node or limit query parameters 95 // are set. 96 var lastN []log.Info 97 if last > defaultLogBufferCount || last <= 0 { 98 last = defaultLogBufferCount 99 } 100 101 lastN = make([]log.Info, last) 102 sys.RLock() 103 sys.logBuf.Do(func(p interface{}) { 104 if p != nil { 105 lg, ok := p.(log.Info) 106 if ok && lg.SendLog(node, logKind) { 107 lastN[cnt%last] = lg 108 cnt++ 109 } 110 } 111 }) 112 sys.RUnlock() 113 // send last n console log messages in order filtered by node 114 if cnt > 0 { 115 for i := 0; i < last; i++ { 116 entry := lastN[(cnt+i)%last] 117 if (entry == log.Info{}) { 118 continue 119 } 120 select { 121 case subCh <- entry: 122 case <-doneCh: 123 return nil 124 } 125 } 126 } 127 return sys.pubsub.Subscribe(madmin.LogMaskAll, subCh, doneCh, filter) 128 } 129 130 // Init if HTTPConsoleLoggerSys is valid, always returns nil right now 131 func (sys *HTTPConsoleLoggerSys) Init(_ context.Context) error { 132 return nil 133 } 134 135 // Endpoint - dummy function for interface compatibility 136 func (sys *HTTPConsoleLoggerSys) Endpoint() string { 137 return sys.console.Endpoint() 138 } 139 140 // String - stringer function for interface compatibility 141 func (sys *HTTPConsoleLoggerSys) String() string { 142 return logger.ConsoleLoggerTgt 143 } 144 145 // Stats returns the target statistics. 146 func (sys *HTTPConsoleLoggerSys) Stats() types.TargetStats { 147 return types.TargetStats{ 148 TotalMessages: atomic.LoadInt64(&sys.totalMessages), 149 FailedMessages: atomic.LoadInt64(&sys.failedMessages), 150 QueueLength: 0, 151 } 152 } 153 154 // Content returns the console stdout log 155 func (sys *HTTPConsoleLoggerSys) Content() (logs []log.Entry) { 156 sys.RLock() 157 sys.logBuf.Do(func(p interface{}) { 158 if p != nil { 159 lg, ok := p.(log.Info) 160 if ok { 161 if (lg.Entry != log.Entry{}) { 162 logs = append(logs, lg.Entry) 163 } 164 } 165 } 166 }) 167 sys.RUnlock() 168 169 return 170 } 171 172 // Cancel - cancels the target 173 func (sys *HTTPConsoleLoggerSys) Cancel() { 174 } 175 176 // Type - returns type of the target 177 func (sys *HTTPConsoleLoggerSys) Type() types.TargetType { 178 return types.TargetConsole 179 } 180 181 // Send log message 'e' to console and publish to console 182 // log pubsub system 183 func (sys *HTTPConsoleLoggerSys) Send(ctx context.Context, entry interface{}) error { 184 var lg log.Info 185 switch e := entry.(type) { 186 case log.Entry: 187 lg = log.Info{Entry: e, NodeName: sys.nodeName} 188 case string: 189 lg = log.Info{ConsoleMsg: e, NodeName: sys.nodeName} 190 } 191 atomic.AddInt64(&sys.totalMessages, 1) 192 193 sys.pubsub.Publish(lg) 194 sys.Lock() 195 // add log to ring buffer 196 sys.logBuf.Value = lg 197 sys.logBuf = sys.logBuf.Next() 198 sys.Unlock() 199 err := sys.console.Send(entry) 200 if err != nil { 201 atomic.AddInt64(&sys.failedMessages, 1) 202 } 203 return err 204 }