| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- // Copyright 2013, Cong Ding. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- // author: Cong Ding <dinggnu@gmail.com>
- // Package logging implements log library for other applications. It provides
- // functions Debug, Info, Warning, Error, Critical, and formatting version
- // Logf.
- //
- // Example:
- //
- // logger := logging.SimpleLogger("main")
- // logger.SetLevel(logging.WARNING)
- // logger.Error("test for error")
- // logger.Warning("test for warning", "second parameter")
- // logger.Debug("test for debug")
- //
- package logging
- import (
- "github.com/ccding/go-config-reader/config"
- "io"
- "os"
- "strconv"
- "sync"
- "sync/atomic"
- "time"
- )
- // Pre-defined formats
- const (
- DefaultFileName = "logging.log" // default logging filename
- DefaultConfigFile = "logging.conf" // default logging configuration file
- DefaultTimeFormat = "2006-01-02 15:04:05.999999999" // defaulttime format
- bufSize = 1000 // buffer size for writer
- queueSize = 10000 // chan queue size in async logging
- reqSize = 10000 // chan queue size in async logging
- )
- // Logger is the logging struct.
- type Logger struct {
- // Be careful of the alignment issue of the variable seqid because it
- // uses the sync/atomic.AddUint64() operation. If the alignment is
- // wrong, it will cause a panic. To solve the alignment issue in an
- // easy way, we put seqid to the beginning of the structure.
- // seqid is only visiable internally.
- seqid uint64 // last used sequence number in record
- // These variables can be configured by users.
- name string // logger name
- level Level // record level higher than this will be printed
- recordFormat string // format of the record
- recordArgs []string // arguments to be used in the recordFormat
- out io.Writer // writer
- sync bool // use sync or async way to record logs
- timeFormat string // format for time
- // These variables are visible to users.
- startTime time.Time // start time of the logger
- // Internally used variables, which don't have get and set functions.
- wlock sync.Mutex // writer lock
- queue chan string // queue used in async logging
- request chan request // queue used in non-runtime logging
- flush chan bool // flush signal for the watcher to write
- quit chan bool // quit signal for the watcher to quit
- fd *os.File // file handler, used to close the file on destroy
- runtime bool // with runtime operation or not
- }
- // SimpleLogger creates a new logger with simple configuration.
- func SimpleLogger(name string) (*Logger, error) {
- return createLogger(name, WARNING, BasicFormat, DefaultTimeFormat, os.Stdout, false)
- }
- // BasicLogger creates a new logger with basic configuration.
- func BasicLogger(name string) (*Logger, error) {
- return FileLogger(name, WARNING, BasicFormat, DefaultTimeFormat, DefaultFileName, false)
- }
- // RichLogger creates a new logger with simple configuration.
- func RichLogger(name string) (*Logger, error) {
- return FileLogger(name, NOTSET, RichFormat, DefaultTimeFormat, DefaultFileName, false)
- }
- // FileLogger creates a new logger with file output.
- func FileLogger(name string, level Level, format string, timeFormat string, file string, sync bool) (*Logger, error) {
- out, err := os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.ModeAppend|0666)
- if err != nil {
- return nil, err
- }
- logger, err := createLogger(name, level, format, timeFormat, out, sync)
- if err == nil {
- logger.fd = out
- return logger, nil
- } else {
- return nil, err
- }
- }
- // WriterLogger creates a new logger with a writer
- func WriterLogger(name string, level Level, format string, timeFormat string, out io.Writer, sync bool) (*Logger, error) {
- return createLogger(name, level, format, timeFormat, out, sync)
- }
- // WriterLogger creates a new logger from a configuration file
- func ConfigLogger(filename string) (*Logger, error) {
- conf := config.NewConfig(filename)
- err := conf.Read()
- if err != nil {
- return nil, err
- }
- name := conf.Get("", "name")
- slevel := conf.Get("", "level")
- if slevel == "" {
- slevel = "0"
- }
- l, err := strconv.Atoi(slevel)
- if err != nil {
- return nil, err
- }
- level := Level(l)
- format := conf.Get("", "format")
- if format == "" {
- format = BasicFormat
- }
- timeFormat := conf.Get("", "timeFormat")
- if timeFormat == "" {
- timeFormat = DefaultTimeFormat
- }
- ssync := conf.Get("", "sync")
- if ssync == "" {
- ssync = "0"
- }
- file := conf.Get("", "file")
- if file == "" {
- file = DefaultFileName
- }
- sync := true
- if ssync == "0" {
- sync = false
- } else if ssync == "1" {
- sync = true
- } else {
- return nil, err
- }
- return FileLogger(name, level, format, timeFormat, file, sync)
- }
- // createLogger create a new logger
- func createLogger(name string, level Level, format string, timeFormat string, out io.Writer, sync bool) (*Logger, error) {
- logger := new(Logger)
- err := logger.parseFormat(format)
- if err != nil {
- return nil, err
- }
- // asign values to logger
- logger.name = name
- logger.level = level
- logger.out = out
- logger.seqid = 0
- logger.sync = sync
- logger.queue = make(chan string, queueSize)
- logger.request = make(chan request, reqSize)
- logger.flush = make(chan bool)
- logger.quit = make(chan bool)
- logger.startTime = time.Now()
- logger.fd = nil
- logger.timeFormat = timeFormat
- // start watcher to write logs if it is async or no runtime field
- if !logger.sync {
- go logger.watcher()
- }
- return logger, nil
- }
- // Destroy sends quit signal to watcher and releases all the resources.
- func (logger *Logger) Destroy() {
- if !logger.sync {
- // quit watcher
- logger.quit <- true
- // wait for watcher quit
- <-logger.quit
- }
- // clean up
- if logger.fd != nil {
- logger.fd.Close()
- }
- }
- // Flush the writer
- func (logger *Logger) Flush() {
- if !logger.sync {
- // send flush signal
- logger.flush <- true
- // wait for flush finish
- <-logger.flush
- }
- }
- // Getter functions
- func (logger *Logger) Name() string {
- return logger.name
- }
- func (logger *Logger) StartTime() int64 {
- return logger.startTime.UnixNano()
- }
- func (logger *Logger) TimeFormat() string {
- return logger.timeFormat
- }
- func (logger *Logger) Level() Level {
- return Level(atomic.LoadInt32((*int32)(&logger.level)))
- }
- func (logger *Logger) RecordFormat() string {
- return logger.recordFormat
- }
- func (logger *Logger) RecordArgs() []string {
- return logger.recordArgs
- }
- func (logger *Logger) Writer() io.Writer {
- return logger.out
- }
- func (logger *Logger) Sync() bool {
- return logger.sync
- }
- // Setter functions
- func (logger *Logger) SetLevel(level Level) {
- atomic.StoreInt32((*int32)(&logger.level), int32(level))
- }
- func (logger *Logger) SetWriter(out ...io.Writer) {
- logger.out = io.MultiWriter(out...)
- }
|