* Rename BaseLogger to WriterLogger to help the creation of other providers * Don't export ColorBytes and ResetBytes from ColoredValues * Make integration tests only print logs if they fail * check can color before coloring * I always forget about MSSQL * Oh and use LEVEL in sqlite.ini * Make the test logger log at info - as it means you see the router * Remove empty expected changes * Make the migrations quieter too * Don't display SQL on error - it can be looked at in the file logs if necessary * Fix skip when using onGiteaRuntags/v1.9.0-rc1
@@ -357,10 +357,10 @@ attributes should be cached if this is a commonly used log message. | |||
of bytes representing the color. | |||
These functions will not double wrap a `log.ColoredValue`. They will | |||
also set the ResetBytes to the cached resetBytes. | |||
also set the `resetBytes` to the cached `resetBytes`. | |||
Be careful not to change the contents of resetBytes or boldBytes as this | |||
will break rendering of logging elsewhere. You have been warned. | |||
The `colorBytes` and `resetBytes` are not exported to prevent | |||
accidental overwriting of internal values. | |||
## Log Spoofing protection | |||
@@ -50,7 +50,7 @@ func createSSHUrl(gitPath string, u *url.URL) *url.URL { | |||
} | |||
func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) { | |||
prepareTestEnv(t) | |||
prepareTestEnv(t, 1) | |||
s := http.Server{ | |||
Handler: mac, | |||
} |
@@ -38,6 +38,7 @@ func testGit(t *testing.T, u *url.URL) { | |||
u.Path = baseAPITestContext.GitPath() | |||
t.Run("HTTP", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
httpContext := baseAPITestContext | |||
httpContext.Reponame = "repo-tmp-17" | |||
@@ -47,6 +48,7 @@ func testGit(t *testing.T, u *url.URL) { | |||
assert.NoError(t, err) | |||
defer os.RemoveAll(dstPath) | |||
t.Run("Standard", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
ensureAnonymousClone(t, u) | |||
t.Run("CreateRepo", doAPICreateRepository(httpContext, false)) | |||
@@ -57,16 +59,21 @@ func testGit(t *testing.T, u *url.URL) { | |||
t.Run("Clone", doGitClone(dstPath, u)) | |||
t.Run("PushCommit", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
t.Run("Little", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
little = commitAndPush(t, littleSize, dstPath) | |||
}) | |||
t.Run("Big", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
big = commitAndPush(t, bigSize, dstPath) | |||
}) | |||
}) | |||
}) | |||
t.Run("LFS", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
t.Run("PushCommit", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
//Setup git LFS | |||
_, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath) | |||
assert.NoError(t, err) | |||
@@ -76,17 +83,21 @@ func testGit(t *testing.T, u *url.URL) { | |||
assert.NoError(t, err) | |||
t.Run("Little", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
littleLFS = commitAndPush(t, littleSize, dstPath) | |||
}) | |||
t.Run("Big", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
bigLFS = commitAndPush(t, bigSize, dstPath) | |||
}) | |||
}) | |||
t.Run("Locks", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
lockTest(t, u.String(), dstPath) | |||
}) | |||
}) | |||
t.Run("Raw", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
session := loginUser(t, "user2") | |||
// Request raw paths | |||
@@ -110,6 +121,7 @@ func testGit(t *testing.T, u *url.URL) { | |||
}) | |||
t.Run("Media", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
session := loginUser(t, "user2") | |||
// Request media paths | |||
@@ -132,12 +144,14 @@ func testGit(t *testing.T, u *url.URL) { | |||
}) | |||
t.Run("SSH", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
sshContext := baseAPITestContext | |||
sshContext.Reponame = "repo-tmp-18" | |||
keyname := "my-testing-key" | |||
//Setup key the user ssh key | |||
withKeyFile(t, keyname, func(keyFile string) { | |||
t.Run("CreateUserKey", doAPICreateUserKey(sshContext, "test-key", keyFile)) | |||
PrintCurrentTest(t) | |||
//Setup remote link | |||
sshURL := createSSHUrl(sshContext.GitPath(), u) | |||
@@ -149,6 +163,7 @@ func testGit(t *testing.T, u *url.URL) { | |||
var little, big, littleLFS, bigLFS string | |||
t.Run("Standard", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
t.Run("CreateRepo", doAPICreateRepository(sshContext, false)) | |||
//TODO get url from api | |||
@@ -156,16 +171,21 @@ func testGit(t *testing.T, u *url.URL) { | |||
//time.Sleep(5 * time.Minute) | |||
t.Run("PushCommit", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
t.Run("Little", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
little = commitAndPush(t, littleSize, dstPath) | |||
}) | |||
t.Run("Big", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
big = commitAndPush(t, bigSize, dstPath) | |||
}) | |||
}) | |||
}) | |||
t.Run("LFS", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
t.Run("PushCommit", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
//Setup git LFS | |||
_, err = git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath) | |||
assert.NoError(t, err) | |||
@@ -175,17 +195,21 @@ func testGit(t *testing.T, u *url.URL) { | |||
assert.NoError(t, err) | |||
t.Run("Little", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
littleLFS = commitAndPush(t, littleSize, dstPath) | |||
}) | |||
t.Run("Big", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
bigLFS = commitAndPush(t, bigSize, dstPath) | |||
}) | |||
}) | |||
t.Run("Locks", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
lockTest(t, u.String(), dstPath) | |||
}) | |||
}) | |||
t.Run("Raw", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
session := loginUser(t, "user2") | |||
// Request raw paths | |||
@@ -209,6 +233,7 @@ func testGit(t *testing.T, u *url.URL) { | |||
}) | |||
t.Run("Media", func(t *testing.T) { | |||
PrintCurrentTest(t) | |||
session := loginUser(t, "user2") | |||
// Request media paths |
@@ -165,7 +165,12 @@ func initIntegrationTest() { | |||
routers.GlobalInit() | |||
} | |||
func prepareTestEnv(t testing.TB) { | |||
func prepareTestEnv(t testing.TB, skip ...int) { | |||
ourSkip := 2 | |||
if len(skip) > 0 { | |||
ourSkip += skip[0] | |||
} | |||
PrintCurrentTest(t, ourSkip) | |||
assert.NoError(t, models.LoadFixtures()) | |||
assert.NoError(t, os.RemoveAll(setting.RepoRootPath)) | |||
assert.NoError(t, os.RemoveAll(models.LocalCopyPath())) |
@@ -9,13 +9,13 @@ import ( | |||
"database/sql" | |||
"fmt" | |||
"io/ioutil" | |||
"log" | |||
"os" | |||
"path" | |||
"regexp" | |||
"sort" | |||
"testing" | |||
"code.gitea.io/gitea/integrations" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/migrations" | |||
"code.gitea.io/gitea/modules/setting" | |||
@@ -26,21 +26,22 @@ import ( | |||
var currentEngine *xorm.Engine | |||
func initMigrationTest() { | |||
func initMigrationTest(t *testing.T) { | |||
integrations.PrintCurrentTest(t, 2) | |||
giteaRoot := os.Getenv("GITEA_ROOT") | |||
if giteaRoot == "" { | |||
fmt.Println("Environment variable $GITEA_ROOT not set") | |||
integrations.Printf("Environment variable $GITEA_ROOT not set\n") | |||
os.Exit(1) | |||
} | |||
setting.AppPath = path.Join(giteaRoot, "gitea") | |||
if _, err := os.Stat(setting.AppPath); err != nil { | |||
fmt.Printf("Could not find gitea binary at %s\n", setting.AppPath) | |||
integrations.Printf("Could not find gitea binary at %s\n", setting.AppPath) | |||
os.Exit(1) | |||
} | |||
giteaConf := os.Getenv("GITEA_CONF") | |||
if giteaConf == "" { | |||
fmt.Println("Environment variable $GITEA_CONF not set") | |||
integrations.Printf("Environment variable $GITEA_CONF not set\n") | |||
os.Exit(1) | |||
} else if !path.IsAbs(giteaConf) { | |||
setting.CustomConf = path.Join(giteaRoot, giteaConf) | |||
@@ -51,6 +52,7 @@ func initMigrationTest() { | |||
setting.NewContext() | |||
setting.CheckLFSVersion() | |||
models.LoadConfigs() | |||
setting.NewLogServices(true) | |||
} | |||
func getDialect() string { | |||
@@ -125,7 +127,7 @@ func restoreOldDB(t *testing.T, version string) bool { | |||
data, err := readSQLFromFile(version) | |||
assert.NoError(t, err) | |||
if len(data) == 0 { | |||
log.Printf("No db found to restore for %s version: %s\n", models.DbCfg.Type, version) | |||
integrations.Printf("No db found to restore for %s version: %s\n", models.DbCfg.Type, version) | |||
return false | |||
} | |||
@@ -212,7 +214,8 @@ func wrappedMigrate(x *xorm.Engine) error { | |||
} | |||
func doMigrationTest(t *testing.T, version string) { | |||
log.Printf("Performing migration test for %s version: %s", models.DbCfg.Type, version) | |||
integrations.PrintCurrentTest(t) | |||
integrations.Printf("Performing migration test for %s version: %s\n", models.DbCfg.Type, version) | |||
if !restoreOldDB(t, version) { | |||
return | |||
} | |||
@@ -227,19 +230,22 @@ func doMigrationTest(t *testing.T, version string) { | |||
} | |||
func TestMigrations(t *testing.T) { | |||
initMigrationTest() | |||
initMigrationTest(t) | |||
dialect := models.DbCfg.Type | |||
versions, err := availableVersions() | |||
assert.NoError(t, err) | |||
if len(versions) == 0 { | |||
log.Printf("No old database versions available to migration test for %s\n", dialect) | |||
integrations.Printf("No old database versions available to migration test for %s\n", dialect) | |||
return | |||
} | |||
log.Printf("Preparing to test %d migrations for %s\n", len(versions), dialect) | |||
integrations.Printf("Preparing to test %d migrations for %s\n", len(versions), dialect) | |||
for _, version := range versions { | |||
doMigrationTest(t, version) | |||
t.Run(fmt.Sprintf("Migrate-%s-%s", dialect, version), func(t *testing.T) { | |||
doMigrationTest(t, version) | |||
}) | |||
} | |||
} |
@@ -60,14 +60,19 @@ PROVIDER = file | |||
PROVIDER_CONFIG = data/sessions-mssql | |||
[log] | |||
MODE = console,file | |||
ROOT_PATH = mssql-log | |||
MODE = test,file | |||
ROOT_PATH = sqlite-log | |||
REDIRECT_MACARON_LOG = true | |||
ROUTER = , | |||
MACARON = , | |||
XORM = file | |||
[log.console] | |||
LEVEL = Warn | |||
[log.test] | |||
LEVEL = Info | |||
COLORIZE = true | |||
[log.file] | |||
LEVEL = Debug | |||
LEVEL = Debug | |||
[security] | |||
INSTALL_LOCK = true |
@@ -60,14 +60,19 @@ PROVIDER = file | |||
PROVIDER_CONFIG = data/sessions-mysql | |||
[log] | |||
MODE = console,file | |||
ROOT_PATH = mysql-log | |||
MODE = test,file | |||
ROOT_PATH = sqlite-log | |||
REDIRECT_MACARON_LOG = true | |||
ROUTER = , | |||
MACARON = , | |||
XORM = file | |||
[log.console] | |||
LEVEL = Warn | |||
[log.test] | |||
LEVEL = Info | |||
COLORIZE = true | |||
[log.file] | |||
LEVEL = Debug | |||
LEVEL = Debug | |||
[security] | |||
INSTALL_LOCK = true |
@@ -57,14 +57,20 @@ PROVIDER = file | |||
PROVIDER_CONFIG = data/sessions-mysql8 | |||
[log] | |||
MODE = console,file | |||
ROOT_PATH = mysql8-log | |||
MODE = test,file | |||
ROOT_PATH = sqlite-log | |||
REDIRECT_MACARON_LOG = true | |||
ROUTER = , | |||
MACARON = , | |||
XORM = file | |||
[log.console] | |||
LEVEL = Warn | |||
[log.test] | |||
LEVEL = Info | |||
COLORIZE = true | |||
[log.file] | |||
LEVEL = Debug | |||
LEVEL = Debug | |||
[security] | |||
INSTALL_LOCK = true |
@@ -60,14 +60,19 @@ PROVIDER = file | |||
PROVIDER_CONFIG = data/sessions-pgsql | |||
[log] | |||
MODE = console,file | |||
ROOT_PATH = pgsql-log | |||
MODE = test,file | |||
ROOT_PATH = sqlite-log | |||
REDIRECT_MACARON_LOG = true | |||
ROUTER = , | |||
MACARON = , | |||
XORM = file | |||
[log.console] | |||
LEVEL = Warn | |||
[log.test] | |||
LEVEL = Info | |||
COLORIZE = true | |||
[log.file] | |||
LEVEL = Debug | |||
LEVEL = Debug | |||
[security] | |||
INSTALL_LOCK = true |
@@ -33,18 +33,19 @@ APP_DATA_PATH = integrations/gitea-integration-sqlite/data | |||
ENABLE_GZIP = true | |||
[mailer] | |||
ENABLED = false | |||
ENABLED = true | |||
MAILER_TYPE = dummy | |||
FROM = sqlite-integration-test@gitea.io | |||
[service] | |||
REGISTER_EMAIL_CONFIRM = false | |||
ENABLE_NOTIFY_MAIL = false | |||
ENABLE_NOTIFY_MAIL = true | |||
DISABLE_REGISTRATION = false | |||
ENABLE_CAPTCHA = false | |||
REQUIRE_SIGNIN_VIEW = false | |||
DEFAULT_KEEP_EMAIL_PRIVATE = false | |||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true | |||
NO_REPLY_ADDRESS = noreply.example.org | |||
ENABLE_NOTIFY_MAIL = true | |||
[picture] | |||
DISABLE_GRAVATAR = false | |||
@@ -54,21 +55,25 @@ ENABLE_FEDERATED_AVATAR = false | |||
PROVIDER = file | |||
[log] | |||
MODE = console,file | |||
ROOT_PATH = sqlite-log | |||
MODE = test,file | |||
ROOT_PATH = sqlite-log | |||
REDIRECT_MACARON_LOG = true | |||
ROUTER = , | |||
MACARON = , | |||
XORM = file | |||
[log.console] | |||
LEVEL = Warn | |||
[log.test] | |||
LEVEL = Info | |||
COLORIZE = true | |||
[log.file] | |||
LEVEL = Debug | |||
LEVEL = Debug | |||
[security] | |||
INSTALL_LOCK = true | |||
SECRET_KEY = 9pCviYTWSb | |||
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8 | |||
[mailer] | |||
ENABLED = true | |||
MAILER_TYPE = dummy | |||
FROM = sqlite-integration-test@gitea.io | |||
[oauth2] | |||
JWT_SECRET = KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko | |||
@@ -0,0 +1,104 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package integrations | |||
import ( | |||
"encoding/json" | |||
"fmt" | |||
"os" | |||
"runtime" | |||
"strings" | |||
"testing" | |||
"code.gitea.io/gitea/modules/log" | |||
) | |||
var prefix string | |||
// TestLogger is a logger which will write to the testing log | |||
type TestLogger struct { | |||
log.WriterLogger | |||
} | |||
var writerCloser = &testLoggerWriterCloser{} | |||
type testLoggerWriterCloser struct { | |||
t testing.TB | |||
} | |||
func (w *testLoggerWriterCloser) Write(p []byte) (int, error) { | |||
if w.t != nil { | |||
if len(p) > 0 && p[len(p)-1] == '\n' { | |||
p = p[:len(p)-1] | |||
} | |||
w.t.Log(string(p)) | |||
return len(p), nil | |||
} | |||
return len(p), nil | |||
} | |||
func (w *testLoggerWriterCloser) Close() error { | |||
return nil | |||
} | |||
// PrintCurrentTest prints the current test to os.Stdout | |||
func PrintCurrentTest(t testing.TB, skip ...int) { | |||
actualSkip := 1 | |||
if len(skip) > 0 { | |||
actualSkip = skip[0] | |||
} | |||
_, filename, line, _ := runtime.Caller(actualSkip) | |||
if log.CanColorStdout { | |||
fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line) | |||
} else { | |||
fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", t.Name(), strings.TrimPrefix(filename, prefix), line) | |||
} | |||
writerCloser.t = t | |||
} | |||
// Printf takes a format and args and prints the string to os.Stdout | |||
func Printf(format string, args ...interface{}) { | |||
if log.CanColorStdout { | |||
for i := 0; i < len(args); i++ { | |||
args[i] = log.NewColoredValue(args[i]) | |||
} | |||
} | |||
fmt.Fprintf(os.Stdout, "\t"+format, args...) | |||
} | |||
// NewTestLogger creates a TestLogger as a log.LoggerProvider | |||
func NewTestLogger() log.LoggerProvider { | |||
logger := &TestLogger{} | |||
logger.Colorize = log.CanColorStdout | |||
logger.Level = log.TRACE | |||
return logger | |||
} | |||
// Init inits connection writer with json config. | |||
// json config only need key "level". | |||
func (log *TestLogger) Init(config string) error { | |||
err := json.Unmarshal([]byte(config), log) | |||
if err != nil { | |||
return err | |||
} | |||
log.NewWriterLogger(writerCloser) | |||
return nil | |||
} | |||
// Flush when log should be flushed | |||
func (log *TestLogger) Flush() { | |||
} | |||
// GetName returns the default name for this implementation | |||
func (log *TestLogger) GetName() string { | |||
return "test" | |||
} | |||
func init() { | |||
log.Register("test", NewTestLogger) | |||
_, filename, _, _ := runtime.Caller(0) | |||
prefix = strings.TrimSuffix(filename, "integrations/testlogger.go") | |||
} |
@@ -1,328 +0,0 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package log | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io" | |||
"regexp" | |||
"strings" | |||
"sync" | |||
) | |||
// These flags define which text to prefix to each log entry generated | |||
// by the Logger. Bits are or'ed together to control what's printed. | |||
// There is no control over the order they appear (the order listed | |||
// here) or the format they present (as described in the comments). | |||
// The prefix is followed by a colon only if more than time is stated | |||
// is specified. For example, flags Ldate | Ltime | |||
// produce, 2009/01/23 01:23:23 message. | |||
// The standard is: | |||
// 2009/01/23 01:23:23 ...a/b/c/d.go:23:runtime.Caller() [I]: message | |||
const ( | |||
Ldate = 1 << iota // the date in the local time zone: 2009/01/23 | |||
Ltime // the time in the local time zone: 01:23:23 | |||
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. | |||
Llongfile // full file name and line number: /a/b/c/d.go:23 | |||
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile | |||
Lfuncname // function name of the caller: runtime.Caller() | |||
Lshortfuncname // last part of the function name | |||
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone | |||
Llevelinitial // Initial character of the provided level in brackets eg. [I] for info | |||
Llevel // Provided level in brackets [INFO] | |||
// Last 20 characters of the filename | |||
Lmedfile = Lshortfile | Llongfile | |||
// LstdFlags is the initial value for the standard logger | |||
LstdFlags = Ldate | Ltime | Lmedfile | Lshortfuncname | Llevelinitial | |||
) | |||
var flagFromString = map[string]int{ | |||
"none": 0, | |||
"date": Ldate, | |||
"time": Ltime, | |||
"microseconds": Lmicroseconds, | |||
"longfile": Llongfile, | |||
"shortfile": Lshortfile, | |||
"funcname": Lfuncname, | |||
"shortfuncname": Lshortfuncname, | |||
"utc": LUTC, | |||
"levelinitial": Llevelinitial, | |||
"level": Llevel, | |||
"medfile": Lmedfile, | |||
"stdflags": LstdFlags, | |||
} | |||
// FlagsFromString takes a comma separated list of flags and returns | |||
// the flags for this string | |||
func FlagsFromString(from string) int { | |||
flags := 0 | |||
for _, flag := range strings.Split(strings.ToLower(from), ",") { | |||
f, ok := flagFromString[strings.TrimSpace(flag)] | |||
if ok { | |||
flags = flags | f | |||
} | |||
} | |||
return flags | |||
} | |||
type byteArrayWriter []byte | |||
func (b *byteArrayWriter) Write(p []byte) (int, error) { | |||
*b = append(*b, p...) | |||
return len(p), nil | |||
} | |||
// BaseLogger represent a basic logger for Gitea | |||
type BaseLogger struct { | |||
out io.WriteCloser | |||
mu sync.Mutex | |||
Level Level `json:"level"` | |||
StacktraceLevel Level `json:"stacktraceLevel"` | |||
Flags int `json:"flags"` | |||
Prefix string `json:"prefix"` | |||
Colorize bool `json:"colorize"` | |||
Expression string `json:"expression"` | |||
regexp *regexp.Regexp | |||
} | |||
func (b *BaseLogger) createLogger(out io.WriteCloser, level ...Level) { | |||
b.mu.Lock() | |||
defer b.mu.Unlock() | |||
b.out = out | |||
switch b.Flags { | |||
case 0: | |||
b.Flags = LstdFlags | |||
case -1: | |||
b.Flags = 0 | |||
} | |||
if len(level) > 0 { | |||
b.Level = level[0] | |||
} | |||
b.createExpression() | |||
} | |||
func (b *BaseLogger) createExpression() { | |||
if len(b.Expression) > 0 { | |||
var err error | |||
b.regexp, err = regexp.Compile(b.Expression) | |||
if err != nil { | |||
b.regexp = nil | |||
} | |||
} | |||
} | |||
// GetLevel returns the logging level for this logger | |||
func (b *BaseLogger) GetLevel() Level { | |||
return b.Level | |||
} | |||
// GetStacktraceLevel returns the stacktrace logging level for this logger | |||
func (b *BaseLogger) GetStacktraceLevel() Level { | |||
return b.StacktraceLevel | |||
} | |||
// Copy of cheap integer to fixed-width decimal to ascii from logger. | |||
func itoa(buf *[]byte, i int, wid int) { | |||
var b [20]byte | |||
bp := len(b) - 1 | |||
for i >= 10 || wid > 1 { | |||
wid-- | |||
q := i / 10 | |||
b[bp] = byte('0' + i - q*10) | |||
bp-- | |||
i = q | |||
} | |||
// i < 10 | |||
b[bp] = byte('0' + i) | |||
*buf = append(*buf, b[bp:]...) | |||
} | |||
func (b *BaseLogger) createMsg(buf *[]byte, event *Event) { | |||
*buf = append(*buf, b.Prefix...) | |||
t := event.time | |||
if b.Flags&(Ldate|Ltime|Lmicroseconds) != 0 { | |||
if b.Colorize { | |||
*buf = append(*buf, fgCyanBytes...) | |||
} | |||
if b.Flags&LUTC != 0 { | |||
t = t.UTC() | |||
} | |||
if b.Flags&Ldate != 0 { | |||
year, month, day := t.Date() | |||
itoa(buf, year, 4) | |||
*buf = append(*buf, '/') | |||
itoa(buf, int(month), 2) | |||
*buf = append(*buf, '/') | |||
itoa(buf, day, 2) | |||
*buf = append(*buf, ' ') | |||
} | |||
if b.Flags&(Ltime|Lmicroseconds) != 0 { | |||
hour, min, sec := t.Clock() | |||
itoa(buf, hour, 2) | |||
*buf = append(*buf, ':') | |||
itoa(buf, min, 2) | |||
*buf = append(*buf, ':') | |||
itoa(buf, sec, 2) | |||
if b.Flags&Lmicroseconds != 0 { | |||
*buf = append(*buf, '.') | |||
itoa(buf, t.Nanosecond()/1e3, 6) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
if b.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
} | |||
if b.Flags&(Lshortfile|Llongfile) != 0 { | |||
if b.Colorize { | |||
*buf = append(*buf, fgGreenBytes...) | |||
} | |||
file := event.filename | |||
if b.Flags&Lmedfile == Lmedfile { | |||
startIndex := len(file) - 20 | |||
if startIndex > 0 { | |||
file = "..." + file[startIndex:] | |||
} | |||
} else if b.Flags&Lshortfile != 0 { | |||
startIndex := strings.LastIndexByte(file, '/') | |||
if startIndex > 0 && startIndex < len(file) { | |||
file = file[startIndex+1:] | |||
} | |||
} | |||
*buf = append(*buf, file...) | |||
*buf = append(*buf, ':') | |||
itoa(buf, event.line, -1) | |||
if b.Flags&(Lfuncname|Lshortfuncname) != 0 { | |||
*buf = append(*buf, ':') | |||
} else { | |||
if b.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
} | |||
if b.Flags&(Lfuncname|Lshortfuncname) != 0 { | |||
if b.Colorize { | |||
*buf = append(*buf, fgGreenBytes...) | |||
} | |||
funcname := event.caller | |||
if b.Flags&Lshortfuncname != 0 { | |||
lastIndex := strings.LastIndexByte(funcname, '.') | |||
if lastIndex > 0 && len(funcname) > lastIndex+1 { | |||
funcname = funcname[lastIndex+1:] | |||
} | |||
} | |||
*buf = append(*buf, funcname...) | |||
if b.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
if b.Flags&(Llevel|Llevelinitial) != 0 { | |||
level := strings.ToUpper(event.level.String()) | |||
if b.Colorize { | |||
*buf = append(*buf, levelToColor[event.level]...) | |||
} | |||
*buf = append(*buf, '[') | |||
if b.Flags&Llevelinitial != 0 { | |||
*buf = append(*buf, level[0]) | |||
} else { | |||
*buf = append(*buf, level...) | |||
} | |||
*buf = append(*buf, ']') | |||
if b.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
var msg = []byte(event.msg) | |||
if len(msg) > 0 && msg[len(msg)-1] == '\n' { | |||
msg = msg[:len(msg)-1] | |||
} | |||
pawMode := allowColor | |||
if !b.Colorize { | |||
pawMode = removeColor | |||
} | |||
baw := byteArrayWriter(*buf) | |||
(&protectedANSIWriter{ | |||
w: &baw, | |||
mode: pawMode, | |||
}).Write([]byte(msg)) | |||
*buf = baw | |||
if event.stacktrace != "" && b.StacktraceLevel <= event.level { | |||
lines := bytes.Split([]byte(event.stacktrace), []byte("\n")) | |||
if len(lines) > 1 { | |||
for _, line := range lines { | |||
*buf = append(*buf, "\n\t"...) | |||
*buf = append(*buf, line...) | |||
} | |||
} | |||
*buf = append(*buf, '\n') | |||
} | |||
*buf = append(*buf, '\n') | |||
} | |||
// LogEvent logs the event to the internal writer | |||
func (b *BaseLogger) LogEvent(event *Event) error { | |||
if b.Level > event.level { | |||
return nil | |||
} | |||
b.mu.Lock() | |||
defer b.mu.Unlock() | |||
if !b.Match(event) { | |||
return nil | |||
} | |||
var buf []byte | |||
b.createMsg(&buf, event) | |||
_, err := b.out.Write(buf) | |||
return err | |||
} | |||
// Match checks if the given event matches the logger's regexp expression | |||
func (b *BaseLogger) Match(event *Event) bool { | |||
if b.regexp == nil { | |||
return true | |||
} | |||
if b.regexp.Match([]byte(fmt.Sprintf("%s:%d:%s", event.filename, event.line, event.caller))) { | |||
return true | |||
} | |||
// Match on the non-colored msg - therefore strip out colors | |||
var msg []byte | |||
baw := byteArrayWriter(msg) | |||
(&protectedANSIWriter{ | |||
w: &baw, | |||
mode: removeColor, | |||
}).Write([]byte(event.msg)) | |||
msg = baw | |||
if b.regexp.Match(msg) { | |||
return true | |||
} | |||
return false | |||
} | |||
// Close the base logger | |||
func (b *BaseLogger) Close() { | |||
b.mu.Lock() | |||
defer b.mu.Unlock() | |||
if b.out != nil { | |||
b.out.Close() | |||
} | |||
} | |||
// GetName returns empty for these provider loggers | |||
func (b *BaseLogger) GetName() string { | |||
return "" | |||
} |
@@ -268,8 +268,8 @@ normalLoop: | |||
// ColoredValue will Color the provided value | |||
type ColoredValue struct { | |||
ColorBytes *[]byte | |||
ResetBytes *[]byte | |||
colorBytes *[]byte | |||
resetBytes *[]byte | |||
Value *interface{} | |||
} | |||
@@ -290,14 +290,14 @@ func NewColoredValuePointer(value *interface{}, color ...ColorAttribute) *Colore | |||
if len(color) > 0 { | |||
bytes := ColorBytes(color...) | |||
return &ColoredValue{ | |||
ColorBytes: &bytes, | |||
ResetBytes: &resetBytes, | |||
colorBytes: &bytes, | |||
resetBytes: &resetBytes, | |||
Value: value, | |||
} | |||
} | |||
return &ColoredValue{ | |||
ColorBytes: &fgBoldBytes, | |||
ResetBytes: &resetBytes, | |||
colorBytes: &fgBoldBytes, | |||
resetBytes: &resetBytes, | |||
Value: value, | |||
} | |||
@@ -310,17 +310,37 @@ func NewColoredValueBytes(value interface{}, colorBytes *[]byte) *ColoredValue { | |||
return val | |||
} | |||
return &ColoredValue{ | |||
ColorBytes: colorBytes, | |||
ResetBytes: &resetBytes, | |||
colorBytes: colorBytes, | |||
resetBytes: &resetBytes, | |||
Value: &value, | |||
} | |||
} | |||
// Format will format the provided value and protect against ANSI spoofing within the value | |||
func (cv *ColoredValue) Format(s fmt.State, c rune) { | |||
s.Write([]byte(*cv.ColorBytes)) | |||
s.Write([]byte(*cv.colorBytes)) | |||
fmt.Fprintf(&protectedANSIWriter{w: s}, fmtString(s, c), *(cv.Value)) | |||
s.Write([]byte(*cv.ResetBytes)) | |||
s.Write([]byte(*cv.resetBytes)) | |||
} | |||
// SetColorBytes will allow a user to set the colorBytes of a colored value | |||
func (cv *ColoredValue) SetColorBytes(colorBytes []byte) { | |||
cv.colorBytes = &colorBytes | |||
} | |||
// SetColorBytesPointer will allow a user to set the colorBytes pointer of a colored value | |||
func (cv *ColoredValue) SetColorBytesPointer(colorBytes *[]byte) { | |||
cv.colorBytes = colorBytes | |||
} | |||
// SetResetBytes will allow a user to set the resetBytes pointer of a colored value | |||
func (cv *ColoredValue) SetResetBytes(resetBytes []byte) { | |||
cv.resetBytes = &resetBytes | |||
} | |||
// SetResetBytesPointer will allow a user to set the resetBytes pointer of a colored value | |||
func (cv *ColoredValue) SetResetBytesPointer(resetBytes *[]byte) { | |||
cv.resetBytes = resetBytes | |||
} | |||
func fmtString(s fmt.State, c rune) string { |
@@ -77,7 +77,7 @@ func (i *connWriter) connect() error { | |||
// ConnLogger implements LoggerProvider. | |||
// it writes messages in keep-live tcp connection. | |||
type ConnLogger struct { | |||
BaseLogger | |||
WriterLogger | |||
ReconnectOnMsg bool `json:"reconnectOnMsg"` | |||
Reconnect bool `json:"reconnect"` | |||
Net string `json:"net"` | |||
@@ -98,7 +98,7 @@ func (log *ConnLogger) Init(jsonconfig string) error { | |||
if err != nil { | |||
return err | |||
} | |||
log.createLogger(&connWriter{ | |||
log.NewWriterLogger(&connWriter{ | |||
ReconnectOnMsg: log.ReconnectOnMsg, | |||
Reconnect: log.Reconnect, | |||
Net: log.Net, |
@@ -34,14 +34,14 @@ func (n *nopWriteCloser) Close() error { | |||
// ConsoleLogger implements LoggerProvider and writes messages to terminal. | |||
type ConsoleLogger struct { | |||
BaseLogger | |||
WriterLogger | |||
Stderr bool `json:"stderr"` | |||
} | |||
// NewConsoleLogger create ConsoleLogger returning as LoggerProvider. | |||
func NewConsoleLogger() LoggerProvider { | |||
log := &ConsoleLogger{} | |||
log.createLogger(&nopWriteCloser{ | |||
log.NewWriterLogger(&nopWriteCloser{ | |||
w: os.Stdout, | |||
}) | |||
return log | |||
@@ -55,11 +55,11 @@ func (log *ConsoleLogger) Init(config string) error { | |||
return err | |||
} | |||
if log.Stderr { | |||
log.createLogger(&nopWriteCloser{ | |||
log.NewWriterLogger(&nopWriteCloser{ | |||
w: os.Stderr, | |||
}) | |||
} else { | |||
log.createLogger(log.out) | |||
log.NewWriterLogger(log.out) | |||
} | |||
return nil | |||
} |
@@ -20,7 +20,7 @@ import ( | |||
// FileLogger implements LoggerProvider. | |||
// It writes messages by lines limit, file size limit, or time frequency. | |||
type FileLogger struct { | |||
BaseLogger | |||
WriterLogger | |||
mw *MuxWriter | |||
// The opened file | |||
Filename string `json:"filename"` | |||
@@ -106,7 +106,7 @@ func (log *FileLogger) Init(config string) error { | |||
return errors.New("config must have filename") | |||
} | |||
// set MuxWriter as Logger's io.Writer | |||
log.createLogger(log.mw) | |||
log.NewWriterLogger(log.mw) | |||
return log.StartLogger() | |||
} | |||
@@ -89,7 +89,6 @@ func TestFileLogger(t *testing.T) { | |||
assert.Equal(t, expected, string(logData)) | |||
event.level = DEBUG | |||
expected = expected + "" | |||
fileLogger.LogEvent(&event) | |||
fileLogger.Flush() | |||
logData, err = ioutil.ReadFile(filename) | |||
@@ -97,7 +96,6 @@ func TestFileLogger(t *testing.T) { | |||
assert.Equal(t, expected, string(logData)) | |||
event.level = TRACE | |||
expected = expected + "" | |||
fileLogger.LogEvent(&event) | |||
fileLogger.Flush() | |||
logData, err = ioutil.ReadFile(filename) |
@@ -0,0 +1,64 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package log | |||
import "strings" | |||
// These flags define which text to prefix to each log entry generated | |||
// by the Logger. Bits are or'ed together to control what's printed. | |||
// There is no control over the order they appear (the order listed | |||
// here) or the format they present (as described in the comments). | |||
// The prefix is followed by a colon only if more than time is stated | |||
// is specified. For example, flags Ldate | Ltime | |||
// produce, 2009/01/23 01:23:23 message. | |||
// The standard is: | |||
// 2009/01/23 01:23:23 ...a/logger/c/d.go:23:runtime.Caller() [I]: message | |||
const ( | |||
Ldate = 1 << iota // the date in the local time zone: 2009/01/23 | |||
Ltime // the time in the local time zone: 01:23:23 | |||
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. | |||
Llongfile // full file name and line number: /a/logger/c/d.go:23 | |||
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile | |||
Lfuncname // function name of the caller: runtime.Caller() | |||
Lshortfuncname // last part of the function name | |||
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone | |||
Llevelinitial // Initial character of the provided level in brackets eg. [I] for info | |||
Llevel // Provided level in brackets [INFO] | |||
// Last 20 characters of the filename | |||
Lmedfile = Lshortfile | Llongfile | |||
// LstdFlags is the initial value for the standard logger | |||
LstdFlags = Ldate | Ltime | Lmedfile | Lshortfuncname | Llevelinitial | |||
) | |||
var flagFromString = map[string]int{ | |||
"none": 0, | |||
"date": Ldate, | |||
"time": Ltime, | |||
"microseconds": Lmicroseconds, | |||
"longfile": Llongfile, | |||
"shortfile": Lshortfile, | |||
"funcname": Lfuncname, | |||
"shortfuncname": Lshortfuncname, | |||
"utc": LUTC, | |||
"levelinitial": Llevelinitial, | |||
"level": Llevel, | |||
"medfile": Lmedfile, | |||
"stdflags": LstdFlags, | |||
} | |||
// FlagsFromString takes a comma separated list of flags and returns | |||
// the flags for this string | |||
func FlagsFromString(from string) int { | |||
flags := 0 | |||
for _, flag := range strings.Split(strings.ToLower(from), ",") { | |||
f, ok := flagFromString[strings.TrimSpace(flag)] | |||
if ok { | |||
flags = flags | f | |||
} | |||
} | |||
return flags | |||
} |
@@ -31,7 +31,7 @@ func (s *smtpWriter) Close() error { | |||
// SMTPLogger implements LoggerProvider and is used to send emails via given SMTP-server. | |||
type SMTPLogger struct { | |||
BaseLogger | |||
WriterLogger | |||
Username string `json:"Username"` | |||
Password string `json:"password"` | |||
Host string `json:"host"` | |||
@@ -63,7 +63,7 @@ func (log *SMTPLogger) Init(jsonconfig string) error { | |||
if err != nil { | |||
return err | |||
} | |||
log.createLogger(&smtpWriter{ | |||
log.NewWriterLogger(&smtpWriter{ | |||
owner: log, | |||
}) | |||
log.sendMailFn = smtp.SendMail |
@@ -0,0 +1,273 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package log | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io" | |||
"regexp" | |||
"strings" | |||
"sync" | |||
) | |||
type byteArrayWriter []byte | |||
func (b *byteArrayWriter) Write(p []byte) (int, error) { | |||
*b = append(*b, p...) | |||
return len(p), nil | |||
} | |||
// WriterLogger represent a basic logger for Gitea | |||
type WriterLogger struct { | |||
out io.WriteCloser | |||
mu sync.Mutex | |||
Level Level `json:"level"` | |||
StacktraceLevel Level `json:"stacktraceLevel"` | |||
Flags int `json:"flags"` | |||
Prefix string `json:"prefix"` | |||
Colorize bool `json:"colorize"` | |||
Expression string `json:"expression"` | |||
regexp *regexp.Regexp | |||
} | |||
// NewWriterLogger creates a new WriterLogger from the provided WriteCloser. | |||
// Optionally the level can be changed at the same time. | |||
func (logger *WriterLogger) NewWriterLogger(out io.WriteCloser, level ...Level) { | |||
logger.mu.Lock() | |||
defer logger.mu.Unlock() | |||
logger.out = out | |||
switch logger.Flags { | |||
case 0: | |||
logger.Flags = LstdFlags | |||
case -1: | |||
logger.Flags = 0 | |||
} | |||
if len(level) > 0 { | |||
logger.Level = level[0] | |||
} | |||
logger.createExpression() | |||
} | |||
func (logger *WriterLogger) createExpression() { | |||
if len(logger.Expression) > 0 { | |||
var err error | |||
logger.regexp, err = regexp.Compile(logger.Expression) | |||
if err != nil { | |||
logger.regexp = nil | |||
} | |||
} | |||
} | |||
// GetLevel returns the logging level for this logger | |||
func (logger *WriterLogger) GetLevel() Level { | |||
return logger.Level | |||
} | |||
// GetStacktraceLevel returns the stacktrace logging level for this logger | |||
func (logger *WriterLogger) GetStacktraceLevel() Level { | |||
return logger.StacktraceLevel | |||
} | |||
// Copy of cheap integer to fixed-width decimal to ascii from logger. | |||
func itoa(buf *[]byte, i int, wid int) { | |||
var logger [20]byte | |||
bp := len(logger) - 1 | |||
for i >= 10 || wid > 1 { | |||
wid-- | |||
q := i / 10 | |||
logger[bp] = byte('0' + i - q*10) | |||
bp-- | |||
i = q | |||
} | |||
// i < 10 | |||
logger[bp] = byte('0' + i) | |||
*buf = append(*buf, logger[bp:]...) | |||
} | |||
func (logger *WriterLogger) createMsg(buf *[]byte, event *Event) { | |||
*buf = append(*buf, logger.Prefix...) | |||
t := event.time | |||
if logger.Flags&(Ldate|Ltime|Lmicroseconds) != 0 { | |||
if logger.Colorize { | |||
*buf = append(*buf, fgCyanBytes...) | |||
} | |||
if logger.Flags&LUTC != 0 { | |||
t = t.UTC() | |||
} | |||
if logger.Flags&Ldate != 0 { | |||
year, month, day := t.Date() | |||
itoa(buf, year, 4) | |||
*buf = append(*buf, '/') | |||
itoa(buf, int(month), 2) | |||
*buf = append(*buf, '/') | |||
itoa(buf, day, 2) | |||
*buf = append(*buf, ' ') | |||
} | |||
if logger.Flags&(Ltime|Lmicroseconds) != 0 { | |||
hour, min, sec := t.Clock() | |||
itoa(buf, hour, 2) | |||
*buf = append(*buf, ':') | |||
itoa(buf, min, 2) | |||
*buf = append(*buf, ':') | |||
itoa(buf, sec, 2) | |||
if logger.Flags&Lmicroseconds != 0 { | |||
*buf = append(*buf, '.') | |||
itoa(buf, t.Nanosecond()/1e3, 6) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
if logger.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
} | |||
if logger.Flags&(Lshortfile|Llongfile) != 0 { | |||
if logger.Colorize { | |||
*buf = append(*buf, fgGreenBytes...) | |||
} | |||
file := event.filename | |||
if logger.Flags&Lmedfile == Lmedfile { | |||
startIndex := len(file) - 20 | |||
if startIndex > 0 { | |||
file = "..." + file[startIndex:] | |||
} | |||
} else if logger.Flags&Lshortfile != 0 { | |||
startIndex := strings.LastIndexByte(file, '/') | |||
if startIndex > 0 && startIndex < len(file) { | |||
file = file[startIndex+1:] | |||
} | |||
} | |||
*buf = append(*buf, file...) | |||
*buf = append(*buf, ':') | |||
itoa(buf, event.line, -1) | |||
if logger.Flags&(Lfuncname|Lshortfuncname) != 0 { | |||
*buf = append(*buf, ':') | |||
} else { | |||
if logger.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
} | |||
if logger.Flags&(Lfuncname|Lshortfuncname) != 0 { | |||
if logger.Colorize { | |||
*buf = append(*buf, fgGreenBytes...) | |||
} | |||
funcname := event.caller | |||
if logger.Flags&Lshortfuncname != 0 { | |||
lastIndex := strings.LastIndexByte(funcname, '.') | |||
if lastIndex > 0 && len(funcname) > lastIndex+1 { | |||
funcname = funcname[lastIndex+1:] | |||
} | |||
} | |||
*buf = append(*buf, funcname...) | |||
if logger.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
if logger.Flags&(Llevel|Llevelinitial) != 0 { | |||
level := strings.ToUpper(event.level.String()) | |||
if logger.Colorize { | |||
*buf = append(*buf, levelToColor[event.level]...) | |||
} | |||
*buf = append(*buf, '[') | |||
if logger.Flags&Llevelinitial != 0 { | |||
*buf = append(*buf, level[0]) | |||
} else { | |||
*buf = append(*buf, level...) | |||
} | |||
*buf = append(*buf, ']') | |||
if logger.Colorize { | |||
*buf = append(*buf, resetBytes...) | |||
} | |||
*buf = append(*buf, ' ') | |||
} | |||
var msg = []byte(event.msg) | |||
if len(msg) > 0 && msg[len(msg)-1] == '\n' { | |||
msg = msg[:len(msg)-1] | |||
} | |||
pawMode := allowColor | |||
if !logger.Colorize { | |||
pawMode = removeColor | |||
} | |||
baw := byteArrayWriter(*buf) | |||
(&protectedANSIWriter{ | |||
w: &baw, | |||
mode: pawMode, | |||
}).Write([]byte(msg)) | |||
*buf = baw | |||
if event.stacktrace != "" && logger.StacktraceLevel <= event.level { | |||
lines := bytes.Split([]byte(event.stacktrace), []byte("\n")) | |||
if len(lines) > 1 { | |||
for _, line := range lines { | |||
*buf = append(*buf, "\n\t"...) | |||
*buf = append(*buf, line...) | |||
} | |||
} | |||
*buf = append(*buf, '\n') | |||
} | |||
*buf = append(*buf, '\n') | |||
} | |||
// LogEvent logs the event to the internal writer | |||
func (logger *WriterLogger) LogEvent(event *Event) error { | |||
if logger.Level > event.level { | |||
return nil | |||
} | |||
logger.mu.Lock() | |||
defer logger.mu.Unlock() | |||
if !logger.Match(event) { | |||
return nil | |||
} | |||
var buf []byte | |||
logger.createMsg(&buf, event) | |||
_, err := logger.out.Write(buf) | |||
return err | |||
} | |||
// Match checks if the given event matches the logger's regexp expression | |||
func (logger *WriterLogger) Match(event *Event) bool { | |||
if logger.regexp == nil { | |||
return true | |||
} | |||
if logger.regexp.Match([]byte(fmt.Sprintf("%s:%d:%s", event.filename, event.line, event.caller))) { | |||
return true | |||
} | |||
// Match on the non-colored msg - therefore strip out colors | |||
var msg []byte | |||
baw := byteArrayWriter(msg) | |||
(&protectedANSIWriter{ | |||
w: &baw, | |||
mode: removeColor, | |||
}).Write([]byte(event.msg)) | |||
msg = baw | |||
if logger.regexp.Match(msg) { | |||
return true | |||
} | |||
return false | |||
} | |||
// Close the base logger | |||
func (logger *WriterLogger) Close() { | |||
logger.mu.Lock() | |||
defer logger.mu.Unlock() | |||
if logger.out != nil { | |||
logger.out.Close() | |||
} | |||
} | |||
// GetName returns empty for these provider loggers | |||
func (logger *WriterLogger) GetName() string { | |||
return "" | |||
} |
@@ -38,7 +38,7 @@ func TestBaseLogger(t *testing.T) { | |||
}, | |||
} | |||
prefix := "TestPrefix " | |||
b := BaseLogger{ | |||
b := WriterLogger{ | |||
out: c, | |||
Level: INFO, | |||
Flags: LstdFlags | LUTC, | |||
@@ -115,7 +115,7 @@ func TestBaseLoggerDated(t *testing.T) { | |||
}, | |||
} | |||
prefix := "" | |||
b := BaseLogger{ | |||
b := WriterLogger{ | |||
out: c, | |||
Level: WARN, | |||
Flags: Ldate | Ltime | Lmicroseconds | Lshortfile | Llevel, | |||
@@ -195,14 +195,14 @@ func TestBaseLoggerMultiLineNoFlagsRegexp(t *testing.T) { | |||
}, | |||
} | |||
prefix := "" | |||
b := BaseLogger{ | |||
b := WriterLogger{ | |||
Level: DEBUG, | |||
StacktraceLevel: ERROR, | |||
Flags: -1, | |||
Prefix: prefix, | |||
Expression: "FILENAME", | |||
} | |||
b.createLogger(c) | |||
b.NewWriterLogger(c) | |||
location, _ := time.LoadLocation("EST") | |||
@@ -263,14 +263,14 @@ func TestBrokenRegexp(t *testing.T) { | |||
}, | |||
} | |||
b := BaseLogger{ | |||
b := WriterLogger{ | |||
Level: DEBUG, | |||
StacktraceLevel: ERROR, | |||
Flags: -1, | |||
Prefix: prefix, | |||
Expression: "\\", | |||
} | |||
b.createLogger(c) | |||
b.NewWriterLogger(c) | |||
assert.Empty(t, b.regexp) | |||
b.Close() | |||
assert.Equal(t, true, closed) |
@@ -273,6 +273,15 @@ func newLogService() { | |||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT))) | |||
} | |||
// NewLogServices creates all the log services | |||
func NewLogServices(disableConsole bool) { | |||
newLogService() | |||
newMacaronLogService() | |||
newRouterLogService() | |||
newAccessLogService() | |||
NewXORMLogService(disableConsole) | |||
} | |||
// NewXORMLogService initializes xorm logger service | |||
func NewXORMLogService(disableConsole bool) { | |||
EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true) |
@@ -982,11 +982,7 @@ func loadOrGenerateInternalToken(sec *ini.Section) string { | |||
// NewServices initializes the services | |||
func NewServices() { | |||
newService() | |||
newLogService() | |||
newMacaronLogService() | |||
newAccessLogService() | |||
newRouterLogService() | |||
NewXORMLogService(false) | |||
NewLogServices(false) | |||
newCacheService() | |||
newSessionService() | |||
newMailService() |