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

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 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 cmd
    18  
    19  import (
    20  	ring "container/ring"
    21  	"context"
    22  	"sync"
    23  
    24  	"storj.io/minio/cmd/logger"
    25  	"storj.io/minio/cmd/logger/message/log"
    26  	"storj.io/minio/cmd/logger/target/console"
    27  	xnet "storj.io/minio/pkg/net"
    28  	"storj.io/minio/pkg/pubsub"
    29  )
    30  
    31  // number of log messages to buffer
    32  const defaultLogBufferCount = 10000
    33  
    34  //HTTPConsoleLoggerSys holds global console logger state
    35  type HTTPConsoleLoggerSys struct {
    36  	sync.RWMutex
    37  	pubsub   *pubsub.PubSub
    38  	console  *console.Target
    39  	nodeName string
    40  	logBuf   *ring.Ring
    41  }
    42  
    43  // NewConsoleLogger - creates new HTTPConsoleLoggerSys with all nodes subscribed to
    44  // the console logging pub sub system
    45  func NewConsoleLogger(ctx context.Context) *HTTPConsoleLoggerSys {
    46  	ps := pubsub.New()
    47  	return &HTTPConsoleLoggerSys{
    48  		pubsub:  ps,
    49  		console: console.New(),
    50  		logBuf:  ring.New(defaultLogBufferCount),
    51  	}
    52  }
    53  
    54  // SetNodeName - sets the node name if any after distributed setup has initialized
    55  func (sys *HTTPConsoleLoggerSys) SetNodeName(nodeName string) {
    56  	if !globalIsDistErasure {
    57  		sys.nodeName = ""
    58  		return
    59  	}
    60  
    61  	host, err := xnet.ParseHost(globalLocalNodeName)
    62  	if err != nil {
    63  		logger.FatalIf(err, "Unable to start console logging subsystem")
    64  	}
    65  
    66  	sys.nodeName = host.Name
    67  }
    68  
    69  // HasLogListeners returns true if console log listeners are registered
    70  // for this node or peers
    71  func (sys *HTTPConsoleLoggerSys) HasLogListeners() bool {
    72  	return sys != nil && sys.pubsub.NumSubscribers() > 0
    73  }
    74  
    75  // Subscribe starts console logging for this node.
    76  func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan interface{}, doneCh <-chan struct{}, node string, last int, logKind string, filter func(entry interface{}) bool) {
    77  	// Enable console logging for remote client.
    78  	if !sys.HasLogListeners() {
    79  		logger.AddTarget(sys)
    80  	}
    81  
    82  	cnt := 0
    83  	// by default send all console logs in the ring buffer unless node or limit query parameters
    84  	// are set.
    85  	var lastN []log.Info
    86  	if last > defaultLogBufferCount || last <= 0 {
    87  		last = defaultLogBufferCount
    88  	}
    89  
    90  	lastN = make([]log.Info, last)
    91  	sys.RLock()
    92  	sys.logBuf.Do(func(p interface{}) {
    93  		if p != nil {
    94  			lg, ok := p.(log.Info)
    95  			if ok && lg.SendLog(node, logKind) {
    96  				lastN[cnt%last] = lg
    97  				cnt++
    98  			}
    99  		}
   100  	})
   101  	sys.RUnlock()
   102  	// send last n console log messages in order filtered by node
   103  	if cnt > 0 {
   104  		for i := 0; i < last; i++ {
   105  			entry := lastN[(cnt+i)%last]
   106  			if (entry == log.Info{}) {
   107  				continue
   108  			}
   109  			select {
   110  			case subCh <- entry:
   111  			case <-doneCh:
   112  				return
   113  			}
   114  		}
   115  	}
   116  	sys.pubsub.Subscribe(subCh, doneCh, filter)
   117  }
   118  
   119  // Validate if HTTPConsoleLoggerSys is valid, always returns nil right now
   120  func (sys *HTTPConsoleLoggerSys) Validate() error {
   121  	return nil
   122  }
   123  
   124  // Endpoint - dummy function for interface compatibility
   125  func (sys *HTTPConsoleLoggerSys) Endpoint() string {
   126  	return sys.console.Endpoint()
   127  }
   128  
   129  // String - stringer function for interface compatibility
   130  func (sys *HTTPConsoleLoggerSys) String() string {
   131  	return "console+http"
   132  }
   133  
   134  // Content returns the console stdout log
   135  func (sys *HTTPConsoleLoggerSys) Content() (logs []log.Entry) {
   136  	sys.RLock()
   137  	sys.logBuf.Do(func(p interface{}) {
   138  		if p != nil {
   139  			lg, ok := p.(log.Info)
   140  			if ok {
   141  				if (lg.Entry != log.Entry{}) {
   142  					logs = append(logs, lg.Entry)
   143  				}
   144  			}
   145  		}
   146  	})
   147  	sys.RUnlock()
   148  
   149  	return
   150  }
   151  
   152  // Send log message 'e' to console and publish to console
   153  // log pubsub system
   154  func (sys *HTTPConsoleLoggerSys) Send(e interface{}, logKind string) error {
   155  	var lg log.Info
   156  	switch e := e.(type) {
   157  	case log.Entry:
   158  		lg = log.Info{Entry: e, NodeName: sys.nodeName}
   159  	case string:
   160  		lg = log.Info{ConsoleMsg: e, NodeName: sys.nodeName}
   161  	}
   162  
   163  	sys.pubsub.Publish(lg)
   164  	sys.Lock()
   165  	// add log to ring buffer
   166  	sys.logBuf.Value = lg
   167  	sys.logBuf = sys.logBuf.Next()
   168  	sys.Unlock()
   169  
   170  	return sys.console.Send(e, string(logger.All))
   171  }