diff --git a/clock.go b/clock.go new file mode 100644 index 0000000..b77d2df --- /dev/null +++ b/clock.go @@ -0,0 +1,22 @@ +package blammo + +import ( + "time" +) + +// Clock is used to allow for the injection of false clocks into log writers. +type Clock interface { + Now() time.Time +} + +// SystemClock is the default system clock +type SystemClock struct{} + +// Now returns the current system time +func (c SystemClock) Now() time.Time { return time.Now() } + +// FixedClock is a Clock implementation that always returns the same time. +type FixedClock time.Time + +// Now always returns the same time +func (c FixedClock) Now() time.Time { return time.Time(c) } diff --git a/console.go b/console.go new file mode 100644 index 0000000..6893582 --- /dev/null +++ b/console.go @@ -0,0 +1,4 @@ +package blammo + +type ConsoleWriter struct { +} diff --git a/event.go b/event.go index a81fdd6..bc140e4 100644 --- a/event.go +++ b/event.go @@ -8,7 +8,8 @@ import ( // database row if you're writing to a database, etc. Everything internally is // expressed as an event. // -// Event is exported to support the implementation of custom log writers. +// Event is exported to support the implementation of custom log writers. Most +// users should not need to handle this type directly. type Event struct { // severity of the event Level Level diff --git a/level.go b/level.go index da95d09..feede8f 100644 --- a/level.go +++ b/level.go @@ -8,14 +8,10 @@ const ( Debug Level = iota // Info is intended to be used to report expected behaviors; it's used to - // log usage and observe normal behaviors. + // log usage and observe normal behaviors. This is intended to be used + // along an applications "happy path". Info - // Warn is intended to be used to report events that are not along the - // expected "happy path" of the application. These events should generally - // represent failures of -other- systems. - Warn - // Error is intended to b e used to report things that the application was // not able to handle. These events should generally represent failures of // the system at hand. diff --git a/line.go b/line.go index f357b29..e9447e6 100644 --- a/line.go +++ b/line.go @@ -1,4 +1,87 @@ package blammo +import ( + "bytes" + "io" + "strconv" + "strings" + "sync" + "time" +) + type LineWriter struct { + pool sync.Pool + out struct { + sync.Mutex + io.Writer + } +} + +func NewLineWriter(w io.Writer) *LineWriter { + lw := new(LineWriter) + lw.pool = sync.Pool{ + New: func() interface{} { return new(bytes.Buffer) }, + } + lw.out.Writer = w + return lw +} + +func (l *LineWriter) WriteEvent(e *Event) { + buf := l.pool.Get().(*bytes.Buffer) + buf.Reset() + + buf.WriteString(e.Time.Format(time.RFC3339)) + buf.WriteRune(' ') + + switch e.Level { + case Debug: + buf.WriteString("d ") + case Info: + buf.WriteString("i ") + case Error: + buf.WriteString("e ") + default: + buf.WriteString("? ") + } + + buf.WriteRune('[') + buf.WriteString(e.Path.String()) + buf.WriteRune(']') + buf.WriteRune(' ') + + if e.Tags == nil { + buf.WriteString("[] ") + } else { + buf.WriteRune('[') + writeTags(buf, e.Tags) + buf.WriteString("] ") + } + + buf.WriteString(strings.ReplaceAll(e.Text, string('\n'), "\n")) + + l.out.Lock() + l.out.Write(buf.Bytes()) + l.out.Unlock() + l.pool.Put(buf) +} + +func writeTags(buf *bytes.Buffer, tags *Tags) { + if tags.parent != nil { + writeTags(buf, tags.parent) + buf.WriteRune('+') + } + buf.WriteString(tags.key) + if tags.value != nil { + buf.WriteRune('=') + switch v := tags.value.(type) { + case int: + buf.WriteString(strconv.Itoa(v)) + case string: + buf.WriteString(MakeSafeName(v)) + case float64: + buf.WriteString(strconv.FormatFloat(v, 'f', -1, 64)) + default: + buf.WriteString("???") + } + } } diff --git a/line_test.go b/line_test.go new file mode 100644 index 0000000..758535e --- /dev/null +++ b/line_test.go @@ -0,0 +1,26 @@ +package blammo + +import ( + "bytes" + "testing" + "time" +) + +func TestLineWriter(t *testing.T) { + var buf bytes.Buffer + w := NewLineWriter(&buf) + w.WriteEvent(&Event{ + Level: Debug, + Time: time.Now(), + Path: NewPath("alice").Child("bob").Child("carol"), + Text: "hey you farthead", + Tags: &Tags{ + key: "poop", + parent: &Tags{ + key: "num_poops", + value: 27, + }, + }, + }) + t.Error(buf.String()) +} diff --git a/log.go b/log.go new file mode 100644 index 0000000..095d65c --- /dev/null +++ b/log.go @@ -0,0 +1,55 @@ +package blammo + +import ( + "fmt" +) + +type Log struct { + lvl Level + dw EventWriter + iw EventWriter + ew EventWriter + path *Path + clock Clock + tags *Tags +} + +type Options struct { + Debug EventWriter + Info EventWriter + Error EventWriter +} + +var defaults Options + +func init() { + defaults.Debug = NullWriter{} +} + +func NewLog() *Log { + return new(Log) +} + +func format(t string, args ...interface{}) string { + if len(args) == 0 { + return t + } + return fmt.Sprintf(t, args...) +} + +func (l *Log) Debug(t string, args ...interface{}) { +} + +func (l *Log) Info(t string, args ...interface{}) { +} + +func (l *Log) Error(t string, args ...interface{}) { +} + +func (l *Log) Child(name string) *Log { + return l +} + +func (l *Log) Tag(key string, value interface{}) *Log { + return l +} diff --git a/path_test.go b/path_test.go index fcf060d..214afeb 100644 --- a/path_test.go +++ b/path_test.go @@ -59,6 +59,9 @@ func TestSafeNames(t *testing.T) { "alice[bob]", "alice{bob}", "alice=bob", + "alice&bob", + "alice+bob", + "alice(bob", } for _, n := range unsafeNames { if IsSafeName(n) { diff --git a/tag.go b/tag.go index 0692b8b..73e7a23 100644 --- a/tag.go +++ b/tag.go @@ -5,3 +5,19 @@ type Tags struct { value interface{} parent *Tags } + +func (t *Tags) Tag(key string) *Tags { + return &Tags{key: key, parent: t} +} + +func (t *Tags) TagInt(key string, v int) *Tags { + return &Tags{key: key, value: v, parent: t} +} + +func (t *Tags) TagString(key, v string) *Tags { + return &Tags{key: key, value: v, parent: t} +} + +func (t *Tags) TagFloat(key string, v float64) *Tags { + return &Tags{key: key, value: v, parent: t} +} diff --git a/writer.go b/writer.go index 293aa53..821f9bc 100644 --- a/writer.go +++ b/writer.go @@ -1,5 +1,7 @@ package blammo -type EventWriter interface { - WriteEvent(*Event) error -} +type EventWriter interface{ WriteEvent(*Event) } + +type NullWriter struct{} + +func (w NullWriter) WriteEvent(*Event) {}