summaryrefslogtreecommitdiff
path: root/vendor/github.com/op/go-logging/memory.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/op/go-logging/memory.go')
-rw-r--r--vendor/github.com/op/go-logging/memory.go237
1 files changed, 237 insertions, 0 deletions
diff --git a/vendor/github.com/op/go-logging/memory.go b/vendor/github.com/op/go-logging/memory.go
new file mode 100644
index 0000000..8d5152c
--- /dev/null
+++ b/vendor/github.com/op/go-logging/memory.go
@@ -0,0 +1,237 @@
+// Copyright 2013, Örjan Persson. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !appengine
+
+package logging
+
+import (
+ "sync"
+ "sync/atomic"
+ "time"
+ "unsafe"
+)
+
+// TODO pick one of the memory backends and stick with it or share interface.
+
+// InitForTesting is a convenient method when using logging in a test. Once
+// called, the time will be frozen to January 1, 1970 UTC.
+func InitForTesting(level Level) *MemoryBackend {
+ Reset()
+
+ memoryBackend := NewMemoryBackend(10240)
+
+ leveledBackend := AddModuleLevel(memoryBackend)
+ leveledBackend.SetLevel(level, "")
+ SetBackend(leveledBackend)
+
+ timeNow = func() time.Time {
+ return time.Unix(0, 0).UTC()
+ }
+ return memoryBackend
+}
+
+// Node is a record node pointing to an optional next node.
+type node struct {
+ next *node
+ Record *Record
+}
+
+// Next returns the next record node. If there's no node available, it will
+// return nil.
+func (n *node) Next() *node {
+ return n.next
+}
+
+// MemoryBackend is a simple memory based logging backend that will not produce
+// any output but merly keep records, up to the given size, in memory.
+type MemoryBackend struct {
+ size int32
+ maxSize int32
+ head, tail unsafe.Pointer
+}
+
+// NewMemoryBackend creates a simple in-memory logging backend.
+func NewMemoryBackend(size int) *MemoryBackend {
+ return &MemoryBackend{maxSize: int32(size)}
+}
+
+// Log implements the Log method required by Backend.
+func (b *MemoryBackend) Log(level Level, calldepth int, rec *Record) error {
+ var size int32
+
+ n := &node{Record: rec}
+ np := unsafe.Pointer(n)
+
+ // Add the record to the tail. If there's no records available, tail and
+ // head will both be nil. When we successfully set the tail and the previous
+ // value was nil, it's safe to set the head to the current value too.
+ for {
+ tailp := b.tail
+ swapped := atomic.CompareAndSwapPointer(
+ &b.tail,
+ tailp,
+ np,
+ )
+ if swapped == true {
+ if tailp == nil {
+ b.head = np
+ } else {
+ (*node)(tailp).next = n
+ }
+ size = atomic.AddInt32(&b.size, 1)
+ break
+ }
+ }
+
+ // Since one record was added, we might have overflowed the list. Remove
+ // a record if that is the case. The size will fluctate a bit, but
+ // eventual consistent.
+ if b.maxSize > 0 && size > b.maxSize {
+ for {
+ headp := b.head
+ head := (*node)(b.head)
+ if head.next == nil {
+ break
+ }
+ swapped := atomic.CompareAndSwapPointer(
+ &b.head,
+ headp,
+ unsafe.Pointer(head.next),
+ )
+ if swapped == true {
+ atomic.AddInt32(&b.size, -1)
+ break
+ }
+ }
+ }
+ return nil
+}
+
+// Head returns the oldest record node kept in memory. It can be used to
+// iterate over records, one by one, up to the last record.
+//
+// Note: new records can get added while iterating. Hence the number of records
+// iterated over might be larger than the maximum size.
+func (b *MemoryBackend) Head() *node {
+ return (*node)(b.head)
+}
+
+type event int
+
+const (
+ eventFlush event = iota
+ eventStop
+)
+
+// ChannelMemoryBackend is very similar to the MemoryBackend, except that it
+// internally utilizes a channel.
+type ChannelMemoryBackend struct {
+ maxSize int
+ size int
+ incoming chan *Record
+ events chan event
+ mu sync.Mutex
+ running bool
+ flushWg sync.WaitGroup
+ stopWg sync.WaitGroup
+ head, tail *node
+}
+
+// NewChannelMemoryBackend creates a simple in-memory logging backend which
+// utilizes a go channel for communication.
+//
+// Start will automatically be called by this function.
+func NewChannelMemoryBackend(size int) *ChannelMemoryBackend {
+ backend := &ChannelMemoryBackend{
+ maxSize: size,
+ incoming: make(chan *Record, 1024),
+ events: make(chan event),
+ }
+ backend.Start()
+ return backend
+}
+
+// Start launches the internal goroutine which starts processing data from the
+// input channel.
+func (b *ChannelMemoryBackend) Start() {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+
+ // Launch the goroutine unless it's already running.
+ if b.running != true {
+ b.running = true
+ b.stopWg.Add(1)
+ go b.process()
+ }
+}
+
+func (b *ChannelMemoryBackend) process() {
+ defer b.stopWg.Done()
+ for {
+ select {
+ case rec := <-b.incoming:
+ b.insertRecord(rec)
+ case e := <-b.events:
+ switch e {
+ case eventStop:
+ return
+ case eventFlush:
+ for len(b.incoming) > 0 {
+ b.insertRecord(<-b.incoming)
+ }
+ b.flushWg.Done()
+ }
+ }
+ }
+}
+
+func (b *ChannelMemoryBackend) insertRecord(rec *Record) {
+ prev := b.tail
+ b.tail = &node{Record: rec}
+ if prev == nil {
+ b.head = b.tail
+ } else {
+ prev.next = b.tail
+ }
+
+ if b.maxSize > 0 && b.size >= b.maxSize {
+ b.head = b.head.next
+ } else {
+ b.size++
+ }
+}
+
+// Flush waits until all records in the buffered channel have been processed.
+func (b *ChannelMemoryBackend) Flush() {
+ b.flushWg.Add(1)
+ b.events <- eventFlush
+ b.flushWg.Wait()
+}
+
+// Stop signals the internal goroutine to exit and waits until it have.
+func (b *ChannelMemoryBackend) Stop() {
+ b.mu.Lock()
+ if b.running == true {
+ b.running = false
+ b.events <- eventStop
+ }
+ b.mu.Unlock()
+ b.stopWg.Wait()
+}
+
+// Log implements the Log method required by Backend.
+func (b *ChannelMemoryBackend) Log(level Level, calldepth int, rec *Record) error {
+ b.incoming <- rec
+ return nil
+}
+
+// Head returns the oldest record node kept in memory. It can be used to
+// iterate over records, one by one, up to the last record.
+//
+// Note: new records can get added while iterating. Hence the number of records
+// iterated over might be larger than the maximum size.
+func (b *ChannelMemoryBackend) Head() *node {
+ return b.head
+}