github.com/crowdsecurity/crowdsec@v1.6.1/pkg/csplugin/hclog_adapter.go (about)

     1  // Copyright 2021 Workrise Technologies Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package csplugin
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"log"
    21  	"os"
    22  	"reflect"
    23  
    24  	"github.com/hashicorp/go-hclog"
    25  	"github.com/sirupsen/logrus"
    26  )
    27  
    28  // NewHCLogAdapter takes an instance of a Logrus logger and returns an hclog
    29  // logger in the form of an HCLogAdapter.
    30  func NewHCLogAdapter(l *logrus.Logger, name string) hclog.Logger {
    31  	return &HCLogAdapter{l, name, nil}
    32  }
    33  
    34  // HCLogAdapter implements the hclog interface.  Plugins use hclog to send
    35  // log entries back to ephemeral-iam and this adapter allows for those logs
    36  // to be handled by ephemeral-iam's Logrus logger.
    37  type HCLogAdapter struct {
    38  	log  *logrus.Logger
    39  	name string
    40  
    41  	impliedArgs []interface{}
    42  }
    43  
    44  func (h HCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
    45  	switch level {
    46  	case hclog.NoLevel:
    47  		return
    48  	case hclog.Trace:
    49  		h.Trace(msg, args...)
    50  	case hclog.Debug:
    51  		h.Debug(msg, args...)
    52  	case hclog.Info:
    53  		h.Info(msg, args...)
    54  	case hclog.Warn:
    55  		h.Warn(msg, args...)
    56  	case hclog.Error:
    57  		h.Error(msg, args...)
    58  	}
    59  }
    60  
    61  func (h HCLogAdapter) Trace(msg string, args ...interface{}) {
    62  	h.log.WithFields(toLogrusFields(args)).Trace(msg)
    63  }
    64  
    65  func (h HCLogAdapter) Debug(msg string, args ...interface{}) {
    66  	h.log.WithFields(toLogrusFields(args)).Debug(msg)
    67  }
    68  
    69  func (h HCLogAdapter) Info(msg string, args ...interface{}) {
    70  	h.log.WithFields(toLogrusFields(args)).Info(msg)
    71  }
    72  
    73  func (h HCLogAdapter) Warn(msg string, args ...interface{}) {
    74  	h.log.WithFields(toLogrusFields(args)).Warn(msg)
    75  }
    76  
    77  func (h HCLogAdapter) Error(msg string, args ...interface{}) {
    78  	h.log.WithFields(toLogrusFields(args)).Error(msg)
    79  }
    80  
    81  func (h HCLogAdapter) IsTrace() bool {
    82  	return h.log.GetLevel() >= logrus.TraceLevel
    83  }
    84  
    85  func (h HCLogAdapter) IsDebug() bool {
    86  	return h.log.GetLevel() >= logrus.DebugLevel
    87  }
    88  
    89  func (h HCLogAdapter) IsInfo() bool {
    90  	return h.log.GetLevel() >= logrus.InfoLevel
    91  }
    92  
    93  func (h HCLogAdapter) IsWarn() bool {
    94  	return h.log.GetLevel() >= logrus.WarnLevel
    95  }
    96  
    97  func (h HCLogAdapter) IsError() bool {
    98  	return h.log.GetLevel() >= logrus.ErrorLevel
    99  }
   100  
   101  func (h HCLogAdapter) ImpliedArgs() []interface{} {
   102  	// Not supported.
   103  	return nil
   104  }
   105  
   106  func (h HCLogAdapter) With(args ...interface{}) hclog.Logger {
   107  	return &h
   108  }
   109  
   110  func (h HCLogAdapter) Name() string {
   111  	return h.name
   112  }
   113  
   114  func (h HCLogAdapter) Named(name string) hclog.Logger {
   115  	return NewHCLogAdapter(h.log, name)
   116  }
   117  
   118  func (h HCLogAdapter) ResetNamed(name string) hclog.Logger {
   119  	return &h
   120  }
   121  
   122  func (h *HCLogAdapter) SetLevel(level hclog.Level) {
   123  	h.log.SetLevel(level2logrus(level))
   124  }
   125  
   126  func (h HCLogAdapter) GetLevel() hclog.Level {
   127  	return level2hclog(h.log.GetLevel())
   128  }
   129  
   130  func (h HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
   131  	if opts == nil {
   132  		opts = &hclog.StandardLoggerOptions{}
   133  	}
   134  	return log.New(h.StandardWriter(opts), "", 0)
   135  }
   136  
   137  func (h HCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
   138  	return os.Stderr
   139  }
   140  
   141  // level2logrus maps hclog levels to Logrus levels.
   142  func level2logrus(level hclog.Level) logrus.Level {
   143  	switch level {
   144  	case hclog.NoLevel:
   145  		// Logrus does not have NoLevel, so use Info instead.
   146  		return logrus.InfoLevel
   147  	case hclog.Trace:
   148  		return logrus.TraceLevel
   149  	case hclog.Debug:
   150  		return logrus.DebugLevel
   151  	case hclog.Info:
   152  		return logrus.InfoLevel
   153  	case hclog.Warn:
   154  		return logrus.WarnLevel
   155  	case hclog.Error:
   156  		return logrus.ErrorLevel
   157  	default:
   158  		return logrus.InfoLevel
   159  	}
   160  }
   161  
   162  func level2hclog(level logrus.Level) hclog.Level {
   163  	switch level {
   164  	case logrus.TraceLevel:
   165  		return hclog.Trace
   166  	case logrus.DebugLevel:
   167  		return hclog.Debug
   168  	case logrus.InfoLevel:
   169  		return hclog.Info
   170  	case logrus.WarnLevel:
   171  		return hclog.Warn
   172  	case logrus.ErrorLevel:
   173  		return hclog.Error
   174  	default:
   175  		return hclog.Info
   176  	}
   177  }
   178  
   179  // toLogrusFields takes a list of key/value pairs passed to the hclog logger
   180  // and converts them to a map to be used as Logrus fields.
   181  func toLogrusFields(kvPairs []interface{}) map[string]interface{} {
   182  	m := map[string]interface{}{}
   183  	if len(kvPairs) == 0 {
   184  		return m
   185  	}
   186  
   187  	if len(kvPairs)%2 == 1 {
   188  		// There are an odd number of key/value pairs so append nil as the final value.
   189  		kvPairs = append(kvPairs, nil)
   190  	}
   191  
   192  	for i := 0; i < len(kvPairs); i += 2 {
   193  		// hclog automatically adds the timestamp field, ignore it.
   194  		if kvPairs[i] != "timestamp" {
   195  			merge(m, kvPairs[i], kvPairs[i+1])
   196  		}
   197  	}
   198  	return m
   199  }
   200  
   201  // merge takes a key/value pair and converts them to strings then adds them to
   202  // the dst map.
   203  func merge(dst map[string]interface{}, k, v interface{}) {
   204  	var key string
   205  
   206  	switch x := k.(type) {
   207  	case string:
   208  		key = x
   209  	case fmt.Stringer:
   210  		key = safeString(x)
   211  	default:
   212  		key = fmt.Sprint(x)
   213  	}
   214  
   215  	dst[key] = v
   216  }
   217  
   218  // safeString takes an interface that implements the String() function and calls it
   219  // to attempt to convert it to a string.  If a panic occurs, and it's caused by a
   220  // nil pointer, the value will be set to "NULL".
   221  func safeString(str fmt.Stringer) (s string) {
   222  	defer func() {
   223  		if panicVal := recover(); panicVal != nil {
   224  			if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
   225  				s = "NULL"
   226  			} else {
   227  				panic(panicVal)
   228  			}
   229  		}
   230  	}()
   231  
   232  	s = str.String()
   233  	return
   234  }