github.com/singularityware/singularity@v3.1.1+incompatible/internal/pkg/sylog/sylog.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  // Package sylog implements a basic logger for Singularity Go code to log
     7  // messages in the same format as singularity_message() from C code
     8  package sylog
     9  
    10  import (
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"runtime"
    16  	"strconv"
    17  	"strings"
    18  )
    19  
    20  type messageLevel int
    21  
    22  const (
    23  	fatal    messageLevel = iota - 4 // fatal    : -4
    24  	error                            // error    : -3
    25  	warn                             // warn     : -2
    26  	log                              // log      : -1
    27  	_                                // SKIP     : 0
    28  	info                             // info     : 1
    29  	verbose                          // verbose  : 2
    30  	verbose2                         // verbose2 : 3
    31  	verbose3                         // verbose3 : 4
    32  	debug                            // debug    : 5
    33  )
    34  
    35  func (l messageLevel) String() string {
    36  	str, ok := messageLabels[l]
    37  
    38  	if !ok {
    39  		str = "????"
    40  	}
    41  
    42  	return str
    43  }
    44  
    45  var messageLabels = map[messageLevel]string{
    46  	fatal:    "FATAL",
    47  	error:    "ERROR",
    48  	warn:     "WARNING",
    49  	log:      "LOG",
    50  	info:     "INFO",
    51  	verbose:  "VERBOSE",
    52  	verbose2: "VERBOSE",
    53  	verbose3: "VERBOSE",
    54  	debug:    "DEBUG",
    55  }
    56  
    57  var messageColors = map[messageLevel]string{
    58  	fatal: "\x1b[31m",
    59  	error: "\x1b[31m",
    60  	warn:  "\x1b[33m",
    61  	info:  "\x1b[34m",
    62  }
    63  
    64  const colorReset string = "\x1b[0m"
    65  
    66  var loggerLevel messageLevel
    67  
    68  func init() {
    69  	_level, ok := os.LookupEnv("SINGULARITY_MESSAGELEVEL")
    70  	if !ok {
    71  		loggerLevel = debug
    72  	} else {
    73  		_levelint, err := strconv.Atoi(_level)
    74  		if err != nil {
    75  			loggerLevel = debug
    76  		} else {
    77  			loggerLevel = messageLevel(_levelint)
    78  		}
    79  	}
    80  }
    81  
    82  func prefix(level messageLevel) string {
    83  	messageColor, ok := messageColors[level]
    84  	if !ok {
    85  		messageColor = "\x1b[0m"
    86  	}
    87  
    88  	if loggerLevel < debug {
    89  		return fmt.Sprintf("%s%-8s%s ", messageColor, level.String()+":", colorReset)
    90  	}
    91  
    92  	pc, _, _, ok := runtime.Caller(3)
    93  	details := runtime.FuncForPC(pc)
    94  
    95  	var funcName string
    96  	if ok && details == nil {
    97  		fmt.Printf("Unable to get details of calling function\n")
    98  		funcName = "UNKNOWN CALLING FUNC"
    99  	} else {
   100  		funcNameSplit := strings.Split(details.Name(), ".")
   101  		funcName = funcNameSplit[len(funcNameSplit)-1] + "()"
   102  	}
   103  
   104  	uid := os.Geteuid()
   105  	pid := os.Getpid()
   106  	uidStr := fmt.Sprintf("[U=%d,P=%d]", uid, pid)
   107  
   108  	return fmt.Sprintf("%s%-8s%s%-19s%-30s", messageColor, level, colorReset, uidStr, funcName)
   109  }
   110  
   111  func writef(level messageLevel, format string, a ...interface{}) {
   112  	if loggerLevel < level {
   113  		return
   114  	}
   115  
   116  	message := fmt.Sprintf(format, a...)
   117  	message = strings.TrimSuffix(message, "\n")
   118  
   119  	fmt.Fprintf(os.Stderr, "%s%s\n", prefix(level), message)
   120  }
   121  
   122  // Fatalf is equivalent to a call to Errorf followed by os.Exit(255). Code that
   123  // may be imported by other projects should NOT use Fatalf.
   124  func Fatalf(format string, a ...interface{}) {
   125  	writef(fatal, format, a...)
   126  	os.Exit(255)
   127  }
   128  
   129  // Errorf writes an ERROR level message to the log but does not exit. This
   130  // should be called when an error is being returned to the calling thread
   131  func Errorf(format string, a ...interface{}) {
   132  	writef(error, format, a...)
   133  }
   134  
   135  // Warningf writes a WARNING level message to the log.
   136  func Warningf(format string, a ...interface{}) {
   137  	writef(warn, format, a...)
   138  }
   139  
   140  // Infof writes an INFO level message to the log. By default, INFO level messages
   141  // will always be output (unless running in silent)
   142  func Infof(format string, a ...interface{}) {
   143  	writef(info, format, a...)
   144  }
   145  
   146  // Verbosef writes a VERBOSE level message to the log. This should probably be
   147  // deprecated since the granularity is often too fine to be useful.
   148  func Verbosef(format string, a ...interface{}) {
   149  	writef(verbose, format, a...)
   150  }
   151  
   152  // Debugf writes a DEBUG level message to the log.
   153  func Debugf(format string, a ...interface{}) {
   154  	writef(debug, format, a...)
   155  }
   156  
   157  // SetLevel explicitly sets the loggerLevel
   158  func SetLevel(l int) {
   159  	loggerLevel = messageLevel(l)
   160  }
   161  
   162  // GetLevel returns the current log level as integer
   163  func GetLevel() int {
   164  	return int(loggerLevel)
   165  }
   166  
   167  // GetEnvVar returns a formatted environment variable string which
   168  // can later be interpreted by init() in a child proc
   169  func GetEnvVar() string {
   170  	return fmt.Sprintf("SINGULARITY_MESSAGELEVEL=%d", loggerLevel)
   171  }
   172  
   173  // Writer returns an io.Writer to pass to an external packages logging utility.
   174  // i.e when --quiet option is set, this function returns ioutil.Discard writer to ignore output
   175  func Writer() io.Writer {
   176  	if loggerLevel <= -1 {
   177  		return ioutil.Discard
   178  	}
   179  
   180  	return os.Stderr
   181  }