mirror of
https://github.com/taigrr/log-socket
synced 2025-01-18 04:53:14 -08:00
Adds logger directory refactor
This commit is contained in:
250
logger/logger.go
Normal file
250
logger/logger.go
Normal file
@@ -0,0 +1,250 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
LTrace Level = iota
|
||||
LDebug
|
||||
LInfo
|
||||
LWarn
|
||||
LError
|
||||
LPanic
|
||||
LFatal
|
||||
)
|
||||
|
||||
var (
|
||||
clients []*Client
|
||||
sliceTex sync.Mutex
|
||||
stderrClient *Client
|
||||
cleanup sync.Once
|
||||
stderrFinished chan bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
stderrClient = CreateClient()
|
||||
stderrClient.SetLogLevel(LTrace)
|
||||
stderrFinished = make(chan bool, 1)
|
||||
go stderrClient.logStdErr()
|
||||
}
|
||||
|
||||
func (c *Client) logStdErr() {
|
||||
for {
|
||||
select {
|
||||
case e, more := <-c.writer:
|
||||
if e.level >= c.LogLevel {
|
||||
fmt.Fprintf(os.Stderr, "%s\t%s\t%s\t%s\n", e.Timestamp.String(), e.Level, e.Output, e.File)
|
||||
}
|
||||
if !more {
|
||||
stderrFinished <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CreateClient() *Client {
|
||||
var client Client
|
||||
client.initialized = true
|
||||
client.writer = make(LogWriter, 1000)
|
||||
sliceTex.Lock()
|
||||
clients = append(clients, &client)
|
||||
sliceTex.Unlock()
|
||||
return &client
|
||||
}
|
||||
|
||||
func Flush() {
|
||||
cleanup.Do(func() {
|
||||
close(stderrClient.writer)
|
||||
<-stderrFinished
|
||||
stderrClient.Destroy()
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) Destroy() error {
|
||||
var otherClients []*Client
|
||||
if !c.initialized {
|
||||
panic(errors.New("Cannot delete uninitialized client, did you use CreateClient?"))
|
||||
}
|
||||
sliceTex.Lock()
|
||||
c.writer = nil
|
||||
c.initialized = false
|
||||
for _, x := range clients {
|
||||
if x.initialized {
|
||||
otherClients = append(otherClients, x)
|
||||
}
|
||||
}
|
||||
clients = otherClients
|
||||
sliceTex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) GetLogLevel() Level {
|
||||
if !c.initialized {
|
||||
panic(errors.New("Cannot get level for uninitialized client, use CreateClient instead"))
|
||||
}
|
||||
return c.LogLevel
|
||||
}
|
||||
|
||||
func createLog(e Entry) {
|
||||
sliceTex.Lock()
|
||||
for _, c := range clients {
|
||||
func(c *Client, e Entry) {
|
||||
select {
|
||||
case c.writer <- e:
|
||||
// try to clear out one of the older entries
|
||||
default:
|
||||
select {
|
||||
case <-c.writer:
|
||||
c.writer <- e
|
||||
default:
|
||||
}
|
||||
}
|
||||
}(c, e)
|
||||
}
|
||||
sliceTex.Unlock()
|
||||
}
|
||||
|
||||
// SetLogLevel set log level of logger
|
||||
func (c *Client) SetLogLevel(level Level) {
|
||||
if !c.initialized {
|
||||
panic(errors.New("Cannot set level for uninitialized client, use CreateClient instead"))
|
||||
}
|
||||
c.LogLevel = level
|
||||
}
|
||||
|
||||
func (c *Client) Get() Entry {
|
||||
if !c.initialized {
|
||||
panic(errors.New("Cannot get logs for uninitialized client, did you use CreateClient?"))
|
||||
}
|
||||
return <-c.writer
|
||||
}
|
||||
|
||||
// Trace prints out logs on trace level
|
||||
func Trace(args ...interface{}) {
|
||||
output := fmt.Sprint(args...)
|
||||
e := Entry{
|
||||
Timestamp: time.Now(),
|
||||
Output: output,
|
||||
File: fileInfo(2),
|
||||
Level: "TRACE",
|
||||
level: LTrace,
|
||||
}
|
||||
createLog(e)
|
||||
// entry := logger.WithFields(logrus.Fields{})
|
||||
// entry.Data["file"] = fileInfo(2)
|
||||
// entry.Debug(args...)
|
||||
}
|
||||
|
||||
// Debug prints out logs on debug level
|
||||
func Debug(args ...interface{}) {
|
||||
output := fmt.Sprint(args...)
|
||||
e := Entry{
|
||||
Timestamp: time.Now(),
|
||||
Output: output,
|
||||
File: fileInfo(2),
|
||||
Level: "DEBUG",
|
||||
level: LDebug,
|
||||
}
|
||||
createLog(e)
|
||||
|
||||
}
|
||||
|
||||
// Info prints out logs on info level
|
||||
func Info(args ...interface{}) {
|
||||
output := fmt.Sprint(args...)
|
||||
e := Entry{
|
||||
Timestamp: time.Now(),
|
||||
Output: output,
|
||||
File: fileInfo(2),
|
||||
Level: "INFO",
|
||||
level: LInfo,
|
||||
}
|
||||
createLog(e)
|
||||
}
|
||||
|
||||
// Warn prints out logs on warn level
|
||||
func Warn(args ...interface{}) {
|
||||
output := fmt.Sprint(args...)
|
||||
e := Entry{
|
||||
Timestamp: time.Now(),
|
||||
Output: output,
|
||||
File: fileInfo(2),
|
||||
Level: "WARN",
|
||||
level: LWarn,
|
||||
}
|
||||
createLog(e)
|
||||
}
|
||||
|
||||
// Error prints out logs on error level
|
||||
func Error(args ...interface{}) {
|
||||
output := fmt.Sprint(args...)
|
||||
e := Entry{
|
||||
Timestamp: time.Now(),
|
||||
Output: output,
|
||||
File: fileInfo(2),
|
||||
Level: "ERROR",
|
||||
level: LError,
|
||||
}
|
||||
createLog(e)
|
||||
}
|
||||
|
||||
// Panic prints out logs on panic level
|
||||
func Panic(args ...interface{}) {
|
||||
output := fmt.Sprint(args...)
|
||||
e := Entry{
|
||||
Timestamp: time.Now(),
|
||||
Output: output,
|
||||
File: fileInfo(2),
|
||||
Level: "PANIC",
|
||||
level: LPanic,
|
||||
}
|
||||
createLog(e)
|
||||
if len(args) >= 0 {
|
||||
switch args[0].(type) {
|
||||
case error:
|
||||
panic(args[0])
|
||||
default:
|
||||
// falls through to default below
|
||||
}
|
||||
}
|
||||
Flush()
|
||||
panic(errors.New(output))
|
||||
}
|
||||
|
||||
// Fatal prints out logs on fatal level
|
||||
func Fatal(args ...interface{}) {
|
||||
output := fmt.Sprint(args...)
|
||||
e := Entry{
|
||||
Timestamp: time.Now(),
|
||||
Output: output,
|
||||
File: fileInfo(2),
|
||||
Level: "FATAL",
|
||||
level: LFatal,
|
||||
}
|
||||
createLog(e)
|
||||
Flush()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// fileInfo for getting which line in which file
|
||||
func fileInfo(skip int) string {
|
||||
_, file, line, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
file = "<???>"
|
||||
line = 1
|
||||
} else {
|
||||
slash := strings.LastIndex(file, "/")
|
||||
if slash >= 0 {
|
||||
file = file[slash+1:]
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
126
logger/logger_test.go
Normal file
126
logger/logger_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test CreateClient() and Client.Destroy()
|
||||
func TestCreateDestroy(t *testing.T) {
|
||||
// Ensure only stderr exists at the beginning
|
||||
if len(clients) != 1 {
|
||||
t.Errorf("Expected 1 client, but found %d", len(clients))
|
||||
}
|
||||
// Create a new client, ensure it's added
|
||||
c := CreateClient()
|
||||
if len(clients) != 2 {
|
||||
t.Errorf("Expected 2 clients, but found %d", len(clients))
|
||||
}
|
||||
// Destroy it and ensure it's actually removed from the array
|
||||
c.Destroy()
|
||||
if len(clients) != 1 {
|
||||
t.Errorf("Expected 1 client, but found %d", len(clients))
|
||||
}
|
||||
}
|
||||
|
||||
// SetLogLevel set log level of logger
|
||||
func TestSetLogLevel(t *testing.T) {
|
||||
logLevels := [...]Level{LTrace, LDebug, LInfo, LWarn, LError, LPanic, LFatal}
|
||||
c := CreateClient()
|
||||
for _, x := range logLevels {
|
||||
c.SetLogLevel(x)
|
||||
if c.GetLogLevel() != x {
|
||||
t.Errorf("Got logLevel %d, but expected %d", int(c.GetLogLevel()), int(x))
|
||||
}
|
||||
}
|
||||
c.Destroy()
|
||||
}
|
||||
|
||||
func BenchmarkDebugSerial(b *testing.B) {
|
||||
c := CreateClient()
|
||||
var x sync.WaitGroup
|
||||
x.Add(b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
Debug(i)
|
||||
go func() {
|
||||
c.Get()
|
||||
x.Done()
|
||||
}()
|
||||
}
|
||||
x.Wait()
|
||||
c.Destroy()
|
||||
}
|
||||
|
||||
// Trace ensure logs come out in the right order
|
||||
func TestOrder(t *testing.T) {
|
||||
testString := "Testing trace: "
|
||||
var c *Client
|
||||
c = CreateClient()
|
||||
c.SetLogLevel(LTrace)
|
||||
|
||||
for i := 0; i < 5000; i++ {
|
||||
Trace(testString + strconv.Itoa(i))
|
||||
if testString+strconv.Itoa(i) != c.Get().Output {
|
||||
t.Error("Trace input doesn't match output")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug prints out logs on debug level
|
||||
func TestDebug(t *testing.T) {
|
||||
Debug("Test of Debug")
|
||||
// if logLevel >= LDebug {
|
||||
// entry := logger.WithFields(logrus.Fields{})
|
||||
// entry.Data["file"] = fileInfo(2)
|
||||
// entry.Debug(args...)
|
||||
// }
|
||||
}
|
||||
|
||||
// Info prints out logs on info level
|
||||
func TestInfo(t *testing.T) {
|
||||
// if logLevel >= LInfo {
|
||||
// entry := logger.WithFields(logrus.Fields{})
|
||||
// entry.Data["file"] = fileInfo(2)
|
||||
// entry.Info(args...)
|
||||
// }
|
||||
}
|
||||
|
||||
// Warn prints out logs on warn level
|
||||
func TestWarn(t *testing.T) {
|
||||
// if logLevel >= LWarn {
|
||||
// entry := logger.WithFields(logrus.Fields{})
|
||||
// entry.Data["file"] = fileInfo(2)
|
||||
// entry.Warn(args...)
|
||||
// }
|
||||
}
|
||||
|
||||
// Error prints out logs on error level
|
||||
func TestError(t *testing.T) {
|
||||
// if logLevel >= LError {
|
||||
// entry := logger.WithFields(logrus.Fields{})
|
||||
// entry.Data["file"] = fileInfo(2)
|
||||
// entry.Error(args...)
|
||||
// }
|
||||
}
|
||||
|
||||
// Fatal prints out logs on fatal level
|
||||
func TestFatal(t *testing.T) {
|
||||
// if logLevel >= LFatal {
|
||||
// entry := logger.WithFields(logrus.Fields{})
|
||||
// entry.Data["file"] = fileInfo(2)
|
||||
// entry.Fatal(args...)
|
||||
// }
|
||||
}
|
||||
|
||||
// Panic prints out logs on panic level
|
||||
func TestPanic(t *testing.T) {
|
||||
// if logLevel >= LPanic {
|
||||
// entry := logger.WithFields(logrus.Fields{})
|
||||
// entry.Data["file"] = fileInfo(2)
|
||||
// entry.Panic(args...)
|
||||
// }
|
||||
}
|
||||
func TestFlush(t *testing.T) {
|
||||
defer Flush()
|
||||
}
|
||||
20
logger/structs.go
Normal file
20
logger/structs.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package logger
|
||||
|
||||
import "time"
|
||||
|
||||
type LogWriter chan Entry
|
||||
type Level int
|
||||
|
||||
type Client struct {
|
||||
LogLevel Level `json:"level"`
|
||||
writer LogWriter
|
||||
initialized bool
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Output string `json:"output"`
|
||||
File string `json:"file"`
|
||||
Level string `json:"level"`
|
||||
level Level
|
||||
}
|
||||
Reference in New Issue
Block a user