<commit_before>package main

import (
	"flag"
	"github.com/howeyc/fsnotify"
	daemon "github.com/sevlyar/go-daemon"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

var source = flag.String("s", "", "Source path")
var distanation = flag.String("d", "", "Distanation path")
var signal = flag.String("signal", "", "send signal to daemon")
var pidf = flag.String("pid", "", "pid file")
var logf = flag.String("log", "", "log file")

type UpdateHeader struct {
	Event fsnotify.FileEvent
	Path  *Directory
}

type Directory struct {
	Path        string
	Update      chan UpdateHeader
	Quit        chan bool
	WatcherQuit chan bool
	Exit        chan bool
	fileWatcher *fsnotify.Watcher
}

func main() {

	handler := func(sig os.Signal) error {
		log.Println("signal:", sig)
		if sig == syscall.SIGTERM {
			os.Exit(0)
			return daemon.ErrStop
		}
		return nil
	}
	var (
		logfile,
		pidfile string
	)
	if len(*logf) == 0 {
		logfile = "/var/log/lnsync.log"
	} else {
		logfile = *logf
	}

	if len(*pidf) == 0 {
		pidfile = "/var/run/lnsync.pid"
	} else {
		pidfile = *pidf
	}

	// Define command: command-line arg, system signal and handler
	daemon.AddCommand(daemon.StringFlag(signal, "term"), syscall.SIGTERM, handler)
	daemon.AddCommand(daemon.StringFlag(signal, "reload"), syscall.SIGHUP, handler)
	flag.Parse()
	dmn := &daemon.Context{
		PidFileName: pidfile,
		PidFilePerm: 0644,
		LogFileName: logfile,
		LogFilePerm: 0640,
		WorkDir:     "/",
		Umask:       027,
	}

	if len(daemon.ActiveFlags()) > 0 {
		d, err := dmn.Search()
		if err != nil {
			log.Fatalln("Unable send signal to the daemon:", err)
		}
		daemon.SendCommands(d)
		return
	}
	child, _ := dmn.Reborn()

	if child != nil {
		return
	}
	defer dmn.Release()
	chanQuit := make(chan bool)
	chanExit := make(chan bool)
	chanWatcheQuit := make(chan bool)
	chanUpdate := make(chan UpdateHeader)
	dirs := strings.Split(*source, ",")
	manageDirs := make([]Directory, 0)
	for idx, dir := range dirs {
		manageDirs = append(manageDirs, Directory{Path: dir,
			Update:      chanUpdate,
			Quit:        chanQuit,
			WatcherQuit: chanWatcheQuit,
			Exit:        chanExit,
		})
		go manageDirs[idx].InitFSWatch()
	}

	log.Println("Starting pre-cleaner process")
	if err := cleanDirs(manageDirs, *distanation); err != nil {
		log.Fatalln("First clean dirs was corrapted: " + err.Error())
	}
	exitCnt := len(manageDirs)

	go func() {
		for {
			select {
			case _ = <-chanQuit:
				for _, dir := range manageDirs {
					dir.WatcherQuit <- true
				}
			case fileUpdate := <-chanUpdate:
				go fileUpdate.Path.UpdateDirs(*distanation, fileUpdate)
			case _ = <-chanExit:
				exitCnt--
			}
			if exitCnt == 0 {
				return
			}
		}
	}()
	err := daemon.ServeSignals()
	if err != nil {
		log.Println("Error:", err)
	}
}

func cleanDirs(sources []Directory, target string) (err error) {
	filenames := make(map[string]string)
	for _, source := range sources {
		files, err := ioutil.ReadDir(source.Path)
		if err != nil {
			return err
		}
		for _, f := range files {
			filenames[f.Name()] = source.Path
		}
	}
	files, err := ioutil.ReadDir(target)
	if err != nil {
		return err
	}
	for _, f := range files {
		info, err := os.Lstat(target + "/" + f.Name())
		if err != nil {
			return err
		}
		if info.Mode()&os.ModeSymlink == os.ModeSymlink {
			_, err := filepath.EvalSymlinks(target + "/" + f.Name())
			if err != nil {
				log.Println("Unresolved link: " + target + "/" + f.Name() + ". Deleted")
				err := os.Remove(target + "/" + f.Name())
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func (d *Directory) UpdateDirs(dist string, updated UpdateHeader) error {
	if updated.Event.IsCreate() {
		err := os.Symlink(updated.Event.Name, dist+"/"+path.Base(updated.Event.Name))
		if err != nil {
			log.Println(err.Error())
			return err
		}
		log.Println("Updated link: " + updated.Event.Name)
	}
	if updated.Event.IsDelete() {
		err := os.Remove(dist + "/" + path.Base(updated.Event.Name))
		if err != nil {
			log.Println(err.Error())
			return err
		}
		log.Println("Delete link: " + dist + "/" + path.Base(updated.Event.Name))
	}

	return nil
}

func (d *Directory) InitFSWatch() {
	var err error
	d.fileWatcher, err = fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("Filed to initialize file system watcher for <" + d.Path + ">:" + err.Error())
	}

	go d.fsEvent(d.fileWatcher)
	d.StartFSWatch()
	<-d.WatcherQuit
	d.Exit <- true
	d.fileWatcher.Close()
}

func (d *Directory) StartFSWatch() {
	err := d.fileWatcher.Watch(d.Path)
	log.Println("Add directory for watch: " + d.Path)
	if err != nil {
		log.Println("FS Monitor error monitor path [" +
			d.Path + "]: " + err.Error())
	}
}

func (d *Directory) StopFSWatch() {
	err := d.fileWatcher.RemoveWatch(d.Path)
	if err != nil {
		log.Println("Remove directory from watching [" + d.Path +
			"]: " + err.Error())
		return
	}
	log.Println("Remove directory from watching: " + d.Path)
}

func (d *Directory) fsEvent(watcher *fsnotify.Watcher) {
	for {
		select {
		case ev := <-watcher.Event:
			d.Update <- UpdateHeader{Event: *ev, Path: d}
		case err := <-watcher.Error:
			log.Println("File watcher exitting... Path: " + d.Path + ". Quit: " + err.Error())
			return
		}
	}
}
<commit_msg>Check imput parameters<commit_after>package main

import (
	"flag"
	"github.com/howeyc/fsnotify"
	daemon "github.com/sevlyar/go-daemon"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

var source = flag.String("s", "", "Source path")
var distanation = flag.String("d", "", "Distanation path")
var signal = flag.String("signal", "", "send signal to daemon")
var pidf = flag.String("pid", "", "pid file")
var logf = flag.String("log", "", "log file")

type UpdateHeader struct {
	Event fsnotify.FileEvent
	Path  *Directory
}

type Directory struct {
	Path        string
	Update      chan UpdateHeader
	Quit        chan bool
	WatcherQuit chan bool
	Exit        chan bool
	fileWatcher *fsnotify.Watcher
}

func main() {

	handler := func(sig os.Signal) error {
		log.Println("signal:", sig)
		if sig == syscall.SIGTERM {
			os.Exit(0)
			return daemon.ErrStop
		}
		return nil
	}
	var (
		logfile,
		pidfile string
	)
	if len(*logf) == 0 {
		logfile = "/var/log/lnsync.log"
	} else {
		logfile = *logf
	}

	if len(*pidf) == 0 {
		pidfile = "/var/run/lnsync.pid"
	} else {
		pidfile = *pidf
	}

	// Define command: command-line arg, system signal and handler
	daemon.AddCommand(daemon.StringFlag(signal, "term"), syscall.SIGTERM, handler)
	daemon.AddCommand(daemon.StringFlag(signal, "reload"), syscall.SIGHUP, handler)
	flag.Parse()
	dmn := &daemon.Context{
		PidFileName: pidfile,
		PidFilePerm: 0644,
		LogFileName: logfile,
		LogFilePerm: 0640,
		WorkDir:     "/",
		Umask:       027,
	}

	if len(daemon.ActiveFlags()) > 0 {
		d, err := dmn.Search()
		if err != nil {
			log.Fatalln("Unable send signal to the daemon:", err)
		}
		daemon.SendCommands(d)
		return
	}
	child, _ := dmn.Reborn()

	if child != nil {
		return
	}
	defer dmn.Release()
	chanQuit := make(chan bool)
	chanExit := make(chan bool)
	chanWatcheQuit := make(chan bool)
	chanUpdate := make(chan UpdateHeader)
	dirs := strings.Split(*source, ",")
	if len(dirs) == 0 || len(*distanation) == 0 {
		flag.PrintDefaults()
		os.Exit(1)
	}
	manageDirs := make([]Directory, 0)
	for idx, dir := range dirs {
		manageDirs = append(manageDirs, Directory{Path: dir,
			Update:      chanUpdate,
			Quit:        chanQuit,
			WatcherQuit: chanWatcheQuit,
			Exit:        chanExit,
		})
		go manageDirs[idx].InitFSWatch()
	}

	log.Println("Starting pre-cleaner process")
	if err := cleanDirs(manageDirs, *distanation); err != nil {
		log.Fatalln("First clean dirs was corrapted: " + err.Error())
	}
	exitCnt := len(manageDirs)

	go func() {
		for {
			select {
			case _ = <-chanQuit:
				for _, dir := range manageDirs {
					dir.WatcherQuit <- true
				}
			case fileUpdate := <-chanUpdate:
				go fileUpdate.Path.UpdateDirs(*distanation, fileUpdate)
			case _ = <-chanExit:
				exitCnt--
			}
			if exitCnt == 0 {
				return
			}
		}
	}()
	err := daemon.ServeSignals()
	if err != nil {
		log.Println("Error:", err)
	}
}

func cleanDirs(sources []Directory, target string) (err error) {
	filenames := make(map[string]string)
	for _, source := range sources {
		files, err := ioutil.ReadDir(source.Path)
		if err != nil {
			return err
		}
		for _, f := range files {
			filenames[f.Name()] = source.Path
		}
	}
	files, err := ioutil.ReadDir(target)
	if err != nil {
		return err
	}
	for _, f := range files {
		info, err := os.Lstat(target + "/" + f.Name())
		if err != nil {
			return err
		}
		if info.Mode()&os.ModeSymlink == os.ModeSymlink {
			_, err := filepath.EvalSymlinks(target + "/" + f.Name())
			if err != nil {
				log.Println("Unresolved link: " + target + "/" + f.Name() + ". Deleted")
				err := os.Remove(target + "/" + f.Name())
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func (d *Directory) UpdateDirs(dist string, updated UpdateHeader) error {
	if updated.Event.IsCreate() {
		err := os.Symlink(updated.Event.Name, dist+"/"+path.Base(updated.Event.Name))
		if err != nil {
			log.Println(err.Error())
			return err
		}
		log.Println("Updated link: " + updated.Event.Name)
	}
	if updated.Event.IsDelete() {
		err := os.Remove(dist + "/" + path.Base(updated.Event.Name))
		if err != nil {
			log.Println(err.Error())
			return err
		}
		log.Println("Delete link: " + dist + "/" + path.Base(updated.Event.Name))
	}

	return nil
}

func (d *Directory) InitFSWatch() {
	var err error
	d.fileWatcher, err = fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("Filed to initialize file system watcher for <" + d.Path + ">:" + err.Error())
	}

	go d.fsEvent(d.fileWatcher)
	d.StartFSWatch()
	<-d.WatcherQuit
	d.Exit <- true
	d.fileWatcher.Close()
}

func (d *Directory) StartFSWatch() {
	err := d.fileWatcher.Watch(d.Path)
	log.Println("Add directory for watch: " + d.Path)
	if err != nil {
		log.Println("FS Monitor error monitor path [" +
			d.Path + "]: " + err.Error())
	}
}

func (d *Directory) StopFSWatch() {
	err := d.fileWatcher.RemoveWatch(d.Path)
	if err != nil {
		log.Println("Remove directory from watching [" + d.Path +
			"]: " + err.Error())
		return
	}
	log.Println("Remove directory from watching: " + d.Path)
}

func (d *Directory) fsEvent(watcher *fsnotify.Watcher) {
	for {
		select {
		case ev := <-watcher.Event:
			d.Update <- UpdateHeader{Event: *ev, Path: d}
		case err := <-watcher.Error:
			log.Println("File watcher exitting... Path: " + d.Path + ". Quit: " + err.Error())
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package syslog

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	piazza "github.com/venicegeo/pz-gocommon/gocommon"
)

const (
	SyslogdNetwork = ""
	SyslogdRaddr   = ""
)

func GetRequiredEnvVars() (string, string, string, error) {
	var loggerIndex, loggerType, auditType string
	if loggerIndex = os.Getenv("LOGGER_INDEX"); loggerIndex == "" {
		return "", "", "", errors.New("Elasticsearch index name not set")
	}
	if loggerType = os.Getenv("LOGGER_TYPE"); loggerType == "" {
		return "", "", "", errors.New("Elasticsearch type name not set")
	}
	if auditType = os.Getenv("AUDIT_TYPE"); auditType == "" {
		return "", "", "", errors.New("Elasticsearch audit type name not set")
	}
	return loggerIndex, loggerType, auditType, nil
}

func GetRequiredESIWriters(esi elasticsearch.IIndex, loggerType string, auditType string) (*ElasticWriter, *ElasticWriter, error) {
	var logWriter, auditWriter ElasticWriter
	var indexExisted, typeExisted, auditTypeExisted bool
	var err error
	logWriter = ElasticWriter{Esi: esi}
	if err = logWriter.SetType(loggerType); err != nil {
		return nil, nil, err
	}
	if indexExisted, err = logWriter.CreateIndex(); err != nil {
		return nil, nil, err
	}
	if !indexExisted {
		//fmt.Println("Created index:", esi.IndexName())
	}
	if typeExisted, err = logWriter.CreateType(LogMapping); err != nil {
		return nil, nil, err
	}
	if !typeExisted {
		//fmt.Println("Created type:", loggerType)
	}
	auditWriter = ElasticWriter{Esi: esi}
	if err = auditWriter.SetType(auditType); err != nil {
		return &logWriter, nil, err
	}
	if auditTypeExisted, err = auditWriter.CreateType(LogMapping); err != nil {
		return &logWriter, nil, err
	}
	if !auditTypeExisted {
		//fmt.Println("Created type:", auditType)
	}
	return &logWriter, &auditWriter, err
}

//---------------------------------------------------------------------

// Writer is an interface for writing a Message to some sort of output.
type Writer interface {
	Write(*Message, bool) error
	writeWork(*Message) error
	Close() error
}

// Reader is an interface for reading Messages from some sort of input.
// count is the number of messages to read: 1 means the latest message,
// 2 means the two latest messages, etc. The newest message is at the end
// of the array.
type Reader interface {
	Read(count int) ([]Message, error)
	GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error)
}

type WriterReader interface {
	Reader
	Writer
}

//---------------------------------------------------------------------

type writeWork func(*Message) error

func aSyncLogic(write writeWork, mssg *Message, async bool) error {
	if async {
		go func() {
			if err := write(mssg); err != nil {
				log.Println(err.Error())
			}
		}()
		return nil
	}
	return write(mssg)
}

//---------------------------------------------------------------------

// FileWriter implements the Writer interface, writing to a given file
type FileWriter struct {
	FileName string
	file     *os.File
}

// Write writes the message to the supplied file.
func (w *FileWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*FileWriter)(nil)
	return w.writeWork(mssg)
}

func (w *FileWriter) writeWork(mssg *Message) (err error) {
	if w == nil || w.FileName == "" {
		return fmt.Errorf("writer not set not set")
	}

	if w.file == nil {
		w.file, err = os.OpenFile(w.FileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return err
		}
	}

	s := mssg.String()
	s += "\n"

	_, err = io.WriteString(w.file, s)
	return err
}

// Close closes the file. The creator of the FileWriter must call this.
func (w *FileWriter) Close() error {
	return w.file.Close()
}

//---------------------------------------------------------------------

//StdoutWriter writes messages to STDOUT
type StdoutWriter struct {
}

//Writes message to STDOUT
func (w *StdoutWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*StdoutWriter)(nil)
	return w.writeWork(mssg)
}

func (w *StdoutWriter) writeWork(mssg *Message) error {
	fmt.Println(mssg.String())
	return nil
}

//Nothing to close for this writer
func (w *StdoutWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MessageWriter implements Reader and Writer, using an array of Messages
// as the backing store
type LocalReaderWriter struct {
	messages []Message
}

// Write writes the message to the backing array
func (w *LocalReaderWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*LocalReaderWriter)(nil)
	return w.writeWork(mssg)
}

func (w *LocalReaderWriter) writeWork(mssg *Message) error {
	if w.messages == nil {
		w.messages = make([]Message, 0)
	}
	w.messages = append(w.messages, *mssg)
	return nil
}

// Read reads messages from the backing array. Will only return as many as are
// available; asking for too many is not an error.
func (w *LocalReaderWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*LocalReaderWriter)(nil)

	if count < 0 {
		return nil, fmt.Errorf("invalid count: %d", count)
	}

	if w.messages == nil || count == 0 {
		return make([]Message, 0), nil
	}

	if count > len(w.messages) {
		count = len(w.messages)
	}

	n := len(w.messages)
	a := w.messages[n-count : n]

	return a, nil
}

func (w *LocalReaderWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("LocalReaderWriter.GetMessages() not supported")
}

func (w *LocalReaderWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// HttpWriter implements Writer, by talking to the actual pz-logger service
type HttpWriter struct {
	url string
	h   piazza.Http
}

func NewHttpWriter(url string, apiKey string) (*HttpWriter, error) {
	w := &HttpWriter{url: url}
	w.h = piazza.Http{
		BaseUrl: url,
		ApiKey:  apiKey,
		//FlightCheck: &piazza.SimpleFlightCheck{},
	}

	return w, nil
}

func (w *HttpWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*HttpWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *HttpWriter) writeWork(mssg *Message) error {
	if jresp := w.h.PzPost("/syslog", mssg); jresp.IsError() {
		return jresp.ToError()
	}
	return nil
}

func (w *HttpWriter) Close() error {
	return nil
}

func (w *HttpWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*HttpWriter)(nil)

	format := &piazza.JsonPagination{
		Page:    0,
		PerPage: count,
		SortBy:  "timeStamp",
		Order:   "desc",
	}
	params := &piazza.HttpQueryParams{}

	mssgs, _, err := w.GetMessages(format, params)
	return mssgs, err

}

// GetMessages is only implemented for HttpWriter as it is most likely the only
// Writer to be used for reading back data.
func (w *HttpWriter) GetMessages(
	format *piazza.JsonPagination,
	params *piazza.HttpQueryParams) ([]Message, int, error) {
	var _ Reader = (*HttpWriter)(nil)

	formatString := format.String()
	paramString := params.String()

	var ext string
	if formatString != "" && paramString != "" {
		ext = "?" + formatString + "&" + paramString
	} else if formatString == "" && paramString != "" {
		ext = "?" + paramString
	} else if formatString != "" && paramString == "" {
		ext = "?" + formatString
	} else if formatString == "" && paramString == "" {
		ext = ""
	} else {
		return nil, 0, errors.New("Internal error: failed to parse query params")
	}

	endpoint := "/syslog" + ext
	jresp := w.h.PzGet(endpoint)
	if jresp.IsError() {
		return nil, 0, jresp.ToError()
	}
	var mssgs []Message
	err := jresp.ExtractData(&mssgs)
	if err != nil {
		return nil, 0, err
	}

	return mssgs, jresp.Pagination.Count, nil
}

//---------------------------------------------------------------------

// SyslogdWriter implements a Writer that writes to the syslogd system service.
// This will almost certainly not work on Windows, but that is okay because Piazza
// does not support Windows.
type SyslogdWriter struct {
	writer *DaemonWriter
}

func (w *SyslogdWriter) initWriter() error {
	if w.writer != nil {
		return nil
	}

	tw, err := Dial(SyslogdNetwork, SyslogdRaddr)
	if err != nil {
		return err
	}

	w.writer = tw

	return nil
}

// Write writes the message to the OS's syslogd system.
func (w *SyslogdWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*SyslogdWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *SyslogdWriter) writeWork(mssg *Message) error {
	if err := w.initWriter(); err != nil {
		return err
	}
	s := mssg.String()
	return w.writer.Write(s)
}

// Close closes the underlying network connection.
func (w *SyslogdWriter) Close() error {
	if w.writer == nil {
		return nil
	}
	return w.writer.Close()
}

//---------------------------------------------------------------------

// ElasticWriter implements the Writer, writing to elasticsearch
type ElasticWriter struct {
	Esi elasticsearch.IIndex
	typ string
	id  string
}

func NewElasticWriter(esi elasticsearch.IIndex, typ string) *ElasticWriter {
	ew := &ElasticWriter{
		Esi: esi,
		typ: typ,
	}
	return ew
}

// Write writes the message to the elasticsearch index, type, id
func (w *ElasticWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*ElasticWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *ElasticWriter) writeWork(mssg *Message) error {
	if w == nil || w.Esi == nil || w.typ == "" {
		return fmt.Errorf("writer not set not set")
	}

	_, err := w.Esi.PostData(w.typ, w.id, mssg)
	return err
}

// SetType sets the type to write to
func (w *ElasticWriter) SetType(typ string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.typ = typ
	return nil
}

func (w *ElasticWriter) CreateIndex() (bool, error) {
	if w == nil || w.Esi == nil {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.IndexExists()
	if err != nil {
		return exists, err
	}
	if !exists {
		return exists, w.Esi.Create("")
	}
	return exists, nil
}

func (w *ElasticWriter) CreateType(mapping string) (bool, error) {
	if w == nil || w.Esi == nil || w.typ == "" {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.TypeExists(w.typ)
	if err != nil {
		return exists, err
	}
	if !exists {
		return exists, w.Esi.SetMapping(w.typ, piazza.JsonString(mapping))
	}
	return exists, nil
}

// SetID sets the id to write to
func (w *ElasticWriter) SetID(id string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.id = id
	return nil
}

// Close does nothing but satisfy an interface.
func (w *ElasticWriter) Close() error {
	return nil
}

func (w *ElasticWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("ElasticWriter.GetMessages not supported")
}
func (w *ElasticWriter) Read(count int) ([]Message, error) {
	return nil, fmt.Errorf("ElasticWriter.Read not supported")
}

//-------------------------------

// NilWriter doesn't do anything
type NilWriter struct {
}

func (*NilWriter) Write(*Message, bool) error {
	var _ Writer = (*NilWriter)(nil)
	return nil
}

func (*NilWriter) writeWork(*Message) error {
	return nil
}

func (*NilWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MultiWriter will write to N different Writers at once. When doing a
// Write or a Close, we call the function on all of them. If any have failed,
// we return the error from the first one that failed.
type MultiWriter struct {
	writers []Writer
}

func NewMultiWriter(ws []Writer) *MultiWriter {
	mw := &MultiWriter{}
	mw.writers = make([]Writer, len(ws))
	copy(mw.writers, ws)
	return mw
}

func (mw *MultiWriter) Write(m *Message, async bool) (err error) {
	var _ Writer = (*HttpWriter)(nil)
	for _, w := range mw.writers {
		e := w.Write(m, async)
		if e != nil && err != nil {
			err = e
		}
	}
	return err
}

func (mw *MultiWriter) writeWork(m *Message) (err error) {
	return nil
}

func (mw *MultiWriter) Close() error {
	var err error

	for _, w := range mw.writers {
		e := w.Close()
		if e != nil && err != nil {
			err = e
		}
	}

	return err
}
<commit_msg>Better log message<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package syslog

import (
	"errors"
	"fmt"
	"io"
	"log"
	"os"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	piazza "github.com/venicegeo/pz-gocommon/gocommon"
)

const (
	SyslogdNetwork = ""
	SyslogdRaddr   = ""
)

func GetRequiredEnvVars() (string, string, string, error) {
	var loggerIndex, loggerType, auditType string
	if loggerIndex = os.Getenv("LOGGER_INDEX"); loggerIndex == "" {
		return "", "", "", errors.New("Elasticsearch index name not set")
	}
	if loggerType = os.Getenv("LOGGER_TYPE"); loggerType == "" {
		return "", "", "", errors.New("Elasticsearch type name not set")
	}
	if auditType = os.Getenv("AUDIT_TYPE"); auditType == "" {
		return "", "", "", errors.New("Elasticsearch audit type name not set")
	}
	return loggerIndex, loggerType, auditType, nil
}

func GetRequiredESIWriters(esi elasticsearch.IIndex, loggerType string, auditType string) (*ElasticWriter, *ElasticWriter, error) {
	var logWriter, auditWriter ElasticWriter
	var indexExisted, typeExisted, auditTypeExisted bool
	var err error
	logWriter = ElasticWriter{Esi: esi}
	if err = logWriter.SetType(loggerType); err != nil {
		return nil, nil, err
	}
	if indexExisted, err = logWriter.CreateIndex(); err != nil {
		return nil, nil, err
	}
	if !indexExisted {
		//fmt.Println("Created index:", esi.IndexName())
	}
	if typeExisted, err = logWriter.CreateType(LogMapping); err != nil {
		return nil, nil, err
	}
	if !typeExisted {
		//fmt.Println("Created type:", loggerType)
	}
	auditWriter = ElasticWriter{Esi: esi}
	if err = auditWriter.SetType(auditType); err != nil {
		return &logWriter, nil, err
	}
	if auditTypeExisted, err = auditWriter.CreateType(LogMapping); err != nil {
		return &logWriter, nil, err
	}
	if !auditTypeExisted {
		//fmt.Println("Created type:", auditType)
	}
	return &logWriter, &auditWriter, err
}

//---------------------------------------------------------------------

// Writer is an interface for writing a Message to some sort of output.
type Writer interface {
	Write(*Message, bool) error
	writeWork(*Message) error
	Close() error
}

// Reader is an interface for reading Messages from some sort of input.
// count is the number of messages to read: 1 means the latest message,
// 2 means the two latest messages, etc. The newest message is at the end
// of the array.
type Reader interface {
	Read(count int) ([]Message, error)
	GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error)
}

type WriterReader interface {
	Reader
	Writer
}

//---------------------------------------------------------------------

type writeWork func(*Message) error

func aSyncLogic(write writeWork, mssg *Message, async bool) error {
	if async {
		go func() {
			if err := write(mssg); err != nil {
				log.Println(fmt.Sprintf("Unable to log message [%s] : %s", mssg.String(), err.Error()))
			}
		}()
		return nil
	}
	return write(mssg)
}

//---------------------------------------------------------------------

// FileWriter implements the Writer interface, writing to a given file
type FileWriter struct {
	FileName string
	file     *os.File
}

// Write writes the message to the supplied file.
func (w *FileWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*FileWriter)(nil)
	return w.writeWork(mssg)
}

func (w *FileWriter) writeWork(mssg *Message) (err error) {
	if w == nil || w.FileName == "" {
		return fmt.Errorf("writer not set not set")
	}

	if w.file == nil {
		w.file, err = os.OpenFile(w.FileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
		if err != nil {
			return err
		}
	}

	s := mssg.String()
	s += "\n"

	_, err = io.WriteString(w.file, s)
	return err
}

// Close closes the file. The creator of the FileWriter must call this.
func (w *FileWriter) Close() error {
	return w.file.Close()
}

//---------------------------------------------------------------------

//StdoutWriter writes messages to STDOUT
type StdoutWriter struct {
}

//Writes message to STDOUT
func (w *StdoutWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*StdoutWriter)(nil)
	return w.writeWork(mssg)
}

func (w *StdoutWriter) writeWork(mssg *Message) error {
	fmt.Println(mssg.String())
	return nil
}

//Nothing to close for this writer
func (w *StdoutWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MessageWriter implements Reader and Writer, using an array of Messages
// as the backing store
type LocalReaderWriter struct {
	messages []Message
}

// Write writes the message to the backing array
func (w *LocalReaderWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*LocalReaderWriter)(nil)
	return w.writeWork(mssg)
}

func (w *LocalReaderWriter) writeWork(mssg *Message) error {
	if w.messages == nil {
		w.messages = make([]Message, 0)
	}
	w.messages = append(w.messages, *mssg)
	return nil
}

// Read reads messages from the backing array. Will only return as many as are
// available; asking for too many is not an error.
func (w *LocalReaderWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*LocalReaderWriter)(nil)

	if count < 0 {
		return nil, fmt.Errorf("invalid count: %d", count)
	}

	if w.messages == nil || count == 0 {
		return make([]Message, 0), nil
	}

	if count > len(w.messages) {
		count = len(w.messages)
	}

	n := len(w.messages)
	a := w.messages[n-count : n]

	return a, nil
}

func (w *LocalReaderWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("LocalReaderWriter.GetMessages() not supported")
}

func (w *LocalReaderWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// HttpWriter implements Writer, by talking to the actual pz-logger service
type HttpWriter struct {
	url string
	h   piazza.Http
}

func NewHttpWriter(url string, apiKey string) (*HttpWriter, error) {
	w := &HttpWriter{url: url}
	w.h = piazza.Http{
		BaseUrl: url,
		ApiKey:  apiKey,
		//FlightCheck: &piazza.SimpleFlightCheck{},
	}

	return w, nil
}

func (w *HttpWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*HttpWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *HttpWriter) writeWork(mssg *Message) error {
	if jresp := w.h.PzPost("/syslog", mssg); jresp.IsError() {
		return jresp.ToError()
	}
	return nil
}

func (w *HttpWriter) Close() error {
	return nil
}

func (w *HttpWriter) Read(count int) ([]Message, error) {
	var _ Reader = (*HttpWriter)(nil)

	format := &piazza.JsonPagination{
		Page:    0,
		PerPage: count,
		SortBy:  "timeStamp",
		Order:   "desc",
	}
	params := &piazza.HttpQueryParams{}

	mssgs, _, err := w.GetMessages(format, params)
	return mssgs, err

}

// GetMessages is only implemented for HttpWriter as it is most likely the only
// Writer to be used for reading back data.
func (w *HttpWriter) GetMessages(
	format *piazza.JsonPagination,
	params *piazza.HttpQueryParams) ([]Message, int, error) {
	var _ Reader = (*HttpWriter)(nil)

	formatString := format.String()
	paramString := params.String()

	var ext string
	if formatString != "" && paramString != "" {
		ext = "?" + formatString + "&" + paramString
	} else if formatString == "" && paramString != "" {
		ext = "?" + paramString
	} else if formatString != "" && paramString == "" {
		ext = "?" + formatString
	} else if formatString == "" && paramString == "" {
		ext = ""
	} else {
		return nil, 0, errors.New("Internal error: failed to parse query params")
	}

	endpoint := "/syslog" + ext
	jresp := w.h.PzGet(endpoint)
	if jresp.IsError() {
		return nil, 0, jresp.ToError()
	}
	var mssgs []Message
	err := jresp.ExtractData(&mssgs)
	if err != nil {
		return nil, 0, err
	}

	return mssgs, jresp.Pagination.Count, nil
}

//---------------------------------------------------------------------

// SyslogdWriter implements a Writer that writes to the syslogd system service.
// This will almost certainly not work on Windows, but that is okay because Piazza
// does not support Windows.
type SyslogdWriter struct {
	writer *DaemonWriter
}

func (w *SyslogdWriter) initWriter() error {
	if w.writer != nil {
		return nil
	}

	tw, err := Dial(SyslogdNetwork, SyslogdRaddr)
	if err != nil {
		return err
	}

	w.writer = tw

	return nil
}

// Write writes the message to the OS's syslogd system.
func (w *SyslogdWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*SyslogdWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *SyslogdWriter) writeWork(mssg *Message) error {
	if err := w.initWriter(); err != nil {
		return err
	}
	s := mssg.String()
	return w.writer.Write(s)
}

// Close closes the underlying network connection.
func (w *SyslogdWriter) Close() error {
	if w.writer == nil {
		return nil
	}
	return w.writer.Close()
}

//---------------------------------------------------------------------

// ElasticWriter implements the Writer, writing to elasticsearch
type ElasticWriter struct {
	Esi elasticsearch.IIndex
	typ string
	id  string
}

func NewElasticWriter(esi elasticsearch.IIndex, typ string) *ElasticWriter {
	ew := &ElasticWriter{
		Esi: esi,
		typ: typ,
	}
	return ew
}

// Write writes the message to the elasticsearch index, type, id
func (w *ElasticWriter) Write(mssg *Message, async bool) error {
	var _ Writer = (*ElasticWriter)(nil)
	return aSyncLogic(w.writeWork, mssg, async)
}

func (w *ElasticWriter) writeWork(mssg *Message) error {
	if w == nil || w.Esi == nil || w.typ == "" {
		return fmt.Errorf("writer not set not set")
	}

	_, err := w.Esi.PostData(w.typ, w.id, mssg)
	return err
}

// SetType sets the type to write to
func (w *ElasticWriter) SetType(typ string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.typ = typ
	return nil
}

func (w *ElasticWriter) CreateIndex() (bool, error) {
	if w == nil || w.Esi == nil {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.IndexExists()
	if err != nil {
		return exists, err
	}
	if !exists {
		return exists, w.Esi.Create("")
	}
	return exists, nil
}

func (w *ElasticWriter) CreateType(mapping string) (bool, error) {
	if w == nil || w.Esi == nil || w.typ == "" {
		return false, fmt.Errorf("writer not set not set")
	}
	exists, err := w.Esi.TypeExists(w.typ)
	if err != nil {
		return exists, err
	}
	if !exists {
		return exists, w.Esi.SetMapping(w.typ, piazza.JsonString(mapping))
	}
	return exists, nil
}

// SetID sets the id to write to
func (w *ElasticWriter) SetID(id string) error {
	if w == nil {
		return fmt.Errorf("writer not set not set")
	}
	w.id = id
	return nil
}

// Close does nothing but satisfy an interface.
func (w *ElasticWriter) Close() error {
	return nil
}

func (w *ElasticWriter) GetMessages(*piazza.JsonPagination, *piazza.HttpQueryParams) ([]Message, int, error) {
	return nil, 0, fmt.Errorf("ElasticWriter.GetMessages not supported")
}
func (w *ElasticWriter) Read(count int) ([]Message, error) {
	return nil, fmt.Errorf("ElasticWriter.Read not supported")
}

//-------------------------------

// NilWriter doesn't do anything
type NilWriter struct {
}

func (*NilWriter) Write(*Message, bool) error {
	var _ Writer = (*NilWriter)(nil)
	return nil
}

func (*NilWriter) writeWork(*Message) error {
	return nil
}

func (*NilWriter) Close() error {
	return nil
}

//---------------------------------------------------------------------

// MultiWriter will write to N different Writers at once. When doing a
// Write or a Close, we call the function on all of them. If any have failed,
// we return the error from the first one that failed.
type MultiWriter struct {
	writers []Writer
}

func NewMultiWriter(ws []Writer) *MultiWriter {
	mw := &MultiWriter{}
	mw.writers = make([]Writer, len(ws))
	copy(mw.writers, ws)
	return mw
}

func (mw *MultiWriter) Write(m *Message, async bool) (err error) {
	var _ Writer = (*HttpWriter)(nil)
	for _, w := range mw.writers {
		e := w.Write(m, async)
		if e != nil && err != nil {
			err = e
		}
	}
	return err
}

func (mw *MultiWriter) writeWork(m *Message) (err error) {
	return nil
}

func (mw *MultiWriter) Close() error {
	var err error

	for _, w := range mw.writers {
		e := w.Close()
		if e != nil && err != nil {
			err = e
		}
	}

	return err
}
<|endoftext|>


<commit_before>package db

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/bosssauce/ponzu/system/admin/user"

	"github.com/boltdb/bolt"
	"github.com/nilslice/jwt"
	"github.com/nilslice/rand"
)

// ErrUserExists is used for the db to report to admin user of existing user
var ErrUserExists = errors.New("Error. User exists.")

// ErrNoUserExists is used for the db to report to admin user of non-existing user
var ErrNoUserExists = errors.New("Error. No user exists.")

// SetUser sets key:value pairs in the db for user settings
func SetUser(usr *user.User) (int, error) {
	err := store.Update(func(tx *bolt.Tx) error {
		email := []byte(usr.Email)
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get(email)
		if exists != nil {
			return ErrUserExists
		}

		// get NextSequence int64 and set it as the User.ID
		id, err := users.NextSequence()
		if err != nil {
			return err
		}
		usr.ID = int(id)

		// marshal User to json and put into bucket
		j, err := json.Marshal(usr)
		if err != nil {
			return err
		}

		err = users.Put(email, j)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return 0, err
	}

	return usr.ID, nil
}

// UpdateUser sets key:value pairs in the db for existing user settings
func UpdateUser(usr, updatedUsr *user.User) error {
	err := store.Update(func(tx *bolt.Tx) error {
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get([]byte(usr.Email))
		if exists == nil {
			return ErrNoUserExists
		}

		// marshal User to json and put into bucket
		j, err := json.Marshal(updatedUsr)
		if err != nil {
			return err
		}

		err = users.Put([]byte(updatedUsr.Email), j)
		if err != nil {
			return err
		}

		// if email address was changed, delete the old record of former
		// user with original email address
		if usr.Email != updatedUsr.Email {
			err = users.Delete([]byte(usr.Email))
			if err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// DeleteUser deletes a user from the db by email
func DeleteUser(email string) error {
	err := store.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.Delete([]byte(email))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// User gets the user by email from the db
func User(email string) ([]byte, error) {
	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		usr := b.Get([]byte(email))

		_, err := val.Write(usr)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	if val.Bytes() == nil {
		return nil, ErrNoUserExists
	}

	return val.Bytes(), nil
}

// UserAll returns all users from the db
func UserAll() ([][]byte, error) {
	var users [][]byte
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.ForEach(func(k, v []byte) error {
			users = append(users, v)
			return nil
		})
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return users, nil
}

// CurrentUser extracts the user from the request data and returns the current user from the db
func CurrentUser(req *http.Request) ([]byte, error) {
	if !user.IsValid(req) {
		return nil, fmt.Errorf("Error. Invalid User.")
	}

	token, err := req.Cookie("_token")
	if err != nil {
		return nil, err
	}

	claims := jwt.GetClaims(token.Value)
	email, ok := claims["user"]
	if !ok {
		return nil, fmt.Errorf("Error. No user data found in request token.")
	}

	usr, err := User(email.(string))
	if err != nil {
		return nil, err
	}

	return usr, nil
}

// SetRecoveryKey generates and saves a random secret key to verify an email
// address submitted in order to recover/reset an account password
func SetRecoveryKey(email string) (string, error) {
	key := fmt.Sprintf("%d", rand.Int63())

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte("_recoveryKeys"))
		if err != nil {
			return err
		}

		err = b.Put([]byte(email), []byte(key))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key, nil
}

// RecoveryKey generates and saves a random secret key to verify an email
// address submitted in order to recover/reset an account password
func RecoveryKey(email string) (string, error) {
	key := &bytes.Buffer{}

	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_recoveryKeys"))
		if b == nil {
			return errors.New("No database found for checking keys.")
		}

		_, err := key.Write(b.Get([]byte("email")))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key.String(), nil
}
<commit_msg>updating docs for recovery key getter<commit_after>package db

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"

	"github.com/bosssauce/ponzu/system/admin/user"

	"github.com/boltdb/bolt"
	"github.com/nilslice/jwt"
	"github.com/nilslice/rand"
)

// ErrUserExists is used for the db to report to admin user of existing user
var ErrUserExists = errors.New("Error. User exists.")

// ErrNoUserExists is used for the db to report to admin user of non-existing user
var ErrNoUserExists = errors.New("Error. No user exists.")

// SetUser sets key:value pairs in the db for user settings
func SetUser(usr *user.User) (int, error) {
	err := store.Update(func(tx *bolt.Tx) error {
		email := []byte(usr.Email)
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get(email)
		if exists != nil {
			return ErrUserExists
		}

		// get NextSequence int64 and set it as the User.ID
		id, err := users.NextSequence()
		if err != nil {
			return err
		}
		usr.ID = int(id)

		// marshal User to json and put into bucket
		j, err := json.Marshal(usr)
		if err != nil {
			return err
		}

		err = users.Put(email, j)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return 0, err
	}

	return usr.ID, nil
}

// UpdateUser sets key:value pairs in the db for existing user settings
func UpdateUser(usr, updatedUsr *user.User) error {
	err := store.Update(func(tx *bolt.Tx) error {
		users := tx.Bucket([]byte("_users"))

		// check if user is found by email, fail if nil
		exists := users.Get([]byte(usr.Email))
		if exists == nil {
			return ErrNoUserExists
		}

		// marshal User to json and put into bucket
		j, err := json.Marshal(updatedUsr)
		if err != nil {
			return err
		}

		err = users.Put([]byte(updatedUsr.Email), j)
		if err != nil {
			return err
		}

		// if email address was changed, delete the old record of former
		// user with original email address
		if usr.Email != updatedUsr.Email {
			err = users.Delete([]byte(usr.Email))
			if err != nil {
				return err
			}

		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// DeleteUser deletes a user from the db by email
func DeleteUser(email string) error {
	err := store.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.Delete([]byte(email))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

// User gets the user by email from the db
func User(email string) ([]byte, error) {
	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		usr := b.Get([]byte(email))

		_, err := val.Write(usr)
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	if val.Bytes() == nil {
		return nil, ErrNoUserExists
	}

	return val.Bytes(), nil
}

// UserAll returns all users from the db
func UserAll() ([][]byte, error) {
	var users [][]byte
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_users"))
		err := b.ForEach(func(k, v []byte) error {
			users = append(users, v)
			return nil
		})
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	return users, nil
}

// CurrentUser extracts the user from the request data and returns the current user from the db
func CurrentUser(req *http.Request) ([]byte, error) {
	if !user.IsValid(req) {
		return nil, fmt.Errorf("Error. Invalid User.")
	}

	token, err := req.Cookie("_token")
	if err != nil {
		return nil, err
	}

	claims := jwt.GetClaims(token.Value)
	email, ok := claims["user"]
	if !ok {
		return nil, fmt.Errorf("Error. No user data found in request token.")
	}

	usr, err := User(email.(string))
	if err != nil {
		return nil, err
	}

	return usr, nil
}

// SetRecoveryKey generates and saves a random secret key to verify an email
// address submitted in order to recover/reset an account password
func SetRecoveryKey(email string) (string, error) {
	key := fmt.Sprintf("%d", rand.Int63())

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte("_recoveryKeys"))
		if err != nil {
			return err
		}

		err = b.Put([]byte(email), []byte(key))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key, nil
}

// RecoveryKey gets a previously set recovery key to verify an email address
// submitted in order to recover/reset an account password
func RecoveryKey(email string) (string, error) {
	key := &bytes.Buffer{}

	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("_recoveryKeys"))
		if b == nil {
			return errors.New("No database found for checking keys.")
		}

		_, err := key.Write(b.Get([]byte("email")))
		if err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return "", err
	}

	return key.String(), nil
}
<|endoftext|>


<commit_before>// Author: Liam Stanley <me@liamstanley.io>
// Docs: https://marill.liam.sh/
// Repo: https://github.com/Liamraystanley/marill

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
)

// may just setup a global logger, and change output during runtime...
// http://codereview.stackexchange.com/a/59733

var logf *os.File
var logger *log.Logger

func initLoggerWriter(w io.Writer) {
	logger = log.New(w, "", log.Lshortfile|log.LstdFlags)
	logger.Println("initializing logger")
}

func initLogger() {
	var err error
	if conf.out.logFile != "" && conf.out.printDebug {
		logf, err = os.OpenFile(conf.out.logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			fmt.Printf("error opening log file: %s, %v", conf.out.logFile, err)
			os.Exit(1)
		}

		initLoggerWriter(io.MultiWriter(logf, os.Stdout))
		return
	}

	if conf.out.logFile != "" {
		logf, err = os.OpenFile(conf.out.logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			fmt.Printf("error opening log file: %s, %v", conf.out.logFile, err)
			os.Exit(1)
		}

		initLoggerWriter(logf)
		return
	}

	if conf.out.printDebug {
		initLoggerWriter(os.Stdout)
		return
	}

	initLoggerWriter(ioutil.Discard)
}

func closeLogger() {
	if logf != nil {
		logf.Close()
	}
}

// Color represents an ASCII color sequence for use with prettified output
type Color struct {
	Name string
	ID   int
}

var colors = []*Color{
	{"c", 0}, {"bold", 1}, {"black", 30}, {"red", 31}, {"green", 32}, {"yellow", 33},
	{"blue", 34}, {"magenta", 35}, {"cyan", 36}, {"white", 37}, {"gray", 90},
	{"lightred", 91}, {"lightgreen", 92}, {"lightyellow", 93}, {"lightblue", 94},
	{"lightmagenta", 95}, {"lightcyan", 96}, {"lightgray", 97},
}

// StripColor strips all color {patterns} from input
func StripColor(format *string) {
	for _, clr := range colors {
		*format = strings.Replace(*format, "{"+clr.Name+"}", "", -1)
	}
}

// FmtColor adds (or removes) color output depending on user input
func FmtColor(format *string, shouldStrip bool) {
	if shouldStrip {
		StripColor(format)

		return
	}

	for _, clr := range colors {
		*format = strings.Replace(*format, "{"+clr.Name+"}", "\x1b["+strconv.Itoa(clr.ID)+"m", -1)
	}

	*format = *format + "\x1b[0;m"
}

// Output is the bare out struct for which stdout messages are passed to
type Output struct{}

// Printf interprets []*Color{} escape codes and prints them to stdout
func (o Output) Printf(format string, a ...interface{}) (n int, err error) {
	if conf.out.ignoreStd {
		return 0, nil
	}

	str := fmt.Sprintf(format, a...)
	FmtColor(&str, conf.out.noColors)

	return fmt.Print(str)
}

// Println interprets []*Color{} escape codes and prints them to stdout
func (o Output) Println(a ...interface{}) (n int, err error) {
	if conf.out.ignoreStd {
		return 0, nil
	}

	str := fmt.Sprintln(a...)
	FmtColor(&str, conf.out.noColors)
	return fmt.Print(str)
}

// Fatalf interprets []*Color{} escape codes and prints them to stdout/logger, and exits
func (o Output) Fatalf(format string, a ...interface{}) {
	// print to regular stdout
	if !conf.out.ignoreStd {
		str := fmt.Sprintf(fmt.Sprintf("{bold}{red}error:{c} %s\n", format), a...)
		FmtColor(&str, conf.out.noColors)
		fmt.Print(str)
	}

	// strip color from format
	StripColor(&format)
	logger.Fatalf("error: "+format, a...)
}

// Fatal interprets []*Color{} escape codes and prints them to stdout, and exits
func (o Output) Fatal(a ...interface{}) {
	// print to regular stdout
	if !conf.out.ignoreStd {
		str := fmt.Sprintf("{bold}{red}error:{c} %s", fmt.Sprintln(a...))
		FmtColor(&str, conf.out.noColors)
		fmt.Print(str)
	}

	str := fmt.Sprintln(a...)

	logger.Fatal("error: " + str)
}
<commit_msg>add support to redirect out.* to any io.Writer<commit_after>// Author: Liam Stanley <me@liamstanley.io>
// Docs: https://marill.liam.sh/
// Repo: https://github.com/Liamraystanley/marill

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"
)

// just setup a global logger, and change output during runtime...

var logf *os.File
var logger *log.Logger

func initLoggerWriter(w io.Writer) {
	logger = log.New(w, "", log.Lshortfile|log.LstdFlags)
	logger.Println("initializing logger")
}

func initLogger() {
	var err error
	if conf.out.logFile != "" && conf.out.printDebug {
		logf, err = os.OpenFile(conf.out.logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			fmt.Printf("error opening log file: %s, %v", conf.out.logFile, err)
			os.Exit(1)
		}

		initLoggerWriter(io.MultiWriter(logf, os.Stdout))
		return
	}

	if conf.out.logFile != "" {
		logf, err = os.OpenFile(conf.out.logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			fmt.Printf("error opening log file: %s, %v", conf.out.logFile, err)
			os.Exit(1)
		}

		initLoggerWriter(logf)
		return
	}

	if conf.out.printDebug {
		initLoggerWriter(os.Stdout)
		return
	}

	initLoggerWriter(ioutil.Discard)
}

func closeLogger() {
	if logf != nil {
		logf.Close()
	}
}

// Color represents an ASCII color sequence for use with prettified output
type Color struct {
	Name string
	ID   int
}

var colors = []*Color{
	{"c", 0}, {"bold", 1}, {"black", 30}, {"red", 31}, {"green", 32}, {"yellow", 33},
	{"blue", 34}, {"magenta", 35}, {"cyan", 36}, {"white", 37}, {"gray", 90},
	{"lightred", 91}, {"lightgreen", 92}, {"lightyellow", 93}, {"lightblue", 94},
	{"lightmagenta", 95}, {"lightcyan", 96}, {"lightgray", 97},
}

// StripColor strips all color {patterns} from input
func StripColor(format *string) {
	for _, clr := range colors {
		*format = strings.Replace(*format, "{"+clr.Name+"}", "", -1)
	}
}

// FmtColor adds (or removes) color output depending on user input
func FmtColor(format *string, shouldStrip bool) {
	if shouldStrip {
		StripColor(format)

		return
	}

	for _, clr := range colors {
		*format = strings.Replace(*format, "{"+clr.Name+"}", "\x1b["+strconv.Itoa(clr.ID)+"m", -1)
	}

	*format = *format + "\x1b[0;m"
}

// Output is the bare out struct for which stdout messages are passed to
type Output struct {
	log    *log.Logger
	buffer []string
}

var out = Output{}

func initOut(w io.Writer) {
	out.log = log.New(w, "", 0)
}

func (o *Output) Write(b []byte) (int, error) {
	str := fmt.Sprintf("%s", b)
	o.log.Print(str)
	o.AddLog(str)

	return len(b), nil
}

func (o *Output) AddLog(line string) {
	o.buffer = append(o.buffer, line)
}

// Printf interprets []*Color{} escape codes and prints them to stdout
func (o *Output) Printf(format string, a ...interface{}) {
	if conf.out.ignoreStd {
		return
	}

	str := fmt.Sprintf(format, a...)
	FmtColor(&str, conf.out.noColors)

	out.log.Print(str)
	o.AddLog(str)
}

// Println interprets []*Color{} escape codes and prints them to stdout
func (o *Output) Println(a ...interface{}) {
	if conf.out.ignoreStd {
		return
	}

	str := fmt.Sprintln(a...)
	FmtColor(&str, conf.out.noColors)

	out.log.Print(str)
	o.AddLog(str)
}

// Fatalf interprets []*Color{} escape codes and prints them to stdout/logger, and exits
func (o *Output) Fatalf(format string, a ...interface{}) {
	// print to regular stdout
	if !conf.out.ignoreStd {
		str := fmt.Sprintf(fmt.Sprintf("{bold}{red}error:{c} %s\n", format), a...)
		FmtColor(&str, conf.out.noColors)
		out.log.Print(str)
		o.AddLog(str)
	}

	// strip color from format
	StripColor(&format)
	logger.Fatalf("error: "+format, a...)
}

// Fatal interprets []*Color{} escape codes and prints them to stdout, and exits
func (o *Output) Fatal(a ...interface{}) {
	// print to regular stdout
	if !conf.out.ignoreStd {
		str := fmt.Sprintf("{bold}{red}error:{c} %s", fmt.Sprintln(a...))
		FmtColor(&str, conf.out.noColors)
		out.log.Print(str)
		o.AddLog(str)
	}

	str := fmt.Sprintln(a...)

	logger.Fatal("error: " + str)
}
<|endoftext|>


<commit_before>package fluent

import (
	"errors"
	"log"
	"net"
	"strconv"
	"time"
)

// Logger owns asynchronous logging to fluentd.
type Logger struct {
	config   Config
	postCh   chan message
	buffer   []byte
	conn     net.Conn
	ticker   *time.Ticker
	logError bool
}

// NewLogger() launches a goroutine to log and returns logger.
// Logger has a channel to interact with the goroutine.
func NewLogger(config Config) *Logger {
	config.applyDefaultValues()

	logger := &Logger{
		config:   config,
		postCh:   make(chan message, config.ChannelLength),
		ticker:   time.NewTicker(config.BufferingTimeout),
		logError: true,
	}
	logger.connect()
	go logger.loop()

	return logger
}

// You can send message to logger's goroutine via channel.
// This logging is executed asynchronously.
func (l *Logger) Post(tag string, data interface{}) {
	tag = l.prependTagPrefix(tag)
	l.postCh <- message{tag: tag, time: time.Now(), data: data}
}

// You can send message immediately to fluentd.
func (l *Logger) Log(tag string, data interface{}) error {
	tag = l.prependTagPrefix(tag)
	msg := &message{tag: tag, time: time.Now(), data: data}
	pack, err := msg.toMsgpack()
	if err != nil {
		return err
	}

	l.buffer = append(l.buffer, pack...)
	return l.sendMessage()
}

func (l *Logger) loop() {
	for {
		select {
		case msg := <-l.postCh:
			pack, err := msg.toMsgpack()
			if err != nil {
				log.Printf("message pack dump error: " + err.Error())
				continue
			}

			l.buffer = append(l.buffer, pack...)
			if len(l.buffer) > l.config.BufferLength {
				l.sendMessage()
			}
		case <-l.ticker.C:
			l.sendMessage()
		}
	}
}

func (l *Logger) sendMessage() error {
	if len(l.buffer) == 0 {
		return errors.New("Buffer is empty")
	}

	l.connect()
	if l.conn == nil {
		return errors.New("Failed to establish connection with fluentd")
	}

	_, err := l.conn.Write(l.buffer)

	if err == nil {
		l.buffer = l.buffer[0:0]
	} else {
		log.Printf("failed to send message: " + err.Error())
		l.conn.Close()
		l.conn = nil
	}
	return err
}

func (l *Logger) connect() {
	if l.conn != nil {
		return
	}

	var err error
	for i := 0; i < l.config.MaxTrialForConnection; i++ {
		l.conn, err = net.DialTimeout(
			"tcp",
			l.config.FluentHost+":"+strconv.Itoa(l.config.FluentPort),
			l.config.ConnectionTimeout,
		)

		if err == nil {
			l.logError = true
			return
		}
	}

	if l.logError {
		log.Printf("failed to establish connection with fluentd: " + err.Error())
		l.logError = false
	}
}

func (l *Logger) prependTagPrefix(tag string) string {
	if l.config.TagPrefix != "" {
		tag = l.config.TagPrefix + "." + tag
	}
	return tag
}
<commit_msg>Allocate buffer capacity first<commit_after>package fluent

import (
	"errors"
	"log"
	"net"
	"strconv"
	"time"
)

// Logger owns asynchronous logging to fluentd.
type Logger struct {
	config   Config
	postCh   chan message
	buffer   []byte
	conn     net.Conn
	ticker   *time.Ticker
	logError bool
}

// NewLogger() launches a goroutine to log and returns logger.
// Logger has a channel to interact with the goroutine.
func NewLogger(config Config) *Logger {
	config.applyDefaultValues()

	logger := &Logger{
		config:   config,
		postCh:   make(chan message, config.ChannelLength),
		buffer:   make([]byte, 0, config.BufferLength),
		ticker:   time.NewTicker(config.BufferingTimeout),
		logError: true,
	}
	logger.connect()
	go logger.loop()

	return logger
}

// You can send message to logger's goroutine via channel.
// This logging is executed asynchronously.
func (l *Logger) Post(tag string, data interface{}) {
	tag = l.prependTagPrefix(tag)
	l.postCh <- message{tag: tag, time: time.Now(), data: data}
}

// You can send message immediately to fluentd.
func (l *Logger) Log(tag string, data interface{}) error {
	tag = l.prependTagPrefix(tag)
	msg := &message{tag: tag, time: time.Now(), data: data}
	pack, err := msg.toMsgpack()
	if err != nil {
		return err
	}

	l.buffer = append(l.buffer, pack...)
	return l.sendMessage()
}

func (l *Logger) loop() {
	for {
		select {
		case msg := <-l.postCh:
			pack, err := msg.toMsgpack()
			if err != nil {
				log.Printf("message pack dump error: " + err.Error())
				continue
			}

			l.buffer = append(l.buffer, pack...)
			if len(l.buffer) > l.config.BufferLength {
				l.sendMessage()
			}
		case <-l.ticker.C:
			l.sendMessage()
		}
	}
}

func (l *Logger) sendMessage() error {
	if len(l.buffer) == 0 {
		return errors.New("Buffer is empty")
	}

	l.connect()
	if l.conn == nil {
		return errors.New("Failed to establish connection with fluentd")
	}

	_, err := l.conn.Write(l.buffer)

	if err == nil {
		l.buffer = l.buffer[0:0]
	} else {
		log.Printf("failed to send message: " + err.Error())
		l.conn.Close()
		l.conn = nil
	}
	return err
}

func (l *Logger) connect() {
	if l.conn != nil {
		return
	}

	var err error
	for i := 0; i < l.config.MaxTrialForConnection; i++ {
		l.conn, err = net.DialTimeout(
			"tcp",
			l.config.FluentHost+":"+strconv.Itoa(l.config.FluentPort),
			l.config.ConnectionTimeout,
		)

		if err == nil {
			l.logError = true
			return
		}
	}

	if l.logError {
		log.Printf("failed to establish connection with fluentd: " + err.Error())
		l.logError = false
	}
}

func (l *Logger) prependTagPrefix(tag string) string {
	if l.config.TagPrefix != "" {
		tag = l.config.TagPrefix + "." + tag
	}
	return tag
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Thomas de Zeeuw.
//
// Licensed onder the MIT license that can be found in the LICENSE file.

// TODO: DRY New, NewMsgWriter & Combine functions.

// Package logger provides multiple ways to log information of different level
// of importance. No default logger is created, but Get is provided to get any
// logger at any location.
package logger

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"
	"time"
)

const (
	defaultLogsSize  = 1024
)

// MsgWriter takes a msg and writes it to the output.
type MsgWriter interface {
	WriteMsg(Msg) (int, error)
}

// Flusher interface to check if the writer can flush.
type flusher interface {
	Flush() error
}

// FileWriter is an struct used in closing the underlying file if using a
// buffered writer.
type fileWriter struct {
	*bufio.Writer
	f *os.File
}

// Close calls Close on the underlying os.File.
func (w *fileWriter) Close() error {
	return w.f.Close()
}

// Tags are keywords usefull in searching logs. Examples of these are
// - "file.go", "myFn"; indicating the location of the log operation.
// - "user:$id"; indicating a user is logged in (usefull in user specific bugs)
type Tags []string

// String creates a comma separated list from the tags in string.
func (tags *Tags) String() string {
	return string(tags.Bytes())
}

// Bytes creates a comma separated list from the tags in bytes.
func (tags *Tags) Bytes() []byte {
	buf := make([]byte, 0, 50)

	// Add each tag in the form of "tag, "
	for _, tag := range *tags {
		buf = append(buf, tag...)
		buf = append(buf, ',')
		buf = append(buf, ' ')
	}

	// Drop the last ", "
	if len(buf) > 2 {
		buf = buf[:len(buf)-2]
	}

	return buf
}

// Msg is a message created by a log operation.
type Msg struct {
	Level, Msg string
	Tags       Tags
	Timestamp  time.Time
}

// String creates a string message in the following format:
//	YYYY-MM-DD HH:MM:SS [LEVEL] tag1, tag2...: message
func (msg *Msg) String() string {
	return string(msg.Bytes())
}

// Bytes formats a message in the following format:
//	YYYY-MM-DD HH:MM:SS [LEVEL] tag1, tag2...: message
func (msg *Msg) Bytes() []byte {
	buf := make([]byte, 0, 100)

	// Write the date and time.
	// Format: "YYYY-MM-DD HH:MM:SS ".
	year, month, day := msg.Timestamp.Date()
	hour, min, sec := msg.Timestamp.Clock()
	itoa(&buf, year, 4)
	buf = append(buf, '-')
	itoa(&buf, int(month), 2)
	buf = append(buf, '-')
	itoa(&buf, day, 2)
	buf = append(buf, ' ')
	itoa(&buf, hour, 2)
	buf = append(buf, ':')
	itoa(&buf, min, 2)
	buf = append(buf, ':')
	itoa(&buf, sec, 2)
	buf = append(buf, ' ')

	// Write the level.
	// Format: "[LEVEL] " (levle is always 5 characters long).
	buf = append(buf, '[')
	buf = append(buf, msg.Level...)
	buf = append(buf, ']')
	buf = append(buf, ' ')

	// Write the tags.
	// Format: "tag1, tag2, etc...: ".
	buf = append(buf, msg.Tags.Bytes()...)
	buf = append(buf, ':')
	buf = append(buf, ' ')

	// The actual message.
	buf = append(buf, strings.TrimSpace(msg.Msg)...)
	buf = append(buf, '\n')

	return buf
}

// Collection of all created logger by name, used by the Get function.
var loggers = map[string]*Logger{}

// The Logger is an logging object which logs to an io.Writer where each Write
// call is a single log item. Each logging operation makes a single call to the
// Writer's Write method, but not necessarily at the same time a Log operation
// (Fatal, Error etc.) is called. A Logger can be used simultaneously from
// multiple goroutines, it guarantees to serialize access to the Writer.
// Messages will always be in the following format:
//	YYYY-MM-DD HH:MM:SS [LEVEL] tag1, tag2...: message
//
// There are four different log levels (from higher to lower): Fatal, Error,
// Info and Debug, aswell as Thumbstone which is a special case. Thumbstone is
// used for testing if a function is used in production.
//
// Note: Log operations (Fatal, Error etc.) don't instally write to the
// io.Writer, before closing the program call Logger.Close to ensure that all
// log items are written to the io.Writer.
type Logger struct {
	// Name of the logger, used in getting the logger.
	Name string

	// Wether or not to show debug log statements, should really only be set on
	// creating of the logger.
	ShowDebug bool

	// The writers where the items are written to. Either w or wMsg is used, the
	// one not used should be nil.
	w    io.Writer
	wMsg MsgWriter

	// The log messages channel, it's used for actually writing the messages.
	logs chan Msg

	// Indicating the writer closed, having a possible flush or close error.
	closed chan error
}

// Fatal logs a recovered error which could have killed the program.
func (l *Logger) Fatal(tags Tags, recv interface{}) {
	// Capture the stack trace and drop null bytes (they'll show up as spaces).
	buf := make([]byte, 8192)
	runtime.Stack(buf, false)
	buf = bytes.Trim(buf, "\x00")

	// Try to make some sense of the recoverd value.
	var item string
	switch v := recv.(type) {
	case string:
		item = v
	case error:
		item = v.Error()
	default:
		item = fmt.Sprintf("%v", recv)
	}

	item += "\n" + string(buf)
	l.logs <- Msg{"FATAL", item, tags, time.Now()}
}

// Error logs a recoverable error.
func (l *Logger) Error(tags Tags, err error) {
	l.logs <- Msg{"ERROR", err.Error(), tags, time.Now()}
}

// Info logs an informational message.
func (l *Logger) Info(tags Tags, format string, v ...interface{}) {
	l.logs <- Msg{"INFO ", fmt.Sprintf(format, v...), tags, time.Now()}
}

// Debug logs the lowest level of information, only usefull when debugging
// the application. Only shows when Logger.ShowDebug is set to true, which
// defaults to false.
func (l *Logger) Debug(tags Tags, format string, v ...interface{}) {
	if l.ShowDebug {
		l.logs <- Msg{"DEBUG", fmt.Sprintf(format, v...), tags, time.Now()}
	}
}

// Thumbstone indicates a function is still used in production. When developing
// software it's possible to introduce dead code with updates and new features.
// If a function is being suspected of being dead (not used) in production, add
// a call to Thumbstone and check the production logs to see if you're right.
func (l *Logger) Thumbstone(item string) {
	l.logs <- Msg{"THUMB", item, Tags{"thumbstone"}, time.Now()}
}

// Close blocks until all logs are written to the writer. It will call Flush()
// and Close() on the writer if supported. If either of the functions returns
// an error it will be returned by this function (Flush error first).
//
// Note: it will block forever if logs are still being written, so it's
// required to first stop writing logs and then call Logger.Close.
func (l *Logger) Close() error {
	// First send to the go routine that we need to close, then wait for the go
	// routine to close.
	l.closed <- nil

	// Wait for an response with a possible error.
	err := <-l.closed

	// Either try to flush the io writer or the message writer. Also try to close
	// the writer.
	// TODO: improve thise code.
	if l.w != nil {
		if fw, ok := l.w.(flusher); ok {
			err = fw.Flush()
		}
		if cw, ok := l.w.(io.Closer); ok {
			if closeErr := cw.Close(); err == nil {
				err = closeErr
			}
		}
	} else {
		if fw, ok := l.wMsg.(flusher); ok {
			err = fw.Flush()
		}
		if cw, ok := l.wMsg.(io.Closer); ok {
			if closeErr := cw.Close(); err == nil {
				err = closeErr
			}
		}
	}

	return err
}

// New creates a new logger, which starts a go routine which writes to the
// writer. This way the main thread won't be blocked. Name is the name of the
// logger used in getting (via Get) from any location within your code.
//
// Note: because the logging isn't done on the main thread it's possible that
// the program will close before all the log items are written to the writer.
// It is required to call logger.Close before closing down the program!
// Otherwise logs might be lost!
func New(name string, w io.Writer) (*Logger, error) {
	log, err := newLogger(name, w)
	if err != nil {
		return nil, err
	}

	go func(log *Logger) {
		var closed bool

		for {
			select {
			case <-log.closed: // Indicator that the logger is closing.
				closed = true
			case msg := <-log.logs: // Received a log message.
				// TODO: handle error!
				log.w.Write(msg.Bytes())
			case <-time.After(50 * time.Millisecond):
				// After a timeout we check if we need to close the logger.
				if closed {
					// We logged all the log items, and we can let the Close function
					// know we're done, without an error.
					log.closed <- nil
					return
				}
			}
		}
	}(log)

	return log, nil
}

// NewMsgWriter creates a new logger, similar to New but with a message writer.
// A message writer takes a Msg, which is usefull for writer which don't write
// to a text output.
func NewMsgWriter(name string, w MsgWriter) (*Logger, error) {
	// Create a regular logger with nil as an io.Writer
	log, err := newLogger(name, nil)
	if err != nil {
		return nil, err
	}

	// Then set our message writer.
	log.wMsg = w

	go func(log *Logger) {
		var closed bool

		for {
			select {
			case <-log.closed: // Indicator that the logger is closing.
				closed = true
			case msg := <-log.logs: // Received a log message.
				// TODO: handle error!
				log.wMsg.WriteMsg(msg)
			case <-time.After(50 * time.Millisecond):
				// After a timeout we check if we need to close the logger.
				if closed {
					// We logged all the log items, and we can let the Close function
					// know we're done, without an error.
					log.closed <- nil
					return
				}
			}
		}
	}(log)

	return log, nil
}

// NewFile creates a new logger that logs to a file, it uses bufio to buffer
// the writes.
func NewFile(name, path string) (*Logger, error) {
	file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		return nil, err
	}

	// Create a new writer that writes and flushes the bufio.Writer, but closed
	// the os.File.
	w := &fileWriter{bufio.NewWriter(file), file}
	return New(name, w)
}

// Get gets a logger by its name.
func Get(name string) (*Logger, error) {
	log, ok := loggers[name]
	if !ok {
		return nil, errors.New("logger: no logger found with name " + name)
	}

	return log, nil
}

// Combine combines multiple loggers. It's advised to use the same size for all
// loggers.
func Combine(name string, logs ...*Logger) (*Logger, error) {
	if len(logs) == 0 {
		return nil, errors.New("logger: Combine requires atleast one logger")
	}

	log, err := newLogger(name, nil)
	if err != nil {
		return nil, err
	}

	go func(log *Logger, logs []*Logger) {
		var closed bool

		for {
			select {
			case <-log.closed: // Indicator that the logger is closing.
				closed = true
			case msg := <-log.logs: // Received an log message.
				for _, log := range logs {
					log.logs <- msg
				}
			case <-time.After(50 * time.Millisecond):
				// After a timeout we check if we need to close the logger.
				if closed {
					i := len(logs)
					errChan := make(chan error, i)

					// Close all underlying loggers.
					for _, log := range logs {
						go func(log *Logger) {
							errChan <- log.Close()
						}(log)
					}

					// Wait for all underlying loggers to respond.
					var err error
					for i > 0 {
						// Check for the error, if no error has happend yet we'll use this
						// error.
						if lErr := <-errChan; err == nil && lErr != nil {
							err = lErr
						}
						i--
					}

					// Return a possible error.
					log.closed <- err
					return
				}
			}
		}
	}(log, logs)

	return log, nil
}

// newLogger creates a new logger and add it to the loggers map. It only
// returns an error if the name is already used.
func newLogger(name string, w io.Writer) (*Logger, error) {
	if _, ok := loggers[name]; ok {
		return nil, errors.New("logger: name " + name + " already taken")
	}

	log := &Logger{
		Name:   name,
		w:      w,
		logs:   make(chan Msg, defaultLogsSize),
		closed: make(chan error), // needs to block!
	}

	loggers[name] = log
	return log, nil
}

// Cheap integer to fixed-width decimal ASCII. Modified version from the Golang
// logger package.
func itoa(buf *[]byte, i int, wid int) {
	var b [4]byte // only used for year, month and day so 4 is enough.
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	b[bp] = byte('0' + i)
	*buf = append(*buf, b[bp:]...)
}
<commit_msg>Move multiple variables to constants<commit_after>// Copyright (C) 2015 Thomas de Zeeuw.
//
// Licensed onder the MIT license that can be found in the LICENSE file.

// TODO: DRY New, NewMsgWriter & Combine functions.

// Package logger provides multiple ways to log information of different level
// of importance. No default logger is created, but Get is provided to get any
// logger at any location.
package logger

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"
	"time"
)

const (
	defaultStackSize = 4096
	defaultTagsSize  = 50
	defaultMsgSize   = 100
	defaultLogsSize  = 1024
)

const (
	fileFlag       = os.O_CREATE | os.O_APPEND | os.O_WRONLY
	filePermission = 0644
)

// MsgWriter takes a msg and writes it to the output.
type MsgWriter interface {
	WriteMsg(Msg) (int, error)
}

// Flusher interface to check if the writer can flush.
type flusher interface {
	Flush() error
}

// FileWriter is an struct used in closing the underlying file if using a
// buffered writer.
type fileWriter struct {
	*bufio.Writer
	f *os.File
}

// Close calls Close on the underlying os.File.
func (w *fileWriter) Close() error {
	return w.f.Close()
}

// Tags are keywords usefull in searching logs. Examples of these are
// - "file.go", "myFn"; indicating the location of the log operation.
// - "user:$id"; indicating a user is logged in (usefull in user specific bugs)
type Tags []string

// String creates a comma separated list from the tags in string.
func (tags *Tags) String() string {
	return string(tags.Bytes())
}

// Bytes creates a comma separated list from the tags in bytes.
func (tags *Tags) Bytes() []byte {
	buf := make([]byte, 0, defaultTagsSize)

	// Add each tag in the form of "tag, "
	for _, tag := range *tags {
		buf = append(buf, tag...)
		buf = append(buf, ',')
		buf = append(buf, ' ')
	}

	// Drop the last ", "
	if len(buf) > 2 {
		buf = buf[:len(buf)-2]
	}

	return buf
}

// Msg is a message created by a log operation.
type Msg struct {
	Level, Msg string
	Tags       Tags
	Timestamp  time.Time
}

// String creates a string message in the following format:
//	YYYY-MM-DD HH:MM:SS [LEVEL] tag1, tag2...: message
func (msg *Msg) String() string {
	return string(msg.Bytes())
}

// Bytes formats a message in the following format:
//	YYYY-MM-DD HH:MM:SS [LEVEL] tag1, tag2...: message
func (msg *Msg) Bytes() []byte {
	buf := make([]byte, 0, defaultMsgSize)

	// Write the date and time.
	// Format: "YYYY-MM-DD HH:MM:SS ".
	year, month, day := msg.Timestamp.Date()
	hour, min, sec := msg.Timestamp.Clock()
	itoa(&buf, year, 4)
	buf = append(buf, '-')
	itoa(&buf, int(month), 2)
	buf = append(buf, '-')
	itoa(&buf, day, 2)
	buf = append(buf, ' ')
	itoa(&buf, hour, 2)
	buf = append(buf, ':')
	itoa(&buf, min, 2)
	buf = append(buf, ':')
	itoa(&buf, sec, 2)
	buf = append(buf, ' ')

	// Write the level.
	// Format: "[LEVEL] " (levle is always 5 characters long).
	buf = append(buf, '[')
	buf = append(buf, msg.Level...)
	buf = append(buf, ']')
	buf = append(buf, ' ')

	// Write the tags.
	// Format: "tag1, tag2, etc...: ".
	buf = append(buf, msg.Tags.Bytes()...)
	buf = append(buf, ':')
	buf = append(buf, ' ')

	// The actual message.
	buf = append(buf, strings.TrimSpace(msg.Msg)...)
	buf = append(buf, '\n')

	return buf
}

// Collection of all created logger by name, used by the Get function.
var loggers = map[string]*Logger{}

// The Logger is an logging object which logs to an io.Writer where each Write
// call is a single log item. Each logging operation makes a single call to the
// Writer's Write method, but not necessarily at the same time a Log operation
// (Fatal, Error etc.) is called. A Logger can be used simultaneously from
// multiple goroutines, it guarantees to serialize access to the Writer.
// Messages will always be in the following format:
//	YYYY-MM-DD HH:MM:SS [LEVEL] tag1, tag2...: message
//
// There are four different log levels (from higher to lower): Fatal, Error,
// Info and Debug, aswell as Thumbstone which is a special case. Thumbstone is
// used for testing if a function is used in production.
//
// Note: Log operations (Fatal, Error etc.) don't instally write to the
// io.Writer, before closing the program call Logger.Close to ensure that all
// log items are written to the io.Writer.
type Logger struct {
	// Name of the logger, used in getting the logger.
	Name string

	// Wether or not to show debug log statements, should really only be set on
	// creating of the logger.
	ShowDebug bool

	// The writers where the items are written to. Either w or wMsg is used, the
	// one not used should be nil.
	w    io.Writer
	wMsg MsgWriter

	// The log messages channel, it's used for actually writing the messages.
	logs chan Msg

	// Indicating the writer closed, having a possible flush or close error.
	closed chan error
}

// Fatal logs a recovered error which could have killed the program.
func (l *Logger) Fatal(tags Tags, recv interface{}) {
	// Capture the stack trace and drop null bytes (they'll show up as spaces).
	buf := make([]byte, defaultStackSize)
	runtime.Stack(buf, false)
	buf = bytes.Trim(buf, "\x00")

	// Try to make some sense of the recoverd value.
	var item string
	switch v := recv.(type) {
	case string:
		item = v
	case error:
		item = v.Error()
	default:
		item = fmt.Sprintf("%v", recv)
	}

	item += "\n" + string(buf)
	l.logs <- Msg{"FATAL", item, tags, time.Now()}
}

// Error logs a recoverable error.
func (l *Logger) Error(tags Tags, err error) {
	l.logs <- Msg{"ERROR", err.Error(), tags, time.Now()}
}

// Info logs an informational message.
func (l *Logger) Info(tags Tags, format string, v ...interface{}) {
	l.logs <- Msg{"INFO ", fmt.Sprintf(format, v...), tags, time.Now()}
}

// Debug logs the lowest level of information, only usefull when debugging
// the application. Only shows when Logger.ShowDebug is set to true, which
// defaults to false.
func (l *Logger) Debug(tags Tags, format string, v ...interface{}) {
	if l.ShowDebug {
		l.logs <- Msg{"DEBUG", fmt.Sprintf(format, v...), tags, time.Now()}
	}
}

// Thumbstone indicates a function is still used in production. When developing
// software it's possible to introduce dead code with updates and new features.
// If a function is being suspected of being dead (not used) in production, add
// a call to Thumbstone and check the production logs to see if you're right.
func (l *Logger) Thumbstone(item string) {
	l.logs <- Msg{"THUMB", item, Tags{"thumbstone"}, time.Now()}
}

// Close blocks until all logs are written to the writer. It will call Flush()
// and Close() on the writer if supported. If either of the functions returns
// an error it will be returned by this function (Flush error first).
//
// Note: it will block forever if logs are still being written, so it's
// required to first stop writing logs and then call Logger.Close.
func (l *Logger) Close() error {
	// First send to the go routine that we need to close, then wait for the go
	// routine to close.
	l.closed <- nil

	// Wait for an response with a possible error.
	err := <-l.closed

	// Either try to flush the io writer or the message writer. Also try to close
	// the writer.
	// TODO: improve thise code.
	if l.w != nil {
		if fw, ok := l.w.(flusher); ok {
			err = fw.Flush()
		}
		if cw, ok := l.w.(io.Closer); ok {
			if closeErr := cw.Close(); err == nil {
				err = closeErr
			}
		}
	} else {
		if fw, ok := l.wMsg.(flusher); ok {
			err = fw.Flush()
		}
		if cw, ok := l.wMsg.(io.Closer); ok {
			if closeErr := cw.Close(); err == nil {
				err = closeErr
			}
		}
	}

	return err
}

// New creates a new logger, which starts a go routine which writes to the
// writer. This way the main thread won't be blocked. Name is the name of the
// logger used in getting (via Get) from any location within your code.
//
// Note: because the logging isn't done on the main thread it's possible that
// the program will close before all the log items are written to the writer.
// It is required to call logger.Close before closing down the program!
// Otherwise logs might be lost!
func New(name string, w io.Writer) (*Logger, error) {
	log, err := newLogger(name, w)
	if err != nil {
		return nil, err
	}

	go func(log *Logger) {
		var closed bool

		for {
			select {
			case <-log.closed: // Indicator that the logger is closing.
				closed = true
			case msg := <-log.logs: // Received a log message.
				// TODO: handle error!
				log.w.Write(msg.Bytes())
			case <-time.After(50 * time.Millisecond):
				// After a timeout we check if we need to close the logger.
				if closed {
					// We logged all the log items, and we can let the Close function
					// know we're done, without an error.
					log.closed <- nil
					return
				}
			}
		}
	}(log)

	return log, nil
}

// NewMsgWriter creates a new logger, similar to New but with a message writer.
// A message writer takes a Msg, which is usefull for writer which don't write
// to a text output.
func NewMsgWriter(name string, w MsgWriter) (*Logger, error) {
	// Create a regular logger with nil as an io.Writer
	log, err := newLogger(name, nil)
	if err != nil {
		return nil, err
	}

	// Then set our message writer.
	log.wMsg = w

	go func(log *Logger) {
		var closed bool

		for {
			select {
			case <-log.closed: // Indicator that the logger is closing.
				closed = true
			case msg := <-log.logs: // Received a log message.
				// TODO: handle error!
				log.wMsg.WriteMsg(msg)
			case <-time.After(50 * time.Millisecond):
				// After a timeout we check if we need to close the logger.
				if closed {
					// We logged all the log items, and we can let the Close function
					// know we're done, without an error.
					log.closed <- nil
					return
				}
			}
		}
	}(log)

	return log, nil
}

// NewFile creates a new logger that logs to a file, it uses bufio to buffer
// the writes.
func NewFile(name, path string) (*Logger, error) {
	file, err := os.OpenFile(path, fileFlag, filePermission)
	if err != nil {
		return nil, err
	}

	// Create a new writer that writes and flushes the bufio.Writer, but closed
	// the os.File.
	w := &fileWriter{bufio.NewWriter(file), file}
	return New(name, w)
}

// Get gets a logger by its name.
func Get(name string) (*Logger, error) {
	log, ok := loggers[name]
	if !ok {
		return nil, errors.New("logger: no logger found with name " + name)
	}

	return log, nil
}

// Combine combines multiple loggers. It's advised to use the same size for all
// loggers.
func Combine(name string, logs ...*Logger) (*Logger, error) {
	if len(logs) == 0 {
		return nil, errors.New("logger: Combine requires atleast one logger")
	}

	log, err := newLogger(name, nil)
	if err != nil {
		return nil, err
	}

	go func(log *Logger, logs []*Logger) {
		var closed bool

		for {
			select {
			case <-log.closed: // Indicator that the logger is closing.
				closed = true
			case msg := <-log.logs: // Received an log message.
				for _, log := range logs {
					log.logs <- msg
				}
			case <-time.After(50 * time.Millisecond):
				// After a timeout we check if we need to close the logger.
				if closed {
					i := len(logs)
					errChan := make(chan error, i)

					// Close all underlying loggers.
					for _, log := range logs {
						go func(log *Logger) {
							errChan <- log.Close()
						}(log)
					}

					// Wait for all underlying loggers to respond.
					var err error
					for i > 0 {
						// Check for the error, if no error has happend yet we'll use this
						// error.
						if lErr := <-errChan; err == nil && lErr != nil {
							err = lErr
						}
						i--
					}

					// Return a possible error.
					log.closed <- err
					return
				}
			}
		}
	}(log, logs)

	return log, nil
}

// newLogger creates a new logger and add it to the loggers map. It only
// returns an error if the name is already used.
func newLogger(name string, w io.Writer) (*Logger, error) {
	if _, ok := loggers[name]; ok {
		return nil, errors.New("logger: name " + name + " already taken")
	}

	log := &Logger{
		Name:   name,
		w:      w,
		logs:   make(chan Msg, defaultLogsSize),
		closed: make(chan error), // needs to block!
	}

	loggers[name] = log
	return log, nil
}

// Cheap integer to fixed-width decimal ASCII. Modified version from the Golang
// logger package.
func itoa(buf *[]byte, i int, wid int) {
	var b [4]byte // only used for year, month and day so 4 is enough.
	bp := len(b) - 1
	for i >= 10 || wid > 1 {
		wid--
		q := i / 10
		b[bp] = byte('0' + i - q*10)
		bp--
		i = q
	}
	b[bp] = byte('0' + i)
	*buf = append(*buf, b[bp:]...)
}
<|endoftext|>


<commit_before>package logtap

// Overview

import (
	"github.com/pagodabox/golang-hatchet"
	"reflect"
	"time"
)

type Collector interface {
	CollectChan() chan Message
	SetLogger(hatchet.Logger)
}

type Drain interface {
	Write(Message)
	SetLogger(hatchet.Logger)
}

type Message struct {
	Type     string
	Time     time.Time
	Priority int
	Content  string
}

type Logtap struct {
	log        hatchet.Logger
	Collectors map[string]Collector
	Drains     map[string]Drain
}

// Establishes a new logtap object
// and makes sure it has the some logger
func New(log hatchet.Logger) *Logtap {
	if log == nil {
		log = hatchet.DevNullLogger{}
	}
	return &Logtap{
		log:        log,
		Collectors: make(map[string]Collector),
		Drains:     make(map[string]Drain),
	}
}

// AddDrain addes a drain to the listeners and sets its logger
func (l *Logtap) AddDrain(tag string, d Drain) {
	d.SetLogger(l.log)
	l.Drains[tag] = d
}

// RemoveDrain drops a drain
func (l *Logtap) RemoveDrain(tag string) {
	delete(l.Drains, tag)
}

// AddCollector adds a collector and begins listening to it
// also adds logging
func (l *Logtap) AddCollector(tag string, c Collector) {
	c.SetLogger(l.log)
	l.Collectors[tag] = c
}

// RemoveCollector remove given collector
func (l *Logtap) RemoveCollector(tag string) {
	delete(l.Collectors, tag)
}

// Start begins listening to all the collectors that are registered.
// it then broadcasts all messages to all the drains that are registered
// this is backgrounded so it can be used by a parent process without getting in the way
func (l *Logtap) Start() {
	go func() {
		for {
			cases := make([]reflect.SelectCase, len(l.Collectors))
			index := 0
			for _, col := range l.Collectors {
				cases[index] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(col.CollectChan())}
				index++
			}

			_, value, ok := reflect.Select(cases)
			// ok will be true if the channel has not been closed.
			if ok {
				l.log.Debug("[LOGTAP][start][collect] %v", value.Interface().(Message))
				l.writeMessage(value.Interface().(Message))
			}

		}

	}()
}

// writeMessage broadcasts to all drains in seperate go routines
func (l *Logtap) writeMessage(msg Message) {
	for _, drain := range l.Drains {
		go drain.Write(msg)
	}
}
<commit_msg>add in a way of publishing message internally not just from a collector<commit_after>package logtap

// Overview

import (
	"github.com/pagodabox/golang-hatchet"
	"reflect"
	"time"
)

type Collector interface {
	CollectChan() chan Message
	SetLogger(hatchet.Logger)
}

type Drain interface {
	Write(Message)
	SetLogger(hatchet.Logger)
}

type Message struct {
	Type     string
	Time     time.Time
	Priority int
	Content  string
}

type Logtap struct {
	log        hatchet.Logger
	Collectors map[string]Collector
	Drains     map[string]Drain
}

// Establishes a new logtap object
// and makes sure it has the some logger
func New(log hatchet.Logger) *Logtap {
	if log == nil {
		log = hatchet.DevNullLogger{}
	}
	return &Logtap{
		log:        log,
		Collectors: make(map[string]Collector),
		Drains:     make(map[string]Drain),
	}
}

// AddDrain addes a drain to the listeners and sets its logger
func (l *Logtap) AddDrain(tag string, d Drain) {
	d.SetLogger(l.log)
	l.Drains[tag] = d
}

// RemoveDrain drops a drain
func (l *Logtap) RemoveDrain(tag string) {
	delete(l.Drains, tag)
}

// AddCollector adds a collector and begins listening to it
// also adds logging
func (l *Logtap) AddCollector(tag string, c Collector) {
	c.SetLogger(l.log)
	l.Collectors[tag] = c
}

// RemoveCollector remove given collector
func (l *Logtap) RemoveCollector(tag string) {
	delete(l.Collectors, tag)
}

// Start begins listening to all the collectors that are registered.
// it then broadcasts all messages to all the drains that are registered
// this is backgrounded so it can be used by a parent process without getting in the way
func (l *Logtap) Start() {
	go func() {
		for {
			cases := make([]reflect.SelectCase, len(l.Collectors))
			index := 0
			for _, col := range l.Collectors {
				cases[index] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(col.CollectChan())}
				index++
			}

			_, value, ok := reflect.Select(cases)
			// ok will be true if the channel has not been closed.
			if ok {
				l.log.Debug("[LOGTAP][start][collect] %v", value.Interface().(Message))
				l.writeMessage(value.Interface().(Message))
			}

		}

	}()
}

func (l *Logtap) Publish(category, content string) {
	m := Message{
		Type:     category,
		Time:     time.Now(),
		Priority: 1,
		Content:  content,
	}
	l.writeMessage(m)
}

// writeMessage broadcasts to all drains in seperate go routines
func (l *Logtap) writeMessage(msg Message) {
	for _, drain := range l.Drains {
		go drain.Write(msg)
	}
}
<|endoftext|>