github.com/cs3org/reva/v2@v2.27.7/pkg/logger/logger.go (about)

     1  // Copyright 2018-2021 CERN
     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  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package logger
    20  
    21  import (
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"time"
    26  
    27  	"github.com/mitchellh/mapstructure"
    28  	"github.com/pkg/errors"
    29  	"github.com/rs/zerolog"
    30  )
    31  
    32  func init() {
    33  	zerolog.CallerSkipFrameCount = 2
    34  	zerolog.TimeFieldFormat = time.RFC3339Nano
    35  }
    36  
    37  // Mode changes the logging format.
    38  type Mode string
    39  
    40  const (
    41  	// JSONMode outputs JSON.
    42  	JSONMode Mode = "json"
    43  	// ConsoleMode outputs human-readable logs.
    44  	ConsoleMode Mode = "console"
    45  )
    46  
    47  // Option is the option to use to configure the logger.
    48  type Option func(l *zerolog.Logger)
    49  
    50  // New creates a new logger.
    51  func New(opts ...Option) *zerolog.Logger {
    52  	// create a default logger
    53  	zl := zerolog.New(os.Stderr).With().Timestamp().Caller().Logger()
    54  	for _, opt := range opts {
    55  		opt(&zl)
    56  	}
    57  	return &zl
    58  }
    59  
    60  // WithLevel is an option to configure the logging level.
    61  func WithLevel(lvl string) Option {
    62  	return func(l *zerolog.Logger) {
    63  		zlvl := parseLevel(lvl)
    64  		*l = l.Level(zlvl)
    65  	}
    66  }
    67  
    68  // WithWriter is an option to configure the logging output.
    69  func WithWriter(w io.Writer, m Mode) Option {
    70  	return func(l *zerolog.Logger) {
    71  		if m == ConsoleMode {
    72  			*l = l.Output(zerolog.ConsoleWriter{Out: w, TimeFormat: "2006-01-02 15:04:05.999"})
    73  		} else {
    74  			*l = l.Output(w)
    75  		}
    76  	}
    77  }
    78  
    79  func parseLevel(v string) zerolog.Level {
    80  	if v == "" {
    81  		return zerolog.InfoLevel
    82  	}
    83  
    84  	lvl, err := zerolog.ParseLevel(v)
    85  	if err != nil {
    86  		return zerolog.InfoLevel
    87  	}
    88  
    89  	return lvl
    90  }
    91  
    92  func InitLoggerOrDie(v interface{}, logLevel string) *zerolog.Logger {
    93  	conf := ParseLogConfOrDie(v, logLevel)
    94  	log, err := fromConfig(conf)
    95  	if err != nil {
    96  		fmt.Fprintf(os.Stderr, "error creating logger, exiting ...")
    97  		os.Exit(1)
    98  	}
    99  	return log
   100  }
   101  
   102  func ParseLogConfOrDie(v interface{}, logLevel string) *LogConf {
   103  	c := &LogConf{}
   104  	if err := mapstructure.Decode(v, c); err != nil {
   105  		fmt.Fprintf(os.Stderr, "error decoding log config: %s\n", err.Error())
   106  		os.Exit(1)
   107  	}
   108  
   109  	// if mode is not set, we use console mode, easier for devs
   110  	if c.Mode == "" {
   111  		c.Mode = "console"
   112  	}
   113  
   114  	// Give priority to the log level passed through the command line.
   115  	if logLevel != "" {
   116  		c.Level = logLevel
   117  	}
   118  
   119  	return c
   120  }
   121  
   122  type LogConf struct {
   123  	Output string `mapstructure:"output"`
   124  	Mode   string `mapstructure:"mode"`
   125  	Level  string `mapstructure:"level"`
   126  }
   127  
   128  func fromConfig(conf *LogConf) (*zerolog.Logger, error) {
   129  	if conf.Level == "" {
   130  		conf.Level = zerolog.DebugLevel.String()
   131  	}
   132  
   133  	var opts []Option
   134  	opts = append(opts, WithLevel(conf.Level))
   135  
   136  	w, err := getWriter(conf.Output)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  
   141  	opts = append(opts, WithWriter(w, Mode(conf.Mode)))
   142  
   143  	l := New(opts...)
   144  	sub := l.With().Int("pid", os.Getpid()).Logger()
   145  	return &sub, nil
   146  }
   147  
   148  func getWriter(out string) (io.Writer, error) {
   149  	if out == "stderr" || out == "" {
   150  		return os.Stderr, nil
   151  	}
   152  
   153  	if out == "stdout" {
   154  		return os.Stdout, nil
   155  	}
   156  
   157  	fd, err := os.OpenFile(out, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
   158  	if err != nil {
   159  		err = errors.Wrap(err, "error creating log file: "+out)
   160  		return nil, err
   161  	}
   162  
   163  	return fd, nil
   164  }