glide,vendor: update go-ntlm to remove log4go dependency

This commit is contained in:
Taylor Blau 2016-05-23 16:52:43 -06:00
parent ef19d83808
commit 460a2a1828
19 changed files with 35 additions and 2106 deletions

8
glide.lock generated

@ -1,5 +1,5 @@
hash: 1e9829c101da10af5fb20af5e85d872bbbbba1eb924972614121dc3282d35ea4 hash: 37412dc1ad154f55d7aebe5a937ef74ac15fa0dea30ba040d59e4b0135d03025
updated: 2016-05-23T16:47:27.50226743-06:00 updated: 2016-05-23T16:51:06.736505156-06:00
imports: imports:
- name: github.com/bgentry/go-netrc - name: github.com/bgentry/go-netrc
version: 9fd32a8b3d3d3f9d43c341bfe098430e07609480 version: 9fd32a8b3d3d3f9d43c341bfe098430e07609480
@ -30,11 +30,9 @@ imports:
- name: github.com/technoweenie/go-contentaddressable - name: github.com/technoweenie/go-contentaddressable
version: 38171def3cd15e3b76eb156219b3d48704643899 version: 38171def3cd15e3b76eb156219b3d48704643899
- name: github.com/ThomsonReutersEikon/go-ntlm - name: github.com/ThomsonReutersEikon/go-ntlm
version: 52b7efa603f1b809167b528b8bbaa467e36fdc02 version: b00ec39bbdd04f845950f4dbb4fd0a2c3155e830
subpackages: subpackages:
- ntlm - ntlm
- name: github.com/ThomsonReutersEikon/log4go
version: 173466d4d37ffd4ca9c400bc0b67d57b4a73cdae
- name: github.com/xeipuuv/gojsonpointer - name: github.com/xeipuuv/gojsonpointer
version: e0fe6f68307607d540ed8eac07a342c33fa1b54a version: e0fe6f68307607d540ed8eac07a342c33fa1b54a
- name: github.com/xeipuuv/gojsonreference - name: github.com/xeipuuv/gojsonreference

@ -25,7 +25,7 @@ import:
- package: github.com/stretchr/testify - package: github.com/stretchr/testify
version: 6cb3b85ef5a0efef77caef88363ec4d4b5c0976d version: 6cb3b85ef5a0efef77caef88363ec4d4b5c0976d
- package: github.com/ThomsonReutersEikon/go-ntlm - package: github.com/ThomsonReutersEikon/go-ntlm
version: 52b7efa603f1b809167b528b8bbaa467e36fdc02 version: b00ec39bbdd04f845950f4dbb4fd0a2c3155e830
# go-ntlm includes a util/ directory, which was removed # go-ntlm includes a util/ directory, which was removed
subpackages: subpackages:
- ntlm - ntlm
@ -39,5 +39,3 @@ import:
version: e02fc20de94c78484cd5ffb007f8af96be030a45 version: e02fc20de94c78484cd5ffb007f8af96be030a45
- package: github.com/xeipuuv/gojsonschema - package: github.com/xeipuuv/gojsonschema
version: d5336c75940ef31c9ceeb0ae64cf92944bccb4ee version: d5336c75940ef31c9ceeb0ae64cf92944bccb4ee
- package: github.com/ThomsonReutersEikon/log4go
# log4go includes an examples/ directory, which was removed

