get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/log.go (about)

     1  // Copyright 2012-2020 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"fmt"
    18  	"io"
    19  	"os"
    20  	"sync/atomic"
    21  	"time"
    22  
    23  	srvlog "get.pme.sh/pnats/logger"
    24  )
    25  
    26  // Logger interface of the NATS Server
    27  type Logger interface {
    28  
    29  	// Log a notice statement
    30  	Noticef(format string, v ...interface{})
    31  
    32  	// Log a warning statement
    33  	Warnf(format string, v ...interface{})
    34  
    35  	// Log a fatal error
    36  	Fatalf(format string, v ...interface{})
    37  
    38  	// Log an error
    39  	Errorf(format string, v ...interface{})
    40  
    41  	// Log a debug statement
    42  	Debugf(format string, v ...interface{})
    43  
    44  	// Log a trace statement
    45  	Tracef(format string, v ...interface{})
    46  }
    47  
    48  // ConfigureLogger configures and sets the logger for the server.
    49  func (s *Server) ConfigureLogger() {
    50  	var (
    51  		log Logger
    52  
    53  		// Snapshot server options.
    54  		opts = s.getOpts()
    55  	)
    56  
    57  	if opts.NoLog {
    58  		return
    59  	}
    60  
    61  	syslog := opts.Syslog
    62  	if isWindowsService() && opts.LogFile == "" {
    63  		// Enable syslog if no log file is specified and we're running as a
    64  		// Windows service so that logs are written to the Windows event log.
    65  		syslog = true
    66  	}
    67  
    68  	if opts.LogFile != "" {
    69  		log = srvlog.NewFileLogger(opts.LogFile, opts.Logtime, opts.Debug, opts.Trace, true, srvlog.LogUTC(opts.LogtimeUTC))
    70  		if opts.LogSizeLimit > 0 {
    71  			if l, ok := log.(*srvlog.Logger); ok {
    72  				l.SetSizeLimit(opts.LogSizeLimit)
    73  			}
    74  		}
    75  		if opts.LogMaxFiles > 0 {
    76  			if l, ok := log.(*srvlog.Logger); ok {
    77  				al := int(opts.LogMaxFiles)
    78  				if int64(al) != opts.LogMaxFiles {
    79  					// set to default (no max) on overflow
    80  					al = 0
    81  				}
    82  				l.SetMaxNumFiles(al)
    83  			}
    84  		}
    85  	} else if opts.RemoteSyslog != "" {
    86  		log = srvlog.NewRemoteSysLogger(opts.RemoteSyslog, opts.Debug, opts.Trace)
    87  	} else if syslog {
    88  		log = srvlog.NewSysLogger(opts.Debug, opts.Trace)
    89  	} else {
    90  		colors := true
    91  		// Check to see if stderr is being redirected and if so turn off color
    92  		// Also turn off colors if we're running on Windows where os.Stderr.Stat() returns an invalid handle-error
    93  		stat, err := os.Stderr.Stat()
    94  		if err != nil || (stat.Mode()&os.ModeCharDevice) == 0 {
    95  			colors = false
    96  		}
    97  		log = srvlog.NewStdLogger(opts.Logtime, opts.Debug, opts.Trace, colors, true, srvlog.LogUTC(opts.LogtimeUTC))
    98  	}
    99  
   100  	s.SetLoggerV2(log, opts.Debug, opts.Trace, opts.TraceVerbose)
   101  }
   102  
   103  // Returns our current logger.
   104  func (s *Server) Logger() Logger {
   105  	s.logging.Lock()
   106  	defer s.logging.Unlock()
   107  	return s.logging.logger
   108  }
   109  
   110  // SetLogger sets the logger of the server
   111  func (s *Server) SetLogger(logger Logger, debugFlag, traceFlag bool) {
   112  	s.SetLoggerV2(logger, debugFlag, traceFlag, false)
   113  }
   114  
   115  // SetLogger sets the logger of the server
   116  func (s *Server) SetLoggerV2(logger Logger, debugFlag, traceFlag, sysTrace bool) {
   117  	if debugFlag {
   118  		atomic.StoreInt32(&s.logging.debug, 1)
   119  	} else {
   120  		atomic.StoreInt32(&s.logging.debug, 0)
   121  	}
   122  	if traceFlag {
   123  		atomic.StoreInt32(&s.logging.trace, 1)
   124  	} else {
   125  		atomic.StoreInt32(&s.logging.trace, 0)
   126  	}
   127  	if sysTrace {
   128  		atomic.StoreInt32(&s.logging.traceSysAcc, 1)
   129  	} else {
   130  		atomic.StoreInt32(&s.logging.traceSysAcc, 0)
   131  	}
   132  	s.logging.Lock()
   133  	if s.logging.logger != nil {
   134  		// Check to see if the logger implements io.Closer.  This could be a
   135  		// logger from another process embedding the NATS server or a dummy
   136  		// test logger that may not implement that interface.
   137  		if l, ok := s.logging.logger.(io.Closer); ok {
   138  			if err := l.Close(); err != nil {
   139  				s.Errorf("Error closing logger: %v", err)
   140  			}
   141  		}
   142  	}
   143  	s.logging.logger = logger
   144  	s.logging.Unlock()
   145  }
   146  
   147  // ReOpenLogFile if the logger is a file based logger, close and re-open the file.
   148  // This allows for file rotation by 'mv'ing the file then signaling
   149  // the process to trigger this function.
   150  func (s *Server) ReOpenLogFile() {
   151  	// Check to make sure this is a file logger.
   152  	s.logging.RLock()
   153  	ll := s.logging.logger
   154  	s.logging.RUnlock()
   155  
   156  	if ll == nil {
   157  		s.Noticef("File log re-open ignored, no logger")
   158  		return
   159  	}
   160  
   161  	// Snapshot server options.
   162  	opts := s.getOpts()
   163  
   164  	if opts.LogFile == "" {
   165  		s.Noticef("File log re-open ignored, not a file logger")
   166  	} else {
   167  		fileLog := srvlog.NewFileLogger(
   168  			opts.LogFile, opts.Logtime,
   169  			opts.Debug, opts.Trace, true,
   170  			srvlog.LogUTC(opts.LogtimeUTC),
   171  		)
   172  		s.SetLogger(fileLog, opts.Debug, opts.Trace)
   173  		if opts.LogSizeLimit > 0 {
   174  			fileLog.SetSizeLimit(opts.LogSizeLimit)
   175  		}
   176  		s.Noticef("File log re-opened")
   177  	}
   178  }
   179  
   180  // Noticef logs a notice statement
   181  func (s *Server) Noticef(format string, v ...interface{}) {
   182  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   183  		logger.Noticef(format, v...)
   184  	}, format, v...)
   185  }
   186  
   187  // Errorf logs an error
   188  func (s *Server) Errorf(format string, v ...interface{}) {
   189  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   190  		logger.Errorf(format, v...)
   191  	}, format, v...)
   192  }
   193  
   194  // Error logs an error with a scope
   195  func (s *Server) Errors(scope interface{}, e error) {
   196  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   197  		logger.Errorf(format, v...)
   198  	}, "%s - %s", scope, UnpackIfErrorCtx(e))
   199  }
   200  
   201  // Error logs an error with a context
   202  func (s *Server) Errorc(ctx string, e error) {
   203  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   204  		logger.Errorf(format, v...)
   205  	}, "%s: %s", ctx, UnpackIfErrorCtx(e))
   206  }
   207  
   208  // Error logs an error with a scope and context
   209  func (s *Server) Errorsc(scope interface{}, ctx string, e error) {
   210  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   211  		logger.Errorf(format, v...)
   212  	}, "%s - %s: %s", scope, ctx, UnpackIfErrorCtx(e))
   213  }
   214  
   215  // Warnf logs a warning error
   216  func (s *Server) Warnf(format string, v ...interface{}) {
   217  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   218  		logger.Warnf(format, v...)
   219  	}, format, v...)
   220  }
   221  
   222  func (s *Server) RateLimitWarnf(format string, v ...interface{}) {
   223  	statement := fmt.Sprintf(format, v...)
   224  	if _, loaded := s.rateLimitLogging.LoadOrStore(statement, time.Now()); loaded {
   225  		return
   226  	}
   227  	s.Warnf("%s", statement)
   228  }
   229  
   230  func (s *Server) RateLimitDebugf(format string, v ...interface{}) {
   231  	statement := fmt.Sprintf(format, v...)
   232  	if _, loaded := s.rateLimitLogging.LoadOrStore(statement, time.Now()); loaded {
   233  		return
   234  	}
   235  	s.Debugf("%s", statement)
   236  }
   237  
   238  // Fatalf logs a fatal error
   239  func (s *Server) Fatalf(format string, v ...interface{}) {
   240  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   241  		logger.Fatalf(format, v...)
   242  	}, format, v...)
   243  }
   244  
   245  // Debugf logs a debug statement
   246  func (s *Server) Debugf(format string, v ...interface{}) {
   247  	if atomic.LoadInt32(&s.logging.debug) == 0 {
   248  		return
   249  	}
   250  
   251  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   252  		logger.Debugf(format, v...)
   253  	}, format, v...)
   254  }
   255  
   256  // Tracef logs a trace statement
   257  func (s *Server) Tracef(format string, v ...interface{}) {
   258  	if atomic.LoadInt32(&s.logging.trace) == 0 {
   259  		return
   260  	}
   261  
   262  	s.executeLogCall(func(logger Logger, format string, v ...interface{}) {
   263  		logger.Tracef(format, v...)
   264  	}, format, v...)
   265  }
   266  
   267  func (s *Server) executeLogCall(f func(logger Logger, format string, v ...interface{}), format string, args ...interface{}) {
   268  	s.logging.RLock()
   269  	defer s.logging.RUnlock()
   270  	if s.logging.logger == nil {
   271  		return
   272  	}
   273  
   274  	f(s.logging.logger, format, args...)
   275  }