github.com/metacurrency/holochain@v0.1.0-alpha-26.0.20200915073418-5c83169c9b5b/log.go (about)

     1  // Copyright (C) 2013-2017, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.)
     2  // Use of this source code is governed by GPLv3 found in the LICENSE file
     3  //----------------------------------------------------------------------------------------
     4  
     5  // log encapsulates logging
     6  
     7  package holochain
     8  
     9  import (
    10  	"fmt"
    11  	"github.com/fatih/color"
    12  	"io"
    13  	"os"
    14  	"path/filepath"
    15  	"regexp"
    16  	"runtime"
    17  	"strings"
    18  	"time"
    19  )
    20  
    21  // Logger holds logger configuration
    22  type Logger struct {
    23  	Name    string
    24  	Enabled bool
    25  	Format  string
    26  	f       string
    27  	tf      string
    28  	color   *color.Color
    29  	w       io.Writer
    30  
    31  	Prefix      string
    32  	PrefixColor *color.Color
    33  }
    34  
    35  var colorMap map[string]*color.Color
    36  var EnableAllLoggersEnv string = "HC_ENABLE_ALL_LOGS"
    37  
    38  func (h *Logger) GetColor(colorName string) *color.Color {
    39  	if _, ok := colorMap["red"]; !ok {
    40  		colorMap = make(map[string]*color.Color)
    41  		colorMap["red"] = color.New(color.FgRed)
    42  		colorMap["blue"] = color.New(color.FgBlue)
    43  		colorMap["green"] = color.New(color.FgGreen)
    44  		colorMap["yellow"] = color.New(color.FgYellow)
    45  		colorMap["white"] = color.New(color.FgWhite)
    46  		colorMap["cyan"] = color.New(color.FgCyan)
    47  		colorMap["magenta"] = color.New(color.FgMagenta)
    48  	}
    49  	if val, ok := colorMap[colorName]; ok {
    50  		return val
    51  	} else {
    52  		return colorMap["white"]
    53  	}
    54  }
    55  
    56  func (l *Logger) setupColor(f string) (colorResult *color.Color, result string) {
    57  	re := regexp.MustCompile(`(.*)\%\{color:([^\}]+)\}(.*)`)
    58  	x := re.FindStringSubmatch(f)
    59  	var txtColor string
    60  	if len(x) > 0 {
    61  		result = x[1] + x[3]
    62  		txtColor = x[2]
    63  	} else {
    64  		result = f
    65  	}
    66  
    67  	if txtColor != "" {
    68  		colorResult = l.GetColor(txtColor)
    69  	}
    70  	return
    71  }
    72  
    73  func (l *Logger) setupTime(f string) (timeFormat string, result string) {
    74  	re := regexp.MustCompile(`(.*)\%\{time(:[^\}]+)*\}(.*)`)
    75  	x := re.FindStringSubmatch(f)
    76  	if len(x) > 0 {
    77  		result = x[1] + "%{time}" + x[3]
    78  		timeFormat = strings.TrimLeft(x[2], ":")
    79  		if timeFormat == "" {
    80  			timeFormat = time.Stamp
    81  		}
    82  	} else {
    83  		result = f
    84  	}
    85  	return
    86  }
    87  
    88  func (l *Logger) New(w io.Writer) (err error) {
    89  
    90  	if w == nil {
    91  		l.w = os.Stdout
    92  	} else {
    93  		l.w = w
    94  	}
    95  
    96  	if l.Format == "" {
    97  		l.f = `%{message}`
    98  		l.color = nil
    99  	} else {
   100  		l.color, l.f = l.setupColor(l.Format)
   101  		l.tf, l.f = l.setupTime(l.f)
   102  	}
   103  
   104  	d := os.Getenv(EnableAllLoggersEnv)
   105  	switch d {
   106  	case "1":
   107  		l.Enabled = true
   108  	case "0":
   109  		l.Enabled = false
   110  	}
   111  
   112  	return
   113  }
   114  
   115  func (l *Logger) SetPrefix(prefixFormat string) {
   116  	l.PrefixColor, l.Prefix = l.setupColor(prefixFormat)
   117  }
   118  
   119  func (l *Logger) parse(m string) (output string) {
   120  	var t *time.Time
   121  	if l.tf != "" {
   122  		now := time.Now()
   123  		t = &now
   124  	}
   125  	return l._parse(m, t)
   126  }
   127  
   128  func (l *Logger) _parse(m string, t *time.Time) (output string) {
   129  	output = strings.Replace(l.f, "%{message}", m, -1)
   130  	if t != nil {
   131  		tTxt := t.Format(l.tf)
   132  		output = strings.Replace(output, "%{time}", tTxt, -1)
   133  	}
   134  
   135  	// TODO add the calling depth to the line format string.
   136  	re := regexp.MustCompile(`(%{line})|(%{file})`)
   137  	matches := re.FindStringSubmatch(l.f)
   138  	if len(matches) > 0 {
   139  		_, file, line, ok := runtime.Caller(6)
   140  		if ok {
   141  			// sometimes the stack is one less deep than we expect in which case
   142  			// the file shows "asm_" so check for this case and redo!
   143  			if strings.Index(file, "asm_") > 0 {
   144  				_, file, line, ok = runtime.Caller(5)
   145  			}
   146  			output = strings.Replace(output, "%{file}", filepath.Base(file), -1)
   147  			output = strings.Replace(output, "%{line}", fmt.Sprintf("%d", line), -1)
   148  		}
   149  	}
   150  	return
   151  }
   152  
   153  func (l *Logger) p(m interface{}) {
   154  	l.pf("%v", m)
   155  }
   156  
   157  func (l *Logger) pf(m string, args ...interface{}) {
   158  	if l != nil && l.Enabled {
   159  		l.prefixPrint()
   160  		f := l.parse(m)
   161  		if l.color != nil {
   162  			l.color.Fprintf(l.w, f+"\n", args...)
   163  		} else {
   164  			fmt.Fprintf(l.w, f+"\n", args...)
   165  		}
   166  	}
   167  }
   168  
   169  func (l *Logger) prefixPrint(args ...interface{}) {
   170  	if l.PrefixColor != nil {
   171  		l.PrefixColor.Fprintf(l.w, l.Prefix, args...)
   172  	} else {
   173  		fmt.Fprintf(l.w, l.Prefix, args...)
   174  	}
   175  }
   176  
   177  func (l *Logger) Log(m interface{}) {
   178  	l.p(m)
   179  }
   180  
   181  func (l *Logger) Logf(m string, args ...interface{}) {
   182  	l.pf(m, args...)
   183  }