read configuration file
This commit is contained in:
0
vendor/github.com/pyke369/golang-support/ulog/README.md
generated
vendored
Normal file
0
vendor/github.com/pyke369/golang-support/ulog/README.md
generated
vendored
Normal file
545
vendor/github.com/pyke369/golang-support/ulog/ulog.go
generated
vendored
Normal file
545
vendor/github.com/pyke369/golang-support/ulog/ulog.go
generated
vendored
Normal file
@@ -0,0 +1,545 @@
|
||||
package ulog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
TIME_NONE int = iota
|
||||
TIME_DATETIME
|
||||
TIME_MSDATETIME
|
||||
TIME_TIMESTAMP
|
||||
TIME_MSTIMESTAMP
|
||||
)
|
||||
|
||||
var (
|
||||
cores int
|
||||
facilities = map[string]syslog.Priority{
|
||||
"user": syslog.LOG_USER,
|
||||
"daemon": syslog.LOG_DAEMON,
|
||||
"local0": syslog.LOG_LOCAL0,
|
||||
"local1": syslog.LOG_LOCAL1,
|
||||
"local2": syslog.LOG_LOCAL2,
|
||||
"local3": syslog.LOG_LOCAL3,
|
||||
"local4": syslog.LOG_LOCAL4,
|
||||
"local5": syslog.LOG_LOCAL5,
|
||||
"local6": syslog.LOG_LOCAL6,
|
||||
"local7": syslog.LOG_LOCAL7,
|
||||
}
|
||||
severities = map[string]syslog.Priority{
|
||||
"error": syslog.LOG_ERR,
|
||||
"warning": syslog.LOG_WARNING,
|
||||
"info": syslog.LOG_INFO,
|
||||
"debug": syslog.LOG_DEBUG,
|
||||
}
|
||||
severityLabels = map[syslog.Priority]string{
|
||||
syslog.LOG_ERR: "ERRO ",
|
||||
syslog.LOG_WARNING: "WARN ",
|
||||
syslog.LOG_INFO: "INFO ",
|
||||
syslog.LOG_DEBUG: "DBUG ",
|
||||
}
|
||||
severityColors = map[syslog.Priority]string{
|
||||
syslog.LOG_ERR: "\x1b[31m",
|
||||
syslog.LOG_WARNING: "\x1b[33m",
|
||||
syslog.LOG_INFO: "\x1b[36m",
|
||||
syslog.LOG_DEBUG: "\x1b[32m",
|
||||
}
|
||||
)
|
||||
|
||||
type FileOutput struct {
|
||||
handle *os.File
|
||||
last time.Time
|
||||
}
|
||||
type ULog struct {
|
||||
file, console, syslog bool
|
||||
fileOutputs map[string]*FileOutput
|
||||
filePath string
|
||||
fileTime int
|
||||
fileLast time.Time
|
||||
fileSeverity bool
|
||||
consoleHandle io.Writer
|
||||
consoleTime int
|
||||
consoleSeverity bool
|
||||
consoleColors bool
|
||||
syslogHandle *syslog.Writer
|
||||
syslogRemote string
|
||||
syslogName string
|
||||
syslogFacility syslog.Priority
|
||||
optionUTC bool
|
||||
level syslog.Priority
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func New(target string) *ULog {
|
||||
if cores == 0 {
|
||||
cores = runtime.NumCPU()
|
||||
}
|
||||
log := &ULog{
|
||||
fileOutputs: map[string]*FileOutput{},
|
||||
syslogHandle: nil,
|
||||
}
|
||||
return log.Load(target)
|
||||
}
|
||||
|
||||
func (this *ULog) Load(target string) *ULog {
|
||||
this.Close()
|
||||
if cores > 1 {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
}
|
||||
|
||||
this.file = false
|
||||
this.filePath = ""
|
||||
this.fileTime = TIME_DATETIME
|
||||
this.fileSeverity = true
|
||||
this.console = false
|
||||
this.consoleTime = TIME_DATETIME
|
||||
this.consoleSeverity = true
|
||||
this.consoleColors = true
|
||||
this.consoleHandle = os.Stderr
|
||||
this.syslog = false
|
||||
this.syslogRemote = ""
|
||||
this.syslogName = filepath.Base(os.Args[0])
|
||||
this.syslogFacility = syslog.LOG_DAEMON
|
||||
this.optionUTC = false
|
||||
this.level = syslog.LOG_INFO
|
||||
for _, target := range regexp.MustCompile("(file|console|syslog|option)\\s*\\(([^\\)]*)\\)").FindAllStringSubmatch(target, -1) {
|
||||
switch strings.ToLower(target[1]) {
|
||||
case "file":
|
||||
this.file = true
|
||||
for _, option := range regexp.MustCompile("([^:=,\\s]+)\\s*[:=]\\s*([^,\\s]+)").FindAllStringSubmatch(target[2], -1) {
|
||||
switch strings.ToLower(option[1]) {
|
||||
case "path":
|
||||
this.filePath = option[2]
|
||||
case "time":
|
||||
option[2] = strings.ToLower(option[2])
|
||||
switch {
|
||||
case option[2] == "datetime":
|
||||
this.fileTime = TIME_DATETIME
|
||||
case option[2] == "msdatetime":
|
||||
this.fileTime = TIME_MSDATETIME
|
||||
case option[2] == "stamp" || option[2] == "timestamp":
|
||||
this.fileTime = TIME_TIMESTAMP
|
||||
case option[2] == "msstamp" || option[2] == "mstimestamp":
|
||||
this.fileTime = TIME_MSTIMESTAMP
|
||||
case option[2] != "1" && option[2] != "true" && option[2] != "on" && option[2] != "yes":
|
||||
this.fileTime = TIME_NONE
|
||||
}
|
||||
case "severity":
|
||||
option[2] = strings.ToLower(option[2])
|
||||
if option[2] != "1" && option[2] != "true" && option[2] != "on" && option[2] != "yes" {
|
||||
this.fileSeverity = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if this.filePath == "" {
|
||||
this.file = false
|
||||
}
|
||||
case "console":
|
||||
this.console = true
|
||||
for _, option := range regexp.MustCompile("([^:=,\\s]+)\\s*[:=]\\s*([^,\\s]+)").FindAllStringSubmatch(target[2], -1) {
|
||||
option[2] = strings.ToLower(option[2])
|
||||
switch strings.ToLower(option[1]) {
|
||||
case "output":
|
||||
if option[2] == "stdout" {
|
||||
this.consoleHandle = os.Stdout
|
||||
}
|
||||
case "time":
|
||||
switch {
|
||||
case option[2] == "datetime":
|
||||
this.consoleTime = TIME_DATETIME
|
||||
case option[2] == "msdatetime":
|
||||
this.consoleTime = TIME_MSDATETIME
|
||||
case option[2] == "stamp" || option[2] == "timestamp":
|
||||
this.consoleTime = TIME_TIMESTAMP
|
||||
case option[2] == "msstamp" || option[2] == "mstimestamp":
|
||||
this.consoleTime = TIME_MSTIMESTAMP
|
||||
case option[2] != "1" && option[2] != "true" && option[2] != "on" && option[2] != "yes":
|
||||
this.consoleTime = TIME_NONE
|
||||
}
|
||||
case "severity":
|
||||
if option[2] != "1" && option[2] != "true" && option[2] != "on" && option[2] != "yes" {
|
||||
this.consoleSeverity = false
|
||||
}
|
||||
case "colors":
|
||||
if option[2] != "1" && option[2] != "true" && option[2] != "on" && option[2] != "yes" {
|
||||
this.consoleColors = false
|
||||
}
|
||||
}
|
||||
}
|
||||
case "syslog":
|
||||
this.syslog = true
|
||||
for _, option := range regexp.MustCompile("([^:=,\\s]+)\\s*[:=]\\s*([^,\\s]+)").FindAllStringSubmatch(target[2], -1) {
|
||||
switch strings.ToLower(option[1]) {
|
||||
case "remote":
|
||||
this.syslogRemote = option[2]
|
||||
if !regexp.MustCompile(":\\d+$").MatchString(this.syslogRemote) {
|
||||
this.syslogRemote += ":514"
|
||||
}
|
||||
case "name":
|
||||
this.syslogName = option[2]
|
||||
case "facility":
|
||||
this.syslogFacility = facilities[strings.ToLower(option[2])]
|
||||
}
|
||||
}
|
||||
case "option":
|
||||
for _, option := range regexp.MustCompile("([^:=,\\s]+)\\s*[:=]\\s*([^,\\s]+)").FindAllStringSubmatch(target[2], -1) {
|
||||
option[2] = strings.ToLower(option[2])
|
||||
switch strings.ToLower(option[1]) {
|
||||
case "utc":
|
||||
if option[2] == "1" || option[2] == "true" || option[2] == "on" || option[2] == "yes" {
|
||||
this.optionUTC = true
|
||||
}
|
||||
case "level":
|
||||
this.level = severities[strings.ToLower(option[2])]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var info syscall.Termios
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, this.consoleHandle.(*os.File).Fd(), syscall.TCGETS, uintptr(unsafe.Pointer(&info)), 0, 0, 0); err != 0 {
|
||||
this.consoleColors = false
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *ULog) Close() {
|
||||
if cores > 1 {
|
||||
this.Lock()
|
||||
defer this.Unlock()
|
||||
}
|
||||
if this.syslogHandle != nil {
|
||||
this.syslogHandle.Close()
|
||||
this.syslogHandle = nil
|
||||
}
|
||||
for path, output := range this.fileOutputs {
|
||||
if output.handle != nil {
|
||||
output.handle.Close()
|
||||
}
|
||||
delete(this.fileOutputs, path)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ULog) SetLevel(level string) {
|
||||
level = strings.ToLower(level)
|
||||
switch level {
|
||||
case "error":
|
||||
this.level = syslog.LOG_ERR
|
||||
case "warning":
|
||||
this.level = syslog.LOG_WARNING
|
||||
case "info":
|
||||
this.level = syslog.LOG_INFO
|
||||
case "debug":
|
||||
this.level = syslog.LOG_DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
func strftime(layout string, base time.Time) string {
|
||||
var output []string
|
||||
|
||||
length := len(layout)
|
||||
for index := 0; index < length; index++ {
|
||||
switch layout[index] {
|
||||
case '%':
|
||||
if index < length-1 {
|
||||
switch layout[index+1] {
|
||||
case 'a':
|
||||
output = append(output, base.Format("Mon"))
|
||||
case 'A':
|
||||
output = append(output, base.Format("Monday"))
|
||||
case 'b':
|
||||
output = append(output, base.Format("Jan"))
|
||||
case 'B':
|
||||
output = append(output, base.Format("January"))
|
||||
case 'c':
|
||||
output = append(output, base.Format("Mon Jan 2 15:04:05 2006"))
|
||||
case 'C':
|
||||
output = append(output, fmt.Sprintf("%02d", base.Year()/100))
|
||||
case 'd':
|
||||
output = append(output, fmt.Sprintf("%02d", base.Day()))
|
||||
case 'D':
|
||||
output = append(output, fmt.Sprintf("%02d/%02d/%02d", base.Month(), base.Day(), base.Year()%100))
|
||||
case 'e':
|
||||
output = append(output, fmt.Sprintf("%2d", base.Day()))
|
||||
case 'f':
|
||||
output = append(output, fmt.Sprintf("%06d", base.Nanosecond()/1000))
|
||||
case 'F':
|
||||
output = append(output, fmt.Sprintf("%04d-%02d-%02d", base.Year(), base.Month(), base.Day()))
|
||||
case 'g':
|
||||
year, _ := base.ISOWeek()
|
||||
output = append(output, fmt.Sprintf("%02d", year%100))
|
||||
case 'G':
|
||||
year, _ := base.ISOWeek()
|
||||
output = append(output, fmt.Sprintf("%04d", year))
|
||||
case 'h':
|
||||
output = append(output, base.Format("Jan"))
|
||||
case 'H':
|
||||
output = append(output, fmt.Sprintf("%02d", base.Hour()))
|
||||
case 'I':
|
||||
if base.Hour() == 0 || base.Hour() == 12 {
|
||||
output = append(output, "12")
|
||||
} else {
|
||||
output = append(output, fmt.Sprintf("%02d", base.Hour()%12))
|
||||
}
|
||||
case 'j':
|
||||
output = append(output, fmt.Sprintf("%03d", base.YearDay()))
|
||||
case 'k':
|
||||
output = append(output, fmt.Sprintf("%2d", base.Hour()))
|
||||
case 'l':
|
||||
if base.Hour() == 0 || base.Hour() == 12 {
|
||||
output = append(output, "12")
|
||||
} else {
|
||||
output = append(output, fmt.Sprintf("%2d", base.Hour()%12))
|
||||
}
|
||||
case 'm':
|
||||
output = append(output, fmt.Sprintf("%02d", base.Month()))
|
||||
case 'M':
|
||||
output = append(output, fmt.Sprintf("%02d", base.Minute()))
|
||||
case 'n':
|
||||
output = append(output, "\n")
|
||||
case 'p':
|
||||
if base.Hour() < 12 {
|
||||
output = append(output, "AM")
|
||||
} else {
|
||||
output = append(output, "PM")
|
||||
}
|
||||
case 'P':
|
||||
if base.Hour() < 12 {
|
||||
output = append(output, "am")
|
||||
} else {
|
||||
output = append(output, "pm")
|
||||
}
|
||||
case 'r':
|
||||
if base.Hour() == 0 || base.Hour() == 12 {
|
||||
output = append(output, "12")
|
||||
} else {
|
||||
output = append(output, fmt.Sprintf("%02d", base.Hour()%12))
|
||||
}
|
||||
output = append(output, fmt.Sprintf(":%02d:%02d", base.Minute(), base.Second()))
|
||||
if base.Hour() < 12 {
|
||||
output = append(output, " AM")
|
||||
} else {
|
||||
output = append(output, " PM")
|
||||
}
|
||||
case 'R':
|
||||
output = append(output, fmt.Sprintf("%02d:%02d", base.Hour(), base.Minute()))
|
||||
case 's':
|
||||
output = append(output, fmt.Sprintf("%d", base.Unix()))
|
||||
case 'S':
|
||||
output = append(output, fmt.Sprintf("%02d", base.Second()))
|
||||
case 't':
|
||||
output = append(output, "\t")
|
||||
case 'T':
|
||||
output = append(output, fmt.Sprintf("%02d:%02d:%02d", base.Hour(), base.Minute(), base.Second()))
|
||||
case 'u':
|
||||
day := base.Weekday()
|
||||
if day == 0 {
|
||||
day = 7
|
||||
}
|
||||
output = append(output, fmt.Sprintf("%d", day))
|
||||
case 'U':
|
||||
output = append(output, fmt.Sprintf("%d", (base.YearDay()+6-int(base.Weekday()))/7))
|
||||
case 'V':
|
||||
_, week := base.ISOWeek()
|
||||
output = append(output, fmt.Sprintf("%02d", week))
|
||||
case 'w':
|
||||
output = append(output, fmt.Sprintf("%d", base.Weekday()))
|
||||
case 'W':
|
||||
day := int(base.Weekday())
|
||||
if day == 0 {
|
||||
day = 6
|
||||
} else {
|
||||
day -= 1
|
||||
}
|
||||
output = append(output, fmt.Sprintf("%d", (base.YearDay()+6-day)/7))
|
||||
case 'x':
|
||||
output = append(output, fmt.Sprintf("%02d/%02d/%02d", base.Month(), base.Day(), base.Year()%100))
|
||||
case 'X':
|
||||
output = append(output, fmt.Sprintf("%02d:%02d:%02d", base.Hour(), base.Minute(), base.Second()))
|
||||
case 'y':
|
||||
output = append(output, fmt.Sprintf("%02d", base.Year()%100))
|
||||
case 'Y':
|
||||
output = append(output, fmt.Sprintf("%04d", base.Year()))
|
||||
case 'z':
|
||||
output = append(output, base.Format("-0700"))
|
||||
case 'Z':
|
||||
output = append(output, base.Format("MST"))
|
||||
case '%':
|
||||
output = append(output, "%")
|
||||
}
|
||||
index++
|
||||
}
|
||||
default:
|
||||
output = append(output, string(layout[index]))
|
||||
}
|
||||
}
|
||||
return strings.Join(output, "")
|
||||
}
|
||||
|
||||
func (this *ULog) log(now time.Time, severity syslog.Priority, xlayout interface{}, a ...interface{}) {
|
||||
var err error
|
||||
if this.level < severity || (!this.syslog && !this.file && !this.console) {
|
||||
return
|
||||
}
|
||||
layout := ""
|
||||
switch reflect.TypeOf(xlayout).Kind() {
|
||||
case reflect.Map:
|
||||
var buffer bytes.Buffer
|
||||
|
||||
encoder := json.NewEncoder(&buffer)
|
||||
encoder.SetEscapeHTML(false)
|
||||
if err := encoder.Encode(xlayout); err == nil {
|
||||
layout = "%s"
|
||||
a = []interface{}{bytes.TrimSpace(buffer.Bytes())}
|
||||
}
|
||||
case reflect.String:
|
||||
layout = xlayout.(string)
|
||||
}
|
||||
layout = strings.TrimSpace(layout)
|
||||
if this.syslog {
|
||||
if this.syslogHandle == nil {
|
||||
if cores > 1 {
|
||||
this.Lock()
|
||||
}
|
||||
if this.syslogHandle == nil {
|
||||
protocol := ""
|
||||
if this.syslogRemote != "" {
|
||||
protocol = "udp"
|
||||
}
|
||||
if this.syslogHandle, err = syslog.Dial(protocol, this.syslogRemote, this.syslogFacility, this.syslogName); err != nil {
|
||||
this.syslogHandle = nil
|
||||
}
|
||||
}
|
||||
if cores > 1 {
|
||||
this.Unlock()
|
||||
}
|
||||
}
|
||||
if this.syslogHandle != nil {
|
||||
switch severity {
|
||||
case syslog.LOG_ERR:
|
||||
this.syslogHandle.Err(fmt.Sprintf(layout, a...))
|
||||
case syslog.LOG_WARNING:
|
||||
this.syslogHandle.Warning(fmt.Sprintf(layout, a...))
|
||||
case syslog.LOG_INFO:
|
||||
this.syslogHandle.Info(fmt.Sprintf(layout, a...))
|
||||
case syslog.LOG_DEBUG:
|
||||
this.syslogHandle.Debug(fmt.Sprintf(layout, a...))
|
||||
}
|
||||
}
|
||||
}
|
||||
if this.optionUTC {
|
||||
now = now.UTC()
|
||||
} else {
|
||||
now = now.Local()
|
||||
}
|
||||
if this.file {
|
||||
path := strftime(this.filePath, now)
|
||||
if cores > 1 {
|
||||
this.Lock()
|
||||
}
|
||||
if this.fileOutputs[path] == nil {
|
||||
os.MkdirAll(filepath.Dir(path), 0755)
|
||||
if handle, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err == nil {
|
||||
this.fileOutputs[path] = &FileOutput{handle: handle}
|
||||
}
|
||||
}
|
||||
if this.fileOutputs[path] != nil && this.fileOutputs[path].handle != nil {
|
||||
prefix := ""
|
||||
switch this.fileTime {
|
||||
case TIME_DATETIME:
|
||||
prefix = fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d ", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
|
||||
case TIME_MSDATETIME:
|
||||
prefix = fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), now.Nanosecond()/int(time.Millisecond))
|
||||
case TIME_TIMESTAMP:
|
||||
prefix = fmt.Sprintf("%d ", now.Unix())
|
||||
case TIME_MSTIMESTAMP:
|
||||
prefix = fmt.Sprintf("%d ", now.UnixNano()/int64(time.Millisecond))
|
||||
}
|
||||
if this.fileSeverity {
|
||||
prefix += severityLabels[severity]
|
||||
}
|
||||
this.fileOutputs[path].handle.WriteString(fmt.Sprintf(prefix+layout+"\n", a...))
|
||||
this.fileOutputs[path].last = now
|
||||
}
|
||||
if now.Sub(this.fileLast) >= 5*time.Second {
|
||||
this.fileLast = now
|
||||
for path, output := range this.fileOutputs {
|
||||
if now.Sub(output.last) >= 5*time.Second {
|
||||
output.handle.Close()
|
||||
delete(this.fileOutputs, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
if cores > 1 {
|
||||
this.Unlock()
|
||||
}
|
||||
}
|
||||
if this.console {
|
||||
prefix := ""
|
||||
switch this.consoleTime {
|
||||
case TIME_DATETIME:
|
||||
prefix = fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d ", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
|
||||
case TIME_MSDATETIME:
|
||||
prefix = fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), now.Nanosecond()/int(time.Millisecond))
|
||||
case TIME_TIMESTAMP:
|
||||
prefix = fmt.Sprintf("%d ", now.Unix())
|
||||
case TIME_MSTIMESTAMP:
|
||||
prefix = fmt.Sprintf("%d ", now.UnixNano()/int64(time.Millisecond))
|
||||
}
|
||||
if this.consoleSeverity {
|
||||
if this.consoleColors {
|
||||
prefix += fmt.Sprintf("%s%s\x1b[0m", severityColors[severity], severityLabels[severity])
|
||||
} else {
|
||||
prefix += severityLabels[severity]
|
||||
}
|
||||
}
|
||||
if cores > 1 {
|
||||
this.Lock()
|
||||
}
|
||||
fmt.Fprintf(this.consoleHandle, prefix+layout+"\n", a...)
|
||||
if cores > 1 {
|
||||
this.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ULog) Error(layout interface{}, a ...interface{}) {
|
||||
this.log(time.Now(), syslog.LOG_ERR, layout, a...)
|
||||
}
|
||||
func (this *ULog) Warn(layout interface{}, a ...interface{}) {
|
||||
this.log(time.Now(), syslog.LOG_WARNING, layout, a...)
|
||||
}
|
||||
func (this *ULog) Info(layout interface{}, a ...interface{}) {
|
||||
this.log(time.Now(), syslog.LOG_INFO, layout, a...)
|
||||
}
|
||||
func (this *ULog) Debug(layout interface{}, a ...interface{}) {
|
||||
this.log(time.Now(), syslog.LOG_DEBUG, layout, a...)
|
||||
}
|
||||
|
||||
func (this *ULog) ErrorTime(now time.Time, layout interface{}, a ...interface{}) {
|
||||
this.log(now, syslog.LOG_ERR, layout, a...)
|
||||
}
|
||||
func (this *ULog) WarnTime(now time.Time, layout interface{}, a ...interface{}) {
|
||||
this.log(now, syslog.LOG_WARNING, layout, a...)
|
||||
}
|
||||
func (this *ULog) InfoTime(now time.Time, layout interface{}, a ...interface{}) {
|
||||
this.log(now, syslog.LOG_INFO, layout, a...)
|
||||
}
|
||||
func (this *ULog) DebugTime(now time.Time, layout interface{}, a ...interface{}) {
|
||||
this.log(now, syslog.LOG_DEBUG, layout, a...)
|
||||
}
|
||||
Reference in New Issue
Block a user