github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/logging.go (about)

     1  package goa
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  
     8  	"golang.org/x/net/context"
     9  )
    10  
    11  // ErrMissingLogValue is the value used to log keys with missing values
    12  const ErrMissingLogValue = "MISSING"
    13  
    14  type (
    15  	// LogAdapter is the logger interface used by goa to log informational and error messages.
    16  	// Adapters to different logging backends are provided in the logging sub-packages.
    17  	// goa takes care of initializing the logging context with the service, controller and
    18  	// action names.
    19  	LogAdapter interface {
    20  		// Info logs an informational message.
    21  		Info(msg string, keyvals ...interface{})
    22  		// Error logs an error.
    23  		Error(msg string, keyvals ...interface{})
    24  		// New appends to the logger context and returns the updated logger logger.
    25  		New(keyvals ...interface{}) LogAdapter
    26  	}
    27  
    28  	// adapter is the stdlib logger adapter.
    29  	adapter struct {
    30  		*log.Logger
    31  		keyvals []interface{}
    32  	}
    33  )
    34  
    35  // NewLogger returns a goa log adpater backed by a log logger.
    36  func NewLogger(logger *log.Logger) LogAdapter {
    37  	return &adapter{Logger: logger}
    38  }
    39  
    40  // Logger returns the logger stored in the context if any, nil otherwise.
    41  func Logger(ctx context.Context) *log.Logger {
    42  	logger := ContextLogger(ctx)
    43  	if a, ok := logger.(*adapter); ok {
    44  		return a.Logger
    45  	}
    46  	return nil
    47  }
    48  
    49  func (a *adapter) Info(msg string, keyvals ...interface{}) {
    50  	a.logit(msg, keyvals, false)
    51  }
    52  
    53  func (a *adapter) Error(msg string, keyvals ...interface{}) {
    54  	a.logit(msg, keyvals, true)
    55  }
    56  
    57  func (a *adapter) New(keyvals ...interface{}) LogAdapter {
    58  	if len(keyvals) == 0 {
    59  		return a
    60  	}
    61  	kvs := append(a.keyvals, keyvals...)
    62  	if len(kvs)%2 != 0 {
    63  		kvs = append(kvs, ErrMissingLogValue)
    64  	}
    65  	return &adapter{
    66  		Logger: a.Logger,
    67  		// Limiting the capacity of the stored keyvals ensures that a new
    68  		// backing array is created if the slice must grow.
    69  		keyvals: kvs[:len(kvs):len(kvs)],
    70  	}
    71  }
    72  
    73  func (a *adapter) logit(msg string, keyvals []interface{}, iserror bool) {
    74  	n := (len(keyvals) + 1) / 2
    75  	if len(keyvals)%2 != 0 {
    76  		keyvals = append(keyvals, ErrMissingLogValue)
    77  	}
    78  	m := (len(a.keyvals) + 1) / 2
    79  	n += m
    80  	var fm bytes.Buffer
    81  	lvl := "INFO"
    82  	if iserror {
    83  		lvl = "EROR"
    84  	}
    85  	fm.WriteString(fmt.Sprintf("[%s] %s", lvl, msg))
    86  	vals := make([]interface{}, n)
    87  	offset := len(a.keyvals)
    88  	for i := 0; i < offset; i += 2 {
    89  		k := a.keyvals[i]
    90  		v := a.keyvals[i+1]
    91  		vals[i/2] = v
    92  		fm.WriteString(fmt.Sprintf(" %s=%%+v", k))
    93  	}
    94  	for i := 0; i < len(keyvals); i += 2 {
    95  		k := keyvals[i]
    96  		v := keyvals[i+1]
    97  		vals[i/2+offset/2] = v
    98  		fm.WriteString(fmt.Sprintf(" %s=%%+v", k))
    99  	}
   100  	a.Logger.Printf(fm.String(), vals...)
   101  }
   102  
   103  // LogInfo extracts the logger from the given context and calls Info on it.
   104  // This is intended for code that needs portable logging such as the internal code of goa and
   105  // middleware. User code should use the log adapters instead.
   106  func LogInfo(ctx context.Context, msg string, keyvals ...interface{}) {
   107  	if l := ctx.Value(logKey); l != nil {
   108  		if logger, ok := l.(LogAdapter); ok {
   109  			logger.Info(msg, keyvals...)
   110  		}
   111  	}
   112  }
   113  
   114  // LogError extracts the logger from the given context and calls Error on it.
   115  // This is intended for code that needs portable logging such as the internal code of goa and
   116  // middleware. User code should use the log adapters instead.
   117  func LogError(ctx context.Context, msg string, keyvals ...interface{}) {
   118  	if l := ctx.Value(logKey); l != nil {
   119  		if logger, ok := l.(LogAdapter); ok {
   120  			logger.Error(msg, keyvals...)
   121  		}
   122  	}
   123  }