Update direct dependencies where possible
parent
f716b8fc0f
commit
09875fe160
@ -1,2 +0,0 @@
|
||||
*.sw[op]
|
||||
.DS_Store
|
@ -1,13 +0,0 @@
|
||||
Copyright (c) 2010, Kyle Lemons <kyle@kylelemons.net>. 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.
|
@ -1,14 +0,0 @@
|
||||
# This is an unmaintained fork, left only so it doesn't break imports.
|
||||
|
||||
Please see http://log4go.googlecode.com/
|
||||
|
||||
Installation:
|
||||
- Run `goinstall log4go.googlecode.com/hg`
|
||||
|
||||
Usage:
|
||||
- Add the following import:
|
||||
import l4g "log4go.googlecode.com/hg"
|
||||
|
||||
Acknowledgements:
|
||||
- pomack
|
||||
For providing awesome patches to bring log4go up to the latest Go spec
|
@ -1,288 +0,0 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type xmlProperty struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type xmlFilter struct {
|
||||
Enabled string `xml:"enabled,attr"`
|
||||
Tag string `xml:"tag"`
|
||||
Level string `xml:"level"`
|
||||
Type string `xml:"type"`
|
||||
Property []xmlProperty `xml:"property"`
|
||||
}
|
||||
|
||||
type xmlLoggerConfig struct {
|
||||
Filter []xmlFilter `xml:"filter"`
|
||||
}
|
||||
|
||||
// Load XML configuration; see examples/example.xml for documentation
|
||||
func (log Logger) LoadConfiguration(filename string) {
|
||||
log.Close()
|
||||
|
||||
// Open the configuration file
|
||||
fd, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadAll(fd)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
xc := new(xmlLoggerConfig)
|
||||
if err := xml.Unmarshal(contents, xc); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, xmlfilt := range xc.Filter {
|
||||
var filt LogWriter
|
||||
var lvl Level
|
||||
bad, good, enabled := false, true, false
|
||||
|
||||
// Check required children
|
||||
if len(xmlfilt.Enabled) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
|
||||
bad = true
|
||||
} else {
|
||||
enabled = xmlfilt.Enabled != "false"
|
||||
}
|
||||
if len(xmlfilt.Tag) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
|
||||
bad = true
|
||||
}
|
||||
if len(xmlfilt.Type) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
|
||||
bad = true
|
||||
}
|
||||
if len(xmlfilt.Level) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
|
||||
bad = true
|
||||
}
|
||||
|
||||
switch xmlfilt.Level {
|
||||
case "FINEST":
|
||||
lvl = FINEST
|
||||
case "FINE":
|
||||
lvl = FINE
|
||||
case "DEBUG":
|
||||
lvl = DEBUG
|
||||
case "TRACE":
|
||||
lvl = TRACE
|
||||
case "INFO":
|
||||
lvl = INFO
|
||||
case "WARNING":
|
||||
lvl = WARNING
|
||||
case "ERROR":
|
||||
lvl = ERROR
|
||||
case "CRITICAL":
|
||||
lvl = CRITICAL
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
|
||||
bad = true
|
||||
}
|
||||
|
||||
// Just so all of the required attributes are errored at the same time if missing
|
||||
if bad {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch xmlfilt.Type {
|
||||
case "console":
|
||||
filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "file":
|
||||
filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "xml":
|
||||
filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
|
||||
case "socket":
|
||||
filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Just so all of the required params are errored at the same time if wrong
|
||||
if !good {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// If we're disabled (syntax and correctness checks only), don't add to logger
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
|
||||
log[xmlfilt.Tag] = &Filter{lvl, filt}
|
||||
}
|
||||
}
|
||||
|
||||
func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (*ConsoleLogWriter, bool) {
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return NewConsoleLogWriter(), true
|
||||
}
|
||||
|
||||
// Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024)
|
||||
func strToNumSuffix(str string, mult int) int {
|
||||
num := 1
|
||||
if len(str) > 1 {
|
||||
switch str[len(str)-1] {
|
||||
case 'G', 'g':
|
||||
num *= mult
|
||||
fallthrough
|
||||
case 'M', 'm':
|
||||
num *= mult
|
||||
fallthrough
|
||||
case 'K', 'k':
|
||||
num *= mult
|
||||
str = str[0 : len(str)-1]
|
||||
}
|
||||
}
|
||||
parsed, _ := strconv.Atoi(str)
|
||||
return parsed * num
|
||||
}
|
||||
func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||
file := ""
|
||||
format := "[%D %T] [%L] (%S) %M"
|
||||
maxlines := 0
|
||||
maxsize := 0
|
||||
daily := false
|
||||
rotate := false
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "filename":
|
||||
file = strings.Trim(prop.Value, " \r\n")
|
||||
case "format":
|
||||
format = strings.Trim(prop.Value, " \r\n")
|
||||
case "maxlines":
|
||||
maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||
case "maxsize":
|
||||
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||
case "daily":
|
||||
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
case "rotate":
|
||||
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(file) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
flw := NewFileLogWriter(file, rotate)
|
||||
flw.SetFormat(format)
|
||||
flw.SetRotateLines(maxlines)
|
||||
flw.SetRotateSize(maxsize)
|
||||
flw.SetRotateDaily(daily)
|
||||
return flw, true
|
||||
}
|
||||
|
||||
func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, bool) {
|
||||
file := ""
|
||||
maxrecords := 0
|
||||
maxsize := 0
|
||||
daily := false
|
||||
rotate := false
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "filename":
|
||||
file = strings.Trim(prop.Value, " \r\n")
|
||||
case "maxrecords":
|
||||
maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000)
|
||||
case "maxsize":
|
||||
maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024)
|
||||
case "daily":
|
||||
daily = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
case "rotate":
|
||||
rotate = strings.Trim(prop.Value, " \r\n") != "false"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(file) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
xlw := NewXMLLogWriter(file, rotate)
|
||||
xlw.SetRotateLines(maxrecords)
|
||||
xlw.SetRotateSize(maxsize)
|
||||
xlw.SetRotateDaily(daily)
|
||||
return xlw, true
|
||||
}
|
||||
|
||||
func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) {
|
||||
endpoint := ""
|
||||
protocol := "udp"
|
||||
|
||||
// Parse properties
|
||||
for _, prop := range props {
|
||||
switch prop.Name {
|
||||
case "endpoint":
|
||||
endpoint = strings.Trim(prop.Value, " \r\n")
|
||||
case "protocol":
|
||||
protocol = strings.Trim(prop.Value, " \r\n")
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename)
|
||||
}
|
||||
}
|
||||
|
||||
// Check properties
|
||||
if len(endpoint) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If it's disabled, we're just checking syntax
|
||||
if !enabled {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return NewSocketLogWriter(protocol, endpoint), true
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This log writer sends output to a file
|
||||
type FileLogWriter struct {
|
||||
rec chan *LogRecord
|
||||
rot chan bool
|
||||
|
||||
// The opened file
|
||||
filename string
|
||||
file *os.File
|
||||
|
||||
// The logging format
|
||||
format string
|
||||
|
||||
// File header/trailer
|
||||
header, trailer string
|
||||
|
||||
// Rotate at linecount
|
||||
maxlines int
|
||||
maxlines_curlines int
|
||||
|
||||
// Rotate at size
|
||||
maxsize int
|
||||
maxsize_cursize int
|
||||
|
||||
// Rotate daily
|
||||
daily bool
|
||||
daily_opendate int
|
||||
|
||||
// Keep old logfiles (.001, .002, etc)
|
||||
rotate bool
|
||||
maxbackup int
|
||||
}
|
||||
|
||||
// This is the FileLogWriter's output method
|
||||
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
|
||||
w.rec <- rec
|
||||
}
|
||||
|
||||
func (w *FileLogWriter) Close() {
|
||||
close(w.rec)
|
||||
w.file.Sync()
|
||||
}
|
||||
|
||||
// NewFileLogWriter creates a new LogWriter which writes to the given file and
|
||||
// has rotation enabled if rotate is true.
|
||||
//
|
||||
// If rotate is true, any time a new log file is opened, the old one is renamed
|
||||
// with a .### extension to preserve it. The various Set* methods can be used
|
||||
// to configure log rotation based on lines, size, and daily.
|
||||
//
|
||||
// The standard log-line format is:
|
||||
// [%D %T] [%L] (%S) %M
|
||||
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||
w := &FileLogWriter{
|
||||
rec: make(chan *LogRecord, LogBufferLength),
|
||||
rot: make(chan bool),
|
||||
filename: fname,
|
||||
format: "[%D %T] [%L] (%S) %M",
|
||||
rotate: rotate,
|
||||
maxbackup: 999,
|
||||
}
|
||||
|
||||
// open the file for the first time
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if w.file != nil {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||
w.file.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.rot:
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
case rec, ok := <-w.rec:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
|
||||
(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
|
||||
(w.daily && now.Day() != w.daily_opendate) {
|
||||
if err := w.intRotate(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the write
|
||||
n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Update the counts
|
||||
w.maxlines_curlines++
|
||||
w.maxsize_cursize += n
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
// Request that the logs rotate
|
||||
func (w *FileLogWriter) Rotate() {
|
||||
w.rot <- true
|
||||
}
|
||||
|
||||
// If this is called in a threaded context, it MUST be synchronized
|
||||
func (w *FileLogWriter) intRotate() error {
|
||||
// Close any log file that may be open
|
||||
if w.file != nil {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
|
||||
w.file.Close()
|
||||
}
|
||||
|
||||
// If we are keeping log files, move it to the next available number
|
||||
if w.rotate {
|
||||
_, err := os.Lstat(w.filename)
|
||||
if err == nil { // file exists
|
||||
// Find the next available number
|
||||
num := 1
|
||||
fname := ""
|
||||
if w.daily && time.Now().Day() != w.daily_opendate {
|
||||
yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
|
||||
|
||||
for ; err == nil && num <= 999; num++ {
|
||||
fname = w.filename + fmt.Sprintf(".%s.%03d", yesterday, num)
|
||||
_, err = os.Lstat(fname)
|
||||
}
|
||||
// return error if the last file checked still existed
|
||||
if err == nil {
|
||||
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
|
||||
}
|
||||
} else {
|
||||
num = w.maxbackup - 1
|
||||
for ; num >= 1; num-- {
|
||||
fname = w.filename + fmt.Sprintf(".%d", num)
|
||||
nfname := w.filename + fmt.Sprintf(".%d", num+1)
|
||||
_, err = os.Lstat(fname)
|
||||
if err == nil {
|
||||
os.Rename(fname, nfname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.file.Close()
|
||||
// Rename the file to its newfound home
|
||||
err = os.Rename(w.filename, fname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Rotate: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the log file
|
||||
fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.file = fd
|
||||
|
||||
now := time.Now()
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))
|
||||
|
||||
// Set the daily open date to the current date
|
||||
w.daily_opendate = now.Day()
|
||||
|
||||
// initialize rotation values
|
||||
w.maxlines_curlines = 0
|
||||
w.maxsize_cursize = 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the logging format (chainable). Must be called before the first log
|
||||
// message is written.
|
||||
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
|
||||
w.format = format
|
||||
return w
|
||||
}
|
||||
|
||||
// Set the logfile header and footer (chainable). Must be called before the first log
|
||||
// message is written. These are formatted similar to the FormatLogRecord (e.g.
|
||||
// you can use %D and %T in your header/footer for date and time).
|
||||
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
|
||||
w.header, w.trailer = head, foot
|
||||
if w.maxlines_curlines == 0 {
|
||||
fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate at linecount (chainable). Must be called before the first log
|
||||
// message is written.
|
||||
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
|
||||
w.maxlines = maxlines
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate at size (chainable). Must be called before the first log message
|
||||
// is written.
|
||||
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
|
||||
w.maxsize = maxsize
|
||||
return w
|
||||
}
|
||||
|
||||
// Set rotate daily (chainable). Must be called before the first log message is
|
||||
// written.
|
||||
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
|
||||
w.daily = daily
|
||||
return w
|
||||
}
|
||||
|
||||
// Set max backup files. Must be called before the first log message
|
||||
// is written.
|
||||
func (w *FileLogWriter) SetRotateMaxBackup(maxbackup int) *FileLogWriter {
|
||||
w.maxbackup = maxbackup
|
||||
return w
|
||||
}
|
||||
|
||||
// SetRotate changes whether or not the old logs are kept. (chainable) Must be
|
||||
// called before the first log message is written. If rotate is false, the
|
||||
// files are overwritten; otherwise, they are rotated to another file before the
|
||||
// new log is opened.
|
||||
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
|
||||
//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
|
||||
w.rotate = rotate
|
||||
return w
|
||||
}
|
||||
|
||||
// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
|
||||
// output XML record log messages instead of line-based ones.
|
||||
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
|
||||
return NewFileLogWriter(fname, rotate).SetFormat(
|
||||
` <record level="%L">
|
||||
<timestamp>%D %T</timestamp>
|
||||
<source>%S</source>
|
||||
<message>%M</message>
|
||||
</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
|
||||
}
|
@ -1,484 +0,0 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
// Package log4go provides level-based and highly configurable logging.
|
||||
//
|
||||
// Enhanced Logging
|
||||
//
|
||||
// This is inspired by the logging functionality in Java. Essentially, you create a Logger
|
||||
// object and create output filters for it. You can send whatever you want to the Logger,
|
||||
// and it will filter that based on your settings and send it to the outputs. This way, you
|
||||
// can put as much debug code in your program as you want, and when you're done you can filter
|
||||
// out the mundane messages so only the important ones show up.
|
||||
//
|
||||
// Utility functions are provided to make life easier. Here is some example code to get started:
|
||||
//
|
||||
// log := log4go.NewLogger()
|
||||
// log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter())
|
||||
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||
//
|
||||
// The first two lines can be combined with the utility NewDefaultLogger:
|
||||
//
|
||||
// log := log4go.NewDefaultLogger(log4go.DEBUG)
|
||||
// log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true))
|
||||
// log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02"))
|
||||
//
|
||||
// Usage notes:
|
||||
// - The ConsoleLogWriter does not display the source of the message to standard
|
||||
// output, but the FileLogWriter does.
|
||||
// - The utility functions (Info, Debug, Warn, etc) derive their source from the
|
||||
// calling function, and this incurs extra overhead.
|
||||
//
|
||||
// Changes from 2.0:
|
||||
// - The external interface has remained mostly stable, but a lot of the
|
||||
// internals have been changed, so if you depended on any of this or created
|
||||
// your own LogWriter, then you will probably have to update your code. In
|
||||
// particular, Logger is now a map and ConsoleLogWriter is now a channel
|
||||
// behind-the-scenes, and the LogWrite method no longer has return values.
|
||||
//
|
||||
// Future work: (please let me know if you think I should work on any of these particularly)
|
||||
// - Log file rotation
|
||||
// - Logging configuration files ala log4j
|
||||
// - Have the ability to remove filters?
|
||||
// - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows
|
||||
// for another method of logging
|
||||
// - Add an XML filter type
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Version information
|
||||
const (
|
||||
L4G_VERSION = "log4go-v3.0.1"
|
||||
L4G_MAJOR = 3
|
||||
L4G_MINOR = 0
|
||||
L4G_BUILD = 1
|
||||
)
|
||||
|
||||
/****** Constants ******/
|
||||
|
||||
// These are the integer logging levels used by the logger
|
||||
type Level int
|
||||
|
||||
const (
|
||||
FINEST Level = iota
|
||||
FINE
|
||||
DEBUG
|
||||
TRACE
|
||||
INFO
|
||||
WARNING
|
||||
ERROR
|
||||
CRITICAL
|
||||
)
|
||||
|
||||
// Logging level strings
|
||||
var (
|
||||
levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"}
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
if l < 0 || int(l) > len(levelStrings) {
|
||||
return "UNKNOWN"
|
||||
}
|
||||
return levelStrings[int(l)]
|
||||
}
|
||||
|
||||
/****** Variables ******/
|
||||
var (
|
||||
// LogBufferLength specifies how many log messages a particular log4go
|
||||
// logger can buffer at a time before writing them.
|
||||
LogBufferLength = 32
|
||||
)
|
||||
|
||||
/****** LogRecord ******/
|
||||
|
||||
// A LogRecord contains all of the pertinent information for each message
|
||||
type LogRecord struct {
|
||||
Level Level // The log level
|
||||
Created time.Time // The time at which the log message was created (nanoseconds)
|
||||
Source string // The message source
|
||||
Message string // The log message
|
||||
}
|
||||
|
||||
/****** LogWriter ******/
|
||||
|
||||
// This is an interface for anything that should be able to write logs
|
||||
type LogWriter interface {
|
||||
// This will be called to log a LogRecord message.
|
||||
LogWrite(rec *LogRecord)
|
||||
|
||||
// This should clean up anything lingering about the LogWriter, as it is called before
|
||||
// the LogWriter is removed. LogWrite should not be called after Close.
|
||||
Close()
|
||||
}
|
||||
|
||||
/****** Logger ******/
|
||||
|
||||
// A Filter represents the log level below which no log records are written to
|
||||
// the associated LogWriter.
|
||||
type Filter struct {
|
||||
Level Level
|
||||
LogWriter
|
||||
}
|
||||
|
||||
// A Logger represents a collection of Filters through which log messages are
|
||||
// written.
|
||||
type Logger map[string]*Filter
|
||||
|
||||
// Create a new logger.
|
||||
//
|
||||
// DEPRECATED: Use make(Logger) instead.
|
||||
func NewLogger() Logger {
|
||||
os.Stderr.WriteString("warning: use of deprecated NewLogger\n")
|
||||
return make(Logger)
|
||||
}
|
||||
|
||||
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||
// or above lvl to standard output.
|
||||
//
|
||||
// DEPRECATED: use NewDefaultLogger instead.
|
||||
func NewConsoleLogger(lvl Level) Logger {
|
||||
os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n")
|
||||
return Logger{
|
||||
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new logger with a "stdout" filter configured to send log messages at
|
||||
// or above lvl to standard output.
|
||||
func NewDefaultLogger(lvl Level) Logger {
|
||||
return Logger{
|
||||
"stdout": &Filter{lvl, NewConsoleLogWriter()},
|
||||
}
|
||||
}
|
||||
|
||||
// Closes all log writers in preparation for exiting the program or a
|
||||
// reconfiguration of logging. Calling this is not really imperative, unless
|
||||
// you want to guarantee that all log messages are written. Close removes
|
||||
// all filters (and thus all LogWriters) from the logger.
|
||||
func (log Logger) Close() {
|
||||
// Close all open loggers
|
||||
for name, filt := range log {
|
||||
filt.Close()
|
||||
delete(log, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new LogWriter to the Logger which will only log messages at lvl or
|
||||
// higher. This function should not be called from multiple goroutines.
|
||||
// Returns the logger for chaining.
|
||||
func (log Logger) AddFilter(name string, lvl Level, writer LogWriter) Logger {
|
||||
log[name] = &Filter{lvl, writer}
|
||||
return log
|
||||
}
|
||||
|
||||
/******* Logging *******/
|
||||
// Send a formatted log message internally
|
||||
func (log Logger) intLogf(lvl Level, format string, args ...interface{}) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine caller func
|
||||
pc, _, lineno, ok := runtime.Caller(2)
|
||||
src := ""
|
||||
if ok {
|
||||
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||
}
|
||||
|
||||
msg := format
|
||||
if len(args) > 0 {
|
||||
msg = fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: src,
|
||||
Message: msg,
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a closure log message internally
|
||||
func (log Logger) intLogc(lvl Level, closure func() string) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Determine caller func
|
||||
pc, _, lineno, ok := runtime.Caller(2)
|
||||
src := ""
|
||||
if ok {
|
||||
src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno)
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: src,
|
||||
Message: closure(),
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a log message with manual level, source, and message.
|
||||
func (log Logger) Log(lvl Level, source, message string) {
|
||||
skip := true
|
||||
|
||||
// Determine if any logging will be done
|
||||
for _, filt := range log {
|
||||
if lvl >= filt.Level {
|
||||
skip = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
// Make the log record
|
||||
rec := &LogRecord{
|
||||
Level: lvl,
|
||||
Created: time.Now(),
|
||||
Source: source,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
// Dispatch the logs
|
||||
for _, filt := range log {
|
||||
if lvl < filt.Level {
|
||||
continue
|
||||
}
|
||||
filt.LogWrite(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Logf logs a formatted log message at the given log level, using the caller as
|
||||
// its source.
|
||||
func (log Logger) Logf(lvl Level, format string, args ...interface{}) {
|
||||
log.intLogf(lvl, format, args...)
|
||||
}
|
||||
|
||||
// Logc logs a string returned by the closure at the given log level, using the caller as
|
||||
// its source. If no log message would be written, the closure is never called.
|
||||
func (log Logger) Logc(lvl Level, closure func() string) {
|
||||
log.intLogc(lvl, closure)
|
||||
}
|
||||
|
||||
// Finest logs a message at the finest log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Finest(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINEST
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fine logs a message at the fine log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Fine(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug is a utility method for debug log messages.
|
||||
// The behavior of Debug depends on the first argument:
|
||||
// - arg0 is a string
|
||||
// When given a string as the first argument, this behaves like Logf but with
|
||||
// the DEBUG log level: the first argument is interpreted as a format for the
|
||||
// latter arguments.
|
||||
// - arg0 is a func()string
|
||||
// When given a closure of type func()string, this logs the string returned by
|
||||
// the closure iff it will be logged. The closure runs at most one time.
|
||||
// - arg0 is interface{}
|
||||
// When given anything else, the log message will be each of the arguments
|
||||
// formatted with %v and separated by spaces (ala Sprint).
|
||||
func (log Logger) Debug(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = DEBUG
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace logs a message at the trace log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Trace(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = TRACE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info logs a message at the info log level.
|
||||
// See Debug for an explanation of the arguments.
|
||||
func (log Logger) Info(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = INFO
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
log.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
log.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn logs a message at the warning log level and returns the formatted error.
|
||||
// At the warning level and higher, there is no performance benefit if the
|
||||
// message is not actually logged, because all formats are processed and all
|
||||
// closures are executed to format the error message.
|
||||
// See Debug for further explanation of the arguments.
|
||||
func (log Logger) Warn(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = WARNING
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
// Error logs a message at the error log level and returns the formatted error,
|
||||
// See Warn for an explanation of the performance and Debug for an explanation
|
||||
// of the parameters.
|
||||
func (log Logger) Error(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = ERROR
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
// Critical logs a message at the critical log level and returns the formatted error,
|
||||
// See Warn for an explanation of the performance and Debug for an explanation
|
||||
// of the parameters.
|
||||
func (log Logger) Critical(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = CRITICAL
|
||||
)
|
||||
var msg string
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
msg = fmt.Sprintf(first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
msg = first()
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
log.intLogf(lvl, msg)
|
||||
return errors.New(msg)
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M"
|
||||
FORMAT_SHORT = "[%t %d] [%L] %M"
|
||||
FORMAT_ABBREV = "[%L] %M"
|
||||
)
|
||||
|
||||
type formatCacheType struct {
|
||||
LastUpdateSeconds int64
|
||||
shortTime, shortDate string
|
||||
longTime, longDate string
|
||||
}
|
||||
|
||||
var formatCache = &formatCacheType{}
|
||||
|
||||
// Known format codes:
|
||||
// %T - Time (15:04:05 MST)
|
||||
// %t - Time (15:04)
|
||||
// %D - Date (2006/01/02)
|
||||
// %d - Date (01/02/06)
|
||||
// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
|
||||
// %S - Source
|
||||
// %M - Message
|
||||
// Ignores unknown formats
|
||||
// Recommended: "[%D %T] [%L] (%S) %M"
|
||||
func FormatLogRecord(format string, rec *LogRecord) string {
|
||||
if rec == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
if len(format) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(make([]byte, 0, 64))
|
||||
secs := rec.Created.UnixNano() / 1e9
|
||||
|
||||
cache := *formatCache
|
||||
if cache.LastUpdateSeconds != secs {
|
||||
month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year()
|
||||
hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second()
|
||||
zone, _ := rec.Created.Zone()
|
||||
updated := &formatCacheType{
|
||||
LastUpdateSeconds: secs,
|
||||
shortTime: fmt.Sprintf("%02d:%02d", hour, minute),
|
||||
shortDate: fmt.Sprintf("%02d/%02d/%02d", day, month, year%100),
|
||||
longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
|
||||
longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day),
|
||||
}
|
||||
cache = *updated
|
||||
formatCache = updated
|
||||
}
|
||||
|
||||
// Split the string into pieces by % signs
|
||||
pieces := bytes.Split([]byte(format), []byte{'%'})
|
||||
|
||||
// Iterate over the pieces, replacing known formats
|
||||
for i, piece := range pieces {
|
||||
if i > 0 && len(piece) > 0 {
|
||||
switch piece[0] {
|
||||
case 'T':
|
||||
out.WriteString(cache.longTime)
|
||||
case 't':
|
||||
out.WriteString(cache.shortTime)
|
||||
case 'D':
|
||||
out.WriteString(cache.longDate)
|
||||
case 'd':
|
||||
out.WriteString(cache.shortDate)
|
||||
case 'L':
|
||||
out.WriteString(levelStrings[rec.Level])
|
||||
case 'S':
|
||||
out.WriteString(rec.Source)
|
||||
case 's':
|
||||
slice := strings.Split(rec.Source, "/")
|
||||
out.WriteString(slice[len(slice)-1])
|
||||
case 'M':
|
||||
out.WriteString(rec.Message)
|
||||
}
|
||||
if len(piece) > 1 {
|
||||
out.Write(piece[1:])
|
||||
}
|
||||
} else if len(piece) > 0 {
|
||||
out.Write(piece)
|
||||
}
|
||||
}
|
||||
out.WriteByte('\n')
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// This is the standard writer that prints to standard output.
|
||||
type FormatLogWriter chan *LogRecord
|
||||
|
||||
// This creates a new FormatLogWriter
|
||||
func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter {
|
||||
records := make(FormatLogWriter, LogBufferLength)
|
||||
go records.run(out, format)
|
||||
return records
|
||||
}
|
||||
|
||||
func (w FormatLogWriter) run(out io.Writer, format string) {
|
||||
for rec := range w {
|
||||
fmt.Fprint(out, FormatLogRecord(format, rec))
|
||||
}
|
||||
}
|
||||
|
||||
// This is the FormatLogWriter's output method. This will block if the output
|
||||
// buffer is full.
|
||||
func (w FormatLogWriter) LogWrite(rec *LogRecord) {
|
||||
w <- rec
|
||||
}
|
||||
|
||||
// Close stops the logger from sending messages to standard output. Attempts to
|
||||
// send log messages to this logger after a Close have undefined behavior.
|
||||
func (w FormatLogWriter) Close() {
|
||||
close(w)
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
// This log writer sends output to a socket
|
||||
type SocketLogWriter chan *LogRecord
|
||||
|
||||
// This is the SocketLogWriter's output method
|
||||
func (w SocketLogWriter) LogWrite(rec *LogRecord) {
|
||||
w <- rec
|
||||
}
|
||||
|
||||
func (w SocketLogWriter) Close() {
|
||||
close(w)
|
||||
}
|
||||
|
||||
func NewSocketLogWriter(proto, hostport string) SocketLogWriter {
|
||||
sock, err := net.Dial(proto, hostport)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
w := SocketLogWriter(make(chan *LogRecord, LogBufferLength))
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if sock != nil && proto == "tcp" {
|
||||
sock.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for rec := range w {
|
||||
// Marshall into JSON
|
||||
js, err := json.Marshal(rec)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = sock.Write(js)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return w
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var stdout io.Writer = os.Stdout
|
||||
|
||||
// This is the standard writer that prints to standard output.
|
||||
type ConsoleLogWriter struct {
|
||||
format string
|
||||
w chan *LogRecord
|
||||
}
|
||||
|
||||
// This creates a new ConsoleLogWriter
|
||||
func NewConsoleLogWriter() *ConsoleLogWriter {
|
||||
consoleWriter := &ConsoleLogWriter{
|
||||
format: "[%T %D] [%L] (%S) %M",
|
||||
w: make(chan *LogRecord, LogBufferLength),
|
||||
}
|
||||
go consoleWriter.run(stdout)
|
||||
return consoleWriter
|
||||
}
|
||||
func (c *ConsoleLogWriter) SetFormat(format string) {
|
||||
c.format = format
|
||||
}
|
||||
func (c *ConsoleLogWriter) run(out io.Writer) {
|
||||
for rec := range c.w {
|
||||
fmt.Fprint(out, FormatLogRecord(c.format, rec))
|
||||
}
|
||||
}
|
||||
|
||||
// This is the ConsoleLogWriter's output method. This will block if the output
|
||||
// buffer is full.
|
||||
func (c *ConsoleLogWriter) LogWrite(rec *LogRecord) {
|
||||
c.w <- rec
|
||||
}
|
||||
|
||||
// Close stops the logger from sending messages to standard output. Attempts to
|
||||
// send log messages to this logger after a Close have undefined behavior.
|
||||
func (c *ConsoleLogWriter) Close() {
|
||||
close(c.w)
|
||||
time.Sleep(50 * time.Millisecond) // Try to give console I/O time to complete
|
||||
}
|
@ -1,278 +0,0 @@
|
||||
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
|
||||
|
||||
package log4go
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
Global Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
Global = NewDefaultLogger(DEBUG)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).LoadConfiguration
|
||||
func LoadConfiguration(filename string) {
|
||||
Global.LoadConfiguration(filename)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).AddFilter
|
||||
func AddFilter(name string, lvl Level, writer LogWriter) {
|
||||
Global.AddFilter(name, lvl, writer)
|
||||
}
|
||||
|
||||
// Wrapper for (*Logger).Close (closes and removes all logwriters)
|
||||
func Close() {
|
||||
Global.Close()
|
||||
}
|
||||
|
||||
func Crash(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
panic(args)
|
||||
}
|
||||
|
||||
// Logs the given message and crashes the program
|
||||
func Crashf(format string, args ...interface{}) {
|
||||
Global.intLogf(CRITICAL, format, args...)
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Exit(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Exitf(format string, args ...interface{}) {
|
||||
Global.intLogf(ERROR, format, args...)
|
||||
Global.Close() // so that hopefully the messages get logged
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stderr(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stderrf(format string, args ...interface{}) {
|
||||
Global.intLogf(ERROR, format, args...)
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stdout(args ...interface{}) {
|
||||
if len(args) > 0 {
|
||||
Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Compatibility with `log`
|
||||
func Stdoutf(format string, args ...interface{}) {
|
||||
Global.intLogf(INFO, format, args...)
|
||||
}
|
||||
|
||||
// Send a log message manually
|
||||
// Wrapper for (*Logger).Log
|
||||
func Log(lvl Level, source, message string) {
|
||||
Global.Log(lvl, source, message)
|
||||
}
|
||||
|
||||
// Send a formatted log message easily
|
||||
// Wrapper for (*Logger).Logf
|
||||
func Logf(lvl Level, format string, args ...interface{}) {
|
||||
Global.intLogf(lvl, format, args...)
|
||||
}
|
||||
|
||||
// Send a closure log message
|
||||
// Wrapper for (*Logger).Logc
|
||||
func Logc(lvl Level, closure func() string) {
|
||||
Global.intLogc(lvl, closure)
|
||||
}
|
||||
|
||||
// Utility for finest log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Finest
|
||||
func Finest(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINEST
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for fine log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Fine
|
||||
func Fine(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = FINE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for debug log messages
|
||||
// When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments)
|
||||
// When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time.
|
||||
// When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint).
|
||||
// Wrapper for (*Logger).Debug
|
||||
func Debug(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = DEBUG
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for trace log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Trace
|
||||
func Trace(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = TRACE
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for info log messages (see Debug() for parameter explanation)
|
||||
// Wrapper for (*Logger).Info
|
||||
func Info(arg0 interface{}, args ...interface{}) {
|
||||
const (
|
||||
lvl = INFO
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
Global.intLogc(lvl, first)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Warn
|
||||
func Warn(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = WARNING
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Error
|
||||
func Error(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = ERROR
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation)
|
||||
// These functions will execute a closure exactly once, to build the error message for the return
|
||||
// Wrapper for (*Logger).Critical
|
||||
func Critical(arg0 interface{}, args ...interface{}) error {
|
||||
const (
|
||||
lvl = CRITICAL
|
||||
)
|
||||
switch first := arg0.(type) {
|
||||
case string:
|
||||
// Use the string as a format string
|
||||
Global.intLogf(lvl, first, args...)
|
||||
return errors.New(fmt.Sprintf(first, args...))
|
||||
case func() string:
|
||||
// Log the closure (no other arguments used)
|
||||
str := first()
|
||||
Global.intLogf(lvl, "%s", str)
|
||||
return errors.New(str)
|
||||
default:
|
||||
// Build a format string so that it will be similar to Sprint
|
||||
Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...)
|
||||
return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...))
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
.idea/
|
||||
coverage.out
|
||||
tmp/
|
||||
|
@ -1,5 +1,6 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- tip
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- tip
|
||||
|
@ -0,0 +1,27 @@
|
||||
package tgbotapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
stdlog "log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// BotLogger is an interface that represents the required methods to log data.
|
||||
//
|
||||
// Instead of requiring the standard logger, we can just specify the methods we
|
||||
// use and allow users to pass anything that implements these.
|
||||
type BotLogger interface {
|
||||
Println(v ...interface{})
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
var log BotLogger = stdlog.New(os.Stderr, "", stdlog.LstdFlags)
|
||||
|
||||
// SetLogger specifies the logger that the package should use.
|
||||
func SetLogger(logger BotLogger) error {
|
||||
if logger == nil {
|
||||
return errors.New("logger is nil")
|
||||
}
|
||||
log = logger
|
||||
return nil
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
package tgbotapi
|
||||
|
||||
// PassportRequestInfoConfig allows you to request passport info
|
||||
type PassportRequestInfoConfig struct {
|
||||
BotID int `json:"bot_id"`
|
||||
Scope *PassportScope `json:"scope"`
|
||||
Nonce string `json:"nonce"`
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
// PassportScopeElement supports using one or one of several elements.
|
||||
type PassportScopeElement interface {
|
||||
ScopeType() string
|
||||
}
|
||||
|
||||
// PassportScope is the requested scopes of data.
|
||||
type PassportScope struct {
|
||||
V int `json:"v"`
|
||||
Data []PassportScopeElement `json:"data"`
|
||||
}
|
||||
|
||||
// PassportScopeElementOneOfSeveral allows you to request any one of the
|
||||
// requested documents.
|
||||
type PassportScopeElementOneOfSeveral struct {
|
||||
}
|
||||
|
||||
// ScopeType is the scope type.
|
||||
func (eo *PassportScopeElementOneOfSeveral) ScopeType() string {
|
||||
return "one_of"
|
||||
}
|
||||
|
||||
// PassportScopeElementOne requires the specified element be provided.
|
||||
type PassportScopeElementOne struct {
|
||||
Type string `json:"type"` // One of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”, “phone_number”, “email”
|
||||
Selfie bool `json:"selfie"`
|
||||
Translation bool `json:"translation"`
|
||||
NativeNames bool `json:"native_name"`
|
||||
}
|
||||
|
||||
// ScopeType is the scope type.
|
||||
func (eo *PassportScopeElementOne) ScopeType() string {
|
||||
return "one"
|
||||
}
|
||||
|
||||
type (
|
||||
// PassportData contains information about Telegram Passport data shared with
|
||||
// the bot by the user.
|
||||
PassportData struct {
|
||||
// Array with information about documents and other Telegram Passport
|
||||
// elements that was shared with the bot
|
||||
Data []EncryptedPassportElement `json:"data"`
|
||||
|
||||
// Encrypted credentials required to decrypt the data
|
||||
Credentials *EncryptedCredentials `json:"credentials"`
|
||||
}
|
||||
|
||||
// PassportFile represents a file uploaded to Telegram Passport. Currently all
|
||||
// Telegram Passport files are in JPEG format when decrypted and don't exceed
|
||||
// 10MB.
|
||||
PassportFile struct {
|
||||
// Unique identifier for this file
|
||||
FileID string `json:"file_id"`
|
||||
|
||||
// File size
|
||||
FileSize int `json:"file_size"`
|
||||
|
||||
// Unix time when the file was uploaded
|
||||
FileDate int64 `json:"file_date"`
|
||||
}
|
||||
|
||||
// EncryptedPassportElement contains information about documents or other
|
||||
// Telegram Passport elements shared with the bot by the user.
|
||||
EncryptedPassportElement struct {
|
||||
// Element type.
|
||||
Type string `json:"type"`
|
||||
|
||||
// Base64-encoded encrypted Telegram Passport element data provided by
|
||||
// the user, available for "personal_details", "passport",
|
||||
// "driver_license", "identity_card", "identity_passport" and "address"
|
||||
// types. Can be decrypted and verified using the accompanying
|
||||
// EncryptedCredentials.
|
||||
Data string `json:"data,omitempty"`
|
||||
|
||||
// User's verified phone number, available only for "phone_number" type
|
||||
PhoneNumber string `json:"phone_number,omitempty"`
|
||||
|
||||
// User's verified email address, available only for "email" type
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
// Array of encrypted files with documents provided by the user,
|
||||
// available for "utility_bill", "bank_statement", "rental_agreement",
|
||||
// "passport_registration" and "temporary_registration" types. Files can
|
||||
// be decrypted and verified using the accompanying EncryptedCredentials.
|
||||
Files []PassportFile `json:"files,omitempty"`
|
||||
|
||||
// Encrypted file with the front side of the document, provided by the
|
||||
// user. Available for "passport", "driver_license", "identity_card" and
|
||||
// "internal_passport". The file can be decrypted and verified using the
|
||||
// accompanying EncryptedCredentials.
|
||||
FrontSide *PassportFile `json:"front_side,omitempty"`
|
||||
|
||||
// Encrypted file with the reverse side of the document, provided by the
|
||||
// user. Available for "driver_license" and "identity_card". The file can
|
||||
// be decrypted and verified using the accompanying EncryptedCredentials.
|
||||
ReverseSide *PassportFile `json:"reverse_side,omitempty"`
|
||||
|
||||
// Encrypted file with the selfie of the user holding a document,
|
||||
// provided by the user; available for "passport", "driver_license",
|
||||
// "identity_card" and "internal_passport". The file can be decrypted
|
||||
// and verified using the accompanying EncryptedCredentials.
|
||||
Selfie *PassportFile `json:"selfie,omitempty"`
|
||||
}
|
||||
|
||||
// EncryptedCredentials contains data required for decrypting and
|
||||
// authenticating EncryptedPassportElement. See the Telegram Passport
|
||||
// Documentation for a complete description of the data decryption and
|
||||
// authentication processes.
|
||||
EncryptedCredentials struct {
|
||||
// Base64-encoded encrypted JSON-serialized data with unique user's
|
||||
// payload, data hashes and secrets required for EncryptedPassportElement
|
||||
// decryption and authentication
|
||||
Data string `json:"data"`
|
||||
|
||||
// Base64-encoded data hash for data authentication
|
||||
Hash string `json:"hash"`
|
||||
|
||||
// Base64-encoded secret, encrypted with the bot's public RSA key,
|
||||
// required for data decryption
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
// PassportElementError represents an error in the Telegram Passport element
|
||||
// which was submitted that should be resolved by the user.
|
||||
PassportElementError interface{}
|
||||
|
||||
// PassportElementErrorDataField represents an issue in one of the data
|
||||
// fields that was provided by the user. The error is considered resolved
|
||||
// when the field's value changes.
|
||||
PassportElementErrorDataField struct {
|
||||
// Error source, must be data
|
||||
Source string `json:"source"`
|
||||
|
||||
// The section of the user's Telegram Passport which has the error, one
|
||||
// of "personal_details", "passport", "driver_license", "identity_card",
|
||||
// "internal_passport", "address"
|
||||
Type string `json:"type"`
|
||||
|
||||
// Name of the data field which has the error
|
||||
FieldName string `json:"field_name"`
|
||||
|
||||
// Base64-encoded data hash
|
||||
DataHash string `json:"data_hash"`
|
||||
|
||||
// Error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PassportElementErrorFrontSide represents an issue with the front side of
|
||||
// a document. The error is considered resolved when the file with the front
|
||||
// side of the document changes.
|
||||
PassportElementErrorFrontSide struct {
|
||||
// Error source, must be front_side
|
||||
Source string `json:"source"`
|
||||
|
||||
// The section of the user's Telegram Passport which has the issue, one
|
||||
// of "passport", "driver_license", "identity_card", "internal_passport"
|
||||
Type string `json:"type"`
|
||||
|
||||
// Base64-encoded hash of the file with the front side of the document
|
||||
FileHash string `json:"file_hash"`
|
||||
|
||||
// Error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PassportElementErrorReverseSide represents an issue with the reverse side
|
||||
// of a document. The error is considered resolved when the file with reverse
|
||||
// side of the document changes.
|
||||
PassportElementErrorReverseSide struct {
|
||||
// Error source, must be reverse_side
|
||||
Source string `json:"source"`
|
||||
|
||||
// The section of the user's Telegram Passport which has the issue, one
|
||||
// of "driver_license", "identity_card"
|
||||
Type string `json:"type"`
|
||||
|
||||
// Base64-encoded hash of the file with the reverse side of the document
|
||||
FileHash string `json:"file_hash"`
|
||||
|
||||
// Error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PassportElementErrorSelfie represents an issue with the selfie with a
|
||||
// document. The error is considered resolved when the file with the selfie
|
||||
// changes.
|
||||
PassportElementErrorSelfie struct {
|
||||
// Error source, must be selfie
|
||||
Source string `json:"source"`
|
||||
|
||||
// The section of the user's Telegram Passport which has the issue, one
|
||||
// of "passport", "driver_license", "identity_card", "internal_passport"
|
||||
Type string `json:"type"`
|
||||
|
||||
// Base64-encoded hash of the file with the selfie
|
||||
FileHash string `json:"file_hash"`
|
||||
|
||||
// Error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PassportElementErrorFile represents an issue with a document scan. The
|
||||
// error is considered resolved when the file with the document scan changes.
|
||||
PassportElementErrorFile struct {
|
||||
// Error source, must be file
|
||||
Source string `json:"source"`
|
||||
|
||||
// The section of the user's Telegram Passport which has the issue, one
|
||||
// of "utility_bill", "bank_statement", "rental_agreement",
|
||||
// "passport_registration", "temporary_registration"
|
||||
Type string `json:"type"`
|
||||
|
||||
// Base64-encoded file hash
|
||||
FileHash string `json:"file_hash"`
|
||||
|
||||
// Error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PassportElementErrorFiles represents an issue with a list of scans. The
|
||||
// error is considered resolved when the list of files containing the scans
|
||||
// changes.
|
||||
PassportElementErrorFiles struct {
|
||||
// Error source, must be files
|
||||
Source string `json:"source"`
|
||||
|
||||
// The section of the user's Telegram Passport which has the issue, one
|
||||
// of "utility_bill", "bank_statement", "rental_agreement",
|
||||
// "passport_registration", "temporary_registration"
|
||||
Type string `json:"type"`
|
||||
|
||||
// List of base64-encoded file hashes
|
||||
FileHashes []string `json:"file_hashes"`
|
||||
|
||||
// Error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Credentials contains encrypted data.
|
||||
Credentials struct {
|
||||
Data SecureData `json:"secure_data"`
|
||||
// Nonce the same nonce given in the request
|
||||
Nonce string `json:"nonce"`
|
||||
}
|
||||
|
||||
// SecureData is a map of the fields and their encrypted values.
|
||||
SecureData map[string]*SecureValue
|
||||
// PersonalDetails *SecureValue `json:"personal_details"`
|
||||
// Passport *SecureValue `json:"passport"`
|
||||
// InternalPassport *SecureValue `json:"internal_passport"`
|
||||
// DriverLicense *SecureValue `json:"driver_license"`
|
||||
// IdentityCard *SecureValue `json:"identity_card"`
|
||||
// Address *SecureValue `json:"address"`
|
||||
// UtilityBill *SecureValue `json:"utility_bill"`
|
||||
// BankStatement *SecureValue `json:"bank_statement"`
|
||||
// RentalAgreement *SecureValue `json:"rental_agreement"`
|
||||
// PassportRegistration *SecureValue `json:"passport_registration"`
|
||||
// TemporaryRegistration *SecureValue `json:"temporary_registration"`
|
||||
|
||||
// SecureValue contains encrypted values for a SecureData item.
|
||||
SecureValue struct {
|
||||
Data *DataCredentials `json:"data"`
|
||||
FrontSide *FileCredentials `json:"front_side"`
|
||||
ReverseSide *FileCredentials `json:"reverse_side"`
|
||||
Selfie *FileCredentials `json:"selfie"`
|
||||
Translation []*FileCredentials `json:"translation"`
|
||||
Files []*FileCredentials `json:"files"`
|
||||
}
|
||||
|
||||
// DataCredentials contains information required to decrypt data.
|
||||
DataCredentials struct {
|
||||
// DataHash checksum of encrypted data
|
||||
DataHash string `json:"data_hash"`
|
||||
// Secret of encrypted data
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
// FileCredentials contains information required to decrypt files.
|
||||
FileCredentials struct {
|
||||
// FileHash checksum of encrypted data
|
||||
FileHash string `json:"file_hash"`
|
||||
// Secret of encrypted data
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
// PersonalDetails https://core.telegram.org/passport#personaldetails
|
||||
PersonalDetails struct {
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
MiddleName string `json:"middle_name"`
|
||||
BirthDate string `json:"birth_date"`
|
||||
Gender string `json:"gender"`
|
||||
CountryCode string `json:"country_code"`
|
||||
ResidenceCountryCode string `json:"residence_country_code"`
|
||||
FirstNameNative string `json:"first_name_native"`
|
||||
LastNameNative string `json:"last_name_native"`
|
||||
MiddleNameNative string `json:"middle_name_native"`
|
||||
}
|
||||
|
||||
// IDDocumentData https://core.telegram.org/passport#iddocumentdata
|
||||
IDDocumentData struct {
|
||||
DocumentNumber string `json:"document_no"`
|
||||
ExpiryDate string `json:"expiry_date"`
|
||||
}
|
||||
)
|
@ -0,0 +1,21 @@
|
||||
// Package lru provides three different LRU caches of varying sophistication.
|
||||
//
|
||||
// Cache is a simple LRU cache. It is based on the
|
||||
// LRU implementation in groupcache:
|
||||
// https://github.com/golang/groupcache/tree/master/lru
|
||||
//
|
||||
// TwoQueueCache tracks frequently used and recently used entries separately.
|
||||
// This avoids a burst of accesses from taking out frequently used entries,
|
||||
// at the cost of about 2x computational overhead and some extra bookkeeping.
|
||||
//
|
||||
// ARCCache is an adaptive replacement cache. It tracks recent evictions as
|
||||
// well as recent usage in both the frequent and recent caches. Its
|
||||
// computational overhead is comparable to TwoQueueCache, but the memory
|
||||
// overhead is linear with the size of the cache.
|
||||
//
|
||||
// ARC has been patented by IBM, so do not use it if that is problematic for
|
||||
// your program.
|
||||
//
|
||||
// All caches in this package take locks while operating, and are therefore
|
||||
// thread-safe for consumers.
|
||||
package lru
|
@ -0,0 +1 @@
|
||||
module github.com/hashicorp/golang-lru
|
@ -0,0 +1,36 @@
|
||||
package simplelru
|
||||
|
||||
// LRUCache is the interface for simple LRU cache.
|
||||
type LRUCache interface {
|
||||
// Adds a value to the cache, returns true if an eviction occurred and
|
||||
// updates the "recently used"-ness of the key.
|
||||
Add(key, value interface{}) bool
|
||||
|
||||
// Returns key's value from the cache and
|
||||
// updates the "recently used"-ness of the key. #value, isFound
|
||||
Get(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Check if a key exsists in cache without updating the recent-ness.
|
||||
Contains(key interface{}) (ok bool)
|
||||
|
||||
// Returns key's value without updating the "recently used"-ness of the key.
|
||||
Peek(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Removes a key from the cache.
|
||||
Remove(key interface{}) bool
|
||||
|
||||
// Removes the oldest entry from cache.
|
||||
RemoveOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns the oldest entry from the cache. #key, value, isFound
|
||||
GetOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns a slice of the keys in the cache, from oldest to newest.
|
||||
Keys() []interface{}
|
||||
|
||||
// Returns the number of items in the cache.
|
||||
Len() int
|
||||
|
||||
// Clear all cache entries
|
||||
Purge()
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
module github.com/hashicorp/hcl
|
||||
|
||||
require github.com/davecgh/go-spew v1.1.1
|
@ -0,0 +1,2 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
@ -1,25 +0,0 @@
|
||||
// +build go1.8
|
||||
|
||||
package echo
|
||||
|
||||
import (
|
||||
stdContext "context"
|
||||
)
|
||||
|
||||
// Close immediately stops the server.
|
||||
// It internally calls `http.Server#Close()`.
|
||||
func (e *Echo) Close() error {
|
||||
if err := e.TLSServer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Server.Close()
|
||||
}
|
||||
|
||||
// Shutdown stops server the gracefully.
|
||||
// It internally calls `http.Server#Shutdown()`.
|
||||
func (e *Echo) Shutdown(ctx stdContext.Context) error {
|
||||
if err := e.TLSServer.Shutdown(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return e.Server.Shutdown(ctx)
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// +build go1.7, !go1.8
|
||||
|
||||
package echo
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// PathUnescape is wraps `url.QueryUnescape`
|
||||
func PathUnescape(s string) (string, error) {
|
||||
return url.QueryUnescape(s)
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
// +build go1.8
|
||||
|
||||
package echo
|
||||
|
||||
import "net/url"
|
||||
|
||||
// PathUnescape is wraps `url.PathUnescape`
|
||||
func PathUnescape(s string) (string, error) {
|
||||
return url.PathUnescape(s)
|
||||
}
|
@ -0,0 +1 @@
|
||||
module github.com/lrstanley/girc
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package mlog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// defaultLog manually encodes the log to STDOUT, providing a basic, default logging implementation
|
||||
// before mlog is fully configured.
|
||||
func defaultLog(level, msg string, fields ...Field) {
|
||||
log := struct {
|
||||
Level string `json:"level"`
|
||||
Message string `json:"msg"`
|
||||
Fields []Field `json:"fields,omitempty"`
|
||||
}{
|
||||
level,
|
||||
msg,
|
||||
fields,
|
||||
}
|
||||
|
||||
if b, err := json.Marshal(log); err != nil {
|
||||
fmt.Printf(`{"level":"error","msg":"failed to encode log message"}%s`, "\n")
|
||||
} else {
|
||||
fmt.Printf("%s\n", b)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultDebugLog(msg string, fields ...Field) {
|
||||
defaultLog("debug", msg, fields...)
|
||||
}
|
||||
|
||||
func defaultInfoLog(msg string, fields ...Field) {
|
||||
defaultLog("info", msg, fields...)
|
||||
}
|
||||
|
||||
func defaultWarnLog(msg string, fields ...Field) {
|
||||
defaultLog("warn", msg, fields...)
|
||||
}
|
||||
|
||||
func defaultErrorLog(msg string, fields ...Field) {
|
||||
defaultLog("error", msg, fields...)
|
||||
}
|
||||
|
||||
func defaultCriticalLog(msg string, fields ...Field) {
|
||||
// We map critical to error in zap, so be consistent.
|
||||
defaultLog("error", msg, fields...)
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package mlog
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
var globalLogger *Logger
|
||||
|
||||
func InitGlobalLogger(logger *Logger) {
|
||||
glob := *logger
|
||||
glob.zap = glob.zap.WithOptions(zap.AddCallerSkip(1))
|
||||
globalLogger = &glob
|
||||
Debug = globalLogger.Debug
|
||||
Info = globalLogger.Info
|
||||
Warn = globalLogger.Warn
|
||||
Error = globalLogger.Error
|
||||
Critical = globalLogger.Critical
|
||||
}
|
||||
|
||||
func RedirectStdLog(logger *Logger) {
|
||||
zap.RedirectStdLogAt(logger.zap.With(zap.String("source", "stdlog")).WithOptions(zap.AddCallerSkip(-2)), zapcore.ErrorLevel)
|
||||
}
|
||||
|
||||
type LogFunc func(string, ...Field)
|
||||
|
||||
// DON'T USE THIS Modify the level on the app logger
|
||||
func GloballyDisableDebugLogForTest() {
|
||||
globalLogger.consoleLevel.SetLevel(zapcore.ErrorLevel)
|
||||
}
|
||||
|
||||
// DON'T USE THIS Modify the level on the app logger
|
||||
func GloballyEnableDebugLogForTest() {
|
||||
globalLogger.consoleLevel.SetLevel(zapcore.DebugLevel)
|
||||
}
|
||||
|
||||
var Debug LogFunc = defaultDebugLog
|
||||
var Info LogFunc = defaultInfoLog
|
||||
var Warn LogFunc = defaultWarnLog
|
||||
var Error LogFunc = defaultErrorLog
|
||||
var Critical LogFunc = defaultCriticalLog
|
@ -0,0 +1,173 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package mlog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// Very verbose messages for debugging specific issues
|
||||
LevelDebug = "debug"
|
||||
// Default log level, informational
|
||||
LevelInfo = "info"
|
||||
// Warnings are messages about possible issues
|
||||
LevelWarn = "warn"
|
||||
// Errors are messages about things we know are problems
|
||||
LevelError = "error"
|
||||
)
|
||||
|
||||
// Type and function aliases from zap to limit the libraries scope into MM code
|
||||
type Field = zapcore.Field
|
||||
|
||||
var Int64 = zap.Int64
|
||||
var Int = zap.Int
|
||||
var Uint32 = zap.Uint32
|
||||
var String = zap.String
|
||||
var Any = zap.Any
|
||||
var Err = zap.Error
|
||||
var Bool = zap.Bool
|
||||
|
||||
type LoggerConfiguration struct {
|
||||
EnableConsole bool
|
||||
ConsoleJson bool
|
||||
ConsoleLevel string
|
||||
EnableFile bool
|
||||
FileJson bool
|
||||
FileLevel string
|
||||
FileLocation string
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
zap *zap.Logger
|
||||
consoleLevel zap.AtomicLevel
|
||||
fileLevel zap.AtomicLevel
|
||||
}
|
||||
|
||||
func getZapLevel(level string) zapcore.Level {
|
||||
switch level {
|
||||
case LevelInfo:
|
||||
return zapcore.InfoLevel
|
||||
case LevelWarn:
|
||||
return zapcore.WarnLevel
|
||||
case LevelDebug:
|
||||
return zapcore.DebugLevel
|
||||
case LevelError:
|
||||
return zapcore.ErrorLevel
|
||||
default:
|
||||
return zapcore.InfoLevel
|
||||
}
|
||||
}
|
||||
|
||||
func makeEncoder(json bool) zapcore.Encoder {
|
||||
encoderConfig := zap.NewProductionEncoderConfig()
|
||||
if json {
|
||||
return zapcore.NewJSONEncoder(encoderConfig)
|
||||
}
|
||||
|
||||
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
return zapcore.NewConsoleEncoder(encoderConfig)
|
||||
}
|
||||
|
||||
func NewLogger(config *LoggerConfiguration) *Logger {
|
||||
cores := []zapcore.Core{}
|
||||
logger := &Logger{
|
||||
consoleLevel: zap.NewAtomicLevelAt(getZapLevel(config.ConsoleLevel)),
|
||||
fileLevel: zap.NewAtomicLevelAt(getZapLevel(config.FileLevel)),
|
||||
}
|
||||
|
||||
if config.EnableConsole {
|
||||
writer := zapcore.Lock(os.Stdout)
|
||||
core := zapcore.NewCore(makeEncoder(config.ConsoleJson), writer, logger.consoleLevel)
|
||||
cores = append(cores, core)
|
||||
}
|
||||
|
||||
if config.EnableFile {
|
||||
writer := zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: config.FileLocation,
|
||||
MaxSize: 100,
|
||||
Compress: true,
|
||||
})
|
||||
core := zapcore.NewCore(makeEncoder(config.FileJson), writer, logger.fileLevel)
|
||||
cores = append(cores, core)
|
||||
}
|
||||
|
||||
combinedCore := zapcore.NewTee(cores...)
|
||||
|
||||
logger.zap = zap.New(combinedCore,
|
||||
zap.AddCallerSkip(1),
|
||||
zap.AddCaller(),
|
||||
)
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
func (l *Logger) ChangeLevels(config *LoggerConfiguration) {
|
||||
l.consoleLevel.SetLevel(getZapLevel(config.ConsoleLevel))
|
||||
l.fileLevel.SetLevel(getZapLevel(config.FileLevel))
|
||||
}
|
||||
|
||||
func (l *Logger) SetConsoleLevel(level string) {
|
||||
l.consoleLevel.SetLevel(getZapLevel(level))
|
||||
}
|
||||
|
||||
func (l *Logger) With(fields ...Field) *Logger {
|
||||
newlogger := *l
|
||||
newlogger.zap = newlogger.zap.With(fields...)
|
||||
return &newlogger
|
||||
}
|
||||
|
||||
func (l *Logger) StdLog(fields ...Field) *log.Logger {
|
||||
return zap.NewStdLog(l.With(fields...).zap.WithOptions(getStdLogOption()))
|
||||
}
|
||||
|
||||
// StdLogWriter returns a writer that can be hooked up to the output of a golang standard logger
|
||||
// anything written will be interpreted as log entries accordingly
|
||||
func (l *Logger) StdLogWriter() io.Writer {
|
||||
newLogger := *l
|
||||
newLogger.zap = newLogger.zap.WithOptions(zap.AddCallerSkip(4), getStdLogOption())
|
||||
f := newLogger.Info
|
||||
return &loggerWriter{f}
|
||||
}
|
||||
|
||||
func (l *Logger) WithCallerSkip(skip int) *Logger {
|
||||
newlogger := *l
|
||||
newlogger.zap = newlogger.zap.WithOptions(zap.AddCallerSkip(skip))
|
||||
return &newlogger
|
||||
}
|
||||
|
||||
// Made for the plugin interface, wraps mlog in a simpler interface
|
||||
// at the cost of performance
|
||||
func (l *Logger) Sugar() *SugarLogger {
|
||||
return &SugarLogger{
|
||||
wrappedLogger: l,
|
||||
zapSugar: l.zap.Sugar(),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(message string, fields ...Field) {
|
||||
l.zap.Debug(message, fields...)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(message string, fields ...Field) {
|
||||
l.zap.Info(message, fields...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(message string, fields ...Field) {
|
||||
l.zap.Warn(message, fields...)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(message string, fields ...Field) {
|
||||
l.zap.Error(message, fields...)
|
||||
}
|
||||
|
||||
func (l *Logger) Critical(message string, fields ...Field) {
|
||||
l.zap.Error(message, fields...)
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package mlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// Implementation of zapcore.Core to interpret log messages from a standard logger
|
||||
// and translate the levels to zapcore levels.
|
||||
type stdLogLevelInterpreterCore struct {
|
||||
wrappedCore zapcore.Core
|
||||
}
|
||||
|
||||
func stdLogInterpretZapEntry(entry zapcore.Entry) zapcore.Entry {
|
||||
message := entry.Message
|
||||
if strings.Index(message, "[DEBUG]") == 0 {
|
||||
entry.Level = zapcore.DebugLevel
|
||||
entry.Message = message[7:]
|
||||
} else if strings.Index(message, "[DEBG]") == 0 {
|
||||
entry.Level = zapcore.DebugLevel
|
||||
entry.Message = message[6:]
|
||||
} else if strings.Index(message, "[WARN]") == 0 {
|
||||
entry.Level = zapcore.WarnLevel
|
||||
entry.Message = message[6:]
|
||||
} else if strings.Index(message, "[ERROR]") == 0 {
|
||||
entry.Level = zapcore.ErrorLevel
|
||||
entry.Message = message[7:]
|
||||
} else if strings.Index(message, "[EROR]") == 0 {
|
||||
entry.Level = zapcore.ErrorLevel
|
||||
entry.Message = message[6:]
|
||||
} else if strings.Index(message, "[ERR]") == 0 {
|
||||
entry.Level = zapcore.ErrorLevel
|
||||
entry.Message = message[5:]
|
||||
} else if strings.Index(message, "[INFO]") == 0 {
|
||||
entry.Level = zapcore.InfoLevel
|
||||
entry.Message = message[6:]
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
func (s *stdLogLevelInterpreterCore) Enabled(lvl zapcore.Level) bool {
|
||||
return s.wrappedCore.Enabled(lvl)
|
||||
}
|
||||
|
||||
func (s *stdLogLevelInterpreterCore) With(fields []zapcore.Field) zapcore.Core {
|
||||
return s.wrappedCore.With(fields)
|
||||
}
|
||||
|
||||
func (s *stdLogLevelInterpreterCore) Check(entry zapcore.Entry, checkedEntry *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||
entry = stdLogInterpretZapEntry(entry)
|
||||
return s.wrappedCore.Check(entry, checkedEntry)
|
||||
}
|
||||
|
||||
func (s *stdLogLevelInterpreterCore) Write(entry zapcore.Entry, fields []zapcore.Field) error {
|
||||
entry = stdLogInterpretZapEntry(entry)
|
||||
return s.wrappedCore.Write(entry, fields)
|
||||
}
|
||||
|
||||
func (s *stdLogLevelInterpreterCore) Sync() error {
|
||||
return s.wrappedCore.Sync()
|
||||
}
|
||||
|
||||
func getStdLogOption() zap.Option {
|
||||
return zap.WrapCore(
|
||||
func(core zapcore.Core) zapcore.Core {
|
||||
return &stdLogLevelInterpreterCore{core}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type loggerWriter struct {
|
||||
logFunc func(msg string, fields ...Field)
|
||||
}
|
||||
|
||||
func (l *loggerWriter) Write(p []byte) (int, error) {
|
||||
trimmed := string(bytes.TrimSpace(p))
|
||||
for _, line := range strings.Split(trimmed, "\n") {
|
||||
l.logFunc(string(line))
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package mlog
|
||||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
// Made for the plugin interface, use the regular logger for other uses
|
||||
type SugarLogger struct {
|
||||
wrappedLogger *Logger
|
||||
zapSugar *zap.SugaredLogger
|
||||
}
|
||||
|
||||
func (l *SugarLogger) Debug(msg string, keyValuePairs ...interface{}) {
|
||||
l.zapSugar.Debugw(msg, keyValuePairs...)
|
||||
}
|
||||
|
||||
func (l *SugarLogger) Info(msg string, keyValuePairs ...interface{}) {
|
||||
l.zapSugar.Infow(msg, keyValuePairs...)
|
||||
}
|
||||
|
||||
func (l *SugarLogger) Error(msg string, keyValuePairs ...interface{}) {
|
||||
l.zapSugar.Errorw(msg, keyValuePairs...)
|
||||
}
|
||||
|
||||
func (l *SugarLogger) Warn(msg string, keyValuePairs ...interface{}) {
|
||||
l.zapSugar.Warnw(msg, keyValuePairs...)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue