Initial commit
This commit is contained in:
commit
248ff19dbd
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Noah Petherbridge
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
71
README.md
Normal file
71
README.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Go Log
|
||||
|
||||
This is Yet Another Logger for Go programs.
|
||||
|
||||
![Screenshot](https://raw.githubusercontent.com/kirsle/golog/master/screenshot.png)
|
||||
|
||||
This is a logging package designed for local interactive shells running text
|
||||
based Go programs. To that end, this prints colorful log lines with customizable
|
||||
themes.
|
||||
|
||||
The color options for the log lines are `NoColor` (default), `ANSIColor`
|
||||
which limits the color codes to the standard 16 ANSI colors, and
|
||||
`ExtendedColor` which supports the 256-color palette of `xterm` and other
|
||||
modern terminal emulators. The theming engine supports defining colors using
|
||||
hex codes, supported by [tomnomnom/xtermcolor](https://github.com/tomnomnom/xtermcolor).
|
||||
|
||||
This module is still a work in progress and will be extended and improved as I
|
||||
use it for other personal Go projects.
|
||||
|
||||
# Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "github.com/kirsle/golog"
|
||||
|
||||
var log golog.Logger
|
||||
|
||||
func init() {
|
||||
// Get a named logger and configure it. Note: you can call GetLogger any
|
||||
// number of times from any place in your codebase. It implements the
|
||||
// singleton pattern.
|
||||
log = golog.GetLogger("main")
|
||||
log.Configure(&golog.Config{
|
||||
Colors: golog.ExtendedColor,
|
||||
Theme: golog.DarkTheme,
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
// The log functions work like `fmt.Printf`
|
||||
log.Debug("Running on %s", runtime.GOOS)
|
||||
log.Info("Hello, world!")
|
||||
}
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Noah Petherbridge
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
23
ansi/colors.go
Normal file
23
ansi/colors.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package ansi
|
||||
|
||||
// Names and escape codes for the standard ANSI colors.
|
||||
const (
|
||||
Black = `30`
|
||||
BrightBlack = `30;1`
|
||||
Red = `31`
|
||||
BrightRed = `31;1`
|
||||
Green = `32`
|
||||
BrightGreen = `32;1`
|
||||
Yellow = `33`
|
||||
BrightYellow = `33;1`
|
||||
Blue = `34`
|
||||
BrightBlue = `34;1`
|
||||
Magenta = `35`
|
||||
BrightMagenta = `35;1`
|
||||
Cyan = `36`
|
||||
BrightCyan = `36;1`
|
||||
White = `37`
|
||||
BrightWhite = `37;1`
|
||||
|
||||
Reset = `0`
|
||||
)
|
34
colors.go
Normal file
34
colors.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package golog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tomnomnom/xtermcolor"
|
||||
)
|
||||
|
||||
type colorLevel int
|
||||
|
||||
// Options for color support in your logger.
|
||||
const (
|
||||
// NoColor doesn't use any color codes at all (plain text).
|
||||
NoColor colorLevel = iota
|
||||
|
||||
// ANSIColor uses the standard 16 colors supported by most terminals. This
|
||||
// option is the most portable across platforms.
|
||||
ANSIColor
|
||||
|
||||
// ExtendedColor allows the use of 256 colors supported by most modern
|
||||
// terminals (24-bit color codes).
|
||||
ExtendedColor
|
||||
)
|
||||
|
||||
// HexColor is a convenient wrapper around `xtermcolor.FromHexStr` to define colors
|
||||
// for themes for xterm-256color codes.
|
||||
func HexColor(hex string) string {
|
||||
code, err := xtermcolor.FromHexStr(hex)
|
||||
if err != nil {
|
||||
code = 201 // bright magenta seems like a good default
|
||||
}
|
||||
|
||||
return fmt.Sprintf("38;5;%d", code)
|
||||
}
|
72
config.go
Normal file
72
config.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package golog
|
||||
|
||||
import "io"
|
||||
|
||||
// Config stores settings that control the logger's behavior.
|
||||
type Config struct {
|
||||
// Level is one of DebugLevel, InfoLevel, WarnLevel, ErrorLevel or FatalLevel.
|
||||
// Messages emitted by the logger must be 'at least' this level to be logged.
|
||||
Level logLevel
|
||||
|
||||
// What colors are supported? Default is NoColor. Use ANSIColor to support
|
||||
// legacy terminal emulators, or ExtendedColor for modern 256-color support.
|
||||
Colors colorLevel
|
||||
|
||||
// Which color theme are you using? The default is DarkTheme.
|
||||
Theme Theme
|
||||
|
||||
// Where to write the log messages to? If not defined with a custom io.Writer,
|
||||
// the default goes to standard output for Debug and Info messages and
|
||||
// standard error for warnings, errors, and fatal messages.
|
||||
Writer *io.Writer
|
||||
|
||||
// How do you want to format your log lines? This should be a Go text format
|
||||
// string, with the following variable placeholders:
|
||||
//
|
||||
// {{.Time}} inserts the date/time stamp for the log message.
|
||||
// {{.Level}} inserts a label for the log level, e.g. "INFO" or "WARN"
|
||||
// {{.Message}} inserts the text of the log message itself.
|
||||
// {{.Primary}} inserts the color sequence for the primary color based
|
||||
// on the log level for the message.
|
||||
// {{.Secondary}} inserts the color sequence for the secondary color.
|
||||
// {{.Reset}} inserts the 'reset' color sequence to stop coloring
|
||||
// the rest of the text that follows.
|
||||
//
|
||||
// The default log format is as follows:
|
||||
//
|
||||
// {{.Secondary}}{{.Time}}{{.Reset}} {{.Primary}}[{{.Level}}]{{.Reset}} {{.Message}}
|
||||
Format string
|
||||
|
||||
// How do you want to format your time stamps? (The `{{.Time}}`). This uses
|
||||
// the Go `time` module, so the TimeFormat should use their reference date/time.
|
||||
// The default TimeFormat is: `2006-01-02 15:04:05`
|
||||
TimeFormat string
|
||||
}
|
||||
|
||||
// DefaultConfig returns a Config with the default values filled in.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Theme: DarkTheme,
|
||||
Format: DefaultFormat,
|
||||
TimeFormat: DefaultTime,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure applies the configuration to the logger. If any of the following
|
||||
// keys are not defined (or have zero-values), the default value for the key will
|
||||
// be used instead:
|
||||
//
|
||||
// Format
|
||||
// TimeFormat
|
||||
func (l *Logger) Configure(cfg *Config) {
|
||||
// Important keys and their defaults.
|
||||
if cfg.Format == "" {
|
||||
cfg.Format = DefaultFormat
|
||||
}
|
||||
if cfg.TimeFormat == "" {
|
||||
cfg.TimeFormat = DefaultTime
|
||||
}
|
||||
|
||||
l.Config = cfg
|
||||
l.template = nil
|
||||
}
|
102
formatter.go
Normal file
102
formatter.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package golog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/kirsle/golog/ansi"
|
||||
)
|
||||
|
||||
// Convenient log formats to use in your logger.
|
||||
const (
|
||||
// DefaultFormat: shows the date in the secondary (dark) color, the label
|
||||
// in the bright color, and the message text in the normal color.
|
||||
DefaultFormat = `{{.Secondary}}{{.Time}}{{.Reset}} {{.Primary}}[{{.Level}}]{{.Reset}} {{.Message}}`
|
||||
|
||||
// ColorfulFormat: like the DefaultFormat, but the message itself is also
|
||||
// colored using the secondary color.
|
||||
ColorfulFormat = `{{.Secondary}}{{.Time}}{{.Reset}} {{.Primary}}[{{.Level}}]{{.Reset}} {{.Secondary}}{{.Message}}{{.Reset}}`
|
||||
)
|
||||
|
||||
// Convenient time formats to use in your logger.
|
||||
const (
|
||||
// DefaultTime is the default, in `yyyy-mm-dd hh:mm:ss` format.
|
||||
DefaultTime = `2006-01-02 15:04:05`
|
||||
|
||||
// FriendlyTime is a human readable `Jan 2 15:04:05 2006` format.
|
||||
FriendlyTime = `Jan 2 15:04:05 2006`
|
||||
)
|
||||
|
||||
// formatter provides the variables that can be used in the log format.
|
||||
type formatter struct {
|
||||
Time string
|
||||
Level string
|
||||
Message string
|
||||
Primary string
|
||||
Secondary string
|
||||
Reset string
|
||||
}
|
||||
|
||||
// Format and return a log message.
|
||||
func (l *Logger) Format(level logLevel, tmpl string, args ...interface{}) string {
|
||||
// Prepare the variables to apply to the log message format.
|
||||
format := formatter{
|
||||
Time: time.Now().Format(l.Config.TimeFormat),
|
||||
Level: levelNames[level],
|
||||
Message: fmt.Sprintf(tmpl, args...),
|
||||
}
|
||||
|
||||
// Find the theme color to use.
|
||||
if l.Config.Colors != NoColor {
|
||||
var (
|
||||
primary ThemeColor
|
||||
secondary ThemeColor
|
||||
)
|
||||
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
primary = l.Config.Theme.Debug
|
||||
secondary = l.Config.Theme.DebugSecondary
|
||||
case InfoLevel:
|
||||
primary = l.Config.Theme.Info
|
||||
secondary = l.Config.Theme.InfoSecondary
|
||||
case WarnLevel:
|
||||
primary = l.Config.Theme.Warn
|
||||
secondary = l.Config.Theme.WarnSecondary
|
||||
case ErrorLevel:
|
||||
primary = l.Config.Theme.Error
|
||||
secondary = l.Config.Theme.ErrorSecondary
|
||||
}
|
||||
|
||||
// What color level are we supporting?
|
||||
if l.Config.Colors == ANSIColor {
|
||||
format.Primary = fmt.Sprintf("\x1B[%sm", primary.ANSI)
|
||||
format.Secondary = fmt.Sprintf("\x1B[%sm", secondary.ANSI)
|
||||
format.Reset = fmt.Sprintf("\x1B[%sm", ansi.Reset)
|
||||
} else if l.Config.Colors == ExtendedColor {
|
||||
format.Primary = fmt.Sprintf("\x1B[%sm", primary.Extended)
|
||||
format.Secondary = fmt.Sprintf("\x1B[%sm", secondary.Extended)
|
||||
format.Reset = fmt.Sprintf("\x1B[%sm", ansi.Reset)
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have the template cached?
|
||||
if l.template == nil {
|
||||
template, err := template.New("golog").Parse(l.Config.Format)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("[GoLog format error: %s]", err)
|
||||
}
|
||||
l.template = template
|
||||
}
|
||||
|
||||
// Evaluate the template.
|
||||
var buf bytes.Buffer
|
||||
err := l.template.Execute(&buf, format)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("[GoLog template error: %s]", err)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
43
golog.go
Normal file
43
golog.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package golog
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// An internal map of named loggers. This allows for GetLogger() to be called
|
||||
// many times from anywhere in your code base, but for only one logger instance
|
||||
// to be created for it.
|
||||
var (
|
||||
loggers map[string]*Logger
|
||||
loggerMutex sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
loggers = map[string]*Logger{}
|
||||
}
|
||||
|
||||
// Logger stores the configuration for a named logger instance.
|
||||
type Logger struct {
|
||||
Name string
|
||||
Config *Config
|
||||
|
||||
// Private cached text/template, the first time the formatter is used.
|
||||
template *template.Template
|
||||
}
|
||||
|
||||
// GetLogger initializes and returns a new Logger.
|
||||
func GetLogger(name string) *Logger {
|
||||
loggerMutex.Lock()
|
||||
defer loggerMutex.Unlock()
|
||||
|
||||
// Initialize the logger the first time we ask for it.
|
||||
if _, ok := loggers[name]; !ok {
|
||||
loggers[name] = &Logger{
|
||||
Name: name,
|
||||
Config: DefaultConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
return loggers[name]
|
||||
}
|
54
golog_test.go
Normal file
54
golog_test.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package golog
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestColors(t *testing.T) {
|
||||
log := GetLogger("test")
|
||||
|
||||
// Helper function to emit all the log types.
|
||||
emitLogs := func(message string) {
|
||||
log.Debug(message)
|
||||
log.Info(message)
|
||||
log.Warn(message)
|
||||
log.Error(message)
|
||||
}
|
||||
|
||||
log.Configure(&Config{
|
||||
Theme: DarkTheme,
|
||||
Colors: ANSIColor,
|
||||
})
|
||||
emitLogs("With standard 16-color ANSI codes.")
|
||||
|
||||
log.Configure(&Config{
|
||||
Theme: DarkTheme,
|
||||
Colors: ExtendedColor,
|
||||
})
|
||||
emitLogs("With xterm-256color codes.")
|
||||
|
||||
log.Configure(&Config{
|
||||
Theme: DarkTheme,
|
||||
Colors: ExtendedColor,
|
||||
Format: ColorfulFormat,
|
||||
})
|
||||
emitLogs("Colorful format.")
|
||||
}
|
||||
|
||||
func TestLogLevels(t *testing.T) {
|
||||
log := GetLogger("levels")
|
||||
|
||||
// Helper function to emit all the log types.
|
||||
emitLogs := func(message string) {
|
||||
log.Debug(message)
|
||||
log.Info(message)
|
||||
log.Warn(message)
|
||||
log.Error(message)
|
||||
}
|
||||
|
||||
emitLogs("Default log level=debug")
|
||||
log.Config.Level = InfoLevel
|
||||
emitLogs("With Level=Info")
|
||||
log.Config.Level = WarnLevel
|
||||
emitLogs("With Level=Warn")
|
||||
log.Config.Level = ErrorLevel
|
||||
emitLogs("With Level=Error")
|
||||
}
|
70
levels.go
Normal file
70
levels.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package golog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type logLevel int
|
||||
|
||||
// Log levels for controlling whether or not logs of certain types will be
|
||||
// emitted by your logger.
|
||||
const (
|
||||
DebugLevel logLevel = iota
|
||||
InfoLevel
|
||||
WarnLevel
|
||||
ErrorLevel
|
||||
)
|
||||
|
||||
// Map log levels to human readable labels.
|
||||
var levelNames = map[logLevel]string{
|
||||
DebugLevel: "DEBUG",
|
||||
InfoLevel: "INFO",
|
||||
WarnLevel: "WARN",
|
||||
ErrorLevel: "ERROR",
|
||||
}
|
||||
|
||||
// emit is the general purpose log line emitter.
|
||||
func (l *Logger) emit(level logLevel, tmpl string, args ...interface{}) {
|
||||
message := l.Format(level, tmpl, args...)
|
||||
|
||||
// If we have a log writer, send it there.
|
||||
if l.Config.Writer != nil {
|
||||
// l.Config.Writer.Write(message)
|
||||
} else {
|
||||
// No writer given so we default to standard out/error.
|
||||
if level <= InfoLevel {
|
||||
fmt.Fprintln(os.Stdout, message)
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug emits a debug-level message from the logger.
|
||||
func (l *Logger) Debug(tmpl string, args ...interface{}) {
|
||||
if l.Config.Level <= DebugLevel {
|
||||
l.emit(DebugLevel, tmpl, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info emits an informational message.
|
||||
func (l *Logger) Info(tmpl string, args ...interface{}) {
|
||||
if l.Config.Level <= InfoLevel {
|
||||
l.emit(InfoLevel, tmpl, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn emits a warning message.
|
||||
func (l *Logger) Warn(tmpl string, args ...interface{}) {
|
||||
if l.Config.Level <= WarnLevel {
|
||||
l.emit(WarnLevel, tmpl, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error emits an error message.
|
||||
func (l *Logger) Error(tmpl string, args ...interface{}) {
|
||||
if l.Config.Level <= ErrorLevel {
|
||||
l.emit(ErrorLevel, tmpl, args...)
|
||||
}
|
||||
}
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
37
themes.go
Normal file
37
themes.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package golog
|
||||
|
||||
import "github.com/kirsle/golog/ansi"
|
||||
|
||||
// Theme defines the color scheme for a logger. Each log level has two colors:
|
||||
// a primary (for the label itself) and a secondary color. For example, if your
|
||||
// log lines include a date/time this could be colored using the secondary
|
||||
// color.
|
||||
type Theme struct {
|
||||
Debug ThemeColor
|
||||
DebugSecondary ThemeColor
|
||||
Info ThemeColor
|
||||
InfoSecondary ThemeColor
|
||||
Warn ThemeColor
|
||||
WarnSecondary ThemeColor
|
||||
Error ThemeColor
|
||||
ErrorSecondary ThemeColor
|
||||
}
|
||||
|
||||
// ThemeColor defines a color tuple for ANSI (legacy) support and modern
|
||||
// 256-color support.
|
||||
type ThemeColor struct {
|
||||
ANSI string
|
||||
Extended string
|
||||
}
|
||||
|
||||
// DarkTheme is a suitable default theme for dark terminal backgrounds.
|
||||
var DarkTheme = Theme{
|
||||
Debug: ThemeColor{ansi.BrightCyan, HexColor("#FF99FF")},
|
||||
DebugSecondary: ThemeColor{ansi.Cyan, HexColor("#996699")},
|
||||
Info: ThemeColor{ansi.BrightGreen, HexColor("#0099FF")},
|
||||
InfoSecondary: ThemeColor{ansi.Green, HexColor("#006699")},
|
||||
Warn: ThemeColor{ansi.BrightYellow, HexColor("#FF9900")},
|
||||
WarnSecondary: ThemeColor{ansi.Yellow, HexColor("#996600")},
|
||||
Error: ThemeColor{ansi.BrightRed, HexColor("#FF0000")},
|
||||
ErrorSecondary: ThemeColor{ansi.Red, HexColor("#CC0000")},
|
||||
}
|
Loading…
Reference in New Issue
Block a user