zoukankan      html  css  js  c++  java
  • blog4go.go

    package blog4go

    import (
    "bufio"
    "errors"
    "fmt"
    "io"
    "os"
    "strings"
    "sync"
    )

    const (
    // EOL end of a line
    EOL = ' '
    // ESCAPE escape character
    ESCAPE = '\'
    // PLACEHOLDER placeholder
    PLACEHOLDER = '%'
    )

    var (
    // blog is the singleton instance use for blog.write/writef
    blog Writer

    // global mutex log used for singlton
    singltonLock *sync.Mutex

    // DefaultBufferSize bufio buffer size
    DefaultBufferSize = 4096 // default memory page size
    // ErrInvalidFormat invalid format error
    ErrInvalidFormat = errors.New("Invalid format type")
    // ErrAlreadyInit show that blog is already initialized once
    ErrAlreadyInit = errors.New("blog4go has been already initialized")
    )

    // Writer interface is a common definition of any writers in this package.
    // Any struct implements Writer interface must implement functions below.
    // Close is used for close the writer and free any elements if needed.
    // write is an internal function that write pure message with specific
    // logging level.
    // writef is an internal function that formatting message with specific
    // logging level. Placeholders in the format string will be replaced with
    // args given.
    // Both write and writef may have an asynchronous call of user defined
    // function before write and writef function end..
    type Writer interface {
    // Close do anything end before program end
    Close()

    // SetLevel set logging level threshold
    SetLevel(level LevelType)
    // Level get log level
    Level() LevelType

    // write/writef functions with different levels
    write(level LevelType, args ...interface{})
    writef(level LevelType, format string, args ...interface{})
    Debug(args ...interface{})
    Debugf(format string, args ...interface{})
    Trace(args ...interface{})
    Tracef(format string, args ...interface{})
    Info(args ...interface{})
    Infof(format string, args ...interface{})
    Warn(args ...interface{})
    Warnf(format string, args ...interface{})
    Error(args ...interface{})
    Errorf(format string, args ...interface{})
    Critical(args ...interface{})
    Criticalf(format string, args ...interface{})

    // flush log to disk
    flush()

    // hook
    SetHook(hook Hook)
    SetHookLevel(level LevelType)
    SetHookAsync(async bool)

    // logrotate
    SetTimeRotated(timeRotated bool)
    TimeRotated() bool
    SetRotateSize(rotateSize int64)
    RotateSize() int64
    SetRotateLines(rotateLines int)
    RotateLines() int
    SetRetentions(retentions int64)
    Retentions() int64
    SetColored(colored bool)
    Colored() bool
    }

    func init() {
    singltonLock = new(sync.Mutex)
    DefaultBufferSize = os.Getpagesize()
    }

    // NewWriterFromConfigAsFile initialize a writer according to given config file
    // configFile must be the path to the config file
    func NewWriterFromConfigAsFile(configFile string) (err error) {
    singltonLock.Lock()
    defer singltonLock.Unlock()
    if nil != blog {
    return ErrAlreadyInit
    }

    // read config from file
    config, err := readConfig(configFile)
    if nil != err {
    return
    }

    if err = config.valid(); nil != err {
    return
    }

    multiWriter := new(MultiWriter)

    multiWriter.level = DEBUG
    if level := LevelFromString(config.MinLevel); level.valid() {
    multiWriter.level = level
    }

    multiWriter.closed = false
    multiWriter.writers = make(map[LevelType]Writer)

    for _, filter := range config.Filters {
    var rotate = false
    var timeRotate = false
    var isSocket = false
    var isConsole = false

    var f *os.File
    var blog *BLog
    var fileLock *sync.RWMutex

    // get file path
    var filePath string
    if (file{}) != filter.File {
    // file do not need logrotate
    filePath = filter.File.Path
    rotate = false

    f, err = os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
    if nil != err {
    return err
    }
    blog = NewBLog(f)
    fileLock = new(sync.RWMutex)
    } else if (rotateFile{}) != filter.RotateFile {
    // file need logrotate
    filePath = filter.RotateFile.Path
    rotate = true
    timeRotate = TypeTimeBaseRotate == filter.RotateFile.Type
    fileName := filePath
    if timeRotate {
    fileName = fmt.Sprintf("%s.%s", fileName, timeCache.Date())
    }
    f, err = os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(0644))
    if nil != err {
    return err
    }
    blog = NewBLog(f)
    fileLock = new(sync.RWMutex)
    } else if (socket{}) != filter.Socket {
    isSocket = true
    } else {
    // use console writer as default
    isConsole = true
    }

    levels := strings.Split(filter.Levels, ",")
    for _, levelStr := range levels {
    var level LevelType
    if level = LevelFromString(levelStr); !level.valid() {
    return ErrInvalidLevel
    }

    if isConsole {
    // console writer
    writer, err := newConsoleWriter(filter.Console.Redirect)
    if nil != err {
    return err
    }

    multiWriter.writers[level] = writer
    continue
    }

    if isSocket {
    // socket writer
    writer, err := newSocketWriter(filter.Socket.Network, filter.Socket.Address)
    if nil != err {
    return err
    }

    multiWriter.writers[level] = writer
    continue
    }

    // init a base file writer
    writer, err := newBaseFileWriter(filePath, timeRotate)
    if nil != err {
    return err
    }

    if rotate {
    // set logrotate strategy
    if TypeTimeBaseRotate == filter.RotateFile.Type {
    writer.SetTimeRotated(true)
    writer.SetRetentions(filter.RotateFile.Retentions)
    } else if TypeSizeBaseRotate == filter.RotateFile.Type {
    writer.SetRotateSize(filter.RotateFile.RotateSize)
    writer.SetRotateLines(filter.RotateFile.RotateLines)
    writer.SetRetentions(filter.RotateFile.Retentions)
    } else {
    return ErrInvalidRotateType
    }
    }

    writer.file = f
    writer.blog = blog
    writer.lock = fileLock

    // set color
    multiWriter.SetColored(filter.Colored)
    multiWriter.writers[level] = writer
    }
    }

    blog = multiWriter
    return
    }

    // BLog struct is a threadsafe log writer inherit bufio.Writer
    type BLog struct {
    // logging level
    // every message level exceed this level will be written
    level LevelType

    // input io
    in io.Writer

    // bufio.Writer object of the input io
    writer *bufio.Writer

    // exclusive lock while calling write function of bufio.Writer
    lock *sync.Mutex

    // closed tag
    closed bool
    }

    // NewBLog create a BLog instance and return the pointer of it.
    // fileName must be an absolute path to the destination log file
    func NewBLog(in io.Writer) (blog *BLog) {
    blog = new(BLog)
    blog.in = in
    blog.level = TRACE
    blog.lock = new(sync.Mutex)
    blog.closed = false

    blog.writer = bufio.NewWriterSize(in, DefaultBufferSize)
    return
    }

    // write writes pure message with specific level
    func (blog *BLog) write(level LevelType, args ...interface{}) int {
    blog.lock.Lock()
    defer blog.lock.Unlock()

    // 统计日志size
    var size = 0
    format := fmt.Sprint(args...)

    blog.writer.Write(timeCache.Format())
    blog.writer.WriteString(level.prefix())
    blog.writer.WriteString(format)
    blog.writer.WriteByte(EOL)

    size = len(timeCache.Format()) + len(level.prefix()) + len(format) + 1
    return size
    }

    // write formats message with specific level and write it
    func (blog *BLog) writef(level LevelType, format string, args ...interface{}) int {
    // 格式化构造message
    // 边解析边输出
    // 使用 % 作占位符
    blog.lock.Lock()
    defer blog.lock.Unlock()

    // 统计日志size
    var size = 0

    // 识别占位符标记
    var tag = false
    var tagPos int
    // 转义字符标记
    var escape = false
    // 在处理的args 下标
    var n int
    // 未输出的,第一个普通字符位置
    var last int
    var s int

    blog.writer.Write(timeCache.Format())
    blog.writer.WriteString(level.prefix())

    size += len(timeCache.Format()) + len(level.prefix())

    for i, v := range format {
    if tag {
    switch v {
    case 'd', 'f', 'v', 'b', 'o', 'x', 'X', 'c', 'p', 't', 's', 'T', 'q', 'U', 'e', 'E', 'g', 'G':
    if escape {
    escape = false
    }

    s, _ = blog.writer.WriteString(fmt.Sprintf(format[tagPos:i+1], args[n]))
    size += s
    n++
    last = i + 1
    tag = false
    //转义符
    case ESCAPE:
    if escape {
    blog.writer.WriteByte(ESCAPE)
    size++
    }
    escape = !escape
    //默认
    default:

    }

    } else {
    // 占位符,百分号
    if PLACEHOLDER == format[i] && !escape {
    tag = true
    tagPos = i
    s, _ = blog.writer.WriteString(format[last:i])
    size += s
    escape = false
    }
    }
    }
    blog.writer.WriteString(format[last:])
    blog.writer.WriteByte(EOL)

    size += len(format[last:]) + 1
    return size
    }

    // Flush flush buffer to disk
    func (blog *BLog) flush() {
    blog.lock.Lock()
    defer blog.lock.Unlock()

    if blog.closed {
    return
    }

    blog.writer.Flush()
    }

    // Close close file writer
    func (blog *BLog) Close() {
    blog.lock.Lock()
    defer blog.lock.Unlock()

    if nil == blog || blog.closed {
    return
    }

    blog.closed = true
    blog.writer.Flush()
    blog.writer = nil
    }

    // In return the input io.Writer
    func (blog *BLog) In() io.Writer {
    return blog.in
    }

    // Level return logging level threshold
    func (blog *BLog) Level() LevelType {
    return blog.level
    }

    // SetLevel set logging level threshold
    func (blog *BLog) SetLevel(level LevelType) *BLog {
    blog.level = level
    return blog
    }

    // resetFile resets file descriptor of the writer with specific file name
    func (blog *BLog) resetFile(in io.Writer) (err error) {
    blog.lock.Lock()
    defer blog.lock.Unlock()

    blog.writer.Flush()

    blog.in = in
    blog.writer.Reset(in)

    return
    }

    // SetBufferSize set bufio buffer size in bytes
    func SetBufferSize(size int) {
    DefaultBufferSize = size
    }

    // Level get log level
    func Level() LevelType {
    return blog.Level()
    }

    // SetLevel set level for logging action
    func SetLevel(level LevelType) {
    blog.SetLevel(level)
    }

    // SetHook set hook for logging action
    func SetHook(hook Hook) {
    blog.SetHook(hook)
    }

    // SetHookLevel set when hook will be called
    func SetHookLevel(level LevelType) {
    blog.SetHookLevel(level)
    }

    // SetHookAsync set whether hook is called async
    func SetHookAsync(async bool) {
    blog.SetHookAsync(async)
    }

    // Colored get whether it is log with colored
    func Colored() bool {
    return blog.Colored()
    }

    // SetColored set logging color
    func SetColored(colored bool) {
    blog.SetColored(colored)
    }

    // TimeRotated get timeRotated
    func TimeRotated() bool {
    return blog.TimeRotated()
    }

    // SetTimeRotated toggle time base logrotate on the fly
    func SetTimeRotated(timeRotated bool) {
    blog.SetTimeRotated(timeRotated)
    }

    // Retentions get retentions
    func Retentions() int64 {
    return blog.Retentions()
    }

    // SetRetentions set how many logs will keep after logrotate
    func SetRetentions(retentions int64) {
    blog.SetRetentions(retentions)
    }

    // RotateSize get rotateSize
    func RotateSize() int64 {
    return blog.RotateSize()
    }

    // SetRotateSize set size when logroatate
    func SetRotateSize(rotateSize int64) {
    blog.SetRotateSize(rotateSize)
    }

    // RotateLines get rotateLines
    func RotateLines() int {
    return blog.RotateLines()
    }

    // SetRotateLines set line number when logrotate
    func SetRotateLines(rotateLines int) {
    blog.SetRotateLines(rotateLines)
    }

    // Flush flush logs to disk
    func Flush() {
    blog.flush()
    }

    // Trace static function for Trace
    func Trace(args ...interface{}) {
    blog.Trace(args...)
    }

    // Tracef static function for Tracef
    func Tracef(format string, args ...interface{}) {
    blog.Tracef(format, args...)
    }

    // Debug static function for Debug
    func Debug(args ...interface{}) {
    blog.Debug(args...)
    }

    // Debugf static function for Debugf
    func Debugf(format string, args ...interface{}) {
    blog.Debugf(format, args...)
    }

    // Info static function for Info
    func Info(args ...interface{}) {
    blog.Info(args...)
    }

    // Infof static function for Infof
    func Infof(format string, args ...interface{}) {
    blog.Infof(format, args...)
    }

    // Warn static function for Warn
    func Warn(args ...interface{}) {
    blog.Warn(args...)
    }

    // Warnf static function for Warnf
    func Warnf(format string, args ...interface{}) {
    blog.Warnf(format, args...)
    }

    // Error static function for Error
    func Error(args ...interface{}) {
    blog.Error(args...)
    }

    // Errorf static function for Errorf
    func Errorf(format string, args ...interface{}) {
    blog.Errorf(format, args...)
    }

    // Critical static function for Critical
    func Critical(args ...interface{}) {
    blog.Critical(args...)
    }

    // Criticalf static function for Criticalf
    func Criticalf(format string, args ...interface{}) {
    blog.Criticalf(format, args...)
    }

    // Close close the logger
    func Close() {
    singltonLock.Lock()
    defer singltonLock.Unlock()

    if nil == blog {
    return
    }

    blog.Close()
    blog = nil
    }

  • 相关阅读:
    弧长的参方程表示形式
    selenium实例
    中英文混合分词
    准确率(Precision)、召回率(Recall)以及F值(F-Measure)
    安装zeromq以及zeromq的python示例
    uwsgi安装过程中遇到的问题
    安装python-devel 在升级到python2.7之后
    更新yum源
    yum mysql on centos 7
    vue--子组件主动获取父组件的数据和方法
  • 原文地址:https://www.cnblogs.com/zhangboyu/p/7449625.html
Copyright © 2011-2022 走看看