mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
261 lines
7.1 KiB
Go
261 lines
7.1 KiB
Go
// 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.Create(file)
|
|
if err != nil {
|
|
return new(Logger), err
|
|
}
|
|
logger, err := createLogger(name, level, format, timeFormat, out, sync)
|
|
if err == nil {
|
|
logger.fd = out
|
|
}
|
|
return logger, 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, err := config.Read(filename)
|
|
if err != nil {
|
|
return new(Logger), err
|
|
}
|
|
ok := true
|
|
name, ok := conf["name"]
|
|
if !ok {
|
|
name = ""
|
|
}
|
|
slevel, ok := conf["level"]
|
|
if !ok {
|
|
slevel = "0"
|
|
}
|
|
l, err := strconv.Atoi(slevel)
|
|
if err != nil {
|
|
return new(Logger), err
|
|
}
|
|
level := Level(l)
|
|
format, ok := conf["format"]
|
|
if !ok {
|
|
format = BasicFormat
|
|
}
|
|
timeFormat, ok := conf["timeFormat"]
|
|
if !ok {
|
|
timeFormat = DefaultTimeFormat
|
|
}
|
|
ssync, ok := conf["sync"]
|
|
if !ok {
|
|
ssync = "0"
|
|
}
|
|
file, ok := conf["file"]
|
|
if !ok {
|
|
file = DefaultFileName
|
|
}
|
|
sync := true
|
|
if ssync == "0" {
|
|
sync = false
|
|
} else if ssync == "1" {
|
|
sync = true
|
|
} else {
|
|
return new(Logger), 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 logger, 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...)
|
|
}
|