@ -8,8 +8,9 @@ import (
md5P "crypto/md5" md5P "crypto/md5"
"crypto/rand" "crypto/rand"
rc4P "crypto/rc4" rc4P "crypto/rc4"
md4P "github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4"
crc32P "hash/crc32" crc32P "hash/crc32"
md4P "github.com/ThomsonReutersEikon/go-ntlm/ntlm/md4"
) )
func md4(data []byte) []byte { func md4(data []byte) []byte {

@ -5,8 +5,8 @@ package ntlm
import ( import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"unicode/utf16"
"encoding/binary" "encoding/binary"
"unicode/utf16"
) )
// Concatenate two byte slices into a new slice // Concatenate two byte slices into a new slice
@ -67,23 +67,22 @@ func utf16FromString(s string) []byte {
// Convert a UTF16 string to UTF8 string for Go usage // Convert a UTF16 string to UTF8 string for Go usage
func utf16ToString(bytes []byte) string { func utf16ToString(bytes []byte) string {
var data []uint16 var data []uint16
// NOTE: This is definitely not the best way to do this, but when I tried using a buffer.Read I could not get it to work // NOTE: This is definitely not the best way to do this, but when I tried using a buffer.Read I could not get it to work
for offset := 0; offset < len(bytes); offset = offset + 2 { for offset := 0; offset < len(bytes); offset = offset + 2 {
i := binary.LittleEndian.Uint16(bytes[offset : offset+2]) i := binary.LittleEndian.Uint16(bytes[offset : offset+2])
data = append(data, i) data = append(data, i)
} }
return string(utf16.Decode(data)) return string(utf16.Decode(data))
} }
func uint32ToBytes(v uint32) []byte { func uint32ToBytes(v uint32) []byte {
bytes := make([]byte, 4) bytes := make([]byte, 4)
bytes[0] = byte(v & 0xff) bytes[0] = byte(v & 0xff)
bytes[1] = byte((v >> 8) & 0xff) bytes[1] = byte((v >> 8) & 0xff)
bytes[2] = byte((v >> 16) & 0xff) bytes[2] = byte((v >> 16) & 0xff)
bytes[3] = byte((v >> 24) & 0xff) bytes[3] = byte((v >> 24) & 0xff)
return bytes return bytes
} }

@ -38,7 +38,7 @@ type AuthenticateMessage struct {
/// MS-NLMP 2.2.1.3 - In connectionless mode, a NEGOTIATE structure that contains a set of bit flags (section 2.2.2.5) and represents the /// MS-NLMP 2.2.1.3 - In connectionless mode, a NEGOTIATE structure that contains a set of bit flags (section 2.2.2.5) and represents the
// conclusion of negotiation—the choices the client has made from the options the server offered in the CHALLENGE_MESSAGE. // conclusion of negotiation—the choices the client has made from the options the server offered in the CHALLENGE_MESSAGE.
// In connection-oriented mode, a NEGOTIATE structure that contains the set of bit flags (section 2.2.2.5) negotiated in // In connection-oriented mode, a NEGOTIATE structure that contains the set of bit flags (section 2.2.2.5) negotiated in
// the previous // the previous
NegotiateFlags uint32 // 4 bytes NegotiateFlags uint32 // 4 bytes
// Version (8 bytes): A VERSION structure (section 2.2.2.10) that is present only when the NTLMSSP_NEGOTIATE_VERSION // Version (8 bytes): A VERSION structure (section 2.2.2.10) that is present only when the NTLMSSP_NEGOTIATE_VERSION

@ -6,7 +6,7 @@ import (
"bytes" "bytes"
rc4P "crypto/rc4" rc4P "crypto/rc4"
"errors" "errors"
l4g "github.com/ThomsonReutersEikon/log4go" "log"
"strings" "strings"
) )
@ -181,7 +181,7 @@ func (n *V1ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (e
// They should always be correct (I hope) // They should always be correct (I hope)
n.user = am.UserName.String() n.user = am.UserName.String()
n.userDomain = am.DomainName.String() n.userDomain = am.DomainName.String()
l4g.Info("(ProcessAuthenticateMessage)NTLM v1 User %s Domain %s", n.user, n.userDomain) log.Printf("(ProcessAuthenticateMessage)NTLM v1 User %s Domain %s", n.user, n.userDomain)
err = n.fetchResponseKeys() err = n.fetchResponseKeys()
if err != nil { if err != nil {
@ -225,7 +225,7 @@ func (n *V1ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (e
//UGH not entirely sure how this could possibly happen, going to put this in for now //UGH not entirely sure how this could possibly happen, going to put this in for now
//TODO investigate if this ever is really happening //TODO investigate if this ever is really happening
am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)}
l4g.Error("Nil version in ntlmv1") log.Printf("Nil version in ntlmv1")
} }
err = n.calculateKeys(am.Version.NTLMRevisionCurrent) err = n.calculateKeys(am.Version.NTLMRevisionCurrent)

@ -42,14 +42,14 @@ func checkV1Value(t *testing.T, name string, value []byte, expected string, err
// would authenticate. This was due to a bug in the MS-NLMP docs. This tests for that issue // would authenticate. This was due to a bug in the MS-NLMP docs. This tests for that issue
func TestNtlmV1ExtendedSessionSecurity(t *testing.T) { func TestNtlmV1ExtendedSessionSecurity(t *testing.T) {
// NTLMv1 with extended session security // NTLMv1 with extended session security
challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAABVgphiRy3oSZvn1I4AAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA=" challengeMessage := "TlRMTVNTUAACAAAAAAAAADgAAABVgphiRy3oSZvn1I4AAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA="
authenticateMessage := "TlRMTVNTUAADAAAAGAAYAJgAAAAYABgAsAAAAAAAAABIAAAAOgA6AEgAAAAWABYAggAAABAAEADIAAAAVYKYYgUCzg4AAAAPMQAwADAAMAAwADEALgB3AGMAcABAAHQAaABvAG0AcwBvAG4AcgBlAHUAdABlAHIAcwAuAGMAbwBtAE4AWQBDAFMATQBTAEcAOQA5ADAAOQBRWAK3h/TIywAAAAAAAAAAAAAAAAAAAAA3tp89kZU1hs1XZp7KTyGm3XsFAT9stEDW9YXDaeYVBmBcBb//2FOu" authenticateMessage := "TlRMTVNTUAADAAAAGAAYAJgAAAAYABgAsAAAAAAAAABIAAAAOgA6AEgAAAAWABYAggAAABAAEADIAAAAVYKYYgUCzg4AAAAPMQAwADAAMAAwADEALgB3AGMAcABAAHQAaABvAG0AcwBvAG4AcgBlAHUAdABlAHIAcwAuAGMAbwBtAE4AWQBDAFMATQBTAEcAOQA5ADAAOQBRWAK3h/TIywAAAAAAAAAAAAAAAAAAAAA3tp89kZU1hs1XZp7KTyGm3XsFAT9stEDW9YXDaeYVBmBcBb//2FOu"
challengeData, _ := base64.StdEncoding.DecodeString(challengeMessage) challengeData, _ := base64.StdEncoding.DecodeString(challengeMessage)
c, _ := ParseChallengeMessage(challengeData) c, _ := ParseChallengeMessage(challengeData)
authenticateData, _ := base64.StdEncoding.DecodeString(authenticateMessage) authenticateData, _ := base64.StdEncoding.DecodeString(authenticateMessage)
msg, err := ParseAuthenticateMessage(authenticateData, 1) msg, err := ParseAuthenticateMessage(authenticateData, 1)
if err != nil { if err != nil {
t.Errorf("Could not process authenticate message: %s", err) t.Errorf("Could not process authenticate message: %s", err)
} }

@ -7,7 +7,7 @@ import (
rc4P "crypto/rc4" rc4P "crypto/rc4"
"encoding/binary" "encoding/binary"
"errors" "errors"
l4g "github.com/ThomsonReutersEikon/log4go" "log"
"strings" "strings"
"time" "time"
) )
@ -204,7 +204,7 @@ func (n *V2ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (e
// They should always be correct (I hope) // They should always be correct (I hope)
n.user = am.UserName.String() n.user = am.UserName.String()
n.userDomain = am.DomainName.String() n.userDomain = am.DomainName.String()
l4g.Info("(ProcessAuthenticateMessage)NTLM v2 User %s Domain %s", n.user, n.userDomain) log.Printf("(ProcessAuthenticateMessage)NTLM v2 User %s Domain %s", n.user, n.userDomain)
err = n.fetchResponseKeys() err = n.fetchResponseKeys()
if err != nil { if err != nil {
@ -238,14 +238,13 @@ func (n *V2ServerSession) ProcessAuthenticateMessage(am *AuthenticateMessage) (e
return err return err
} }
if am.Version == nil { if am.Version == nil {
//UGH not entirely sure how this could possibly happen, going to put this in for now //UGH not entirely sure how this could possibly happen, going to put this in for now
//TODO investigate if this ever is really happening //TODO investigate if this ever is really happening
am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)} am.Version = &VersionStruct{ProductMajorVersion: uint8(5), ProductMinorVersion: uint8(1), ProductBuild: uint16(2600), NTLMRevisionCurrent: uint8(15)}
l4g.Error("Nil version in ntlmv2") log.Printf("Nil version in ntlmv2")
} }
err = n.calculateKeys(am.Version.NTLMRevisionCurrent) err = n.calculateKeys(am.Version.NTLMRevisionCurrent)
if err != nil { if err != nil {

@ -1 +0,0 @@
tags

@ -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,12 +0,0 @@
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,283 +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)
}
}
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,239 +0,0 @@
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
package log4go
import (
"os"
"fmt"
"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
}
// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
w.rec <- rec
}
func (w *FileLogWriter) Close() {
close(w.rec)
}
// 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,
}
// 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 := ""
for ; err == nil && num <= 999; num++ {
fname = w.filename + fmt.Sprintf(".%03d", 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)
}
// 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
}
// 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"
"os"
"fmt"
"time"
"strings"
"runtime"
)
// 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,537 +0,0 @@
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
package log4go
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
"runtime"
"testing"
"time"
)
const testLogFile = "_logtest.log"
var now time.Time = time.Unix(0, 1234567890123456789).In(time.UTC)
func newLogRecord(lvl level, src string, msg string) *LogRecord {
return &LogRecord{
Level: lvl,
Source: src,
Created: now,
Message: msg,
}
}
func TestELog(t *testing.T) {
fmt.Printf("Testing %s\n", L4G_VERSION)
lr := newLogRecord(CRITICAL, "source", "message")
if lr.Level != CRITICAL {
t.Errorf("Incorrect level: %d should be %d", lr.Level, CRITICAL)
}
if lr.Source != "source" {
t.Errorf("Incorrect source: %s should be %s", lr.Source, "source")
}
if lr.Message != "message" {
t.Errorf("Incorrect message: %s should be %s", lr.Source, "message")
}
}
var formatTests = []struct {
Test string
Record *LogRecord
Formats map[string]string
}{
{
Test: "Standard formats",
Record: &LogRecord{
Level: ERROR,
Source: "source",
Message: "message",
Created: now,
},
Formats: map[string]string{
// TODO(kevlar): How can I do this so it'll work outside of PST?
FORMAT_DEFAULT: "[2009/02/13 23:31:30 UTC] [EROR] (source) message\n",
FORMAT_SHORT: "[23:31 02/13/09] [EROR] message\n",
FORMAT_ABBREV: "[EROR] message\n",
},
},
}
func TestFormatLogRecord(t *testing.T) {
for _, test := range formatTests {
name := test.Test
for fmt, want := range test.Formats {
if got := FormatLogRecord(fmt, test.Record); got != want {
t.Errorf("%s - %s:", name, fmt)
t.Errorf(" got %q", got)
t.Errorf(" want %q", want)
}
}
}
}
var logRecordWriteTests = []struct {
Test string
Record *LogRecord
Console string
}{
{
Test: "Normal message",
Record: &LogRecord{
Level: CRITICAL,
Source: "source",
Message: "message",
Created: now,
},
Console: "[Feb 13 23:31:30.123456] [CRIT] message\n",
},
}
func TestConsoleLogWriter(t *testing.T) {
r, w := io.Pipe()
stdout = w
console := NewConsoleLogWriter()
// Write
go func() {
for _, test := range logRecordWriteTests {
console.LogWrite(test.Record)
}
}()
// Read and verify
buf := make([]byte, 1024)
for _, test := range logRecordWriteTests {
name := test.Test
n, _ := r.Read(buf)
if got, want := string(buf[:n]), test.Console; got != want {
t.Errorf("%s: got %q", name, got)
t.Errorf("%s: want %q", name, want)
}
}
stdout = os.Stdout
}
func TestFileLogWriter(t *testing.T) {
defer func(buflen int) {
LogBufferLength = buflen
}(LogBufferLength)
LogBufferLength = 0
w := NewFileLogWriter(testLogFile, false)
if w == nil {
t.Fatalf("Invalid return: w should not be nil")
}
defer os.Remove(testLogFile)
w.LogWrite(newLogRecord(CRITICAL, "source", "message"))
w.Close()
runtime.Gosched()
if contents, err := ioutil.ReadFile(testLogFile); err != nil {
t.Errorf("read(%q): %s", testLogFile, err)
} else if len(contents) != 50 {
t.Errorf("malformed filelog: %q (%d bytes)", string(contents), len(contents))
}
}
func TestXMLLogWriter(t *testing.T) {
defer func(buflen int) {
LogBufferLength = buflen
}(LogBufferLength)
LogBufferLength = 0
w := NewXMLLogWriter(testLogFile, false)
if w == nil {
t.Fatalf("Invalid return: w should not be nil")
}
defer os.Remove(testLogFile)
w.LogWrite(newLogRecord(CRITICAL, "source", "message"))
w.Close()
runtime.Gosched()
if contents, err := ioutil.ReadFile(testLogFile); err != nil {
t.Errorf("read(%q): %s", testLogFile, err)
} else if len(contents) != 185 {
t.Errorf("malformed xmllog: %q (%d bytes)", string(contents), len(contents))
}
}
func TestLogger(t *testing.T) {
sl := NewDefaultLogger(WARNING)
if sl == nil {
t.Fatalf("NewDefaultLogger should never return nil")
}
if lw, exist := sl["stdout"]; lw == nil || exist != true {
t.Fatalf("NewDefaultLogger produced invalid logger (DNE or nil)")
}
if sl["stdout"].Level != WARNING {
t.Fatalf("NewDefaultLogger produced invalid logger (incorrect level)")
}
if len(sl) != 1 {
t.Fatalf("NewDefaultLogger produced invalid logger (incorrect map count)")
}
//func (l *Logger) AddFilter(name string, level int, writer LogWriter) {}
l := make(Logger)
l.AddFilter("stdout", DEBUG, NewConsoleLogWriter())
if lw, exist := l["stdout"]; lw == nil || exist != true {
t.Fatalf("AddFilter produced invalid logger (DNE or nil)")
}
if l["stdout"].Level != DEBUG {
t.Fatalf("AddFilter produced invalid logger (incorrect level)")
}
if len(l) != 1 {
t.Fatalf("AddFilter produced invalid logger (incorrect map count)")
}
//func (l *Logger) Warn(format string, args ...interface{}) error {}
if err := l.Warn("%s %d %#v", "Warning:", 1, []int{}); err.Error() != "Warning: 1 []int{}" {
t.Errorf("Warn returned invalid error: %s", err)
}
//func (l *Logger) Error(format string, args ...interface{}) error {}
if err := l.Error("%s %d %#v", "Error:", 10, []string{}); err.Error() != "Error: 10 []string{}" {
t.Errorf("Error returned invalid error: %s", err)
}
//func (l *Logger) Critical(format string, args ...interface{}) error {}
if err := l.Critical("%s %d %#v", "Critical:", 100, []int64{}); err.Error() != "Critical: 100 []int64{}" {
t.Errorf("Critical returned invalid error: %s", err)
}
// Already tested or basically untestable
//func (l *Logger) Log(level int, source, message string) {}
//func (l *Logger) Logf(level int, format string, args ...interface{}) {}
//func (l *Logger) intLogf(level int, format string, args ...interface{}) string {}
//func (l *Logger) Finest(format string, args ...interface{}) {}
//func (l *Logger) Fine(format string, args ...interface{}) {}
//func (l *Logger) Debug(format string, args ...interface{}) {}
//func (l *Logger) Trace(format string, args ...interface{}) {}
//func (l *Logger) Info(format string, args ...interface{}) {}
}
func TestLogOutput(t *testing.T) {
const (
expected = "fdf3e51e444da56b4cb400f30bc47424"
)
// Unbuffered output
defer func(buflen int) {
LogBufferLength = buflen
}(LogBufferLength)
LogBufferLength = 0
l := make(Logger)
// Delete and open the output log without a timestamp (for a constant md5sum)
l.AddFilter("file", FINEST, NewFileLogWriter(testLogFile, false).SetFormat("[%L] %M"))
defer os.Remove(testLogFile)
// Send some log messages
l.Log(CRITICAL, "testsrc1", fmt.Sprintf("This message is level %d", int(CRITICAL)))
l.Logf(ERROR, "This message is level %v", ERROR)
l.Logf(WARNING, "This message is level %s", WARNING)
l.Logc(INFO, func() string { return "This message is level INFO" })
l.Trace("This message is level %d", int(TRACE))
l.Debug("This message is level %s", DEBUG)
l.Fine(func() string { return fmt.Sprintf("This message is level %v", FINE) })
l.Finest("This message is level %v", FINEST)
l.Finest(FINEST, "is also this message's level")
l.Close()
contents, err := ioutil.ReadFile(testLogFile)
if err != nil {
t.Fatalf("Could not read output log: %s", err)
}
sum := md5.New()
sum.Write(contents)
if sumstr := hex.EncodeToString(sum.Sum(nil)); sumstr != expected {
t.Errorf("--- Log Contents:\n%s---", string(contents))
t.Fatalf("Checksum does not match: %s (expecting %s)", sumstr, expected)
}
}
func TestCountMallocs(t *testing.T) {
const N = 1
var m runtime.MemStats
getMallocs := func() uint64 {
runtime.ReadMemStats(&m)
return m.Mallocs
}
// Console logger
sl := NewDefaultLogger(INFO)
mallocs := 0 - getMallocs()
for i := 0; i < N; i++ {
sl.Log(WARNING, "here", "This is a WARNING message")
}
mallocs += getMallocs()
fmt.Printf("mallocs per sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N)
// Console logger formatted
mallocs = 0 - getMallocs()
for i := 0; i < N; i++ {
sl.Logf(WARNING, "%s is a log message with level %d", "This", WARNING)
}
mallocs += getMallocs()
fmt.Printf("mallocs per sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N)
// Console logger (not logged)
sl = NewDefaultLogger(INFO)
mallocs = 0 - getMallocs()
for i := 0; i < N; i++ {
sl.Log(DEBUG, "here", "This is a DEBUG log message")
}
mallocs += getMallocs()
fmt.Printf("mallocs per unlogged sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N)
// Console logger formatted (not logged)
mallocs = 0 - getMallocs()
for i := 0; i < N; i++ {
sl.Logf(DEBUG, "%s is a log message with level %d", "This", DEBUG)
}
mallocs += getMallocs()
fmt.Printf("mallocs per unlogged sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N)
}
func TestXMLConfig(t *testing.T) {
const (
configfile = "example.xml"
)
fd, err := os.Create(configfile)
if err != nil {
t.Fatalf("Could not open %s for writing: %s", configfile, err)
}
fmt.Fprintln(fd, "<logging>")
fmt.Fprintln(fd, " <filter enabled=\"true\">")
fmt.Fprintln(fd, " <tag>stdout</tag>")
fmt.Fprintln(fd, " <type>console</type>")
fmt.Fprintln(fd, " <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->")
fmt.Fprintln(fd, " <level>DEBUG</level>")
fmt.Fprintln(fd, " </filter>")
fmt.Fprintln(fd, " <filter enabled=\"true\">")
fmt.Fprintln(fd, " <tag>file</tag>")
fmt.Fprintln(fd, " <type>file</type>")
fmt.Fprintln(fd, " <level>FINEST</level>")
fmt.Fprintln(fd, " <property name=\"filename\">test.log</property>")
fmt.Fprintln(fd, " <!--")
fmt.Fprintln(fd, " %T - Time (15:04:05 MST)")
fmt.Fprintln(fd, " %t - Time (15:04)")
fmt.Fprintln(fd, " %D - Date (2006/01/02)")
fmt.Fprintln(fd, " %d - Date (01/02/06)")
fmt.Fprintln(fd, " %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)")
fmt.Fprintln(fd, " %S - Source")
fmt.Fprintln(fd, " %M - Message")
fmt.Fprintln(fd, " It ignores unknown format strings (and removes them)")
fmt.Fprintln(fd, " Recommended: \"[%D %T] [%L] (%S) %M\"")
fmt.Fprintln(fd, " -->")
fmt.Fprintln(fd, " <property name=\"format\">[%D %T] [%L] (%S) %M</property>")
fmt.Fprintln(fd, " <property name=\"rotate\">false</property> <!-- true enables log rotation, otherwise append -->")
fmt.Fprintln(fd, " <property name=\"maxsize\">0M</property> <!-- \\d+[KMG]? Suffixes are in terms of 2**10 -->")
fmt.Fprintln(fd, " <property name=\"maxlines\">0K</property> <!-- \\d+[KMG]? Suffixes are in terms of thousands -->")
fmt.Fprintln(fd, " <property name=\"daily\">true</property> <!-- Automatically rotates when a log message is written after midnight -->")
fmt.Fprintln(fd, " </filter>")
fmt.Fprintln(fd, " <filter enabled=\"true\">")
fmt.Fprintln(fd, " <tag>xmllog</tag>")
fmt.Fprintln(fd, " <type>xml</type>")
fmt.Fprintln(fd, " <level>TRACE</level>")
fmt.Fprintln(fd, " <property name=\"filename\">trace.xml</property>")
fmt.Fprintln(fd, " <property name=\"rotate\">true</property> <!-- true enables log rotation, otherwise append -->")
fmt.Fprintln(fd, " <property name=\"maxsize\">100M</property> <!-- \\d+[KMG]? Suffixes are in terms of 2**10 -->")
fmt.Fprintln(fd, " <property name=\"maxrecords\">6K</property> <!-- \\d+[KMG]? Suffixes are in terms of thousands -->")
fmt.Fprintln(fd, " <property name=\"daily\">false</property> <!-- Automatically rotates when a log message is written after midnight -->")
fmt.Fprintln(fd, " </filter>")
fmt.Fprintln(fd, " <filter enabled=\"false\"><!-- enabled=false means this logger won't actually be created -->")
fmt.Fprintln(fd, " <tag>donotopen</tag>")
fmt.Fprintln(fd, " <type>socket</type>")
fmt.Fprintln(fd, " <level>FINEST</level>")
fmt.Fprintln(fd, " <property name=\"endpoint\">192.168.1.255:12124</property> <!-- recommend UDP broadcast -->")
fmt.Fprintln(fd, " <property name=\"protocol\">udp</property> <!-- tcp or udp -->")
fmt.Fprintln(fd, " </filter>")
fmt.Fprintln(fd, "</logging>")
fd.Close()
log := make(Logger)
log.LoadConfiguration(configfile)
defer os.Remove("trace.xml")
defer os.Remove("test.log")
defer log.Close()
// Make sure we got all loggers
if len(log) != 3 {
t.Fatalf("XMLConfig: Expected 3 filters, found %d", len(log))
}
// Make sure they're the right keys
if _, ok := log["stdout"]; !ok {
t.Errorf("XMLConfig: Expected stdout logger")
}
if _, ok := log["file"]; !ok {
t.Fatalf("XMLConfig: Expected file logger")
}
if _, ok := log["xmllog"]; !ok {
t.Fatalf("XMLConfig: Expected xmllog logger")
}
// Make sure they're the right type
if _, ok := log["stdout"].LogWriter.(ConsoleLogWriter); !ok {
t.Fatalf("XMLConfig: Expected stdout to be ConsoleLogWriter, found %T", log["stdout"].LogWriter)
}
if _, ok := log["file"].LogWriter.(*FileLogWriter); !ok {
t.Fatalf("XMLConfig: Expected file to be *FileLogWriter, found %T", log["file"].LogWriter)
}
if _, ok := log["xmllog"].LogWriter.(*FileLogWriter); !ok {
t.Fatalf("XMLConfig: Expected xmllog to be *FileLogWriter, found %T", log["xmllog"].LogWriter)
}
// Make sure levels are set
if lvl := log["stdout"].Level; lvl != DEBUG {
t.Errorf("XMLConfig: Expected stdout to be set to level %d, found %d", DEBUG, lvl)
}
if lvl := log["file"].Level; lvl != FINEST {
t.Errorf("XMLConfig: Expected file to be set to level %d, found %d", FINEST, lvl)
}
if lvl := log["xmllog"].Level; lvl != TRACE {
t.Errorf("XMLConfig: Expected xmllog to be set to level %d, found %d", TRACE, lvl)
}
// Make sure the w is open and points to the right file
if fname := log["file"].LogWriter.(*FileLogWriter).file.Name(); fname != "test.log" {
t.Errorf("XMLConfig: Expected file to have opened %s, found %s", "test.log", fname)
}
// Make sure the XLW is open and points to the right file
if fname := log["xmllog"].LogWriter.(*FileLogWriter).file.Name(); fname != "trace.xml" {
t.Errorf("XMLConfig: Expected xmllog to have opened %s, found %s", "trace.xml", fname)
}
// Move XML log file
os.Rename(configfile, "examples/"+configfile) // Keep this so that an example with the documentation is available
}
func BenchmarkFormatLogRecord(b *testing.B) {
const updateEvery = 1
rec := &LogRecord{
Level: CRITICAL,
Created: now,
Source: "source",
Message: "message",
}
for i := 0; i < b.N; i++ {
rec.Created = rec.Created.Add(1 * time.Second / updateEvery)
if i%2 == 0 {
FormatLogRecord(FORMAT_DEFAULT, rec)
} else {
FormatLogRecord(FORMAT_SHORT, rec)
}
}
}
func BenchmarkConsoleLog(b *testing.B) {
/* This doesn't seem to work on OS X
sink, err := os.Open(os.DevNull)
if err != nil {
panic(err)
}
if err := syscall.Dup2(int(sink.Fd()), syscall.Stdout); err != nil {
panic(err)
}
*/
stdout = ioutil.Discard
sl := NewDefaultLogger(INFO)
for i := 0; i < b.N; i++ {
sl.Log(WARNING, "here", "This is a log message")
}
}
func BenchmarkConsoleNotLogged(b *testing.B) {
sl := NewDefaultLogger(INFO)
for i := 0; i < b.N; i++ {
sl.Log(DEBUG, "here", "This is a log message")
}
}
func BenchmarkConsoleUtilLog(b *testing.B) {
sl := NewDefaultLogger(INFO)
for i := 0; i < b.N; i++ {
sl.Info("%s is a log message", "This")
}
}
func BenchmarkConsoleUtilNotLog(b *testing.B) {
sl := NewDefaultLogger(INFO)
for i := 0; i < b.N; i++ {
sl.Debug("%s is a log message", "This")
}
}
func BenchmarkFileLog(b *testing.B) {
sl := make(Logger)
b.StopTimer()
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
b.StartTimer()
for i := 0; i < b.N; i++ {
sl.Log(WARNING, "here", "This is a log message")
}
b.StopTimer()
os.Remove("benchlog.log")
}
func BenchmarkFileNotLogged(b *testing.B) {
sl := make(Logger)
b.StopTimer()
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
b.StartTimer()
for i := 0; i < b.N; i++ {
sl.Log(DEBUG, "here", "This is a log message")
}
b.StopTimer()
os.Remove("benchlog.log")
}
func BenchmarkFileUtilLog(b *testing.B) {
sl := make(Logger)
b.StopTimer()
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
b.StartTimer()
for i := 0; i < b.N; i++ {
sl.Info("%s is a log message", "This")
}
b.StopTimer()
os.Remove("benchlog.log")
}
func BenchmarkFileUtilNotLog(b *testing.B) {
sl := make(Logger)
b.StopTimer()
sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false))
b.StartTimer()
for i := 0; i < b.N; i++ {
sl.Debug("%s is a log message", "This")
}
b.StopTimer()
os.Remove("benchlog.log")
}
// Benchmark results (darwin amd64 6g)
//elog.BenchmarkConsoleLog 100000 22819 ns/op
//elog.BenchmarkConsoleNotLogged 2000000 879 ns/op
//elog.BenchmarkConsoleUtilLog 50000 34380 ns/op
//elog.BenchmarkConsoleUtilNotLog 1000000 1339 ns/op
//elog.BenchmarkFileLog 100000 26497 ns/op
//elog.BenchmarkFileNotLogged 2000000 821 ns/op
//elog.BenchmarkFileUtilLog 50000 33945 ns/op
//elog.BenchmarkFileUtilNotLog 1000000 1258 ns/op

@ -1,131 +0,0 @@
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
package log4go
import (
"bytes"
"fmt"
"io"
"os"
"strconv"
"sync/atomic"
"unsafe"
)
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 = unsafe.Pointer(&formatCacheType{})
var pid = os.Getpid()
// Known format codes:
// %T - Time (15:04:05 MST)
// %t - Time (15:04)
// %R - Real time (2006-01-02 15:04:05.999999)
// %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 := *(*formatCacheType)(atomic.LoadPointer(&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()
cache = formatCacheType{
LastUpdateSeconds: secs,
shortTime: fmt.Sprintf("%02d:%02d", hour, minute),
shortDate: fmt.Sprintf("%02d/%02d/%02d", month, day, year%100),
longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day),
}
atomic.StorePointer(&formatCache, unsafe.Pointer(&cache))
}
// 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 'P':
out.WriteString(strconv.Itoa(pid))
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 'M':
out.WriteString(rec.Message)
case 'R':
out.WriteString(rec.Created.Format("2006-01-02 15:04:05.999999"))
}
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,31 +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{}
// This creates a new ConsoleLogWriter
func NewConsoleLogWriter() ConsoleLogWriter {
return ConsoleLogWriter{}
}
// This is the ConsoleLogWriter's output method.
func (w ConsoleLogWriter) LogWrite(rec *LogRecord) {
timestr := rec.Created.Format(time.StampMicro)
fmt.Fprint(stdout, "[", timestr, "] [", levelStrings[rec.Level], "] ", rec.Message, "\n")
}
// Close flushes the log. Probably don't need this any more.
func (w ConsoleLogWriter) Close() {
os.Stdout.Sync()
}

@ -1,278 +0,0 @@
// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved.
package log4go
import (
"errors"
"os"
"fmt"
"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
}