Commit dd9ff9ae authored by “李磊”'s avatar “李磊”
Browse files

Initial commit

parents
# public
公共基础库
module linkfog.com/public
go 1.23.2
require golang.org/x/sys v0.26.0
package bufpool
import (
"bytes"
"sync"
)
// BufPool maintains a buffer pool.
type BufPool struct {
p sync.Pool
}
// New creates a new BufPool.
func New() *BufPool {
return &BufPool{
p: sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
},
}
}
// Put resets the buffer and puts it back to the pool.
func (p *BufPool) Put(b *bytes.Buffer) {
b.Reset()
p.p.Put(b)
}
// Get returns an empty buffer from the pool. It creates a new buffer, if there
// is no bytes.Buffer available in the pool.
func (p *BufPool) Get() *bytes.Buffer {
return p.p.Get().(*bytes.Buffer)
}
//go:build !windows && !darwin
// +build !windows,!darwin
package fadvise
import (
"os"
"golang.org/x/sys/unix"
)
// 该库仅为sys/unix库的封装,调试环境兼容windows和mac操作系统
// 需要结合启动参数调用,不能直接调用
func Fadvise(f *os.File) error {
if err := unix.Fadvise(int(f.Fd()), 0, 0, unix.FADV_DONTNEED); err != nil {
return err
}
return nil
}
//go:build windows || darwin
// +build windows darwin
package fadvise
import (
"os"
)
// 调试环境兼容windows和mac操作系统
func Fadvise(f *os.File) error {
return nil
}
package fadvise
import (
"os"
"linkfog.com/public/option"
)
// 可以实现通过option.Fadvise开关控制所有fadvise的启停
func Switch(f *os.File) (err error) {
if option.Fadvise {
if err = Fadvise(f); err != nil {
return err
}
}
return nil
}
package l
import "testing"
func BenchmarkLogMsg(b *testing.B) {
err := SetConfig(Config{
Path: "/tmp/dlog.log",
MaxSize: 100000,
Quiet: true,
})
if err != nil {
b.Error(err)
}
for i := 0; i < b.N; i++ {
Info("hello logxxxxxxxlogxxxxxxxlogxxxxxxxlogxxxxxxxlogxxxxxxx")
}
}
package l
// 模块
const (
Scanner ErrCode = "10" // 扫描模块
NetTopology ErrCode = "11" // 网络雷达
AssetCenter ErrCode = "12" // 资产中心
AlarmResponse ErrCode = "13" // 告警响应
SecurityCompliance ErrCode = "14" // 安全合规
IaCSecurity ErrCode = "15" // IaC安全
)
const (
Timeout ErrCode = "001" // 各种超时场景
ImageFormat ErrCode = "002"
InitRule ErrCode = "003" // 可用于镜像基线初始化规则
ImageLayer ErrCode = "004"
JsonUnmarshal ErrCode = "005"
File ErrCode = "006" // 文件及目录操作错误
FeatureRpm ErrCode = "007"
JsonMarshal ErrCode = "008"
BinaryFile ErrCode = "009"
BackendModNotRunning ErrCode = "010"
Grpc ErrCode = "011"
RunTime ErrCode = "012"
)
package l
import (
"errors"
"fmt"
)
/*
本包ERROR函数使用
1.在首位传入错误码,函数自动使用首位的错误码
2.在使用WrapError,WrapErrorf,GetError,GetError这些函数会返回一个携带错误码的error(ErrorCode)
3.可以不在函数中传入错误码,可传入一个携带错误码的error(ErrorCode),以下函数会自动解析传入函数中的error中的错误码
4.如果以下函数不传入错误码,也不传入携带错误码的error(ErrorCode),会正常打印普通error信息
*/
func Error(msgs ...interface{}) {
if logger.logLevel > LevelError {
return
}
code, firstArg := getErrCode(msgs...)
if code != "" {
msgs = append(msgs, code)
}
if firstArg && len(msgs) >= 2 {
logMsg("ERROR", msgs[1:]...)
return
}
logMsg("ERROR", msgs...)
}
func Errorf(msgs ...interface{}) {
if logger.logLevel > LevelError {
return
}
code, firstArg := getErrCode(msgs...)
switch len(msgs) {
case 0:
logMsg("ERROR", msgs...)
case 1:
if code != "" && !firstArg {
msgs = append(msgs, code)
}
logMsg("ERROR", msgs...)
case 2:
if firstArg {
msgs = append(msgs, code)
logMsg("ERROR", msgs[1:]...)
return
}
if _, ok := msgs[0].(string); ok {
logMsg("ERROR", fmt.Sprintf(msgs[0].(string), msgs[1:]...), code)
}
default:
if firstArg {
if _, ok := msgs[1].(string); ok {
logMsg("ERROR", fmt.Sprintf(msgs[1].(string), msgs[2:]...), code)
}
return
}
if _, ok := msgs[0].(string); ok {
logMsg("ERROR", fmt.Sprintf(msgs[0].(string), msgs[1:]...), code)
}
}
}
func WrapError(msgs ...interface{}) error {
code, firstArg := getErrCode(msgs...)
fmtStr := getFmtMsg(msgs...)
if firstArg {
fmtStr = getFmtMsg(msgs[1:]...)
}
if logger.logLevel <= LevelError {
logMsg("ERROR", fmtStr, code)
}
return ErrorCode{
code,
errors.New(fmtStr),
}
}
func WrapErrorf(msgs ...interface{}) error {
code, firstArg := getErrCode(msgs...)
var fmtStr string
switch len(msgs) {
case 0:
fmtStr = ""
case 1:
if firstArg {
fmtStr = ""
break
}
fmtStr = fmt.Sprintf("%v", msgs[0])
case 2:
if firstArg {
fmtStr = fmt.Sprintf("%v", msgs[1])
break
}
if _, ok := msgs[0].(string); ok {
fmtStr = fmt.Sprintf(msgs[0].(string), msgs[1:]...)
}
default:
if firstArg {
if _, ok := msgs[1].(string); ok {
fmtStr = fmt.Sprintf(msgs[1].(string), msgs[2:]...)
}
break
}
if _, ok := msgs[0].(string); ok {
fmtStr = fmt.Sprintf(msgs[0].(string), msgs[1:]...)
}
}
if logger.logLevel <= LevelError {
logMsg("ERROR", fmtStr, code)
}
return ErrorCode{
code,
errors.New(fmtStr),
}
}
func GetError(msgs ...interface{}) error {
code, firstArg := getErrCode(msgs...)
if code == "" {
return wrapError(msgs...)
}
fmtStr := getFmtMsg(msgs...)
if firstArg {
fmtStr = getFmtMsg(msgs[1:]...)
}
return ErrorCode{
code,
errors.New(fmtStr),
}
}
func GetErrorf(msgs ...interface{}) error {
code, firstArg := getErrCode(msgs...)
var fmtStr string
switch len(msgs) {
case 0:
fmtStr = ""
case 1:
if firstArg {
fmtStr = ""
break
}
fmtStr = fmt.Sprintf("%v", msgs[0])
case 2:
if firstArg {
fmtStr = fmt.Sprintf("%v", msgs[1])
break
}
if _, ok := msgs[0].(string); ok {
fmtStr = fmt.Sprintf(msgs[0].(string), msgs[1:]...)
}
default:
if firstArg {
if _, ok := msgs[1].(string); ok {
fmtStr = fmt.Sprintf(msgs[1].(string), msgs[2:]...)
}
break
}
if _, ok := msgs[0].(string); ok {
fmtStr = fmt.Sprintf(msgs[0].(string), msgs[1:]...)
}
}
return ErrorCode{
code,
errors.New(fmtStr),
}
}
// 获取错误码,优先获取手动输入的错误码,其次获取解析error中的错误码
func getErrCode(msgs ...interface{}) (errCode ErrCode, firstArg bool) {
for i, v := range msgs {
if code, ok := v.(ErrCode); ok && i == 0 {
errCode = code
firstArg = true
break
}
if _, ok := v.(ErrorCode); !ok {
continue
}
errCode = v.(ErrorCode).Code
}
return errCode, firstArg
}
func getFmtMsg(msgs ...interface{}) string {
fmtStr := ""
for i, msg := range msgs {
if i == 0 {
fmtStr = fmt.Sprintf("%+v", msg)
} else {
fmtStr = fmt.Sprintf("%s %+v", fmtStr, msg)
}
}
return fmtStr
}
package l
import "fmt"
var color colors
type colors struct{}
// can't use ``
const ansi string = "\x1b[%dm%s\x1b[0m"
const (
color_red = uint8(iota + 91)
color_green
color_yellow
color_blue
)
func (c *colors) Red(s string) string {
return fmt.Sprintf(ansi, color_red, s)
}
func (c *colors) Yellow(s string) string {
return fmt.Sprintf(ansi, color_yellow, s)
}
func (c *colors) Blue(s string) string {
return fmt.Sprintf(ansi, color_blue, s)
}
func (c *colors) Green(s string) string {
return fmt.Sprintf(ansi, color_green, s)
}
func (c *colors) autoPaint(level string) string {
colorLevel := ""
switch level {
case "INFO":
colorLevel = c.Blue(level)
case "ERROR":
colorLevel = c.Red(level)
case "WARN":
colorLevel = c.Yellow(level)
case "DEBUG":
colorLevel = c.Green(level)
default:
colorLevel = level
}
return colorLevel
}
package l
import (
"testing"
)
func TestColor(t *testing.T) {
t.Log(color.Red("hello"))
}
package l
type ErrCode string
type ErrorCode struct {
Code ErrCode
error
}
package l
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strconv"
"time"
"linkfog.com/public/lib/bufpool"
)
const (
DefaultStdoutLimit uint64 = 2 * 1024 * 1024 * 1024
)
var (
logger Logger
logBuf = bufpool.New()
)
type Logger struct {
r *Rotator
logLevel LogLevel
verboseLevel int
quiet bool
stdoutLimit uint64
stdoutBytes uint64
}
type LogLevel int
// var prefixPath string
// func init() {
// _, prefixPath, _, _ = runtime.Caller(0)
// for i := 0; i < 4; i++ {
// prefixPath = filepath.Dir(prefixPath)
// }
// prefixPath = prefixPath + "/"
// }
const (
LevelDebug LogLevel = iota
LevelInfo
LevelWarn
LevelError
LevelFatal
LevelNone
)
type Config struct {
Path string
MaxSize int64
MaxRolls int
LogLevel LogLevel
VerboseLevel int
Quiet bool
StdoutLimit uint64
}
// SetConfig init logger
func SetConfig(c Config) (err error) {
err = os.MkdirAll(filepath.Dir(c.Path), 0700)
if err != nil {
return
}
logger.logLevel = c.LogLevel
logger.verboseLevel = c.VerboseLevel
logger.quiet = c.Quiet
if c.StdoutLimit != 0 {
logger.stdoutLimit = c.StdoutLimit
} else {
logger.stdoutLimit = DefaultStdoutLimit
}
logger.r, err = New(c.Path, c.MaxSize, false, c.MaxRolls)
return
}
// Close close logger handle when exit
func Close() {
if logger.r != nil {
logger.r.Close()
}
}
func logMsg(level string, msgs ...interface{}) {
_, file, line, ok := runtime.Caller(2)
if !ok {
file = "none"
line = 0
}
buf := logBuf.Get()
defer logBuf.Put(buf)
buf.WriteString(time.Now().Format("2006-01-02 15:04:05"))
buf.WriteString(" [")
if logger.r != nil {
buf.WriteString(level)
} else {
buf.WriteString(color.autoPaint(level))
}
buf.WriteString("] ")
var isCode bool
if len(msgs) > 0 {
if code, ok := msgs[len(msgs)-1].(ErrCode); ok && code != "" {
buf.WriteString("[")
buf.WriteString(string(code))
buf.WriteString("] ")
isCode = true
}
}
buf.WriteString(fmt.Sprintf("%s:%d", filepath.Base(file), line))
for i, msg := range msgs {
if isCode && i == (len(msgs)-1) {
continue
}
buf.WriteString(fmt.Sprintf(" %+v", msg))
}
buf.WriteString("\n")
if !isPrint(level, string(buf.Bytes())) {
return
}
if logger.r != nil {
logger.r.Write(buf.Bytes())
}
// add some color when print console
if !logger.quiet {
n, _ := io.Copy(os.Stdout, buf)
logger.stdoutBytes += uint64(n)
if logger.stdoutLimit != 0 && logger.stdoutBytes >= logger.stdoutLimit {
logger.quiet = true
notice := fmt.Sprintf("NOTICE: log stdout bytes reach limit %d\n", logger.stdoutLimit)
if logger.r != nil {
logger.r.Write([]byte(notice))
}
os.Stdout.WriteString(notice)
}
}
}
func Info(msgs ...interface{}) {
if logger.logLevel > LevelInfo {
return
}
logMsg("INFO", msgs...)
}
func Infof(msgs ...interface{}) {
if logger.logLevel > LevelInfo {
return
}
if len(msgs) < 2 {
logMsg("INFO", msgs...)
return
}
logMsg("INFO", fmt.Sprintf(msgs[0].(string), msgs[1:]...))
}
func Debug(msgs ...interface{}) {
if logger.logLevel > LevelDebug {
return
}
logMsg("DEBUG", msgs...)
}
func Debugf(msgs ...interface{}) {
if logger.logLevel > LevelDebug {
return
}
if len(msgs) < 2 {
logMsg("DEBUG", msgs...)
return
}
logMsg("DEBUG", fmt.Sprintf(msgs[0].(string), msgs[1:]...))
}
func Debugj(msgs ...interface{}) {
if logger.logLevel > LevelDebug {
return
}
jsonMsg := []interface{}{}
for _, msg := range msgs {
b, err := json.MarshalIndent(msg, "", " ")
if err != nil {
jsonMsg = append(jsonMsg, msg)
} else {
jsonMsg = append(jsonMsg, string(b))
}
}
logMsg("DEBUG", jsonMsg...)
}
func Debugs(msgs ...interface{}) {
if logger.logLevel > LevelDebug {
return
}
logMsgWithStack("DEBUG", msgs...)
}
func wrapError(msgs ...interface{}) error {
fmtStr := getFmtMsg(msgs...)
return errors.New(fmtStr)
}
func WrapWarn(msgs ...interface{}) error {
fmtStr := ""
for i, msg := range msgs {
if i == 0 {
fmtStr = fmt.Sprintf("%+v", msg)
} else {
fmtStr = fmt.Sprintf("%s %+v", fmtStr, msg)
}
}
if logger.logLevel <= LevelWarn {
logMsg("WARN", fmtStr)
}
return errors.New(fmtStr)
}
func Warn(msgs ...interface{}) {
if logger.logLevel > LevelWarn {
return
}
logMsg("WARN", msgs...)
}
func Warnf(msgs ...interface{}) {
if logger.logLevel > LevelWarn {
return
}
if len(msgs) < 2 {
logMsg("WARN", msgs...)
return
}
logMsg("WARN", fmt.Sprintf(msgs[0].(string), msgs[1:]...))
}
func Fatal(msgs ...interface{}) {
if logger.logLevel > LevelFatal {
return
}
logMsg("FATAL", msgs...)
os.Exit(1)
}
func Fatalf(msgs ...interface{}) {
if logger.logLevel > LevelFatal {
return
}
if len(msgs) < 2 {
logMsg("FATAL", msgs...)
os.Exit(1)
}
logMsg("FATAL", fmt.Sprintf(msgs[0].(string), msgs[1:]...))
os.Exit(1)
}
func logMsgWithStack(level string, msgs ...interface{}) {
buf := logBuf.Get()
defer logBuf.Put(buf)
buf.WriteString(time.Now().Format("2006-01-02 15:04:05"))
buf.WriteString(" [")
if logger.r != nil {
buf.WriteString(level)
} else {
buf.WriteString(color.autoPaint(level))
}
buf.WriteString("] ")
for i := 0; i < 10; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
if i != 0 {
buf.WriteString("<-")
}
buf.WriteString(file)
buf.WriteString(":")
buf.WriteString(strconv.Itoa(line))
}
for _, msg := range msgs {
buf.WriteString(fmt.Sprintf(" %+v", msg))
}
buf.WriteString("\n")
if logger.r != nil {
logger.r.Write(buf.Bytes())
}
if !logger.quiet {
n, _ := io.Copy(os.Stdout, buf)
logger.stdoutBytes += uint64(n)
if logger.stdoutLimit != 0 && logger.stdoutBytes >= logger.stdoutLimit {
logger.quiet = true
notice := fmt.Sprintf("NOTICE: log stdout bytes reach limit %d\n", logger.stdoutLimit)
if logger.r != nil {
logger.r.Write([]byte(notice))
}
os.Stdout.WriteString(notice)
}
}
}
// func relativePath(path string) string {
// return strings.TrimPrefix(filepath.ToSlash(path), prefixPath)
// }
func EnableDebug() {
logger.logLevel = LevelDebug
}
func DisableDebug() {
logger.logLevel = LevelInfo
}
func CheckEnableDebug() bool {
if logger.logLevel == LevelDebug {
return true
}
return false
}
func SetVerboseLevel(verboseLevel int) {
logger.verboseLevel = verboseLevel
}
func GetVerboseLevel() int {
return logger.verboseLevel
}
func EnableQuiet() {
logger.quiet = true
}
func DisableQuiet() {
logger.quiet = false
}
func IsQuiet() bool {
return logger.quiet
}
func GetStdoutLimit() uint64 {
return logger.stdoutLimit
}
func SetStdoutLimit(limit uint64) {
logger.stdoutLimit = limit
}
func GetStdoutBytes() uint64 {
return logger.stdoutBytes
}
package l
import (
"fmt"
"testing"
)
func TestLog(t *testing.T) {
EnableDebug()
Debug("Debug", "log")
Debugf("Debugf %s", "log")
Debugj("Debugj", map[string]interface{}{"name": "zhangsan", "age": 10})
Debugs("Debugs")
Info("Info", "log")
Infof("Infof %s", "log")
Warn("Warn", "log")
Warnf("Warnf %s", "log")
// 正常err测试
Error()
Errorf()
WrapError()
WrapErrorf()
fmt.Println(GetError())
fmt.Println(GetErrorf())
Error("Error", "log")
Errorf("Errorf %s", "log")
fmt.Println(GetError("Error", "log"))
fmt.Println(GetErrorf("Errorf %s", "log"))
err0 := WrapError("Error", "log")
fmt.Println(err0)
err1 := WrapErrorf("Errorf %s", "log")
fmt.Println(err1)
// 测试只传入code
Error(BackendModNotRunning)
Errorf(BackendModNotRunning)
fmt.Println(GetError(BackendModNotRunning))
fmt.Println(GetErrorf(BackendModNotRunning))
err2 := WrapError(BackendModNotRunning)
fmt.Println(err2)
err3 := WrapErrorf(BackendModNotRunning)
fmt.Println(err3)
// 测试只传入code及一个参数
Error(BackendModNotRunning, "test one code one error")
Errorf(BackendModNotRunning, "test one code one error")
Errorf(BackendModNotRunning, "error %s", "log")
fmt.Println(GetError(BackendModNotRunning, "test one code one error"))
fmt.Println(GetErrorf(BackendModNotRunning, "test one code one error"))
fmt.Println(GetErrorf(BackendModNotRunning, "error %s", "log"))
err4 := WrapError(BackendModNotRunning, "test one code one error")
fmt.Println(err4)
err5 := WrapErrorf(BackendModNotRunning, "test one code one error")
fmt.Println(err5)
err6 := WrapErrorf(BackendModNotRunning, "error %s", "log")
fmt.Println(err6)
// 测试获取errcode
Error(err4)
Errorf(err4)
fmt.Println(GetError(err4))
fmt.Println(GetErrorf(err4))
err7 := WrapError(err4)
fmt.Println(err7)
err8 := WrapErrorf(err4)
fmt.Println(err8)
// Fatal("Fatal", "log")
// Fatalf("Fatalf %s", "log")
}
func TestStdoutBytesAndStdoutLimit(t *testing.T) {
t.Logf("before write bytes: %d", GetStdoutBytes())
SetStdoutLimit(10)
t.Logf("stdout limit: %d", GetStdoutLimit())
Info("hello")
Info("not print")
t.Logf("after write bytes: %d", GetStdoutBytes())
}
package l
import (
"strings"
"linkfog.com/public/option"
)
func isPrint(level, msg string) bool {
if option.LogContain.Err && level == "ERROR" {
return true
}
if option.LogContain.Warn && level == "WARN" {
return true
}
if option.LogContain.AllNot.Enabled() && isContainAllNot(msg) {
return false
}
if option.LogContain.AnyNot.Enabled() && isContainAnyNot(msg) {
return false
}
if option.LogContain.All.Enabled() && !isContainAll(msg) {
return false
}
if option.LogContain.Any.Enabled() && !isContainAny(msg) {
return false
}
return true
}
// all contain
func isContainAll(msg string) bool {
for _, keyword := range option.LogContain.All {
if !strings.Contains(msg, keyword) {
return false
}
}
return true
}
// any contain
func isContainAny(msg string) bool {
for _, keyword := range option.LogContain.Any {
if strings.Contains(msg, keyword) {
return true
}
}
return false
}
// all not contain
func isContainAllNot(msg string) bool {
for _, keyword := range option.LogContain.AllNot {
if strings.Contains(msg, keyword) {
return false
}
}
return true
}
// any not contain
func isContainAnyNot(msg string) bool {
for _, keyword := range option.LogContain.AnyNot {
if strings.Contains(msg, keyword) {
return true
}
}
return false
}
// Copyright (c) 2017, moshee
// Copyright (c) 2017, Josh Rickmar <jrick@devio.us>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package rotator implements a simple logfile rotator. Logs are read from an
// io.Reader and are written to a file until they reach a specified size. The
// log is then gzipped to another file and truncated.
package l
import (
"bufio"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"linkfog.com/public/lib/fadvise"
)
// nl is a byte slice containing a newline byte. It is used to avoid creating
// additional allocations when writing newlines to the log file.
var nl = []byte{'\n'}
// A Rotator writes input to a file, splitting it up into gzipped chunks once
// the filesize reaches a certain threshold.
type Rotator struct {
size int64
threshold int64
maxRolls int
filename string
out *os.File
tee bool
wg sync.WaitGroup
sync.Mutex
}
// New returns a new Rotator. The rotator can be used either by reading input
// from an io.Reader by calling Run, or writing directly to the Rotator with
// Write.
func New(filename string, thresholdKB int64, tee bool, maxRolls int) (*Rotator, error) {
err := os.MkdirAll(filepath.Dir(filename), 0700)
if err != nil {
return nil, err
}
f, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0600)
if err != nil {
return nil, err
}
stat, err := f.Stat()
if err != nil {
return nil, err
}
return &Rotator{
size: stat.Size(),
threshold: 1000 * thresholdKB,
maxRolls: maxRolls,
filename: filename,
out: f,
tee: tee,
}, nil
}
// Run begins reading lines from the reader and rotating logs as necessary. Run
// should not be called concurrently with Write.
//
// Prefer to use Rotator as a writer instead to avoid unnecessary scanning of
// input, as this job is better handled using io.Pipe.
func (r *Rotator) Run(reader io.Reader) error {
in := bufio.NewReader(reader)
// Rotate file immediately if it is already over the size limit.
if r.size >= r.threshold {
if err := r.rotate(); err != nil {
return err
}
r.size = 0
}
for {
line, isPrefix, err := in.ReadLine()
if err != nil {
return err
}
n, _ := r.out.Write(line)
r.size += int64(n)
if r.tee {
os.Stdout.Write(line)
}
if isPrefix {
continue
}
m, _ := r.out.Write(nl)
if r.tee {
os.Stdout.Write(nl)
}
r.size += int64(m)
if r.size >= r.threshold {
err := r.rotate()
if err != nil {
return err
}
r.size = 0
}
}
}
// Write implements the io.Writer interface for Rotator. If p ends in a newline
// and the file has exceeded the threshold size, the file is rotated.
func (r *Rotator) Write(p []byte) (n int, err error) {
r.Lock()
defer r.Unlock()
n, _ = r.out.Write(p)
r.size += int64(n)
if err = fadvise.Switch(r.out); err != nil {
return n, err
}
if r.size >= r.threshold && len(p) > 0 && p[len(p)-1] == '\n' {
err := r.rotate()
if err != nil {
return 0, err
}
r.size = 0
}
return n, nil
}
// Close closes the output logfile.
func (r *Rotator) Close() error {
err := r.out.Close()
r.wg.Wait()
return err
}
func (r *Rotator) rotate() error {
dir := filepath.Dir(r.filename)
glob := filepath.Join(dir, filepath.Base(r.filename)+".*")
existing, err := filepath.Glob(glob)
if err != nil {
return err
}
maxNum := 0
for _, name := range existing {
parts := strings.Split(name, ".")
if len(parts) < 2 {
continue
}
numIdx := len(parts) - 1
if parts[numIdx] == "gz" {
numIdx--
}
num, err := strconv.Atoi(parts[numIdx])
if err != nil {
continue
}
if num > maxNum {
maxNum = num
}
}
err = r.out.Close()
if err != nil {
return err
}
rotname := fmt.Sprintf("%s.%d", r.filename, maxNum+1)
err = os.Rename(r.filename, rotname)
if err != nil {
return err
}
if r.maxRolls > 0 {
for n := maxNum + 1 - r.maxRolls; ; n-- {
err := os.Remove(fmt.Sprintf("%s.%d.gz", r.filename, n))
if err != nil {
break
}
}
}
r.out, err = os.OpenFile(r.filename, os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
return err
}
r.wg.Add(1)
go func() {
err := compress(rotname)
if err == nil {
os.Remove(rotname)
}
r.wg.Done()
}()
return nil
}
func compress(name string) (err error) {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
arc, err := os.OpenFile(name+".gz", os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600)
if err != nil {
return err
}
z := gzip.NewWriter(arc)
if _, err = io.Copy(z, f); err != nil {
return err
}
if err = z.Close(); err != nil {
return err
}
err = fadvise.Switch(arc)
if err != nil {
Error(err)
}
return arc.Close()
}
package l
import (
"encoding/json"
"fmt"
"sync"
)
const (
TError = 1
TWarning = 2
TInfo = 3
TDebug = 4
TTrace = 5
TMax = 99
)
var traceModules = make(map[string]bool)
var mutex sync.RWMutex
var tracePool = sync.Pool{
New: func() interface{} {
return new(TraceObj)
},
}
type TraceObj struct {
module string
level int
trace bool
}
// SetTraceModules set trace modules, "all" to trace all.
// e.g. SetTraceModules([]string{"inotify", "psnotify"})
// trace level use Logger.verboseLevel
func SetTraceModules(modules []string) {
mutex.Lock()
defer mutex.Unlock()
traceModules = make(map[string]bool)
for _, module := range modules {
traceModules[module] = true
}
}
func T(module string, level int) *TraceObj {
obj := tracePool.Get().(*TraceObj)
obj.module = module
obj.level = level
if logger.verboseLevel < level {
obj.trace = false
return obj
}
mutex.RLock()
defer mutex.RUnlock()
if _, ok := traceModules["all"]; ok {
obj.trace = true
return obj
}
if _, ok := traceModules[module]; ok {
obj.trace = true
return obj
}
obj.trace = false
return obj
}
func (t *TraceObj) Trace(msgs ...interface{}) {
defer tracePool.Put(t)
if !t.trace {
return
}
if logger.logLevel > LevelDebug {
return
}
tag := fmt.Sprintf("TRACE-%s-%d", t.module, t.level)
logMsg(tag, msgs...)
}
func (t *TraceObj) Tracef(msgs ...interface{}) {
defer tracePool.Put(t)
if !t.trace {
return
}
if logger.logLevel > LevelDebug {
return
}
tag := fmt.Sprintf("TRACE-%s-%d", t.module, t.level)
if len(msgs) < 2 {
logMsg(tag, msgs...)
return
}
logMsg(tag, fmt.Sprintf(msgs[0].(string), msgs[1:]...))
}
func (t *TraceObj) Tracej(msgs ...interface{}) {
defer tracePool.Put(t)
if !t.trace {
return
}
if logger.logLevel > LevelDebug {
return
}
jsonMsg := []interface{}{}
for _, msg := range msgs {
b, err := json.MarshalIndent(msg, "", " ")
if err != nil {
jsonMsg = append(jsonMsg, msg)
} else {
jsonMsg = append(jsonMsg, string(b))
}
}
tag := fmt.Sprintf("TRACE-%s-%d", t.module, t.level)
logMsg(tag, jsonMsg...)
}
package l
import (
"testing"
)
func TestTrace(t *testing.T) {
logger.logLevel = LevelDebug
Debug("===== trace inotify TMax =====")
logger.verboseLevel = TMax
SetTraceModules([]string{"inotify"})
T("inotify", TError).Trace("inotify trace error")
T("inotify", TWarning).Trace("inotify trace warning")
T("inotify", TInfo).Trace("inotify trace info")
T("inotify", TDebug).Trace("inotify trace debug")
T("inotify", TTrace).Trace("inotify trace trace")
T("inotify", 6).Trace("inotify trace more...")
T("psnotify", TDebug).Trace("psnotify trace debug")
Debug("===== trace all TTrace =====")
logger.verboseLevel = TTrace
SetTraceModules([]string{"all"})
T("inotify", 6).Trace("inotify trace more...")
T("psnotify", TDebug).Trace("psnotify trace debug")
Debug("===== Tracef =====")
T("psnotify", TInfo).Tracef("pname:%s, pid:%d", "a.out", 111)
Debug("===== Tracej =====")
event := map[string]interface{}{
"proc.name": "a.out",
"proc.pid": 111,
"proc.pname": "test",
"proc.ppid": 122,
}
T("psnotify", TDebug).Tracej("process exec event:", event)
}
package l
import (
"encoding/json"
"fmt"
)
type Verbose bool
func V(level int) Verbose {
if logger.verboseLevel < level {
return Verbose(false)
}
return Verbose(true)
}
func (v Verbose) Debug(msgs ...interface{}) {
if !v {
return
}
if logger.logLevel > LevelDebug {
return
}
logMsg("DEBUG", msgs...)
}
func (v Verbose) Debugf(msgs ...interface{}) {
if !v {
return
}
if logger.logLevel > LevelDebug {
return
}
if len(msgs) < 2 {
logMsg("DEBUG", msgs...)
return
}
logMsg("DEBUG", fmt.Sprintf(msgs[0].(string), msgs[1:]...))
}
func (v Verbose) Debugj(msgs ...interface{}) {
if !v {
return
}
if logger.logLevel > LevelDebug {
return
}
jsonMsg := []interface{}{}
for _, msg := range msgs {
b, err := json.MarshalIndent(msg, "", " ")
if err != nil {
jsonMsg = append(jsonMsg, msg)
} else {
jsonMsg = append(jsonMsg, string(b))
}
}
logMsg("DEBUG", jsonMsg...)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment