gopkg.in/essentialkaos/ek.v3@v3.5.1/log/log.go (about)

     1  // Package log provides improved logger
     2  package log
     3  
     4  // ////////////////////////////////////////////////////////////////////////////////// //
     5  //                                                                                    //
     6  //                     Copyright (c) 2009-2016 Essential Kaos                         //
     7  //      Essential Kaos Open Source License <http://essentialkaos.com/ekol?en>         //
     8  //                                                                                    //
     9  // ////////////////////////////////////////////////////////////////////////////////// //
    10  
    11  import (
    12  	"bufio"
    13  	"errors"
    14  	"fmt"
    15  	"io"
    16  	"os"
    17  	"strings"
    18  	"time"
    19  )
    20  
    21  // ////////////////////////////////////////////////////////////////////////////////// //
    22  
    23  // DEBUG debug messages
    24  // INFO info messages
    25  // WARN warning messages
    26  // ERROR error messages
    27  // CRIT critical error messages
    28  // AUX unskipable messages (separators, headers, etc...)
    29  const (
    30  	DEBUG = 0
    31  	INFO  = 1
    32  	WARN  = 2
    33  	ERROR = 3
    34  	CRIT  = 4
    35  	AUX   = 99
    36  )
    37  
    38  // ////////////////////////////////////////////////////////////////////////////////// //
    39  
    40  // Logger is a basic logger struct
    41  type Logger struct {
    42  	PrefixDebug bool // Prefix for debug messages
    43  	PrefixInfo  bool // Prefix for info messages
    44  	PrefixWarn  bool // Prefix for warning messages
    45  	PrefixError bool // Prefix for error messages
    46  	PrefixCrit  bool // Prefix for critical error messages
    47  
    48  	file     string
    49  	fd       *os.File
    50  	w        *bufio.Writer
    51  	level    int
    52  	perms    os.FileMode
    53  	useBufIO bool
    54  }
    55  
    56  // ////////////////////////////////////////////////////////////////////////////////// //
    57  
    58  // Global is global logger struct
    59  var Global = &Logger{
    60  	PrefixWarn:  true,
    61  	PrefixError: true,
    62  	PrefixCrit:  true,
    63  
    64  	level: INFO,
    65  }
    66  
    67  // ////////////////////////////////////////////////////////////////////////////////// //
    68  
    69  // PrefixMap is map with messages prefixes
    70  var PrefixMap = map[int]string{
    71  	DEBUG: "[DEBUG]",
    72  	INFO:  "[INFO]",
    73  	WARN:  "[WARNING]",
    74  	ERROR: "[ERROR]",
    75  	CRIT:  "[CRITICAL]",
    76  }
    77  
    78  // TimeFormat contains format string for time in logs
    79  var TimeFormat = "2006/01/02 15:04:05.000"
    80  
    81  // ////////////////////////////////////////////////////////////////////////////////// //
    82  
    83  var logLevelsNames = map[string]int{
    84  	"debug":    0,
    85  	"info":     1,
    86  	"warn":     2,
    87  	"warning":  2,
    88  	"error":    3,
    89  	"crit":     4,
    90  	"critical": 4,
    91  }
    92  
    93  // ////////////////////////////////////////////////////////////////////////////////// //
    94  
    95  // New creates new logger struct
    96  func New(file string, perms os.FileMode) (*Logger, error) {
    97  	logger := &Logger{
    98  		PrefixWarn:  true,
    99  		PrefixCrit:  true,
   100  		PrefixError: true,
   101  
   102  		level: INFO,
   103  	}
   104  
   105  	err := logger.Set(file, perms)
   106  
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	return logger, nil
   112  }
   113  
   114  // Reopen close file descriptor for global logger and open it again
   115  // Useful for log rotation
   116  func Reopen() error {
   117  	return Global.Reopen()
   118  }
   119  
   120  // MinLevel defines minimal logging level
   121  func MinLevel(level interface{}) error {
   122  	return Global.MinLevel(level)
   123  }
   124  
   125  // Set change global logger output target
   126  func Set(file string, perms os.FileMode) error {
   127  	return Global.Set(file, perms)
   128  }
   129  
   130  // EnableBufIO enable buffered I/O
   131  func EnableBufIO(interval time.Duration) {
   132  	Global.EnableBufIO(interval)
   133  }
   134  
   135  // Flush write buffered data to file
   136  func Flush() error {
   137  	return Global.Flush()
   138  }
   139  
   140  // Print write message to global logger output
   141  func Print(level int, f string, a ...interface{}) (int, error) {
   142  	return Global.Print(level, f, a...)
   143  }
   144  
   145  // Debug write debug message to global logger output
   146  func Debug(f string, a ...interface{}) (int, error) {
   147  	return Global.Debug(f, a...)
   148  }
   149  
   150  // Info write info message to global logger output
   151  func Info(f string, a ...interface{}) (int, error) {
   152  	return Global.Info(f, a...)
   153  }
   154  
   155  // Warn write warning message to global logger output
   156  func Warn(f string, a ...interface{}) (int, error) {
   157  	return Global.Warn(f, a...)
   158  }
   159  
   160  // Error write error message to global logger output
   161  func Error(f string, a ...interface{}) (int, error) {
   162  	return Global.Error(f, a...)
   163  }
   164  
   165  // Crit write critical message to global logger output
   166  func Crit(f string, a ...interface{}) (int, error) {
   167  	return Global.Crit(f, a...)
   168  }
   169  
   170  // Aux write unskipable message (for separators/headers)
   171  func Aux(f string, a ...interface{}) (int, error) {
   172  	return Global.Aux(f, a...)
   173  }
   174  
   175  // ////////////////////////////////////////////////////////////////////////////////// //
   176  
   177  // Reopen close file descriptor and open again
   178  // Useful for log rotation
   179  func (l *Logger) Reopen() error {
   180  	if l == nil {
   181  		return errors.New("Logger is nil")
   182  	}
   183  
   184  	if l.fd == nil {
   185  		return errors.New("Output file is not set")
   186  	}
   187  
   188  	if l.w != nil {
   189  		l.w.Flush()
   190  	}
   191  
   192  	l.fd.Close()
   193  
   194  	return l.Set(l.file, l.perms)
   195  }
   196  
   197  // MinLevel defines minimal logging level
   198  func (l *Logger) MinLevel(level interface{}) error {
   199  	if l == nil {
   200  		return errors.New("Logger is nil")
   201  	}
   202  
   203  	levelCode, err := convertMinLevelValue(level)
   204  
   205  	if err != nil {
   206  		return err
   207  	}
   208  
   209  	switch {
   210  	case levelCode < DEBUG:
   211  		levelCode = DEBUG
   212  	case levelCode > CRIT:
   213  		levelCode = CRIT
   214  	}
   215  
   216  	l.level = levelCode
   217  
   218  	return nil
   219  }
   220  
   221  // EnableBufIO enable buffered I/O support
   222  func (l *Logger) EnableBufIO(interval time.Duration) {
   223  	l.useBufIO = true
   224  
   225  	if l.fd != nil {
   226  		l.w = bufio.NewWriter(l.fd)
   227  	}
   228  
   229  	go l.flushDaemon(interval)
   230  }
   231  
   232  // Set change logger output target
   233  func (l *Logger) Set(file string, perms os.FileMode) error {
   234  	fd, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, perms)
   235  
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	// Flush data if writter exist
   241  	if l.w != nil {
   242  		l.w.Flush()
   243  		l.w = nil
   244  	}
   245  
   246  	if l.fd != nil {
   247  		l.fd.Close()
   248  		l.fd = nil
   249  	}
   250  
   251  	l.fd, l.file, l.perms = fd, file, perms
   252  
   253  	if l.useBufIO {
   254  		l.w = bufio.NewWriter(l.fd)
   255  	}
   256  
   257  	return nil
   258  }
   259  
   260  // Print write message to logger output
   261  func (l *Logger) Print(level int, f string, a ...interface{}) (int, error) {
   262  	if l == nil {
   263  		return -1, errors.New("Logger is nil")
   264  	}
   265  
   266  	if l.level > level {
   267  		return 0, nil
   268  	}
   269  
   270  	var w io.Writer
   271  
   272  	if l.fd == nil {
   273  		switch level {
   274  		case ERROR, CRIT:
   275  			w = os.Stderr
   276  		default:
   277  			w = os.Stdout
   278  		}
   279  	} else {
   280  		if l.w != nil {
   281  			w = l.w
   282  		} else {
   283  			w = l.fd
   284  		}
   285  	}
   286  
   287  	var showPrefixes bool
   288  
   289  	switch {
   290  	case level == DEBUG && l.PrefixDebug,
   291  		level == INFO && l.PrefixInfo,
   292  		level == WARN && l.PrefixWarn,
   293  		level == ERROR && l.PrefixError,
   294  		level == CRIT && l.PrefixCrit:
   295  		showPrefixes = true
   296  	}
   297  
   298  	if f == "" || f[len(f)-1:] != "\n" {
   299  		f += "\n"
   300  	}
   301  
   302  	if showPrefixes {
   303  		return fmt.Fprintf(w, "%s %s %s", getTime(), PrefixMap[level], fmt.Sprintf(f, a...))
   304  	}
   305  
   306  	return fmt.Fprintf(w, "%s %s", getTime(), fmt.Sprintf(f, a...))
   307  }
   308  
   309  // Flush write buffered data to file
   310  func (l *Logger) Flush() error {
   311  	if l == nil {
   312  		return errors.New("Logger is nil")
   313  	}
   314  
   315  	if l.w == nil {
   316  		return nil
   317  	}
   318  
   319  	return l.w.Flush()
   320  }
   321  
   322  // Debug write debug message to logger output
   323  func (l *Logger) Debug(f string, a ...interface{}) (int, error) {
   324  	if l == nil {
   325  		return -1, errors.New("Logger is nil")
   326  	}
   327  
   328  	return l.Print(DEBUG, f, a...)
   329  }
   330  
   331  // Info write info message to logger output
   332  func (l *Logger) Info(f string, a ...interface{}) (int, error) {
   333  	if l == nil {
   334  		return -1, errors.New("Logger is nil")
   335  	}
   336  
   337  	return l.Print(INFO, f, a...)
   338  }
   339  
   340  // Warn write warning message to logger output
   341  func (l *Logger) Warn(f string, a ...interface{}) (int, error) {
   342  	if l == nil {
   343  		return -1, errors.New("Logger is nil")
   344  	}
   345  
   346  	return l.Print(WARN, f, a...)
   347  }
   348  
   349  // Error write error message to logger output
   350  func (l *Logger) Error(f string, a ...interface{}) (int, error) {
   351  	if l == nil {
   352  		return -1, errors.New("Logger is nil")
   353  	}
   354  
   355  	return l.Print(ERROR, f, a...)
   356  }
   357  
   358  // Crit write critical message to logger output
   359  func (l *Logger) Crit(f string, a ...interface{}) (int, error) {
   360  	if l == nil {
   361  		return -1, errors.New("Logger is nil")
   362  	}
   363  
   364  	return l.Print(CRIT, f, a...)
   365  }
   366  
   367  // Aux write unskipable message (for separators/headers)
   368  func (l *Logger) Aux(f string, a ...interface{}) (int, error) {
   369  	if l == nil {
   370  		return -1, errors.New("Logger is nil")
   371  	}
   372  
   373  	return l.Print(AUX, f, a...)
   374  }
   375  
   376  // ////////////////////////////////////////////////////////////////////////////////// //
   377  
   378  func (l *Logger) flushDaemon(interval time.Duration) {
   379  	for range time.NewTicker(interval).C {
   380  		l.Flush()
   381  	}
   382  }
   383  
   384  // ////////////////////////////////////////////////////////////////////////////////// //
   385  
   386  func getTime() string {
   387  	return "[ " + time.Now().Format(TimeFormat) + " ]"
   388  }
   389  
   390  func convertMinLevelValue(level interface{}) (int, error) {
   391  	switch level.(type) {
   392  
   393  	case int:
   394  		return level.(int), nil
   395  
   396  	case int8:
   397  		return int(level.(int8)), nil
   398  
   399  	case int16:
   400  		return int(level.(int16)), nil
   401  
   402  	case int32:
   403  		return int(level.(int32)), nil
   404  
   405  	case int64:
   406  		return int(level.(int64)), nil
   407  
   408  	case uint:
   409  		return int(level.(uint)), nil
   410  
   411  	case uint8:
   412  		return int(level.(uint8)), nil
   413  
   414  	case uint16:
   415  		return int(level.(uint16)), nil
   416  
   417  	case uint32:
   418  		return int(level.(uint32)), nil
   419  
   420  	case uint64:
   421  		return int(level.(uint64)), nil
   422  
   423  	case float32:
   424  		return int(level.(float32)), nil
   425  
   426  	case float64:
   427  		return int(level.(float64)), nil
   428  
   429  	case string:
   430  		code, ok := logLevelsNames[strings.ToLower(level.(string))]
   431  
   432  		if !ok {
   433  			return -1, errors.New("Unknown level " + level.(string))
   434  		}
   435  
   436  		return code, nil
   437  	}
   438  
   439  	return -1, errors.New("Unexpected level type")
   440  }