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