func convertFields(fields logrus.Fields) map[string]string {
	m := make(map[string]string)
	for k, v := range fields {
		switch t := v.(type) {
		case time.Time:
			m[k] = t.Format(time.RFC3339)
		default:
			if s, ok := v.(fmt.Stringer); ok {
				m[k] = s.String()
			} else {
				m[k] = fmt.Sprintf("%+v", t)
			}
		}
	}
	return m
}
convertFields converts from log.Fields to map[string]string so that we can
report extra fields to Rollbar
package rollrus
import (
	"fmt"
	"os"
	"time"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/stvp/roll"
)
var defaultTriggerLevels = []logrus.Level{
	logrus.ErrorLevel,
	logrus.FatalLevel,
	logrus.PanicLevel,
}
type Hook struct {
	roll.Client
	triggers        []logrus.Level
	ignoredErrors   map[error]struct{}
	ignoreErrorFunc func(error) bool
	reported bool
}
type OptionFunc func(*Hook)
var wellKnownErrorFields = []string{
	logrus.ErrorKey, "err",
}
func WithLevels(levels ...logrus.Level) OptionFunc {
	return func(h *Hook) {
		h.triggers = levels
	}
}
func WithIgnoredErrors(errors ...error) OptionFunc {
	return func(h *Hook) {
		for _, e := range errors {
			h.ignoredErrors[e] = struct{}{}
		}
	}
}
func WithIgnoreErrorFunc(fn func(error) bool) OptionFunc {
	return func(h *Hook) {
		h.ignoreErrorFunc = fn
	}
}
func NewHook(token string, env string, opts ...OptionFunc) *Hook {
	h := NewHookForLevels(token, env, defaultTriggerLevels)
	for _, o := range opts {
		o(h)
	}
	return h
}
func NewHookForLevels(token string, env string, levels []logrus.Level) *Hook {
	return &Hook{
		Client:          roll.New(token, env),
		triggers:        levels,
		ignoredErrors:   make(map[error]struct{}),
		ignoreErrorFunc: func(error) bool { return false },
	}
}
func SetupLogging(token, env string) {
	setupLogging(token, env, defaultTriggerLevels)
}
func SetupLoggingForLevels(token, env string, levels []logrus.Level) {
	setupLogging(token, env, levels)
}
func setupLogging(token, env string, levels []logrus.Level) {
	logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true})
	if token != "" {
		logrus.AddHook(NewHookForLevels(token, env, levels))
	}
}
func (r *Hook) ReportPanic() {
	if p := recover(); p != nil {
		if _, err := r.Client.Critical(fmt.Errorf("panic: %q", p), nil); err != nil {
			fmt.Fprintf(os.Stderr, "reporting_panic=false err=%q\n", err)
		}
		panic(p)
	}
}
func ReportPanic(token, env string) {
	if token != "" {
		h := &Hook{Client: roll.New(token, env)}
		h.ReportPanic()
	}
}
func (r *Hook) Levels() []logrus.Level {
	if r.triggers == nil {
		return defaultTriggerLevels
	}
	return r.triggers
}
func (r *Hook) Fire(entry *logrus.Entry) error {
	trace, cause := extractError(entry)
	if _, ok := r.ignoredErrors[cause]; ok {
		return nil
	}
	if r.ignoreErrorFunc(cause) {
		return nil
	}
	m := convertFields(entry.Data)
	if _, exists := m["time"]; !exists {
		m["time"] = entry.Time.Format(time.RFC3339)
	}
	return r.report(entry, cause, m, trace)
}
func (r *Hook) report(entry *logrus.Entry, cause error, m map[string]string, trace []uintptr) (err error) {
	hasTrace := len(trace) > 0
	level := entry.Level
	r.reported = true
	switch {
	case hasTrace && level == logrus.FatalLevel:
		_, err = r.Client.CriticalStack(cause, trace, m)
	case hasTrace && level == logrus.PanicLevel:
		_, err = r.Client.CriticalStack(cause, trace, m)
	case hasTrace && level == logrus.ErrorLevel:
		_, err = r.Client.ErrorStack(cause, trace, m)
	case hasTrace && level == logrus.WarnLevel:
		_, err = r.Client.WarningStack(cause, trace, m)
	case level == logrus.FatalLevel || level == logrus.PanicLevel:
		_, err = r.Client.Critical(cause, m)
	case level == logrus.ErrorLevel:
		_, err = r.Client.Error(cause, m)
	case level == logrus.WarnLevel:
		_, err = r.Client.Warning(cause, m)
	case level == logrus.InfoLevel:
		_, err = r.Client.Info(entry.Message, m)
	case level == logrus.DebugLevel:
		_, err = r.Client.Debug(entry.Message, m)
	}
	return err
}