# log

[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](https://godoc.internal.justin.tv/code.justin.tv/creator-collab/log)
[![Build Status](https://jenkins.internal.justin.tv/buildStatus/icon?job=creator-collab/log/master)](https://jenkins.internal.justin.tv/job/creator-collab/job/log/job/master/)
[![Go Report Card](https://goreportcard.internal.justin.tv/badge/code.justin.tv/creator-collab/log)](https://goreportcard.internal.justin.tv/report/code.justin.tv/creator-collab/log)

Package log is an opinionated logger that integrates really well with Rollbar and CloudWatch logs.
It supports semi-structured logging, and logging stack traces when used with package log/errors.

## Usage

Package log provides two loggers, a production logger and a development logger.  The production logger
is meant to be used when your service is running in AWS.  It sends events to Rollbar, and writes
events to standard error as JSON objects.

```go
package main

import (
  "code.justin.tv/creator-collab/log"
)

func main() {
    logger := NewProductionLogger("ROLLBAR_TOKEN", "staging")
    logger.Info("service is starting", log.Fields{
        "numWorkers": 3,
    })
}
```

The development logger is meant to be used when running a service locally.  It writes to standard
error using a human readable format.

```go
logger := NewDevelopmentLogger()
```

### Logging Errors

You can log errors using the `Error()` method on the logger object.

A problem that we've had with logging errors is that they don't contain enough information to help with
debugging.  They may not contain stack traces, or the stack trace may point at the line the error gets
logged, instead of where the error occurred.  We have addressed this by providing package log/error.  It
supports sending stack traces to both Rollbar and CloudWatch logs.

Package log/error works similarly to popular libraries like [pkg/errors](https://github.com/pkg/errors).
You wrap errors, creating a new error that has a stack trace that points to where the wrapping occurs.
This is usually close enough to where the original error occurrered to be helpful when debugging.
Unlike [pkg/errors](https://github.com/pkg/errors), package log/errors was written to be compatible with
[rollbar-go](https://github.com/rollbar/rollbar-go).  Package log uses rollbar-go to send events to Rollbar,
and so errors sent to Rollbar will have stack traces.

#### Example

```go
package host

import (
    "code.justin.tv/creator-collab/log"
    "code.justin.tv/creator-collab/log/errors"
)

func (s *service) hostChannel(logger log.Logger, sourceID, targetID string) {
    // snip
	
    err := s.db.hostChannel(sourceID, targetID)
    if err != nil {
    	logger.Error(err)
    }
}

func (d *db) hostChannel(sourceID, targetID string) error {
	// snip
	
	err := db.execute(query, sourceID, targetID)
	if err != nil {
		// Wrap the error so that we have a stack trace.
		return errors.Wrap(err, "updating host table failed")
	} 
}
```

### Fields

Package log supports semi structured logging, by allowing you to attach fields to log events and errors.
These should be used to attach details that help with debugging or analysis, such as parameters.
We prefer using fields instead of embedding details into messages
(e.g. `fmt.Sprintf("hosting failed, sourceID=%s", sourceID)`) because Rollbar and CloudWatch logs can query
and aggregate using these field values, which can be useful when analyzing an issue.


#### Example

```go
logger.Info("starting service", log.Fields{
	"numWorkers": 5,
})

logger.Error(err, log.Fields{
	"userID": "234234342",
})

err := errors.New("hosting failed", errors.Fields{
	"sourceID": "222222",
	"targetID": "213123",
})

err := errors.Wrap(innerErr, errors.Fields{
	"sourceID": "222222",
	"targetID": "213123",
})
```

## Feedback

Slack: \#collab-feedback

