From 0970d6cc38a79c57ffe8e3c85687f7c7c49059be Mon Sep 17 00:00:00 2001
From: Unknown <joe2010xtmf@163.com>
Date: Sun, 11 May 2014 14:37:12 -0400
Subject: [PATCH] Log to different adapter according to level

---
 conf/app.ini                |   7 ++-
 modules/base/conf.go        | 109 +++++++++++++++++++-----------------
 modules/log/log.go          |  41 ++++++++++----
 routers/admin/admin.go      |  10 +++-
 templates/admin/config.tmpl |   6 +-
 5 files changed, 104 insertions(+), 69 deletions(-)

diff --git a/conf/app.ini b/conf/app.ini
index f20665eab3..16a1e2cd0a 100644
--- a/conf/app.ini
+++ b/conf/app.ini
@@ -143,9 +143,9 @@ HOST =
 PROVIDER = file
 ; Provider config options
 ; memory: not have any config yet
-; file: session file path, e.g. data/sessions
-; redis: config like redis server addr, poolSize, password, e.g. 127.0.0.1:6379,100,astaxie
-; mysql: go-sql-driver/mysql dsn config string, e.g. root:password@/session_table
+; file: session file path, e.g. "data/sessions"
+; redis: config like redis server addr, poolSize, password, e.g. "127.0.0.1:6379,100,astaxie"
+; mysql: go-sql-driver/mysql dsn config string, e.g. "root:password@/session_table"
 PROVIDER_CONFIG = data/sessions
 ; Session cookie name
 COOKIE_NAME = i_like_gogits
@@ -169,6 +169,7 @@ DISABLE_GRAVATAR = false
 
 [log]
 ; Either "console", "file", "conn", "smtp" or "database", default is "console"
+; Use comma to separate multiple modes, e.g. "console, file"
 MODE = console
 ; Buffer length of channel, keep it as it is if you don't know what it is.
 BUFFER_LEN = 10000
