github.com/safing/portbase@v0.19.5/log/input.go (about)

     1  package log
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"strings"
     7  	"sync/atomic"
     8  	"time"
     9  )
    10  
    11  var (
    12  	warnLogLines = new(uint64)
    13  	errLogLines  = new(uint64)
    14  	critLogLines = new(uint64)
    15  )
    16  
    17  func log(level Severity, msg string, tracer *ContextTracer) {
    18  	if !started.IsSet() {
    19  		// a bit resource intense, but keeps logs before logging started.
    20  		// TODO: create option to disable logging
    21  		go func() {
    22  			<-startedSignal
    23  			log(level, msg, tracer)
    24  		}()
    25  		return
    26  	}
    27  
    28  	// get time
    29  	now := time.Now()
    30  
    31  	// get file and line
    32  	_, file, line, ok := runtime.Caller(2)
    33  	if !ok {
    34  		file = ""
    35  		line = 0
    36  	} else {
    37  		if len(file) > 3 {
    38  			file = file[:len(file)-3]
    39  		} else {
    40  			file = ""
    41  		}
    42  	}
    43  
    44  	// check if level is enabled for file or generally
    45  	if pkgLevelsActive.IsSet() {
    46  		pathSegments := strings.Split(file, "/")
    47  		if len(pathSegments) < 2 {
    48  			// file too short for package levels
    49  			return
    50  		}
    51  		pkgLevelsLock.Lock()
    52  		severity, ok := pkgLevels[pathSegments[len(pathSegments)-2]]
    53  		pkgLevelsLock.Unlock()
    54  		if ok {
    55  			if level < severity {
    56  				return
    57  			}
    58  		} else {
    59  			// no package level set, check against global level
    60  			if uint32(level) < atomic.LoadUint32(logLevel) {
    61  				return
    62  			}
    63  		}
    64  	} else if uint32(level) < atomic.LoadUint32(logLevel) {
    65  		// no package levels set, check against global level
    66  		return
    67  	}
    68  
    69  	// create log object
    70  	log := &logLine{
    71  		msg:       msg,
    72  		tracer:    tracer,
    73  		level:     level,
    74  		timestamp: now,
    75  		file:      file,
    76  		line:      line,
    77  	}
    78  
    79  	// send log to processing
    80  	select {
    81  	case logBuffer <- log:
    82  	default:
    83  	forceEmptyingLoop:
    84  		// force empty buffer until we can send to it
    85  		for {
    86  			select {
    87  			case forceEmptyingOfBuffer <- struct{}{}:
    88  			case logBuffer <- log:
    89  				break forceEmptyingLoop
    90  			}
    91  		}
    92  	}
    93  
    94  	// wake up writer if necessary
    95  	if logsWaitingFlag.SetToIf(false, true) {
    96  		select {
    97  		case logsWaiting <- struct{}{}:
    98  		default:
    99  		}
   100  	}
   101  }
   102  
   103  func fastcheck(level Severity) bool {
   104  	if pkgLevelsActive.IsSet() {
   105  		return true
   106  	}
   107  	if uint32(level) >= atomic.LoadUint32(logLevel) {
   108  		return true
   109  	}
   110  	return false
   111  }
   112  
   113  // Trace is used to log tiny steps. Log traces to context if you can!
   114  func Trace(msg string) {
   115  	if fastcheck(TraceLevel) {
   116  		log(TraceLevel, msg, nil)
   117  	}
   118  }
   119  
   120  // Tracef is used to log tiny steps. Log traces to context if you can!
   121  func Tracef(format string, things ...interface{}) {
   122  	if fastcheck(TraceLevel) {
   123  		log(TraceLevel, fmt.Sprintf(format, things...), nil)
   124  	}
   125  }
   126  
   127  // Debug is used to log minor errors or unexpected events. These occurrences are usually not worth mentioning in itself, but they might hint at a bigger problem.
   128  func Debug(msg string) {
   129  	if fastcheck(DebugLevel) {
   130  		log(DebugLevel, msg, nil)
   131  	}
   132  }
   133  
   134  // Debugf is used to log minor errors or unexpected events. These occurrences are usually not worth mentioning in itself, but they might hint at a bigger problem.
   135  func Debugf(format string, things ...interface{}) {
   136  	if fastcheck(DebugLevel) {
   137  		log(DebugLevel, fmt.Sprintf(format, things...), nil)
   138  	}
   139  }
   140  
   141  // Info is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen.
   142  func Info(msg string) {
   143  	if fastcheck(InfoLevel) {
   144  		log(InfoLevel, msg, nil)
   145  	}
   146  }
   147  
   148  // Infof is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen.
   149  func Infof(format string, things ...interface{}) {
   150  	if fastcheck(InfoLevel) {
   151  		log(InfoLevel, fmt.Sprintf(format, things...), nil)
   152  	}
   153  }
   154  
   155  // Warning is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet.
   156  func Warning(msg string) {
   157  	atomic.AddUint64(warnLogLines, 1)
   158  	if fastcheck(WarningLevel) {
   159  		log(WarningLevel, msg, nil)
   160  	}
   161  }
   162  
   163  // Warningf is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet.
   164  func Warningf(format string, things ...interface{}) {
   165  	atomic.AddUint64(warnLogLines, 1)
   166  	if fastcheck(WarningLevel) {
   167  		log(WarningLevel, fmt.Sprintf(format, things...), nil)
   168  	}
   169  }
   170  
   171  // Error is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational. Maybe User/Admin should be informed.
   172  func Error(msg string) {
   173  	atomic.AddUint64(errLogLines, 1)
   174  	if fastcheck(ErrorLevel) {
   175  		log(ErrorLevel, msg, nil)
   176  	}
   177  }
   178  
   179  // Errorf is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational.
   180  func Errorf(format string, things ...interface{}) {
   181  	atomic.AddUint64(errLogLines, 1)
   182  	if fastcheck(ErrorLevel) {
   183  		log(ErrorLevel, fmt.Sprintf(format, things...), nil)
   184  	}
   185  }
   186  
   187  // Critical is used to log events that completely break the system. Operation cannot continue. User/Admin must be informed.
   188  func Critical(msg string) {
   189  	atomic.AddUint64(critLogLines, 1)
   190  	if fastcheck(CriticalLevel) {
   191  		log(CriticalLevel, msg, nil)
   192  	}
   193  }
   194  
   195  // Criticalf is used to log events that completely break the system. Operation cannot continue. User/Admin must be informed.
   196  func Criticalf(format string, things ...interface{}) {
   197  	atomic.AddUint64(critLogLines, 1)
   198  	if fastcheck(CriticalLevel) {
   199  		log(CriticalLevel, fmt.Sprintf(format, things...), nil)
   200  	}
   201  }
   202  
   203  // TotalWarningLogLines returns the total amount of warning log lines since
   204  // start of the program.
   205  func TotalWarningLogLines() uint64 {
   206  	return atomic.LoadUint64(warnLogLines)
   207  }
   208  
   209  // TotalErrorLogLines returns the total amount of error log lines since start
   210  // of the program.
   211  func TotalErrorLogLines() uint64 {
   212  	return atomic.LoadUint64(errLogLines)
   213  }
   214  
   215  // TotalCriticalLogLines returns the total amount of critical log lines since
   216  // start of the program.
   217  func TotalCriticalLogLines() uint64 {
   218  	return atomic.LoadUint64(critLogLines)
   219  }