func (f AcraJSONFormatter) Format(e *logrus.Entry) ([]byte, error) {
	ne := copyEntry(e, f.Fields)
	if value, ok := ne.Data[FieldKeyUnixTime]; !ok || value == 0 {
		ne.Data[FieldKeyUnixTime] = unixTimeWithMilliseconds(e)
	}
	dataBytes, err := formatEntry(ne, f.Formatter, f.Hooks)
	releaseEntry(ne)
	return dataBytes, err
}
Format formats an entry to a AcraJSON format according to the given Formatter and Fields.

Note: the given entry is copied and not changed during the formatting process.
package logging
import (
	"bytes"
	"fmt"
	"sync"
	"time"
	"github.com/cossacklabs/acra/utils"
	"github.com/sirupsen/logrus"
	log "github.com/sirupsen/logrus"
)
func TextFormatter() Formatter {
	return &AcraTextFormatter{
		Formatter: &logrus.TextFormatter{
			FullTimestamp:    true,
			TimestampFormat:  time.RFC3339,
			QuoteEmptyFields: true,
		},
		Hooks: nil,
	}
}
func JSONFormatter() Formatter {
	return &AcraJSONFormatter{
		Formatter: &logrus.JSONFormatter{
			FieldMap:        JSONFieldMap,
			TimestampFormat: time.RFC3339,
		},
		Hooks:  nil,
		Fields: nil,
	}
}
func CEFFormatter() Formatter {
	return &AcraCEFFormatter{
		CEFTextFormatter: CEFTextFormatter{
			TimestampFormat: time.RFC3339,
		},
		Fields: nil,
		Hooks:  nil,
	}
}
var entryPool = sync.Pool{
	New: func() interface{} {
		return &logrus.Entry{}
	},
}
func copyEntry(e *logrus.Entry, fields logrus.Fields) *logrus.Entry {
	ne := entryPool.Get().(*logrus.Entry)
	ne.Message = e.Message
	ne.Level = e.Level
	ne.Time = e.Time
	ne.Data = logrus.Fields{}
	for k, v := range fields {
		ne.Data[k] = v
	}
	for k, v := range e.Data {
		ne.Data[k] = v
	}
	return ne
}
func releaseEntry(e *logrus.Entry) {
	entryPool.Put(e)
}
type AcraTextFormatter struct {
	logrus.Formatter
	Hooks []FormatterHook
}
func (f *AcraTextFormatter) SetServiceName(serviceName string) {
}
func (f *AcraTextFormatter) SetHooks(hooks []FormatterHook) {
	f.Hooks = hooks
}
func (f *AcraTextFormatter) GetHooks() []FormatterHook {
	return f.Hooks
}
type AcraJSONFormatter struct {
	logrus.Formatter
	logrus.Fields
	Hooks []FormatterHook
}
func (f *AcraJSONFormatter) SetServiceName(serviceName string) {
	fields := log.Fields{FieldKeyProduct: serviceName}
	for k, v := range extraJSONFields {
		if _, ok := fields[k]; !ok {
			fields[k] = v
		}
	}
	f.Fields = fields
}
func (f *AcraJSONFormatter) SetHooks(hooks []FormatterHook) {
	f.Hooks = hooks
}
func (f *AcraJSONFormatter) GetHooks() []FormatterHook {
	return f.Hooks
}
type AcraCEFFormatter struct {
	CEFTextFormatter
	logrus.Fields
	Hooks []FormatterHook
}
func (f *AcraCEFFormatter) SetServiceName(serviceName string) {
	fields := log.Fields{FieldKeyProduct: serviceName}
	for k, v := range extraJSONFields {
		if _, ok := fields[k]; !ok {
			fields[k] = v
		}
	}
	for k, v := range extraCEFFields {
		if _, ok := fields[k]; !ok {
			fields[k] = v
		}
	}
	f.Fields = fields
}
func (f *AcraCEFFormatter) SetHooks(hooks []FormatterHook) {
	f.Hooks = hooks
}
func (f *AcraCEFFormatter) GetHooks() []FormatterHook {
	return f.Hooks
}
var (
	extraJSONFields = logrus.Fields{
		FieldKeyProduct:  "acra",
		FieldKeyUnixTime: 0,
		FieldKeyVersion:  utils.VERSION,
	}
	extraCEFFields = logrus.Fields{
		FieldKeyVendor:    "cossacklabs",
		FieldKeyEventCode: EventCodeGeneral,
	}
	JSONFieldMap = logrus.FieldMap{
		logrus.FieldKeyTime:  "timestamp",
		logrus.FieldKeyMsg:   "msg",
		logrus.FieldKeyLevel: "level",
	}
)
func (f *AcraTextFormatter) Format(e *logrus.Entry) ([]byte, error) {
	return formatEntry(e, f.Formatter, f.Hooks)
}