diff --git a/modules/base/conf.go b/modules/base/conf.go
index 99bac9006f..c38d335775 100644
--- a/modules/base/conf.go
+++ b/modules/base/conf.go
@@ -70,8 +70,8 @@ var (
 	MailService  *Mailer
 	OauthService *Oauther
 
-	LogMode   string
-	LogConfig string
+	LogModes   []string
+	LogConfigs []string
 
 	Cache        cache.Cache
 	CacheAdapter string
@@ -130,57 +130,62 @@ func newService() {
 }
 
 func newLogService() {
-	// Get and check log mode.
-	LogMode = Cfg.MustValue("log", "MODE", "console")
-	modeSec := "log." + LogMode
-	if _, err := Cfg.GetSection(modeSec); err != nil {
-		qlog.Fatalf("Unknown log mode: %s\n", LogMode)
-	}
-
-	// Log level.
-	levelName := Cfg.MustValue("log."+LogMode, "LEVEL", "Trace")
-	level, ok := logLevels[levelName]
-	if !ok {
-		qlog.Fatalf("Unknown log level: %s\n", levelName)
-	}
-
-	// Generate log configuration.
-	switch LogMode {
-	case "console":
-		LogConfig = fmt.Sprintf(`{"level":%s}`, level)
-	case "file":
-		logPath := Cfg.MustValue(modeSec, "FILE_NAME", "log/gogs.log")
-		os.MkdirAll(path.Dir(logPath), os.ModePerm)
-		LogConfig = fmt.Sprintf(
-			`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
-			logPath,
-			Cfg.MustBool(modeSec, "LOG_ROTATE", true),
-			Cfg.MustInt(modeSec, "MAX_LINES", 1000000),
-			1<<uint(Cfg.MustInt(modeSec, "MAX_SIZE_SHIFT", 28)),
-			Cfg.MustBool(modeSec, "DAILY_ROTATE", true),
-			Cfg.MustInt(modeSec, "MAX_DAYS", 7))
-	case "conn":
-		LogConfig = fmt.Sprintf(`{"level":"%s","reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
-			Cfg.MustBool(modeSec, "RECONNECT_ON_MSG", false),
-			Cfg.MustBool(modeSec, "RECONNECT", false),
-			Cfg.MustValue(modeSec, "PROTOCOL", "tcp"),
-			Cfg.MustValue(modeSec, "ADDR", ":7020"))
-	case "smtp":
-		LogConfig = fmt.Sprintf(`{"level":"%s","username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
-			Cfg.MustValue(modeSec, "USER", "example@example.com"),
-			Cfg.MustValue(modeSec, "PASSWD", "******"),
-			Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"),
-			Cfg.MustValue(modeSec, "RECEIVERS", "[]"),
-			Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve"))
-	case "database":
-		LogConfig = fmt.Sprintf(`{"level":"%s","driver":"%s","conn":"%s"}`, level,
-			Cfg.MustValue(modeSec, "Driver"),
-			Cfg.MustValue(modeSec, "CONN"))
-	}
-
 	log.Info("%s %s", AppName, AppVer)
-	log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), LogMode, LogConfig)
-	log.Info("Log Mode: %s(%s)", strings.Title(LogMode), levelName)
+
+	// Get and check log mode.
+	LogModes = strings.Split(Cfg.MustValue("log", "MODE", "console"), ",")
+	LogConfigs = make([]string, len(LogModes))
+	for i, mode := range LogModes {
+		mode = strings.TrimSpace(mode)
+		modeSec := "log." + mode
+		if _, err := Cfg.GetSection(modeSec); err != nil {
+			qlog.Fatalf("Unknown log mode: %s\n", mode)
+		}
+
+		// Log level.
+		levelName := Cfg.MustValue("log."+mode, "LEVEL", "Trace")
+		level, ok := logLevels[levelName]
+		if !ok {
+			qlog.Fatalf("Unknown log level: %s\n", levelName)
+		}
+
+		// Generate log configuration.
+		switch mode {
+		case "console":
+			LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level)
+		case "file":
+			logPath := Cfg.MustValue(modeSec, "FILE_NAME", "log/gogs.log")
+			os.MkdirAll(path.Dir(logPath), os.ModePerm)
+			LogConfigs[i] = fmt.Sprintf(
+				`{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level,
+				logPath,
+				Cfg.MustBool(modeSec, "LOG_ROTATE", true),
+				Cfg.MustInt(modeSec, "MAX_LINES", 1000000),
+				1<<uint(Cfg.MustInt(modeSec, "MAX_SIZE_SHIFT", 28)),
+				Cfg.MustBool(modeSec, "DAILY_ROTATE", true),
+				Cfg.MustInt(modeSec, "MAX_DAYS", 7))
+		case "conn":
+			LogConfigs[i] = fmt.Sprintf(`{"level":"%s","reconnectOnMsg":%v,"reconnect":%v,"net":"%s","addr":"%s"}`, level,
+				Cfg.MustBool(modeSec, "RECONNECT_ON_MSG", false),
+				Cfg.MustBool(modeSec, "RECONNECT", false),
+				Cfg.MustValue(modeSec, "PROTOCOL", "tcp"),
+				Cfg.MustValue(modeSec, "ADDR", ":7020"))
+		case "smtp":
+			LogConfigs[i] = fmt.Sprintf(`{"level":"%s","username":"%s","password":"%s","host":"%s","sendTos":"%s","subject":"%s"}`, level,
+				Cfg.MustValue(modeSec, "USER", "example@example.com"),
+				Cfg.MustValue(modeSec, "PASSWD", "******"),
+				Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"),
+				Cfg.MustValue(modeSec, "RECEIVERS", "[]"),
+				Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve"))
+		case "database":
+			LogConfigs[i] = fmt.Sprintf(`{"level":"%s","driver":"%s","conn":"%s"}`, level,
+				Cfg.MustValue(modeSec, "Driver"),
+				Cfg.MustValue(modeSec, "CONN"))
+		}
+
+		log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), mode, LogConfigs[i])
+		log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName)
+	}
 }
 
 func newLdapService() {
diff --git a/modules/log/log.go b/modules/log/log.go
index 636ea787ca..eea3c8ada1 100644
--- a/modules/log/log.go
+++ b/modules/log/log.go
@@ -10,8 +10,7 @@ import (
 )
 
 var (
-	logger       *logs.BeeLogger
-	Mode, Config string
+	loggers []*logs.BeeLogger
 )
 
 func init() {
@@ -19,32 +18,54 @@ func init() {
 }
 
 func NewLogger(bufLen int64, mode, config string) {
-	Mode, Config = mode, config
-	logger = logs.NewLogger(bufLen)
+	logger := logs.NewLogger(bufLen)
+
+	isExist := false
+	for _, l := range loggers {
+		if l.Adapter == mode {
+			isExist = true
+			l = logger
+		}
+	}
+	if !isExist {
+		loggers = append(loggers, logger)
+	}
 	logger.SetLogFuncCallDepth(3)
 	logger.SetLogger(mode, config)
 }
 
 func Trace(format string, v ...interface{}) {
-	logger.Trace(format, v...)
+	for _, logger := range loggers {
+		logger.Trace(format, v...)
+	}
 }
 
 func Debug(format string, v ...interface{}) {
-	logger.Debug(format, v...)
+	for _, logger := range loggers {
+		logger.Debug(format, v...)
+	}
 }
 
 func Info(format string, v ...interface{}) {
-	logger.Info(format, v...)
+	for _, logger := range loggers {
+		logger.Info(format, v...)
+	}
 }
 
 func Error(format string, v ...interface{}) {
-	logger.Error(format, v...)
+	for _, logger := range loggers {
+		logger.Error(format, v...)
+	}
 }
 
 func Warn(format string, v ...interface{}) {
-	logger.Warn(format, v...)
+	for _, logger := range loggers {
+		logger.Warn(format, v...)
+	}
 }
 
 func Critical(format string, v ...interface{}) {
-	logger.Critical(format, v...)
+	for _, logger := range loggers {
+		logger.Critical(format, v...)
+	}
 }
diff --git a/routers/admin/admin.go b/routers/admin/admin.go
index dbd5e94594..f9c11f8378 100644
--- a/routers/admin/admin.go
+++ b/routers/admin/admin.go
@@ -211,8 +211,14 @@ func Config(ctx *middleware.Context) {
 	ctx.Data["PictureService"] = base.PictureService
 	ctx.Data["DisableGravatar"] = base.DisableGravatar
 
-	ctx.Data["LogMode"] = base.LogMode
-	ctx.Data["LogConfig"] = base.LogConfig
+	type logger struct {
+		Mode, Config string
+	}
+	loggers := make([]*logger, len(base.LogModes))
+	for i := range base.LogModes {
+		loggers[i] = &logger{base.LogModes[i], base.LogConfigs[i]}
+	}
+	ctx.Data["Loggers"] = loggers
 
 	ctx.HTML(200, "admin/config")
 }
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index 96565bac87..f49e382705 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -194,12 +194,14 @@
 
             <div class="panel-body">
                 <dl class="dl-horizontal admin-dl-horizontal">
+                    {{range .Loggers}}
                     <dt>Log Mode</dt>
-                    <dd>{{.LogMode}}</dd>
+                    <dd>{{.Mode}}</dd>
                     <dt>Log Config</dt>
                     <dd>
-                        <div style="padding-top: 5px;"><pre>{{.LogConfig}}</pre></div>
+                        <div style="padding-top: 5px;"><pre>{{.Config}}</pre></div>
                     </dd>
+                    {{end}}
                 </dl>
                 
             </div>