pax_global_header00006660000000000000000000000064140571143200014507gustar00rootroot0000000000000052 comment=60e9d1233e2247a28b28e1ea4cd62b6310185ce4 golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/000077500000000000000000000000001405711432000212405ustar00rootroot00000000000000golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/LICENSE000066400000000000000000000020551405711432000222470ustar00rootroot00000000000000MIT License Copyright (c) 2021 Denis Dyakov 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. golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/README.md000066400000000000000000000017201405711432000225170ustar00rootroot00000000000000Golang logger functionality with package-level logging separation to improve application debug process ====================================================================================================== [![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) Functionality of this package is similar to [logrus](https://github.com/sirupsen/logrus) and other popular Golang logging packages, but still the capabilities of this package are not as flexible and advanced as the mentioned popular packages. One of the reasons for creating this library was the ability to log with more verbosity levels. For instance, we have here Fatal, Panic, Error, Warning, **Notify**, Information and Debug levels, while others ordinary do not include Notify verbosity. I'll add more information about the API here later, but for now, folks, that's all now. License ------- Go-logger is licensed under MIT License, like other similar Golang logging libraries. golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/file.go000066400000000000000000000061061405711432000225110ustar00rootroot00000000000000package logger import ( "bytes" "fmt" "io" "os" "path" "path/filepath" "sort" "sync" ) type File struct { sync.RWMutex Path string File *os.File } func (v *File) Flush() error { v.Lock() defer v.Unlock() if v.File != nil { err := v.File.Sync() v.File = nil return err } return nil } func (v *File) Close() error { v.Lock() defer v.Unlock() if v.File != nil { err := v.File.Close() v.File = nil return err } return nil } func (v *File) getRotatedFileList() ([]logFile, error) { var list []logFile err := filepath.Walk(path.Dir(v.Path), func(p string, info os.FileInfo, err error) error { pattern := "*" + path.Base(v.Path) + "*" if ok, err := path.Match(pattern, path.Base(p)); ok && err == nil { i := extractIndex(info) list = append(list, logFile{FileInfo: info, Index: i}) } else if err != nil { return err } return nil }) if err != nil { return nil, err } s := &sortLogFiles{Items: list} sort.Sort(s) return s.Items, nil } func (v *File) doRotate(items []logFile, rotateMaxCount int) error { if len(items) > 0 { // delete last files deleteCount := len(items) - rotateMaxCount + 1 if deleteCount > 0 { for i := 0; i < deleteCount; i++ { err := os.Remove(items[i].FileInfo.Name()) if err != nil { return err } } items = items[deleteCount:] } // change names of rest files baseFilePath := items[len(items)-1].FileInfo.Name() movs := make([]int, len(items)) // 1st round to change names for i, item := range items { movs[i] = i + 100000 err := os.Rename(item.FileInfo.Name(), fmt.Sprintf("%s.%d", baseFilePath, movs[i])) if err != nil { return err } } // 2nd round to change names for i, item := range movs { err := os.Rename(fmt.Sprintf("%s.%d", baseFilePath, item), fmt.Sprintf("%s.%d", baseFilePath, len(items)-i)) if err != nil { return err } } } return nil } func (v *File) rotateFiles(rotateMaxSize int64, rotateMaxCount int) error { fs, err := v.File.Stat() if err != nil { return err } if fs.Size() > rotateMaxSize { if v.File != nil { err := v.File.Close() if err != nil { return err } v.File = nil } list, err := v.getRotatedFileList() if err != nil { return err } if err = v.doRotate(list, rotateMaxCount); err != nil { return err } } return nil } func (v *File) getFile() (*os.File, error) { v.Lock() defer v.Unlock() if v.File == nil { file, err := os.OpenFile(v.Path, os.O_RDWR|os.O_APPEND, 0660) if err != nil { file, err = os.Create(v.Path) if err != nil { return nil, err } } v.File = file } return v.File, nil } func (v *File) writeToFile(msg string, rotateMaxSize int64, rotateMaxCount int) error { file, err := v.getFile() if err != nil { return err } v.Lock() defer v.Unlock() var buf bytes.Buffer buf.WriteString(msg) buf.WriteString(fmt.Sprintln()) if _, err := io.Copy(file, &buf); err != nil { return err } // if err = file.Sync(); err != nil { // return err // } if err := v.rotateFiles(rotateMaxSize, rotateMaxCount); err != nil { return err } return nil } golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/format.go000066400000000000000000000011551405711432000230610ustar00rootroot00000000000000package logger import "os" type LevelLength int const ( LevelShort LevelLength = iota LevelLong ) type FormatOptions struct { TimeFormat string PackageLength int LevelLength LevelLength } func FormatMessage(options FormatOptions, level LogLevel, packageName, msg string, colored bool) string { appName := os.Args[0] out := metaFmtStr(colored, level, options, appName, packageName, msg, "%[1]s [%[3]s] %[4]s %[5]s") return out } func (options FormatOptions) GetLevelStr(level LogLevel) string { if options.LevelLength == LevelLong { return level.LongStr() } else { return level.ShortStr() } } golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/go.mod000066400000000000000000000001411405711432000223420ustar00rootroot00000000000000module github.com/d2r2/go-logger go 1.15 require github.com/davecgh/go-spew v1.1.1 // indirect golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/go.sum000066400000000000000000000002531405711432000223730ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/logger.go000066400000000000000000000127541405711432000230570ustar00rootroot00000000000000package logger import ( "fmt" "io" "log" "os" "path" "path/filepath" "sync" ) type LogLevel int const ( FatalLevel LogLevel = iota PanicLevel ErrorLevel WarnLevel NotifyLevel InfoLevel DebugLevel ) func (v LogLevel) String() string { switch v { case FatalLevel: return "Fatal" case PanicLevel: return "Panic" case ErrorLevel: return "Error" case WarnLevel: return "Warning" case NotifyLevel: return "Notice" case InfoLevel: return "Information" case DebugLevel: return "Debug" default: return "" } } func (v LogLevel) LongStr() string { return v.String() } func (v LogLevel) ShortStr() string { switch v { case FatalLevel: return "Fatal" case PanicLevel: return "Panic" case ErrorLevel: return "Error" case WarnLevel: return "Warn" case NotifyLevel: return "Notice" case InfoLevel: return "Info" case DebugLevel: return "Debug" default: return "" } } type Log struct { log *log.Logger colored bool level LogLevel } func NewLog(log *log.Logger, colored bool, level LogLevel) *Log { v := &Log{log: log, colored: colored, level: level} return v } type Logger struct { sync.RWMutex logs []*Log packages []*Package options FormatOptions logFile *File rotateMaxSize int64 rotateMaxCount int enableSyslog bool } func NewLogger() *Logger { stdout := NewLog(log.New(os.Stdout, "", 0), true, DebugLevel) logs := []*Log{stdout} options := FormatOptions{TimeFormat: "2006-01-02T15:04:05.000", LevelLength: LevelShort, PackageLength: 8} l := &Logger{ logs: logs, options: options, rotateMaxSize: 1024 * 1024 * 512, rotateMaxCount: 3, } return l } func (v *Logger) Close() error { v.Lock() defer v.Unlock() for _, pack := range v.packages { pack.Close() } v.packages = nil if v.logFile != nil { v.logFile.Close() } return nil } func (v *Logger) SetRotateParams(rotateMaxSize int64, rotateMaxCount int) { v.Lock() defer v.Unlock() v.rotateMaxSize = rotateMaxSize v.rotateMaxCount = rotateMaxCount } func (v *Logger) GetRotateMaxSize() int64 { v.Lock() defer v.Unlock() return v.rotateMaxSize } func (v *Logger) GetRotateMaxCount() int { v.Lock() defer v.Unlock() return v.rotateMaxCount } /* func (v *Logger) SetApplicationName(appName string) { v.Lock() defer v.Unlock() v.appName = appName } func (v *Logger) GetApplicationName() string { v.RLock() defer v.RUnlock() return v.appName } */ func (v *Logger) EnableSyslog(enable bool) { v.Lock() defer v.Unlock() v.enableSyslog = enable } func (v *Logger) GetSyslogEnabled() bool { v.RLock() defer v.RUnlock() return v.enableSyslog } func (v *Logger) SetFormatOptions(options FormatOptions) { v.Lock() defer v.Unlock() v.options = options } func (v *Logger) GetFormatOptions() FormatOptions { v.RLock() defer v.RUnlock() return v.options } func (v *Logger) SetLogFileName(logFilePath string) error { if path.Ext(logFilePath) == "" { logFilePath += ".log" } fp, err := filepath.Abs(logFilePath) if err != nil { return err } v.Lock() defer v.Unlock() lf := &File{Path: fp} v.logFile = lf return nil } func (v *Logger) GetLogFileInfo() *File { v.RLock() defer v.RUnlock() return v.logFile } func (v *Logger) NewPackageLogger(packageName string, level LogLevel) PackageLog { v.Lock() defer v.Unlock() p := &Package{parent: v, packageName: packageName, level: level} v.packages = append(v.packages, p) return p } func (v *Logger) ChangePackageLogLevel(packageName string, level LogLevel) error { var p *Package for _, item := range v.packages { if item.packageName == packageName { p = item break } } if p != nil { p.SetLogLevel(level) } else { err := fmt.Errorf("Package log %q is not found", packageName) return err } return nil } func (v *Logger) AddCustomLog(writer io.Writer, colored bool, level LogLevel) { v.Lock() defer v.Unlock() log := NewLog(log.New(writer, "", 0), colored, level) v.logs = append(v.logs, log) } func (v *Logger) getLogs() []*Log { v.RLock() defer v.RUnlock() lst := []*Log{} for _, item := range v.logs { lst = append(lst, item) } return lst } var ( globalLock sync.RWMutex lgr *Logger ) func SetFormatOptions(format FormatOptions) { globalLock.RLock() defer globalLock.RUnlock() lgr.SetFormatOptions(format) } func SetRotateParams(rotateMaxSize int64, rotateMaxCount int) { globalLock.RLock() defer globalLock.RUnlock() lgr.SetRotateParams(rotateMaxSize, rotateMaxCount) } func NewPackageLogger(module string, level LogLevel) PackageLog { globalLock.RLock() defer globalLock.RUnlock() return lgr.NewPackageLogger(module, level) } func ChangePackageLogLevel(packageName string, level LogLevel) error { globalLock.RLock() defer globalLock.RUnlock() return lgr.ChangePackageLogLevel(packageName, level) } func SetLogFileName(logFilePath string) error { globalLock.RLock() defer globalLock.RUnlock() return lgr.SetLogFileName(logFilePath) } /* func SetApplicationName(appName string) { globalLock.RLock() defer globalLock.RUnlock() lgr.SetApplicationName(appName) } */ func EnableSyslog(enable bool) { globalLock.RLock() defer globalLock.RUnlock() lgr.EnableSyslog(enable) } func AddCustomLog(writer io.Writer, colored bool, level LogLevel) { globalLock.RLock() defer globalLock.RUnlock() lgr.AddCustomLog(writer, colored, level) } func FinalizeLogger() error { var err error if lgr != nil { err = lgr.Close() } globalLock.Lock() defer globalLock.Unlock() lgr = nil return err } func init() { lgr = NewLogger() } golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/package.go000066400000000000000000000137671405711432000232000ustar00rootroot00000000000000package logger import ( "fmt" "log/syslog" "os" "sync" "github.com/davecgh/go-spew/spew" ) type PackageLog interface { Printf(level LogLevel, format string, args ...interface{}) Print(level LogLevel, args ...interface{}) Debugf(format string, args ...interface{}) Debug(args ...interface{}) Infof(format string, args ...interface{}) Info(args ...interface{}) Notifyf(format string, args ...interface{}) Notify(args ...interface{}) Warningf(format string, args ...interface{}) Warnf(format string, args ...interface{}) Warning(args ...interface{}) Warn(args ...interface{}) Errorf(format string, args ...interface{}) Error(args ...interface{}) Panicf(format string, args ...interface{}) Panic(args ...interface{}) Fatalf(format string, args ...interface{}) Fatal(args ...interface{}) } type Package struct { sync.RWMutex parent *Logger packageName string level LogLevel syslog *syslog.Writer } // Static cast to verify that object implement interface. var _ PackageLog = &Package{} func (v *Package) Close() error { v.Lock() defer v.Unlock() if v.syslog != nil { err := v.syslog.Close() v.syslog = nil if err != nil { return err } } return nil } func (v *Package) SetLogLevel(level LogLevel) { v.Lock() defer v.Unlock() v.level = level } func (v *Package) GetLogLevel() LogLevel { v.RLock() defer v.RUnlock() return v.level } func (v *Package) getSyslog(level LogLevel, options FormatOptions, appName string) (*syslog.Writer, error) { v.Lock() defer v.Unlock() if v.syslog == nil { tag := metaFmtStr(false, level, options, appName, v.packageName, "", "%[2]s-%[3]s") sl, err := syslog.New(syslog.LOG_DEBUG, tag) if err != nil { err = spew.Errorf("Failed to connect to syslog: %v\n", err) return nil, err } v.syslog = sl } return v.syslog, nil } func (v *Package) writeToSyslog(options FormatOptions, level LogLevel, appName string, msg string) error { sl, err := v.getSyslog(level, options, appName) if err != nil { return err } switch level { case DebugLevel: return sl.Debug(msg) case InfoLevel: return sl.Info(msg) case NotifyLevel: return sl.Notice(msg) case WarnLevel: return sl.Warning(msg) case ErrorLevel: return sl.Err(msg) case PanicLevel: return sl.Crit(msg) case FatalLevel: return sl.Emerg(msg) default: return sl.Debug(msg) } } type printLog func(log *Log, msg interface{}) type getMessage func(colored bool) interface{} func printLogs(logs []*Log, level LogLevel, prnt printLog, getMsg getMessage) { // Console and custom logs output for _, log := range logs { if log.level >= level { prnt(log, getMsg(log.colored)) } } } func (v *Package) print(level LogLevel, msg string) { lvl := v.GetLogLevel() if lvl >= level { appName := getApplicationName() logs := v.parent.getLogs() options := v.parent.GetFormatOptions() out1 := FormatMessage(options, level, v.packageName, msg, false) // File output if lf := v.parent.GetLogFileInfo(); lf != nil { rotateMaxSize := v.parent.GetRotateMaxSize() rotateMaxCount := v.parent.GetRotateMaxCount() if err := lf.writeToFile(out1, rotateMaxSize, rotateMaxCount); err != nil { err = spew.Errorf("Failed to report syslog message %q: %v\n", out1, err) printLogs(logs, FatalLevel, func(log *Log, msg interface{}) { log.log.Fatal(msg) }, func(colored bool) interface{} { return err }) } } // Syslog output if v.parent.GetSyslogEnabled() { if err := v.writeToSyslog(options, level, appName, msg); err != nil { err = spew.Errorf("Failed to report syslog message %q: %v\n", msg, err) printLogs(logs, FatalLevel, func(log *Log, msg interface{}) { log.log.Fatal(msg) }, func(colored bool) interface{} { return err }) } } // Console and custom logs output outColored1 := FormatMessage(options, level, v.packageName, msg, true) printLogs(logs, level, func(log *Log, msg interface{}) { log.log.Print(msg) }, func(colored bool) interface{} { if colored { return outColored1 + fmt.Sprintln() } else { return out1 + fmt.Sprintln() } }) // Check critical events if level == PanicLevel { panic(out1) } else if level == FatalLevel { os.Exit(1) } } } func (v *Package) Printf(level LogLevel, format string, args ...interface{}) { lvl := v.GetLogLevel() if lvl >= level { msg := spew.Sprintf(format, args...) v.print(level, msg) } } func (v *Package) Print(level LogLevel, args ...interface{}) { lvl := v.GetLogLevel() if lvl >= level { msg := fmt.Sprint(args...) v.print(level, msg) } } func (v *Package) Debugf(format string, args ...interface{}) { v.Printf(DebugLevel, format, args...) } func (v *Package) Debug(args ...interface{}) { v.Print(DebugLevel, args...) } func (v *Package) Infof(format string, args ...interface{}) { v.Printf(InfoLevel, format, args...) } func (v *Package) Info(args ...interface{}) { v.Print(InfoLevel, args...) } func (v *Package) Notifyf(format string, args ...interface{}) { v.Printf(NotifyLevel, format, args...) } func (v *Package) Notify(args ...interface{}) { v.Print(NotifyLevel, args...) } func (v *Package) Warningf(format string, args ...interface{}) { v.Printf(WarnLevel, format, args...) } func (v *Package) Warnf(format string, args ...interface{}) { v.Printf(WarnLevel, format, args...) } func (v *Package) Warning(args ...interface{}) { v.Print(WarnLevel, args...) } func (v *Package) Warn(args ...interface{}) { v.Print(WarnLevel, args...) } func (v *Package) Errorf(format string, args ...interface{}) { v.Printf(ErrorLevel, format, args...) } func (v *Package) Error(args ...interface{}) { v.Print(ErrorLevel, args...) } func (v *Package) Panicf(format string, args ...interface{}) { v.Printf(PanicLevel, format, args...) } func (v *Package) Panic(args ...interface{}) { v.Print(PanicLevel, args...) } func (v *Package) Fatalf(format string, args ...interface{}) { v.Printf(FatalLevel, format, args...) } func (v *Package) Fatal(args ...interface{}) { v.Print(FatalLevel, args...) } golang-github-d2r2-go-logger-0.0~git20210606.60e9d12/utils.go000066400000000000000000000053741405711432000227400ustar00rootroot00000000000000package logger import ( "fmt" "os" "path" "regexp" "strconv" "strings" "time" ) type logFile struct { FileInfo os.FileInfo Index int } type sortLogFiles struct { Items []logFile } func (sf *sortLogFiles) Len() int { return len(sf.Items) } func (sf *sortLogFiles) Less(i, j int) bool { return sf.Items[j].Index < sf.Items[i].Index } func (sf *sortLogFiles) Swap(i, j int) { item := sf.Items[i] sf.Items[i] = sf.Items[j] sf.Items[j] = item } func findStringSubmatchIndexes(r *regexp.Regexp, s string) map[string][2]int { captures := make(map[string][2]int) ind := r.FindStringSubmatchIndex(s) names := r.SubexpNames() for i, name := range names { if name != "" && i < len(ind)/2 { if ind[i*2] != -1 && ind[i*2+1] != -1 { captures[name] = [2]int{ind[i*2], ind[i*2+1]} } } } return captures } func extractIndex(item os.FileInfo) int { r := regexp.MustCompile(`.+\.log(\.(?P\d+))?`) fileName := path.Base(item.Name()) m := findStringSubmatchIndexes(r, fileName) if v, ok := m["index"]; ok { i, _ := strconv.Atoi(fileName[v[0]:v[1]]) return i } else { return 0 } } const ( nocolor = 0 red = 31 green = 32 yellow = 33 blue = 34 gray = 37 ) type IndentKind int const ( LeftIndent = iota CenterIndent RightIndent ) func cutOrIndentText(text string, length int, indent IndentKind) string { if length < 0 { return text } else if len(text) > length { text = text[:length] } else { switch indent { case LeftIndent: text = text + strings.Repeat(" ", length-len(text)) case RightIndent: text = strings.Repeat(" ", length-len(text)) + text case CenterIndent: text = strings.Repeat(" ", (length-len(text))/2) + text + strings.Repeat(" ", length-len(text)-(length-len(text))/2) } } return text } func metaFmtStr(colored bool, level LogLevel, options FormatOptions, appName string, packageName string, message string, format string) string { var colorPfx, colorSfx string if colored { var levelColor int switch level { case DebugLevel: levelColor = gray case InfoLevel: levelColor = blue case NotifyLevel, WarnLevel: levelColor = yellow case ErrorLevel, PanicLevel, FatalLevel: levelColor = red default: levelColor = nocolor } colorPfx = "\x1b[" + strconv.Itoa(levelColor) + "m" colorSfx = "\x1b[0m" } arg1 := time.Now().Format(options.TimeFormat) arg2 := appName arg3 := cutOrIndentText(packageName, options.PackageLength, RightIndent) lvlStr := options.GetLevelStr(level) lvlLen := len([]rune(lvlStr)) arg4 := colorPfx + cutOrIndentText(strings.ToUpper(lvlStr), lvlLen, LeftIndent) + colorSfx arg5 := message out := fmt.Sprintf(format, arg1, arg2, arg3, arg4, arg5) return out } func getApplicationName() string { appName := os.Args[0] return appName }