github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/log/doc.go (about)

     1  /*
     2  Package log15 provides an opinionated, simple toolkit for best-practice logging that is
     3  both human and machine readable. It is modeled after the standard library's io and net/http
     4  packages.
     5  
     6  This package enforces you to only log key/value pairs. Keys must be strings. Values may be
     7  any type that you like. The default output format is logfmt, but you may also choose to use
     8  JSON instead if that suits you. Here's how you log:
     9  
    10      log.Info("page accessed", "path", r.URL.Path, "user_id", user.id)
    11  
    12  This will output a line that looks like:
    13  
    14       lvl=info t=2014-05-02T16:07:23-0700 msg="page accessed" path=/org/71/profile user_id=9
    15  
    16  Getting Started
    17  
    18  To get started, you'll want to import the library:
    19  
    20      import log "github.com/inconshreveable/log15"
    21  
    22  
    23  Now you're ready to start logging:
    24  
    25      func main() {
    26          log.Info("Program starting", "args", os.Args())
    27      }
    28  
    29  
    30  Convention
    31  
    32  Because recording a human-meaningful message is common and good practice, the first argument to every
    33  logging method is the value to the *implicit* key 'msg'.
    34  
    35  Additionally, the level you choose for a message will be automatically added with the key 'lvl', and so
    36  will the current timestamp with key 't'.
    37  
    38  You may supply any additional context as a set of key/value pairs to the logging function. log15 allows
    39  you to favor terseness, ordering, and speed over safety. This is a reasonable tradeoff for
    40  logging functions. You don't need to explicitly state keys/values, log15 understands that they alternate
    41  in the variadic argument list:
    42  
    43      log.Warn("size out of bounds", "low", lowBound, "high", highBound, "val", val)
    44  
    45  If you really do favor your type-safety, you may choose to pass a log.Ctx instead:
    46  
    47      log.Warn("size out of bounds", log.Ctx{"low": lowBound, "high": highBound, "val": val})
    48  
    49  
    50  Context loggers
    51  
    52  Frequently, you want to add context to a logger so that you can track actions associated with it. An http
    53  request is a good example. You can easily create new loggers that have context that is automatically included
    54  with each log line:
    55  
    56      requestlogger := log.New("path", r.URL.Path)
    57  
    58      // later
    59      requestlogger.Debug("db txn commit", "duration", txnTimer.Finish())
    60  
    61  This will output a log line that includes the path context that is attached to the logger:
    62  
    63      lvl=dbug t=2014-05-02T16:07:23-0700 path=/repo/12/add_hook msg="db txn commit" duration=0.12
    64  
    65  
    66  Handlers
    67  
    68  The Handler interface defines where log lines are printed to and how they are formatted. Handler is a
    69  single interface that is inspired by net/http's handler interface:
    70  
    71      type Handler interface {
    72          Log(r *Record) error
    73      }
    74  
    75  
    76  Handlers can filter records, format them, or dispatch to multiple other Handlers.
    77  This package implements a number of Handlers for common logging patterns that are
    78  easily composed to create flexible, custom logging structures.
    79  
    80  Here's an example handler that prints logfmt output to Stdout:
    81  
    82      handler := log.StreamHandler(os.Stdout, log.LogfmtFormat())
    83  
    84  Here's an example handler that defers to two other handlers. One handler only prints records
    85  from the rpc package in logfmt to standard out. The other prints records at Error level
    86  or above in JSON formatted output to the file /var/log/service.json
    87  
    88      handler := log.MultiHandler(
    89          log.LvlFilterHandler(log.LvlError, log.Must.FileHandler("/var/log/service.json", log.JSONFormat())),
    90          log.MatchFilterHandler("pkg", "app/rpc" log.StdoutHandler())
    91      )
    92  
    93  Logging File Names and Line Numbers
    94  
    95  This package implements three Handlers that add debugging information to the
    96  context, CallerFileHandler, CallerFuncHandler and CallerStackHandler. Here's
    97  an example that adds the source file and line number of each logging call to
    98  the context.
    99  
   100      h := log.CallerFileHandler(log.StdoutHandler)
   101      log.Root().SetHandler(h)
   102      ...
   103      log.Error("open file", "err", err)
   104  
   105  This will output a line that looks like:
   106  
   107      lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" caller=data.go:42
   108  
   109  Here's an example that logs the call stack rather than just the call site.
   110  
   111      h := log.CallerStackHandler("%+v", log.StdoutHandler)
   112      log.Root().SetHandler(h)
   113      ...
   114      log.Error("open file", "err", err)
   115  
   116  This will output a line that looks like:
   117  
   118      lvl=eror t=2014-05-02T16:07:23-0700 msg="open file" err="file not found" stack="[pkg/data.go:42 pkg/cmd/main.go]"
   119  
   120  The "%+v" format instructs the handler to include the path of the source file
   121  relative to the compile time GOPATH. The github.com/go-stack/stack package
   122  documents the full list of formatting verbs and modifiers available.
   123  
   124  Custom Handlers
   125  
   126  The Handler interface is so simple that it's also trivial to write your own. Let's create an
   127  example handler which tries to write to one handler, but if that fails it falls back to
   128  writing to another handler and includes the error that it encountered when trying to write
   129  to the primary. This might be useful when trying to log over a network socket, but if that
   130  fails you want to log those records to a file on disk.
   131  
   132      type BackupHandler struct {
   133          Primary Handler
   134          Secondary Handler
   135      }
   136  
   137      func (h *BackupHandler) Log (r *Record) error {
   138          err := h.Primary.Log(r)
   139          if err != nil {
   140              r.Ctx = append(ctx, "primary_err", err)
   141              return h.Secondary.Log(r)
   142          }
   143          return nil
   144      }
   145  
   146  This pattern is so useful that a generic version that handles an arbitrary number of Handlers
   147  is included as part of this library called FailoverHandler.
   148  
   149  Logging Expensive Operations
   150  
   151  Sometimes, you want to log values that are extremely expensive to compute, but you don't want to pay
   152  the price of computing them if you haven't turned up your logging level to a high level of detail.
   153  
   154  This package provides a simple type to annotate a logging operation that you want to be evaluated
   155  lazily, just when it is about to be logged, so that it would not be evaluated if an upstream Handler
   156  filters it out. Just wrap any function which takes no arguments with the log.Lazy type. For example:
   157  
   158      func factorRSAKey() (factors []int) {
   159          // return the factors of a very large number
   160      }
   161  
   162      log.Debug("factors", log.Lazy{factorRSAKey})
   163  
   164  If this message is not logged for any reason (like logging at the Error level), then
   165  factorRSAKey is never evaluated.
   166  
   167  Dynamic context values
   168  
   169  The same log.Lazy mechanism can be used to attach context to a logger which you want to be
   170  evaluated when the message is logged, but not when the logger is created. For example, let's imagine
   171  a game where you have Player objects:
   172  
   173      type Player struct {
   174          name string
   175          alive bool
   176          log.Logger
   177      }
   178  
   179  You always want to log a player's name and whether they're alive or dead, so when you create the player
   180  object, you might do:
   181  
   182      p := &Player{name: name, alive: true}
   183      p.Logger = log.New("name", p.name, "alive", p.alive)
   184  
   185  Only now, even after a player has died, the logger will still report they are alive because the logging
   186  context is evaluated when the logger was created. By using the Lazy wrapper, we can defer the evaluation
   187  of whether the player is alive or not to each log message, so that the log records will reflect the player's
   188  current state no matter when the log message is written:
   189  
   190      p := &Player{name: name, alive: true}
   191      isAlive := func() bool { return p.alive }
   192      player.Logger = log.New("name", p.name, "alive", log.Lazy{isAlive})
   193  
   194  Terminal Format
   195  
   196  If log15 detects that stdout is a terminal, it will configure the default
   197  handler for it (which is log.StdoutHandler) to use TerminalFormat. This format
   198  logs records nicely for your terminal, including color-coded output based
   199  on log level.
   200  
   201  Error Handling
   202  
   203  Becasuse log15 allows you to step around the type system, there are a few ways you can specify
   204  invalid arguments to the logging functions. You could, for example, wrap something that is not
   205  a zero-argument function with log.Lazy or pass a context key that is not a string. Since logging libraries
   206  are typically the mechanism by which errors are reported, it would be onerous for the logging functions
   207  to return errors. Instead, log15 handles errors by making these guarantees to you:
   208  
   209  - Any log record containing an error will still be printed with the error explained to you as part of the log record.
   210  
   211  - Any log record containing an error will include the context key LOG15_ERROR, enabling you to easily
   212  (and if you like, automatically) detect if any of your logging calls are passing bad values.
   213  
   214  Understanding this, you might wonder why the Handler interface can return an error value in its Log method. Handlers
   215  are encouraged to return errors only if they fail to write their log records out to an external source like if the
   216  syslog daemon is not responding. This allows the construction of useful handlers which cope with those failures
   217  like the FailoverHandler.
   218  
   219  Library Use
   220  
   221  log15 is intended to be useful for library authors as a way to provide configurable logging to
   222  users of their library. Best practice for use in a library is to always disable all output for your logger
   223  by default and to provide a public Logger instance that consumers of your library can configure. Like so:
   224  
   225      package yourlib
   226  
   227      import "github.com/inconshreveable/log15"
   228  
   229      var Log = log.New()
   230  
   231      func init() {
   232          Log.SetHandler(log.DiscardHandler())
   233      }
   234  
   235  Users of your library may then enable it if they like:
   236  
   237      import "github.com/inconshreveable/log15"
   238      import "example.com/yourlib"
   239  
   240      func main() {
   241          handler := // custom handler setup
   242          yourlib.Log.SetHandler(handler)
   243      }
   244  
   245  Best practices attaching logger context
   246  
   247  The ability to attach context to a logger is a powerful one. Where should you do it and why?
   248  I favor embedding a Logger directly into any persistent object in my application and adding
   249  unique, tracing context keys to it. For instance, imagine I am writing a web browser:
   250  
   251      type Tab struct {
   252          url string
   253          render *RenderingContext
   254          // ...
   255  
   256          Logger
   257      }
   258  
   259      func NewTab(url string) *Tab {
   260          return &Tab {
   261              // ...
   262              url: url,
   263  
   264              Logger: log.New("url", url),
   265          }
   266      }
   267  
   268  When a new tab is created, I assign a logger to it with the url of
   269  the tab as context so it can easily be traced through the logs.
   270  Now, whenever we perform any operation with the tab, we'll log with its
   271  embedded logger and it will include the tab title automatically:
   272  
   273      tab.Debug("moved position", "idx", tab.idx)
   274  
   275  There's only one problem. What if the tab url changes? We could
   276  use log.Lazy to make sure the current url is always written, but that
   277  would mean that we couldn't trace a tab's full lifetime through our
   278  logs after the user navigate to a new URL.
   279  
   280  Instead, think about what values to attach to your loggers the
   281  same way you think about what to use as a key in a SQL database schema.
   282  If it's possible to use a natural key that is unique for the lifetime of the
   283  object, do so. But otherwise, log15's ext package has a handy RandId
   284  function to let you generate what you might call "surrogate keys"
   285  They're just random hex identifiers to use for tracing. Back to our
   286  Tab example, we would prefer to set up our Logger like so:
   287  
   288          import logext "github.com/inconshreveable/log15/ext"
   289  
   290          t := &Tab {
   291              // ...
   292              url: url,
   293          }
   294  
   295          t.Logger = log.New("id", logext.RandId(8), "url", log.Lazy{t.getUrl})
   296          return t
   297  
   298  Now we'll have a unique traceable identifier even across loading new urls, but
   299  we'll still be able to see the tab's current url in the log messages.
   300  
   301  Must
   302  
   303  For all Handler functions which can return an error, there is a version of that
   304  function which will return no error but panics on failure. They are all available
   305  on the Must object. For example:
   306  
   307      log.Must.FileHandler("/path", log.JSONFormat)
   308      log.Must.NetHandler("tcp", ":1234", log.JSONFormat)
   309  
   310  Inspiration and Credit
   311  
   312  All of the following excellent projects inspired the design of this library:
   313  
   314  code.google.com/p/log4go
   315  
   316  github.com/op/go-logging
   317  
   318  github.com/technoweenie/grohl
   319  
   320  github.com/Sirupsen/logrus
   321  
   322  github.com/kr/logfmt
   323  
   324  github.com/spacemonkeygo/spacelog
   325  
   326  golang's stdlib, notably io and net/http
   327  
   328  The Name
   329  
   330  https://xkcd.com/927/
   331  
   332  */
   333  package log