func (r *JournalReader) buildMessage() (string, error) {
	var msg string
	var usec uint64
	var err error
	if msg, err = r.journal.GetData("MESSAGE"); err != nil {
		return "", err
	}
	if usec, err = r.journal.GetRealtimeUsec(); err != nil {
		return "", err
	}
	timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond))
	return fmt.Sprintf("%s %s\n", timestamp, msg), nil
}
buildMessage returns a string representing the current journal entry in a simple format which
includes the entry timestamp and MESSAGE field.
package sdjournal
import (
	"errors"
	"fmt"
	"io"
	"log"
	"strings"
	"time"
)
var (
	ErrExpired = errors.New("Timeout expired")
)
type JournalReaderConfig struct {
	Since       time.Duration  
	NumFromTail uint64         
	Cursor      string         
	Matches []Match
	Path string
}
type JournalReader struct {
	journal   *Journal
	msgReader *strings.Reader
}
func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
	r := &JournalReader{}
	var err error
	if config.Path != "" {
		r.journal, err = NewJournalFromDir(config.Path)
	} else {
		r.journal, err = NewJournal()
	}
	if err != nil {
		return nil, err
	}
	for _, m := range config.Matches {
		r.journal.AddMatch(m.String())
	}
	if config.Since != 0 {
		start := time.Now().Add(config.Since)
		if err := r.journal.SeekRealtimeUsec(uint64(start.UnixNano() / 1000)); err != nil {
			return nil, err
		}
	} else if config.NumFromTail != 0 {
		if err := r.journal.SeekTail(); err != nil {
			return nil, err
		}
		skip, err := r.journal.PreviousSkip(config.NumFromTail + 1)
		if err != nil {
			return nil, err
		}
		if skip != config.NumFromTail+1 {
			if err := r.journal.SeekHead(); err != nil {
				return nil, err
			}
		}
	} else if config.Cursor != "" {
		if err := r.journal.SeekCursor(config.Cursor); err != nil {
			return nil, err
		}
	}
	return r, nil
}
func (r *JournalReader) Read(b []byte) (int, error) {
	var err error
	if r.msgReader == nil {
		var c uint64
		c, err = r.journal.Next()
		if err != nil {
			return 0, err
		}
		if c == 0 {
			return 0, io.EOF
		}
		var msg string
		msg, err = r.buildMessage()
		if err != nil {
			return 0, err
		}
		r.msgReader = strings.NewReader(msg)
	}
	var sz int
	sz, err = r.msgReader.Read(b)
	if err == io.EOF {
		r.msgReader = nil
		return sz, nil
	}
	if err != nil {
		return sz, err
	}
	if r.msgReader.Len() == 0 {
		r.msgReader = nil
	}
	return sz, nil
}
func (r *JournalReader) Close() error {
	return r.journal.Close()
}
func (r *JournalReader) Rewind() error {
	r.msgReader = nil
	return r.journal.SeekHead()
}
func (r *JournalReader) Follow(until <-chan time.Time, writer io.Writer) (err error) {
	var msg = make([]byte, 64*1<<(10))
process:
	for {
		c, err := r.Read(msg)
		if err != nil && err != io.EOF {
			break process
		}
		select {
		case <-until:
			return ErrExpired
		default:
			if c > 0 {
				if _, err = writer.Write(msg[:c]); err != nil {
					break process
				}
				continue process
			}
		}
		events := make(chan int, 1)
		pollDone := make(chan bool, 1)
		go func() {
			for {
				select {
				case <-pollDone:
					return
				default:
					events <- r.journal.Wait(time.Duration(1) * time.Second)
				}
			}
		}()
		select {
		case <-until:
			pollDone <- true
			return ErrExpired
		case e := <-events:
			pollDone <- true
			switch e {
			case SD_JOURNAL_NOP, SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
			default:
				log.Printf("Received unknown event: %d\n", e)
			}
			continue process
		}
	}
	return
}