github.com/apptainer/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 }