github.com/jayanthvn/pure-gobpf@v0.0.0-20230623131354-8d1d959d9e0b/pkg/logger/logger.go (about)

     1  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
     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 logger
    16  
    17  import (
    18  	"os"
    19  	"runtime"
    20  	"strings"
    21  
    22  	"go.uber.org/zap"
    23  	"go.uber.org/zap/zapcore"
    24  	"gopkg.in/natefinch/lumberjack.v2"
    25  )
    26  
    27  const (
    28  	defaultLogFilePath = "/var/log/aws-routed-eni/ebpf-sdk.log"
    29  	defaultLogLevel    = "Debug"
    30  	envLogLevel        = "AWS_EBPF_SDK_LOGLEVEL"
    31  	envLogFilePath     = "AWS_EBPF_SDK_LOG_FILE"
    32  )
    33  
    34  // Log is global variable so that log functions can be directly accessed
    35  var log Logger
    36  
    37  // Fields Type to pass when we want to call WithFields for structured logging
    38  type Fields map[string]interface{}
    39  
    40  // Logger is our contract for the logger
    41  type Logger interface {
    42  	Debugf(format string, args ...interface{})
    43  
    44  	Debug(format string)
    45  
    46  	Infof(format string, args ...interface{})
    47  
    48  	Info(format string)
    49  
    50  	Warnf(format string, args ...interface{})
    51  
    52  	Warn(format string)
    53  
    54  	Errorf(format string, args ...interface{})
    55  
    56  	Error(format string)
    57  
    58  	Fatalf(format string, args ...interface{})
    59  
    60  	Panicf(format string, args ...interface{})
    61  
    62  	WithFields(keyValues Fields) Logger
    63  }
    64  
    65  // Configuration stores the config for the logger
    66  type Configuration struct {
    67  	LogLevel    string
    68  	LogLocation string
    69  }
    70  
    71  // LoadLogConfig returns the log configuration
    72  func LoadLogConfig() *Configuration {
    73  	return &Configuration{
    74  		LogLevel:    GetLogLevel(),
    75  		LogLocation: GetLogLocation(),
    76  	}
    77  }
    78  
    79  // GetLogLocation returns the log file path
    80  func GetLogLocation() string {
    81  	logFilePath := os.Getenv(envLogFilePath)
    82  	if logFilePath == "" {
    83  		logFilePath = defaultLogFilePath
    84  	}
    85  	return logFilePath
    86  }
    87  
    88  // GetLogLevel returns the log level
    89  func GetLogLevel() string {
    90  	logLevel := os.Getenv(envLogLevel)
    91  	switch logLevel {
    92  	case "":
    93  		logLevel = defaultLogLevel
    94  		return logLevel
    95  	default:
    96  		return logLevel
    97  	}
    98  }
    99  
   100  type structuredLogger struct {
   101  	zapLogger *zap.SugaredLogger
   102  }
   103  
   104  // getZapLevel converts log level string to zapcore.Level
   105  func getZapLevel(inputLogLevel string) zapcore.Level {
   106  	lvl := strings.ToLower(inputLogLevel)
   107  
   108  	switch lvl {
   109  	case "debug":
   110  		return zapcore.DebugLevel
   111  	case "info":
   112  		return zapcore.InfoLevel
   113  	case "warn":
   114  		return zapcore.WarnLevel
   115  	case "error":
   116  		return zapcore.ErrorLevel
   117  	case "fatal":
   118  		return zapcore.FatalLevel
   119  	default:
   120  		return zapcore.DebugLevel
   121  	}
   122  }
   123  
   124  func (logf *structuredLogger) Debugf(format string, args ...interface{}) {
   125  	logf.zapLogger.Debugf(format, args...)
   126  }
   127  
   128  func (logf *structuredLogger) Debug(format string) {
   129  	logf.zapLogger.Desugar().Debug(format)
   130  }
   131  
   132  func (logf *structuredLogger) Infof(format string, args ...interface{}) {
   133  	logf.zapLogger.Infof(format, args...)
   134  }
   135  
   136  func (logf *structuredLogger) Info(format string) {
   137  	logf.zapLogger.Desugar().Info(format)
   138  }
   139  
   140  func (logf *structuredLogger) Warnf(format string, args ...interface{}) {
   141  	logf.zapLogger.Warnf(format, args...)
   142  }
   143  
   144  func (logf *structuredLogger) Warn(format string) {
   145  	logf.zapLogger.Desugar().Warn(format)
   146  }
   147  
   148  func (logf *structuredLogger) Error(format string) {
   149  	logf.zapLogger.Desugar().Error(format)
   150  }
   151  
   152  func (logf *structuredLogger) Errorf(format string, args ...interface{}) {
   153  	logf.zapLogger.Errorf(format, args...)
   154  }
   155  
   156  func (logf *structuredLogger) Fatalf(format string, args ...interface{}) {
   157  	logf.zapLogger.Fatalf(format, args...)
   158  }
   159  
   160  func (logf *structuredLogger) Panicf(format string, args ...interface{}) {
   161  	logf.zapLogger.Fatalf(format, args...)
   162  }
   163  
   164  func (logf *structuredLogger) WithFields(fields Fields) Logger {
   165  	var f = make([]interface{}, 0)
   166  	for k, v := range fields {
   167  		f = append(f, k)
   168  		f = append(f, v)
   169  	}
   170  	newLogger := logf.zapLogger.With(f...)
   171  	return &structuredLogger{newLogger}
   172  }
   173  
   174  func getEncoder() zapcore.Encoder {
   175  	encoderConfig := zap.NewProductionEncoderConfig()
   176  	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
   177  	return zapcore.NewJSONEncoder(encoderConfig)
   178  }
   179  
   180  func (logConfig *Configuration) newZapLogger() *structuredLogger {
   181  	var cores []zapcore.Core
   182  
   183  	logLevel := getZapLevel(logConfig.LogLevel)
   184  
   185  	writer := getSDKLogFilePath(logConfig.LogLocation)
   186  
   187  	cores = append(cores, zapcore.NewCore(getEncoder(), writer, logLevel))
   188  
   189  	combinedCore := zapcore.NewTee(cores...)
   190  
   191  	logger := zap.New(combinedCore,
   192  		zap.AddCaller(),
   193  		zap.AddCallerSkip(2),
   194  	)
   195  	defer logger.Sync()
   196  	sugar := logger.Sugar()
   197  
   198  	return &structuredLogger{
   199  		zapLogger: sugar,
   200  	}
   201  }
   202  
   203  // getSDKLogFilePath returns the writer
   204  func getSDKLogFilePath(logFilePath string) zapcore.WriteSyncer {
   205  	var writer zapcore.WriteSyncer
   206  
   207  	if logFilePath == "" {
   208  		writer = zapcore.Lock(os.Stderr)
   209  	} else if strings.ToLower(logFilePath) != "stdout" {
   210  		writer = getLogWriter(logFilePath)
   211  	} else {
   212  		writer = zapcore.Lock(os.Stdout)
   213  	}
   214  
   215  	return writer
   216  }
   217  
   218  // getLogWriter is for lumberjack
   219  func getLogWriter(logFilePath string) zapcore.WriteSyncer {
   220  	lumberJackLogger := &lumberjack.Logger{
   221  		Filename:   logFilePath,
   222  		MaxSize:    100,
   223  		MaxBackups: 5,
   224  		MaxAge:     30,
   225  		Compress:   true,
   226  	}
   227  	return zapcore.AddSync(lumberJackLogger)
   228  }
   229  
   230  // DefaultLogger creates and returns a new default logger.
   231  func DefaultLogger() Logger {
   232  	productionConfig := zap.NewProductionConfig()
   233  	productionConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
   234  	productionConfig.EncoderConfig.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
   235  		_, caller.File, caller.Line, _ = runtime.Caller(8)
   236  		enc.AppendString(caller.FullPath())
   237  	}
   238  	logger, _ := productionConfig.Build()
   239  	defer logger.Sync()
   240  	sugar := logger.Sugar()
   241  	return &structuredLogger{
   242  		zapLogger: sugar,
   243  	}
   244  }
   245  
   246  // Get returns an default instance of the zap logger
   247  func Get() Logger {
   248  	if log == nil {
   249  		logConfig := LoadLogConfig()
   250  		log = New(logConfig)
   251  		log.Info("Initialized new logger as an existing instance was not found")
   252  	}
   253  	return log
   254  }
   255  
   256  // New logger initializes logger
   257  func New(inputLogConfig *Configuration) Logger {
   258  	log = inputLogConfig.newZapLogger()
   259  	log.Info("Constructed new logger instance")
   260  	return log
   261  }