github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/logger/default.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  //     https://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  //
    13  // Original source: github.com/micro/go-micro/v3/logger/default.go
    14  
    15  package logger
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  	"runtime"
    22  	"sort"
    23  	"strings"
    24  	"sync"
    25  	"time"
    26  
    27  	dlog "github.com/tickoalcantara12/micro/v3/service/debug/log"
    28  )
    29  
    30  func init() {
    31  	lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL"))
    32  	if err != nil {
    33  		lvl = InfoLevel
    34  	}
    35  
    36  	DefaultLogger = NewHelper(NewLogger(WithLevel(lvl)))
    37  }
    38  
    39  type defaultLogger struct {
    40  	sync.RWMutex
    41  	opts Options
    42  }
    43  
    44  // Init(opts...) should only overwrite provided options
    45  func (l *defaultLogger) Init(opts ...Option) error {
    46  	for _, o := range opts {
    47  		o(&l.opts)
    48  	}
    49  	return nil
    50  }
    51  
    52  func (l *defaultLogger) String() string {
    53  	return "default"
    54  }
    55  
    56  func (l *defaultLogger) Fields(fields map[string]interface{}) Logger {
    57  	l.Lock()
    58  	l.opts.Fields = copyFields(fields)
    59  	l.Unlock()
    60  	return l
    61  }
    62  
    63  func copyFields(src map[string]interface{}) map[string]interface{} {
    64  	dst := make(map[string]interface{}, len(src))
    65  	for k, v := range src {
    66  		dst[k] = v
    67  	}
    68  	return dst
    69  }
    70  
    71  // logCallerfilePath returns a package/file:line description of the caller,
    72  // preserving only the leaf directory name and file name.
    73  func logCallerfilePath(loggingFilePath string) string {
    74  	// To make sure we trim the path correctly on Windows too, we
    75  	// counter-intuitively need to use '/' and *not* os.PathSeparator here,
    76  	// because the path given originates from Go stdlib, specifically
    77  	// runtime.Caller() which (as of Mar/17) returns forward slashes even on
    78  	// Windows.
    79  	//
    80  	// See https://github.com/golang/go/issues/3335
    81  	// and https://github.com/golang/go/issues/18151
    82  	//
    83  	// for discussion on the issue on Go side.
    84  	idx := strings.LastIndexByte(loggingFilePath, '/')
    85  	if idx == -1 {
    86  		return loggingFilePath
    87  	}
    88  	idx = strings.LastIndexByte(loggingFilePath[:idx], '/')
    89  	if idx == -1 {
    90  		return loggingFilePath
    91  	}
    92  	return loggingFilePath[idx+1:]
    93  }
    94  
    95  func (l *defaultLogger) Log(level Level, v ...interface{}) {
    96  	// TODO decide does we need to write message if log level not used?
    97  	if !l.opts.Level.Enabled(level) {
    98  		return
    99  	}
   100  
   101  	l.RLock()
   102  	fields := copyFields(l.opts.Fields)
   103  	l.RUnlock()
   104  
   105  	fields["level"] = level.String()
   106  
   107  	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
   108  		fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line)
   109  	}
   110  
   111  	rec := dlog.Record{
   112  		Timestamp: time.Now(),
   113  		Message:   strings.ReplaceAll(fmt.Sprint(v...), "\n", ""),
   114  		Metadata:  make(map[string]string, len(fields)),
   115  	}
   116  
   117  	keys := make([]string, 0, len(fields))
   118  	for k, v := range fields {
   119  		keys = append(keys, k)
   120  		rec.Metadata[k] = fmt.Sprintf("%v", v)
   121  	}
   122  
   123  	sort.Strings(keys)
   124  	metadata := ""
   125  
   126  	for _, k := range keys {
   127  		metadata += fmt.Sprintf(" %s=%v", k, fields[k])
   128  	}
   129  
   130  	t := rec.Timestamp.Format("2006-01-02 15:04:05")
   131  	fmt.Fprintf(l.opts.Out, "%s %s %v\n", t, metadata, rec.Message)
   132  }
   133  
   134  func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) {
   135  	//	 TODO decide does we need to write message if log level not used?
   136  	if level < l.opts.Level {
   137  		return
   138  	}
   139  
   140  	l.RLock()
   141  	fields := copyFields(l.opts.Fields)
   142  	l.RUnlock()
   143  
   144  	fields["level"] = level.String()
   145  
   146  	if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
   147  		fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line)
   148  	}
   149  
   150  	rec := dlog.Record{
   151  		Timestamp: time.Now(),
   152  		Message:   strings.ReplaceAll(fmt.Sprintf(format, v...), "\n", ""),
   153  		Metadata:  make(map[string]string, len(fields)),
   154  	}
   155  
   156  	keys := make([]string, 0, len(fields))
   157  	for k, v := range fields {
   158  		keys = append(keys, k)
   159  		rec.Metadata[k] = fmt.Sprintf("%v", v)
   160  	}
   161  
   162  	sort.Strings(keys)
   163  	metadata := ""
   164  
   165  	for _, k := range keys {
   166  		metadata += fmt.Sprintf(" %s=%v", k, fields[k])
   167  	}
   168  
   169  	t := rec.Timestamp.Format("2006-01-02 15:04:05")
   170  	fmt.Fprintf(l.opts.Out, "%s %s %v\n", t, metadata, rec.Message)
   171  }
   172  
   173  func (l *defaultLogger) Options() Options {
   174  	// not guard against options Context values
   175  	l.RLock()
   176  	opts := l.opts
   177  	opts.Fields = copyFields(l.opts.Fields)
   178  	l.RUnlock()
   179  	return opts
   180  }
   181  
   182  // NewLogger builds a new logger based on options
   183  func NewLogger(opts ...Option) Logger {
   184  	// Default options
   185  	options := Options{
   186  		Level:           InfoLevel,
   187  		Fields:          make(map[string]interface{}),
   188  		Out:             os.Stderr,
   189  		CallerSkipCount: 2,
   190  		Context:         context.Background(),
   191  	}
   192  
   193  	l := &defaultLogger{opts: options}
   194  	if err := l.Init(opts...); err != nil {
   195  		l.Log(FatalLevel, err)
   196  	}
   197  
   198  	return l
   199  }