func (queryWriter *QueryWriter) Start() {
	for {
		select {
		case query := <-queryWriter.signalWriteQuery:
			queryWriter.captureQuery(query)
			break
		case <-queryWriter.serializationTicker.C:
			err := queryWriter.dumpBufferedQueries()
			if err != nil {
				queryWriter.logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCensorIOError).Errorln("Can't dump buffered queries")
			}
			queryWriter.serializationTicker.Stop()
			queryWriter.serializationTicker = time.NewTicker(queryWriter.serializationTimeout)
			break
		case <-queryWriter.signalBackgroundExit:
			queryWriter.serializationTicker.Stop()
			queryWriter.DumpQueries()
			err := queryWriter.logStorage.Close()
			if err != nil {
				queryWriter.logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCensorIOError).Errorln("Error occurred on exit QueryWriter instance")
			}
			return
		case <-queryWriter.signalShutdown:
			queryWriter.serializationTicker.Stop()
			queryWriter.DumpQueries()
			err := queryWriter.logStorage.Close()
			if err != nil {
				queryWriter.logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCensorIOError).Errorln("Error occurred on shutdown QueryWriter instance")
			}
			return
		default:
		}
	}
}
Start starts background logging of input queries. Should be called in separate goroutine
package common
import (
	"bytes"
	"encoding/json"
	"github.com/cossacklabs/acra/logging"
	log "github.com/sirupsen/logrus"
	"io"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"
)
const DefaultSerializationTimeout = time.Second
const DefaultWriteQueryChannelSize = 50
type QueryInfo struct {
	RawQuery    string `json:"raw_query"`
	IsForbidden bool   `json:"_blacklisted_by_web_config"`
}
type LogStorage interface {
	io.Closer
	ReadAll() ([]byte, error)
	WriteAll([]byte) error
	Append([]byte) error
}
type QueryWriter struct {
	Queries              []*QueryInfo
	logStorage           LogStorage
	queryIndex           int
	mutex                *sync.RWMutex
	signalBackgroundExit chan bool
	signalWriteQuery     chan string
	signalShutdown       chan os.Signal
	serializationTimeout time.Duration
	serializationTicker  *time.Ticker
	logger               *log.Entry
}
func NewFileQueryWriter(filePath string) (*QueryWriter, error) {
	writer := &QueryWriter{
		queryIndex:           0,
		mutex:                &sync.RWMutex{},
		serializationTimeout: DefaultSerializationTimeout,
		serializationTicker:  time.NewTicker(DefaultSerializationTimeout),
		logger:               log.WithField("internal_object", "querywriter"),
		signalBackgroundExit: make(chan bool),
		signalWriteQuery:     make(chan string, DefaultWriteQueryChannelSize),
		signalShutdown:       make(chan os.Signal, 2),
	}
	signal.Notify(writer.signalShutdown, os.Interrupt, syscall.SIGTERM)
	storage, err := NewFileLogStorage(filePath)
	if err != nil {
		writer.logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCensorIOError).Errorln("Can't create QueryWriter instance")
		return nil, err
	}
	writer.logStorage = storage
	err = writer.readStoredQueries()
	if err != nil {
		writer.logger.WithError(ErrCantReadQueriesFromStorageError).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCensorIOError).Errorln("Can't create QueryWriter instance")
		writer.logStorage.Close()
		return nil, err
	}
	return writer, nil
}
func (queryWriter *QueryWriter) WalkQueries(visitor func(query *QueryInfo) error) error {
	queryWriter.mutex.Lock()
	defer queryWriter.mutex.Unlock()
	for _, query := range queryWriter.Queries {
		if err := visitor(query); err != nil {
			return err
		}
	}
	return nil
}
func (queryWriter *QueryWriter) DumpQueries() error {
	queryWriter.mutex.Lock()
	rawData := queryWriter.serializeQueries(queryWriter.Queries)
	queryWriter.mutex.Unlock()
	if err := queryWriter.logStorage.WriteAll(rawData); err != nil {
		queryWriter.logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCensorIOError).Errorln("Can't dump queries to storage")
		return err
	}
	return nil
}
func (queryWriter *QueryWriter) Free() {
	queryWriter.DumpQueries()
	queryWriter.signalBackgroundExit <- true
	queryWriter.reset()
}